diff --git a/Cargo.toml b/Cargo.toml index c20cb10..4a2aefd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,12 +14,14 @@ features = ["expose-arith"] [dependencies] rand = "0.4" -blake2-rfc = "0.2.18" digest = "0.7" bellman = "0.0.9" - byteorder = "1" +[dependencies.blake2-rfc] +git = "https://github.com/gtank/blake2-rfc" +rev = "7a5b5fc99ae483a0043db7547fb79a6fa44b88a9" + [dev-dependencies] hex-literal = "0.1" diff --git a/src/circuit/blake2s.rs b/src/circuit/blake2s.rs index c46b243..f75f5c7 100644 --- a/src/circuit/blake2s.rs +++ b/src/circuit/blake2s.rs @@ -254,9 +254,13 @@ fn blake2s_compression>( pub fn blake2s>( mut cs: CS, - input: &[Boolean] + input: &[Boolean], + personalization: &[u8] ) -> Result, SynthesisError> { + use byteorder::{ByteOrder, LittleEndian}; + + assert_eq!(personalization.len(), 8); assert!(input.len() % 8 == 0); let mut h = Vec::with_capacity(8); @@ -266,8 +270,10 @@ pub fn blake2s>( h.push(UInt32::constant(0xA54FF53A)); h.push(UInt32::constant(0x510E527F)); h.push(UInt32::constant(0x9B05688C)); - h.push(UInt32::constant(0x1F83D9AB)); - h.push(UInt32::constant(0x5BE0CD19)); + + // Personalization is stored here + h.push(UInt32::constant(0x1F83D9AB ^ LittleEndian::read_u32(&personalization[0..4]))); + h.push(UInt32::constant(0x5BE0CD19 ^ LittleEndian::read_u32(&personalization[4..8]))); let mut blocks: Vec> = vec![]; @@ -315,11 +321,34 @@ mod test { use bellman::{ConstraintSystem}; use blake2_rfc::blake2s::Blake2s; + #[test] + fn test_blank_hash() { + let mut cs = TestConstraintSystem::::new(); + let input_bits = vec![]; + let out = blake2s(&mut cs, &input_bits, b"12345678").unwrap(); + assert!(cs.is_satisfied()); + assert_eq!(cs.num_constraints(), 0); + + // >>> import blake2s from hashlib + // >>> h = blake2s(digest_size=32, person=b'12345678') + // >>> h.hexdigest() + let expected = hex!("c59f682376d137f3f255e671e207d1f2374ebe504e9314208a52d9f88d69e8c8"); + + let mut out = out.into_iter(); + for b in expected.into_iter() { + for i in (0..8).rev() { + let c = out.next().unwrap().get_value().unwrap(); + + assert_eq!(c, (b >> i) & 1u8 == 1u8); + } + } + } + #[test] fn test_blake2s_constraints() { let mut cs = TestConstraintSystem::::new(); let input_bits: Vec<_> = (0..512).map(|i| AllocatedBit::alloc(cs.namespace(|| format!("input bit {}", i)), Some(true)).unwrap().into()).collect(); - blake2s(&mut cs, &input_bits).unwrap(); + blake2s(&mut cs, &input_bits, b"12345678").unwrap(); assert!(cs.is_satisfied()); assert_eq!(cs.num_constraints(), 21792); } @@ -336,7 +365,7 @@ mod test { .chain((0..512) .map(|i| AllocatedBit::alloc(cs.namespace(|| format!("input bit {}", i)), Some(true)).unwrap().into())) .collect(); - blake2s(&mut cs, &input_bits).unwrap(); + blake2s(&mut cs, &input_bits, b"12345678").unwrap(); assert!(cs.is_satisfied()); assert_eq!(cs.num_constraints(), 21792); } @@ -346,7 +375,7 @@ mod test { let mut cs = TestConstraintSystem::::new(); let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let input_bits: Vec<_> = (0..512).map(|_| Boolean::constant(rng.gen())).collect(); - blake2s(&mut cs, &input_bits).unwrap(); + blake2s(&mut cs, &input_bits, b"12345678").unwrap(); assert_eq!(cs.num_constraints(), 0); } @@ -356,7 +385,7 @@ mod test { for input_len in (0..32).chain((32..256).filter(|a| a % 8 == 0)) { - let mut h = Blake2s::new(32); + let mut h = Blake2s::with_params(32, &[], &[], b"12345678"); let data: Vec = (0..input_len).map(|_| rng.gen()).collect(); @@ -376,7 +405,7 @@ mod test { } } - let r = blake2s(&mut cs, &input_bits).unwrap(); + let r = blake2s(&mut cs, &input_bits, b"12345678").unwrap(); assert!(cs.is_satisfied()); diff --git a/src/circuit/mod.rs b/src/circuit/mod.rs index f057a83..6ee8853 100644 --- a/src/circuit/mod.rs +++ b/src/circuit/mod.rs @@ -156,7 +156,8 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { // Compute the incoming viewing key let mut ivk = blake2s::blake2s( cs.namespace(|| "computation of ivk"), - &vk + &vk, + ::CRH_IVK_PERSONALIZATION )?; // Little endian bit order @@ -299,7 +300,8 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { let mut rho = blake2s::blake2s( cs.namespace(|| "rho computation"), - &rho_preimage + &rho_preimage, + ::PRF_NR_PERSONALIZATION )?; // Little endian bit order @@ -515,7 +517,7 @@ fn test_input_circuit_with_bls12_381() { assert!(cs.is_satisfied()); assert_eq!(cs.num_constraints(), 97395); - assert_eq!(cs.hash(), "cdd3cde0a4e076b46a59ef85fb70369eb14e3ee921a06d88bad6be4f78b5f261"); + assert_eq!(cs.hash(), "9f730803965612392772c3c1fbb110c1539656e1bab40d5a9a124b06e927ef40"); } } @@ -553,6 +555,6 @@ fn test_output_circuit_with_bls12_381() { assert!(cs.is_satisfied()); assert_eq!(cs.num_constraints(), 7827); - assert_eq!(cs.hash(), "67518baade37a3cf76453fa474cb8c9b2ee4223ed5502151e3b83dd1ec98a261"); + assert_eq!(cs.hash(), "f4219872738a81ef3ea66199ea5019d87f53ec369ee7f64d0b7c63ade6014114"); } } diff --git a/src/group_hash.rs b/src/group_hash.rs index 7d04e36..58ece78 100644 --- a/src/group_hash.rs +++ b/src/group_hash.rs @@ -12,13 +12,16 @@ pub const FIRST_BLOCK: &'static [u8; 64] = b"0000000000000000002ffe76b973aabaff1 /// identity. pub fn group_hash( tag: &[u8], + personalization: &[u8], params: &E::Params ) -> Option> { + assert_eq!(personalization.len(), 8); + // Check to see that scalar field is 255 bits assert!(E::Fr::NUM_BITS == 255); - let mut h = Blake2s::new(32); + let mut h = Blake2s::with_params(32, &[], &[], personalization); h.update(FIRST_BLOCK); h.update(tag); let mut h = h.finalize().as_ref().to_vec(); diff --git a/src/jubjub/mod.rs b/src/jubjub/mod.rs index dfc34a2..c293b34 100644 --- a/src/jubjub/mod.rs +++ b/src/jubjub/mod.rs @@ -186,7 +186,7 @@ impl JubjubBls12 { let mut pedersen_hash_generators = vec![]; while pedersen_hash_generators.len() < 10 { - let gh = group_hash(&[cur], &tmp); + let gh = group_hash(&[cur], ::PEDERSEN_HASH_GENERATORS_PERSONALIZATION, &tmp); // We don't want to overflow and start reusing generators assert!(cur != u8::max_value()); cur += 1; @@ -205,7 +205,7 @@ impl JubjubBls12 { let mut fixed_base_generators = vec![]; while fixed_base_generators.len() < (FixedGenerators::Max as usize) { - let gh = group_hash(&[cur], &tmp); + let gh = group_hash(&[cur], ::OTHER_PERSONALIZATION, &tmp); // We don't want to overflow and start reusing generators assert!(cur != u8::max_value()); cur += 1; diff --git a/src/lib.rs b/src/lib.rs index 8501bde..e14cd9b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,3 +15,11 @@ pub mod circuit; pub mod group_hash; pub mod pedersen_hash; pub mod primitives; + +// BLAKE2s personalizations +pub const CRH_IVK_PERSONALIZATION: &'static [u8; 8] = b"Zcashivk"; +pub const PRF_NR_PERSONALIZATION: &'static [u8; 8] = b"WhatTheH"; +pub const PEDERSEN_HASH_GENERATORS_PERSONALIZATION: &'static [u8; 8] = b"PEDERSEN"; + +// TODO: Expand the personalizations to the specific generators +pub const OTHER_PERSONALIZATION: &'static [u8; 8] = b"GOTOFAIL"; diff --git a/src/pedersen_hash.rs b/src/pedersen_hash.rs index a1eda3b..5c3bb90 100644 --- a/src/pedersen_hash.rs +++ b/src/pedersen_hash.rs @@ -11,8 +11,11 @@ impl Personalization { match *self { Personalization::NoteCommitment => vec![true, true, true, true, true, true], - Personalization::MerkleTree(num) => + Personalization::MerkleTree(num) => { + assert!(num < 63); + (0..6).map(|i| (num >> i) & 1 == 1).collect() + } } } }