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, ¶meters)
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}