#![allow(clippy::many_single_char_names)]
use ff::PrimeField;
use super::{boolean::Boolean, multieq::MultiEq, uint32::UInt32};
use crate::frontend::{ConstraintSystem, SynthesisError};
#[allow(clippy::unreadable_literal)]
const ROUND_CONSTANTS: [u32; 64] = [
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
];
#[allow(clippy::unreadable_literal)]
const IV: [u32; 8] = [
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
];
pub fn sha256<Scalar, CS>(mut cs: CS, input: &[Boolean]) -> Result<Vec<Boolean>, SynthesisError>
where
Scalar: PrimeField,
CS: ConstraintSystem<Scalar>,
{
assert!(input.len() % 8 == 0);
let mut padded = input.to_vec();
let plen = padded.len() as u64;
padded.push(Boolean::constant(true));
while (padded.len() + 64) % 512 != 0 {
padded.push(Boolean::constant(false));
}
for b in (0..64).rev().map(|i| (plen >> i) & 1 == 1) {
padded.push(Boolean::constant(b));
}
assert!(padded.len() % 512 == 0);
let mut cur = get_sha256_iv();
for (i, block) in padded.chunks(512).enumerate() {
cur = sha256_compression_function(cs.namespace(|| format!("block {i}")), block, &cur)?;
}
Ok(cur.into_iter().flat_map(|e| e.into_bits_be()).collect())
}
fn get_sha256_iv() -> Vec<UInt32> {
IV.iter().map(|&v| UInt32::constant(v)).collect()
}
pub fn sha256_compression_function<Scalar, CS>(
cs: CS,
input: &[Boolean],
current_hash_value: &[UInt32],
) -> Result<Vec<UInt32>, SynthesisError>
where
Scalar: PrimeField,
CS: ConstraintSystem<Scalar>,
{
assert_eq!(input.len(), 512);
assert_eq!(current_hash_value.len(), 8);
let mut w = input
.chunks(32)
.map(UInt32::from_bits_be)
.collect::<Vec<_>>();
let mut cs = MultiEq::new(cs);
for i in 16..64 {
let cs = &mut cs.namespace(|| format!("w extension {i}"));
let mut s0 = w[i - 15].rotr(7);
s0 = s0.xor(cs.namespace(|| "first xor for s0"), &w[i - 15].rotr(18))?;
s0 = s0.xor(cs.namespace(|| "second xor for s0"), &w[i - 15].shr(3))?;
let mut s1 = w[i - 2].rotr(17);
s1 = s1.xor(cs.namespace(|| "first xor for s1"), &w[i - 2].rotr(19))?;
s1 = s1.xor(cs.namespace(|| "second xor for s1"), &w[i - 2].shr(10))?;
let tmp = UInt32::addmany(
cs.namespace(|| "computation of w[i]"),
&[w[i - 16].clone(), s0, w[i - 7].clone(), s1],
)?;
w.push(tmp);
}
assert_eq!(w.len(), 64);
enum Maybe {
Deferred(Vec<UInt32>),
Concrete(UInt32),
}
impl Maybe {
fn compute<Scalar, CS, M>(self, cs: M, others: &[UInt32]) -> Result<UInt32, SynthesisError>
where
Scalar: PrimeField,
CS: ConstraintSystem<Scalar>,
M: ConstraintSystem<Scalar, Root = MultiEq<Scalar, CS>>,
{
Ok(match self {
Maybe::Concrete(ref v) => return Ok(v.clone()),
Maybe::Deferred(mut v) => {
v.extend(others.iter().cloned());
UInt32::addmany(cs, &v)?
}
})
}
}
let mut a = Maybe::Concrete(current_hash_value[0].clone());
let mut b = current_hash_value[1].clone();
let mut c = current_hash_value[2].clone();
let mut d = current_hash_value[3].clone();
let mut e = Maybe::Concrete(current_hash_value[4].clone());
let mut f = current_hash_value[5].clone();
let mut g = current_hash_value[6].clone();
let mut h = current_hash_value[7].clone();
for i in 0..64 {
let cs = &mut cs.namespace(|| format!("compression round {i}"));
let new_e = e.compute(cs.namespace(|| "deferred e computation"), &[])?;
let mut s1 = new_e.rotr(6);
s1 = s1.xor(cs.namespace(|| "first xor for s1"), &new_e.rotr(11))?;
s1 = s1.xor(cs.namespace(|| "second xor for s1"), &new_e.rotr(25))?;
let ch = UInt32::sha256_ch(cs.namespace(|| "ch"), &new_e, &f, &g)?;
let temp1 = [
h.clone(),
s1,
ch,
UInt32::constant(ROUND_CONSTANTS[i]),
w[i].clone(),
];
let new_a = a.compute(cs.namespace(|| "deferred a computation"), &[])?;
let mut s0 = new_a.rotr(2);
s0 = s0.xor(cs.namespace(|| "first xor for s0"), &new_a.rotr(13))?;
s0 = s0.xor(cs.namespace(|| "second xor for s0"), &new_a.rotr(22))?;
let maj = UInt32::sha256_maj(cs.namespace(|| "maj"), &new_a, &b, &c)?;
let temp2 = [s0, maj];
h = g;
g = f;
f = new_e;
e = Maybe::Deferred(temp1.iter().cloned().chain(Some(d)).collect::<Vec<_>>());
d = c;
c = b;
b = new_a;
a = Maybe::Deferred(
temp1
.iter()
.cloned()
.chain(temp2.iter().cloned())
.collect::<Vec<_>>(),
);
}
let h0 = a.compute(
cs.namespace(|| "deferred h0 computation"),
&[current_hash_value[0].clone()],
)?;
let h1 = UInt32::addmany(
cs.namespace(|| "new h1"),
&[current_hash_value[1].clone(), b],
)?;
let h2 = UInt32::addmany(
cs.namespace(|| "new h2"),
&[current_hash_value[2].clone(), c],
)?;
let h3 = UInt32::addmany(
cs.namespace(|| "new h3"),
&[current_hash_value[3].clone(), d],
)?;
let h4 = e.compute(
cs.namespace(|| "deferred h4 computation"),
&[current_hash_value[4].clone()],
)?;
let h5 = UInt32::addmany(
cs.namespace(|| "new h5"),
&[current_hash_value[5].clone(), f],
)?;
let h6 = UInt32::addmany(
cs.namespace(|| "new h6"),
&[current_hash_value[6].clone(), g],
)?;
let h7 = UInt32::addmany(
cs.namespace(|| "new h7"),
&[current_hash_value[7].clone(), h],
)?;
Ok(vec![h0, h1, h2, h3, h4, h5, h6, h7])
}