From 0df0a15e1b43699923e1279c7f2d4d5933a0a36d Mon Sep 17 00:00:00 2001 From: Pratyush Mishra Date: Thu, 5 Mar 2020 10:51:59 -0800 Subject: [PATCH] Blake PRF & R1CS Boolean Refactor (#132) * refactor(r1cs-std/boolean): expose enforcing value less than functionality * fix(r1cs-std/boolean): ensure num_bits is calculated correctly from the arg * feat(primitives/blake2s): allow creating Blake2s with custom params --- .../src/prf/blake2s/constraints.rs | 46 +++++++++++--- crypto-primitives/src/prf/blake2s/mod.rs | 61 +++++++++++++++++++ r1cs-std/src/bits/boolean.rs | 54 ++++++++++++---- 3 files changed, 142 insertions(+), 19 deletions(-) diff --git a/crypto-primitives/src/prf/blake2s/constraints.rs b/crypto-primitives/src/prf/blake2s/constraints.rs index ef2dc1f..f23d620 100644 --- a/crypto-primitives/src/prf/blake2s/constraints.rs +++ b/crypto-primitives/src/prf/blake2s/constraints.rs @@ -313,20 +313,50 @@ fn blake2s_compression>( + cs: CS, + input: &[Boolean], +) -> Result, SynthesisError> { + assert!(input.len() % 8 == 0); + let mut parameters = [0; 8]; + parameters[0] = 0x01010000 ^ 32; + blake2s_gadget_with_parameters(cs, input, ¶meters) +} + +pub fn blake2s_gadget_with_parameters< + ConstraintF: PrimeField, + CS: ConstraintSystem, +>( mut cs: CS, input: &[Boolean], + parameters: &[u32; 8], ) -> Result, SynthesisError> { assert!(input.len() % 8 == 0); let mut h = Vec::with_capacity(8); - h.push(UInt32::constant(0x6A09E667 ^ 0x01010000 ^ 32)); - h.push(UInt32::constant(0xBB67AE85)); - h.push(UInt32::constant(0x3C6EF372)); - 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)); + h.push( + UInt32::constant(0x6A09E667).xor(cs.ns(|| "xor h[0]"), &UInt32::constant(parameters[0]))?, + ); + h.push( + UInt32::constant(0xBB67AE85).xor(cs.ns(|| "xor h[1]"), &UInt32::constant(parameters[1]))?, + ); + h.push( + UInt32::constant(0x3C6EF372).xor(cs.ns(|| "xor h[2]"), &UInt32::constant(parameters[2]))?, + ); + h.push( + UInt32::constant(0xA54FF53A).xor(cs.ns(|| "xor h[3]"), &UInt32::constant(parameters[3]))?, + ); + h.push( + UInt32::constant(0x510E527F).xor(cs.ns(|| "xor h[4]"), &UInt32::constant(parameters[4]))?, + ); + h.push( + UInt32::constant(0x9B05688C).xor(cs.ns(|| "xor h[5]"), &UInt32::constant(parameters[5]))?, + ); + h.push( + UInt32::constant(0x1F83D9AB).xor(cs.ns(|| "xor h[6]"), &UInt32::constant(parameters[6]))?, + ); + h.push( + UInt32::constant(0x5BE0CD19).xor(cs.ns(|| "xor h[7]"), &UInt32::constant(parameters[7]))?, + ); let mut blocks: Vec> = vec![]; diff --git a/crypto-primitives/src/prf/blake2s/mod.rs b/crypto-primitives/src/prf/blake2s/mod.rs index 23d1d67..18c1dc9 100644 --- a/crypto-primitives/src/prf/blake2s/mod.rs +++ b/crypto-primitives/src/prf/blake2s/mod.rs @@ -1,3 +1,4 @@ +use alloc::vec::Vec; use blake2::Blake2s as b2s; use digest::Digest; @@ -26,3 +27,63 @@ impl PRF for Blake2s { Ok(result) } } + +#[derive(Clone)] +pub struct Blake2sWithParameterBlock { + pub digest_length: u8, + pub key_length: u8, + pub fan_out: u8, + pub depth: u8, + pub leaf_length: u32, + pub node_offset: u32, + pub xof_digest_length: u16, + pub node_depth: u8, + pub inner_length: u8, + pub salt: [u8; 8], + pub personalization: [u8; 8], +} + +impl Blake2sWithParameterBlock { + pub fn parameters(&self) -> [u32; 8] { + let mut parameters = [0; 8]; + parameters[0] = u32::from_le_bytes([ + self.digest_length, + self.key_length, + self.fan_out, + self.depth, + ]); + parameters[1] = self.leaf_length; + parameters[2] = self.node_offset; + parameters[3] = u32::from_le_bytes([ + self.xof_digest_length as u8, + (self.xof_digest_length >> 8) as u8, + self.node_depth, + self.inner_length, + ]); + + let mut salt_bytes_1 = [0; 4]; + let mut salt_bytes_2 = [0; 4]; + let mut personalization_bytes_1 = [0; 4]; + let mut personalization_bytes_2 = [0; 4]; + for i in 0..4 { + salt_bytes_1[i] = self.salt[i]; + salt_bytes_2[i] = self.salt[4 + i]; + personalization_bytes_1[i] = self.personalization[i]; + personalization_bytes_2[i] = self.personalization[4 + i]; + } + parameters[4] = u32::from_le_bytes(salt_bytes_1); + parameters[5] = u32::from_le_bytes(salt_bytes_2); + parameters[6] = u32::from_le_bytes(personalization_bytes_1); + parameters[7] = u32::from_le_bytes(personalization_bytes_2); + + parameters + } + + pub fn evaluate(&self, input: &[u8]) -> Vec { + let eval_time = start_timer!(|| "Blake2sWithParameterBlock::Eval"); + let mut h = b2s::with_parameter_block(&self.parameters()); + h.input(input.as_ref()); + end_timer!(eval_time); + h.result().to_vec() + } +} diff --git a/r1cs-std/src/bits/boolean.rs b/r1cs-std/src/bits/boolean.rs index be4f6e8..bbcef65 100644 --- a/r1cs-std/src/bits/boolean.rs +++ b/r1cs-std/src/bits/boolean.rs @@ -1,4 +1,4 @@ -use algebra::{BitIterator, Field, FpParameters, PrimeField}; +use algebra::{BitIterator, Field, PrimeField}; use crate::{prelude::*, Assignment, Vec}; use core::borrow::Borrow; @@ -576,19 +576,40 @@ impl Boolean { /// Asserts that this bit_gadget representation is "in /// the field" when interpreted in big endian. pub fn enforce_in_field( - mut cs: CS, + cs: CS, bits: &[Self], ) -> Result<(), SynthesisError> where ConstraintF: Field, CS: ConstraintSystem, { - let mut bits_iter = bits.iter(); - // b = char() - 1 let mut b = F::characteristic().to_vec(); assert_eq!(b[0] % 2, 1); b[0] -= 1; + let run = Self::enforce_smaller_or_equal_than::<_, _, F, _>(cs, bits, b)?; + + // We should always end in a "run" of zeros, because + // the characteristic is an odd prime. So, this should + // be empty. + assert!(run.is_empty()); + + Ok(()) + } + + /// Asserts that this bit_gadget representation is smaller + /// or equal than the provided element + pub fn enforce_smaller_or_equal_than>( + mut cs: CS, + bits: &[Self], + element: E, + ) -> Result, SynthesisError> + where + ConstraintF: Field, + CS: ConstraintSystem, + { + let mut bits_iter = bits.iter(); + let b: &[u64] = element.as_ref(); // Runs of ones in r let mut last_run = Boolean::constant(true); @@ -598,7 +619,23 @@ impl Boolean { let mut run_i = 0; let mut nand_i = 0; - let char_num_bits = ::Params::MODULUS_BITS as usize; + let char_num_bits = { + let mut leading_zeros = 0; + let mut total_bits = 0; + let mut found_one = false; + for b in BitIterator::new(b.clone()) { + total_bits += 1; + if !b && !found_one { + leading_zeros += 1 + } + if b { + found_one = true; + } + } + + total_bits - leading_zeros + }; + if bits.len() > char_num_bits { let num_extra_bits = bits.len() - char_num_bits; let mut or_result = Boolean::constant(false); @@ -651,12 +688,7 @@ impl Boolean { } assert!(bits_iter.next().is_none()); - // We should always end in a "run" of zeros, because - // the characteristic is an odd prime. So, this should - // be empty. - assert!(current_run.is_empty()); - - Ok(()) + Ok(current_run) } }