sapling_crypto/
constants.rs

1//! Various constants used by the Sapling protocol.
2
3use alloc::vec::Vec;
4use ff::PrimeField;
5use group::Group;
6use jubjub::SubgroupPoint;
7use lazy_static::lazy_static;
8
9/// First 64 bytes of the BLAKE2s input during group hash.
10/// This is chosen to be some random string that we couldn't have anticipated when we designed
11/// the algorithm, for rigidity purposes.
12/// We deliberately use an ASCII hex string of 32 bytes here.
13pub const GH_FIRST_BLOCK: &[u8; 64] =
14    b"096b36a5804bfacef1691e173c366a47ff5ba84a44f26ddd7e8d9f79d5b42df0";
15
16// BLAKE2s invocation personalizations
17/// BLAKE2s Personalization for CRH^ivk = BLAKE2s(ak | nk)
18pub const CRH_IVK_PERSONALIZATION: &[u8; 8] = b"Zcashivk";
19
20/// BLAKE2s Personalization for PRF^nf = BLAKE2s(nk | rho)
21pub const PRF_NF_PERSONALIZATION: &[u8; 8] = b"Zcash_nf";
22
23// Group hash personalizations
24/// BLAKE2s Personalization for Pedersen hash generators.
25pub const PEDERSEN_HASH_GENERATORS_PERSONALIZATION: &[u8; 8] = b"Zcash_PH";
26
27/// BLAKE2s Personalization for the group hash for key diversification
28pub const KEY_DIVERSIFICATION_PERSONALIZATION: &[u8; 8] = b"Zcash_gd";
29
30/// BLAKE2s Personalization for the spending key base point
31pub const SPENDING_KEY_GENERATOR_PERSONALIZATION: &[u8; 8] = b"Zcash_G_";
32
33/// BLAKE2s Personalization for the proof generation key base point
34pub const PROOF_GENERATION_KEY_BASE_GENERATOR_PERSONALIZATION: &[u8; 8] = b"Zcash_H_";
35
36/// BLAKE2s Personalization for the value commitment generator for the value
37pub const VALUE_COMMITMENT_GENERATOR_PERSONALIZATION: &[u8; 8] = b"Zcash_cv";
38
39/// BLAKE2s Personalization for the nullifier position generator (for computing rho)
40pub const NULLIFIER_POSITION_IN_TREE_GENERATOR_PERSONALIZATION: &[u8; 8] = b"Zcash_J_";
41
42// π_A + π_B + π_C
43pub(crate) const GROTH_PROOF_SIZE: usize = 48 + 96 + 48;
44
45/// The prover will demonstrate knowledge of discrete log with respect to this base when
46/// they are constructing a proof, in order to authorize proof construction.
47pub const PROOF_GENERATION_KEY_GENERATOR: SubgroupPoint = SubgroupPoint::from_raw_unchecked(
48    bls12_381::Scalar::from_raw([
49        0x3af2_dbef_b96e_2571,
50        0xadf2_d038_f2fb_b820,
51        0x7043_03f1_e890_6081,
52        0x1457_a502_31cd_e2df,
53    ]),
54    bls12_381::Scalar::from_raw([
55        0x467a_f9f7_e05d_e8e7,
56        0x50df_51ea_f5a1_49d2,
57        0xdec9_0184_0f49_48cc,
58        0x54b6_d107_18df_2a7a,
59    ]),
60);
61
62/// The note commitment is randomized over this generator.
63pub const NOTE_COMMITMENT_RANDOMNESS_GENERATOR: SubgroupPoint = SubgroupPoint::from_raw_unchecked(
64    bls12_381::Scalar::from_raw([
65        0xa514_3b34_a8e3_6462,
66        0xf091_9d06_ffb1_ecda,
67        0xa140_9aa1_f33b_ec2c,
68        0x26eb_9f8a_9ec7_2a8c,
69    ]),
70    bls12_381::Scalar::from_raw([
71        0xd4fc_6365_796c_77ac,
72        0x96b7_8bea_fa9c_c44c,
73        0x949d_7747_6e26_2c95,
74        0x114b_7501_ad10_4c57,
75    ]),
76);
77
78/// The node commitment is randomized again by the position in order to supply the
79/// nullifier computation with a unique input w.r.t. the note being spent, to prevent
80/// Faerie gold attacks.
81pub const NULLIFIER_POSITION_GENERATOR: SubgroupPoint = SubgroupPoint::from_raw_unchecked(
82    bls12_381::Scalar::from_raw([
83        0x2ce3_3921_888d_30db,
84        0xe81c_ee09_a561_229e,
85        0xdb56_b6db_8d80_75ed,
86        0x2400_c2e2_e336_2644,
87    ]),
88    bls12_381::Scalar::from_raw([
89        0xa3f7_fa36_c72b_0065,
90        0xe155_b8e8_ffff_2e42,
91        0xfc9e_8a15_a096_ba8f,
92        0x6136_9d54_40bf_84a5,
93    ]),
94);
95
96/// The value commitment is used to check balance between inputs and outputs. The value is
97/// placed over this generator.
98pub const VALUE_COMMITMENT_VALUE_GENERATOR: SubgroupPoint = SubgroupPoint::from_raw_unchecked(
99    bls12_381::Scalar::from_raw([
100        0x3618_3b2c_b4d7_ef51,
101        0x9472_c89a_c043_042d,
102        0xd861_8ed1_d15f_ef4e,
103        0x273f_910d_9ecc_1615,
104    ]),
105    bls12_381::Scalar::from_raw([
106        0xa77a_81f5_0667_c8d7,
107        0xbc33_32d0_fa1c_cd18,
108        0xd322_94fd_8977_4ad6,
109        0x466a_7e3a_82f6_7ab1,
110    ]),
111);
112
113/// The value commitment is randomized over this generator, for privacy.
114pub const VALUE_COMMITMENT_RANDOMNESS_GENERATOR: SubgroupPoint = SubgroupPoint::from_raw_unchecked(
115    bls12_381::Scalar::from_raw([
116        0x3bce_3b77_9366_4337,
117        0xd1d8_da41_af03_744e,
118        0x7ff6_826a_d580_04b4,
119        0x6800_f4fa_0f00_1cfc,
120    ]),
121    bls12_381::Scalar::from_raw([
122        0x3cae_fab9_380b_6a8b,
123        0xad46_f1b0_473b_803b,
124        0xe6fb_2a6e_1e22_ab50,
125        0x6d81_d3a9_cb45_dedb,
126    ]),
127);
128
129/// The spender proves discrete log with respect to this base at spend time.
130pub const SPENDING_KEY_GENERATOR: SubgroupPoint = SubgroupPoint::from_raw_unchecked(
131    bls12_381::Scalar::from_raw([
132        0x47bf_4692_0a95_a753,
133        0xd5b9_a7d3_ef8e_2827,
134        0xd418_a7ff_2675_3b6a,
135        0x0926_d4f3_2059_c712,
136    ]),
137    bls12_381::Scalar::from_raw([
138        0x3056_32ad_aaf2_b530,
139        0x6d65_674d_cedb_ddbc,
140        0x53bb_37d0_c21c_fd05,
141        0x57a1_019e_6de9_b675,
142    ]),
143);
144
145/// The generators (for each segment) used in all Pedersen commitments.
146pub const PEDERSEN_HASH_GENERATORS: &[SubgroupPoint] = &[
147    SubgroupPoint::from_raw_unchecked(
148        bls12_381::Scalar::from_raw([
149            0x194e_4292_6f66_1b51,
150            0x2f0c_718f_6f0f_badd,
151            0xb5ea_25de_7ec0_e378,
152            0x73c0_16a4_2ded_9578,
153        ]),
154        bls12_381::Scalar::from_raw([
155            0x77bf_abd4_3224_3cca,
156            0xf947_2e8b_c04e_4632,
157            0x79c9_166b_837e_dc5e,
158            0x289e_87a2_d352_1b57,
159        ]),
160    ),
161    SubgroupPoint::from_raw_unchecked(
162        bls12_381::Scalar::from_raw([
163            0xb981_9dc8_2d90_607e,
164            0xa361_ee3f_d48f_df77,
165            0x52a3_5a8c_1908_dd87,
166            0x15a3_6d1f_0f39_0d88,
167        ]),
168        bls12_381::Scalar::from_raw([
169            0x7b0d_c53c_4ebf_1891,
170            0x1f3a_beeb_98fa_d3e8,
171            0xf789_1142_c001_d925,
172            0x015d_8c7f_5b43_fe33,
173        ]),
174    ),
175    SubgroupPoint::from_raw_unchecked(
176        bls12_381::Scalar::from_raw([
177            0x76d6_f7c2_b67f_c475,
178            0xbae8_e5c4_6641_ae5c,
179            0xeb69_ae39_f5c8_4210,
180            0x6643_21a5_8246_e2f6,
181        ]),
182        bls12_381::Scalar::from_raw([
183            0x80ed_502c_9793_d457,
184            0x8bb2_2a7f_1784_b498,
185            0xe000_a46c_8e8c_e853,
186            0x362e_1500_d24e_ee9e,
187        ]),
188    ),
189    SubgroupPoint::from_raw_unchecked(
190        bls12_381::Scalar::from_raw([
191            0x4c76_7804_c1c4_a2cc,
192            0x7d02_d50e_654b_87f2,
193            0xedc5_f4a9_cff2_9fd5,
194            0x323a_6548_ce9d_9876,
195        ]),
196        bls12_381::Scalar::from_raw([
197            0x8471_4bec_a335_70e9,
198            0x5103_afa1_a11f_6a85,
199            0x9107_0acb_d8d9_47b7,
200            0x2f7e_e40c_4b56_cad8,
201        ]),
202    ),
203    SubgroupPoint::from_raw_unchecked(
204        bls12_381::Scalar::from_raw([
205            0x4680_9430_657f_82d1,
206            0xefd5_9313_05f2_f0bf,
207            0x89b6_4b4e_0336_2796,
208            0x3bd2_6660_00b5_4796,
209        ]),
210        bls12_381::Scalar::from_raw([
211            0x9996_8299_c365_8aef,
212            0xb3b9_d809_5859_d14c,
213            0x3978_3238_1406_c9e5,
214            0x494b_c521_03ab_9d0a,
215        ]),
216    ),
217    SubgroupPoint::from_raw_unchecked(
218        bls12_381::Scalar::from_raw([
219            0xcb3c_0232_58d3_2079,
220            0x1d9e_5ca2_1135_ff6f,
221            0xda04_9746_d76d_3ee5,
222            0x6344_7b2b_a31b_b28a,
223        ]),
224        bls12_381::Scalar::from_raw([
225            0x4360_8211_9f8d_629a,
226            0xa802_00d2_c66b_13a7,
227            0x64cd_b107_0a13_6a28,
228            0x64ec_4689_e8bf_b6e5,
229        ]),
230    ),
231];
232
233/// The maximum number of chunks per segment of the Pedersen hash.
234pub const PEDERSEN_HASH_CHUNKS_PER_GENERATOR: usize = 63;
235
236/// The window size for exponentiation of Pedersen hash generators outside the circuit.
237pub const PEDERSEN_HASH_EXP_WINDOW_SIZE: u32 = 8;
238
239lazy_static! {
240    /// The exp table for [`PEDERSEN_HASH_GENERATORS`].
241    pub static ref PEDERSEN_HASH_EXP_TABLE: Vec<Vec<Vec<SubgroupPoint>>> =
242        generate_pedersen_hash_exp_table();
243}
244
245/// Creates the exp table for the Pedersen hash generators.
246fn generate_pedersen_hash_exp_table() -> Vec<Vec<Vec<SubgroupPoint>>> {
247    let window = PEDERSEN_HASH_EXP_WINDOW_SIZE;
248
249    PEDERSEN_HASH_GENERATORS
250        .iter()
251        .cloned()
252        .map(|mut g| {
253            let mut tables = vec![];
254
255            let mut num_bits = 0;
256            while num_bits <= jubjub::Fr::NUM_BITS {
257                let mut table = Vec::with_capacity(1 << window);
258                let mut base = SubgroupPoint::identity();
259
260                for _ in 0..(1 << window) {
261                    table.push(base);
262                    base += g;
263                }
264
265                tables.push(table);
266                num_bits += window;
267
268                for _ in 0..window {
269                    g = g.double();
270                }
271            }
272
273            tables
274        })
275        .collect()
276}
277
278#[cfg(test)]
279mod tests {
280    use jubjub::SubgroupPoint;
281
282    use super::*;
283    use crate::group_hash::group_hash;
284
285    fn find_group_hash(m: &[u8], personalization: &[u8; 8]) -> SubgroupPoint {
286        let mut tag = m.to_vec();
287        let i = tag.len();
288        tag.push(0u8);
289
290        loop {
291            let gh = group_hash(&tag, personalization);
292
293            // We don't want to overflow and start reusing generators
294            assert!(tag[i] != u8::MAX);
295            tag[i] += 1;
296
297            if let Some(gh) = gh {
298                break gh;
299            }
300        }
301    }
302
303    #[test]
304    fn proof_generation_key_base_generator() {
305        assert_eq!(
306            find_group_hash(&[], PROOF_GENERATION_KEY_BASE_GENERATOR_PERSONALIZATION),
307            PROOF_GENERATION_KEY_GENERATOR,
308        );
309    }
310
311    #[test]
312    fn note_commitment_randomness_generator() {
313        assert_eq!(
314            find_group_hash(b"r", PEDERSEN_HASH_GENERATORS_PERSONALIZATION),
315            NOTE_COMMITMENT_RANDOMNESS_GENERATOR,
316        );
317    }
318
319    #[test]
320    fn nullifier_position_generator() {
321        assert_eq!(
322            find_group_hash(&[], NULLIFIER_POSITION_IN_TREE_GENERATOR_PERSONALIZATION),
323            NULLIFIER_POSITION_GENERATOR,
324        );
325    }
326
327    #[test]
328    fn value_commitment_value_generator() {
329        assert_eq!(
330            find_group_hash(b"v", VALUE_COMMITMENT_GENERATOR_PERSONALIZATION),
331            VALUE_COMMITMENT_VALUE_GENERATOR,
332        );
333    }
334
335    #[test]
336    fn value_commitment_randomness_generator() {
337        assert_eq!(
338            find_group_hash(b"r", VALUE_COMMITMENT_GENERATOR_PERSONALIZATION),
339            VALUE_COMMITMENT_RANDOMNESS_GENERATOR,
340        );
341    }
342
343    #[test]
344    fn spending_key_generator() {
345        assert_eq!(
346            find_group_hash(&[], SPENDING_KEY_GENERATOR_PERSONALIZATION),
347            SPENDING_KEY_GENERATOR,
348        );
349    }
350
351    #[test]
352    fn pedersen_hash_generators() {
353        for (m, actual) in PEDERSEN_HASH_GENERATORS.iter().enumerate() {
354            assert_eq!(
355                &find_group_hash(
356                    &(m as u32).to_le_bytes(),
357                    PEDERSEN_HASH_GENERATORS_PERSONALIZATION
358                ),
359                actual
360            );
361        }
362    }
363
364    #[test]
365    fn no_duplicate_fixed_base_generators() {
366        let fixed_base_generators = [
367            PROOF_GENERATION_KEY_GENERATOR,
368            NOTE_COMMITMENT_RANDOMNESS_GENERATOR,
369            NULLIFIER_POSITION_GENERATOR,
370            VALUE_COMMITMENT_VALUE_GENERATOR,
371            VALUE_COMMITMENT_RANDOMNESS_GENERATOR,
372            SPENDING_KEY_GENERATOR,
373        ];
374
375        // Check for duplicates, far worse than spec inconsistencies!
376        for (i, p1) in fixed_base_generators.iter().enumerate() {
377            if p1.is_identity().into() {
378                panic!("Neutral element!");
379            }
380
381            for p2 in fixed_base_generators.iter().skip(i + 1) {
382                if p1 == p2 {
383                    panic!("Duplicate generator!");
384                }
385            }
386        }
387    }
388
389    /// Check for simple relations between the generators, that make finding collisions easy;
390    /// far worse than spec inconsistencies!
391    fn check_consistency_of_pedersen_hash_generators(
392        pedersen_hash_generators: &[jubjub::SubgroupPoint],
393    ) {
394        for (i, p1) in pedersen_hash_generators.iter().enumerate() {
395            if p1.is_identity().into() {
396                panic!("Neutral element!");
397            }
398            for p2 in pedersen_hash_generators.iter().skip(i + 1) {
399                if p1 == p2 {
400                    panic!("Duplicate generator!");
401                }
402                if *p1 == -p2 {
403                    panic!("Inverse generator!");
404                }
405            }
406
407            // check for a generator being the sum of any other two
408            for (j, p2) in pedersen_hash_generators.iter().enumerate() {
409                if j == i {
410                    continue;
411                }
412                for (k, p3) in pedersen_hash_generators.iter().enumerate() {
413                    if k == j || k == i {
414                        continue;
415                    }
416                    let sum = p2 + p3;
417                    if sum == *p1 {
418                        panic!("Linear relation between generators!");
419                    }
420                }
421            }
422        }
423    }
424
425    #[test]
426    fn pedersen_hash_generators_consistency() {
427        check_consistency_of_pedersen_hash_generators(PEDERSEN_HASH_GENERATORS);
428    }
429
430    #[test]
431    #[should_panic(expected = "Linear relation between generators!")]
432    fn test_jubjub_bls12_pedersen_hash_generators_consistency_check_linear_relation() {
433        let mut pedersen_hash_generators = PEDERSEN_HASH_GENERATORS.to_vec();
434
435        // Test for linear relation
436        pedersen_hash_generators.push(PEDERSEN_HASH_GENERATORS[0] + PEDERSEN_HASH_GENERATORS[1]);
437
438        check_consistency_of_pedersen_hash_generators(&pedersen_hash_generators);
439    }
440}