use crate::bellman::pairing::Engine;
use crate::bellman::pairing::ff::{BitIterator, Field, PrimeField, PrimeFieldRepr};
use crate::bellman::SynthesisError;
use crate::bellman::plonk::better_better_cs::cs::{
ArithmeticTerm, Coefficient, ConstraintSystem, Gate, GateInternal, LinearCombinationOfTerms, MainGate, MainGateTerm, PolynomialInConstraint, PolynomialMultiplicativeTerm, TimeDilation, Variable,
Width4MainGateWithDNext,
};
use crate::plonk::circuit::Assignment;
use super::allocated_num::AllocatedNum;
use super::linear_combination::LinearCombination;
use super::boolean::{AllocatedBit, Boolean};
use super::multieq::MultiEq;
use super::uint32::UInt32;
const R1: usize = 16;
const R2: usize = 12;
const R3: usize = 8;
const R4: usize = 7;
const SIGMA: [[usize; 16]; 10] = [
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
[14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3],
[11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4],
[7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8],
[9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13],
[2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9],
[12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11],
[13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10],
[6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5],
[10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0],
];
fn mixing_g<E: Engine, CS: ConstraintSystem<E>>(cs: &mut CS, v: &mut [UInt32], a: usize, b: usize, c: usize, d: usize, x: &UInt32, y: &UInt32) -> Result<(), SynthesisError> {
v[a] = UInt32::addmany(cs, &[v[a].clone(), v[b].clone(), x.clone()])?;
v[d] = v[d].xor(cs, &v[a])?.rotr(R1);
v[c] = UInt32::addmany(cs, &[v[c].clone(), v[d].clone()])?;
v[b] = v[b].xor(cs, &v[c])?.rotr(R2);
v[a] = UInt32::addmany(cs, &[v[a].clone(), v[b].clone(), y.clone()])?;
v[d] = v[d].xor(cs, &v[a])?.rotr(R3);
v[c] = UInt32::addmany(cs, &[v[c].clone(), v[d].clone()])?;
v[b] = v[b].xor(cs, &v[c])?.rotr(R4);
Ok(())
}
fn blake2s_compression<E: Engine, CS: ConstraintSystem<E>>(cs: &mut CS, h: &mut [UInt32], m: &[UInt32], t: u64, f: bool) -> Result<(), SynthesisError> {
assert_eq!(h.len(), 8);
assert_eq!(m.len(), 16);
let mut v = Vec::with_capacity(16);
v.extend_from_slice(h);
v.push(UInt32::constant(0x6A09E667));
v.push(UInt32::constant(0xBB67AE85));
v.push(UInt32::constant(0x3C6EF372));
v.push(UInt32::constant(0xA54FF53A));
v.push(UInt32::constant(0x510E527F));
v.push(UInt32::constant(0x9B05688C));
v.push(UInt32::constant(0x1F83D9AB));
v.push(UInt32::constant(0x5BE0CD19));
assert_eq!(v.len(), 16);
v[12] = v[12].xor(cs, &UInt32::constant(t as u32))?;
v[13] = v[13].xor(cs, &UInt32::constant((t >> 32) as u32))?;
if f {
v[14] = v[14].xor(cs, &UInt32::constant(u32::max_value()))?;
}
{
let mut cs = MultiEq::new(&mut *cs);
for i in 0..10 {
let cs = cs.as_cs();
let s = SIGMA[i % 10];
mixing_g(cs, &mut v, 0, 4, 8, 12, &m[s[0]], &m[s[1]])?;
mixing_g(cs, &mut v, 1, 5, 9, 13, &m[s[2]], &m[s[3]])?;
mixing_g(cs, &mut v, 2, 6, 10, 14, &m[s[4]], &m[s[5]])?;
mixing_g(cs, &mut v, 3, 7, 11, 15, &m[s[6]], &m[s[7]])?;
mixing_g(cs, &mut v, 0, 5, 10, 15, &m[s[8]], &m[s[9]])?;
mixing_g(cs, &mut v, 1, 6, 11, 12, &m[s[10]], &m[s[11]])?;
mixing_g(cs, &mut v, 2, 7, 8, 13, &m[s[12]], &m[s[13]])?;
mixing_g(cs, &mut v, 3, 4, 9, 14, &m[s[14]], &m[s[15]])?;
}
}
for i in 0..8 {
h[i] = h[i].xor(cs, &v[i])?;
h[i] = h[i].xor(cs, &v[i + 8])?;
}
Ok(())
}
pub fn blake2s<E: Engine, CS: ConstraintSystem<E>>(cs: &mut CS, input: &[Boolean], personalization: &[u8]) -> Result<Vec<Boolean>, SynthesisError> {
use byteorder::{ByteOrder, LittleEndian};
assert_eq!(personalization.len(), 8);
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 ^ LittleEndian::read_u32(&personalization[0..4])));
h.push(UInt32::constant(0x5BE0CD19 ^ LittleEndian::read_u32(&personalization[4..8])));
let mut blocks: Vec<Vec<UInt32>> = vec![];
for block in input.chunks(512) {
let mut this_block = Vec::with_capacity(16);
for word in block.chunks(32) {
let mut tmp = word.to_vec();
while tmp.len() < 32 {
tmp.push(Boolean::constant(false));
}
this_block.push(UInt32::from_bits(&tmp));
}
while this_block.len() < 16 {
this_block.push(UInt32::constant(0));
}
blocks.push(this_block);
}
if blocks.len() == 0 {
blocks.push((0..16).map(|_| UInt32::constant(0)).collect());
}
for (i, block) in blocks[0..blocks.len() - 1].iter().enumerate() {
blake2s_compression(cs, &mut h, block, ((i as u64) + 1) * 64, false)?;
}
{
blake2s_compression(cs, &mut h, &blocks[blocks.len() - 1], (input.len() / 8) as u64, true)?;
}
Ok(h.iter().flat_map(|b| b.into_bits()).collect())
}
#[cfg(test)]
mod test {
use super::*;
use crate::rand::{Rng, SeedableRng, XorShiftRng};
use bellman::pairing::bn256::{Bn256, Fr};
use bellman::pairing::ff::{Field, PrimeField};
use blake2_rfc::blake2s::Blake2s;
use crate::bellman::plonk::better_better_cs::cs::*;
#[test]
fn test_blank_hash() {
let mut cs = TrivialAssembly::<Bn256, PlonkCsWidth4WithNextStepParams, Width4MainGateWithDNext>::new();
let input_bits = vec![];
let out = blake2s(&mut cs, &input_bits, b"12345678").unwrap();
assert!(cs.is_satisfied());
assert_eq!(cs.n(), 0);
}
#[test]
#[ignore] fn test_blake2s_constraints() {
let mut cs = TrivialAssembly::<Bn256, PlonkCsWidth4WithNextStepParams, Width4MainGateWithDNext>::new();
let input_bits: Vec<_> = (0..512).map(|_i| AllocatedBit::alloc(&mut cs, Some(true)).unwrap().into()).collect();
blake2s(&mut cs, &input_bits, b"12345678").unwrap();
assert!(cs.is_satisfied());
assert_eq!(cs.n(), 21518);
}
#[test]
fn test_blake2s_precomp_constraints() {
let mut cs = TrivialAssembly::<Bn256, PlonkCsWidth4WithNextStepParams, Width4MainGateWithDNext>::new();
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let input_bits: Vec<_> = (0..512)
.map(|_| Boolean::constant(rng.gen()))
.chain((0..512).map(|_i| AllocatedBit::alloc(&mut cs, Some(true)).unwrap().into()))
.collect();
blake2s(&mut cs, &input_bits, b"12345678").unwrap();
assert!(cs.is_satisfied());
assert_eq!(cs.n(), 33348);
}
#[test]
fn test_blake2s_constant_constraints() {
let mut cs = TrivialAssembly::<Bn256, PlonkCsWidth4WithNextStepParams, Width4MainGateWithDNext>::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, b"12345678").unwrap();
assert_eq!(cs.n(), 0);
}
#[test]
#[ignore] fn test_blake2s() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
for input_len in (0..32).chain((32..256).filter(|a| a % 8 == 0)) {
let mut h = Blake2s::with_params(32, &[], &[], b"12345678");
let data: Vec<u8> = (0..input_len).map(|_| rng.gen()).collect();
h.update(&data);
let hash_result = h.finalize();
let mut cs = TrivialAssembly::<Bn256, PlonkCsWidth4WithNextStepParams, Width4MainGateWithDNext>::new();
let mut input_bits = vec![];
for (_byte_i, input_byte) in data.into_iter().enumerate() {
for bit_i in 0..8 {
input_bits.push(AllocatedBit::alloc(&mut cs, Some((input_byte >> bit_i) & 1u8 == 1u8)).unwrap().into());
}
}
let r = blake2s(&mut cs, &input_bits, b"12345678").unwrap();
assert!(cs.is_satisfied());
let mut s = hash_result.as_ref().iter().flat_map(|&byte| (0..8).map(move |i| (byte >> i) & 1u8 == 1u8));
for b in r {
match b {
Boolean::Is(b) => {
assert!(s.next().unwrap() == b.get_value().unwrap());
}
Boolean::Not(b) => {
assert!(s.next().unwrap() != b.get_value().unwrap());
}
Boolean::Constant(b) => {
assert!(input_len == 0);
assert!(s.next().unwrap() == b);
}
}
}
}
}
}