Implementation of SHA256 choice/majority for UInt32.

This commit is contained in:
Sean Bowe 2018-03-15 13:05:51 -06:00
parent 36a6b5fd90
commit 75c5269d3b
No known key found for this signature in database
GPG Key ID: 95684257D8F8B031
1 changed files with 161 additions and 0 deletions

View File

@ -199,6 +199,85 @@ impl UInt32 {
}
}
fn triop<E, CS, F, U>(
mut cs: CS,
a: &Self,
b: &Self,
c: &Self,
tri_fn: F,
circuit_fn: U
) -> Result<Self, SynthesisError>
where E: Engine,
CS: ConstraintSystem<E>,
F: Fn(u32, u32, u32) -> u32,
U: Fn(&mut CS, usize, &Boolean, &Boolean, &Boolean) -> Result<Boolean, SynthesisError>
{
let new_value = match (a.value, b.value, c.value) {
(Some(a), Some(b), Some(c)) => {
Some(tri_fn(a, b, c))
},
_ => None
};
let bits = a.bits.iter()
.zip(b.bits.iter())
.zip(c.bits.iter())
.enumerate()
.map(|(i, ((a, b), c))| circuit_fn(&mut cs, i, a, b, c))
.collect::<Result<_, _>>()?;
Ok(UInt32 {
bits: bits,
value: new_value
})
}
/// Compute the `maj` value (a and b) xor (a and c) xor (b and c)
/// during SHA256.
pub fn sha256_maj<E, CS>(
cs: CS,
a: &Self,
b: &Self,
c: &Self
) -> Result<Self, SynthesisError>
where E: Engine,
CS: ConstraintSystem<E>
{
Self::triop(cs, a, b, c, |a, b, c| (a & b) ^ (a & c) ^ (b & c),
|cs, i, a, b, c| {
Boolean::sha256_maj(
cs.namespace(|| format!("maj {}", i)),
a,
b,
c
)
}
)
}
/// Compute the `ch` value `(a and b) xor ((not a) and c)`
/// during SHA256.
pub fn sha256_ch<E, CS>(
cs: CS,
a: &Self,
b: &Self,
c: &Self
) -> Result<Self, SynthesisError>
where E: Engine,
CS: ConstraintSystem<E>
{
Self::triop(cs, a, b, c, |a, b, c| (a & b) ^ ((!a) & c),
|cs, i, a, b, c| {
Boolean::sha256_ch(
cs.namespace(|| format!("ch {}", i)),
a,
b,
c
)
}
)
}
/// XOR this `UInt32` with another `UInt32`
pub fn xor<E, CS>(
&self,
@ -597,4 +676,86 @@ mod test {
}
}
}
#[test]
fn test_uint32_sha256_maj() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0653]);
for _ in 0..1000 {
let mut cs = TestConstraintSystem::<Bls12>::new();
let a: u32 = rng.gen();
let b: u32 = rng.gen();
let c: u32 = rng.gen();
let mut expected = (a & b) ^ (a & c) ^ (b & c);
let a_bit = UInt32::alloc(cs.namespace(|| "a_bit"), Some(a)).unwrap();
let b_bit = UInt32::constant(b);
let c_bit = UInt32::alloc(cs.namespace(|| "c_bit"), Some(c)).unwrap();
let r = UInt32::sha256_maj(&mut cs, &a_bit, &b_bit, &c_bit).unwrap();
assert!(cs.is_satisfied());
assert!(r.value == Some(expected));
for b in r.bits.iter() {
match b {
&Boolean::Is(ref b) => {
assert!(b.get_value().unwrap() == (expected & 1 == 1));
},
&Boolean::Not(ref b) => {
assert!(!b.get_value().unwrap() == (expected & 1 == 1));
},
&Boolean::Constant(b) => {
assert!(b == (expected & 1 == 1));
}
}
expected >>= 1;
}
}
}
#[test]
fn test_uint32_sha256_ch() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0653]);
for _ in 0..1000 {
let mut cs = TestConstraintSystem::<Bls12>::new();
let a: u32 = rng.gen();
let b: u32 = rng.gen();
let c: u32 = rng.gen();
let mut expected = (a & b) ^ ((!a) & c);
let a_bit = UInt32::alloc(cs.namespace(|| "a_bit"), Some(a)).unwrap();
let b_bit = UInt32::constant(b);
let c_bit = UInt32::alloc(cs.namespace(|| "c_bit"), Some(c)).unwrap();
let r = UInt32::sha256_ch(&mut cs, &a_bit, &b_bit, &c_bit).unwrap();
assert!(cs.is_satisfied());
assert!(r.value == Some(expected));
for b in r.bits.iter() {
match b {
&Boolean::Is(ref b) => {
assert!(b.get_value().unwrap() == (expected & 1 == 1));
},
&Boolean::Not(ref b) => {
assert!(!b.get_value().unwrap() == (expected & 1 == 1));
},
&Boolean::Constant(b) => {
assert!(b == (expected & 1 == 1));
}
}
expected >>= 1;
}
}
}
}