arnaucube
arnaucube/ark-r1cs-stdPublic Self-hosted repository
ark-r1cs-std/crypto-primitives/src/prf/blake2s/constraints.rs
crypto-primitives/src/prf/blake2s/constraints.rsRaw
  1use algebra_core::PrimeField;
  2use r1cs_core::{ConstraintSystem, SynthesisError};
  3
  4use crate::{prf::PRFGadget, Vec};
  5use r1cs_std::prelude::*;
  6
  7use core::borrow::Borrow;
  8
  9// 2.1.  Parameters
 10// The following table summarizes various parameters and their ranges:
 11//               | BLAKE2b          | BLAKE2s          |
 12// --------------+------------------+------------------+
 13// Bits in word  | w = 64           | w = 32           |
 14// Rounds in F   | r = 12           | r = 10           |
 15// Block bytes   | bb = 128         | bb = 64          |
 16// Hash bytes    | 1 <= nn <= 64    | 1 <= nn <= 32    |
 17// Key bytes     | 0 <= kk <= 64    | 0 <= kk <= 32    |
 18// Input bytes   | 0 <= ll < 2**128 | 0 <= ll < 2**64  |
 19// --------------+------------------+------------------+
 20// G Rotation    | (R1, R2, R3, R4) | (R1, R2, R3, R4) |
 21// constants =   | (32, 24, 16, 63) | (16, 12,  8,  7) |
 22// --------------+------------------+------------------+
 23//
 24
 25const R1: usize = 16;
 26const R2: usize = 12;
 27const R3: usize = 8;
 28const R4: usize = 7;
 29
 30// Round     |  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 |
 31// ----------+-------------------------------------------------+
 32// SIGMA[0]  |  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 |
 33// SIGMA[1]  | 14 10  4  8  9 15 13  6  1 12  0  2 11  7  5  3 |
 34// SIGMA[2]  | 11  8 12  0  5  2 15 13 10 14  3  6  7  1  9  4 |
 35// SIGMA[3]  |  7  9  3  1 13 12 11 14  2  6  5 10  4  0 15  8 |
 36// SIGMA[4]  |  9  0  5  7  2  4 10 15 14  1 11 12  6  8  3 13 |
 37// SIGMA[5]  |  2 12  6 10  0 11  8  3  4 13  7  5 15 14  1  9 |
 38// SIGMA[6]  | 12  5  1 15 14 13  4 10  0  7  6  3  9  2  8 11 |
 39// SIGMA[7]  | 13 11  7 14 12  1  3  9  5  0 15  4  8  6  2 10 |
 40// SIGMA[8]  |  6 15 14  9 11  3  0  8 12  2 13  7  1  4 10  5 |
 41// SIGMA[9]  | 10  2  8  4  7  6  1  5 15 11  9 14  3 12 13  0 |
 42// ----------+-------------------------------------------------+
 43//
 44
 45const SIGMA: [[usize; 16]; 10] = [
 46    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
 47    [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3],
 48    [11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4],
 49    [7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8],
 50    [9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13],
 51    [2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9],
 52    [12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11],
 53    [13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10],
 54    [6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5],
 55    [10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0],
 56];
 57
 58// 3.1.  Mixing Function G
 59// The G primitive function mixes two input words, "x" and "y", into
 60// four words indexed by "a", "b", "c", and "d" in the working vector
 61// v[0..15].  The full modified vector is returned.  The rotation
 62// constants (R1, R2, R3, R4) are given in Section 2.1.
 63// FUNCTION G( v[0..15], a, b, c, d, x, y )
 64// |
 65// |   v[a] := (v[a] + v[b] + x) mod 2**w
 66// |   v[d] := (v[d] ^ v[a]) >>> R1
 67// |   v[c] := (v[c] + v[d])     mod 2**w
 68// |   v[b] := (v[b] ^ v[c]) >>> R2
 69// |   v[a] := (v[a] + v[b] + y) mod 2**w
 70// |   v[d] := (v[d] ^ v[a]) >>> R3
 71// |   v[c] := (v[c] + v[d])     mod 2**w
 72// |   v[b] := (v[b] ^ v[c]) >>> R4
 73// |
 74// |   RETURN v[0..15]
 75// |
 76// END FUNCTION.
 77//
 78
 79fn mixing_g<ConstraintF: PrimeField, CS: ConstraintSystem<ConstraintF>>(
 80    mut cs: CS,
 81    v: &mut [UInt32],
 82    a: usize,
 83    b: usize,
 84    c: usize,
 85    d: usize,
 86    x: &UInt32,
 87    y: &UInt32,
 88) -> Result<(), SynthesisError> {
 89    v[a] = UInt32::addmany(
 90        cs.ns(|| "mixing step 1"),
 91        &[v[a].clone(), v[b].clone(), x.clone()],
 92    )?;
 93    v[d] = v[d].xor(cs.ns(|| "mixing step 2"), &v[a])?.rotr(R1);
 94    v[c] = UInt32::addmany(cs.ns(|| "mixing step 3"), &[v[c].clone(), v[d].clone()])?;
 95    v[b] = v[b].xor(cs.ns(|| "mixing step 4"), &v[c])?.rotr(R2);
 96    v[a] = UInt32::addmany(
 97        cs.ns(|| "mixing step 5"),
 98        &[v[a].clone(), v[b].clone(), y.clone()],
 99    )?;
100    v[d] = v[d].xor(cs.ns(|| "mixing step 6"), &v[a])?.rotr(R3);
101    v[c] = UInt32::addmany(cs.ns(|| "mixing step 7"), &[v[c].clone(), v[d].clone()])?;
102    v[b] = v[b].xor(cs.ns(|| "mixing step 8"), &v[c])?.rotr(R4);
103
104    Ok(())
105}
106
107// 3.2.  Compression Function F
108// Compression function F takes as an argument the state vector "h",
109// message block vector "m" (last block is padded with zeros to full
110// block size, if required), 2w-bit_gadget offset counter "t", and final block
111// indicator flag "f".  Local vector v[0..15] is used in processing.  F
112// returns a new state vector.  The number of rounds, "r", is 12 for
113// BLAKE2b and 10 for BLAKE2s.  Rounds are numbered from 0 to r - 1.
114// FUNCTION F( h[0..7], m[0..15], t, f )
115// |
116// |      // Initialize local work vector v[0..15]
117// |      v[0..7] := h[0..7]              // First half from state.
118// |      v[8..15] := IV[0..7]            // Second half from IV.
119// |
120// |      v[12] := v[12] ^ (t mod 2**w)   // Low word of the offset.
121// |      v[13] := v[13] ^ (t >> w)       // High word.
122// |
123// |      IF f = TRUE THEN                // last block flag?
124// |      |   v[14] := v[14] ^ 0xFF..FF   // Invert all bits.
125// |      END IF.
126// |
127// |      // Cryptographic mixing
128// |      FOR i = 0 TO r - 1 DO           // Ten or twelve rounds.
129// |      |
130// |      |   // Message word selection permutation for this round.
131// |      |   s[0..15] := SIGMA[i mod 10][0..15]
132// |      |
133// |      |   v := G( v, 0, 4,  8, 12, m[s[ 0]], m[s[ 1]] )
134// |      |   v := G( v, 1, 5,  9, 13, m[s[ 2]], m[s[ 3]] )
135// |      |   v := G( v, 2, 6, 10, 14, m[s[ 4]], m[s[ 5]] )
136// |      |   v := G( v, 3, 7, 11, 15, m[s[ 6]], m[s[ 7]] )
137// |      |
138// |      |   v := G( v, 0, 5, 10, 15, m[s[ 8]], m[s[ 9]] )
139// |      |   v := G( v, 1, 6, 11, 12, m[s[10]], m[s[11]] )
140// |      |   v := G( v, 2, 7,  8, 13, m[s[12]], m[s[13]] )
141// |      |   v := G( v, 3, 4,  9, 14, m[s[14]], m[s[15]] )
142// |      |
143// |      END FOR
144// |
145// |      FOR i = 0 TO 7 DO               // XOR the two halves.
146// |      |   h[i] := h[i] ^ v[i] ^ v[i + 8]
147// |      END FOR.
148// |
149// |      RETURN h[0..7]                  // New state.
150// |
151// END FUNCTION.
152//
153
154fn blake2s_compression<ConstraintF: PrimeField, CS: ConstraintSystem<ConstraintF>>(
155    mut cs: CS,
156    h: &mut [UInt32],
157    m: &[UInt32],
158    t: u64,
159    f: bool,
160) -> Result<(), SynthesisError> {
161    assert_eq!(h.len(), 8);
162    assert_eq!(m.len(), 16);
163
164    // static const uint32_t blake2s_iv[8] =
165    // {
166    // 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A,
167    // 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19
168    // };
169    //
170
171    let mut v = Vec::with_capacity(16);
172    v.extend_from_slice(h);
173    v.push(UInt32::constant(0x6A09E667));
174    v.push(UInt32::constant(0xBB67AE85));
175    v.push(UInt32::constant(0x3C6EF372));
176    v.push(UInt32::constant(0xA54FF53A));
177    v.push(UInt32::constant(0x510E527F));
178    v.push(UInt32::constant(0x9B05688C));
179    v.push(UInt32::constant(0x1F83D9AB));
180    v.push(UInt32::constant(0x5BE0CD19));
181
182    assert_eq!(v.len(), 16);
183
184    v[12] = v[12].xor(cs.ns(|| "first xor"), &UInt32::constant(t as u32))?;
185    v[13] = v[13].xor(cs.ns(|| "second xor"), &UInt32::constant((t >> 32) as u32))?;
186
187    if f {
188        v[14] = v[14].xor(cs.ns(|| "third xor"), &UInt32::constant(u32::max_value()))?;
189    }
190
191    for i in 0..10 {
192        let mut cs = cs.ns(|| format!("round {}", i));
193
194        let s = SIGMA[i % 10];
195
196        mixing_g(
197            cs.ns(|| "mixing invocation 1"),
198            &mut v,
199            0,
200            4,
201            8,
202            12,
203            &m[s[0]],
204            &m[s[1]],
205        )?;
206        mixing_g(
207            cs.ns(|| "mixing invocation 2"),
208            &mut v,
209            1,
210            5,
211            9,
212            13,
213            &m[s[2]],
214            &m[s[3]],
215        )?;
216        mixing_g(
217            cs.ns(|| "mixing invocation 3"),
218            &mut v,
219            2,
220            6,
221            10,
222            14,
223            &m[s[4]],
224            &m[s[5]],
225        )?;
226        mixing_g(
227            cs.ns(|| "mixing invocation 4"),
228            &mut v,
229            3,
230            7,
231            11,
232            15,
233            &m[s[6]],
234            &m[s[7]],
235        )?;
236
237        mixing_g(
238            cs.ns(|| "mixing invocation 5"),
239            &mut v,
240            0,
241            5,
242            10,
243            15,
244            &m[s[8]],
245            &m[s[9]],
246        )?;
247        mixing_g(
248            cs.ns(|| "mixing invocation 6"),
249            &mut v,
250            1,
251            6,
252            11,
253            12,
254            &m[s[10]],
255            &m[s[11]],
256        )?;
257        mixing_g(
258            cs.ns(|| "mixing invocation 7"),
259            &mut v,
260            2,
261            7,
262            8,
263            13,
264            &m[s[12]],
265            &m[s[13]],
266        )?;
267        mixing_g(
268            cs.ns(|| "mixing invocation 8"),
269            &mut v,
270            3,
271            4,
272            9,
273            14,
274            &m[s[14]],
275            &m[s[15]],
276        )?;
277    }
278
279    for i in 0..8 {
280        let mut cs = cs.ns(|| format!("h[{i}] ^ v[{i}] ^ v[{i} + 8]", i = i));
281
282        h[i] = h[i].xor(cs.ns(|| "first xor"), &v[i])?;
283        h[i] = h[i].xor(cs.ns(|| "second xor"), &v[i + 8])?;
284    }
285
286    Ok(())
287}
288
289// FUNCTION BLAKE2( d[0..dd-1], ll, kk, nn )
290// |
291// |     h[0..7] := IV[0..7]          // Initialization Vector.
292// |
293// |     // Parameter block p[0]
294// |     h[0] := h[0] ^ 0x01010000 ^ (kk << 8) ^ nn
295// |
296// |     // Process padded key and data blocks
297// |     IF dd > 1 THEN
298// |     |       FOR i = 0 TO dd - 2 DO
299// |     |       |       h := F( h, d[i], (i + 1) * bb, FALSE )
300// |     |       END FOR.
301// |     END IF.
302// |
303// |     // Final block.
304// |     IF kk = 0 THEN
305// |     |       h := F( h, d[dd - 1], ll, TRUE )
306// |     ELSE
307// |     |       h := F( h, d[dd - 1], ll + bb, TRUE )
308// |     END IF.
309// |
310// |     RETURN first "nn" bytes from little-endian word array h[].
311// |
312// END FUNCTION.
313//
314
315pub fn blake2s_gadget<ConstraintF: PrimeField, CS: ConstraintSystem<ConstraintF>>(
316    cs: CS,
317    input: &[Boolean],
318) -> Result<Vec<UInt32>, SynthesisError> {
319    assert!(input.len() % 8 == 0);
320    let mut parameters = [0; 8];
321    parameters[0] = 0x01010000 ^ 32;
322    blake2s_gadget_with_parameters(cs, input, &parameters)
323}
324
325pub fn blake2s_gadget_with_parameters<
326    ConstraintF: PrimeField,
327    CS: ConstraintSystem<ConstraintF>,
328>(
329    mut cs: CS,
330    input: &[Boolean],
331    parameters: &[u32; 8],
332) -> Result<Vec<UInt32>, SynthesisError> {
333    assert!(input.len() % 8 == 0);
334
335    let mut h = Vec::with_capacity(8);
336    h.push(
337        UInt32::constant(0x6A09E667).xor(cs.ns(|| "xor h[0]"), &UInt32::constant(parameters[0]))?,
338    );
339    h.push(
340        UInt32::constant(0xBB67AE85).xor(cs.ns(|| "xor h[1]"), &UInt32::constant(parameters[1]))?,
341    );
342    h.push(
343        UInt32::constant(0x3C6EF372).xor(cs.ns(|| "xor h[2]"), &UInt32::constant(parameters[2]))?,
344    );
345    h.push(
346        UInt32::constant(0xA54FF53A).xor(cs.ns(|| "xor h[3]"), &UInt32::constant(parameters[3]))?,
347    );
348    h.push(
349        UInt32::constant(0x510E527F).xor(cs.ns(|| "xor h[4]"), &UInt32::constant(parameters[4]))?,
350    );
351    h.push(
352        UInt32::constant(0x9B05688C).xor(cs.ns(|| "xor h[5]"), &UInt32::constant(parameters[5]))?,
353    );
354    h.push(
355        UInt32::constant(0x1F83D9AB).xor(cs.ns(|| "xor h[6]"), &UInt32::constant(parameters[6]))?,
356    );
357    h.push(
358        UInt32::constant(0x5BE0CD19).xor(cs.ns(|| "xor h[7]"), &UInt32::constant(parameters[7]))?,
359    );
360
361    let mut blocks: Vec<Vec<UInt32>> = vec![];
362
363    for block in input.chunks(512) {
364        let mut this_block = Vec::with_capacity(16);
365        for word in block.chunks(32) {
366            let mut tmp = word.to_vec();
367            while tmp.len() < 32 {
368                tmp.push(Boolean::constant(false));
369            }
370            this_block.push(UInt32::from_bits_le(&tmp));
371        }
372        while this_block.len() < 16 {
373            this_block.push(UInt32::constant(0));
374        }
375        blocks.push(this_block);
376    }
377
378    if blocks.is_empty() {
379        blocks.push((0..16).map(|_| UInt32::constant(0)).collect());
380    }
381
382    for (i, block) in blocks[0..blocks.len() - 1].iter().enumerate() {
383        let cs = cs.ns(|| format!("block {}", i));
384
385        blake2s_compression(cs, &mut h, block, ((i as u64) + 1) * 64, false)?;
386    }
387
388    {
389        let cs = cs.ns(|| "final block");
390
391        blake2s_compression(
392            cs,
393            &mut h,
394            &blocks[blocks.len() - 1],
395            (input.len() / 8) as u64,
396            true,
397        )?;
398    }
399
400    Ok(h)
401}
402
403use crate::prf::Blake2s;
404
405pub struct Blake2sGadget;
406#[derive(Clone, Debug)]
407pub struct Blake2sOutputGadget(pub Vec<UInt8>);
408
409impl PartialEq for Blake2sOutputGadget {
410    fn eq(&self, other: &Self) -> bool {
411        self.0 == other.0
412    }
413}
414
415impl Eq for Blake2sOutputGadget {}
416
417impl<ConstraintF: PrimeField> EqGadget<ConstraintF> for Blake2sOutputGadget {}
418
419impl<ConstraintF: PrimeField> ConditionalEqGadget<ConstraintF> for Blake2sOutputGadget {
420    #[inline]
421    fn conditional_enforce_equal<CS: ConstraintSystem<ConstraintF>>(
422        &self,
423        mut cs: CS,
424        other: &Self,
425        condition: &Boolean,
426    ) -> Result<(), SynthesisError> {
427        for (i, (a, b)) in self.0.iter().zip(other.0.iter()).enumerate() {
428            a.conditional_enforce_equal(
429                &mut cs.ns(|| format!("blake2s_equal_{}", i)),
430                b,
431                condition,
432            )?;
433        }
434        Ok(())
435    }
436
437    fn cost() -> usize {
438        32 * <UInt8 as ConditionalEqGadget<ConstraintF>>::cost()
439    }
440}
441
442impl<ConstraintF: PrimeField> ToBytesGadget<ConstraintF> for Blake2sOutputGadget {
443    #[inline]
444    fn to_bytes<CS: ConstraintSystem<ConstraintF>>(
445        &self,
446        _cs: CS,
447    ) -> Result<Vec<UInt8>, SynthesisError> {
448        Ok(self.0.clone())
449    }
450}
451
452impl<ConstraintF: PrimeField> AllocGadget<[u8; 32], ConstraintF> for Blake2sOutputGadget {
453    #[inline]
454    fn alloc_constant<T, CS: ConstraintSystem<ConstraintF>>(
455        mut cs: CS,
456        val: T,
457    ) -> Result<Self, SynthesisError>
458    where
459        T: Borrow<[u8; 32]>,
460    {
461        let mut bytes = vec![];
462        for (i, b) in val.borrow().iter().enumerate() {
463            bytes.push(UInt8::alloc_constant(cs.ns(|| format!("value {}", i)), b)?)
464        }
465
466        Ok(Blake2sOutputGadget(bytes))
467    }
468
469    #[inline]
470    fn alloc<F, T, CS: ConstraintSystem<ConstraintF>>(
471        cs: CS,
472        value_gen: F,
473    ) -> Result<Self, SynthesisError>
474    where
475        F: FnOnce() -> Result<T, SynthesisError>,
476        T: Borrow<[u8; 32]>,
477    {
478        let zeros = [0u8; 32];
479        let value = match value_gen() {
480            Ok(val) => *(val.borrow()),
481            Err(_) => zeros,
482        };
483        let bytes = <UInt8>::alloc_vec(cs, &value)?;
484
485        Ok(Blake2sOutputGadget(bytes))
486    }
487
488    #[inline]
489    fn alloc_input<F, T, CS: ConstraintSystem<ConstraintF>>(
490        cs: CS,
491        value_gen: F,
492    ) -> Result<Self, SynthesisError>
493    where
494        F: FnOnce() -> Result<T, SynthesisError>,
495        T: Borrow<[u8; 32]>,
496    {
497        let zeros = [0u8; 32];
498        let value = match value_gen() {
499            Ok(val) => *(val.borrow()),
500            Err(_) => zeros,
501        };
502        let bytes = <UInt8>::alloc_input_vec(cs, &value)?;
503
504        Ok(Blake2sOutputGadget(bytes))
505    }
506}
507
508impl<ConstraintF: PrimeField> PRFGadget<Blake2s, ConstraintF> for Blake2sGadget {
509    type OutputGadget = Blake2sOutputGadget;
510
511    fn new_seed<CS: ConstraintSystem<ConstraintF>>(mut cs: CS, seed: &[u8; 32]) -> Vec<UInt8> {
512        UInt8::alloc_vec(&mut cs.ns(|| "alloc_seed"), seed).unwrap()
513    }
514
515    fn check_evaluation_gadget<CS: ConstraintSystem<ConstraintF>>(
516        mut cs: CS,
517        seed: &[UInt8],
518        input: &[UInt8],
519    ) -> Result<Self::OutputGadget, SynthesisError> {
520        assert_eq!(seed.len(), 32);
521        // assert_eq!(input.len(), 32);
522        let mut gadget_input = Vec::with_capacity(512);
523        for byte in seed.iter().chain(input) {
524            gadget_input.extend_from_slice(&byte.into_bits_le());
525        }
526        let mut result = Vec::new();
527        for (i, int) in blake2s_gadget(cs.ns(|| "Blake2s Eval"), &gadget_input)?
528            .into_iter()
529            .enumerate()
530        {
531            let chunk = int.to_bytes(&mut cs.ns(|| format!("Result ToBytes {}", i)))?;
532            result.extend_from_slice(&chunk);
533        }
534        Ok(Blake2sOutputGadget(result))
535    }
536}
537
538#[cfg(test)]
539mod test {
540    use algebra::ed_on_bls12_381::Fq as Fr;
541    use rand::{Rng, SeedableRng};
542    use rand_xorshift::XorShiftRng;
543
544    use crate::prf::blake2s::{constraints::blake2s_gadget, Blake2s as B2SPRF};
545    use blake2::VarBlake2s;
546    use r1cs_core::ConstraintSystem;
547
548    use super::Blake2sGadget;
549    use r1cs_std::{
550        boolean::AllocatedBit, prelude::*, test_constraint_system::TestConstraintSystem,
551    };
552
553    #[test]
554    fn test_blake2s_constraints() {
555        let mut cs = TestConstraintSystem::<Fr>::new();
556        let input_bits: Vec<_> = (0..512)
557            .map(|i| {
558                AllocatedBit::alloc(cs.ns(|| format!("input bit_gadget {}", i)), || Ok(true))
559                    .unwrap()
560                    .into()
561            })
562            .collect();
563        blake2s_gadget(&mut cs, &input_bits).unwrap();
564        assert!(cs.is_satisfied());
565        assert_eq!(cs.num_constraints(), 21792);
566    }
567
568    #[test]
569    fn test_blake2s_prf() {
570        use crate::prf::{PRFGadget, PRF};
571        use rand::Rng;
572
573        let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
574        let mut cs = TestConstraintSystem::<Fr>::new();
575
576        let mut seed = [0u8; 32];
577        rng.fill(&mut seed);
578
579        let mut input = [0u8; 32];
580        rng.fill(&mut input);
581
582        let seed_gadget = Blake2sGadget::new_seed(&mut cs.ns(|| "declare_seed"), &seed);
583        let input_gadget = UInt8::alloc_vec(&mut cs.ns(|| "declare_input"), &input).unwrap();
584        let out = B2SPRF::evaluate(&seed, &input).unwrap();
585        let actual_out_gadget = <Blake2sGadget as PRFGadget<_, Fr>>::OutputGadget::alloc(
586            &mut cs.ns(|| "declare_output"),
587            || Ok(out),
588        )
589        .unwrap();
590
591        let output_gadget = Blake2sGadget::check_evaluation_gadget(
592            &mut cs.ns(|| "eval_blake2s"),
593            &seed_gadget,
594            &input_gadget,
595        )
596        .unwrap();
597        output_gadget
598            .enforce_equal(&mut cs, &actual_out_gadget)
599            .unwrap();
600
601        if !cs.is_satisfied() {
602            println!(
603                "which is unsatisfied: {:?}",
604                cs.which_is_unsatisfied().unwrap()
605            );
606        }
607        assert!(cs.is_satisfied());
608    }
609
610    #[test]
611    fn test_blake2s_precomp_constraints() {
612        // Test that 512 fixed leading bits (constants)
613        // doesn't result in more constraints.
614
615        let mut cs = TestConstraintSystem::<Fr>::new();
616        let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
617        let input_bits: Vec<_> = (0..512)
618            .map(|_| Boolean::constant(rng.gen()))
619            .chain((0..512).map(|i| {
620                AllocatedBit::alloc(cs.ns(|| format!("input bit_gadget {}", i)), || Ok(true))
621                    .unwrap()
622                    .into()
623            }))
624            .collect();
625        blake2s_gadget(&mut cs, &input_bits).unwrap();
626        assert!(cs.is_satisfied());
627        assert_eq!(cs.num_constraints(), 21792);
628    }
629
630    #[test]
631    fn test_blake2s_constant_constraints() {
632        let mut cs = TestConstraintSystem::<Fr>::new();
633        let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
634        let input_bits: Vec<_> = (0..512).map(|_| Boolean::constant(rng.gen())).collect();
635        blake2s_gadget(&mut cs, &input_bits).unwrap();
636        assert_eq!(cs.num_constraints(), 0);
637    }
638
639    #[test]
640    fn test_blake2s() {
641        let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
642
643        for input_len in (0..32).chain((32..256).filter(|a| a % 8 == 0)) {
644            use digest::*;
645            let mut h = VarBlake2s::new_keyed(&[], 32);
646
647            let data: Vec<u8> = (0..input_len).map(|_| rng.gen()).collect();
648
649            h.input(&data);
650
651            let mut hash_result = Vec::with_capacity(h.output_size());
652            h.variable_result(|res| hash_result.extend_from_slice(res));
653
654            let mut cs = TestConstraintSystem::<Fr>::new();
655
656            let mut input_bits = vec![];
657
658            for (byte_i, input_byte) in data.into_iter().enumerate() {
659                for bit_i in 0..8 {
660                    let cs = cs.ns(|| format!("input bit_gadget {} {}", byte_i, bit_i));
661
662                    input_bits.push(
663                        AllocatedBit::alloc(cs, || Ok((input_byte >> bit_i) & 1u8 == 1u8))
664                            .unwrap()
665                            .into(),
666                    );
667                }
668            }
669
670            let r = blake2s_gadget(&mut cs, &input_bits).unwrap();
671
672            assert!(cs.is_satisfied());
673
674            let mut s = hash_result
675                .iter()
676                .flat_map(|&byte| (0..8).map(move |i| (byte >> i) & 1u8 == 1u8));
677
678            for chunk in r {
679                for b in chunk.to_bits_le() {
680                    match b {
681                        Boolean::Is(b) => {
682                            assert!(s.next().unwrap() == b.get_value().unwrap());
683                        }
684                        Boolean::Not(b) => {
685                            assert!(s.next().unwrap() != b.get_value().unwrap());
686                        }
687                        Boolean::Constant(b) => {
688                            assert!(input_len == 0);
689                            assert!(s.next().unwrap() == b);
690                        }
691                    }
692                }
693            }
694        }
695    }
696}