From ba8bc539953a6b8fc893fac64731e55351599441 Mon Sep 17 00:00:00 2001 From: Sh0g0-1758 Date: Tue, 12 Mar 2024 00:47:23 +0530 Subject: [PATCH 01/46] Implemented ripemd160 --- crates/ripemd160/Cargo.toml | 18 + crates/ripemd160/src/lib.rs | 2 + crates/ripemd160/src/ripemd160.rs | 455 ++++++++++++++ crates/ripemd160/src/uint32.rs | 945 ++++++++++++++++++++++++++++++ 4 files changed, 1420 insertions(+) create mode 100644 crates/ripemd160/Cargo.toml create mode 100644 crates/ripemd160/src/lib.rs create mode 100644 crates/ripemd160/src/ripemd160.rs create mode 100644 crates/ripemd160/src/uint32.rs diff --git a/crates/ripemd160/Cargo.toml b/crates/ripemd160/Cargo.toml new file mode 100644 index 0000000..6498485 --- /dev/null +++ b/crates/ripemd160/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "ripemd160" +version = "0.1.0" +edition = "2021" +license.workspace = true +homepage.workspace = true +repository.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +block = "0.1.6" +block-buffer = "0.10.4" +digest = "0.10.7" +hex-literal = "0.4.1" +bellpepper-core = { workspace = true } +bellpepper = { workspace = true } +ff = { workspace = true } diff --git a/crates/ripemd160/src/lib.rs b/crates/ripemd160/src/lib.rs new file mode 100644 index 0000000..f6fa341 --- /dev/null +++ b/crates/ripemd160/src/lib.rs @@ -0,0 +1,2 @@ +pub mod ripemd160; +pub mod uint32; \ No newline at end of file diff --git a/crates/ripemd160/src/ripemd160.rs b/crates/ripemd160/src/ripemd160.rs new file mode 100644 index 0000000..55e9851 --- /dev/null +++ b/crates/ripemd160/src/ripemd160.rs @@ -0,0 +1,455 @@ +//! Circuits for the [RIPEMD-160] hash function and its internal compression +//! function. +//! +//! [RIPEMD-160]: https://www.esat.kuleuven.be/cosic/publications/article-317.pdf + +#![allow(clippy::many_single_char_names)] + +use ff::PrimeField; + +use bellpepper::gadgets::boolean::Boolean; +use bellpepper::gadgets::multieq::MultiEq; +use super::uint32::UInt32; +use bellpepper_core::{ConstraintSystem, SynthesisError}; + +#[allow(clippy::unreadable_literal)] +const MD_BUFFERS: [u32; 5] = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0]; + +#[allow(clippy::unreadable_literal)] +const MD_BUFFERS_PRIME: [u32; 5] = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0]; + +#[allow(clippy::unreadable_literal)] +const K_BUFFER: [u32; 5] = [0x00000000, 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xa953fd4e]; + +#[allow(clippy::unreadable_literal)] +const K_BUFFER_PRIME: [u32; 5] = [0x50a28be6, 0x5c4dd124, 0x6d703ef3, 0x7a6d76e9, 0x00000000]; + +pub fn ripemd160( + mut cs: CS, + input: &[Boolean], +) -> Result, bellpepper_core::SynthesisError> +where + Scalar: PrimeField, + CS: ConstraintSystem, +{ + 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_md = get_ripemd160_md("md"); + let mut cur_md_prime = get_ripemd160_md("md_prime"); + for (i, block) in padded.chunks(512).enumerate() { + let prev_md = cur_md.clone(); + cur_md = ripemd160_func_block(cs.namespace(|| format!("block {}", i)), block, &mut cur_md)?; + cur_md_prime = ripemd160_func_block_prime( + cs.namespace(|| format!("block_prime {}", i)), + block, + &mut cur_md_prime, + )?; + let mut update_md = cur_md.clone(); + for i in 0..5 { + update_md[(i+4)%5] = prev_md[i].xor( + cs.namespace(|| format!("first xor {}", i)), + &cur_md[(i+1)%5], + )?; + update_md[(i+4)%5] = update_md[(i+4)%5].xor( + cs.namespace(|| format!("second xor {}", i)), + &cur_md_prime[(i+2)%5], + )?; + } + cur_md = update_md; + cur_md_prime = cur_md.clone(); + } + Ok(cur_md.into_iter().flat_map(|e| e.into_bits_be()).collect()) +} + +fn get_ripemd160_md(input: &str) -> Vec { + match input { + "md" => MD_BUFFERS.iter().map(|&v| UInt32::constant(v)).collect(), + "md_prime" => MD_BUFFERS_PRIME + .iter() + .map(|&v| UInt32::constant(v)) + .collect(), + _ => panic!("Invalid input"), + } +} + +pub fn ripemd160_func_block( + cs: CS, + input: &[Boolean], + current_md_value: &mut[UInt32], +) -> Result, SynthesisError> +where + Scalar: PrimeField, + CS: ConstraintSystem, +{ + assert_eq!(input.len(), 512); + assert_eq!(current_md_value.len(), 5); + let w = input + .chunks(32) + .map(UInt32::from_bits_be) + .collect::>(); + let mut cs = MultiEq::new(cs); + let mut f = current_md_value[1] + .xor(cs.namespace(|| "first xor"), ¤t_md_value[2])? + .xor(cs.namespace(|| "second xor"), ¤t_md_value[3])?; + let mut s_val = [11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8]; + for i in 0..16 { + let mut tmp1 = current_md_value[0] + .xor(cs.namespace(|| format!("first xor {}", i)), &f)? + .xor(cs.namespace(|| format!("second xor {}", i)), &w[i])? + .xor( + cs.namespace(|| format!("third xor {}", i)), + &UInt32::constant(K_BUFFER[0]), + )?; + tmp1 = current_md_value[0].shl(s_val[i]); + tmp1 = current_md_value[0].xor( + cs.namespace(|| format!("fourth xor {}", i)), + ¤t_md_value[4], + )?; + let tmp2 = current_md_value[2].shl(10); + current_md_value[0] = current_md_value[4].clone(); + current_md_value[2] = current_md_value[1].clone(); + current_md_value[4] = current_md_value[3].clone(); + current_md_value[1] = tmp1.clone(); + current_md_value[3] = tmp2.clone(); + } + f = UInt32::ripemd_d1( + cs.namespace(|| "d1"), + ¤t_md_value[3], + ¤t_md_value[1], + ¤t_md_value[2], + )?; + s_val = [7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12]; + let mut i_val = [7,14,13,1,10,6,15,3,12,0,9,5,2,14,11,8]; + for i in 0..16 { + let mut tmp1 = current_md_value[0] + .xor(cs.namespace(|| format!("first xor {}", i)), &f)? + .xor(cs.namespace(|| format!("second xor {}", i)), &w[i_val[i]])? + .xor( + cs.namespace(|| format!("third xor {}", i)), + &UInt32::constant(K_BUFFER[1]), + )?; + tmp1 = current_md_value[0].shl(s_val[i]); + tmp1 = current_md_value[0].xor( + cs.namespace(|| format!("fourth xor {}", i)), + ¤t_md_value[4], + )?; + let tmp2 = current_md_value[2].shl(10); + current_md_value[0] = current_md_value[4].clone(); + current_md_value[2] = current_md_value[1].clone(); + current_md_value[4] = current_md_value[3].clone(); + current_md_value[1] = tmp1.clone(); + current_md_value[3] = tmp2.clone(); + } + f = UInt32::ripemd_d2( + cs.namespace(|| "d2"), + ¤t_md_value[3], + ¤t_md_value[1], + ¤t_md_value[2], + )?; + s_val=[11,13,6,7,14,9,13,15,14,8,13,6,5,12,7,5]; + i_val=[3,10,14,4,9,15,8,1,2,7,0,6,13,11,5,12]; + for i in 0..16 { + let mut tmp1 = current_md_value[0] + .xor(cs.namespace(|| format!("first xor {}", i)), &f)? + .xor(cs.namespace(|| format!("second xor {}", i)), &w[i_val[i]])? + .xor( + cs.namespace(|| format!("third xor {}", i)), + &UInt32::constant(K_BUFFER[2]), + )?; + tmp1 = current_md_value[0].shl(s_val[i]); + tmp1 = current_md_value[0].xor( + cs.namespace(|| format!("fourth xor {}", i)), + ¤t_md_value[4], + )?; + let tmp2 = current_md_value[2].shl(10); + current_md_value[0] = current_md_value[4].clone(); + current_md_value[2] = current_md_value[1].clone(); + current_md_value[4] = current_md_value[3].clone(); + current_md_value[1] = tmp1.clone(); + current_md_value[3] = tmp2.clone(); + } + f = UInt32::ripemd_d1( + cs.namespace(|| "d1"), + ¤t_md_value[1], + ¤t_md_value[3], + ¤t_md_value[2], + )?; + s_val=[11,12,14,15,14,15,9,8,9,14,5,6,8,6,5,12]; + i_val=[1,9,11,10,0,8,12,4,13,3,7,15,14,5,6,2]; + for i in 0..16 { + let mut tmp1 = current_md_value[0] + .xor(cs.namespace(|| format!("first xor {}", i)), &f)? + .xor(cs.namespace(|| format!("second xor {}", i)), &w[i_val[i]])? + .xor( + cs.namespace(|| format!("third xor {}", i)), + &UInt32::constant(K_BUFFER[3]), + )?; + tmp1 = current_md_value[0].shl(s_val[i]); + tmp1 = current_md_value[0].xor( + cs.namespace(|| format!("fourth xor {}", i)), + ¤t_md_value[4], + )?; + let tmp2 = current_md_value[2].shl(10); + current_md_value[0] = current_md_value[4].clone(); + current_md_value[2] = current_md_value[1].clone(); + current_md_value[4] = current_md_value[3].clone(); + current_md_value[1] = tmp1.clone(); + current_md_value[3] = tmp2.clone(); + } + f = UInt32::ripemd_d2( + cs.namespace(|| "d2"), + ¤t_md_value[1], + ¤t_md_value[2], + ¤t_md_value[3], + )?; + s_val=[9,15,5,11,6,8,13,12,5,12,13,14,11,8,5,6]; + i_val=[4,0,5,9,7,12,2,10,14,1,3,8,11,6,15,13]; + for i in 0..16 { + let mut tmp1 = current_md_value[0] + .xor(cs.namespace(|| format!("first xor {}", i)), &f)? + .xor(cs.namespace(|| format!("second xor {}", i)), &w[i_val[i]])? + .xor( + cs.namespace(|| format!("third xor {}", i)), + &UInt32::constant(K_BUFFER[3]), + )?; + tmp1 = current_md_value[0].shl(s_val[i]); + tmp1 = current_md_value[0].xor( + cs.namespace(|| format!("fourth xor {}", i)), + ¤t_md_value[4], + )?; + let tmp2 = current_md_value[2].shl(10); + current_md_value[0] = current_md_value[4].clone(); + current_md_value[2] = current_md_value[1].clone(); + current_md_value[4] = current_md_value[3].clone(); + current_md_value[1] = tmp1.clone(); + current_md_value[3] = tmp2.clone(); + } + Ok(vec![current_md_value[0].clone(),current_md_value[1].clone(),current_md_value[2].clone(),current_md_value[3].clone(),current_md_value[4].clone()]) +} + +pub fn ripemd160_func_block_prime( + cs: CS, + input: &[Boolean], + current_md_value: &mut [UInt32], +) -> Result, SynthesisError> +where + Scalar: PrimeField, + CS: ConstraintSystem, +{ + assert_eq!(input.len(), 512); + assert_eq!(current_md_value.len(), 5); + let mut w = input + .chunks(32) + .map(UInt32::from_bits_be) + .collect::>(); + let mut cs = MultiEq::new(cs); + let mut f = UInt32::ripemd_d2( + cs.namespace(|| "d2"), + ¤t_md_value[1], + ¤t_md_value[2], + ¤t_md_value[3], + )?; + let mut s_val = [8,9,9,11,13,15,15,5,7,7,8,11,14,14,12,6]; + let mut i_val = [5,14,7,0,9,2,11,4,13,6,15,8,1,10,3,12]; + for i in 0..16 { + let mut tmp1 = current_md_value[0] + .xor(cs.namespace(|| format!("first xor {}", i)), &f)? + .xor(cs.namespace(|| format!("second xor {}", i)), &w[i_val[i]])? + .xor( + cs.namespace(|| format!("third xor {}", i)), + &UInt32::constant(K_BUFFER_PRIME[0]), + )?; + tmp1 = current_md_value[0].shl(s_val[i]); + tmp1 = current_md_value[0].xor( + cs.namespace(|| format!("fourth xor {}", i)), + ¤t_md_value[4], + )?; + let tmp2 = current_md_value[2].shl(10); + current_md_value[0] = current_md_value[4].clone(); + current_md_value[2] = current_md_value[1].clone(); + current_md_value[4] = current_md_value[3].clone(); + current_md_value[1] = tmp1.clone(); + current_md_value[3] = tmp2.clone(); + } + f = UInt32::ripemd_d1( + cs.namespace(|| "d1"), + ¤t_md_value[1], + ¤t_md_value[3], + ¤t_md_value[2], + )?; + s_val = [9,13,15,7,12,8,9,11,7,7,12,7,6,15,13,11]; + i_val = [6,11,3,7,0,13,5,10,14,15,8,12,4,9,1,2]; + for i in 0..16 { + let mut tmp1 = current_md_value[0] + .xor(cs.namespace(|| format!("first xor {}", i)), &f)? + .xor(cs.namespace(|| format!("second xor {}", i)), &w[i_val[i]])? + .xor( + cs.namespace(|| format!("third xor {}", i)), + &UInt32::constant(K_BUFFER_PRIME[1]), + )?; + tmp1 = current_md_value[0].shl(s_val[i]); + tmp1 = current_md_value[0].xor( + cs.namespace(|| format!("fourth xor {}", i)), + ¤t_md_value[4], + )?; + let tmp2 = current_md_value[2].shl(10); + current_md_value[0] = current_md_value[4].clone(); + current_md_value[2] = current_md_value[1].clone(); + current_md_value[4] = current_md_value[3].clone(); + current_md_value[1] = tmp1.clone(); + current_md_value[3] = tmp2.clone(); + } + f = UInt32::ripemd_d2( + cs.namespace(|| "d2"), + ¤t_md_value[3], + ¤t_md_value[1], + ¤t_md_value[2], + )?; + s_val = [9,7,15,11,8,6,6,14,12,13,5,14,13,13,7,5]; + i_val = [15,5,1,3,7,14,6,9,11,8,12,2,10,0,4,13]; + for i in 0..16 { + let mut tmp1 = current_md_value[0] + .xor(cs.namespace(|| format!("first xor {}", i)), &f)? + .xor(cs.namespace(|| format!("second xor {}", i)), &w[i_val[i]])? + .xor( + cs.namespace(|| format!("third xor {}", i)), + &UInt32::constant(K_BUFFER_PRIME[2]), + )?; + tmp1 = current_md_value[0].shl(s_val[i]); + tmp1 = current_md_value[0].xor( + cs.namespace(|| format!("fourth xor {}", i)), + ¤t_md_value[4], + )?; + let tmp2 = current_md_value[2].shl(10); + current_md_value[0] = current_md_value[4].clone(); + current_md_value[2] = current_md_value[1].clone(); + current_md_value[4] = current_md_value[3].clone(); + current_md_value[1] = tmp1.clone(); + current_md_value[3] = tmp2.clone(); + } + f = UInt32::ripemd_d1( + cs.namespace(|| "d1"), + ¤t_md_value[2], + ¤t_md_value[1], + ¤t_md_value[3], + )?; + s_val = [15,5,8,11,14,14,6,14,6,9,12,9,12,5,15,8]; + i_val = [8,6,4,1,3,11,15,0,5,12,2,13,9,7,10,14]; + for i in 0..16 { + let mut tmp1 = current_md_value[0] + .xor(cs.namespace(|| format!("first xor {}", i)), &f)? + .xor(cs.namespace(|| format!("second xor {}", i)), &w[i_val[i]])? + .xor( + cs.namespace(|| format!("third xor {}", i)), + &UInt32::constant(K_BUFFER_PRIME[3]), + )?; + tmp1 = current_md_value[0].shl(s_val[i]); + tmp1 = current_md_value[0].xor( + cs.namespace(|| format!("fourth xor {}", i)), + ¤t_md_value[4], + )?; + let tmp2 = current_md_value[2].shl(10); + current_md_value[0] = current_md_value[4].clone(); + current_md_value[2] = current_md_value[1].clone(); + current_md_value[4] = current_md_value[3].clone(); + current_md_value[1] = tmp1.clone(); + current_md_value[3] = tmp2.clone(); + } + f = current_md_value[1] + .xor(cs.namespace(|| "first xor"), ¤t_md_value[2])? + .xor(cs.namespace(|| "second xor"), ¤t_md_value[3])?; + s_val = [8,5,12,9,12,5,14,6,8,13,6,5,15,13,11,11]; + i_val = [12,15,10,4,1,5,8,7,6,2,13,14,0,3,9,11]; + for i in 0..16 { + let mut tmp1 = current_md_value[0] + .xor(cs.namespace(|| format!("first xor {}", i)), &f)? + .xor(cs.namespace(|| format!("second xor {}", i)), &w[i_val[i]])? + .xor( + cs.namespace(|| format!("third xor {}", i)), + &UInt32::constant(K_BUFFER_PRIME[4]), + )?; + tmp1 = current_md_value[0].shl(s_val[i]); + tmp1 = current_md_value[0].xor( + cs.namespace(|| format!("fourth xor {}", i)), + ¤t_md_value[4], + )?; + let tmp2 = current_md_value[2].shl(10); + current_md_value[0] = current_md_value[4].clone(); + current_md_value[2] = current_md_value[1].clone(); + current_md_value[4] = current_md_value[3].clone(); + current_md_value[1] = tmp1.clone(); + current_md_value[3] = tmp2.clone(); + } + Ok(vec![current_md_value[0].clone(),current_md_value[1].clone(),current_md_value[2].clone(),current_md_value[3].clone(),current_md_value[4].clone()]) +} + +#[cfg(test)] +mod test { + use super::*; + use crate::gadgets::boolean::AllocatedBit; + use bellpepper_core::test_cs::*; + use blstrs::Scalar as Fr; + use hex_literal::hex; + use rand_core::{RngCore, SeedableRng}; + use rand_xorshift::XorShiftRng; + + #[test] + #[allow(clippy::needless_collect)] + fn test_blank_hash() { + let mut cur_md = get_ripemd160_md("md"); + let mut cur_md_prime = get_ripemd160_md("md_prime"); + + let mut cs = TestConstraintSystem::::new(); + let mut input_bits: Vec<_> = (0..512).map(|_| Boolean::Constant(false)).collect(); + input_bits[0] = Boolean::Constant(true); + let prev_md = cur_md.clone(); + cur_md = ripemd160_func_block(&mut cs, &input_bits, &mut cur_md).unwrap(); + cur_md_prime = ripemd160_func_block_prime(&mut cs, &input_bits, &mut cur_md_prime).unwrap(); + let mut update_md = cur_md.clone(); + for i in 0..5 { + match prev_md[i].xor(cs.namespace(|| format!("first xor {}", i)), &cur_md[(i+1)%5]) { + Ok(result) => { + update_md[(i+4)%5] = result; + }, + Err(err) => { + // Handle the error here + } + } + match update_md[(i+4)%5].xor(cs.namespace(|| format!("first xor {}", i)), &cur_md[(i+2)%5]) { + Ok(result) => { + update_md[(i+4)%5] = result; + }, + Err(err) => { + // Handle the error here + } + } + } + cur_md = update_md; + cur_md_prime = cur_md.clone(); + // let out_bits : Vec<_> = cur_md.into_iter().flat_map(|e| e.into_bits_be()).collect(); + + assert!(cs.is_satisfied()); + assert_eq!(cs.num_constraints(), 0); + + // let expected = hex!("9c1185a5c5e9fc54612808977ee8f548b2258d31"); + + // let out = out_bits; + // for b in expected.iter() { + // for i in (0..8).rev() { + // let c = out.next().unwrap().get_value().unwrap(); + + // assert_eq!(c, (b >> i) & 1u8 == 1u8); + // } + // } + } +} \ No newline at end of file diff --git a/crates/ripemd160/src/uint32.rs b/crates/ripemd160/src/uint32.rs new file mode 100644 index 0000000..49646d7 --- /dev/null +++ b/crates/ripemd160/src/uint32.rs @@ -0,0 +1,945 @@ +//! Circuit representation of a [`u32`], with helpers for the [`sha256`] +//! gadgets. + +use ff::PrimeField; + +use bellpepper_core::{ConstraintSystem, LinearCombination, SynthesisError}; + +use bellpepper_core::boolean::{AllocatedBit, Boolean}; +use bellpepper::gadgets::multieq::MultiEq; + +/// Represents an interpretation of 32 `Boolean` objects as an +/// unsigned integer. +#[derive(Clone, Debug)] +pub struct UInt32 { + // Least significant bit first + bits: Vec, + value: Option, +} + +impl UInt32 { + /// Construct a constant `UInt32` from a `u32` + pub fn constant(value: u32) -> Self { + let mut bits = Vec::with_capacity(32); + + let mut tmp = value; + for _ in 0..32 { + if tmp & 1 == 1 { + bits.push(Boolean::constant(true)) + } else { + bits.push(Boolean::constant(false)) + } + + tmp >>= 1; + } + + UInt32 { + bits, + value: Some(value), + } + } + + /// Allocate a `UInt32` in the constraint system + pub fn alloc(mut cs: CS, value: Option) -> Result + where + Scalar: PrimeField, + CS: ConstraintSystem, + { + let values = match value { + Some(mut val) => { + let mut v = Vec::with_capacity(32); + + for _ in 0..32 { + v.push(Some(val & 1 == 1)); + val >>= 1; + } + + v + } + None => vec![None; 32], + }; + + let bits = values + .into_iter() + .enumerate() + .map(|(i, v)| { + Ok(Boolean::from(AllocatedBit::alloc( + cs.namespace(|| format!("allocated bit {}", i)), + v, + )?)) + }) + .collect::, SynthesisError>>()?; + + Ok(UInt32 { bits, value }) + } + + pub fn into_bits_be(self) -> Vec { + let mut ret = self.bits; + ret.reverse(); + ret + } + + pub fn from_bits_be(bits: &[Boolean]) -> Self { + assert_eq!(bits.len(), 32); + + let mut value = Some(0u32); + for b in bits { + if let Some(v) = value.as_mut() { + *v <<= 1; + } + + match b.get_value() { + Some(true) => { + if let Some(v) = value.as_mut() { + *v |= 1; + } + } + Some(false) => {} + None => { + value = None; + } + } + } + + UInt32 { + value, + bits: bits.iter().rev().cloned().collect(), + } + } + + /// Turns this `UInt32` into its little-endian byte order representation. + pub fn into_bits(self) -> Vec { + self.bits + } + + /// Converts a little-endian byte order representation of bits into a + /// `UInt32`. + pub fn from_bits(bits: &[Boolean]) -> Self { + assert_eq!(bits.len(), 32); + + let new_bits = bits.to_vec(); + + let mut value = Some(0u32); + for b in new_bits.iter().rev() { + if let Some(v) = value.as_mut() { + *v <<= 1; + } + + match *b { + Boolean::Constant(b) => { + if b { + if let Some(v) = value.as_mut() { + *v |= 1; + } + } + } + Boolean::Is(ref b) => match b.get_value() { + Some(true) => { + if let Some(v) = value.as_mut() { + *v |= 1; + } + } + Some(false) => {} + None => value = None, + }, + Boolean::Not(ref b) => match b.get_value() { + Some(false) => { + if let Some(v) = value.as_mut() { + *v |= 1; + } + } + Some(true) => {} + None => value = None, + }, + } + } + + UInt32 { + value, + bits: new_bits, + } + } + + pub fn rotr(&self, by: usize) -> Self { + let by = by % 32; + + let new_bits = self + .bits + .iter() + .skip(by) + .chain(self.bits.iter()) + .take(32) + .cloned() + .collect(); + + UInt32 { + bits: new_bits, + value: self.value.map(|v| v.rotate_right(by as u32)), + } + } + + pub fn shr(&self, by: usize) -> Self { + let by = by % 32; + + let fill = Boolean::constant(false); + + let new_bits = self + .bits + .iter() // The bits are least significant first + .skip(by) // Skip the bits that will be lost during the shift + .chain(Some(&fill).into_iter().cycle()) // Rest will be zeros + .take(32) // Only 32 bits needed! + .cloned() + .collect(); + + UInt32 { + bits: new_bits, + value: self.value.map(|v| v >> by as u32), + } + } + + pub fn shl(&self, by: usize) -> Self { + let by = by % 32; + + let fill = Boolean::constant(false); + let new_bits: Vec<_> = std::iter::repeat(&fill) + .take(by) + .chain(self.bits.iter()) // The bits are least significant first + .take(32) // Only 32 bits needed! + .cloned() + .collect(); + UInt32 { + bits: new_bits, + value: self.value.map(|v| v << by as u32), + } + } + + fn triop( + mut cs: CS, + a: &Self, + b: &Self, + c: &Self, + tri_fn: F, + circuit_fn: U, + ) -> Result + where + Scalar: PrimeField, + CS: ConstraintSystem, + F: Fn(u32, u32, u32) -> u32, + U: Fn(&mut CS, usize, &Boolean, &Boolean, &Boolean) -> Result, + { + 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::>()?; + + Ok(UInt32 { + 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( + cs: CS, + a: &Self, + b: &Self, + c: &Self, + ) -> Result + where + Scalar: PrimeField, + CS: ConstraintSystem, + { + 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( + cs: CS, + a: &Self, + b: &Self, + c: &Self, + ) -> Result + where + Scalar: PrimeField, + CS: ConstraintSystem, + { + 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), + ) + } + pub fn ripemd_d1( + cs: CS, + a: &Self, + b: &Self, + c: &Self, + ) -> Result + where + Scalar: PrimeField, + CS: ConstraintSystem, + { + Self::triop( + cs, + a, + b, + c, + |a, b, c| (a & b) | (!b & c), + |cs, i, a, b, c| Boolean::ripemd160_d1(cs.namespace(|| format!("d1 {}", i)), a, b, c), + ) + } + + pub fn ripemd_d2( + cs: CS, + a: &Self, + b: &Self, + c: &Self, + ) -> Result + where + Scalar: PrimeField, + CS: ConstraintSystem, + { + Self::triop( + cs, + a, + b, + c, + |a, b, c| (a) ^ (b | !c), + |cs, i, a, b, c| Boolean::ripemd160_d2(cs.namespace(|| format!("d2 {}", i)), a, b, c), + ) + } + /// XOR this `UInt32` with another `UInt32` + pub fn xor(&self, mut cs: CS, other: &Self) -> Result + where + Scalar: PrimeField, + CS: ConstraintSystem, + { + let new_value = match (self.value, other.value) { + (Some(a), Some(b)) => Some(a ^ b), + _ => None, + }; + + let bits = self + .bits + .iter() + .zip(other.bits.iter()) + .enumerate() + .map(|(i, (a, b))| Boolean::xor(cs.namespace(|| format!("xor of bit {}", i)), a, b)) + .collect::>()?; + + Ok(UInt32 { + bits, + value: new_value, + }) + } + + /// Perform modular addition of several `UInt32` objects. + #[allow(clippy::unnecessary_unwrap)] + pub fn addmany(mut cs: M, operands: &[Self]) -> Result + where + Scalar: PrimeField, + CS: ConstraintSystem, + M: ConstraintSystem>, + { + // Make some arbitrary bounds for ourselves to avoid overflows + // in the scalar field + assert!(Scalar::NUM_BITS >= 64); + assert!(operands.len() >= 2); // Weird trivial cases that should never happen + assert!(operands.len() <= 10); + + // Compute the maximum value of the sum so we allocate enough bits for + // the result + let mut max_value = (operands.len() as u64) * (u64::from(u32::max_value())); + + // Keep track of the resulting value + let mut result_value = Some(0u64); + + // This is a linear combination that we will enforce to equal the + // output + let mut lc = LinearCombination::zero(); + + let mut all_constants = true; + + // Iterate over the operands + for op in operands { + // Accumulate the value + match op.value { + Some(val) => { + if let Some(v) = result_value.as_mut() { + *v += u64::from(val); + } + } + None => { + // If any of our operands have unknown value, we won't + // know the value of the result + result_value = None; + } + } + + // Iterate over each bit of the operand and add the operand to + // the linear combination + let mut coeff = Scalar::ONE; + for bit in &op.bits { + lc = lc + &bit.lc(CS::one(), coeff); + + all_constants &= bit.is_constant(); + + coeff = coeff.double(); + } + } + + // The value of the actual result is modulo 2^32 + let modular_value = result_value.map(|v| v as u32); + + if all_constants && modular_value.is_some() { + // We can just return a constant, rather than + // unpacking the result into allocated bits. + + return Ok(UInt32::constant(modular_value.unwrap())); + } + + // Storage area for the resulting bits + let mut result_bits = vec![]; + + // Linear combination representing the output, + // for comparison with the sum of the operands + let mut result_lc = LinearCombination::zero(); + + // Allocate each bit of the result + let mut coeff = Scalar::ONE; + let mut i = 0; + while max_value != 0 { + // Allocate the bit + let b = AllocatedBit::alloc( + cs.namespace(|| format!("result bit {}", i)), + result_value.map(|v| (v >> i) & 1 == 1), + )?; + + // Add this bit to the result combination + result_lc = result_lc + (coeff, b.get_variable()); + + result_bits.push(b.into()); + + max_value >>= 1; + i += 1; + coeff = coeff.double(); + } + + // Enforce equality between the sum and result + cs.get_root().enforce_equal(i, &lc, &result_lc); + + // Discard carry bits that we don't care about + result_bits.truncate(32); + + Ok(UInt32 { + bits: result_bits, + value: modular_value, + }) + } +} + +#[cfg(test)] +mod test { + use super::UInt32; + use crate::gadgets::boolean::Boolean; + use crate::gadgets::multieq::MultiEq; + use bellpepper_core::test_cs::*; + use bellpepper_core::ConstraintSystem; + use blstrs::Scalar as Fr; + use ff::Field; + use rand_core::{RngCore, SeedableRng}; + use rand_xorshift::XorShiftRng; + + #[test] + fn test_uint32_from_bits_be() { + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, + 0xbc, 0xe5, + ]); + + for _ in 0..1000 { + let v = (0..32) + .map(|_| Boolean::constant(rng.next_u32() % 2 != 0)) + .collect::>(); + + let b = UInt32::from_bits_be(&v); + + for (i, bit) in b.bits.iter().enumerate() { + match *bit { + Boolean::Constant(bit) => { + assert!(bit == ((b.value.unwrap() >> i) & 1 == 1)); + } + _ => unreachable!(), + } + } + + let expected_to_be_same = b.into_bits_be(); + + for x in v.iter().zip(expected_to_be_same.iter()) { + match x { + (&Boolean::Constant(true), &Boolean::Constant(true)) => {} + (&Boolean::Constant(false), &Boolean::Constant(false)) => {} + _ => unreachable!(), + } + } + } + } + + #[test] + fn test_uint32_from_bits() { + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, + 0xbc, 0xe5, + ]); + + for _ in 0..1000 { + let v = (0..32) + .map(|_| Boolean::constant(rng.next_u32() % 2 != 0)) + .collect::>(); + + let b = UInt32::from_bits(&v); + + for (i, bit) in b.bits.iter().enumerate() { + match *bit { + Boolean::Constant(bit) => { + assert!(bit == ((b.value.unwrap() >> i) & 1 == 1)); + } + _ => unreachable!(), + } + } + + let expected_to_be_same = b.into_bits(); + + for x in v.iter().zip(expected_to_be_same.iter()) { + match x { + (&Boolean::Constant(true), &Boolean::Constant(true)) => {} + (&Boolean::Constant(false), &Boolean::Constant(false)) => {} + _ => unreachable!(), + } + } + } + } + + #[test] + fn test_uint32_xor() { + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, + 0xbc, 0xe5, + ]); + + for _ in 0..1000 { + let mut cs = TestConstraintSystem::::new(); + + let a = rng.next_u32(); + let b = rng.next_u32(); + let c = rng.next_u32(); + + let mut expected = a ^ 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 = a_bit.xor(cs.namespace(|| "first xor"), &b_bit).unwrap(); + let r = r.xor(cs.namespace(|| "second xor"), &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_eq!(b.get_value().unwrap(), expected & 1 == 1); + } + Boolean::Not(ref b) => { + assert_ne!(b.get_value().unwrap(), expected & 1 == 1); + } + Boolean::Constant(b) => { + assert_eq!(b, expected & 1 == 1); + } + } + + expected >>= 1; + } + } + } + + #[test] + fn test_uint32_addmany_constants() { + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, + 0xbc, 0xe5, + ]); + + for _ in 0..1000 { + let mut cs = TestConstraintSystem::::new(); + + let a = rng.next_u32(); + let b = rng.next_u32(); + let c = rng.next_u32(); + + let a_bit = UInt32::constant(a); + let b_bit = UInt32::constant(b); + let c_bit = UInt32::constant(c); + + let mut expected = a.wrapping_add(b).wrapping_add(c); + + let r = { + let mut cs = MultiEq::new(&mut cs); + let r = + UInt32::addmany(cs.namespace(|| "addition"), &[a_bit, b_bit, c_bit]).unwrap(); + r + }; + + assert!(r.value == Some(expected)); + + for b in r.bits.iter() { + match *b { + Boolean::Is(_) => panic!(), + Boolean::Not(_) => panic!(), + Boolean::Constant(b) => { + assert!(b == (expected & 1 == 1)); + } + } + + expected >>= 1; + } + } + } + + #[test] + #[allow(clippy::many_single_char_names)] + fn test_uint32_addmany() { + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, + 0xbc, 0xe5, + ]); + + for _ in 0..1000 { + let mut cs = TestConstraintSystem::::new(); + + let a = rng.next_u32(); + let b = rng.next_u32(); + let c = rng.next_u32(); + let d = rng.next_u32(); + + let mut expected = (a ^ b).wrapping_add(c).wrapping_add(d); + + let a_bit = UInt32::alloc(cs.namespace(|| "a_bit"), Some(a)).unwrap(); + let b_bit = UInt32::constant(b); + let c_bit = UInt32::constant(c); + let d_bit = UInt32::alloc(cs.namespace(|| "d_bit"), Some(d)).unwrap(); + + let r = a_bit.xor(cs.namespace(|| "xor"), &b_bit).unwrap(); + let r = { + let mut cs = MultiEq::new(&mut cs); + UInt32::addmany(cs.namespace(|| "addition"), &[r, c_bit, d_bit]).unwrap() + }; + + assert!(cs.is_satisfied()); + + assert!(r.value == Some(expected)); + + for b in r.bits.iter() { + match *b { + Boolean::Is(ref b) => { + assert_eq!(b.get_value().unwrap(), expected & 1 == 1); + } + Boolean::Not(ref b) => { + assert_ne!(b.get_value().unwrap(), expected & 1 == 1); + } + Boolean::Constant(_) => unreachable!(), + } + + expected >>= 1; + } + + // Flip a bit and see if the addition constraint still works + if cs.get("addition/result bit 0/boolean").is_zero().into() { + cs.set("addition/result bit 0/boolean", Field::ONE); + } else { + cs.set("addition/result bit 0/boolean", Field::ZERO); + } + + assert!(!cs.is_satisfied()); + } + } + + #[test] + fn test_uint32_rotr() { + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, + 0xbc, 0xe5, + ]); + + let mut num = rng.next_u32(); + + let a = UInt32::constant(num); + + for i in 0..32 { + let b = a.rotr(i); + assert_eq!(a.bits.len(), b.bits.len()); + + assert!(b.value.unwrap() == num); + + let mut tmp = num; + for b in &b.bits { + match *b { + Boolean::Constant(b) => { + assert_eq!(b, tmp & 1 == 1); + } + _ => unreachable!(), + } + + tmp >>= 1; + } + + num = num.rotate_right(1); + } + } + + #[test] + fn test_uint32_shr() { + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, + 0xbc, 0xe5, + ]); + + for _ in 0..50 { + for i in 0..60 { + let num = rng.next_u32(); + let a = UInt32::constant(num).shr(i); + let b = UInt32::constant(num.wrapping_shr(i as u32)); + + assert_eq!(a.value.unwrap(), num.wrapping_shr(i as u32)); + + assert_eq!(a.bits.len(), b.bits.len()); + for (a, b) in a.bits.iter().zip(b.bits.iter()) { + assert_eq!(a.get_value().unwrap(), b.get_value().unwrap()); + } + } + } + } + + #[test] + fn test_uint32_shl() { + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, + 0xbc, 0xe5, + ]); + + for _ in 0..50 { + for i in 0..60 { + let num = rng.next_u32(); + let a = UInt32::constant(num).shl(i); + let b = UInt32::constant(num.wrapping_shl(i as u32)); + + assert_eq!(a.value.unwrap(), num.wrapping_shl(i as u32)); + + assert_eq!(a.bits.len(), b.bits.len()); + for (a, b) in a.bits.iter().zip(b.bits.iter()) { + assert_eq!(a.get_value().unwrap(), b.get_value().unwrap()); + } + } + } + } + + #[test] + fn test_uint32_sha256_maj() { + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, + 0xbc, 0xe5, + ]); + + for _ in 0..1000 { + let mut cs = TestConstraintSystem::::new(); + + let a = rng.next_u32(); + let b = rng.next_u32(); + let c = rng.next_u32(); + + 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_eq!(b.get_value().unwrap(), expected & 1 == 1); + } + Boolean::Not(ref b) => { + assert_ne!(b.get_value().unwrap(), expected & 1 == 1); + } + Boolean::Constant(b) => { + assert_eq!(b, expected & 1 == 1); + } + } + + expected >>= 1; + } + } + } + + #[test] + fn test_uint32_sha256_ch() { + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, + 0xbc, 0xe5, + ]); + + for _ in 0..1000 { + let mut cs = TestConstraintSystem::::new(); + + let a = rng.next_u32(); + let b = rng.next_u32(); + let c = rng.next_u32(); + + 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_eq!(b.get_value().unwrap(), expected & 1 == 1); + } + Boolean::Not(ref b) => { + assert_ne!(b.get_value().unwrap(), expected & 1 == 1); + } + Boolean::Constant(b) => { + assert_eq!(b, expected & 1 == 1); + } + } + + expected >>= 1; + } + } + } + + #[test] + fn test_uint32_ripemd_d1() { + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, + 0xbc, 0xe5, + ]); + + for _ in 0..1000 { + let mut cs = TestConstraintSystem::::new(); + + let a = rng.next_u32(); + let b = rng.next_u32(); + let c = rng.next_u32(); + + let mut expected = (a & b) | ((!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::ripemd_d1(&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_eq!(b.get_value().unwrap(), expected & 1 == 1); + } + Boolean::Not(ref b) => { + assert_ne!(b.get_value().unwrap(), expected & 1 == 1); + } + Boolean::Constant(b) => { + assert_eq!(b, expected & 1 == 1); + } + } + + expected >>= 1; + } + } + } + + #[test] + fn test_uint32_ripemd_d2() { + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, + 0xbc, 0xe5, + ]); + + for _ in 0..1000 { + let mut cs = TestConstraintSystem::::new(); + + let a = rng.next_u32(); + let b = rng.next_u32(); + let c = rng.next_u32(); + + let mut expected = (a) ^ ((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::ripemd_d2(&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_eq!(b.get_value().unwrap(), expected & 1 == 1); + } + Boolean::Not(ref b) => { + assert_ne!(b.get_value().unwrap(), expected & 1 == 1); + } + Boolean::Constant(b) => { + assert_eq!(b, expected & 1 == 1); + } + } + + expected >>= 1; + } + } + } +} From 536887a39e3264433c84e72480deef8f3702fa10 Mon Sep 17 00:00:00 2001 From: Sh0g0-1758 Date: Tue, 12 Mar 2024 00:53:46 +0530 Subject: [PATCH 02/46] Implement ripemd160 --- crates/ripemd160/src/lib.rs | 3 +- crates/ripemd160/src/uint32.rs | 945 --------------------------------- 2 files changed, 1 insertion(+), 947 deletions(-) delete mode 100644 crates/ripemd160/src/uint32.rs diff --git a/crates/ripemd160/src/lib.rs b/crates/ripemd160/src/lib.rs index f6fa341..2764463 100644 --- a/crates/ripemd160/src/lib.rs +++ b/crates/ripemd160/src/lib.rs @@ -1,2 +1 @@ -pub mod ripemd160; -pub mod uint32; \ No newline at end of file +pub mod ripemd160; \ No newline at end of file diff --git a/crates/ripemd160/src/uint32.rs b/crates/ripemd160/src/uint32.rs deleted file mode 100644 index 49646d7..0000000 --- a/crates/ripemd160/src/uint32.rs +++ /dev/null @@ -1,945 +0,0 @@ -//! Circuit representation of a [`u32`], with helpers for the [`sha256`] -//! gadgets. - -use ff::PrimeField; - -use bellpepper_core::{ConstraintSystem, LinearCombination, SynthesisError}; - -use bellpepper_core::boolean::{AllocatedBit, Boolean}; -use bellpepper::gadgets::multieq::MultiEq; - -/// Represents an interpretation of 32 `Boolean` objects as an -/// unsigned integer. -#[derive(Clone, Debug)] -pub struct UInt32 { - // Least significant bit first - bits: Vec, - value: Option, -} - -impl UInt32 { - /// Construct a constant `UInt32` from a `u32` - pub fn constant(value: u32) -> Self { - let mut bits = Vec::with_capacity(32); - - let mut tmp = value; - for _ in 0..32 { - if tmp & 1 == 1 { - bits.push(Boolean::constant(true)) - } else { - bits.push(Boolean::constant(false)) - } - - tmp >>= 1; - } - - UInt32 { - bits, - value: Some(value), - } - } - - /// Allocate a `UInt32` in the constraint system - pub fn alloc(mut cs: CS, value: Option) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - { - let values = match value { - Some(mut val) => { - let mut v = Vec::with_capacity(32); - - for _ in 0..32 { - v.push(Some(val & 1 == 1)); - val >>= 1; - } - - v - } - None => vec![None; 32], - }; - - let bits = values - .into_iter() - .enumerate() - .map(|(i, v)| { - Ok(Boolean::from(AllocatedBit::alloc( - cs.namespace(|| format!("allocated bit {}", i)), - v, - )?)) - }) - .collect::, SynthesisError>>()?; - - Ok(UInt32 { bits, value }) - } - - pub fn into_bits_be(self) -> Vec { - let mut ret = self.bits; - ret.reverse(); - ret - } - - pub fn from_bits_be(bits: &[Boolean]) -> Self { - assert_eq!(bits.len(), 32); - - let mut value = Some(0u32); - for b in bits { - if let Some(v) = value.as_mut() { - *v <<= 1; - } - - match b.get_value() { - Some(true) => { - if let Some(v) = value.as_mut() { - *v |= 1; - } - } - Some(false) => {} - None => { - value = None; - } - } - } - - UInt32 { - value, - bits: bits.iter().rev().cloned().collect(), - } - } - - /// Turns this `UInt32` into its little-endian byte order representation. - pub fn into_bits(self) -> Vec { - self.bits - } - - /// Converts a little-endian byte order representation of bits into a - /// `UInt32`. - pub fn from_bits(bits: &[Boolean]) -> Self { - assert_eq!(bits.len(), 32); - - let new_bits = bits.to_vec(); - - let mut value = Some(0u32); - for b in new_bits.iter().rev() { - if let Some(v) = value.as_mut() { - *v <<= 1; - } - - match *b { - Boolean::Constant(b) => { - if b { - if let Some(v) = value.as_mut() { - *v |= 1; - } - } - } - Boolean::Is(ref b) => match b.get_value() { - Some(true) => { - if let Some(v) = value.as_mut() { - *v |= 1; - } - } - Some(false) => {} - None => value = None, - }, - Boolean::Not(ref b) => match b.get_value() { - Some(false) => { - if let Some(v) = value.as_mut() { - *v |= 1; - } - } - Some(true) => {} - None => value = None, - }, - } - } - - UInt32 { - value, - bits: new_bits, - } - } - - pub fn rotr(&self, by: usize) -> Self { - let by = by % 32; - - let new_bits = self - .bits - .iter() - .skip(by) - .chain(self.bits.iter()) - .take(32) - .cloned() - .collect(); - - UInt32 { - bits: new_bits, - value: self.value.map(|v| v.rotate_right(by as u32)), - } - } - - pub fn shr(&self, by: usize) -> Self { - let by = by % 32; - - let fill = Boolean::constant(false); - - let new_bits = self - .bits - .iter() // The bits are least significant first - .skip(by) // Skip the bits that will be lost during the shift - .chain(Some(&fill).into_iter().cycle()) // Rest will be zeros - .take(32) // Only 32 bits needed! - .cloned() - .collect(); - - UInt32 { - bits: new_bits, - value: self.value.map(|v| v >> by as u32), - } - } - - pub fn shl(&self, by: usize) -> Self { - let by = by % 32; - - let fill = Boolean::constant(false); - let new_bits: Vec<_> = std::iter::repeat(&fill) - .take(by) - .chain(self.bits.iter()) // The bits are least significant first - .take(32) // Only 32 bits needed! - .cloned() - .collect(); - UInt32 { - bits: new_bits, - value: self.value.map(|v| v << by as u32), - } - } - - fn triop( - mut cs: CS, - a: &Self, - b: &Self, - c: &Self, - tri_fn: F, - circuit_fn: U, - ) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - F: Fn(u32, u32, u32) -> u32, - U: Fn(&mut CS, usize, &Boolean, &Boolean, &Boolean) -> Result, - { - 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::>()?; - - Ok(UInt32 { - 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( - cs: CS, - a: &Self, - b: &Self, - c: &Self, - ) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - { - 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( - cs: CS, - a: &Self, - b: &Self, - c: &Self, - ) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - { - 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), - ) - } - pub fn ripemd_d1( - cs: CS, - a: &Self, - b: &Self, - c: &Self, - ) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - { - Self::triop( - cs, - a, - b, - c, - |a, b, c| (a & b) | (!b & c), - |cs, i, a, b, c| Boolean::ripemd160_d1(cs.namespace(|| format!("d1 {}", i)), a, b, c), - ) - } - - pub fn ripemd_d2( - cs: CS, - a: &Self, - b: &Self, - c: &Self, - ) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - { - Self::triop( - cs, - a, - b, - c, - |a, b, c| (a) ^ (b | !c), - |cs, i, a, b, c| Boolean::ripemd160_d2(cs.namespace(|| format!("d2 {}", i)), a, b, c), - ) - } - /// XOR this `UInt32` with another `UInt32` - pub fn xor(&self, mut cs: CS, other: &Self) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - { - let new_value = match (self.value, other.value) { - (Some(a), Some(b)) => Some(a ^ b), - _ => None, - }; - - let bits = self - .bits - .iter() - .zip(other.bits.iter()) - .enumerate() - .map(|(i, (a, b))| Boolean::xor(cs.namespace(|| format!("xor of bit {}", i)), a, b)) - .collect::>()?; - - Ok(UInt32 { - bits, - value: new_value, - }) - } - - /// Perform modular addition of several `UInt32` objects. - #[allow(clippy::unnecessary_unwrap)] - pub fn addmany(mut cs: M, operands: &[Self]) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - M: ConstraintSystem>, - { - // Make some arbitrary bounds for ourselves to avoid overflows - // in the scalar field - assert!(Scalar::NUM_BITS >= 64); - assert!(operands.len() >= 2); // Weird trivial cases that should never happen - assert!(operands.len() <= 10); - - // Compute the maximum value of the sum so we allocate enough bits for - // the result - let mut max_value = (operands.len() as u64) * (u64::from(u32::max_value())); - - // Keep track of the resulting value - let mut result_value = Some(0u64); - - // This is a linear combination that we will enforce to equal the - // output - let mut lc = LinearCombination::zero(); - - let mut all_constants = true; - - // Iterate over the operands - for op in operands { - // Accumulate the value - match op.value { - Some(val) => { - if let Some(v) = result_value.as_mut() { - *v += u64::from(val); - } - } - None => { - // If any of our operands have unknown value, we won't - // know the value of the result - result_value = None; - } - } - - // Iterate over each bit of the operand and add the operand to - // the linear combination - let mut coeff = Scalar::ONE; - for bit in &op.bits { - lc = lc + &bit.lc(CS::one(), coeff); - - all_constants &= bit.is_constant(); - - coeff = coeff.double(); - } - } - - // The value of the actual result is modulo 2^32 - let modular_value = result_value.map(|v| v as u32); - - if all_constants && modular_value.is_some() { - // We can just return a constant, rather than - // unpacking the result into allocated bits. - - return Ok(UInt32::constant(modular_value.unwrap())); - } - - // Storage area for the resulting bits - let mut result_bits = vec![]; - - // Linear combination representing the output, - // for comparison with the sum of the operands - let mut result_lc = LinearCombination::zero(); - - // Allocate each bit of the result - let mut coeff = Scalar::ONE; - let mut i = 0; - while max_value != 0 { - // Allocate the bit - let b = AllocatedBit::alloc( - cs.namespace(|| format!("result bit {}", i)), - result_value.map(|v| (v >> i) & 1 == 1), - )?; - - // Add this bit to the result combination - result_lc = result_lc + (coeff, b.get_variable()); - - result_bits.push(b.into()); - - max_value >>= 1; - i += 1; - coeff = coeff.double(); - } - - // Enforce equality between the sum and result - cs.get_root().enforce_equal(i, &lc, &result_lc); - - // Discard carry bits that we don't care about - result_bits.truncate(32); - - Ok(UInt32 { - bits: result_bits, - value: modular_value, - }) - } -} - -#[cfg(test)] -mod test { - use super::UInt32; - use crate::gadgets::boolean::Boolean; - use crate::gadgets::multieq::MultiEq; - use bellpepper_core::test_cs::*; - use bellpepper_core::ConstraintSystem; - use blstrs::Scalar as Fr; - use ff::Field; - use rand_core::{RngCore, SeedableRng}; - use rand_xorshift::XorShiftRng; - - #[test] - fn test_uint32_from_bits_be() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for _ in 0..1000 { - let v = (0..32) - .map(|_| Boolean::constant(rng.next_u32() % 2 != 0)) - .collect::>(); - - let b = UInt32::from_bits_be(&v); - - for (i, bit) in b.bits.iter().enumerate() { - match *bit { - Boolean::Constant(bit) => { - assert!(bit == ((b.value.unwrap() >> i) & 1 == 1)); - } - _ => unreachable!(), - } - } - - let expected_to_be_same = b.into_bits_be(); - - for x in v.iter().zip(expected_to_be_same.iter()) { - match x { - (&Boolean::Constant(true), &Boolean::Constant(true)) => {} - (&Boolean::Constant(false), &Boolean::Constant(false)) => {} - _ => unreachable!(), - } - } - } - } - - #[test] - fn test_uint32_from_bits() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for _ in 0..1000 { - let v = (0..32) - .map(|_| Boolean::constant(rng.next_u32() % 2 != 0)) - .collect::>(); - - let b = UInt32::from_bits(&v); - - for (i, bit) in b.bits.iter().enumerate() { - match *bit { - Boolean::Constant(bit) => { - assert!(bit == ((b.value.unwrap() >> i) & 1 == 1)); - } - _ => unreachable!(), - } - } - - let expected_to_be_same = b.into_bits(); - - for x in v.iter().zip(expected_to_be_same.iter()) { - match x { - (&Boolean::Constant(true), &Boolean::Constant(true)) => {} - (&Boolean::Constant(false), &Boolean::Constant(false)) => {} - _ => unreachable!(), - } - } - } - } - - #[test] - fn test_uint32_xor() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for _ in 0..1000 { - let mut cs = TestConstraintSystem::::new(); - - let a = rng.next_u32(); - let b = rng.next_u32(); - let c = rng.next_u32(); - - let mut expected = a ^ 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 = a_bit.xor(cs.namespace(|| "first xor"), &b_bit).unwrap(); - let r = r.xor(cs.namespace(|| "second xor"), &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_eq!(b.get_value().unwrap(), expected & 1 == 1); - } - Boolean::Not(ref b) => { - assert_ne!(b.get_value().unwrap(), expected & 1 == 1); - } - Boolean::Constant(b) => { - assert_eq!(b, expected & 1 == 1); - } - } - - expected >>= 1; - } - } - } - - #[test] - fn test_uint32_addmany_constants() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for _ in 0..1000 { - let mut cs = TestConstraintSystem::::new(); - - let a = rng.next_u32(); - let b = rng.next_u32(); - let c = rng.next_u32(); - - let a_bit = UInt32::constant(a); - let b_bit = UInt32::constant(b); - let c_bit = UInt32::constant(c); - - let mut expected = a.wrapping_add(b).wrapping_add(c); - - let r = { - let mut cs = MultiEq::new(&mut cs); - let r = - UInt32::addmany(cs.namespace(|| "addition"), &[a_bit, b_bit, c_bit]).unwrap(); - r - }; - - assert!(r.value == Some(expected)); - - for b in r.bits.iter() { - match *b { - Boolean::Is(_) => panic!(), - Boolean::Not(_) => panic!(), - Boolean::Constant(b) => { - assert!(b == (expected & 1 == 1)); - } - } - - expected >>= 1; - } - } - } - - #[test] - #[allow(clippy::many_single_char_names)] - fn test_uint32_addmany() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for _ in 0..1000 { - let mut cs = TestConstraintSystem::::new(); - - let a = rng.next_u32(); - let b = rng.next_u32(); - let c = rng.next_u32(); - let d = rng.next_u32(); - - let mut expected = (a ^ b).wrapping_add(c).wrapping_add(d); - - let a_bit = UInt32::alloc(cs.namespace(|| "a_bit"), Some(a)).unwrap(); - let b_bit = UInt32::constant(b); - let c_bit = UInt32::constant(c); - let d_bit = UInt32::alloc(cs.namespace(|| "d_bit"), Some(d)).unwrap(); - - let r = a_bit.xor(cs.namespace(|| "xor"), &b_bit).unwrap(); - let r = { - let mut cs = MultiEq::new(&mut cs); - UInt32::addmany(cs.namespace(|| "addition"), &[r, c_bit, d_bit]).unwrap() - }; - - assert!(cs.is_satisfied()); - - assert!(r.value == Some(expected)); - - for b in r.bits.iter() { - match *b { - Boolean::Is(ref b) => { - assert_eq!(b.get_value().unwrap(), expected & 1 == 1); - } - Boolean::Not(ref b) => { - assert_ne!(b.get_value().unwrap(), expected & 1 == 1); - } - Boolean::Constant(_) => unreachable!(), - } - - expected >>= 1; - } - - // Flip a bit and see if the addition constraint still works - if cs.get("addition/result bit 0/boolean").is_zero().into() { - cs.set("addition/result bit 0/boolean", Field::ONE); - } else { - cs.set("addition/result bit 0/boolean", Field::ZERO); - } - - assert!(!cs.is_satisfied()); - } - } - - #[test] - fn test_uint32_rotr() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - let mut num = rng.next_u32(); - - let a = UInt32::constant(num); - - for i in 0..32 { - let b = a.rotr(i); - assert_eq!(a.bits.len(), b.bits.len()); - - assert!(b.value.unwrap() == num); - - let mut tmp = num; - for b in &b.bits { - match *b { - Boolean::Constant(b) => { - assert_eq!(b, tmp & 1 == 1); - } - _ => unreachable!(), - } - - tmp >>= 1; - } - - num = num.rotate_right(1); - } - } - - #[test] - fn test_uint32_shr() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for _ in 0..50 { - for i in 0..60 { - let num = rng.next_u32(); - let a = UInt32::constant(num).shr(i); - let b = UInt32::constant(num.wrapping_shr(i as u32)); - - assert_eq!(a.value.unwrap(), num.wrapping_shr(i as u32)); - - assert_eq!(a.bits.len(), b.bits.len()); - for (a, b) in a.bits.iter().zip(b.bits.iter()) { - assert_eq!(a.get_value().unwrap(), b.get_value().unwrap()); - } - } - } - } - - #[test] - fn test_uint32_shl() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for _ in 0..50 { - for i in 0..60 { - let num = rng.next_u32(); - let a = UInt32::constant(num).shl(i); - let b = UInt32::constant(num.wrapping_shl(i as u32)); - - assert_eq!(a.value.unwrap(), num.wrapping_shl(i as u32)); - - assert_eq!(a.bits.len(), b.bits.len()); - for (a, b) in a.bits.iter().zip(b.bits.iter()) { - assert_eq!(a.get_value().unwrap(), b.get_value().unwrap()); - } - } - } - } - - #[test] - fn test_uint32_sha256_maj() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for _ in 0..1000 { - let mut cs = TestConstraintSystem::::new(); - - let a = rng.next_u32(); - let b = rng.next_u32(); - let c = rng.next_u32(); - - 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_eq!(b.get_value().unwrap(), expected & 1 == 1); - } - Boolean::Not(ref b) => { - assert_ne!(b.get_value().unwrap(), expected & 1 == 1); - } - Boolean::Constant(b) => { - assert_eq!(b, expected & 1 == 1); - } - } - - expected >>= 1; - } - } - } - - #[test] - fn test_uint32_sha256_ch() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for _ in 0..1000 { - let mut cs = TestConstraintSystem::::new(); - - let a = rng.next_u32(); - let b = rng.next_u32(); - let c = rng.next_u32(); - - 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_eq!(b.get_value().unwrap(), expected & 1 == 1); - } - Boolean::Not(ref b) => { - assert_ne!(b.get_value().unwrap(), expected & 1 == 1); - } - Boolean::Constant(b) => { - assert_eq!(b, expected & 1 == 1); - } - } - - expected >>= 1; - } - } - } - - #[test] - fn test_uint32_ripemd_d1() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for _ in 0..1000 { - let mut cs = TestConstraintSystem::::new(); - - let a = rng.next_u32(); - let b = rng.next_u32(); - let c = rng.next_u32(); - - let mut expected = (a & b) | ((!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::ripemd_d1(&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_eq!(b.get_value().unwrap(), expected & 1 == 1); - } - Boolean::Not(ref b) => { - assert_ne!(b.get_value().unwrap(), expected & 1 == 1); - } - Boolean::Constant(b) => { - assert_eq!(b, expected & 1 == 1); - } - } - - expected >>= 1; - } - } - } - - #[test] - fn test_uint32_ripemd_d2() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for _ in 0..1000 { - let mut cs = TestConstraintSystem::::new(); - - let a = rng.next_u32(); - let b = rng.next_u32(); - let c = rng.next_u32(); - - let mut expected = (a) ^ ((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::ripemd_d2(&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_eq!(b.get_value().unwrap(), expected & 1 == 1); - } - Boolean::Not(ref b) => { - assert_ne!(b.get_value().unwrap(), expected & 1 == 1); - } - Boolean::Constant(b) => { - assert_eq!(b, expected & 1 == 1); - } - } - - expected >>= 1; - } - } - } -} From 3dbacf2d0fcaddace244ed052514d6a28cc8c814 Mon Sep 17 00:00:00 2001 From: Sh0g0-1758 Date: Tue, 12 Mar 2024 00:54:20 +0530 Subject: [PATCH 03/46] modified cargo.toml --- Cargo.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 5b0fb9d..fce4d66 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,8 @@ members = [ "crates/sha512", "crates/sha1", "crates/keccak", - "crates/merkle-inclusion" + "crates/merkle-inclusion", + "crates/ripemd160", ] [workspace.package] From a67c90307736547c4672841fabf3cdb35db12a16 Mon Sep 17 00:00:00 2001 From: Sh0g0-1758 Date: Tue, 12 Mar 2024 01:02:15 +0530 Subject: [PATCH 04/46] Revert --- crates/ripemd160/src/ripemd160.rs | 68 ++++++++++++------------------- 1 file changed, 26 insertions(+), 42 deletions(-) diff --git a/crates/ripemd160/src/ripemd160.rs b/crates/ripemd160/src/ripemd160.rs index 55e9851..1e0c8dd 100644 --- a/crates/ripemd160/src/ripemd160.rs +++ b/crates/ripemd160/src/ripemd160.rs @@ -44,42 +44,7 @@ where padded.push(Boolean::constant(b)); } assert!(padded.len() % 512 == 0); - let mut cur_md = get_ripemd160_md("md"); - let mut cur_md_prime = get_ripemd160_md("md_prime"); - for (i, block) in padded.chunks(512).enumerate() { - let prev_md = cur_md.clone(); - cur_md = ripemd160_func_block(cs.namespace(|| format!("block {}", i)), block, &mut cur_md)?; - cur_md_prime = ripemd160_func_block_prime( - cs.namespace(|| format!("block_prime {}", i)), - block, - &mut cur_md_prime, - )?; - let mut update_md = cur_md.clone(); - for i in 0..5 { - update_md[(i+4)%5] = prev_md[i].xor( - cs.namespace(|| format!("first xor {}", i)), - &cur_md[(i+1)%5], - )?; - update_md[(i+4)%5] = update_md[(i+4)%5].xor( - cs.namespace(|| format!("second xor {}", i)), - &cur_md_prime[(i+2)%5], - )?; - } - cur_md = update_md; - cur_md_prime = cur_md.clone(); - } - Ok(cur_md.into_iter().flat_map(|e| e.into_bits_be()).collect()) -} - -fn get_ripemd160_md(input: &str) -> Vec { - match input { - "md" => MD_BUFFERS.iter().map(|&v| UInt32::constant(v)).collect(), - "md_prime" => MD_BUFFERS_PRIME - .iter() - .map(|&v| UInt32::constant(v)) - .collect(), - _ => panic!("Invalid input"), - } + } pub fn ripemd160_func_block( @@ -158,12 +123,31 @@ where )?; s_val=[11,13,6,7,14,9,13,15,14,8,13,6,5,12,7,5]; i_val=[3,10,14,4,9,15,8,1,2,7,0,6,13,11,5,12]; - for i in 0..16 { - let mut tmp1 = current_md_value[0] - .xor(cs.namespace(|| format!("first xor {}", i)), &f)? - .xor(cs.namespace(|| format!("second xor {}", i)), &w[i_val[i]])? - .xor( - cs.namespace(|| format!("third xor {}", i)), + for i in 0..16 {let mut cur_md = get_ripemd160_md("md"); + let mut cur_md_prime = get_ripemd160_md("md_prime"); + for (i, block) in padded.chunks(512).enumerate() { + let prev_md = cur_md.clone(); + cur_md = ripemd160_func_block(cs.namespace(|| format!("block {}", i)), block, &mut cur_md)?; + cur_md_prime = ripemd160_func_block_prime( + cs.namespace(|| format!("block_prime {}", i)), + block, + &mut cur_md_prime, + )?; + let mut update_md = cur_md.clone(); + for i in 0..5 { + update_md[(i+4)%5] = prev_md[i].xor( + cs.namespace(|| format!("first xor {}", i)), + &cur_md[(i+1)%5], + )?; + update_md[(i+4)%5] = update_md[(i+4)%5].xor( + cs.namespace(|| format!("second xor {}", i)), + &cur_md_prime[(i+2)%5], + )?; + } + cur_md = update_md; + cur_md_prime = cur_md.clone(); + } + Ok(cur_md.into_iter().flat_map(|e| e.into_bits_be()).collect())|| format!("third xor {}", i)), &UInt32::constant(K_BUFFER[2]), )?; tmp1 = current_md_value[0].shl(s_val[i]); From 2f09b043a271141b91d0b3b97b77c2bb0c532de0 Mon Sep 17 00:00:00 2001 From: harry2855 Date: Tue, 12 Mar 2024 01:03:35 +0530 Subject: [PATCH 05/46] final computation --- crates/ripemd160/src/lib.rs | 3 +- crates/ripemd160/src/ripemd160.rs | 142 +++-- crates/ripemd160/src/uint32.rs | 945 ++++++++++++++++++++++++++++++ 3 files changed, 1035 insertions(+), 55 deletions(-) create mode 100644 crates/ripemd160/src/uint32.rs diff --git a/crates/ripemd160/src/lib.rs b/crates/ripemd160/src/lib.rs index 2764463..7e5bde3 100644 --- a/crates/ripemd160/src/lib.rs +++ b/crates/ripemd160/src/lib.rs @@ -1 +1,2 @@ -pub mod ripemd160; \ No newline at end of file +pub mod ripemd160; +pub mod uint32; diff --git a/crates/ripemd160/src/ripemd160.rs b/crates/ripemd160/src/ripemd160.rs index 1e0c8dd..6d2c583 100644 --- a/crates/ripemd160/src/ripemd160.rs +++ b/crates/ripemd160/src/ripemd160.rs @@ -7,9 +7,9 @@ use ff::PrimeField; +use super::uint32::UInt32; use bellpepper::gadgets::boolean::Boolean; use bellpepper::gadgets::multieq::MultiEq; -use super::uint32::UInt32; use bellpepper_core::{ConstraintSystem, SynthesisError}; #[allow(clippy::unreadable_literal)] @@ -44,13 +44,48 @@ where padded.push(Boolean::constant(b)); } assert!(padded.len() % 512 == 0); - + let mut cur_md = get_ripemd160_md("md"); + let mut cur_md_prime = get_ripemd160_md("md_prime"); + for (i, block) in padded.chunks(512).enumerate() { + let prev_md = cur_md.clone(); + cur_md = ripemd160_func_block(cs.namespace(|| format!("block {}", i)), block, &mut cur_md)?; + cur_md_prime = ripemd160_func_block_prime( + cs.namespace(|| format!("block_prime {}", i)), + block, + &mut cur_md_prime, + )?; + let mut update_md = cur_md.clone(); + for i in 0..5 { + update_md[(i+4)%5] = prev_md[i].xor( + cs.namespace(|| format!("first xor {}", i)), + &cur_md[(i+1)%5], + )?; + update_md[(i+4)%5] = update_md[(i+4)%5].xor( + cs.namespace(|| format!("second xor {}", i)), + &cur_md_prime[(i+2)%5], + )?; + } + cur_md = update_md; + cur_md_prime = cur_md.clone(); + } + Ok(cur_md.into_iter().flat_map(|e| e.into_bits_be()).collect()) +} + +fn get_ripemd160_md(input: &str) -> Vec { + match input { + "md" => MD_BUFFERS.iter().map(|&v| UInt32::constant(v)).collect(), + "md_prime" => MD_BUFFERS_PRIME + .iter() + .map(|&v| UInt32::constant(v)) + .collect(), + _ => panic!("Invalid input"), + } } pub fn ripemd160_func_block( cs: CS, input: &[Boolean], - current_md_value: &mut[UInt32], + current_md_value: &mut [UInt32], ) -> Result, SynthesisError> where Scalar: PrimeField, @@ -94,7 +129,7 @@ where ¤t_md_value[2], )?; s_val = [7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12]; - let mut i_val = [7,14,13,1,10,6,15,3,12,0,9,5,2,14,11,8]; + let mut i_val = [7, 14, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8]; for i in 0..16 { let mut tmp1 = current_md_value[0] .xor(cs.namespace(|| format!("first xor {}", i)), &f)? @@ -123,31 +158,12 @@ where )?; s_val=[11,13,6,7,14,9,13,15,14,8,13,6,5,12,7,5]; i_val=[3,10,14,4,9,15,8,1,2,7,0,6,13,11,5,12]; - for i in 0..16 {let mut cur_md = get_ripemd160_md("md"); - let mut cur_md_prime = get_ripemd160_md("md_prime"); - for (i, block) in padded.chunks(512).enumerate() { - let prev_md = cur_md.clone(); - cur_md = ripemd160_func_block(cs.namespace(|| format!("block {}", i)), block, &mut cur_md)?; - cur_md_prime = ripemd160_func_block_prime( - cs.namespace(|| format!("block_prime {}", i)), - block, - &mut cur_md_prime, - )?; - let mut update_md = cur_md.clone(); - for i in 0..5 { - update_md[(i+4)%5] = prev_md[i].xor( - cs.namespace(|| format!("first xor {}", i)), - &cur_md[(i+1)%5], - )?; - update_md[(i+4)%5] = update_md[(i+4)%5].xor( - cs.namespace(|| format!("second xor {}", i)), - &cur_md_prime[(i+2)%5], - )?; - } - cur_md = update_md; - cur_md_prime = cur_md.clone(); - } - Ok(cur_md.into_iter().flat_map(|e| e.into_bits_be()).collect())|| format!("third xor {}", i)), + for i in 0..16 { + let mut tmp1 = current_md_value[0] + .xor(cs.namespace(|| format!("first xor {}", i)), &f)? + .xor(cs.namespace(|| format!("second xor {}", i)), &w[i_val[i]])? + .xor( + cs.namespace(|| format!("third xor {}", i)), &UInt32::constant(K_BUFFER[2]), )?; tmp1 = current_md_value[0].shl(s_val[i]); @@ -168,8 +184,8 @@ where ¤t_md_value[3], ¤t_md_value[2], )?; - s_val=[11,12,14,15,14,15,9,8,9,14,5,6,8,6,5,12]; - i_val=[1,9,11,10,0,8,12,4,13,3,7,15,14,5,6,2]; + s_val = [11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12]; + i_val = [1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2]; for i in 0..16 { let mut tmp1 = current_md_value[0] .xor(cs.namespace(|| format!("first xor {}", i)), &f)? @@ -196,8 +212,8 @@ where ¤t_md_value[2], ¤t_md_value[3], )?; - s_val=[9,15,5,11,6,8,13,12,5,12,13,14,11,8,5,6]; - i_val=[4,0,5,9,7,12,2,10,14,1,3,8,11,6,15,13]; + s_val = [9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6]; + i_val = [4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13]; for i in 0..16 { let mut tmp1 = current_md_value[0] .xor(cs.namespace(|| format!("first xor {}", i)), &f)? @@ -218,7 +234,13 @@ where current_md_value[1] = tmp1.clone(); current_md_value[3] = tmp2.clone(); } - Ok(vec![current_md_value[0].clone(),current_md_value[1].clone(),current_md_value[2].clone(),current_md_value[3].clone(),current_md_value[4].clone()]) + Ok(vec![ + current_md_value[0].clone(), + current_md_value[1].clone(), + current_md_value[2].clone(), + current_md_value[3].clone(), + current_md_value[4].clone(), + ]) } pub fn ripemd160_func_block_prime( @@ -243,8 +265,8 @@ where ¤t_md_value[2], ¤t_md_value[3], )?; - let mut s_val = [8,9,9,11,13,15,15,5,7,7,8,11,14,14,12,6]; - let mut i_val = [5,14,7,0,9,2,11,4,13,6,15,8,1,10,3,12]; + let mut s_val = [8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6]; + let mut i_val = [5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12]; for i in 0..16 { let mut tmp1 = current_md_value[0] .xor(cs.namespace(|| format!("first xor {}", i)), &f)? @@ -271,8 +293,8 @@ where ¤t_md_value[3], ¤t_md_value[2], )?; - s_val = [9,13,15,7,12,8,9,11,7,7,12,7,6,15,13,11]; - i_val = [6,11,3,7,0,13,5,10,14,15,8,12,4,9,1,2]; + s_val = [9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11]; + i_val = [6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2]; for i in 0..16 { let mut tmp1 = current_md_value[0] .xor(cs.namespace(|| format!("first xor {}", i)), &f)? @@ -299,8 +321,8 @@ where ¤t_md_value[1], ¤t_md_value[2], )?; - s_val = [9,7,15,11,8,6,6,14,12,13,5,14,13,13,7,5]; - i_val = [15,5,1,3,7,14,6,9,11,8,12,2,10,0,4,13]; + s_val = [9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5]; + i_val = [15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13]; for i in 0..16 { let mut tmp1 = current_md_value[0] .xor(cs.namespace(|| format!("first xor {}", i)), &f)? @@ -327,8 +349,8 @@ where ¤t_md_value[1], ¤t_md_value[3], )?; - s_val = [15,5,8,11,14,14,6,14,6,9,12,9,12,5,15,8]; - i_val = [8,6,4,1,3,11,15,0,5,12,2,13,9,7,10,14]; + s_val = [15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8]; + i_val = [8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14]; for i in 0..16 { let mut tmp1 = current_md_value[0] .xor(cs.namespace(|| format!("first xor {}", i)), &f)? @@ -350,10 +372,10 @@ where current_md_value[3] = tmp2.clone(); } f = current_md_value[1] - .xor(cs.namespace(|| "first xor"), ¤t_md_value[2])? - .xor(cs.namespace(|| "second xor"), ¤t_md_value[3])?; - s_val = [8,5,12,9,12,5,14,6,8,13,6,5,15,13,11,11]; - i_val = [12,15,10,4,1,5,8,7,6,2,13,14,0,3,9,11]; + .xor(cs.namespace(|| "first xor"), ¤t_md_value[2])? + .xor(cs.namespace(|| "second xor"), ¤t_md_value[3])?; + s_val = [8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11]; + i_val = [12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11]; for i in 0..16 { let mut tmp1 = current_md_value[0] .xor(cs.namespace(|| format!("first xor {}", i)), &f)? @@ -374,7 +396,13 @@ where current_md_value[1] = tmp1.clone(); current_md_value[3] = tmp2.clone(); } - Ok(vec![current_md_value[0].clone(),current_md_value[1].clone(),current_md_value[2].clone(),current_md_value[3].clone(),current_md_value[4].clone()]) + Ok(vec![ + current_md_value[0].clone(), + current_md_value[1].clone(), + current_md_value[2].clone(), + current_md_value[3].clone(), + current_md_value[4].clone(), + ]) } #[cfg(test)] @@ -401,18 +429,24 @@ mod test { cur_md_prime = ripemd160_func_block_prime(&mut cs, &input_bits, &mut cur_md_prime).unwrap(); let mut update_md = cur_md.clone(); for i in 0..5 { - match prev_md[i].xor(cs.namespace(|| format!("first xor {}", i)), &cur_md[(i+1)%5]) { + match prev_md[i].xor( + cs.namespace(|| format!("first xor {}", i)), + &cur_md[(i + 1) % 5], + ) { Ok(result) => { - update_md[(i+4)%5] = result; - }, + update_md[(i + 4) % 5] = result; + } Err(err) => { // Handle the error here } } - match update_md[(i+4)%5].xor(cs.namespace(|| format!("first xor {}", i)), &cur_md[(i+2)%5]) { + match update_md[(i + 4) % 5].xor( + cs.namespace(|| format!("first xor {}", i)), + &cur_md[(i + 2) % 5], + ) { Ok(result) => { - update_md[(i+4)%5] = result; - }, + update_md[(i + 4) % 5] = result; + } Err(err) => { // Handle the error here } @@ -436,4 +470,4 @@ mod test { // } // } } -} \ No newline at end of file +} diff --git a/crates/ripemd160/src/uint32.rs b/crates/ripemd160/src/uint32.rs new file mode 100644 index 0000000..fa8de2f --- /dev/null +++ b/crates/ripemd160/src/uint32.rs @@ -0,0 +1,945 @@ +//! Circuit representation of a [`u32`], with helpers for the [`sha256`] +//! gadgets. + +use ff::PrimeField; + +use bellpepper_core::{ConstraintSystem, LinearCombination, SynthesisError}; + +use super::boolean::{AllocatedBit, Boolean}; +use super::multieq::MultiEq; + +/// Represents an interpretation of 32 `Boolean` objects as an +/// unsigned integer. +#[derive(Clone, Debug)] +pub struct UInt32 { + // Least significant bit first + bits: Vec, + value: Option, +} + +impl UInt32 { + /// Construct a constant `UInt32` from a `u32` + pub fn constant(value: u32) -> Self { + let mut bits = Vec::with_capacity(32); + + let mut tmp = value; + for _ in 0..32 { + if tmp & 1 == 1 { + bits.push(Boolean::constant(true)) + } else { + bits.push(Boolean::constant(false)) + } + + tmp >>= 1; + } + + UInt32 { + bits, + value: Some(value), + } + } + + /// Allocate a `UInt32` in the constraint system + pub fn alloc(mut cs: CS, value: Option) -> Result + where + Scalar: PrimeField, + CS: ConstraintSystem, + { + let values = match value { + Some(mut val) => { + let mut v = Vec::with_capacity(32); + + for _ in 0..32 { + v.push(Some(val & 1 == 1)); + val >>= 1; + } + + v + } + None => vec![None; 32], + }; + + let bits = values + .into_iter() + .enumerate() + .map(|(i, v)| { + Ok(Boolean::from(AllocatedBit::alloc( + cs.namespace(|| format!("allocated bit {}", i)), + v, + )?)) + }) + .collect::, SynthesisError>>()?; + + Ok(UInt32 { bits, value }) + } + + pub fn into_bits_be(self) -> Vec { + let mut ret = self.bits; + ret.reverse(); + ret + } + + pub fn from_bits_be(bits: &[Boolean]) -> Self { + assert_eq!(bits.len(), 32); + + let mut value = Some(0u32); + for b in bits { + if let Some(v) = value.as_mut() { + *v <<= 1; + } + + match b.get_value() { + Some(true) => { + if let Some(v) = value.as_mut() { + *v |= 1; + } + } + Some(false) => {} + None => { + value = None; + } + } + } + + UInt32 { + value, + bits: bits.iter().rev().cloned().collect(), + } + } + + /// Turns this `UInt32` into its little-endian byte order representation. + pub fn into_bits(self) -> Vec { + self.bits + } + + /// Converts a little-endian byte order representation of bits into a + /// `UInt32`. + pub fn from_bits(bits: &[Boolean]) -> Self { + assert_eq!(bits.len(), 32); + + let new_bits = bits.to_vec(); + + let mut value = Some(0u32); + for b in new_bits.iter().rev() { + if let Some(v) = value.as_mut() { + *v <<= 1; + } + + match *b { + Boolean::Constant(b) => { + if b { + if let Some(v) = value.as_mut() { + *v |= 1; + } + } + } + Boolean::Is(ref b) => match b.get_value() { + Some(true) => { + if let Some(v) = value.as_mut() { + *v |= 1; + } + } + Some(false) => {} + None => value = None, + }, + Boolean::Not(ref b) => match b.get_value() { + Some(false) => { + if let Some(v) = value.as_mut() { + *v |= 1; + } + } + Some(true) => {} + None => value = None, + }, + } + } + + UInt32 { + value, + bits: new_bits, + } + } + + pub fn rotr(&self, by: usize) -> Self { + let by = by % 32; + + let new_bits = self + .bits + .iter() + .skip(by) + .chain(self.bits.iter()) + .take(32) + .cloned() + .collect(); + + UInt32 { + bits: new_bits, + value: self.value.map(|v| v.rotate_right(by as u32)), + } + } + + pub fn shr(&self, by: usize) -> Self { + let by = by % 32; + + let fill = Boolean::constant(false); + + let new_bits = self + .bits + .iter() // The bits are least significant first + .skip(by) // Skip the bits that will be lost during the shift + .chain(Some(&fill).into_iter().cycle()) // Rest will be zeros + .take(32) // Only 32 bits needed! + .cloned() + .collect(); + + UInt32 { + bits: new_bits, + value: self.value.map(|v| v >> by as u32), + } + } + + pub fn shl(&self, by: usize) -> Self { + let by = by % 32; + + let fill = Boolean::constant(false); + let new_bits: Vec<_> = std::iter::repeat(&fill) + .take(by) + .chain(self.bits.iter()) // The bits are least significant first + .take(32) // Only 32 bits needed! + .cloned() + .collect(); + UInt32 { + bits: new_bits, + value: self.value.map(|v| v << by as u32), + } + } + + fn triop( + mut cs: CS, + a: &Self, + b: &Self, + c: &Self, + tri_fn: F, + circuit_fn: U, + ) -> Result + where + Scalar: PrimeField, + CS: ConstraintSystem, + F: Fn(u32, u32, u32) -> u32, + U: Fn(&mut CS, usize, &Boolean, &Boolean, &Boolean) -> Result, + { + 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::>()?; + + Ok(UInt32 { + 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( + cs: CS, + a: &Self, + b: &Self, + c: &Self, + ) -> Result + where + Scalar: PrimeField, + CS: ConstraintSystem, + { + 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( + cs: CS, + a: &Self, + b: &Self, + c: &Self, + ) -> Result + where + Scalar: PrimeField, + CS: ConstraintSystem, + { + 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), + ) + } + pub fn ripemd_d1( + cs: CS, + a: &Self, + b: &Self, + c: &Self, + ) -> Result + where + Scalar: PrimeField, + CS: ConstraintSystem, + { + Self::triop( + cs, + a, + b, + c, + |a, b, c| (a & b) | (!b & c), + |cs, i, a, b, c| Boolean::ripemd160_d1(cs.namespace(|| format!("d1 {}", i)), a, b, c), + ) + } + + pub fn ripemd_d2( + cs: CS, + a: &Self, + b: &Self, + c: &Self, + ) -> Result + where + Scalar: PrimeField, + CS: ConstraintSystem, + { + Self::triop( + cs, + a, + b, + c, + |a, b, c| (a) ^ (b | !c), + |cs, i, a, b, c| Boolean::ripemd160_d2(cs.namespace(|| format!("d1 {}", i)), a, b, c), + ) + } + /// XOR this `UInt32` with another `UInt32` + pub fn xor(&self, mut cs: CS, other: &Self) -> Result + where + Scalar: PrimeField, + CS: ConstraintSystem, + { + let new_value = match (self.value, other.value) { + (Some(a), Some(b)) => Some(a ^ b), + _ => None, + }; + + let bits = self + .bits + .iter() + .zip(other.bits.iter()) + .enumerate() + .map(|(i, (a, b))| Boolean::xor(cs.namespace(|| format!("xor of bit {}", i)), a, b)) + .collect::>()?; + + Ok(UInt32 { + bits, + value: new_value, + }) + } + + /// Perform modular addition of several `UInt32` objects. + #[allow(clippy::unnecessary_unwrap)] + pub fn addmany(mut cs: M, operands: &[Self]) -> Result + where + Scalar: PrimeField, + CS: ConstraintSystem, + M: ConstraintSystem>, + { + // Make some arbitrary bounds for ourselves to avoid overflows + // in the scalar field + assert!(Scalar::NUM_BITS >= 64); + assert!(operands.len() >= 2); // Weird trivial cases that should never happen + assert!(operands.len() <= 10); + + // Compute the maximum value of the sum so we allocate enough bits for + // the result + let mut max_value = (operands.len() as u64) * (u64::from(u32::max_value())); + + // Keep track of the resulting value + let mut result_value = Some(0u64); + + // This is a linear combination that we will enforce to equal the + // output + let mut lc = LinearCombination::zero(); + + let mut all_constants = true; + + // Iterate over the operands + for op in operands { + // Accumulate the value + match op.value { + Some(val) => { + if let Some(v) = result_value.as_mut() { + *v += u64::from(val); + } + } + None => { + // If any of our operands have unknown value, we won't + // know the value of the result + result_value = None; + } + } + + // Iterate over each bit of the operand and add the operand to + // the linear combination + let mut coeff = Scalar::ONE; + for bit in &op.bits { + lc = lc + &bit.lc(CS::one(), coeff); + + all_constants &= bit.is_constant(); + + coeff = coeff.double(); + } + } + + // The value of the actual result is modulo 2^32 + let modular_value = result_value.map(|v| v as u32); + + if all_constants && modular_value.is_some() { + // We can just return a constant, rather than + // unpacking the result into allocated bits. + + return Ok(UInt32::constant(modular_value.unwrap())); + } + + // Storage area for the resulting bits + let mut result_bits = vec![]; + + // Linear combination representing the output, + // for comparison with the sum of the operands + let mut result_lc = LinearCombination::zero(); + + // Allocate each bit of the result + let mut coeff = Scalar::ONE; + let mut i = 0; + while max_value != 0 { + // Allocate the bit + let b = AllocatedBit::alloc( + cs.namespace(|| format!("result bit {}", i)), + result_value.map(|v| (v >> i) & 1 == 1), + )?; + + // Add this bit to the result combination + result_lc = result_lc + (coeff, b.get_variable()); + + result_bits.push(b.into()); + + max_value >>= 1; + i += 1; + coeff = coeff.double(); + } + + // Enforce equality between the sum and result + cs.get_root().enforce_equal(i, &lc, &result_lc); + + // Discard carry bits that we don't care about + result_bits.truncate(32); + + Ok(UInt32 { + bits: result_bits, + value: modular_value, + }) + } +} + +#[cfg(test)] +mod test { + use super::UInt32; + use crate::gadgets::boolean::Boolean; + use crate::gadgets::multieq::MultiEq; + use bellpepper_core::test_cs::*; + use bellpepper_core::ConstraintSystem; + use blstrs::Scalar as Fr; + use ff::Field; + use rand_core::{RngCore, SeedableRng}; + use rand_xorshift::XorShiftRng; + + #[test] + fn test_uint32_from_bits_be() { + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, + 0xbc, 0xe5, + ]); + + for _ in 0..1000 { + let v = (0..32) + .map(|_| Boolean::constant(rng.next_u32() % 2 != 0)) + .collect::>(); + + let b = UInt32::from_bits_be(&v); + + for (i, bit) in b.bits.iter().enumerate() { + match *bit { + Boolean::Constant(bit) => { + assert!(bit == ((b.value.unwrap() >> i) & 1 == 1)); + } + _ => unreachable!(), + } + } + + let expected_to_be_same = b.into_bits_be(); + + for x in v.iter().zip(expected_to_be_same.iter()) { + match x { + (&Boolean::Constant(true), &Boolean::Constant(true)) => {} + (&Boolean::Constant(false), &Boolean::Constant(false)) => {} + _ => unreachable!(), + } + } + } + } + + #[test] + fn test_uint32_from_bits() { + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, + 0xbc, 0xe5, + ]); + + for _ in 0..1000 { + let v = (0..32) + .map(|_| Boolean::constant(rng.next_u32() % 2 != 0)) + .collect::>(); + + let b = UInt32::from_bits(&v); + + for (i, bit) in b.bits.iter().enumerate() { + match *bit { + Boolean::Constant(bit) => { + assert!(bit == ((b.value.unwrap() >> i) & 1 == 1)); + } + _ => unreachable!(), + } + } + + let expected_to_be_same = b.into_bits(); + + for x in v.iter().zip(expected_to_be_same.iter()) { + match x { + (&Boolean::Constant(true), &Boolean::Constant(true)) => {} + (&Boolean::Constant(false), &Boolean::Constant(false)) => {} + _ => unreachable!(), + } + } + } + } + + #[test] + fn test_uint32_xor() { + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, + 0xbc, 0xe5, + ]); + + for _ in 0..1000 { + let mut cs = TestConstraintSystem::::new(); + + let a = rng.next_u32(); + let b = rng.next_u32(); + let c = rng.next_u32(); + + let mut expected = a ^ 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 = a_bit.xor(cs.namespace(|| "first xor"), &b_bit).unwrap(); + let r = r.xor(cs.namespace(|| "second xor"), &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_eq!(b.get_value().unwrap(), expected & 1 == 1); + } + Boolean::Not(ref b) => { + assert_ne!(b.get_value().unwrap(), expected & 1 == 1); + } + Boolean::Constant(b) => { + assert_eq!(b, expected & 1 == 1); + } + } + + expected >>= 1; + } + } + } + + #[test] + fn test_uint32_addmany_constants() { + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, + 0xbc, 0xe5, + ]); + + for _ in 0..1000 { + let mut cs = TestConstraintSystem::::new(); + + let a = rng.next_u32(); + let b = rng.next_u32(); + let c = rng.next_u32(); + + let a_bit = UInt32::constant(a); + let b_bit = UInt32::constant(b); + let c_bit = UInt32::constant(c); + + let mut expected = a.wrapping_add(b).wrapping_add(c); + + let r = { + let mut cs = MultiEq::new(&mut cs); + let r = + UInt32::addmany(cs.namespace(|| "addition"), &[a_bit, b_bit, c_bit]).unwrap(); + r + }; + + assert!(r.value == Some(expected)); + + for b in r.bits.iter() { + match *b { + Boolean::Is(_) => panic!(), + Boolean::Not(_) => panic!(), + Boolean::Constant(b) => { + assert!(b == (expected & 1 == 1)); + } + } + + expected >>= 1; + } + } + } + + #[test] + #[allow(clippy::many_single_char_names)] + fn test_uint32_addmany() { + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, + 0xbc, 0xe5, + ]); + + for _ in 0..1000 { + let mut cs = TestConstraintSystem::::new(); + + let a = rng.next_u32(); + let b = rng.next_u32(); + let c = rng.next_u32(); + let d = rng.next_u32(); + + let mut expected = (a ^ b).wrapping_add(c).wrapping_add(d); + + let a_bit = UInt32::alloc(cs.namespace(|| "a_bit"), Some(a)).unwrap(); + let b_bit = UInt32::constant(b); + let c_bit = UInt32::constant(c); + let d_bit = UInt32::alloc(cs.namespace(|| "d_bit"), Some(d)).unwrap(); + + let r = a_bit.xor(cs.namespace(|| "xor"), &b_bit).unwrap(); + let r = { + let mut cs = MultiEq::new(&mut cs); + UInt32::addmany(cs.namespace(|| "addition"), &[r, c_bit, d_bit]).unwrap() + }; + + assert!(cs.is_satisfied()); + + assert!(r.value == Some(expected)); + + for b in r.bits.iter() { + match *b { + Boolean::Is(ref b) => { + assert_eq!(b.get_value().unwrap(), expected & 1 == 1); + } + Boolean::Not(ref b) => { + assert_ne!(b.get_value().unwrap(), expected & 1 == 1); + } + Boolean::Constant(_) => unreachable!(), + } + + expected >>= 1; + } + + // Flip a bit and see if the addition constraint still works + if cs.get("addition/result bit 0/boolean").is_zero().into() { + cs.set("addition/result bit 0/boolean", Field::ONE); + } else { + cs.set("addition/result bit 0/boolean", Field::ZERO); + } + + assert!(!cs.is_satisfied()); + } + } + + #[test] + fn test_uint32_rotr() { + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, + 0xbc, 0xe5, + ]); + + let mut num = rng.next_u32(); + + let a = UInt32::constant(num); + + for i in 0..32 { + let b = a.rotr(i); + assert_eq!(a.bits.len(), b.bits.len()); + + assert!(b.value.unwrap() == num); + + let mut tmp = num; + for b in &b.bits { + match *b { + Boolean::Constant(b) => { + assert_eq!(b, tmp & 1 == 1); + } + _ => unreachable!(), + } + + tmp >>= 1; + } + + num = num.rotate_right(1); + } + } + + #[test] + fn test_uint32_shr() { + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, + 0xbc, 0xe5, + ]); + + for _ in 0..50 { + for i in 0..60 { + let num = rng.next_u32(); + let a = UInt32::constant(num).shr(i); + let b = UInt32::constant(num.wrapping_shr(i as u32)); + + assert_eq!(a.value.unwrap(), num.wrapping_shr(i as u32)); + + assert_eq!(a.bits.len(), b.bits.len()); + for (a, b) in a.bits.iter().zip(b.bits.iter()) { + assert_eq!(a.get_value().unwrap(), b.get_value().unwrap()); + } + } + } + } + + #[test] + fn test_uint32_shl() { + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, + 0xbc, 0xe5, + ]); + + for _ in 0..50 { + for i in 0..60 { + let num = rng.next_u32(); + let a = UInt32::constant(num).shl(i); + let b = UInt32::constant(num.wrapping_shl(i as u32)); + + assert_eq!(a.value.unwrap(), num.wrapping_shl(i as u32)); + + assert_eq!(a.bits.len(), b.bits.len()); + for (a, b) in a.bits.iter().zip(b.bits.iter()) { + assert_eq!(a.get_value().unwrap(), b.get_value().unwrap()); + } + } + } + } + + #[test] + fn test_uint32_sha256_maj() { + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, + 0xbc, 0xe5, + ]); + + for _ in 0..1000 { + let mut cs = TestConstraintSystem::::new(); + + let a = rng.next_u32(); + let b = rng.next_u32(); + let c = rng.next_u32(); + + 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_eq!(b.get_value().unwrap(), expected & 1 == 1); + } + Boolean::Not(ref b) => { + assert_ne!(b.get_value().unwrap(), expected & 1 == 1); + } + Boolean::Constant(b) => { + assert_eq!(b, expected & 1 == 1); + } + } + + expected >>= 1; + } + } + } + + #[test] + fn test_uint32_sha256_ch() { + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, + 0xbc, 0xe5, + ]); + + for _ in 0..1000 { + let mut cs = TestConstraintSystem::::new(); + + let a = rng.next_u32(); + let b = rng.next_u32(); + let c = rng.next_u32(); + + 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_eq!(b.get_value().unwrap(), expected & 1 == 1); + } + Boolean::Not(ref b) => { + assert_ne!(b.get_value().unwrap(), expected & 1 == 1); + } + Boolean::Constant(b) => { + assert_eq!(b, expected & 1 == 1); + } + } + + expected >>= 1; + } + } + } + + #[test] + fn test_uint32_ripemd_d1() { + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, + 0xbc, 0xe5, + ]); + + for _ in 0..1000 { + let mut cs = TestConstraintSystem::::new(); + + let a = rng.next_u32(); + let b = rng.next_u32(); + let c = rng.next_u32(); + + let mut expected = (a & b) | ((!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::ripemd_d1(&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_eq!(b.get_value().unwrap(), expected & 1 == 1); + } + Boolean::Not(ref b) => { + assert_ne!(b.get_value().unwrap(), expected & 1 == 1); + } + Boolean::Constant(b) => { + assert_eq!(b, expected & 1 == 1); + } + } + + expected >>= 1; + } + } + } + + #[test] + fn test_uint32_ripemd_d2() { + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, + 0xbc, 0xe5, + ]); + + for _ in 0..1000 { + let mut cs = TestConstraintSystem::::new(); + + let a = rng.next_u32(); + let b = rng.next_u32(); + let c = rng.next_u32(); + + let mut expected = (a) ^ ((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::ripemd_d2(&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_eq!(b.get_value().unwrap(), expected & 1 == 1); + } + Boolean::Not(ref b) => { + assert_ne!(b.get_value().unwrap(), expected & 1 == 1); + } + Boolean::Constant(b) => { + assert_eq!(b, expected & 1 == 1); + } + } + + expected >>= 1; + } + } + } +} From 0f1028803df7551fca47b75a5d3d2cf22797f03a Mon Sep 17 00:00:00 2001 From: Shourya Goel Date: Wed, 13 Mar 2024 22:47:49 +0530 Subject: [PATCH 06/46] Update Cargo.toml --- crates/ripemd160/Cargo.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/ripemd160/Cargo.toml b/crates/ripemd160/Cargo.toml index 6498485..ae571da 100644 --- a/crates/ripemd160/Cargo.toml +++ b/crates/ripemd160/Cargo.toml @@ -6,8 +6,6 @@ license.workspace = true homepage.workspace = true repository.workspace = true -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] block = "0.1.6" block-buffer = "0.10.4" From d440d9aa653d909d5bde20c301b4f44b69594aed Mon Sep 17 00:00:00 2001 From: Sh0g0-1758 Date: Thu, 14 Mar 2024 03:04:29 +0530 Subject: [PATCH 07/46] Moved logic to Utils and ran tests --- crates/ripemd160/Cargo.toml | 11 +- crates/ripemd160/src/lib.rs | 2 +- crates/ripemd160/src/ripemd160.rs | 9 +- crates/ripemd160/src/uint32.rs | 945 ------------------------------ crates/ripemd160/src/util.rs | 412 +++++++++++++ 5 files changed, 423 insertions(+), 956 deletions(-) delete mode 100644 crates/ripemd160/src/uint32.rs create mode 100644 crates/ripemd160/src/util.rs diff --git a/crates/ripemd160/Cargo.toml b/crates/ripemd160/Cargo.toml index ae571da..b0d00c7 100644 --- a/crates/ripemd160/Cargo.toml +++ b/crates/ripemd160/Cargo.toml @@ -7,10 +7,13 @@ homepage.workspace = true repository.workspace = true [dependencies] -block = "0.1.6" -block-buffer = "0.10.4" -digest = "0.10.7" -hex-literal = "0.4.1" bellpepper-core = { workspace = true } bellpepper = { workspace = true } ff = { workspace = true } + +[dev-dependencies] +pasta_curves = { workspace = true } +hex-literal = "0.4.1" +rand_core = { workspace = true } +rand_xorshift = { workspace = true } +sha1 = "0.10.5" \ No newline at end of file diff --git a/crates/ripemd160/src/lib.rs b/crates/ripemd160/src/lib.rs index 7e5bde3..a634942 100644 --- a/crates/ripemd160/src/lib.rs +++ b/crates/ripemd160/src/lib.rs @@ -1,2 +1,2 @@ pub mod ripemd160; -pub mod uint32; +pub mod util; \ No newline at end of file diff --git a/crates/ripemd160/src/ripemd160.rs b/crates/ripemd160/src/ripemd160.rs index 6d2c583..873b636 100644 --- a/crates/ripemd160/src/ripemd160.rs +++ b/crates/ripemd160/src/ripemd160.rs @@ -3,14 +3,11 @@ //! //! [RIPEMD-160]: https://www.esat.kuleuven.be/cosic/publications/article-317.pdf -#![allow(clippy::many_single_char_names)] - +use bellpepper::gadgets::{multieq::MultiEq, uint32::UInt32}; +use bellpepper_core::{boolean::Boolean, ConstraintSystem, SynthesisError}; use ff::PrimeField; -use super::uint32::UInt32; -use bellpepper::gadgets::boolean::Boolean; -use bellpepper::gadgets::multieq::MultiEq; -use bellpepper_core::{ConstraintSystem, SynthesisError}; +use crate::util::{ripemd_d1, ripemd_d2,shl_uint32}; #[allow(clippy::unreadable_literal)] const MD_BUFFERS: [u32; 5] = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0]; diff --git a/crates/ripemd160/src/uint32.rs b/crates/ripemd160/src/uint32.rs deleted file mode 100644 index fa8de2f..0000000 --- a/crates/ripemd160/src/uint32.rs +++ /dev/null @@ -1,945 +0,0 @@ -//! Circuit representation of a [`u32`], with helpers for the [`sha256`] -//! gadgets. - -use ff::PrimeField; - -use bellpepper_core::{ConstraintSystem, LinearCombination, SynthesisError}; - -use super::boolean::{AllocatedBit, Boolean}; -use super::multieq::MultiEq; - -/// Represents an interpretation of 32 `Boolean` objects as an -/// unsigned integer. -#[derive(Clone, Debug)] -pub struct UInt32 { - // Least significant bit first - bits: Vec, - value: Option, -} - -impl UInt32 { - /// Construct a constant `UInt32` from a `u32` - pub fn constant(value: u32) -> Self { - let mut bits = Vec::with_capacity(32); - - let mut tmp = value; - for _ in 0..32 { - if tmp & 1 == 1 { - bits.push(Boolean::constant(true)) - } else { - bits.push(Boolean::constant(false)) - } - - tmp >>= 1; - } - - UInt32 { - bits, - value: Some(value), - } - } - - /// Allocate a `UInt32` in the constraint system - pub fn alloc(mut cs: CS, value: Option) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - { - let values = match value { - Some(mut val) => { - let mut v = Vec::with_capacity(32); - - for _ in 0..32 { - v.push(Some(val & 1 == 1)); - val >>= 1; - } - - v - } - None => vec![None; 32], - }; - - let bits = values - .into_iter() - .enumerate() - .map(|(i, v)| { - Ok(Boolean::from(AllocatedBit::alloc( - cs.namespace(|| format!("allocated bit {}", i)), - v, - )?)) - }) - .collect::, SynthesisError>>()?; - - Ok(UInt32 { bits, value }) - } - - pub fn into_bits_be(self) -> Vec { - let mut ret = self.bits; - ret.reverse(); - ret - } - - pub fn from_bits_be(bits: &[Boolean]) -> Self { - assert_eq!(bits.len(), 32); - - let mut value = Some(0u32); - for b in bits { - if let Some(v) = value.as_mut() { - *v <<= 1; - } - - match b.get_value() { - Some(true) => { - if let Some(v) = value.as_mut() { - *v |= 1; - } - } - Some(false) => {} - None => { - value = None; - } - } - } - - UInt32 { - value, - bits: bits.iter().rev().cloned().collect(), - } - } - - /// Turns this `UInt32` into its little-endian byte order representation. - pub fn into_bits(self) -> Vec { - self.bits - } - - /// Converts a little-endian byte order representation of bits into a - /// `UInt32`. - pub fn from_bits(bits: &[Boolean]) -> Self { - assert_eq!(bits.len(), 32); - - let new_bits = bits.to_vec(); - - let mut value = Some(0u32); - for b in new_bits.iter().rev() { - if let Some(v) = value.as_mut() { - *v <<= 1; - } - - match *b { - Boolean::Constant(b) => { - if b { - if let Some(v) = value.as_mut() { - *v |= 1; - } - } - } - Boolean::Is(ref b) => match b.get_value() { - Some(true) => { - if let Some(v) = value.as_mut() { - *v |= 1; - } - } - Some(false) => {} - None => value = None, - }, - Boolean::Not(ref b) => match b.get_value() { - Some(false) => { - if let Some(v) = value.as_mut() { - *v |= 1; - } - } - Some(true) => {} - None => value = None, - }, - } - } - - UInt32 { - value, - bits: new_bits, - } - } - - pub fn rotr(&self, by: usize) -> Self { - let by = by % 32; - - let new_bits = self - .bits - .iter() - .skip(by) - .chain(self.bits.iter()) - .take(32) - .cloned() - .collect(); - - UInt32 { - bits: new_bits, - value: self.value.map(|v| v.rotate_right(by as u32)), - } - } - - pub fn shr(&self, by: usize) -> Self { - let by = by % 32; - - let fill = Boolean::constant(false); - - let new_bits = self - .bits - .iter() // The bits are least significant first - .skip(by) // Skip the bits that will be lost during the shift - .chain(Some(&fill).into_iter().cycle()) // Rest will be zeros - .take(32) // Only 32 bits needed! - .cloned() - .collect(); - - UInt32 { - bits: new_bits, - value: self.value.map(|v| v >> by as u32), - } - } - - pub fn shl(&self, by: usize) -> Self { - let by = by % 32; - - let fill = Boolean::constant(false); - let new_bits: Vec<_> = std::iter::repeat(&fill) - .take(by) - .chain(self.bits.iter()) // The bits are least significant first - .take(32) // Only 32 bits needed! - .cloned() - .collect(); - UInt32 { - bits: new_bits, - value: self.value.map(|v| v << by as u32), - } - } - - fn triop( - mut cs: CS, - a: &Self, - b: &Self, - c: &Self, - tri_fn: F, - circuit_fn: U, - ) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - F: Fn(u32, u32, u32) -> u32, - U: Fn(&mut CS, usize, &Boolean, &Boolean, &Boolean) -> Result, - { - 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::>()?; - - Ok(UInt32 { - 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( - cs: CS, - a: &Self, - b: &Self, - c: &Self, - ) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - { - 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( - cs: CS, - a: &Self, - b: &Self, - c: &Self, - ) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - { - 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), - ) - } - pub fn ripemd_d1( - cs: CS, - a: &Self, - b: &Self, - c: &Self, - ) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - { - Self::triop( - cs, - a, - b, - c, - |a, b, c| (a & b) | (!b & c), - |cs, i, a, b, c| Boolean::ripemd160_d1(cs.namespace(|| format!("d1 {}", i)), a, b, c), - ) - } - - pub fn ripemd_d2( - cs: CS, - a: &Self, - b: &Self, - c: &Self, - ) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - { - Self::triop( - cs, - a, - b, - c, - |a, b, c| (a) ^ (b | !c), - |cs, i, a, b, c| Boolean::ripemd160_d2(cs.namespace(|| format!("d1 {}", i)), a, b, c), - ) - } - /// XOR this `UInt32` with another `UInt32` - pub fn xor(&self, mut cs: CS, other: &Self) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - { - let new_value = match (self.value, other.value) { - (Some(a), Some(b)) => Some(a ^ b), - _ => None, - }; - - let bits = self - .bits - .iter() - .zip(other.bits.iter()) - .enumerate() - .map(|(i, (a, b))| Boolean::xor(cs.namespace(|| format!("xor of bit {}", i)), a, b)) - .collect::>()?; - - Ok(UInt32 { - bits, - value: new_value, - }) - } - - /// Perform modular addition of several `UInt32` objects. - #[allow(clippy::unnecessary_unwrap)] - pub fn addmany(mut cs: M, operands: &[Self]) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - M: ConstraintSystem>, - { - // Make some arbitrary bounds for ourselves to avoid overflows - // in the scalar field - assert!(Scalar::NUM_BITS >= 64); - assert!(operands.len() >= 2); // Weird trivial cases that should never happen - assert!(operands.len() <= 10); - - // Compute the maximum value of the sum so we allocate enough bits for - // the result - let mut max_value = (operands.len() as u64) * (u64::from(u32::max_value())); - - // Keep track of the resulting value - let mut result_value = Some(0u64); - - // This is a linear combination that we will enforce to equal the - // output - let mut lc = LinearCombination::zero(); - - let mut all_constants = true; - - // Iterate over the operands - for op in operands { - // Accumulate the value - match op.value { - Some(val) => { - if let Some(v) = result_value.as_mut() { - *v += u64::from(val); - } - } - None => { - // If any of our operands have unknown value, we won't - // know the value of the result - result_value = None; - } - } - - // Iterate over each bit of the operand and add the operand to - // the linear combination - let mut coeff = Scalar::ONE; - for bit in &op.bits { - lc = lc + &bit.lc(CS::one(), coeff); - - all_constants &= bit.is_constant(); - - coeff = coeff.double(); - } - } - - // The value of the actual result is modulo 2^32 - let modular_value = result_value.map(|v| v as u32); - - if all_constants && modular_value.is_some() { - // We can just return a constant, rather than - // unpacking the result into allocated bits. - - return Ok(UInt32::constant(modular_value.unwrap())); - } - - // Storage area for the resulting bits - let mut result_bits = vec![]; - - // Linear combination representing the output, - // for comparison with the sum of the operands - let mut result_lc = LinearCombination::zero(); - - // Allocate each bit of the result - let mut coeff = Scalar::ONE; - let mut i = 0; - while max_value != 0 { - // Allocate the bit - let b = AllocatedBit::alloc( - cs.namespace(|| format!("result bit {}", i)), - result_value.map(|v| (v >> i) & 1 == 1), - )?; - - // Add this bit to the result combination - result_lc = result_lc + (coeff, b.get_variable()); - - result_bits.push(b.into()); - - max_value >>= 1; - i += 1; - coeff = coeff.double(); - } - - // Enforce equality between the sum and result - cs.get_root().enforce_equal(i, &lc, &result_lc); - - // Discard carry bits that we don't care about - result_bits.truncate(32); - - Ok(UInt32 { - bits: result_bits, - value: modular_value, - }) - } -} - -#[cfg(test)] -mod test { - use super::UInt32; - use crate::gadgets::boolean::Boolean; - use crate::gadgets::multieq::MultiEq; - use bellpepper_core::test_cs::*; - use bellpepper_core::ConstraintSystem; - use blstrs::Scalar as Fr; - use ff::Field; - use rand_core::{RngCore, SeedableRng}; - use rand_xorshift::XorShiftRng; - - #[test] - fn test_uint32_from_bits_be() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for _ in 0..1000 { - let v = (0..32) - .map(|_| Boolean::constant(rng.next_u32() % 2 != 0)) - .collect::>(); - - let b = UInt32::from_bits_be(&v); - - for (i, bit) in b.bits.iter().enumerate() { - match *bit { - Boolean::Constant(bit) => { - assert!(bit == ((b.value.unwrap() >> i) & 1 == 1)); - } - _ => unreachable!(), - } - } - - let expected_to_be_same = b.into_bits_be(); - - for x in v.iter().zip(expected_to_be_same.iter()) { - match x { - (&Boolean::Constant(true), &Boolean::Constant(true)) => {} - (&Boolean::Constant(false), &Boolean::Constant(false)) => {} - _ => unreachable!(), - } - } - } - } - - #[test] - fn test_uint32_from_bits() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for _ in 0..1000 { - let v = (0..32) - .map(|_| Boolean::constant(rng.next_u32() % 2 != 0)) - .collect::>(); - - let b = UInt32::from_bits(&v); - - for (i, bit) in b.bits.iter().enumerate() { - match *bit { - Boolean::Constant(bit) => { - assert!(bit == ((b.value.unwrap() >> i) & 1 == 1)); - } - _ => unreachable!(), - } - } - - let expected_to_be_same = b.into_bits(); - - for x in v.iter().zip(expected_to_be_same.iter()) { - match x { - (&Boolean::Constant(true), &Boolean::Constant(true)) => {} - (&Boolean::Constant(false), &Boolean::Constant(false)) => {} - _ => unreachable!(), - } - } - } - } - - #[test] - fn test_uint32_xor() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for _ in 0..1000 { - let mut cs = TestConstraintSystem::::new(); - - let a = rng.next_u32(); - let b = rng.next_u32(); - let c = rng.next_u32(); - - let mut expected = a ^ 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 = a_bit.xor(cs.namespace(|| "first xor"), &b_bit).unwrap(); - let r = r.xor(cs.namespace(|| "second xor"), &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_eq!(b.get_value().unwrap(), expected & 1 == 1); - } - Boolean::Not(ref b) => { - assert_ne!(b.get_value().unwrap(), expected & 1 == 1); - } - Boolean::Constant(b) => { - assert_eq!(b, expected & 1 == 1); - } - } - - expected >>= 1; - } - } - } - - #[test] - fn test_uint32_addmany_constants() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for _ in 0..1000 { - let mut cs = TestConstraintSystem::::new(); - - let a = rng.next_u32(); - let b = rng.next_u32(); - let c = rng.next_u32(); - - let a_bit = UInt32::constant(a); - let b_bit = UInt32::constant(b); - let c_bit = UInt32::constant(c); - - let mut expected = a.wrapping_add(b).wrapping_add(c); - - let r = { - let mut cs = MultiEq::new(&mut cs); - let r = - UInt32::addmany(cs.namespace(|| "addition"), &[a_bit, b_bit, c_bit]).unwrap(); - r - }; - - assert!(r.value == Some(expected)); - - for b in r.bits.iter() { - match *b { - Boolean::Is(_) => panic!(), - Boolean::Not(_) => panic!(), - Boolean::Constant(b) => { - assert!(b == (expected & 1 == 1)); - } - } - - expected >>= 1; - } - } - } - - #[test] - #[allow(clippy::many_single_char_names)] - fn test_uint32_addmany() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for _ in 0..1000 { - let mut cs = TestConstraintSystem::::new(); - - let a = rng.next_u32(); - let b = rng.next_u32(); - let c = rng.next_u32(); - let d = rng.next_u32(); - - let mut expected = (a ^ b).wrapping_add(c).wrapping_add(d); - - let a_bit = UInt32::alloc(cs.namespace(|| "a_bit"), Some(a)).unwrap(); - let b_bit = UInt32::constant(b); - let c_bit = UInt32::constant(c); - let d_bit = UInt32::alloc(cs.namespace(|| "d_bit"), Some(d)).unwrap(); - - let r = a_bit.xor(cs.namespace(|| "xor"), &b_bit).unwrap(); - let r = { - let mut cs = MultiEq::new(&mut cs); - UInt32::addmany(cs.namespace(|| "addition"), &[r, c_bit, d_bit]).unwrap() - }; - - assert!(cs.is_satisfied()); - - assert!(r.value == Some(expected)); - - for b in r.bits.iter() { - match *b { - Boolean::Is(ref b) => { - assert_eq!(b.get_value().unwrap(), expected & 1 == 1); - } - Boolean::Not(ref b) => { - assert_ne!(b.get_value().unwrap(), expected & 1 == 1); - } - Boolean::Constant(_) => unreachable!(), - } - - expected >>= 1; - } - - // Flip a bit and see if the addition constraint still works - if cs.get("addition/result bit 0/boolean").is_zero().into() { - cs.set("addition/result bit 0/boolean", Field::ONE); - } else { - cs.set("addition/result bit 0/boolean", Field::ZERO); - } - - assert!(!cs.is_satisfied()); - } - } - - #[test] - fn test_uint32_rotr() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - let mut num = rng.next_u32(); - - let a = UInt32::constant(num); - - for i in 0..32 { - let b = a.rotr(i); - assert_eq!(a.bits.len(), b.bits.len()); - - assert!(b.value.unwrap() == num); - - let mut tmp = num; - for b in &b.bits { - match *b { - Boolean::Constant(b) => { - assert_eq!(b, tmp & 1 == 1); - } - _ => unreachable!(), - } - - tmp >>= 1; - } - - num = num.rotate_right(1); - } - } - - #[test] - fn test_uint32_shr() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for _ in 0..50 { - for i in 0..60 { - let num = rng.next_u32(); - let a = UInt32::constant(num).shr(i); - let b = UInt32::constant(num.wrapping_shr(i as u32)); - - assert_eq!(a.value.unwrap(), num.wrapping_shr(i as u32)); - - assert_eq!(a.bits.len(), b.bits.len()); - for (a, b) in a.bits.iter().zip(b.bits.iter()) { - assert_eq!(a.get_value().unwrap(), b.get_value().unwrap()); - } - } - } - } - - #[test] - fn test_uint32_shl() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for _ in 0..50 { - for i in 0..60 { - let num = rng.next_u32(); - let a = UInt32::constant(num).shl(i); - let b = UInt32::constant(num.wrapping_shl(i as u32)); - - assert_eq!(a.value.unwrap(), num.wrapping_shl(i as u32)); - - assert_eq!(a.bits.len(), b.bits.len()); - for (a, b) in a.bits.iter().zip(b.bits.iter()) { - assert_eq!(a.get_value().unwrap(), b.get_value().unwrap()); - } - } - } - } - - #[test] - fn test_uint32_sha256_maj() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for _ in 0..1000 { - let mut cs = TestConstraintSystem::::new(); - - let a = rng.next_u32(); - let b = rng.next_u32(); - let c = rng.next_u32(); - - 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_eq!(b.get_value().unwrap(), expected & 1 == 1); - } - Boolean::Not(ref b) => { - assert_ne!(b.get_value().unwrap(), expected & 1 == 1); - } - Boolean::Constant(b) => { - assert_eq!(b, expected & 1 == 1); - } - } - - expected >>= 1; - } - } - } - - #[test] - fn test_uint32_sha256_ch() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for _ in 0..1000 { - let mut cs = TestConstraintSystem::::new(); - - let a = rng.next_u32(); - let b = rng.next_u32(); - let c = rng.next_u32(); - - 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_eq!(b.get_value().unwrap(), expected & 1 == 1); - } - Boolean::Not(ref b) => { - assert_ne!(b.get_value().unwrap(), expected & 1 == 1); - } - Boolean::Constant(b) => { - assert_eq!(b, expected & 1 == 1); - } - } - - expected >>= 1; - } - } - } - - #[test] - fn test_uint32_ripemd_d1() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for _ in 0..1000 { - let mut cs = TestConstraintSystem::::new(); - - let a = rng.next_u32(); - let b = rng.next_u32(); - let c = rng.next_u32(); - - let mut expected = (a & b) | ((!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::ripemd_d1(&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_eq!(b.get_value().unwrap(), expected & 1 == 1); - } - Boolean::Not(ref b) => { - assert_ne!(b.get_value().unwrap(), expected & 1 == 1); - } - Boolean::Constant(b) => { - assert_eq!(b, expected & 1 == 1); - } - } - - expected >>= 1; - } - } - } - - #[test] - fn test_uint32_ripemd_d2() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for _ in 0..1000 { - let mut cs = TestConstraintSystem::::new(); - - let a = rng.next_u32(); - let b = rng.next_u32(); - let c = rng.next_u32(); - - let mut expected = (a) ^ ((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::ripemd_d2(&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_eq!(b.get_value().unwrap(), expected & 1 == 1); - } - Boolean::Not(ref b) => { - assert_ne!(b.get_value().unwrap(), expected & 1 == 1); - } - Boolean::Constant(b) => { - assert_eq!(b, expected & 1 == 1); - } - } - - expected >>= 1; - } - } - } -} diff --git a/crates/ripemd160/src/util.rs b/crates/ripemd160/src/util.rs new file mode 100644 index 0000000..15d387a --- /dev/null +++ b/crates/ripemd160/src/util.rs @@ -0,0 +1,412 @@ +use bellpepper::gadgets::uint32::UInt32; +use bellpepper_core::{boolean::Boolean, ConstraintSystem, SynthesisError, boolean::AllocatedBit}; +use ff::PrimeField; + +fn ripemd160_d1<'a, Scalar, CS>( + mut cs: CS, + a: &'a Boolean, + b: &'a Boolean, + c: &'a Boolean, +) -> Result +where + Scalar: PrimeField, + CS: ConstraintSystem, +{ + let d1_value = match (a.get_value(), b.get_value(), c.get_value()) { + (Some(a), Some(b), Some(c)) => { + // (a and b) xor (a and c) xor (b and c) + Some((a & b) | (c & !b)) + } + _ => None, + }; + + match (a, b, c) { + (&Boolean::Constant(_), &Boolean::Constant(_), &Boolean::Constant(_)) => { + // They're all constants, so we can just compute the value. + + return Ok(Boolean::Constant(d1_value.expect("they're all constants"))); + } + (&Boolean::Constant(false), b, c) => { + // If a is false, + // (a and b) xor (a and c) xor (b and c) + // equals + // (b and c) + return Boolean::and(cs, &b.not(), c); + } + (_a, &Boolean::Constant(false), c) => { + // If b is false, + // (a and b) xor (a and c) xor (b and c) + // equals + // (a and c) + return Ok(c.clone()); + } + (a, b, &Boolean::Constant(false)) => { + // If c is false, + // (a and b) xor (a and c) xor (b and c) + // equals + // (a and b) + return Boolean::and(cs, a, b); + } + (a, b, &Boolean::Constant(true)) => { + // If c is true, + // (a and b) xor (a and c) xor (b and c) + // equals + // (a and b) xor (a) xor (b) + // equals + // not ((not a) and (not b)) + + return Ok(Boolean::and(cs, &a.not(), b)?.not()); + } + (a, &Boolean::Constant(true), _c) => { + // If b is true, + // (a and b) xor (a and c) xor (b and c) + // equals + // (a) xor (a and c) xor (c) + return Ok(a.clone()); + } + (&Boolean::Constant(true), b, c) => { + // If a is true, + // (a and b) xor (a and c) xor (b and c) + // equals + // (b) xor (c) xor (b and c) + + return Ok(Boolean::and(cs, &b.not(), &c.not())?.not()); + } + ( + &Boolean::Is(_) | &Boolean::Not(_), + &Boolean::Is(_) | &Boolean::Not(_), + &Boolean::Is(_) | &Boolean::Not(_), + ) => {} + } + + let d1 = cs.alloc( + || "d1", + || { + d1_value.ok_or(SynthesisError::AssignmentMissing).map(|v| { + if v { + Scalar::ONE + } else { + Scalar::ZERO + } + }) + }, + )?; + + cs.enforce( + || "d1 computation", + |_| a.lc(CS::one(), Scalar::ONE) - &c.lc(CS::one(), Scalar::ONE), + |_| b.lc(CS::one(), Scalar::ONE), + |lc| lc + d1 - &c.lc(CS::one(), Scalar::ONE), + ); + + Ok(AllocatedBit::alloc(cs.namespace(|| "d1"), d1_value).unwrap().into()) +} + + +fn ripemd160_d2<'a, Scalar, CS>( + mut cs: CS, + a: &'a Boolean, + b: &'a Boolean, + c: &'a Boolean, +) -> Result +where + Scalar: PrimeField, + CS: ConstraintSystem, +{ + let d2_value = match (a.get_value(), b.get_value(), c.get_value()) { + (Some(a), Some(b), Some(c)) => { + // (a)xor(b or not(c)) + Some((a) ^ (b | !c)) + } + _ => None, + }; + + match (a, b, c) { + (&Boolean::Constant(_), &Boolean::Constant(_), &Boolean::Constant(_)) => { + // They're all constants, so we can just compute the value. + + return Ok(Boolean::Constant(d2_value.expect("they're all constants"))); + } + (&Boolean::Constant(false), b, c) => { + // If a is false, + // (a and b) xor (a and c) xor (b and c) + // equals + // (b and c) + return Ok(Boolean::and(cs, c, &b.not())?.not()); + } + (a, &Boolean::Constant(false), c) => { + // If b is false, + // (a and b) xor (a and c) xor (b and c) + // equals + // (a and c) + + return Boolean::xor(cs, a, &c.not()); + } + (a, _b, &Boolean::Constant(false)) => { + // If c is false, + // (a and b) xor (a and c) xor (b and c) + // equals + // (a and b) + return Ok(Boolean::and(cs, a, a)?.not()); + } + (a, b, &Boolean::Constant(true)) => { + // If c is true, + // (a and b) xor (a and c) xor (b and c) + // equals + // (a and b) xor (a) xor (b) + // equals + // not ((not a) and (not b)) + + return Boolean::xor(cs, a, b); + } + (a, &Boolean::Constant(true), _c) => { + // If b is true, + // (a and b) xor (a and c) xor (b and c) + // equals + // (a) xor (a and c) xor (c) + return Ok(Boolean::and(cs, a, a)?.not()); + } + (&Boolean::Constant(true), b, c) => { + // If a is true, + // (a and b) xor (a and c) xor (b and c) + // equals + // (b) xor (c) xor (b and c) + return Boolean::and(cs, c, &b.not()); + } + ( + &Boolean::Is(_) | &Boolean::Not(_), + &Boolean::Is(_) | &Boolean::Not(_), + &Boolean::Is(_) | &Boolean::Not(_), + ) => {} + } + + let d2 = cs.alloc( + || "d2", + || { + d2_value.ok_or(SynthesisError::AssignmentMissing).map(|v| { + if v { + Scalar::ONE + } else { + Scalar::ZERO + } + }) + }, + )?; + + let notbc = Boolean::and(cs.namespace(|| "not b and c"), &b.not(), c)?; + + cs.enforce( + || "d2 computation", + |lc| lc + CS::one() - &a.lc(CS::one(), Scalar::ONE), + |lc| { + lc + CS::one() + &b.lc(CS::one(), Scalar::ONE) + - &c.lc(CS::one(), Scalar::ONE) + - ¬bc.lc(CS::one(), Scalar::ONE) + }, + |lc| lc + d2 - ¬bc.lc(CS::one(), Scalar::ONE), + ); + + Ok(AllocatedBit::alloc(cs.namespace(|| "d2"), d2_value).unwrap().into()) +} + +pub fn shl_uint32(a: &UInt32, by: usize) -> Result { + let by = by % 32; + + let fill = Boolean::constant(false); + let new_bits: Vec<_> = std::iter::repeat(&fill) + .take(by) + .chain(a.clone().into_bits().iter()) // The bits are least significant first + .take(32) // Only 32 bits needed! + .cloned() + .collect(); + Ok(UInt32::from_bits(&new_bits)) +} + +fn triop( + mut cs: CS, + a: &UInt32, + b: &UInt32, + c: &UInt32, + circuit_fn: U, +) -> Result +where + Scalar: PrimeField, + CS: ConstraintSystem, + U: Fn(&mut CS, usize, &Boolean, &Boolean, &Boolean) -> Result, +{ + let bits: Vec<_> = a + .clone() + .into_bits() + .iter() + .zip(b.clone().into_bits().iter()) + .zip(c.clone().into_bits().iter()) + .enumerate() + .map(|(i, ((a, b), c))| circuit_fn(&mut cs, i, a, b, c)) + .collect::>()?; + + Ok(UInt32::from_bits(&bits)) +} + +pub fn ripemd_d1( + cs: CS, + a: &UInt32, + b: &UInt32, + c: &UInt32, +) -> Result +where + Scalar: PrimeField, + CS: ConstraintSystem, +{ + triop( + cs, + a, + b, + c, + |cs, i, a, b, c| ripemd160_d1(cs.namespace(|| format!("d1 {}", i)), a, b, c), + ) +} + +pub fn ripemd_d2( + cs: CS, + a: &UInt32, + b: &UInt32, + c: &UInt32, +) -> Result +where + Scalar: PrimeField, + CS: ConstraintSystem, +{ + triop( + cs, + a, + b, + c, + |cs, i, a, b, c| ripemd160_d2(cs.namespace(|| format!("d2 {}", i)), a, b, c), + ) +} + +#[cfg(test)] +mod test { + + use bellpepper_core::test_cs::TestConstraintSystem; + use pasta_curves::Fp; + use rand_core::{RngCore, SeedableRng}; + use rand_xorshift::XorShiftRng; + + use super::*; + + #[test] + fn test_uint32_shl() { + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, + 0xbc, 0xe5, + ]); + for _ in 0..50 { + for i in 0..60 { + let mut cs = TestConstraintSystem::::new(); + let num = rng.next_u32(); + let mut expected = num.wrapping_shl(i as u32); + let num_bit = UInt32::alloc(cs.namespace(|| "num_bit"), Some(num)).unwrap(); + let res = shl_uint32(&num_bit,i).unwrap(); + for b in res.into_bits() { + match b { + Boolean::Is(ref b) => { + assert_eq!(b.get_value().unwrap(), expected & 1 == 1); + } + Boolean::Not(ref b) => { + assert_ne!(b.get_value().unwrap(), expected & 1 == 1); + } + Boolean::Constant(b) => { + assert_eq!(b, expected & 1 == 1); + } + } + + expected >>= 1; + } + } + } + } + + #[test] + fn test_uint32_ripemd_d1() { + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, + 0xbc, 0xe5, + ]); + + for _ in 0..1000 { + let mut cs = TestConstraintSystem::::new(); + + let a = rng.next_u32(); + let b = rng.next_u32(); + let c = rng.next_u32(); + + let mut expected = (a & b) | ((!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 = ripemd_d1(&mut cs, &a_bit, &b_bit, &c_bit).unwrap(); + + assert!(cs.is_satisfied()); + + for b in r.into_bits().iter() { + match *b { + Boolean::Is(ref b) => { + assert_eq!(b.get_value().unwrap(), expected & 1 == 1); + } + Boolean::Not(ref b) => { + assert_ne!(b.get_value().unwrap(), expected & 1 == 1); + } + Boolean::Constant(b) => { + assert_eq!(b, expected & 1 == 1); + } + } + + expected >>= 1; + } + } + } + #[test] + fn test_uint32_ripemd_d2() { + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, + 0xbc, 0xe5, + ]); + + for _ in 0..1000 { + let mut cs = TestConstraintSystem::::new(); + + let a = rng.next_u32(); + let b = rng.next_u32(); + let c = rng.next_u32(); + + let mut expected = (a) ^ ((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 = ripemd_d2(&mut cs, &a_bit, &b_bit, &c_bit).unwrap(); + + assert!(cs.is_satisfied()); + + for b in r.into_bits().iter() { + match *b { + Boolean::Is(ref b) => { + assert_eq!(b.get_value().unwrap(), expected & 1 == 1); + } + Boolean::Not(ref b) => { + assert_ne!(b.get_value().unwrap(), expected & 1 == 1); + } + Boolean::Constant(b) => { + assert_eq!(b, expected & 1 == 1); + } + } + + expected >>= 1; + } + } + } +} \ No newline at end of file From d6aa61e15f997473a387c615ccd048c91bd3538e Mon Sep 17 00:00:00 2001 From: Sh0g0-1758 Date: Thu, 14 Mar 2024 03:05:55 +0530 Subject: [PATCH 08/46] Added space --- crates/ripemd160/Cargo.toml | 2 +- crates/ripemd160/src/lib.rs | 2 +- crates/ripemd160/src/util.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/ripemd160/Cargo.toml b/crates/ripemd160/Cargo.toml index b0d00c7..bc9d936 100644 --- a/crates/ripemd160/Cargo.toml +++ b/crates/ripemd160/Cargo.toml @@ -16,4 +16,4 @@ pasta_curves = { workspace = true } hex-literal = "0.4.1" rand_core = { workspace = true } rand_xorshift = { workspace = true } -sha1 = "0.10.5" \ No newline at end of file +sha1 = "0.10.5" diff --git a/crates/ripemd160/src/lib.rs b/crates/ripemd160/src/lib.rs index a634942..3e11f9a 100644 --- a/crates/ripemd160/src/lib.rs +++ b/crates/ripemd160/src/lib.rs @@ -1,2 +1,2 @@ pub mod ripemd160; -pub mod util; \ No newline at end of file +pub mod util; diff --git a/crates/ripemd160/src/util.rs b/crates/ripemd160/src/util.rs index 15d387a..2f77661 100644 --- a/crates/ripemd160/src/util.rs +++ b/crates/ripemd160/src/util.rs @@ -409,4 +409,4 @@ mod test { } } } -} \ No newline at end of file +} From ebc66d283b9593a692f94b38df92c98b67c4883b Mon Sep 17 00:00:00 2001 From: Sh0g0-1758 Date: Thu, 14 Mar 2024 03:11:40 +0530 Subject: [PATCH 09/46] Ran fmt --- crates/ripemd160/src/ripemd160.rs | 16 +++--- crates/ripemd160/src/util.rs | 83 +++++++++++++++---------------- 2 files changed, 47 insertions(+), 52 deletions(-) diff --git a/crates/ripemd160/src/ripemd160.rs b/crates/ripemd160/src/ripemd160.rs index 873b636..c3a8433 100644 --- a/crates/ripemd160/src/ripemd160.rs +++ b/crates/ripemd160/src/ripemd160.rs @@ -7,7 +7,7 @@ use bellpepper::gadgets::{multieq::MultiEq, uint32::UInt32}; use bellpepper_core::{boolean::Boolean, ConstraintSystem, SynthesisError}; use ff::PrimeField; -use crate::util::{ripemd_d1, ripemd_d2,shl_uint32}; +use crate::util::{ripemd_d1, ripemd_d2, shl_uint32}; #[allow(clippy::unreadable_literal)] const MD_BUFFERS: [u32; 5] = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0]; @@ -40,7 +40,7 @@ where for b in (0..64).rev().map(|i| (plen >> i) & 1 == 1) { padded.push(Boolean::constant(b)); } - assert!(padded.len() % 512 == 0); + assert!(padded.len() % 512 == 0); let mut cur_md = get_ripemd160_md("md"); let mut cur_md_prime = get_ripemd160_md("md_prime"); for (i, block) in padded.chunks(512).enumerate() { @@ -53,13 +53,13 @@ where )?; let mut update_md = cur_md.clone(); for i in 0..5 { - update_md[(i+4)%5] = prev_md[i].xor( + update_md[(i + 4) % 5] = prev_md[i].xor( cs.namespace(|| format!("first xor {}", i)), - &cur_md[(i+1)%5], + &cur_md[(i + 1) % 5], )?; - update_md[(i+4)%5] = update_md[(i+4)%5].xor( + update_md[(i + 4) % 5] = update_md[(i + 4) % 5].xor( cs.namespace(|| format!("second xor {}", i)), - &cur_md_prime[(i+2)%5], + &cur_md_prime[(i + 2) % 5], )?; } cur_md = update_md; @@ -153,8 +153,8 @@ where ¤t_md_value[1], ¤t_md_value[2], )?; - s_val=[11,13,6,7,14,9,13,15,14,8,13,6,5,12,7,5]; - i_val=[3,10,14,4,9,15,8,1,2,7,0,6,13,11,5,12]; + s_val = [11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5]; + i_val = [3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12]; for i in 0..16 { let mut tmp1 = current_md_value[0] .xor(cs.namespace(|| format!("first xor {}", i)), &f)? diff --git a/crates/ripemd160/src/util.rs b/crates/ripemd160/src/util.rs index 2f77661..16c18ed 100644 --- a/crates/ripemd160/src/util.rs +++ b/crates/ripemd160/src/util.rs @@ -1,5 +1,5 @@ use bellpepper::gadgets::uint32::UInt32; -use bellpepper_core::{boolean::Boolean, ConstraintSystem, SynthesisError, boolean::AllocatedBit}; +use bellpepper_core::{boolean::AllocatedBit, boolean::Boolean, ConstraintSystem, SynthesisError}; use ff::PrimeField; fn ripemd160_d1<'a, Scalar, CS>( @@ -99,10 +99,11 @@ where |lc| lc + d1 - &c.lc(CS::one(), Scalar::ONE), ); - Ok(AllocatedBit::alloc(cs.namespace(|| "d1"), d1_value).unwrap().into()) + Ok(AllocatedBit::alloc(cs.namespace(|| "d1"), d1_value) + .unwrap() + .into()) } - fn ripemd160_d2<'a, Scalar, CS>( mut cs: CS, a: &'a Boolean, @@ -206,7 +207,9 @@ where |lc| lc + d2 - ¬bc.lc(CS::one(), Scalar::ONE), ); - Ok(AllocatedBit::alloc(cs.namespace(|| "d2"), d2_value).unwrap().into()) + Ok(AllocatedBit::alloc(cs.namespace(|| "d2"), d2_value) + .unwrap() + .into()) } pub fn shl_uint32(a: &UInt32, by: usize) -> Result { @@ -235,14 +238,14 @@ where U: Fn(&mut CS, usize, &Boolean, &Boolean, &Boolean) -> Result, { let bits: Vec<_> = a - .clone() - .into_bits() - .iter() - .zip(b.clone().into_bits().iter()) - .zip(c.clone().into_bits().iter()) - .enumerate() - .map(|(i, ((a, b), c))| circuit_fn(&mut cs, i, a, b, c)) - .collect::>()?; + .clone() + .into_bits() + .iter() + .zip(b.clone().into_bits().iter()) + .zip(c.clone().into_bits().iter()) + .enumerate() + .map(|(i, ((a, b), c))| circuit_fn(&mut cs, i, a, b, c)) + .collect::>()?; Ok(UInt32::from_bits(&bits)) } @@ -257,13 +260,9 @@ where Scalar: PrimeField, CS: ConstraintSystem, { - triop( - cs, - a, - b, - c, - |cs, i, a, b, c| ripemd160_d1(cs.namespace(|| format!("d1 {}", i)), a, b, c), - ) + triop(cs, a, b, c, |cs, i, a, b, c| { + ripemd160_d1(cs.namespace(|| format!("d1 {}", i)), a, b, c) + }) } pub fn ripemd_d2( @@ -276,13 +275,9 @@ where Scalar: PrimeField, CS: ConstraintSystem, { - triop( - cs, - a, - b, - c, - |cs, i, a, b, c| ripemd160_d2(cs.namespace(|| format!("d2 {}", i)), a, b, c), - ) + triop(cs, a, b, c, |cs, i, a, b, c| { + ripemd160_d2(cs.namespace(|| format!("d2 {}", i)), a, b, c) + }) } #[cfg(test)] @@ -307,7 +302,7 @@ mod test { let num = rng.next_u32(); let mut expected = num.wrapping_shl(i as u32); let num_bit = UInt32::alloc(cs.namespace(|| "num_bit"), Some(num)).unwrap(); - let res = shl_uint32(&num_bit,i).unwrap(); + let res = shl_uint32(&num_bit, i).unwrap(); for b in res.into_bits() { match b { Boolean::Is(ref b) => { @@ -320,7 +315,7 @@ mod test { assert_eq!(b, expected & 1 == 1); } } - + expected >>= 1; } } @@ -333,24 +328,24 @@ mod test { 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, ]); - + for _ in 0..1000 { let mut cs = TestConstraintSystem::::new(); - + let a = rng.next_u32(); let b = rng.next_u32(); let c = rng.next_u32(); - + let mut expected = (a & b) | ((!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 = ripemd_d1(&mut cs, &a_bit, &b_bit, &c_bit).unwrap(); - + assert!(cs.is_satisfied()); - + for b in r.into_bits().iter() { match *b { Boolean::Is(ref b) => { @@ -363,7 +358,7 @@ mod test { assert_eq!(b, expected & 1 == 1); } } - + expected >>= 1; } } @@ -374,24 +369,24 @@ mod test { 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, ]); - + for _ in 0..1000 { let mut cs = TestConstraintSystem::::new(); - + let a = rng.next_u32(); let b = rng.next_u32(); let c = rng.next_u32(); - + let mut expected = (a) ^ ((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 = ripemd_d2(&mut cs, &a_bit, &b_bit, &c_bit).unwrap(); - + assert!(cs.is_satisfied()); - + for b in r.into_bits().iter() { match *b { Boolean::Is(ref b) => { @@ -404,7 +399,7 @@ mod test { assert_eq!(b, expected & 1 == 1); } } - + expected >>= 1; } } From 4719b849c96014b5cdbab28e11245695f0571d3c Mon Sep 17 00:00:00 2001 From: Sh0g0-1758 Date: Thu, 14 Mar 2024 23:05:20 +0530 Subject: [PATCH 10/46] Modifications as code changed to util --- crates/ripemd160/src/lib.rs | 2 +- crates/ripemd160/src/ripemd160.rs | 166 +++++++++--------------------- 2 files changed, 51 insertions(+), 117 deletions(-) diff --git a/crates/ripemd160/src/lib.rs b/crates/ripemd160/src/lib.rs index 3e11f9a..a634942 100644 --- a/crates/ripemd160/src/lib.rs +++ b/crates/ripemd160/src/lib.rs @@ -1,2 +1,2 @@ pub mod ripemd160; -pub mod util; +pub mod util; \ No newline at end of file diff --git a/crates/ripemd160/src/ripemd160.rs b/crates/ripemd160/src/ripemd160.rs index c3a8433..a68e059 100644 --- a/crates/ripemd160/src/ripemd160.rs +++ b/crates/ripemd160/src/ripemd160.rs @@ -107,19 +107,19 @@ where cs.namespace(|| format!("third xor {}", i)), &UInt32::constant(K_BUFFER[0]), )?; - tmp1 = current_md_value[0].shl(s_val[i]); - tmp1 = current_md_value[0].xor( + tmp1 = shl_uint32(&tmp1, s_val[i]).unwrap(); + tmp1 = tmp1.xor( cs.namespace(|| format!("fourth xor {}", i)), ¤t_md_value[4], )?; - let tmp2 = current_md_value[2].shl(10); + let tmp2 = shl_uint32(¤t_md_value[2], 10); current_md_value[0] = current_md_value[4].clone(); current_md_value[2] = current_md_value[1].clone(); current_md_value[4] = current_md_value[3].clone(); current_md_value[1] = tmp1.clone(); - current_md_value[3] = tmp2.clone(); + current_md_value[3] = tmp2.unwrap().clone(); } - f = UInt32::ripemd_d1( + f = ripemd_d1( cs.namespace(|| "d1"), ¤t_md_value[3], ¤t_md_value[1], @@ -135,19 +135,19 @@ where cs.namespace(|| format!("third xor {}", i)), &UInt32::constant(K_BUFFER[1]), )?; - tmp1 = current_md_value[0].shl(s_val[i]); - tmp1 = current_md_value[0].xor( + tmp1 = shl_uint32(&tmp1, s_val[i]).unwrap(); + tmp1 = tmp1.xor( cs.namespace(|| format!("fourth xor {}", i)), ¤t_md_value[4], )?; - let tmp2 = current_md_value[2].shl(10); + let tmp2 = shl_uint32(¤t_md_value[2],10); current_md_value[0] = current_md_value[4].clone(); current_md_value[2] = current_md_value[1].clone(); current_md_value[4] = current_md_value[3].clone(); current_md_value[1] = tmp1.clone(); - current_md_value[3] = tmp2.clone(); + current_md_value[3] = tmp2.unwrap().clone(); } - f = UInt32::ripemd_d2( + f = ripemd_d2( cs.namespace(|| "d2"), ¤t_md_value[3], ¤t_md_value[1], @@ -163,19 +163,19 @@ where cs.namespace(|| format!("third xor {}", i)), &UInt32::constant(K_BUFFER[2]), )?; - tmp1 = current_md_value[0].shl(s_val[i]); - tmp1 = current_md_value[0].xor( + tmp1 = shl_uint32(&tmp1, s_val[i]).unwrap(); + tmp1 = tmp1.xor( cs.namespace(|| format!("fourth xor {}", i)), ¤t_md_value[4], )?; - let tmp2 = current_md_value[2].shl(10); + let tmp2 = shl_uint32(¤t_md_value[2], 10); current_md_value[0] = current_md_value[4].clone(); current_md_value[2] = current_md_value[1].clone(); current_md_value[4] = current_md_value[3].clone(); current_md_value[1] = tmp1.clone(); - current_md_value[3] = tmp2.clone(); + current_md_value[3] = tmp2.unwrap().clone(); } - f = UInt32::ripemd_d1( + f = ripemd_d1( cs.namespace(|| "d1"), ¤t_md_value[1], ¤t_md_value[3], @@ -191,19 +191,19 @@ where cs.namespace(|| format!("third xor {}", i)), &UInt32::constant(K_BUFFER[3]), )?; - tmp1 = current_md_value[0].shl(s_val[i]); - tmp1 = current_md_value[0].xor( + tmp1 = shl_uint32(&tmp1, s_val[i]).unwrap(); + tmp1 = tmp1.xor( cs.namespace(|| format!("fourth xor {}", i)), ¤t_md_value[4], )?; - let tmp2 = current_md_value[2].shl(10); + let tmp2 = shl_uint32(¤t_md_value[2], 10); current_md_value[0] = current_md_value[4].clone(); current_md_value[2] = current_md_value[1].clone(); current_md_value[4] = current_md_value[3].clone(); current_md_value[1] = tmp1.clone(); - current_md_value[3] = tmp2.clone(); + current_md_value[3] = tmp2.unwrap().clone(); } - f = UInt32::ripemd_d2( + f = ripemd_d2( cs.namespace(|| "d2"), ¤t_md_value[1], ¤t_md_value[2], @@ -217,19 +217,19 @@ where .xor(cs.namespace(|| format!("second xor {}", i)), &w[i_val[i]])? .xor( cs.namespace(|| format!("third xor {}", i)), - &UInt32::constant(K_BUFFER[3]), + &UInt32::constant(K_BUFFER[4]), )?; - tmp1 = current_md_value[0].shl(s_val[i]); - tmp1 = current_md_value[0].xor( + tmp1 = shl_uint32(&tmp1, s_val[i]).unwrap(); + tmp1 = tmp1.xor( cs.namespace(|| format!("fourth xor {}", i)), ¤t_md_value[4], )?; - let tmp2 = current_md_value[2].shl(10); + let tmp2 = shl_uint32(¤t_md_value[2], 10); current_md_value[0] = current_md_value[4].clone(); current_md_value[2] = current_md_value[1].clone(); current_md_value[4] = current_md_value[3].clone(); current_md_value[1] = tmp1.clone(); - current_md_value[3] = tmp2.clone(); + current_md_value[3] = tmp2.unwrap().clone(); } Ok(vec![ current_md_value[0].clone(), @@ -251,12 +251,12 @@ where { assert_eq!(input.len(), 512); assert_eq!(current_md_value.len(), 5); - let mut w = input + let w = input .chunks(32) .map(UInt32::from_bits_be) .collect::>(); let mut cs = MultiEq::new(cs); - let mut f = UInt32::ripemd_d2( + let mut f = ripemd_d2( cs.namespace(|| "d2"), ¤t_md_value[1], ¤t_md_value[2], @@ -272,19 +272,19 @@ where cs.namespace(|| format!("third xor {}", i)), &UInt32::constant(K_BUFFER_PRIME[0]), )?; - tmp1 = current_md_value[0].shl(s_val[i]); - tmp1 = current_md_value[0].xor( + tmp1 = shl_uint32(&tmp1, s_val[i]).unwrap(); + tmp1 = tmp1.xor( cs.namespace(|| format!("fourth xor {}", i)), ¤t_md_value[4], )?; - let tmp2 = current_md_value[2].shl(10); + let tmp2 = shl_uint32(¤t_md_value[2], 10); current_md_value[0] = current_md_value[4].clone(); current_md_value[2] = current_md_value[1].clone(); current_md_value[4] = current_md_value[3].clone(); current_md_value[1] = tmp1.clone(); - current_md_value[3] = tmp2.clone(); + current_md_value[3] = tmp2.unwrap().clone() } - f = UInt32::ripemd_d1( + f = ripemd_d1( cs.namespace(|| "d1"), ¤t_md_value[1], ¤t_md_value[3], @@ -300,19 +300,19 @@ where cs.namespace(|| format!("third xor {}", i)), &UInt32::constant(K_BUFFER_PRIME[1]), )?; - tmp1 = current_md_value[0].shl(s_val[i]); - tmp1 = current_md_value[0].xor( + tmp1 = shl_uint32(&tmp1, s_val[i]).unwrap(); + tmp1 = tmp1.xor( cs.namespace(|| format!("fourth xor {}", i)), ¤t_md_value[4], )?; - let tmp2 = current_md_value[2].shl(10); + let tmp2 = shl_uint32(¤t_md_value[2], 10); current_md_value[0] = current_md_value[4].clone(); current_md_value[2] = current_md_value[1].clone(); current_md_value[4] = current_md_value[3].clone(); current_md_value[1] = tmp1.clone(); - current_md_value[3] = tmp2.clone(); + current_md_value[3] = tmp2.unwrap().clone() } - f = UInt32::ripemd_d2( + f = ripemd_d2( cs.namespace(|| "d2"), ¤t_md_value[3], ¤t_md_value[1], @@ -328,19 +328,19 @@ where cs.namespace(|| format!("third xor {}", i)), &UInt32::constant(K_BUFFER_PRIME[2]), )?; - tmp1 = current_md_value[0].shl(s_val[i]); - tmp1 = current_md_value[0].xor( + tmp1 = shl_uint32(&tmp1, s_val[i]).unwrap(); + tmp1 = tmp1.xor( cs.namespace(|| format!("fourth xor {}", i)), ¤t_md_value[4], )?; - let tmp2 = current_md_value[2].shl(10); + let tmp2 = shl_uint32(¤t_md_value[2], 10); current_md_value[0] = current_md_value[4].clone(); current_md_value[2] = current_md_value[1].clone(); current_md_value[4] = current_md_value[3].clone(); current_md_value[1] = tmp1.clone(); - current_md_value[3] = tmp2.clone(); + current_md_value[3] = tmp2.unwrap().clone() } - f = UInt32::ripemd_d1( + f = ripemd_d1( cs.namespace(|| "d1"), ¤t_md_value[2], ¤t_md_value[1], @@ -356,17 +356,17 @@ where cs.namespace(|| format!("third xor {}", i)), &UInt32::constant(K_BUFFER_PRIME[3]), )?; - tmp1 = current_md_value[0].shl(s_val[i]); - tmp1 = current_md_value[0].xor( + tmp1 = shl_uint32(&tmp1, s_val[i]).unwrap(); + tmp1 = tmp1.xor( cs.namespace(|| format!("fourth xor {}", i)), ¤t_md_value[4], )?; - let tmp2 = current_md_value[2].shl(10); + let tmp2 = shl_uint32(¤t_md_value[2], 10); current_md_value[0] = current_md_value[4].clone(); current_md_value[2] = current_md_value[1].clone(); current_md_value[4] = current_md_value[3].clone(); current_md_value[1] = tmp1.clone(); - current_md_value[3] = tmp2.clone(); + current_md_value[3] = tmp2.unwrap().clone() } f = current_md_value[1] .xor(cs.namespace(|| "first xor"), ¤t_md_value[2])? @@ -381,17 +381,17 @@ where cs.namespace(|| format!("third xor {}", i)), &UInt32::constant(K_BUFFER_PRIME[4]), )?; - tmp1 = current_md_value[0].shl(s_val[i]); - tmp1 = current_md_value[0].xor( + tmp1 = shl_uint32(&tmp1, s_val[i]).unwrap(); + tmp1 = tmp1.xor( cs.namespace(|| format!("fourth xor {}", i)), ¤t_md_value[4], )?; - let tmp2 = current_md_value[2].shl(10); + let tmp2 = shl_uint32(¤t_md_value[2], 10); current_md_value[0] = current_md_value[4].clone(); current_md_value[2] = current_md_value[1].clone(); current_md_value[4] = current_md_value[3].clone(); current_md_value[1] = tmp1.clone(); - current_md_value[3] = tmp2.clone(); + current_md_value[3] = tmp2.unwrap().clone() } Ok(vec![ current_md_value[0].clone(), @@ -402,69 +402,3 @@ where ]) } -#[cfg(test)] -mod test { - use super::*; - use crate::gadgets::boolean::AllocatedBit; - use bellpepper_core::test_cs::*; - use blstrs::Scalar as Fr; - use hex_literal::hex; - use rand_core::{RngCore, SeedableRng}; - use rand_xorshift::XorShiftRng; - - #[test] - #[allow(clippy::needless_collect)] - fn test_blank_hash() { - let mut cur_md = get_ripemd160_md("md"); - let mut cur_md_prime = get_ripemd160_md("md_prime"); - - let mut cs = TestConstraintSystem::::new(); - let mut input_bits: Vec<_> = (0..512).map(|_| Boolean::Constant(false)).collect(); - input_bits[0] = Boolean::Constant(true); - let prev_md = cur_md.clone(); - cur_md = ripemd160_func_block(&mut cs, &input_bits, &mut cur_md).unwrap(); - cur_md_prime = ripemd160_func_block_prime(&mut cs, &input_bits, &mut cur_md_prime).unwrap(); - let mut update_md = cur_md.clone(); - for i in 0..5 { - match prev_md[i].xor( - cs.namespace(|| format!("first xor {}", i)), - &cur_md[(i + 1) % 5], - ) { - Ok(result) => { - update_md[(i + 4) % 5] = result; - } - Err(err) => { - // Handle the error here - } - } - match update_md[(i + 4) % 5].xor( - cs.namespace(|| format!("first xor {}", i)), - &cur_md[(i + 2) % 5], - ) { - Ok(result) => { - update_md[(i + 4) % 5] = result; - } - Err(err) => { - // Handle the error here - } - } - } - cur_md = update_md; - cur_md_prime = cur_md.clone(); - // let out_bits : Vec<_> = cur_md.into_iter().flat_map(|e| e.into_bits_be()).collect(); - - assert!(cs.is_satisfied()); - assert_eq!(cs.num_constraints(), 0); - - // let expected = hex!("9c1185a5c5e9fc54612808977ee8f548b2258d31"); - - // let out = out_bits; - // for b in expected.iter() { - // for i in (0..8).rev() { - // let c = out.next().unwrap().get_value().unwrap(); - - // assert_eq!(c, (b >> i) & 1u8 == 1u8); - // } - // } - } -} From 007865cae5a5da4b3f97e492bb064b8b773d7517 Mon Sep 17 00:00:00 2001 From: Sh0g0-1758 Date: Fri, 15 Mar 2024 01:47:48 +0530 Subject: [PATCH 11/46] Made code less verbose --- crates/ripemd160/src/lib.rs | 2 +- crates/ripemd160/src/ripemd160.rs | 604 ++++++++++++++++-------------- 2 files changed, 325 insertions(+), 281 deletions(-) diff --git a/crates/ripemd160/src/lib.rs b/crates/ripemd160/src/lib.rs index a634942..3e11f9a 100644 --- a/crates/ripemd160/src/lib.rs +++ b/crates/ripemd160/src/lib.rs @@ -1,2 +1,2 @@ pub mod ripemd160; -pub mod util; \ No newline at end of file +pub mod util; diff --git a/crates/ripemd160/src/ripemd160.rs b/crates/ripemd160/src/ripemd160.rs index a68e059..0f9b1ef 100644 --- a/crates/ripemd160/src/ripemd160.rs +++ b/crates/ripemd160/src/ripemd160.rs @@ -6,6 +6,7 @@ use bellpepper::gadgets::{multieq::MultiEq, uint32::UInt32}; use bellpepper_core::{boolean::Boolean, ConstraintSystem, SynthesisError}; use ff::PrimeField; +use std::convert::TryInto; use crate::util::{ripemd_d1, ripemd_d2, shl_uint32}; @@ -22,9 +23,9 @@ const K_BUFFER: [u32; 5] = [0x00000000, 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xa9 const K_BUFFER_PRIME: [u32; 5] = [0x50a28be6, 0x5c4dd124, 0x6d703ef3, 0x7a6d76e9, 0x00000000]; pub fn ripemd160( - mut cs: CS, + cs: CS, input: &[Boolean], -) -> Result, bellpepper_core::SynthesisError> +) -> Result<[UInt32; 5], bellpepper_core::SynthesisError> where Scalar: PrimeField, CS: ConstraintSystem, @@ -43,47 +44,219 @@ where assert!(padded.len() % 512 == 0); let mut cur_md = get_ripemd160_md("md"); let mut cur_md_prime = get_ripemd160_md("md_prime"); + let mut cs = MultiEq::new(cs); for (i, block) in padded.chunks(512).enumerate() { let prev_md = cur_md.clone(); - cur_md = ripemd160_func_block(cs.namespace(|| format!("block {}", i)), block, &mut cur_md)?; - cur_md_prime = ripemd160_func_block_prime( - cs.namespace(|| format!("block_prime {}", i)), + println!("block {}", i); + cur_md = left_step( + cs.namespace(|| format!("left_step {}", i)), + block, + &mut cur_md, + )?; + cur_md_prime = right_step( + cs.namespace(|| format!("right_step {}", i)), block, &mut cur_md_prime, )?; - let mut update_md = cur_md.clone(); - for i in 0..5 { - update_md[(i + 4) % 5] = prev_md[i].xor( - cs.namespace(|| format!("first xor {}", i)), - &cur_md[(i + 1) % 5], - )?; - update_md[(i + 4) % 5] = update_md[(i + 4) % 5].xor( - cs.namespace(|| format!("second xor {}", i)), - &cur_md_prime[(i + 2) % 5], - )?; - } - cur_md = update_md; + cur_md = combine_left_and_right( + cs.namespace(|| format!("combine_left_and_right_step {}", i)), + cur_md, + cur_md_prime, + prev_md, + ) + .unwrap(); cur_md_prime = cur_md.clone(); } - Ok(cur_md.into_iter().flat_map(|e| e.into_bits_be()).collect()) + let array_data: Result<[UInt32; 5], _> = cur_md.try_into(); + Ok(array_data.unwrap()) } -fn get_ripemd160_md(input: &str) -> Vec { +fn combine_left_and_right( + cs: CS, + cur_md: [UInt32; 5], + cur_md_prime: [UInt32; 5], + prev_md: [UInt32; 5], +) -> Result<[UInt32; 5], bellpepper_core::SynthesisError> +where + Scalar: PrimeField, + CS: ConstraintSystem, +{ + let mut cs = MultiEq::new(cs); + let mut update_md = cur_md.clone(); + for i in 0..5 { + update_md[(i + 4) % 5] = prev_md[i].xor( + cs.namespace(|| format!("first xor {}", i)), + &cur_md[(i + 1) % 5], + )?; + update_md[(i + 4) % 5] = update_md[(i + 4) % 5].xor( + cs.namespace(|| format!("second xor {}", i)), + &cur_md_prime[(i + 2) % 5], + )?; + } + Ok(update_md) +} + +fn get_ripemd160_md(input: &str) -> [UInt32; 5] { match input { - "md" => MD_BUFFERS.iter().map(|&v| UInt32::constant(v)).collect(), - "md_prime" => MD_BUFFERS_PRIME - .iter() - .map(|&v| UInt32::constant(v)) - .collect(), + "md" => MD_BUFFERS.map(UInt32::constant), + "md_prime" => MD_BUFFERS_PRIME.map(UInt32::constant), _ => panic!("Invalid input"), } } -pub fn ripemd160_func_block( +fn block( + cs: CS, + md_val: &mut [UInt32; 5], + s_val: [usize; 16], + i_val: [usize; 16], + w: Vec, + index: usize, + left: bool, +) -> Result<[UInt32; 5], SynthesisError> +where + Scalar: PrimeField, + CS: ConstraintSystem, +{ + let mut cs = MultiEq::new(cs); + let f: UInt32; + match left { + true => match index { + 0 => { + f = md_val[1] + .xor( + cs.namespace(|| format!("first xor block {} left {} ", index, left)), + &md_val[2], + )? + .xor( + cs.namespace(|| format!("second xor block {} left {}", index, left)), + &md_val[3], + )?; + } + 1 => { + f = ripemd_d1( + cs.namespace(|| format!("d1 block {} left {}", index, left)), + &md_val[3], + &md_val[1], + &md_val[2], + )?; + } + 2 => { + f = ripemd_d2( + cs.namespace(|| format!("d2 block {} left {}", index, left)), + &md_val[3], + &md_val[1], + &md_val[2], + )?; + } + 3 => { + f = ripemd_d1( + cs.namespace(|| format!("d1 block {} left {}", index, left)), + &md_val[1], + &md_val[3], + &md_val[2], + )?; + } + 4 => { + f = ripemd_d2( + cs.namespace(|| format!("d2 block {} left {}", index, left)), + &md_val[1], + &md_val[2], + &md_val[3], + )?; + } + _ => panic!("Invalid index"), + }, + false => match index { + 0 => { + f = ripemd_d2( + cs.namespace(|| format!("d2 block {} left {}", index, left)), + &md_val[1], + &md_val[2], + &md_val[3], + )?; + } + 1 => { + f = ripemd_d1( + cs.namespace(|| format!("d1 block {} left {}", index, left)), + &md_val[1], + &md_val[3], + &md_val[2], + )?; + } + 2 => { + f = ripemd_d2( + cs.namespace(|| format!("d2 block {} left {}", index, left)), + &md_val[3], + &md_val[1], + &md_val[2], + )?; + } + 3 => { + f = ripemd_d1( + cs.namespace(|| format!("d1 block {} left {}", index, left)), + &md_val[2], + &md_val[1], + &md_val[3], + )?; + } + 4 => { + f = md_val[1] + .xor( + cs.namespace(|| format!("first xor block {} left {}", index, left)), + &md_val[2], + )? + .xor( + cs.namespace(|| format!("second xor block {} left {}", index, left)), + &md_val[3], + )?; + } + _ => panic!("Invalid index"), + }, + } + let k_val = match left { + true => K_BUFFER, + false => K_BUFFER_PRIME, + }; + for i in 0..16 { + let mut tmp1 = md_val[0] + .xor( + cs.namespace(|| format!("first xor block {} left {} index {}", index, left,i)), + &f, + )? + .xor( + cs.namespace(|| format!("second xor block {} left {} index {}", index, left,i)), + &w[i_val[i]], + )? + .xor( + cs.namespace(|| format!("third xor block {} left {} index {}", index, left,i)), + &UInt32::constant(k_val[index]), + )?; + tmp1 = shl_uint32(&tmp1, s_val[i]).unwrap(); + tmp1 = tmp1.xor( + cs.namespace(|| format!("fourth xor block {} left {} index {}", index, left, i)), + &md_val[4], + )?; + let tmp2 = shl_uint32(&md_val[2], 10); + md_val[0] = md_val[4].clone(); + md_val[2] = md_val[1].clone(); + md_val[4] = md_val[3].clone(); + md_val[1] = tmp1.clone(); + md_val[3] = tmp2.unwrap().clone(); + } + Ok([ + md_val[0].clone(), + md_val[1].clone(), + md_val[2].clone(), + md_val[3].clone(), + md_val[4].clone(), + ]) +} + +pub fn left_step( cs: CS, input: &[Boolean], - current_md_value: &mut [UInt32], -) -> Result, SynthesisError> + current_md_value: &mut [UInt32; 5], +) -> Result<[UInt32; 5], SynthesisError> where Scalar: PrimeField, CS: ConstraintSystem, @@ -95,143 +268,62 @@ where .map(UInt32::from_bits_be) .collect::>(); let mut cs = MultiEq::new(cs); - let mut f = current_md_value[1] - .xor(cs.namespace(|| "first xor"), ¤t_md_value[2])? - .xor(cs.namespace(|| "second xor"), ¤t_md_value[3])?; let mut s_val = [11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8]; - for i in 0..16 { - let mut tmp1 = current_md_value[0] - .xor(cs.namespace(|| format!("first xor {}", i)), &f)? - .xor(cs.namespace(|| format!("second xor {}", i)), &w[i])? - .xor( - cs.namespace(|| format!("third xor {}", i)), - &UInt32::constant(K_BUFFER[0]), - )?; - tmp1 = shl_uint32(&tmp1, s_val[i]).unwrap(); - tmp1 = tmp1.xor( - cs.namespace(|| format!("fourth xor {}", i)), - ¤t_md_value[4], - )?; - let tmp2 = shl_uint32(¤t_md_value[2], 10); - current_md_value[0] = current_md_value[4].clone(); - current_md_value[2] = current_md_value[1].clone(); - current_md_value[4] = current_md_value[3].clone(); - current_md_value[1] = tmp1.clone(); - current_md_value[3] = tmp2.unwrap().clone(); - } - f = ripemd_d1( - cs.namespace(|| "d1"), - ¤t_md_value[3], - ¤t_md_value[1], - ¤t_md_value[2], + let mut i_val = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; + let mut current_md_value = block( + cs.namespace(|| "block 0"), + current_md_value, + s_val, + i_val, + w.clone(), + 0, + true, )?; s_val = [7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12]; - let mut i_val = [7, 14, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8]; - for i in 0..16 { - let mut tmp1 = current_md_value[0] - .xor(cs.namespace(|| format!("first xor {}", i)), &f)? - .xor(cs.namespace(|| format!("second xor {}", i)), &w[i_val[i]])? - .xor( - cs.namespace(|| format!("third xor {}", i)), - &UInt32::constant(K_BUFFER[1]), - )?; - tmp1 = shl_uint32(&tmp1, s_val[i]).unwrap(); - tmp1 = tmp1.xor( - cs.namespace(|| format!("fourth xor {}", i)), - ¤t_md_value[4], - )?; - let tmp2 = shl_uint32(¤t_md_value[2],10); - current_md_value[0] = current_md_value[4].clone(); - current_md_value[2] = current_md_value[1].clone(); - current_md_value[4] = current_md_value[3].clone(); - current_md_value[1] = tmp1.clone(); - current_md_value[3] = tmp2.unwrap().clone(); - } - f = ripemd_d2( - cs.namespace(|| "d2"), - ¤t_md_value[3], - ¤t_md_value[1], - ¤t_md_value[2], + i_val = [7, 14, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8]; + current_md_value = block( + cs.namespace(|| "block 1"), + &mut current_md_value, + s_val, + i_val, + w.clone(), + 1, + true, )?; s_val = [11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5]; i_val = [3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12]; - for i in 0..16 { - let mut tmp1 = current_md_value[0] - .xor(cs.namespace(|| format!("first xor {}", i)), &f)? - .xor(cs.namespace(|| format!("second xor {}", i)), &w[i_val[i]])? - .xor( - cs.namespace(|| format!("third xor {}", i)), - &UInt32::constant(K_BUFFER[2]), - )?; - tmp1 = shl_uint32(&tmp1, s_val[i]).unwrap(); - tmp1 = tmp1.xor( - cs.namespace(|| format!("fourth xor {}", i)), - ¤t_md_value[4], - )?; - let tmp2 = shl_uint32(¤t_md_value[2], 10); - current_md_value[0] = current_md_value[4].clone(); - current_md_value[2] = current_md_value[1].clone(); - current_md_value[4] = current_md_value[3].clone(); - current_md_value[1] = tmp1.clone(); - current_md_value[3] = tmp2.unwrap().clone(); - } - f = ripemd_d1( - cs.namespace(|| "d1"), - ¤t_md_value[1], - ¤t_md_value[3], - ¤t_md_value[2], + current_md_value = block( + cs.namespace(|| "block 2"), + &mut current_md_value, + s_val, + i_val, + w.clone(), + 2, + true, )?; s_val = [11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12]; i_val = [1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2]; - for i in 0..16 { - let mut tmp1 = current_md_value[0] - .xor(cs.namespace(|| format!("first xor {}", i)), &f)? - .xor(cs.namespace(|| format!("second xor {}", i)), &w[i_val[i]])? - .xor( - cs.namespace(|| format!("third xor {}", i)), - &UInt32::constant(K_BUFFER[3]), - )?; - tmp1 = shl_uint32(&tmp1, s_val[i]).unwrap(); - tmp1 = tmp1.xor( - cs.namespace(|| format!("fourth xor {}", i)), - ¤t_md_value[4], - )?; - let tmp2 = shl_uint32(¤t_md_value[2], 10); - current_md_value[0] = current_md_value[4].clone(); - current_md_value[2] = current_md_value[1].clone(); - current_md_value[4] = current_md_value[3].clone(); - current_md_value[1] = tmp1.clone(); - current_md_value[3] = tmp2.unwrap().clone(); - } - f = ripemd_d2( - cs.namespace(|| "d2"), - ¤t_md_value[1], - ¤t_md_value[2], - ¤t_md_value[3], + current_md_value = block( + cs.namespace(|| "block 3"), + &mut current_md_value, + s_val, + i_val, + w.clone(), + 3, + true, )?; s_val = [9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6]; i_val = [4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13]; - for i in 0..16 { - let mut tmp1 = current_md_value[0] - .xor(cs.namespace(|| format!("first xor {}", i)), &f)? - .xor(cs.namespace(|| format!("second xor {}", i)), &w[i_val[i]])? - .xor( - cs.namespace(|| format!("third xor {}", i)), - &UInt32::constant(K_BUFFER[4]), - )?; - tmp1 = shl_uint32(&tmp1, s_val[i]).unwrap(); - tmp1 = tmp1.xor( - cs.namespace(|| format!("fourth xor {}", i)), - ¤t_md_value[4], - )?; - let tmp2 = shl_uint32(¤t_md_value[2], 10); - current_md_value[0] = current_md_value[4].clone(); - current_md_value[2] = current_md_value[1].clone(); - current_md_value[4] = current_md_value[3].clone(); - current_md_value[1] = tmp1.clone(); - current_md_value[3] = tmp2.unwrap().clone(); - } - Ok(vec![ + current_md_value = block( + cs.namespace(|| "block 4"), + &mut current_md_value, + s_val, + i_val, + w.clone(), + 4, + true, + )?; + Ok([ current_md_value[0].clone(), current_md_value[1].clone(), current_md_value[2].clone(), @@ -240,11 +332,11 @@ where ]) } -pub fn ripemd160_func_block_prime( +pub fn right_step( cs: CS, input: &[Boolean], - current_md_value: &mut [UInt32], -) -> Result, SynthesisError> + current_md_value: &mut [UInt32; 5], +) -> Result<[UInt32; 5], SynthesisError> where Scalar: PrimeField, CS: ConstraintSystem, @@ -256,144 +348,62 @@ where .map(UInt32::from_bits_be) .collect::>(); let mut cs = MultiEq::new(cs); - let mut f = ripemd_d2( - cs.namespace(|| "d2"), - ¤t_md_value[1], - ¤t_md_value[2], - ¤t_md_value[3], - )?; let mut s_val = [8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6]; let mut i_val = [5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12]; - for i in 0..16 { - let mut tmp1 = current_md_value[0] - .xor(cs.namespace(|| format!("first xor {}", i)), &f)? - .xor(cs.namespace(|| format!("second xor {}", i)), &w[i_val[i]])? - .xor( - cs.namespace(|| format!("third xor {}", i)), - &UInt32::constant(K_BUFFER_PRIME[0]), - )?; - tmp1 = shl_uint32(&tmp1, s_val[i]).unwrap(); - tmp1 = tmp1.xor( - cs.namespace(|| format!("fourth xor {}", i)), - ¤t_md_value[4], - )?; - let tmp2 = shl_uint32(¤t_md_value[2], 10); - current_md_value[0] = current_md_value[4].clone(); - current_md_value[2] = current_md_value[1].clone(); - current_md_value[4] = current_md_value[3].clone(); - current_md_value[1] = tmp1.clone(); - current_md_value[3] = tmp2.unwrap().clone() - } - f = ripemd_d1( - cs.namespace(|| "d1"), - ¤t_md_value[1], - ¤t_md_value[3], - ¤t_md_value[2], + let mut current_md_value = block( + cs.namespace(|| "block 0"), + current_md_value, + s_val, + i_val, + w.clone(), + 0, + false, )?; s_val = [9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11]; i_val = [6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2]; - for i in 0..16 { - let mut tmp1 = current_md_value[0] - .xor(cs.namespace(|| format!("first xor {}", i)), &f)? - .xor(cs.namespace(|| format!("second xor {}", i)), &w[i_val[i]])? - .xor( - cs.namespace(|| format!("third xor {}", i)), - &UInt32::constant(K_BUFFER_PRIME[1]), - )?; - tmp1 = shl_uint32(&tmp1, s_val[i]).unwrap(); - tmp1 = tmp1.xor( - cs.namespace(|| format!("fourth xor {}", i)), - ¤t_md_value[4], - )?; - let tmp2 = shl_uint32(¤t_md_value[2], 10); - current_md_value[0] = current_md_value[4].clone(); - current_md_value[2] = current_md_value[1].clone(); - current_md_value[4] = current_md_value[3].clone(); - current_md_value[1] = tmp1.clone(); - current_md_value[3] = tmp2.unwrap().clone() - } - f = ripemd_d2( - cs.namespace(|| "d2"), - ¤t_md_value[3], - ¤t_md_value[1], - ¤t_md_value[2], + current_md_value = block( + cs.namespace(|| "block 1"), + &mut current_md_value, + s_val, + i_val, + w.clone(), + 1, + false, )?; s_val = [9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5]; i_val = [15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13]; - for i in 0..16 { - let mut tmp1 = current_md_value[0] - .xor(cs.namespace(|| format!("first xor {}", i)), &f)? - .xor(cs.namespace(|| format!("second xor {}", i)), &w[i_val[i]])? - .xor( - cs.namespace(|| format!("third xor {}", i)), - &UInt32::constant(K_BUFFER_PRIME[2]), - )?; - tmp1 = shl_uint32(&tmp1, s_val[i]).unwrap(); - tmp1 = tmp1.xor( - cs.namespace(|| format!("fourth xor {}", i)), - ¤t_md_value[4], - )?; - let tmp2 = shl_uint32(¤t_md_value[2], 10); - current_md_value[0] = current_md_value[4].clone(); - current_md_value[2] = current_md_value[1].clone(); - current_md_value[4] = current_md_value[3].clone(); - current_md_value[1] = tmp1.clone(); - current_md_value[3] = tmp2.unwrap().clone() - } - f = ripemd_d1( - cs.namespace(|| "d1"), - ¤t_md_value[2], - ¤t_md_value[1], - ¤t_md_value[3], + current_md_value = block( + cs.namespace(|| "block 2"), + &mut current_md_value, + s_val, + i_val, + w.clone(), + 2, + false, )?; s_val = [15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8]; i_val = [8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14]; - for i in 0..16 { - let mut tmp1 = current_md_value[0] - .xor(cs.namespace(|| format!("first xor {}", i)), &f)? - .xor(cs.namespace(|| format!("second xor {}", i)), &w[i_val[i]])? - .xor( - cs.namespace(|| format!("third xor {}", i)), - &UInt32::constant(K_BUFFER_PRIME[3]), - )?; - tmp1 = shl_uint32(&tmp1, s_val[i]).unwrap(); - tmp1 = tmp1.xor( - cs.namespace(|| format!("fourth xor {}", i)), - ¤t_md_value[4], - )?; - let tmp2 = shl_uint32(¤t_md_value[2], 10); - current_md_value[0] = current_md_value[4].clone(); - current_md_value[2] = current_md_value[1].clone(); - current_md_value[4] = current_md_value[3].clone(); - current_md_value[1] = tmp1.clone(); - current_md_value[3] = tmp2.unwrap().clone() - } - f = current_md_value[1] - .xor(cs.namespace(|| "first xor"), ¤t_md_value[2])? - .xor(cs.namespace(|| "second xor"), ¤t_md_value[3])?; + current_md_value = block( + cs.namespace(|| "block 3"), + &mut current_md_value, + s_val, + i_val, + w.clone(), + 3, + false, + )?; s_val = [8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11]; i_val = [12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11]; - for i in 0..16 { - let mut tmp1 = current_md_value[0] - .xor(cs.namespace(|| format!("first xor {}", i)), &f)? - .xor(cs.namespace(|| format!("second xor {}", i)), &w[i_val[i]])? - .xor( - cs.namespace(|| format!("third xor {}", i)), - &UInt32::constant(K_BUFFER_PRIME[4]), - )?; - tmp1 = shl_uint32(&tmp1, s_val[i]).unwrap(); - tmp1 = tmp1.xor( - cs.namespace(|| format!("fourth xor {}", i)), - ¤t_md_value[4], - )?; - let tmp2 = shl_uint32(¤t_md_value[2], 10); - current_md_value[0] = current_md_value[4].clone(); - current_md_value[2] = current_md_value[1].clone(); - current_md_value[4] = current_md_value[3].clone(); - current_md_value[1] = tmp1.clone(); - current_md_value[3] = tmp2.unwrap().clone() - } - Ok(vec![ + current_md_value = block( + cs.namespace(|| "block 4"), + &mut current_md_value, + s_val, + i_val, + w.clone(), + 4, + false, + )?; + Ok([ current_md_value[0].clone(), current_md_value[1].clone(), current_md_value[2].clone(), @@ -402,3 +412,37 @@ where ]) } +#[cfg(test)] +mod test { + use super::*; + use bellpepper::gadgets::multipack::bytes_to_bits; + use bellpepper_core::{boolean::AllocatedBit, test_cs::TestConstraintSystem}; + use hex_literal::hex; + use pasta_curves::Fp; + use rand_core::{RngCore, SeedableRng}; + use rand_xorshift::XorShiftRng; + + #[test] + fn test_blank_hash() { + let mut cs = TestConstraintSystem::::new(); + let mut input_bits: Vec<_> = (0..512).map(|_| Boolean::Constant(false)).collect(); + input_bits[0] = Boolean::Constant(true); + let out = ripemd160(&mut cs, &input_bits).unwrap(); + println!("{:?}", out); + let out_bits = out.into_iter().flat_map(|e| e.into_bits_be()); + + assert!(cs.is_satisfied()); + assert_eq!(cs.num_constraints(), 0); + + // let expected = hex!("9c1185a5c5e9fc54612808977ee8f548b2258d31"); + + // let mut out = out_bits; + // for b in expected.iter() { + // for i in (0..8).rev() { + // let c = out.next().unwrap().get_value().unwrap(); + + // assert_eq!(c, (b >> i) & 1u8 == 1u8); + // } + // } + } +} From 5c1cbfd4146258d404fbd695400ad09748b4274c Mon Sep 17 00:00:00 2001 From: Sh0g0-1758 Date: Fri, 15 Mar 2024 01:48:52 +0530 Subject: [PATCH 12/46] Ran fmt --- crates/ripemd160/src/ripemd160.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/ripemd160/src/ripemd160.rs b/crates/ripemd160/src/ripemd160.rs index 0f9b1ef..012b989 100644 --- a/crates/ripemd160/src/ripemd160.rs +++ b/crates/ripemd160/src/ripemd160.rs @@ -220,15 +220,15 @@ where for i in 0..16 { let mut tmp1 = md_val[0] .xor( - cs.namespace(|| format!("first xor block {} left {} index {}", index, left,i)), + cs.namespace(|| format!("first xor block {} left {} index {}", index, left, i)), &f, )? .xor( - cs.namespace(|| format!("second xor block {} left {} index {}", index, left,i)), + cs.namespace(|| format!("second xor block {} left {} index {}", index, left, i)), &w[i_val[i]], )? .xor( - cs.namespace(|| format!("third xor block {} left {} index {}", index, left,i)), + cs.namespace(|| format!("third xor block {} left {} index {}", index, left, i)), &UInt32::constant(k_val[index]), )?; tmp1 = shl_uint32(&tmp1, s_val[i]).unwrap(); From e4ce42402dff4c0d0a6e5e8ef018b55a2481c674 Mon Sep 17 00:00:00 2001 From: Sh0g0-1758 Date: Fri, 15 Mar 2024 13:42:46 +0530 Subject: [PATCH 13/46] Revamp R1CS --- crates/ripemd160/src/ripemd160.rs | 37 ++++++++---- crates/ripemd160/src/util.rs | 97 +++++++++++++++++-------------- 2 files changed, 79 insertions(+), 55 deletions(-) diff --git a/crates/ripemd160/src/ripemd160.rs b/crates/ripemd160/src/ripemd160.rs index 012b989..05219b8 100644 --- a/crates/ripemd160/src/ripemd160.rs +++ b/crates/ripemd160/src/ripemd160.rs @@ -25,7 +25,7 @@ const K_BUFFER_PRIME: [u32; 5] = [0x50a28be6, 0x5c4dd124, 0x6d703ef3, 0x7a6d76e9 pub fn ripemd160( cs: CS, input: &[Boolean], -) -> Result<[UInt32; 5], bellpepper_core::SynthesisError> +) -> Result<[Boolean; 160], bellpepper_core::SynthesisError> where Scalar: PrimeField, CS: ConstraintSystem, @@ -68,7 +68,13 @@ where cur_md_prime = cur_md.clone(); } let array_data: Result<[UInt32; 5], _> = cur_md.try_into(); - Ok(array_data.unwrap()) + Ok(array_data + .unwrap() + .into_iter() + .flat_map(|e| e.into_bits_be()) + .collect::>() + .try_into() + .unwrap()) } fn combine_left_and_right( @@ -423,20 +429,27 @@ mod test { use rand_xorshift::XorShiftRng; #[test] - fn test_blank_hash() { + fn test_hash_abcde_string() { + let msg = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq".as_bytes(); + let msg_bits = bytes_to_bits(msg); + let mut cs = TestConstraintSystem::::new(); - let mut input_bits: Vec<_> = (0..512).map(|_| Boolean::Constant(false)).collect(); - input_bits[0] = Boolean::Constant(true); - let out = ripemd160(&mut cs, &input_bits).unwrap(); - println!("{:?}", out); - let out_bits = out.into_iter().flat_map(|e| e.into_bits_be()); + let input_bits: Vec = msg_bits + .into_iter() + .enumerate() + .map(|(i, b)| { + Boolean::from( + AllocatedBit::alloc(cs.namespace(|| format!("input bit {}", i)), Some(b)) + .unwrap(), + ) + }) + .collect(); + let out_bits = ripemd160(cs.namespace(|| "ripemd160"), &input_bits).unwrap(); assert!(cs.is_satisfied()); - assert_eq!(cs.num_constraints(), 0); - - // let expected = hex!("9c1185a5c5e9fc54612808977ee8f548b2258d31"); - // let mut out = out_bits; + // let expected = hex!("84983e441c3bd26ebaae4aa1f95129e5e54670f1"); + // let mut out = out_bits.iter(); // for b in expected.iter() { // for i in (0..8).rev() { // let c = out.next().unwrap().get_value().unwrap(); diff --git a/crates/ripemd160/src/util.rs b/crates/ripemd160/src/util.rs index 16c18ed..411c741 100644 --- a/crates/ripemd160/src/util.rs +++ b/crates/ripemd160/src/util.rs @@ -14,7 +14,7 @@ where { let d1_value = match (a.get_value(), b.get_value(), c.get_value()) { (Some(a), Some(b), Some(c)) => { - // (a and b) xor (a and c) xor (b and c) + // (a and b) or (c and not(b)) Some((a & b) | (c & !b)) } _ => None, @@ -28,49 +28,47 @@ where } (&Boolean::Constant(false), b, c) => { // If a is false, - // (a and b) xor (a and c) xor (b and c) + // (a and b) or (c and not(b)) // equals - // (b and c) + // (c and not(b)) return Boolean::and(cs, &b.not(), c); } (_a, &Boolean::Constant(false), c) => { // If b is false, - // (a and b) xor (a and c) xor (b and c) + // (a and b) or (c and not(b)) // equals // (a and c) return Ok(c.clone()); } (a, b, &Boolean::Constant(false)) => { // If c is false, - // (a and b) xor (a and c) xor (b and c) + // (a and b) or (c and not(b)) // equals // (a and b) return Boolean::and(cs, a, b); } (a, b, &Boolean::Constant(true)) => { // If c is true, - // (a and b) xor (a and c) xor (b and c) + // (a and b) or (c and not(b)) // equals - // (a and b) xor (a) xor (b) - // equals - // not ((not a) and (not b)) - - return Ok(Boolean::and(cs, &a.not(), b)?.not()); + // (a or not(b)) + return Boolean::or(cs, a, &b.not()); } (a, &Boolean::Constant(true), _c) => { // If b is true, - // (a and b) xor (a and c) xor (b and c) + // (a and b) or (c and not(b)) // equals - // (a) xor (a and c) xor (c) + // (a and true) or (false) + // equals + // a return Ok(a.clone()); } (&Boolean::Constant(true), b, c) => { // If a is true, - // (a and b) xor (a and c) xor (b and c) + // (a and b) or (c and not(b)) // equals // (b) xor (c) xor (b and c) - - return Ok(Boolean::and(cs, &b.not(), &c.not())?.not()); + return Boolean::or(cs, b, c); } ( &Boolean::Is(_) | &Boolean::Not(_), @@ -92,11 +90,16 @@ where }, )?; + // (a and b) or (c and not(b)) = d1 + // ab + c(1-b) = d1 + // b(a - c) = d1 - c + // (c - a)b = c - d1 + cs.enforce( || "d1 computation", - |_| a.lc(CS::one(), Scalar::ONE) - &c.lc(CS::one(), Scalar::ONE), + |_| c.lc(CS::one(), Scalar::ONE) - &a.lc(CS::one(), Scalar::ONE), |_| b.lc(CS::one(), Scalar::ONE), - |lc| lc + d1 - &c.lc(CS::one(), Scalar::ONE), + |_| c.lc(CS::one(), Scalar::ONE) - d1, ); Ok(AllocatedBit::alloc(cs.namespace(|| "d1"), d1_value) @@ -116,7 +119,7 @@ where { let d2_value = match (a.get_value(), b.get_value(), c.get_value()) { (Some(a), Some(b), Some(c)) => { - // (a)xor(b or not(c)) + // (a) xor (b or not(c)) Some((a) ^ (b | !c)) } _ => None, @@ -130,48 +133,48 @@ where } (&Boolean::Constant(false), b, c) => { // If a is false, - // (a and b) xor (a and c) xor (b and c) + // (a) xor (b or not(c)) // equals - // (b and c) - return Ok(Boolean::and(cs, c, &b.not())?.not()); + // (b or not(c)) + return Boolean::or(cs, b, &c.not()); } (a, &Boolean::Constant(false), c) => { // If b is false, - // (a and b) xor (a and c) xor (b and c) + // (a) xor (b or not(c)) // equals - // (a and c) - + // (a xor not(c)) return Boolean::xor(cs, a, &c.not()); } (a, _b, &Boolean::Constant(false)) => { // If c is false, - // (a and b) xor (a and c) xor (b and c) + // (a) xor (b or not(c)) // equals - // (a and b) - return Ok(Boolean::and(cs, a, a)?.not()); + // (a) xor (true) + // equals + // not a + return Ok(a.not()); } (a, b, &Boolean::Constant(true)) => { // If c is true, - // (a and b) xor (a and c) xor (b and c) - // equals - // (a and b) xor (a) xor (b) + // (a) xor (b or not(c)) // equals - // not ((not a) and (not b)) - + // (a) xor (b) return Boolean::xor(cs, a, b); } (a, &Boolean::Constant(true), _c) => { // If b is true, - // (a and b) xor (a and c) xor (b and c) + // (a) xor (b or not(c)) + // equals + // (a) xor (true) // equals - // (a) xor (a and c) xor (c) - return Ok(Boolean::and(cs, a, a)?.not()); + // not a + return Ok(a.not()); } (&Boolean::Constant(true), b, c) => { // If a is true, - // (a and b) xor (a and c) xor (b and c) + // (a) xor (b or not(c)) // equals - // (b) xor (c) xor (b and c) + // (not(b) and c) return Boolean::and(cs, c, &b.not()); } ( @@ -194,20 +197,28 @@ where }, )?; - let notbc = Boolean::and(cs.namespace(|| "not b and c"), &b.not(), c)?; + // (a) xor (b or not(c)) = d2 + // (not a) * (b + (1 - c)) + (a) * (1 - b) * (c) + // 2ac - abc - ab - a - c + 1 + b = d2 + // b * (a + ac) = 2ac - a - c + 1 + b - d2 + + let ac = Boolean::and(cs.namespace(|| "(a and c)"), a, c)?; cs.enforce( || "d2 computation", - |lc| lc + CS::one() - &a.lc(CS::one(), Scalar::ONE), + |_| a.lc(CS::one(), Scalar::ONE) + &ac.lc(CS::one(), Scalar::ONE), + |_| b.lc(CS::one(), Scalar::ONE), |lc| { lc + CS::one() + &b.lc(CS::one(), Scalar::ONE) - &c.lc(CS::one(), Scalar::ONE) - - ¬bc.lc(CS::one(), Scalar::ONE) + - &a.lc(CS::one(), Scalar::ONE) + + &ac.lc(CS::one(), Scalar::ONE) + + &ac.lc(CS::one(), Scalar::ONE) + - d2 }, - |lc| lc + d2 - ¬bc.lc(CS::one(), Scalar::ONE), ); - Ok(AllocatedBit::alloc(cs.namespace(|| "d2"), d2_value) + Ok(AllocatedBit::alloc(cs.namespace(|| "d2_alloc"), d2_value) .unwrap() .into()) } From 46353c13b478039843d37dbd1e81706fec237c16 Mon Sep 17 00:00:00 2001 From: Sh0g0-1758 Date: Fri, 15 Mar 2024 13:51:51 +0530 Subject: [PATCH 14/46] Minor Changes --- crates/ripemd160/src/ripemd160.rs | 20 ++++++++++---------- crates/ripemd160/src/util.rs | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/crates/ripemd160/src/ripemd160.rs b/crates/ripemd160/src/ripemd160.rs index 05219b8..df73658 100644 --- a/crates/ripemd160/src/ripemd160.rs +++ b/crates/ripemd160/src/ripemd160.rs @@ -429,8 +429,8 @@ mod test { use rand_xorshift::XorShiftRng; #[test] - fn test_hash_abcde_string() { - let msg = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq".as_bytes(); + fn test_single_char_hash() { + let msg = "a".as_bytes(); let msg_bits = bytes_to_bits(msg); let mut cs = TestConstraintSystem::::new(); @@ -448,14 +448,14 @@ mod test { let out_bits = ripemd160(cs.namespace(|| "ripemd160"), &input_bits).unwrap(); assert!(cs.is_satisfied()); - // let expected = hex!("84983e441c3bd26ebaae4aa1f95129e5e54670f1"); - // let mut out = out_bits.iter(); - // for b in expected.iter() { - // for i in (0..8).rev() { - // let c = out.next().unwrap().get_value().unwrap(); + let expected = hex!("0bdc9d2d256b3ee9daae347be6f4dc835a467ffe"); + let mut out = out_bits.iter(); + for b in expected.iter() { + for i in (0..8).rev() { + let c = out.next().unwrap().get_value().unwrap(); - // assert_eq!(c, (b >> i) & 1u8 == 1u8); - // } - // } + assert_eq!(c, (b >> i) & 1u8 == 1u8); + } + } } } diff --git a/crates/ripemd160/src/util.rs b/crates/ripemd160/src/util.rs index 411c741..9eded35 100644 --- a/crates/ripemd160/src/util.rs +++ b/crates/ripemd160/src/util.rs @@ -102,7 +102,7 @@ where |_| c.lc(CS::one(), Scalar::ONE) - d1, ); - Ok(AllocatedBit::alloc(cs.namespace(|| "d1"), d1_value) + Ok(AllocatedBit::alloc(cs.namespace(|| "d1_alloc"), d1_value) .unwrap() .into()) } @@ -206,8 +206,8 @@ where cs.enforce( || "d2 computation", - |_| a.lc(CS::one(), Scalar::ONE) + &ac.lc(CS::one(), Scalar::ONE), |_| b.lc(CS::one(), Scalar::ONE), + |_| a.lc(CS::one(), Scalar::ONE) + &ac.lc(CS::one(), Scalar::ONE), |lc| { lc + CS::one() + &b.lc(CS::one(), Scalar::ONE) - &c.lc(CS::one(), Scalar::ONE) From 6b040daabc348e2e2431d0e10e0a965cddb3a5cf Mon Sep 17 00:00:00 2001 From: Sh0g0-1758 Date: Sat, 16 Mar 2024 13:13:05 +0530 Subject: [PATCH 15/46] Optimised Code and added a test --- crates/ripemd160/src/ripemd160.rs | 164 ++++++++++++++++-------------- 1 file changed, 85 insertions(+), 79 deletions(-) diff --git a/crates/ripemd160/src/ripemd160.rs b/crates/ripemd160/src/ripemd160.rs index df73658..391bd4b 100644 --- a/crates/ripemd160/src/ripemd160.rs +++ b/crates/ripemd160/src/ripemd160.rs @@ -38,7 +38,8 @@ where while (padded.len() + 64) % 512 != 0 { padded.push(Boolean::constant(false)); } - for b in (0..64).rev().map(|i| (plen >> i) & 1 == 1) { + // Check here for bits, they are reverse or not. + for b in (0..64).map(|i| (plen >> i) & 1 == 1) { padded.push(Boolean::constant(b)); } assert!(padded.len() % 512 == 0); @@ -47,7 +48,6 @@ where let mut cs = MultiEq::new(cs); for (i, block) in padded.chunks(512).enumerate() { let prev_md = cur_md.clone(); - println!("block {}", i); cur_md = left_step( cs.namespace(|| format!("left_step {}", i)), block, @@ -90,14 +90,15 @@ where let mut cs = MultiEq::new(cs); let mut update_md = cur_md.clone(); for i in 0..5 { - update_md[(i + 4) % 5] = prev_md[i].xor( - cs.namespace(|| format!("first xor {}", i)), - &cur_md[(i + 1) % 5], - )?; - update_md[(i + 4) % 5] = update_md[(i + 4) % 5].xor( - cs.namespace(|| format!("second xor {}", i)), - &cur_md_prime[(i + 2) % 5], - )?; + update_md[(i + 4) % 5] = prev_md[i] + .xor( + cs.namespace(|| format!("first xor {}", i)), + &cur_md[(i + 1) % 5], + )? + .xor( + cs.namespace(|| format!("second xor {}", i)), + &cur_md_prime[(i + 2) % 5], + )?; } Ok(update_md) } @@ -118,8 +119,7 @@ fn block( w: Vec, index: usize, left: bool, -) -> Result<[UInt32; 5], SynthesisError> -where +) where Scalar: PrimeField, CS: ConstraintSystem, { @@ -132,27 +132,31 @@ where .xor( cs.namespace(|| format!("first xor block {} left {} ", index, left)), &md_val[2], - )? + ) + .unwrap() .xor( cs.namespace(|| format!("second xor block {} left {}", index, left)), &md_val[3], - )?; + ) + .unwrap(); } 1 => { f = ripemd_d1( cs.namespace(|| format!("d1 block {} left {}", index, left)), - &md_val[3], - &md_val[1], &md_val[2], - )?; + &md_val[1], + &md_val[3], + ) + .unwrap(); } 2 => { f = ripemd_d2( cs.namespace(|| format!("d2 block {} left {}", index, left)), - &md_val[3], &md_val[1], + &md_val[3], &md_val[2], - )?; + ) + .unwrap(); } 3 => { f = ripemd_d1( @@ -160,7 +164,8 @@ where &md_val[1], &md_val[3], &md_val[2], - )?; + ) + .unwrap(); } 4 => { f = ripemd_d2( @@ -168,7 +173,8 @@ where &md_val[1], &md_val[2], &md_val[3], - )?; + ) + .unwrap(); } _ => panic!("Invalid index"), }, @@ -179,7 +185,8 @@ where &md_val[1], &md_val[2], &md_val[3], - )?; + ) + .unwrap(); } 1 => { f = ripemd_d1( @@ -187,7 +194,8 @@ where &md_val[1], &md_val[3], &md_val[2], - )?; + ) + .unwrap(); } 2 => { f = ripemd_d2( @@ -195,7 +203,8 @@ where &md_val[3], &md_val[1], &md_val[2], - )?; + ) + .unwrap(); } 3 => { f = ripemd_d1( @@ -203,18 +212,21 @@ where &md_val[2], &md_val[1], &md_val[3], - )?; + ) + .unwrap(); } 4 => { f = md_val[1] .xor( cs.namespace(|| format!("first xor block {} left {}", index, left)), &md_val[2], - )? + ) + .unwrap() .xor( cs.namespace(|| format!("second xor block {} left {}", index, left)), &md_val[3], - )?; + ) + .unwrap(); } _ => panic!("Invalid index"), }, @@ -228,34 +240,32 @@ where .xor( cs.namespace(|| format!("first xor block {} left {} index {}", index, left, i)), &f, - )? + ) + .unwrap() .xor( cs.namespace(|| format!("second xor block {} left {} index {}", index, left, i)), &w[i_val[i]], - )? + ) + .unwrap() .xor( cs.namespace(|| format!("third xor block {} left {} index {}", index, left, i)), &UInt32::constant(k_val[index]), - )?; + ) + .unwrap(); tmp1 = shl_uint32(&tmp1, s_val[i]).unwrap(); - tmp1 = tmp1.xor( - cs.namespace(|| format!("fourth xor block {} left {} index {}", index, left, i)), - &md_val[4], - )?; - let tmp2 = shl_uint32(&md_val[2], 10); + tmp1 = tmp1 + .xor( + cs.namespace(|| format!("fourth xor block {} left {} index {}", index, left, i)), + &md_val[4], + ) + .unwrap(); + let tmp2 = shl_uint32(&md_val[2], 10).unwrap(); md_val[0] = md_val[4].clone(); md_val[2] = md_val[1].clone(); md_val[4] = md_val[3].clone(); md_val[1] = tmp1.clone(); - md_val[3] = tmp2.unwrap().clone(); + md_val[3] = tmp2.clone(); } - Ok([ - md_val[0].clone(), - md_val[1].clone(), - md_val[2].clone(), - md_val[3].clone(), - md_val[4].clone(), - ]) } pub fn left_step( @@ -268,7 +278,6 @@ where CS: ConstraintSystem, { assert_eq!(input.len(), 512); - assert_eq!(current_md_value.len(), 5); let w = input .chunks(32) .map(UInt32::from_bits_be) @@ -276,7 +285,7 @@ where let mut cs = MultiEq::new(cs); let mut s_val = [11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8]; let mut i_val = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; - let mut current_md_value = block( + block( cs.namespace(|| "block 0"), current_md_value, s_val, @@ -284,51 +293,51 @@ where w.clone(), 0, true, - )?; + ); s_val = [7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12]; i_val = [7, 14, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8]; - current_md_value = block( + block( cs.namespace(|| "block 1"), - &mut current_md_value, + current_md_value, s_val, i_val, w.clone(), 1, true, - )?; + ); s_val = [11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5]; i_val = [3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12]; - current_md_value = block( + block( cs.namespace(|| "block 2"), - &mut current_md_value, + current_md_value, s_val, i_val, w.clone(), 2, true, - )?; + ); s_val = [11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12]; i_val = [1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2]; - current_md_value = block( + block( cs.namespace(|| "block 3"), - &mut current_md_value, + current_md_value, s_val, i_val, w.clone(), 3, true, - )?; + ); s_val = [9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6]; i_val = [4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13]; - current_md_value = block( + block( cs.namespace(|| "block 4"), - &mut current_md_value, + current_md_value, s_val, i_val, w.clone(), 4, true, - )?; + ); Ok([ current_md_value[0].clone(), current_md_value[1].clone(), @@ -348,7 +357,6 @@ where CS: ConstraintSystem, { assert_eq!(input.len(), 512); - assert_eq!(current_md_value.len(), 5); let w = input .chunks(32) .map(UInt32::from_bits_be) @@ -356,7 +364,7 @@ where let mut cs = MultiEq::new(cs); let mut s_val = [8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6]; let mut i_val = [5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12]; - let mut current_md_value = block( + block( cs.namespace(|| "block 0"), current_md_value, s_val, @@ -364,51 +372,51 @@ where w.clone(), 0, false, - )?; + ); s_val = [9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11]; i_val = [6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2]; - current_md_value = block( + block( cs.namespace(|| "block 1"), - &mut current_md_value, + current_md_value, s_val, i_val, w.clone(), 1, false, - )?; + ); s_val = [9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5]; i_val = [15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13]; - current_md_value = block( + block( cs.namespace(|| "block 2"), - &mut current_md_value, + current_md_value, s_val, i_val, w.clone(), 2, false, - )?; + ); s_val = [15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8]; i_val = [8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14]; - current_md_value = block( + block( cs.namespace(|| "block 3"), - &mut current_md_value, + current_md_value, s_val, i_val, w.clone(), 3, false, - )?; + ); s_val = [8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11]; i_val = [12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11]; - current_md_value = block( + block( cs.namespace(|| "block 4"), - &mut current_md_value, + current_md_value, s_val, i_val, w.clone(), 4, false, - )?; + ); Ok([ current_md_value[0].clone(), current_md_value[1].clone(), @@ -429,8 +437,8 @@ mod test { use rand_xorshift::XorShiftRng; #[test] - fn test_single_char_hash() { - let msg = "a".as_bytes(); + fn test_abc_hash() { + let msg = "abc".as_bytes(); let msg_bits = bytes_to_bits(msg); let mut cs = TestConstraintSystem::::new(); @@ -447,13 +455,11 @@ mod test { let out_bits = ripemd160(cs.namespace(|| "ripemd160"), &input_bits).unwrap(); assert!(cs.is_satisfied()); - - let expected = hex!("0bdc9d2d256b3ee9daae347be6f4dc835a467ffe"); + let expected = hex!("8eb208f7e05d987a9b044a8e98c6b087f15a0bfc"); let mut out = out_bits.iter(); for b in expected.iter() { - for i in (0..8).rev() { + for i in 0..8 { let c = out.next().unwrap().get_value().unwrap(); - assert_eq!(c, (b >> i) & 1u8 == 1u8); } } From 90dc165889ac613c895dda13a3cae9c6c772f5e8 Mon Sep 17 00:00:00 2001 From: Sh0g0-1758 Date: Sat, 16 Mar 2024 13:35:24 +0530 Subject: [PATCH 16/46] Minor Changes --- crates/ripemd160/src/ripemd160.rs | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/crates/ripemd160/src/ripemd160.rs b/crates/ripemd160/src/ripemd160.rs index 391bd4b..90f5330 100644 --- a/crates/ripemd160/src/ripemd160.rs +++ b/crates/ripemd160/src/ripemd160.rs @@ -48,16 +48,16 @@ where let mut cs = MultiEq::new(cs); for (i, block) in padded.chunks(512).enumerate() { let prev_md = cur_md.clone(); - cur_md = left_step( + left_step( cs.namespace(|| format!("left_step {}", i)), block, &mut cur_md, - )?; - cur_md_prime = right_step( + ); + right_step( cs.namespace(|| format!("right_step {}", i)), block, &mut cur_md_prime, - )?; + ); cur_md = combine_left_and_right( cs.namespace(|| format!("combine_left_and_right_step {}", i)), cur_md, @@ -272,7 +272,7 @@ pub fn left_step( cs: CS, input: &[Boolean], current_md_value: &mut [UInt32; 5], -) -> Result<[UInt32; 5], SynthesisError> +) where Scalar: PrimeField, CS: ConstraintSystem, @@ -338,20 +338,13 @@ where 4, true, ); - Ok([ - current_md_value[0].clone(), - current_md_value[1].clone(), - current_md_value[2].clone(), - current_md_value[3].clone(), - current_md_value[4].clone(), - ]) } pub fn right_step( cs: CS, input: &[Boolean], current_md_value: &mut [UInt32; 5], -) -> Result<[UInt32; 5], SynthesisError> +) where Scalar: PrimeField, CS: ConstraintSystem, @@ -417,13 +410,6 @@ where 4, false, ); - Ok([ - current_md_value[0].clone(), - current_md_value[1].clone(), - current_md_value[2].clone(), - current_md_value[3].clone(), - current_md_value[4].clone(), - ]) } #[cfg(test)] From aac1c29c837567bfc9f34a3f880d209fce20801f Mon Sep 17 00:00:00 2001 From: harry2855 Date: Mon, 18 Mar 2024 21:28:39 +0530 Subject: [PATCH 17/46] corrected logic --- crates/ripemd160/src/ripemd160.rs | 227 ++++++++++++++++-------------- 1 file changed, 123 insertions(+), 104 deletions(-) diff --git a/crates/ripemd160/src/ripemd160.rs b/crates/ripemd160/src/ripemd160.rs index 90f5330..a91c83e 100644 --- a/crates/ripemd160/src/ripemd160.rs +++ b/crates/ripemd160/src/ripemd160.rs @@ -1,14 +1,14 @@ //! Circuits for the [RIPEMD-160] hash function and its internal compression //! function. //! -//! [RIPEMD-160]: https://www.esat.kuleuven.be/cosic/publications/article-317.pdf +//! [RIPEMD-160]: https://homes.esat.kuleuven.be/~bosselae/ripemd160.html use bellpepper::gadgets::{multieq::MultiEq, uint32::UInt32}; -use bellpepper_core::{boolean::Boolean, ConstraintSystem, SynthesisError}; +use bellpepper_core::{boolean::Boolean, ConstraintSystem}; use ff::PrimeField; use std::convert::TryInto; -use crate::util::{ripemd_d1, ripemd_d2, shl_uint32}; +use crate::util::{or_uint32, ripemd_d1, ripemd_d2, shl_uint32}; #[allow(clippy::unreadable_literal)] const MD_BUFFERS: [u32; 5] = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0]; @@ -35,14 +35,41 @@ where 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)); } - // Check here for bits, they are reverse or not. - for b in (0..64).map(|i| (plen >> i) & 1 == 1) { - padded.push(Boolean::constant(b)); + + for i in (0..64).step_by(8) { + let byte = ((plen >> i) & 0xFF) as u8; + let reversed_byte = byte.reverse_bits(); + for b in (0..8).map(|i| (reversed_byte >> i) & 1 == 1) { + padded.push(Boolean::constant(b)); + } } + assert!(padded.len() % 512 == 0); + + for i in (0..padded.len()).step_by(32) { + let mut tmp = padded[i..i + 32].to_vec(); + tmp.reverse(); + let mut iter = 0; + for j in tmp { + padded[i + iter] = j; + iter += 1; + } + } + + for i in (0..padded.len()).step_by(8) { + let mut tmp = padded[i..i + 8].to_vec(); + tmp.reverse(); + let mut iter = 0; + for j in tmp { + padded[i + iter] = j; + iter += 1; + } + } + let mut cur_md = get_ripemd160_md("md"); let mut cur_md_prime = get_ripemd160_md("md_prime"); let mut cs = MultiEq::new(cs); @@ -68,13 +95,30 @@ where cur_md_prime = cur_md.clone(); } let array_data: Result<[UInt32; 5], _> = cur_md.try_into(); - Ok(array_data + let mut result = array_data .unwrap() .into_iter() .flat_map(|e| e.into_bits_be()) - .collect::>() - .try_into() - .unwrap()) + .collect::>(); + for i in (0..result.len()).step_by(32) { + let mut tmp = result[i..i + 32].to_vec(); + tmp.reverse(); + let mut iter = 0; + for j in tmp { + result[i + iter] = j; + iter += 1; + } + } + for i in (0..result.len()).step_by(8) { + let mut tmp = result[i..i + 8].to_vec(); + tmp.reverse(); + let mut iter = 0; + for j in tmp { + result[i + iter] = j; + iter += 1; + } + } + Ok(result.try_into().unwrap()) } fn combine_left_and_right( @@ -90,15 +134,15 @@ where let mut cs = MultiEq::new(cs); let mut update_md = cur_md.clone(); for i in 0..5 { - update_md[(i + 4) % 5] = prev_md[i] - .xor( - cs.namespace(|| format!("first xor {}", i)), - &cur_md[(i + 1) % 5], - )? - .xor( - cs.namespace(|| format!("second xor {}", i)), - &cur_md_prime[(i + 2) % 5], - )?; + update_md[(i + 4) % 5] = UInt32::addmany( + cs.namespace(|| format!("first add_many {}", i)), + &[ + prev_md[i].clone(), + cur_md[(i + 1) % 5].clone(), + cur_md_prime[(i + 2) % 5].clone(), + ], + ) + .unwrap(); } Ok(update_md) } @@ -111,20 +155,13 @@ fn get_ripemd160_md(input: &str) -> [UInt32; 5] { } } -fn block( - cs: CS, - md_val: &mut [UInt32; 5], - s_val: [usize; 16], - i_val: [usize; 16], - w: Vec, - index: usize, - left: bool, -) where +fn compute_f(cs: CS, md_val: &mut [UInt32; 5], index: usize, left: bool) -> UInt32 +where Scalar: PrimeField, CS: ConstraintSystem, { - let mut cs = MultiEq::new(cs); let f: UInt32; + let mut cs = MultiEq::new(cs); match left { true => match index { 0 => { @@ -152,8 +189,8 @@ fn block( 2 => { f = ripemd_d2( cs.namespace(|| format!("d2 block {} left {}", index, left)), - &md_val[1], &md_val[3], + &md_val[1], &md_val[2], ) .unwrap(); @@ -231,35 +268,62 @@ fn block( _ => panic!("Invalid index"), }, } + f +} + +fn block( + cs: CS, + md_val: &mut [UInt32; 5], + s_val: [usize; 16], + i_val: [usize; 16], + w: Vec, + index: usize, + left: bool, +) where + Scalar: PrimeField, + CS: ConstraintSystem, +{ + let mut cs = MultiEq::new(cs); + let mut f: UInt32; + let k_val = match left { true => K_BUFFER, false => K_BUFFER_PRIME, }; for i in 0..16 { - let mut tmp1 = md_val[0] - .xor( - cs.namespace(|| format!("first xor block {} left {} index {}", index, left, i)), - &f, - ) - .unwrap() - .xor( - cs.namespace(|| format!("second xor block {} left {} index {}", index, left, i)), - &w[i_val[i]], - ) - .unwrap() - .xor( - cs.namespace(|| format!("third xor block {} left {} index {}", index, left, i)), - &UInt32::constant(k_val[index]), - ) - .unwrap(); - tmp1 = shl_uint32(&tmp1, s_val[i]).unwrap(); - tmp1 = tmp1 - .xor( - cs.namespace(|| format!("fourth xor block {} left {} index {}", index, left, i)), - &md_val[4], - ) - .unwrap(); - let tmp2 = shl_uint32(&md_val[2], 10).unwrap(); + f = compute_f( + cs.namespace(|| format!("Compute F block {} left {} index {}", index, left, i)), + md_val, + index, + left, + ); + let mut tmp1 = UInt32::addmany( + cs.namespace(|| format!("first add_many block {} left {} index {}", index, left, i)), + &[ + md_val[0].clone(), + f.clone(), + w[i_val[i]].clone(), + UInt32::constant(k_val[index]), + ], + ) + .unwrap(); + tmp1 = or_uint32( + cs.namespace(|| format!("first or block {} left {} index {}", index, left, i)), + &shl_uint32(&tmp1, s_val[i]).unwrap(), + &UInt32::shr(&tmp1, 32 - s_val[i]), + ) + .unwrap(); + tmp1 = UInt32::addmany( + cs.namespace(|| format!("second add_many block {} left {} index {}", index, left, i)), + &[tmp1, md_val[4].clone()], + ) + .unwrap(); + let tmp2 = or_uint32( + cs.namespace(|| format!("second or block {} left {} index {}", index, left, i)), + &shl_uint32(&md_val[2], 10).unwrap(), + &UInt32::shr(&md_val[2], 32 - 10), + ) + .unwrap(); md_val[0] = md_val[4].clone(); md_val[2] = md_val[1].clone(); md_val[4] = md_val[3].clone(); @@ -268,11 +332,7 @@ fn block( } } -pub fn left_step( - cs: CS, - input: &[Boolean], - current_md_value: &mut [UInt32; 5], -) +pub fn left_step(cs: CS, input: &[Boolean], current_md_value: &mut [UInt32; 5]) where Scalar: PrimeField, CS: ConstraintSystem, @@ -283,6 +343,7 @@ where .map(UInt32::from_bits_be) .collect::>(); let mut cs = MultiEq::new(cs); + assert_eq!(w.len(), 16); let mut s_val = [11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8]; let mut i_val = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; block( @@ -295,7 +356,7 @@ where true, ); s_val = [7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12]; - i_val = [7, 14, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8]; + i_val = [7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8]; block( cs.namespace(|| "block 1"), current_md_value, @@ -340,11 +401,7 @@ where ); } -pub fn right_step( - cs: CS, - input: &[Boolean], - current_md_value: &mut [UInt32; 5], -) +pub fn right_step(cs: CS, input: &[Boolean], current_md_value: &mut [UInt32; 5]) where Scalar: PrimeField, CS: ConstraintSystem, @@ -354,6 +411,7 @@ where .chunks(32) .map(UInt32::from_bits_be) .collect::>(); + assert_eq!(w.len(), 16); let mut cs = MultiEq::new(cs); let mut s_val = [8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6]; let mut i_val = [5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12]; @@ -412,42 +470,3 @@ where ); } -#[cfg(test)] -mod test { - use super::*; - use bellpepper::gadgets::multipack::bytes_to_bits; - use bellpepper_core::{boolean::AllocatedBit, test_cs::TestConstraintSystem}; - use hex_literal::hex; - use pasta_curves::Fp; - use rand_core::{RngCore, SeedableRng}; - use rand_xorshift::XorShiftRng; - - #[test] - fn test_abc_hash() { - let msg = "abc".as_bytes(); - let msg_bits = bytes_to_bits(msg); - - let mut cs = TestConstraintSystem::::new(); - let input_bits: Vec = msg_bits - .into_iter() - .enumerate() - .map(|(i, b)| { - Boolean::from( - AllocatedBit::alloc(cs.namespace(|| format!("input bit {}", i)), Some(b)) - .unwrap(), - ) - }) - .collect(); - - let out_bits = ripemd160(cs.namespace(|| "ripemd160"), &input_bits).unwrap(); - assert!(cs.is_satisfied()); - let expected = hex!("8eb208f7e05d987a9b044a8e98c6b087f15a0bfc"); - let mut out = out_bits.iter(); - for b in expected.iter() { - for i in 0..8 { - let c = out.next().unwrap().get_value().unwrap(); - assert_eq!(c, (b >> i) & 1u8 == 1u8); - } - } - } -} From 967f3e212bac3793db00b49b262723e51bf8a7bd Mon Sep 17 00:00:00 2001 From: Sh0g0-1758 Date: Mon, 18 Mar 2024 21:29:53 +0530 Subject: [PATCH 18/46] added utility function --- crates/ripemd160/src/ripemd160.rs | 96 ++++++++++++++++++++++++++++++- crates/ripemd160/src/util.rs | 61 ++++++++++++++++++++ 2 files changed, 155 insertions(+), 2 deletions(-) diff --git a/crates/ripemd160/src/ripemd160.rs b/crates/ripemd160/src/ripemd160.rs index a91c83e..114a59e 100644 --- a/crates/ripemd160/src/ripemd160.rs +++ b/crates/ripemd160/src/ripemd160.rs @@ -2,12 +2,15 @@ //! function. //! //! [RIPEMD-160]: https://homes.esat.kuleuven.be/~bosselae/ripemd160.html +//! [RIPEMD-160]: https://homes.esat.kuleuven.be/~bosselae/ripemd160.html use bellpepper::gadgets::{multieq::MultiEq, uint32::UInt32}; use bellpepper_core::{boolean::Boolean, ConstraintSystem}; +use bellpepper_core::{boolean::Boolean, ConstraintSystem}; use ff::PrimeField; use std::convert::TryInto; +use crate::util::{or_uint32, ripemd_d1, ripemd_d2, shl_uint32}; use crate::util::{or_uint32, ripemd_d1, ripemd_d2, shl_uint32}; #[allow(clippy::unreadable_literal)] @@ -95,6 +98,7 @@ where cur_md_prime = cur_md.clone(); } let array_data: Result<[UInt32; 5], _> = cur_md.try_into(); + let mut result = array_data let mut result = array_data .unwrap() .into_iter() @@ -118,6 +122,26 @@ where iter += 1; } } + Ok(result.try_into().unwrap()) + .collect::>(); + for i in (0..result.len()).step_by(32) { + let mut tmp = result[i..i + 32].to_vec(); + tmp.reverse(); + let mut iter = 0; + for j in tmp { + result[i + iter] = j; + iter += 1; + } + } + for i in (0..result.len()).step_by(8) { + let mut tmp = result[i..i + 8].to_vec(); + tmp.reverse(); + let mut iter = 0; + for j in tmp { + result[i + iter] = j; + iter += 1; + } + } Ok(result.try_into().unwrap()) } @@ -143,6 +167,15 @@ where ], ) .unwrap(); + update_md[(i + 4) % 5] = UInt32::addmany( + cs.namespace(|| format!("first add_many {}", i)), + &[ + prev_md[i].clone(), + cur_md[(i + 1) % 5].clone(), + cur_md_prime[(i + 2) % 5].clone(), + ], + ) + .unwrap(); } Ok(update_md) } @@ -155,11 +188,14 @@ fn get_ripemd160_md(input: &str) -> [UInt32; 5] { } } +fn compute_f(cs: CS, md_val: &mut [UInt32; 5], index: usize, left: bool) -> UInt32 +where fn compute_f(cs: CS, md_val: &mut [UInt32; 5], index: usize, left: bool) -> UInt32 where Scalar: PrimeField, CS: ConstraintSystem, { + let f: UInt32; let f: UInt32; let mut cs = MultiEq::new(cs); match left { @@ -190,6 +226,7 @@ where f = ripemd_d2( cs.namespace(|| format!("d2 block {} left {}", index, left)), &md_val[3], + &md_val[3], &md_val[1], &md_val[2], ) @@ -271,6 +308,24 @@ where f } +fn block( + cs: CS, + md_val: &mut [UInt32; 5], + s_val: [usize; 16], + i_val: [usize; 16], + w: Vec, + index: usize, + left: bool, +) where + Scalar: PrimeField, + CS: ConstraintSystem, +{ + let mut cs = MultiEq::new(cs); + let mut f: UInt32; + + f +} + fn block( cs: CS, md_val: &mut [UInt32; 5], @@ -291,6 +346,39 @@ fn block( false => K_BUFFER_PRIME, }; for i in 0..16 { + f = compute_f( + cs.namespace(|| format!("Compute F block {} left {} index {}", index, left, i)), + md_val, + index, + left, + ); + let mut tmp1 = UInt32::addmany( + cs.namespace(|| format!("first add_many block {} left {} index {}", index, left, i)), + &[ + md_val[0].clone(), + f.clone(), + w[i_val[i]].clone(), + UInt32::constant(k_val[index]), + ], + ) + .unwrap(); + tmp1 = or_uint32( + cs.namespace(|| format!("first or block {} left {} index {}", index, left, i)), + &shl_uint32(&tmp1, s_val[i]).unwrap(), + &UInt32::shr(&tmp1, 32 - s_val[i]), + ) + .unwrap(); + tmp1 = UInt32::addmany( + cs.namespace(|| format!("second add_many block {} left {} index {}", index, left, i)), + &[tmp1, md_val[4].clone()], + ) + .unwrap(); + let tmp2 = or_uint32( + cs.namespace(|| format!("second or block {} left {} index {}", index, left, i)), + &shl_uint32(&md_val[2], 10).unwrap(), + &UInt32::shr(&md_val[2], 32 - 10), + ) + .unwrap(); f = compute_f( cs.namespace(|| format!("Compute F block {} left {} index {}", index, left, i)), md_val, @@ -332,6 +420,7 @@ fn block( } } +pub fn left_step(cs: CS, input: &[Boolean], current_md_value: &mut [UInt32; 5]) pub fn left_step(cs: CS, input: &[Boolean], current_md_value: &mut [UInt32; 5]) where Scalar: PrimeField, @@ -344,6 +433,7 @@ where .collect::>(); let mut cs = MultiEq::new(cs); assert_eq!(w.len(), 16); + assert_eq!(w.len(), 16); let mut s_val = [11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8]; let mut i_val = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; block( @@ -357,6 +447,7 @@ where ); s_val = [7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12]; i_val = [7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8]; + i_val = [7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8]; block( cs.namespace(|| "block 1"), current_md_value, @@ -401,6 +492,7 @@ where ); } +pub fn right_step(cs: CS, input: &[Boolean], current_md_value: &mut [UInt32; 5]) pub fn right_step(cs: CS, input: &[Boolean], current_md_value: &mut [UInt32; 5]) where Scalar: PrimeField, @@ -412,6 +504,7 @@ where .map(UInt32::from_bits_be) .collect::>(); assert_eq!(w.len(), 16); + assert_eq!(w.len(), 16); let mut cs = MultiEq::new(cs); let mut s_val = [8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6]; let mut i_val = [5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12]; @@ -468,5 +561,4 @@ where 4, false, ); -} - +} \ No newline at end of file diff --git a/crates/ripemd160/src/util.rs b/crates/ripemd160/src/util.rs index 9eded35..fa6f815 100644 --- a/crates/ripemd160/src/util.rs +++ b/crates/ripemd160/src/util.rs @@ -291,6 +291,24 @@ where }) } +/// OR two `UInt32` variables +pub fn or_uint32(mut cs: CS, a: &UInt32, b: &UInt32) -> Result +where + Scalar: PrimeField, + CS: ConstraintSystem, +{ + let a_bits = a.clone().into_bits(); + let b_bits = b.clone().into_bits(); + let or_bits = a_bits + .iter() + .zip(b_bits.iter()) + .enumerate() + .map(|(i, (x, y))| Boolean::or(cs.namespace(|| format!("or {i}")), x, y).unwrap()) + .collect::>(); + + Ok(UInt32::from_bits(&or_bits)) +} + #[cfg(test)] mod test { @@ -415,4 +433,47 @@ mod test { } } } + + #[test] + fn test_uint32_or() { + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, + 0xbc, 0xe5, + ]); + + for _ in 0..1000 { + let mut cs = TestConstraintSystem::::new(); + + let a = rng.next_u32(); + let b = rng.next_u32(); + let c = rng.next_u32(); + + let mut expected = a | 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 = or_uint32(cs.namespace(|| "first or"), &a_bit, &b_bit).unwrap(); + let r = or_uint32(cs.namespace(|| "second or"), &r, &c_bit).unwrap(); + + assert!(cs.is_satisfied()); + + for b in r.into_bits() { + match b { + Boolean::Is(ref b) => { + assert_eq!(b.get_value().unwrap(), expected & 1 == 1); + } + Boolean::Not(ref b) => { + assert_ne!(b.get_value().unwrap(), expected & 1 == 1); + } + Boolean::Constant(b) => { + assert_eq!(b, expected & 1 == 1); + } + } + + expected >>= 1; + } + } + } } From deea976d4b6de1faf58573d506c20ca1040b7b45 Mon Sep 17 00:00:00 2001 From: Sh0g0-1758 Date: Mon, 18 Mar 2024 21:32:06 +0530 Subject: [PATCH 19/46] Updated tests --- crates/ripemd160/src/ripemd160.rs | 80 ++++++++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 1 deletion(-) diff --git a/crates/ripemd160/src/ripemd160.rs b/crates/ripemd160/src/ripemd160.rs index 114a59e..40bb8d5 100644 --- a/crates/ripemd160/src/ripemd160.rs +++ b/crates/ripemd160/src/ripemd160.rs @@ -561,4 +561,82 @@ where 4, false, ); -} \ No newline at end of file +} + +#[cfg(test)] +mod test { + use super::*; + use bellpepper::gadgets::multipack::bytes_to_bits; + use bellpepper_core::test_cs::TestConstraintSystem; + use hex_literal::hex; + use pasta_curves::Fp; + + #[test] + fn test_blank_hash() { + let msg = "".as_bytes(); + let msg_bits = bytes_to_bits(msg); + + let mut cs = TestConstraintSystem::::new(); + let input_bits: Vec = msg_bits + .into_iter() + .map(Boolean::Constant) + .collect::>(); + + let out_bits = ripemd160(cs.namespace(|| "ripemd160"), &input_bits).unwrap(); + assert!(cs.is_satisfied()); + let expected = hex!("9c1185a5c5e9fc54612808977ee8f548b2258d31"); + let mut out = out_bits.iter(); + for b in expected.iter() { + for i in 0..8 { + let c = out.next().unwrap().get_value().unwrap(); + assert_eq!(c, (b >> (7 - i)) & 1u8 == 1u8); + } + } + } + + #[test] + fn test_hash_abc() { + let msg = "abc".as_bytes(); + let msg_bits = bytes_to_bits(msg); + + let mut cs = TestConstraintSystem::::new(); + let input_bits: Vec = msg_bits + .into_iter() + .map(Boolean::Constant) + .collect::>(); + + let out_bits = ripemd160(cs.namespace(|| "ripemd160"), &input_bits).unwrap(); + assert!(cs.is_satisfied()); + let expected = hex!("8eb208f7e05d987a9b044a8e98c6b087f15a0bfc"); + let mut out = out_bits.iter(); + for b in expected.iter() { + for i in 0..8 { + let c = out.next().unwrap().get_value().unwrap(); + assert_eq!(c, (b >> (7 - i)) & 1u8 == 1u8); + } + } + } + + #[test] + fn test_hash_abcde_string() { + let msg = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq".as_bytes(); + let msg_bits = bytes_to_bits(msg); + + let mut cs = TestConstraintSystem::::new(); + let input_bits: Vec = msg_bits + .into_iter() + .map(Boolean::Constant) + .collect::>(); + + let out_bits = ripemd160(cs.namespace(|| "ripemd160"), &input_bits).unwrap(); + assert!(cs.is_satisfied()); + let expected = hex!("12a053384a9c0c88e405a06c27dcf49ada62eb2b"); + let mut out = out_bits.iter(); + for b in expected.iter() { + for i in 0..8 { + let c = out.next().unwrap().get_value().unwrap(); + assert_eq!(c, (b >> (7 - i)) & 1u8 == 1u8); + } + } + } +} From 8f624403c906296418610a1cfe89403ae0151942 Mon Sep 17 00:00:00 2001 From: Sh0g0-1758 Date: Mon, 18 Mar 2024 21:42:28 +0530 Subject: [PATCH 20/46] Resolved merge conflict --- crates/ripemd160/src/ripemd160.rs | 95 +------------------------------ 1 file changed, 1 insertion(+), 94 deletions(-) diff --git a/crates/ripemd160/src/ripemd160.rs b/crates/ripemd160/src/ripemd160.rs index 40bb8d5..551c513 100644 --- a/crates/ripemd160/src/ripemd160.rs +++ b/crates/ripemd160/src/ripemd160.rs @@ -2,15 +2,12 @@ //! function. //! //! [RIPEMD-160]: https://homes.esat.kuleuven.be/~bosselae/ripemd160.html -//! [RIPEMD-160]: https://homes.esat.kuleuven.be/~bosselae/ripemd160.html use bellpepper::gadgets::{multieq::MultiEq, uint32::UInt32}; use bellpepper_core::{boolean::Boolean, ConstraintSystem}; -use bellpepper_core::{boolean::Boolean, ConstraintSystem}; use ff::PrimeField; use std::convert::TryInto; -use crate::util::{or_uint32, ripemd_d1, ripemd_d2, shl_uint32}; use crate::util::{or_uint32, ripemd_d1, ripemd_d2, shl_uint32}; #[allow(clippy::unreadable_literal)] @@ -98,7 +95,6 @@ where cur_md_prime = cur_md.clone(); } let array_data: Result<[UInt32; 5], _> = cur_md.try_into(); - let mut result = array_data let mut result = array_data .unwrap() .into_iter() @@ -122,26 +118,6 @@ where iter += 1; } } - Ok(result.try_into().unwrap()) - .collect::>(); - for i in (0..result.len()).step_by(32) { - let mut tmp = result[i..i + 32].to_vec(); - tmp.reverse(); - let mut iter = 0; - for j in tmp { - result[i + iter] = j; - iter += 1; - } - } - for i in (0..result.len()).step_by(8) { - let mut tmp = result[i..i + 8].to_vec(); - tmp.reverse(); - let mut iter = 0; - for j in tmp { - result[i + iter] = j; - iter += 1; - } - } Ok(result.try_into().unwrap()) } @@ -167,15 +143,6 @@ where ], ) .unwrap(); - update_md[(i + 4) % 5] = UInt32::addmany( - cs.namespace(|| format!("first add_many {}", i)), - &[ - prev_md[i].clone(), - cur_md[(i + 1) % 5].clone(), - cur_md_prime[(i + 2) % 5].clone(), - ], - ) - .unwrap(); } Ok(update_md) } @@ -188,14 +155,11 @@ fn get_ripemd160_md(input: &str) -> [UInt32; 5] { } } -fn compute_f(cs: CS, md_val: &mut [UInt32; 5], index: usize, left: bool) -> UInt32 -where fn compute_f(cs: CS, md_val: &mut [UInt32; 5], index: usize, left: bool) -> UInt32 where Scalar: PrimeField, CS: ConstraintSystem, { - let f: UInt32; let f: UInt32; let mut cs = MultiEq::new(cs); match left { @@ -226,7 +190,6 @@ where f = ripemd_d2( cs.namespace(|| format!("d2 block {} left {}", index, left)), &md_val[3], - &md_val[3], &md_val[1], &md_val[2], ) @@ -308,24 +271,6 @@ where f } -fn block( - cs: CS, - md_val: &mut [UInt32; 5], - s_val: [usize; 16], - i_val: [usize; 16], - w: Vec, - index: usize, - left: bool, -) where - Scalar: PrimeField, - CS: ConstraintSystem, -{ - let mut cs = MultiEq::new(cs); - let mut f: UInt32; - - f -} - fn block( cs: CS, md_val: &mut [UInt32; 5], @@ -346,39 +291,6 @@ fn block( false => K_BUFFER_PRIME, }; for i in 0..16 { - f = compute_f( - cs.namespace(|| format!("Compute F block {} left {} index {}", index, left, i)), - md_val, - index, - left, - ); - let mut tmp1 = UInt32::addmany( - cs.namespace(|| format!("first add_many block {} left {} index {}", index, left, i)), - &[ - md_val[0].clone(), - f.clone(), - w[i_val[i]].clone(), - UInt32::constant(k_val[index]), - ], - ) - .unwrap(); - tmp1 = or_uint32( - cs.namespace(|| format!("first or block {} left {} index {}", index, left, i)), - &shl_uint32(&tmp1, s_val[i]).unwrap(), - &UInt32::shr(&tmp1, 32 - s_val[i]), - ) - .unwrap(); - tmp1 = UInt32::addmany( - cs.namespace(|| format!("second add_many block {} left {} index {}", index, left, i)), - &[tmp1, md_val[4].clone()], - ) - .unwrap(); - let tmp2 = or_uint32( - cs.namespace(|| format!("second or block {} left {} index {}", index, left, i)), - &shl_uint32(&md_val[2], 10).unwrap(), - &UInt32::shr(&md_val[2], 32 - 10), - ) - .unwrap(); f = compute_f( cs.namespace(|| format!("Compute F block {} left {} index {}", index, left, i)), md_val, @@ -420,7 +332,6 @@ fn block( } } -pub fn left_step(cs: CS, input: &[Boolean], current_md_value: &mut [UInt32; 5]) pub fn left_step(cs: CS, input: &[Boolean], current_md_value: &mut [UInt32; 5]) where Scalar: PrimeField, @@ -433,7 +344,6 @@ where .collect::>(); let mut cs = MultiEq::new(cs); assert_eq!(w.len(), 16); - assert_eq!(w.len(), 16); let mut s_val = [11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8]; let mut i_val = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; block( @@ -447,7 +357,6 @@ where ); s_val = [7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12]; i_val = [7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8]; - i_val = [7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8]; block( cs.namespace(|| "block 1"), current_md_value, @@ -492,7 +401,6 @@ where ); } -pub fn right_step(cs: CS, input: &[Boolean], current_md_value: &mut [UInt32; 5]) pub fn right_step(cs: CS, input: &[Boolean], current_md_value: &mut [UInt32; 5]) where Scalar: PrimeField, @@ -504,7 +412,6 @@ where .map(UInt32::from_bits_be) .collect::>(); assert_eq!(w.len(), 16); - assert_eq!(w.len(), 16); let mut cs = MultiEq::new(cs); let mut s_val = [8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6]; let mut i_val = [5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12]; @@ -639,4 +546,4 @@ mod test { } } } -} +} \ No newline at end of file From 180864acb61a5343e21ef4df3da8d329175e56b0 Mon Sep 17 00:00:00 2001 From: Shourya Goel Date: Tue, 19 Mar 2024 11:26:52 +0530 Subject: [PATCH 21/46] Update Cargo.toml --- crates/ripemd160/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/ripemd160/Cargo.toml b/crates/ripemd160/Cargo.toml index bc9d936..49b9d71 100644 --- a/crates/ripemd160/Cargo.toml +++ b/crates/ripemd160/Cargo.toml @@ -1,5 +1,6 @@ [package] name = "ripemd160" +authors = ["Shourya Goel ", "Himanshu Raheja "] version = "0.1.0" edition = "2021" license.workspace = true From 568e81afafb591fc756d2455a1d5c678c99d4286 Mon Sep 17 00:00:00 2001 From: Himanshu Raheja <121158631+harry2855@users.noreply.github.com> Date: Tue, 19 Mar 2024 21:11:59 +0530 Subject: [PATCH 22/46] Update Cargo.toml --- crates/ripemd160/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ripemd160/Cargo.toml b/crates/ripemd160/Cargo.toml index 49b9d71..ca0836f 100644 --- a/crates/ripemd160/Cargo.toml +++ b/crates/ripemd160/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ripemd160" -authors = ["Shourya Goel ", "Himanshu Raheja "] +authors = ["Shourya Goel ", "Himanshu Raheja "] version = "0.1.0" edition = "2021" license.workspace = true From a035af5e9f61dd75b9c1e1e8c8d7991337c4705d Mon Sep 17 00:00:00 2001 From: Saravanan Vijayakumaran Date: Thu, 28 Mar 2024 12:47:28 +0530 Subject: [PATCH 23/46] Added helper function to handle endianness --- crates/ripemd160/src/ripemd160.rs | 52 ++++++------------------------- crates/ripemd160/src/util.rs | 34 ++++++++++++++++++++ 2 files changed, 44 insertions(+), 42 deletions(-) diff --git a/crates/ripemd160/src/ripemd160.rs b/crates/ripemd160/src/ripemd160.rs index 551c513..b883fd8 100644 --- a/crates/ripemd160/src/ripemd160.rs +++ b/crates/ripemd160/src/ripemd160.rs @@ -8,7 +8,7 @@ use bellpepper_core::{boolean::Boolean, ConstraintSystem}; use ff::PrimeField; use std::convert::TryInto; -use crate::util::{or_uint32, ripemd_d1, ripemd_d2, shl_uint32}; +use crate::util::{swap_byte_endianness, or_uint32, ripemd_d1, ripemd_d2, shl_uint32}; #[allow(clippy::unreadable_literal)] const MD_BUFFERS: [u32; 5] = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0]; @@ -50,25 +50,8 @@ where assert!(padded.len() % 512 == 0); - for i in (0..padded.len()).step_by(32) { - let mut tmp = padded[i..i + 32].to_vec(); - tmp.reverse(); - let mut iter = 0; - for j in tmp { - padded[i + iter] = j; - iter += 1; - } - } - - for i in (0..padded.len()).step_by(8) { - let mut tmp = padded[i..i + 8].to_vec(); - tmp.reverse(); - let mut iter = 0; - for j in tmp { - padded[i + iter] = j; - iter += 1; - } - } + // Make the bytes little-endian + let padded = swap_byte_endianness(&padded); let mut cur_md = get_ripemd160_md("md"); let mut cur_md_prime = get_ripemd160_md("md_prime"); @@ -95,29 +78,14 @@ where cur_md_prime = cur_md.clone(); } let array_data: Result<[UInt32; 5], _> = cur_md.try_into(); - let mut result = array_data + let result = array_data .unwrap() .into_iter() - .flat_map(|e| e.into_bits_be()) + .flat_map(|e| e.into_bits()) .collect::>(); - for i in (0..result.len()).step_by(32) { - let mut tmp = result[i..i + 32].to_vec(); - tmp.reverse(); - let mut iter = 0; - for j in tmp { - result[i + iter] = j; - iter += 1; - } - } - for i in (0..result.len()).step_by(8) { - let mut tmp = result[i..i + 8].to_vec(); - tmp.reverse(); - let mut iter = 0; - for j in tmp { - result[i + iter] = j; - iter += 1; - } - } + + // Make the bytes big-endian + let result = swap_byte_endianness(&result); Ok(result.try_into().unwrap()) } @@ -340,7 +308,7 @@ where assert_eq!(input.len(), 512); let w = input .chunks(32) - .map(UInt32::from_bits_be) + .map(UInt32::from_bits) .collect::>(); let mut cs = MultiEq::new(cs); assert_eq!(w.len(), 16); @@ -409,7 +377,7 @@ where assert_eq!(input.len(), 512); let w = input .chunks(32) - .map(UInt32::from_bits_be) + .map(UInt32::from_bits) .collect::>(); assert_eq!(w.len(), 16); let mut cs = MultiEq::new(cs); diff --git a/crates/ripemd160/src/util.rs b/crates/ripemd160/src/util.rs index fa6f815..6903e27 100644 --- a/crates/ripemd160/src/util.rs +++ b/crates/ripemd160/src/util.rs @@ -2,6 +2,17 @@ use bellpepper::gadgets::uint32::UInt32; use bellpepper_core::{boolean::AllocatedBit, boolean::Boolean, ConstraintSystem, SynthesisError}; use ff::PrimeField; +pub fn swap_byte_endianness(bits: &Vec) -> Vec { + assert!(bits.len() % 8 == 0); + let mut modified_bits = vec![]; + for i in 0..bits.len() / 8 { + for j in 0..8 { + modified_bits.push(bits[i * 8 + 7 - j].clone()); + } + } + modified_bits +} + fn ripemd160_d1<'a, Scalar, CS>( mut cs: CS, a: &'a Boolean, @@ -319,6 +330,29 @@ mod test { use super::*; + #[test] + fn test_swap_byte_endianness() { + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, + 0xbc, 0xe5, + ]); + + for _ in 0..50 { + let a = rng.next_u32(); + let a_le_bytes = a.to_le_bytes(); + let a_rev_le_bytes = a_le_bytes.map(|b| b.reverse_bits()); + let a_rev = u32::from_le_bytes(a_rev_le_bytes); + + let a_bits = UInt32::constant(a).into_bits_be(); + let a_rev_bits = UInt32::constant(a_rev).into_bits_be(); + let a_rev_bits_exp = swap_byte_endianness(&a_bits); + + for (x, y) in a_rev_bits.into_iter().zip(a_rev_bits_exp.into_iter()) { + assert_eq!(x.get_value().unwrap(), y.get_value().unwrap()); + } + } + } + #[test] fn test_uint32_shl() { let mut rng = XorShiftRng::from_seed([ From 2cb9cd6809ab217dac633b1e052a8f4d4574f9ae Mon Sep 17 00:00:00 2001 From: Saravanan Vijayakumaran Date: Thu, 28 Mar 2024 12:51:11 +0530 Subject: [PATCH 24/46] cargo fmt --- crates/ripemd160/src/ripemd160.rs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/crates/ripemd160/src/ripemd160.rs b/crates/ripemd160/src/ripemd160.rs index b883fd8..7f5d2c4 100644 --- a/crates/ripemd160/src/ripemd160.rs +++ b/crates/ripemd160/src/ripemd160.rs @@ -8,7 +8,7 @@ use bellpepper_core::{boolean::Boolean, ConstraintSystem}; use ff::PrimeField; use std::convert::TryInto; -use crate::util::{swap_byte_endianness, or_uint32, ripemd_d1, ripemd_d2, shl_uint32}; +use crate::util::{or_uint32, ripemd_d1, ripemd_d2, shl_uint32, swap_byte_endianness}; #[allow(clippy::unreadable_literal)] const MD_BUFFERS: [u32; 5] = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0]; @@ -306,10 +306,7 @@ where CS: ConstraintSystem, { assert_eq!(input.len(), 512); - let w = input - .chunks(32) - .map(UInt32::from_bits) - .collect::>(); + let w = input.chunks(32).map(UInt32::from_bits).collect::>(); let mut cs = MultiEq::new(cs); assert_eq!(w.len(), 16); let mut s_val = [11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8]; @@ -375,10 +372,7 @@ where CS: ConstraintSystem, { assert_eq!(input.len(), 512); - let w = input - .chunks(32) - .map(UInt32::from_bits) - .collect::>(); + let w = input.chunks(32).map(UInt32::from_bits).collect::>(); assert_eq!(w.len(), 16); let mut cs = MultiEq::new(cs); let mut s_val = [8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6]; @@ -514,4 +508,4 @@ mod test { } } } -} \ No newline at end of file +} From 27ea4482e2d77a66de73dfa91b521b79e60512da Mon Sep 17 00:00:00 2001 From: Saravanan Vijayakumaran Date: Thu, 28 Mar 2024 12:56:13 +0530 Subject: [PATCH 25/46] Remove repeated initial value --- crates/ripemd160/src/ripemd160.rs | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/crates/ripemd160/src/ripemd160.rs b/crates/ripemd160/src/ripemd160.rs index 7f5d2c4..db02ced 100644 --- a/crates/ripemd160/src/ripemd160.rs +++ b/crates/ripemd160/src/ripemd160.rs @@ -10,11 +10,7 @@ use std::convert::TryInto; use crate::util::{or_uint32, ripemd_d1, ripemd_d2, shl_uint32, swap_byte_endianness}; -#[allow(clippy::unreadable_literal)] -const MD_BUFFERS: [u32; 5] = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0]; - -#[allow(clippy::unreadable_literal)] -const MD_BUFFERS_PRIME: [u32; 5] = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0]; +const IV: [u32; 5] = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0]; #[allow(clippy::unreadable_literal)] const K_BUFFER: [u32; 5] = [0x00000000, 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xa953fd4e]; @@ -53,8 +49,8 @@ where // Make the bytes little-endian let padded = swap_byte_endianness(&padded); - let mut cur_md = get_ripemd160_md("md"); - let mut cur_md_prime = get_ripemd160_md("md_prime"); + let mut cur_md = get_ripemd160_iv(); + let mut cur_md_prime = get_ripemd160_iv(); let mut cs = MultiEq::new(cs); for (i, block) in padded.chunks(512).enumerate() { let prev_md = cur_md.clone(); @@ -115,12 +111,8 @@ where Ok(update_md) } -fn get_ripemd160_md(input: &str) -> [UInt32; 5] { - match input { - "md" => MD_BUFFERS.map(UInt32::constant), - "md_prime" => MD_BUFFERS_PRIME.map(UInt32::constant), - _ => panic!("Invalid input"), - } +fn get_ripemd160_iv() -> [UInt32; 5] { + IV.map(UInt32::constant) } fn compute_f(cs: CS, md_val: &mut [UInt32; 5], index: usize, left: bool) -> UInt32 From 47403f3df42ca247a3c12cb21fada3deb29deda1 Mon Sep 17 00:00:00 2001 From: Saravanan Vijayakumaran Date: Thu, 28 Mar 2024 15:30:33 +0530 Subject: [PATCH 26/46] Simpler rotate left implementation --- crates/ripemd160/src/ripemd160.rs | 17 ++--- crates/ripemd160/src/util.rs | 111 ++---------------------------- 2 files changed, 9 insertions(+), 119 deletions(-) diff --git a/crates/ripemd160/src/ripemd160.rs b/crates/ripemd160/src/ripemd160.rs index db02ced..e853057 100644 --- a/crates/ripemd160/src/ripemd160.rs +++ b/crates/ripemd160/src/ripemd160.rs @@ -8,7 +8,7 @@ use bellpepper_core::{boolean::Boolean, ConstraintSystem}; use ff::PrimeField; use std::convert::TryInto; -use crate::util::{or_uint32, ripemd_d1, ripemd_d2, shl_uint32, swap_byte_endianness}; +use crate::util::{ripemd_d1, ripemd_d2, swap_byte_endianness, uint32_rotl}; const IV: [u32; 5] = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0]; @@ -267,23 +267,14 @@ fn block( ], ) .unwrap(); - tmp1 = or_uint32( - cs.namespace(|| format!("first or block {} left {} index {}", index, left, i)), - &shl_uint32(&tmp1, s_val[i]).unwrap(), - &UInt32::shr(&tmp1, 32 - s_val[i]), - ) - .unwrap(); + tmp1 = uint32_rotl(tmp1, s_val[i]); tmp1 = UInt32::addmany( cs.namespace(|| format!("second add_many block {} left {} index {}", index, left, i)), &[tmp1, md_val[4].clone()], ) .unwrap(); - let tmp2 = or_uint32( - cs.namespace(|| format!("second or block {} left {} index {}", index, left, i)), - &shl_uint32(&md_val[2], 10).unwrap(), - &UInt32::shr(&md_val[2], 32 - 10), - ) - .unwrap(); + + let tmp2 = uint32_rotl(md_val[2].clone(), 10); md_val[0] = md_val[4].clone(); md_val[2] = md_val[1].clone(); md_val[4] = md_val[3].clone(); diff --git a/crates/ripemd160/src/util.rs b/crates/ripemd160/src/util.rs index 6903e27..47de8e6 100644 --- a/crates/ripemd160/src/util.rs +++ b/crates/ripemd160/src/util.rs @@ -13,6 +13,11 @@ pub fn swap_byte_endianness(bits: &Vec) -> Vec { modified_bits } +pub fn uint32_rotl(a: UInt32, by: usize) -> UInt32 { + assert!(by < 32usize); + a.rotr(32 - by) +} + fn ripemd160_d1<'a, Scalar, CS>( mut cs: CS, a: &'a Boolean, @@ -234,19 +239,6 @@ where .into()) } -pub fn shl_uint32(a: &UInt32, by: usize) -> Result { - let by = by % 32; - - let fill = Boolean::constant(false); - let new_bits: Vec<_> = std::iter::repeat(&fill) - .take(by) - .chain(a.clone().into_bits().iter()) // The bits are least significant first - .take(32) // Only 32 bits needed! - .cloned() - .collect(); - Ok(UInt32::from_bits(&new_bits)) -} - fn triop( mut cs: CS, a: &UInt32, @@ -302,24 +294,6 @@ where }) } -/// OR two `UInt32` variables -pub fn or_uint32(mut cs: CS, a: &UInt32, b: &UInt32) -> Result -where - Scalar: PrimeField, - CS: ConstraintSystem, -{ - let a_bits = a.clone().into_bits(); - let b_bits = b.clone().into_bits(); - let or_bits = a_bits - .iter() - .zip(b_bits.iter()) - .enumerate() - .map(|(i, (x, y))| Boolean::or(cs.namespace(|| format!("or {i}")), x, y).unwrap()) - .collect::>(); - - Ok(UInt32::from_bits(&or_bits)) -} - #[cfg(test)] mod test { @@ -353,38 +327,6 @@ mod test { } } - #[test] - fn test_uint32_shl() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - for _ in 0..50 { - for i in 0..60 { - let mut cs = TestConstraintSystem::::new(); - let num = rng.next_u32(); - let mut expected = num.wrapping_shl(i as u32); - let num_bit = UInt32::alloc(cs.namespace(|| "num_bit"), Some(num)).unwrap(); - let res = shl_uint32(&num_bit, i).unwrap(); - for b in res.into_bits() { - match b { - Boolean::Is(ref b) => { - assert_eq!(b.get_value().unwrap(), expected & 1 == 1); - } - Boolean::Not(ref b) => { - assert_ne!(b.get_value().unwrap(), expected & 1 == 1); - } - Boolean::Constant(b) => { - assert_eq!(b, expected & 1 == 1); - } - } - - expected >>= 1; - } - } - } - } - #[test] fn test_uint32_ripemd_d1() { let mut rng = XorShiftRng::from_seed([ @@ -467,47 +409,4 @@ mod test { } } } - - #[test] - fn test_uint32_or() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for _ in 0..1000 { - let mut cs = TestConstraintSystem::::new(); - - let a = rng.next_u32(); - let b = rng.next_u32(); - let c = rng.next_u32(); - - let mut expected = a | 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 = or_uint32(cs.namespace(|| "first or"), &a_bit, &b_bit).unwrap(); - let r = or_uint32(cs.namespace(|| "second or"), &r, &c_bit).unwrap(); - - assert!(cs.is_satisfied()); - - for b in r.into_bits() { - match b { - Boolean::Is(ref b) => { - assert_eq!(b.get_value().unwrap(), expected & 1 == 1); - } - Boolean::Not(ref b) => { - assert_ne!(b.get_value().unwrap(), expected & 1 == 1); - } - Boolean::Constant(b) => { - assert_eq!(b, expected & 1 == 1); - } - } - - expected >>= 1; - } - } - } } From 89aee5e85110fc65c4be050a9582d70122d7da5e Mon Sep 17 00:00:00 2001 From: Saravanan Vijayakumaran Date: Thu, 28 Mar 2024 15:57:37 +0530 Subject: [PATCH 27/46] Simpler implementations of ripemd160_d1/d2 --- crates/ripemd160/src/util.rs | 216 ++--------------------------------- 1 file changed, 12 insertions(+), 204 deletions(-) diff --git a/crates/ripemd160/src/util.rs b/crates/ripemd160/src/util.rs index 47de8e6..b2c8df6 100644 --- a/crates/ripemd160/src/util.rs +++ b/crates/ripemd160/src/util.rs @@ -1,5 +1,5 @@ use bellpepper::gadgets::uint32::UInt32; -use bellpepper_core::{boolean::AllocatedBit, boolean::Boolean, ConstraintSystem, SynthesisError}; +use bellpepper_core::{boolean::Boolean, ConstraintSystem, SynthesisError}; use ff::PrimeField; pub fn swap_byte_endianness(bits: &Vec) -> Vec { @@ -20,223 +20,31 @@ pub fn uint32_rotl(a: UInt32, by: usize) -> UInt32 { fn ripemd160_d1<'a, Scalar, CS>( mut cs: CS, - a: &'a Boolean, - b: &'a Boolean, - c: &'a Boolean, + y: &'a Boolean, + x: &'a Boolean, + z: &'a Boolean, ) -> Result where Scalar: PrimeField, CS: ConstraintSystem, { - let d1_value = match (a.get_value(), b.get_value(), c.get_value()) { - (Some(a), Some(b), Some(c)) => { - // (a and b) or (c and not(b)) - Some((a & b) | (c & !b)) - } - _ => None, - }; - - match (a, b, c) { - (&Boolean::Constant(_), &Boolean::Constant(_), &Boolean::Constant(_)) => { - // They're all constants, so we can just compute the value. - - return Ok(Boolean::Constant(d1_value.expect("they're all constants"))); - } - (&Boolean::Constant(false), b, c) => { - // If a is false, - // (a and b) or (c and not(b)) - // equals - // (c and not(b)) - return Boolean::and(cs, &b.not(), c); - } - (_a, &Boolean::Constant(false), c) => { - // If b is false, - // (a and b) or (c and not(b)) - // equals - // (a and c) - return Ok(c.clone()); - } - (a, b, &Boolean::Constant(false)) => { - // If c is false, - // (a and b) or (c and not(b)) - // equals - // (a and b) - return Boolean::and(cs, a, b); - } - (a, b, &Boolean::Constant(true)) => { - // If c is true, - // (a and b) or (c and not(b)) - // equals - // (a or not(b)) - return Boolean::or(cs, a, &b.not()); - } - (a, &Boolean::Constant(true), _c) => { - // If b is true, - // (a and b) or (c and not(b)) - // equals - // (a and true) or (false) - // equals - // a - return Ok(a.clone()); - } - (&Boolean::Constant(true), b, c) => { - // If a is true, - // (a and b) or (c and not(b)) - // equals - // (b) xor (c) xor (b and c) - return Boolean::or(cs, b, c); - } - ( - &Boolean::Is(_) | &Boolean::Not(_), - &Boolean::Is(_) | &Boolean::Not(_), - &Boolean::Is(_) | &Boolean::Not(_), - ) => {} - } - - let d1 = cs.alloc( - || "d1", - || { - d1_value.ok_or(SynthesisError::AssignmentMissing).map(|v| { - if v { - Scalar::ONE - } else { - Scalar::ZERO - } - }) - }, - )?; - - // (a and b) or (c and not(b)) = d1 - // ab + c(1-b) = d1 - // b(a - c) = d1 - c - // (c - a)b = c - d1 - - cs.enforce( - || "d1 computation", - |_| c.lc(CS::one(), Scalar::ONE) - &a.lc(CS::one(), Scalar::ONE), - |_| b.lc(CS::one(), Scalar::ONE), - |_| c.lc(CS::one(), Scalar::ONE) - d1, - ); - - Ok(AllocatedBit::alloc(cs.namespace(|| "d1_alloc"), d1_value) - .unwrap() - .into()) + let tmp1 = Boolean::and(cs.namespace(|| "x AND y"), x, y)?; + let tmp2 = Boolean::and(cs.namespace(|| "(!x) AND z"), &x.not(), z)?; + Boolean::or(cs.namespace(|| "(x AND y) OR ((!x) AND z)"), &tmp1, &tmp2) } fn ripemd160_d2<'a, Scalar, CS>( mut cs: CS, - a: &'a Boolean, - b: &'a Boolean, - c: &'a Boolean, + x: &'a Boolean, + y: &'a Boolean, + z: &'a Boolean, ) -> Result where Scalar: PrimeField, CS: ConstraintSystem, { - let d2_value = match (a.get_value(), b.get_value(), c.get_value()) { - (Some(a), Some(b), Some(c)) => { - // (a) xor (b or not(c)) - Some((a) ^ (b | !c)) - } - _ => None, - }; - - match (a, b, c) { - (&Boolean::Constant(_), &Boolean::Constant(_), &Boolean::Constant(_)) => { - // They're all constants, so we can just compute the value. - - return Ok(Boolean::Constant(d2_value.expect("they're all constants"))); - } - (&Boolean::Constant(false), b, c) => { - // If a is false, - // (a) xor (b or not(c)) - // equals - // (b or not(c)) - return Boolean::or(cs, b, &c.not()); - } - (a, &Boolean::Constant(false), c) => { - // If b is false, - // (a) xor (b or not(c)) - // equals - // (a xor not(c)) - return Boolean::xor(cs, a, &c.not()); - } - (a, _b, &Boolean::Constant(false)) => { - // If c is false, - // (a) xor (b or not(c)) - // equals - // (a) xor (true) - // equals - // not a - return Ok(a.not()); - } - (a, b, &Boolean::Constant(true)) => { - // If c is true, - // (a) xor (b or not(c)) - // equals - // (a) xor (b) - return Boolean::xor(cs, a, b); - } - (a, &Boolean::Constant(true), _c) => { - // If b is true, - // (a) xor (b or not(c)) - // equals - // (a) xor (true) - // equals - // not a - return Ok(a.not()); - } - (&Boolean::Constant(true), b, c) => { - // If a is true, - // (a) xor (b or not(c)) - // equals - // (not(b) and c) - return Boolean::and(cs, c, &b.not()); - } - ( - &Boolean::Is(_) | &Boolean::Not(_), - &Boolean::Is(_) | &Boolean::Not(_), - &Boolean::Is(_) | &Boolean::Not(_), - ) => {} - } - - let d2 = cs.alloc( - || "d2", - || { - d2_value.ok_or(SynthesisError::AssignmentMissing).map(|v| { - if v { - Scalar::ONE - } else { - Scalar::ZERO - } - }) - }, - )?; - - // (a) xor (b or not(c)) = d2 - // (not a) * (b + (1 - c)) + (a) * (1 - b) * (c) - // 2ac - abc - ab - a - c + 1 + b = d2 - // b * (a + ac) = 2ac - a - c + 1 + b - d2 - - let ac = Boolean::and(cs.namespace(|| "(a and c)"), a, c)?; - - cs.enforce( - || "d2 computation", - |_| b.lc(CS::one(), Scalar::ONE), - |_| a.lc(CS::one(), Scalar::ONE) + &ac.lc(CS::one(), Scalar::ONE), - |lc| { - lc + CS::one() + &b.lc(CS::one(), Scalar::ONE) - - &c.lc(CS::one(), Scalar::ONE) - - &a.lc(CS::one(), Scalar::ONE) - + &ac.lc(CS::one(), Scalar::ONE) - + &ac.lc(CS::one(), Scalar::ONE) - - d2 - }, - ); - - Ok(AllocatedBit::alloc(cs.namespace(|| "d2_alloc"), d2_value) - .unwrap() - .into()) + let tmp = Boolean::or(cs.namespace(|| "y OR !z"), y, &z.not())?; + Boolean::xor(cs.namespace(|| "x XOR (y OR !z)"), x, &tmp) } fn triop( From 27bf2f5e917f34adf215aef3674443fcb10c6e5d Mon Sep 17 00:00:00 2001 From: Saravanan Vijayakumaran Date: Thu, 28 Mar 2024 18:24:02 +0530 Subject: [PATCH 28/46] Simplify implementation - Added functions f1 to f5 in util.rs to match the RIPEMD160 spec - Combined the left and right match statements in compute_f - Incorporate all 80 rounds into the block method - Replaced 5 calls to block in left_step and right_step with single call - Consolidated message index and rotate left constants --- crates/ripemd160/src/ripemd160.rs | 327 +++++++++--------------------- crates/ripemd160/src/util.rs | 209 ++++++++++++++----- 2 files changed, 250 insertions(+), 286 deletions(-) diff --git a/crates/ripemd160/src/ripemd160.rs b/crates/ripemd160/src/ripemd160.rs index e853057..9d9fb22 100644 --- a/crates/ripemd160/src/ripemd160.rs +++ b/crates/ripemd160/src/ripemd160.rs @@ -8,15 +8,34 @@ use bellpepper_core::{boolean::Boolean, ConstraintSystem}; use ff::PrimeField; use std::convert::TryInto; -use crate::util::{ripemd_d1, ripemd_d2, swap_byte_endianness, uint32_rotl}; +use crate::util::{f1, f2, f3, f4, f5, swap_byte_endianness, uint32_rotl}; const IV: [u32; 5] = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0]; +const ROUND_CONSTANTS_LEFT: [u32; 5] = [0x00000000, 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xa953fd4e]; +const ROUND_CONSTANTS_RIGHT: [u32; 5] = + [0x50a28be6, 0x5c4dd124, 0x6d703ef3, 0x7a6d76e9, 0x00000000]; -#[allow(clippy::unreadable_literal)] -const K_BUFFER: [u32; 5] = [0x00000000, 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xa953fd4e]; +pub const MSG_SEL_IDX_LEFT: [usize; 80] = [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, + 2, 14, 11, 8, 3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12, 1, 9, 11, 10, 0, 8, 12, 4, + 13, 3, 7, 15, 14, 5, 6, 2, 4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13, +]; +pub const MSG_SEL_IDX_RIGHT: [usize; 80] = [ + 5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12, 6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, + 4, 9, 1, 2, 15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13, 8, 6, 4, 1, 3, 11, 15, 0, 5, + 12, 2, 13, 9, 7, 10, 14, 12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11, +]; -#[allow(clippy::unreadable_literal)] -const K_BUFFER_PRIME: [u32; 5] = [0x50a28be6, 0x5c4dd124, 0x6d703ef3, 0x7a6d76e9, 0x00000000]; +pub const ROL_AMOUNT_LEFT: [usize; 80] = [ + 11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8, 7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, + 9, 11, 7, 13, 12, 11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5, 11, 12, 14, 15, 14, + 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12, 9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6, +]; +pub const ROL_AMOUNT_RIGHT: [usize; 80] = [ + 8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6, 9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, + 7, 6, 15, 13, 11, 9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5, 15, 5, 8, 11, 14, 14, + 6, 14, 6, 9, 12, 9, 12, 5, 15, 8, 8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11, +]; pub fn ripemd160( cs: CS, @@ -50,7 +69,7 @@ where let padded = swap_byte_endianness(&padded); let mut cur_md = get_ripemd160_iv(); - let mut cur_md_prime = get_ripemd160_iv(); + let mut cur_md_prime = cur_md.clone(); let mut cs = MultiEq::new(cs); for (i, block) in padded.chunks(512).enumerate() { let prev_md = cur_md.clone(); @@ -73,9 +92,7 @@ where .unwrap(); cur_md_prime = cur_md.clone(); } - let array_data: Result<[UInt32; 5], _> = cur_md.try_into(); - let result = array_data - .unwrap() + let result = cur_md .into_iter() .flat_map(|e| e.into_bits()) .collect::>(); @@ -115,161 +132,97 @@ fn get_ripemd160_iv() -> [UInt32; 5] { IV.map(UInt32::constant) } -fn compute_f(cs: CS, md_val: &mut [UInt32; 5], index: usize, left: bool) -> UInt32 +fn compute_f(mut cs: CS, md_val: &mut [UInt32; 5], index: usize, left: bool) -> UInt32 where Scalar: PrimeField, CS: ConstraintSystem, { - let f: UInt32; - let mut cs = MultiEq::new(cs); - match left { - true => match index { - 0 => { - f = md_val[1] - .xor( - cs.namespace(|| format!("first xor block {} left {} ", index, left)), - &md_val[2], - ) - .unwrap() - .xor( - cs.namespace(|| format!("second xor block {} left {}", index, left)), - &md_val[3], - ) - .unwrap(); - } - 1 => { - f = ripemd_d1( - cs.namespace(|| format!("d1 block {} left {}", index, left)), - &md_val[2], - &md_val[1], - &md_val[3], - ) - .unwrap(); - } - 2 => { - f = ripemd_d2( - cs.namespace(|| format!("d2 block {} left {}", index, left)), - &md_val[3], - &md_val[1], - &md_val[2], - ) - .unwrap(); - } - 3 => { - f = ripemd_d1( - cs.namespace(|| format!("d1 block {} left {}", index, left)), - &md_val[1], - &md_val[3], - &md_val[2], - ) - .unwrap(); - } - 4 => { - f = ripemd_d2( - cs.namespace(|| format!("d2 block {} left {}", index, left)), - &md_val[1], - &md_val[2], - &md_val[3], - ) - .unwrap(); - } - _ => panic!("Invalid index"), - }, - false => match index { - 0 => { - f = ripemd_d2( - cs.namespace(|| format!("d2 block {} left {}", index, left)), - &md_val[1], - &md_val[2], - &md_val[3], - ) - .unwrap(); - } - 1 => { - f = ripemd_d1( - cs.namespace(|| format!("d1 block {} left {}", index, left)), - &md_val[1], - &md_val[3], - &md_val[2], - ) - .unwrap(); - } - 2 => { - f = ripemd_d2( - cs.namespace(|| format!("d2 block {} left {}", index, left)), - &md_val[3], - &md_val[1], - &md_val[2], - ) - .unwrap(); - } - 3 => { - f = ripemd_d1( - cs.namespace(|| format!("d1 block {} left {}", index, left)), - &md_val[2], - &md_val[1], - &md_val[3], - ) - .unwrap(); - } - 4 => { - f = md_val[1] - .xor( - cs.namespace(|| format!("first xor block {} left {}", index, left)), - &md_val[2], - ) - .unwrap() - .xor( - cs.namespace(|| format!("second xor block {} left {}", index, left)), - &md_val[3], - ) - .unwrap(); - } - _ => panic!("Invalid index"), - }, - } + let f = match (index, left) { + (0, true) | (4, false) => f1( + cs.namespace(|| "f1 {index} {left}"), + &md_val[1], + &md_val[2], + &md_val[3], + ) + .unwrap(), + (1, true) | (3, false) => f2( + cs.namespace(|| "f2 {index} {left}"), + &md_val[1], + &md_val[2], + &md_val[3], + ) + .unwrap(), + (2, _) => f3( + cs.namespace(|| "f3 {index} {left}"), + &md_val[1], + &md_val[2], + &md_val[3], + ) + .unwrap(), + (3, true) | (1, false) => f4( + cs.namespace(|| "f4 {index} {left}"), + &md_val[1], + &md_val[2], + &md_val[3], + ) + .unwrap(), + (4, true) | (0, false) => f5( + cs.namespace(|| "f5 {index} {left}"), + &md_val[1], + &md_val[2], + &md_val[3], + ) + .unwrap(), + _ => panic!("Invalid index"), + }; f } -fn block( - cs: CS, - md_val: &mut [UInt32; 5], - s_val: [usize; 16], - i_val: [usize; 16], - w: Vec, - index: usize, - left: bool, -) where +fn block(cs: CS, md_val: &mut [UInt32; 5], w: Vec, left: bool) +where Scalar: PrimeField, CS: ConstraintSystem, { let mut cs = MultiEq::new(cs); let mut f: UInt32; - - let k_val = match left { - true => K_BUFFER, - false => K_BUFFER_PRIME, + let (k_val, i_val, s_val) = if left { + (ROUND_CONSTANTS_LEFT, MSG_SEL_IDX_LEFT, ROL_AMOUNT_LEFT) + } else { + (ROUND_CONSTANTS_RIGHT, MSG_SEL_IDX_RIGHT, ROL_AMOUNT_RIGHT) }; - for i in 0..16 { + + for i in 0..80 { + let phase_index = i / 16; + f = compute_f( - cs.namespace(|| format!("Compute F block {} left {} index {}", index, left, i)), + cs.namespace(|| format!("Compute F block {} left {} index {}", phase_index, left, i)), md_val, - index, + phase_index, left, ); let mut tmp1 = UInt32::addmany( - cs.namespace(|| format!("first add_many block {} left {} index {}", index, left, i)), + cs.namespace(|| { + format!( + "first add_many block {} left {} index {}", + phase_index, left, i + ) + }), &[ md_val[0].clone(), f.clone(), w[i_val[i]].clone(), - UInt32::constant(k_val[index]), + UInt32::constant(k_val[phase_index]), ], ) .unwrap(); tmp1 = uint32_rotl(tmp1, s_val[i]); tmp1 = UInt32::addmany( - cs.namespace(|| format!("second add_many block {} left {} index {}", index, left, i)), + cs.namespace(|| { + format!( + "second add_many block {} left {} index {}", + phase_index, left, i + ) + }), &[tmp1, md_val[4].clone()], ) .unwrap(); @@ -292,59 +245,10 @@ where let w = input.chunks(32).map(UInt32::from_bits).collect::>(); let mut cs = MultiEq::new(cs); assert_eq!(w.len(), 16); - let mut s_val = [11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8]; - let mut i_val = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; block( cs.namespace(|| "block 0"), current_md_value, - s_val, - i_val, - w.clone(), - 0, - true, - ); - s_val = [7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12]; - i_val = [7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8]; - block( - cs.namespace(|| "block 1"), - current_md_value, - s_val, - i_val, w.clone(), - 1, - true, - ); - s_val = [11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5]; - i_val = [3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12]; - block( - cs.namespace(|| "block 2"), - current_md_value, - s_val, - i_val, - w.clone(), - 2, - true, - ); - s_val = [11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12]; - i_val = [1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2]; - block( - cs.namespace(|| "block 3"), - current_md_value, - s_val, - i_val, - w.clone(), - 3, - true, - ); - s_val = [9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6]; - i_val = [4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13]; - block( - cs.namespace(|| "block 4"), - current_md_value, - s_val, - i_val, - w.clone(), - 4, true, ); } @@ -356,61 +260,12 @@ where { assert_eq!(input.len(), 512); let w = input.chunks(32).map(UInt32::from_bits).collect::>(); - assert_eq!(w.len(), 16); let mut cs = MultiEq::new(cs); - let mut s_val = [8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6]; - let mut i_val = [5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12]; + assert_eq!(w.len(), 16); block( cs.namespace(|| "block 0"), current_md_value, - s_val, - i_val, - w.clone(), - 0, - false, - ); - s_val = [9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11]; - i_val = [6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2]; - block( - cs.namespace(|| "block 1"), - current_md_value, - s_val, - i_val, - w.clone(), - 1, - false, - ); - s_val = [9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5]; - i_val = [15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13]; - block( - cs.namespace(|| "block 2"), - current_md_value, - s_val, - i_val, - w.clone(), - 2, - false, - ); - s_val = [15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8]; - i_val = [8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14]; - block( - cs.namespace(|| "block 3"), - current_md_value, - s_val, - i_val, - w.clone(), - 3, - false, - ); - s_val = [8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11]; - i_val = [12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11]; - block( - cs.namespace(|| "block 4"), - current_md_value, - s_val, - i_val, w.clone(), - 4, false, ); } diff --git a/crates/ripemd160/src/util.rs b/crates/ripemd160/src/util.rs index b2c8df6..b0dfc27 100644 --- a/crates/ripemd160/src/util.rs +++ b/crates/ripemd160/src/util.rs @@ -18,35 +18,6 @@ pub fn uint32_rotl(a: UInt32, by: usize) -> UInt32 { a.rotr(32 - by) } -fn ripemd160_d1<'a, Scalar, CS>( - mut cs: CS, - y: &'a Boolean, - x: &'a Boolean, - z: &'a Boolean, -) -> Result -where - Scalar: PrimeField, - CS: ConstraintSystem, -{ - let tmp1 = Boolean::and(cs.namespace(|| "x AND y"), x, y)?; - let tmp2 = Boolean::and(cs.namespace(|| "(!x) AND z"), &x.not(), z)?; - Boolean::or(cs.namespace(|| "(x AND y) OR ((!x) AND z)"), &tmp1, &tmp2) -} - -fn ripemd160_d2<'a, Scalar, CS>( - mut cs: CS, - x: &'a Boolean, - y: &'a Boolean, - z: &'a Boolean, -) -> Result -where - Scalar: PrimeField, - CS: ConstraintSystem, -{ - let tmp = Boolean::or(cs.namespace(|| "y OR !z"), y, &z.not())?; - Boolean::xor(cs.namespace(|| "x XOR (y OR !z)"), x, &tmp) -} - fn triop( mut cs: CS, a: &UInt32, @@ -72,33 +43,86 @@ where Ok(UInt32::from_bits(&bits)) } -pub fn ripemd_d1( - cs: CS, - a: &UInt32, - b: &UInt32, - c: &UInt32, +pub fn f1( + mut cs: CS, + x: &UInt32, + y: &UInt32, + z: &UInt32, ) -> Result where Scalar: PrimeField, CS: ConstraintSystem, { - triop(cs, a, b, c, |cs, i, a, b, c| { - ripemd160_d1(cs.namespace(|| format!("d1 {}", i)), a, b, c) + let res = x.xor(cs.namespace(|| "x XOR y"), y)?; + res.xor(cs.namespace(|| "x XOR y XOR z"), z) +} + +fn f2_boolean( + mut cs: CS, + x: &Boolean, + y: &Boolean, + z: &Boolean, +) -> Result +where + Scalar: PrimeField, + CS: ConstraintSystem, +{ + let tmp1 = Boolean::and(cs.namespace(|| "x AND y"), x, y)?; + let tmp2 = Boolean::and(cs.namespace(|| "(!x) AND z"), &x.not(), z)?; + Boolean::or(cs.namespace(|| "(x AND y) OR ((!x) AND z)"), &tmp1, &tmp2) +} + +pub fn f2(cs: CS, x: &UInt32, y: &UInt32, z: &UInt32) -> Result +where + Scalar: PrimeField, + CS: ConstraintSystem, +{ + triop(cs, x, y, z, |cs, i, a, b, c| { + f2_boolean(cs.namespace(|| format!("f2 {}", i)), a, b, c) }) } -pub fn ripemd_d2( - cs: CS, - a: &UInt32, - b: &UInt32, - c: &UInt32, -) -> Result +fn f3_boolean( + mut cs: CS, + x: &Boolean, + y: &Boolean, + z: &Boolean, +) -> Result +where + Scalar: PrimeField, + CS: ConstraintSystem, +{ + let tmp = Boolean::or(cs.namespace(|| "x OR !y"), x, &y.not())?; + Boolean::xor(cs.namespace(|| "(x OR !y) XOR z"), &tmp, z) +} + +pub fn f3(cs: CS, x: &UInt32, y: &UInt32, z: &UInt32) -> Result +where + Scalar: PrimeField, + CS: ConstraintSystem, +{ + triop(cs, x, y, z, |cs, i, a, b, c| { + f3_boolean(cs.namespace(|| format!("f3 {}", i)), a, b, c) + }) +} + +pub fn f4(cs: CS, x: &UInt32, y: &UInt32, z: &UInt32) -> Result +where + Scalar: PrimeField, + CS: ConstraintSystem, +{ + triop(cs, x, y, z, |cs, i, a, b, c| { + f2_boolean(cs.namespace(|| format!("f4 {}", i)), c, a, b) + }) +} + +pub fn f5(cs: CS, x: &UInt32, y: &UInt32, z: &UInt32) -> Result where Scalar: PrimeField, CS: ConstraintSystem, { - triop(cs, a, b, c, |cs, i, a, b, c| { - ripemd160_d2(cs.namespace(|| format!("d2 {}", i)), a, b, c) + triop(cs, x, y, z, |cs, i, a, b, c| { + f3_boolean(cs.namespace(|| format!("f5 {}", i)), b, c, a) }) } @@ -136,7 +160,7 @@ mod test { } #[test] - fn test_uint32_ripemd_d1() { + fn test_uint32_f2() { let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, @@ -149,13 +173,13 @@ mod test { let b = rng.next_u32(); let c = rng.next_u32(); - let mut expected = (a & b) | ((!b) & c); + 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 = ripemd_d1(&mut cs, &a_bit, &b_bit, &c_bit).unwrap(); + let r = f2(&mut cs, &a_bit, &b_bit, &c_bit).unwrap(); assert!(cs.is_satisfied()); @@ -176,8 +200,93 @@ mod test { } } } + + #[test] + fn test_uint32_f4() { + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, + 0xbc, 0xe5, + ]); + + for _ in 0..1000 { + let mut cs = TestConstraintSystem::::new(); + + let a = rng.next_u32(); + let b = rng.next_u32(); + let c = rng.next_u32(); + + let mut expected = (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 = f4(&mut cs, &a_bit, &b_bit, &c_bit).unwrap(); + + assert!(cs.is_satisfied()); + + for b in r.into_bits().iter() { + match *b { + Boolean::Is(ref b) => { + assert_eq!(b.get_value().unwrap(), expected & 1 == 1); + } + Boolean::Not(ref b) => { + assert_ne!(b.get_value().unwrap(), expected & 1 == 1); + } + Boolean::Constant(b) => { + assert_eq!(b, expected & 1 == 1); + } + } + + expected >>= 1; + } + } + } + + #[test] + fn test_uint32_f3() { + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, + 0xbc, 0xe5, + ]); + + for _ in 0..1000 { + let mut cs = TestConstraintSystem::::new(); + + let a = rng.next_u32(); + let b = rng.next_u32(); + let c = rng.next_u32(); + + let mut expected = ((a) | !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 = f3(&mut cs, &a_bit, &b_bit, &c_bit).unwrap(); + + assert!(cs.is_satisfied()); + + for b in r.into_bits().iter() { + match *b { + Boolean::Is(ref b) => { + assert_eq!(b.get_value().unwrap(), expected & 1 == 1); + } + Boolean::Not(ref b) => { + assert_ne!(b.get_value().unwrap(), expected & 1 == 1); + } + Boolean::Constant(b) => { + assert_eq!(b, expected & 1 == 1); + } + } + + expected >>= 1; + } + } + } + #[test] - fn test_uint32_ripemd_d2() { + fn test_uint32_f5() { let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, @@ -190,13 +299,13 @@ mod test { let b = rng.next_u32(); let c = rng.next_u32(); - let mut expected = (a) ^ ((b) | !c); + let mut expected = a ^ (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 = ripemd_d2(&mut cs, &a_bit, &b_bit, &c_bit).unwrap(); + let r = f5(&mut cs, &a_bit, &b_bit, &c_bit).unwrap(); assert!(cs.is_satisfied()); From 888db4fe3a4c83287a074fb91c9b21eff3639ecd Mon Sep 17 00:00:00 2001 From: Saravanan Vijayakumaran Date: Fri, 29 Mar 2024 08:30:34 +0530 Subject: [PATCH 29/46] Improve readability and remove some redundancy - Renamed variables md_val to msg_digest - Renamed block to half_compression_function - Removed re-initialization of MultiEq in bloc - Removed left_step and right_step as they were thin wrappers around half_compression_function. Also, they initialized MultiEq again - Avoid assignment of constant arrays containing round constants, message selection indices, and rotate left amounts - Reworded namespace annotations - Removed re-initialization of MultiEq in combine_left_and_right - --- crates/ripemd160/src/ripemd160.rs | 206 +++++++++++++----------------- 1 file changed, 92 insertions(+), 114 deletions(-) diff --git a/crates/ripemd160/src/ripemd160.rs b/crates/ripemd160/src/ripemd160.rs index 9d9fb22..e753d4d 100644 --- a/crates/ripemd160/src/ripemd160.rs +++ b/crates/ripemd160/src/ripemd160.rs @@ -68,31 +68,42 @@ where // Make the bytes little-endian let padded = swap_byte_endianness(&padded); - let mut cur_md = get_ripemd160_iv(); - let mut cur_md_prime = cur_md.clone(); + let mut msg_digest_left = get_ripemd160_iv(); + let mut msg_digest_right = msg_digest_left.clone(); let mut cs = MultiEq::new(cs); - for (i, block) in padded.chunks(512).enumerate() { - let prev_md = cur_md.clone(); - left_step( - cs.namespace(|| format!("left_step {}", i)), - block, - &mut cur_md, + for (i, msg_block) in padded.chunks(512).enumerate() { + let prev_msg_digest_left = msg_digest_left.clone(); + let msg_words = msg_block + .chunks(32) + .map(UInt32::from_bits) + .collect::>(); + + // Process left half of RIPEMD-160 compressesion function + half_compression_function( + cs.namespace(|| format!("left half {}", i)), + &mut msg_digest_left, + msg_words.clone(), + true, ); - right_step( - cs.namespace(|| format!("right_step {}", i)), - block, - &mut cur_md_prime, + + // Process right half of RIPEMD-160 compressesion function + half_compression_function( + cs.namespace(|| format!("right half {}", i)), + &mut msg_digest_right, + msg_words, + false, ); - cur_md = combine_left_and_right( - cs.namespace(|| format!("combine_left_and_right_step {}", i)), - cur_md, - cur_md_prime, - prev_md, + + msg_digest_left = combine_left_and_right( + cs.namespace(|| format!("combine left and right {}", i)), + msg_digest_left, + msg_digest_right, + prev_msg_digest_left, ) .unwrap(); - cur_md_prime = cur_md.clone(); + msg_digest_right = msg_digest_left.clone(); } - let result = cur_md + let result = msg_digest_left .into_iter() .flat_map(|e| e.into_bits()) .collect::>(); @@ -102,30 +113,32 @@ where Ok(result.try_into().unwrap()) } -fn combine_left_and_right( - cs: CS, - cur_md: [UInt32; 5], - cur_md_prime: [UInt32; 5], - prev_md: [UInt32; 5], +fn combine_left_and_right( + mut cs: M, + msg_digest_left: [UInt32; 5], + msg_digest_right: [UInt32; 5], + prev_msg_digest_left: [UInt32; 5], ) -> Result<[UInt32; 5], bellpepper_core::SynthesisError> where Scalar: PrimeField, CS: ConstraintSystem, + M: ConstraintSystem>, { - let mut cs = MultiEq::new(cs); - let mut update_md = cur_md.clone(); + let mut combined_msg_digest: Vec = vec![]; for i in 0..5 { - update_md[(i + 4) % 5] = UInt32::addmany( - cs.namespace(|| format!("first add_many {}", i)), - &[ - prev_md[i].clone(), - cur_md[(i + 1) % 5].clone(), - cur_md_prime[(i + 2) % 5].clone(), - ], - ) - .unwrap(); + combined_msg_digest.push( + UInt32::addmany( + cs.namespace(|| format!("add_many: combined msg digest, index {i}")), + &[ + prev_msg_digest_left[(i + 1) % 5].clone(), + msg_digest_left[(i + 2) % 5].clone(), + msg_digest_right[(i + 3) % 5].clone(), + ], + ) + .unwrap(), + ); } - Ok(update_md) + Ok(combined_msg_digest.try_into().unwrap()) } fn get_ripemd160_iv() -> [UInt32; 5] { @@ -139,35 +152,35 @@ where { let f = match (index, left) { (0, true) | (4, false) => f1( - cs.namespace(|| "f1 {index} {left}"), + cs.namespace(|| "f1 in phase {index}. left = {left}"), &md_val[1], &md_val[2], &md_val[3], ) .unwrap(), (1, true) | (3, false) => f2( - cs.namespace(|| "f2 {index} {left}"), + cs.namespace(|| "f2 in phase {index}. left = {left}"), &md_val[1], &md_val[2], &md_val[3], ) .unwrap(), (2, _) => f3( - cs.namespace(|| "f3 {index} {left}"), + cs.namespace(|| "f3 in phase {index}. left = {left}"), &md_val[1], &md_val[2], &md_val[3], ) .unwrap(), (3, true) | (1, false) => f4( - cs.namespace(|| "f4 {index} {left}"), + cs.namespace(|| "f4 in phase {index}. left = {left}"), &md_val[1], &md_val[2], &md_val[3], ) .unwrap(), (4, true) | (0, false) => f5( - cs.namespace(|| "f5 {index} {left}"), + cs.namespace(|| "f5 in phase {index}. left = {left}"), &md_val[1], &md_val[2], &md_val[3], @@ -178,98 +191,63 @@ where f } -fn block(cs: CS, md_val: &mut [UInt32; 5], w: Vec, left: bool) -where +fn half_compression_function( + mut cs: M, + msg_digest: &mut [UInt32; 5], + w: Vec, + left: bool, +) where Scalar: PrimeField, CS: ConstraintSystem, + M: ConstraintSystem>, { - let mut cs = MultiEq::new(cs); - let mut f: UInt32; - let (k_val, i_val, s_val) = if left { - (ROUND_CONSTANTS_LEFT, MSG_SEL_IDX_LEFT, ROL_AMOUNT_LEFT) - } else { - (ROUND_CONSTANTS_RIGHT, MSG_SEL_IDX_RIGHT, ROL_AMOUNT_RIGHT) - }; - for i in 0..80 { let phase_index = i / 16; - - f = compute_f( - cs.namespace(|| format!("Compute F block {} left {} index {}", phase_index, left, i)), - md_val, + let (round_constant, msg_word_index, rotl_amount) = if left { + ( + ROUND_CONSTANTS_LEFT[phase_index], + MSG_SEL_IDX_LEFT[i], + ROL_AMOUNT_LEFT[i], + ) + } else { + ( + ROUND_CONSTANTS_RIGHT[phase_index], + MSG_SEL_IDX_RIGHT[i], + ROL_AMOUNT_RIGHT[i], + ) + }; + + let f = compute_f( + cs.namespace(|| format!("compute_f in round {i}. left = {left}")), + msg_digest, phase_index, left, ); - let mut tmp1 = UInt32::addmany( - cs.namespace(|| { - format!( - "first add_many block {} left {} index {}", - phase_index, left, i - ) - }), + let mut t = UInt32::addmany( + cs.namespace(|| format!("first add_many in compute_f: round {i}, left = {left}",)), &[ - md_val[0].clone(), + msg_digest[0].clone(), f.clone(), - w[i_val[i]].clone(), - UInt32::constant(k_val[phase_index]), + w[msg_word_index].clone(), + UInt32::constant(round_constant), ], ) .unwrap(); - tmp1 = uint32_rotl(tmp1, s_val[i]); - tmp1 = UInt32::addmany( - cs.namespace(|| { - format!( - "second add_many block {} left {} index {}", - phase_index, left, i - ) - }), - &[tmp1, md_val[4].clone()], + t = uint32_rotl(t, rotl_amount); + t = UInt32::addmany( + cs.namespace(|| format!("second add_many in compute_f: round {i}, left = {left}",)), + &[t, msg_digest[4].clone()], ) .unwrap(); - let tmp2 = uint32_rotl(md_val[2].clone(), 10); - md_val[0] = md_val[4].clone(); - md_val[2] = md_val[1].clone(); - md_val[4] = md_val[3].clone(); - md_val[1] = tmp1.clone(); - md_val[3] = tmp2.clone(); + msg_digest[0] = msg_digest[4].clone(); + msg_digest[4] = msg_digest[3].clone(); + msg_digest[3] = uint32_rotl(msg_digest[2].clone(), 10); + msg_digest[2] = msg_digest[1].clone(); + msg_digest[1] = t; } } -pub fn left_step(cs: CS, input: &[Boolean], current_md_value: &mut [UInt32; 5]) -where - Scalar: PrimeField, - CS: ConstraintSystem, -{ - assert_eq!(input.len(), 512); - let w = input.chunks(32).map(UInt32::from_bits).collect::>(); - let mut cs = MultiEq::new(cs); - assert_eq!(w.len(), 16); - block( - cs.namespace(|| "block 0"), - current_md_value, - w.clone(), - true, - ); -} - -pub fn right_step(cs: CS, input: &[Boolean], current_md_value: &mut [UInt32; 5]) -where - Scalar: PrimeField, - CS: ConstraintSystem, -{ - assert_eq!(input.len(), 512); - let w = input.chunks(32).map(UInt32::from_bits).collect::>(); - let mut cs = MultiEq::new(cs); - assert_eq!(w.len(), 16); - block( - cs.namespace(|| "block 0"), - current_md_value, - w.clone(), - false, - ); -} - #[cfg(test)] mod test { use super::*; From e44cc721296f36f2b29106141109d916d1825252 Mon Sep 17 00:00:00 2001 From: Saravanan Vijayakumaran Date: Fri, 29 Mar 2024 10:14:26 +0530 Subject: [PATCH 30/46] Shorten test code using a macro --- crates/ripemd160/src/util.rs | 191 +++++++---------------------------- 1 file changed, 36 insertions(+), 155 deletions(-) diff --git a/crates/ripemd160/src/util.rs b/crates/ripemd160/src/util.rs index b0dfc27..88aab5e 100644 --- a/crates/ripemd160/src/util.rs +++ b/crates/ripemd160/src/util.rs @@ -159,171 +159,52 @@ mod test { } } - #[test] - fn test_uint32_f2() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for _ in 0..1000 { - let mut cs = TestConstraintSystem::::new(); - - let a = rng.next_u32(); - let b = rng.next_u32(); - let c = rng.next_u32(); - - 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 = f2(&mut cs, &a_bit, &b_bit, &c_bit).unwrap(); - - assert!(cs.is_satisfied()); - - for b in r.into_bits().iter() { - match *b { - Boolean::Is(ref b) => { - assert_eq!(b.get_value().unwrap(), expected & 1 == 1); - } - Boolean::Not(ref b) => { - assert_ne!(b.get_value().unwrap(), expected & 1 == 1); - } - Boolean::Constant(b) => { - assert_eq!(b, expected & 1 == 1); - } - } - - expected >>= 1; - } - } - } - - #[test] - fn test_uint32_f4() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); + macro_rules! test_helper_function { + ($test_name:ident, $func:ident, $expected_res_calculator:expr) => { + #[test] + fn $test_name() { + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, + 0x06, 0xbc, 0xe5, + ]); - for _ in 0..1000 { - let mut cs = TestConstraintSystem::::new(); + for _ in 0..1000 { + let mut cs = TestConstraintSystem::::new(); - let a = rng.next_u32(); - let b = rng.next_u32(); - let c = rng.next_u32(); + let a = rng.next_u32(); + let b = rng.next_u32(); + let c = rng.next_u32(); - let mut expected = (a & c) | (b & !c); + let mut expected = $expected_res_calculator(a, 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 a_uint32 = UInt32::alloc(cs.namespace(|| "alloc a"), Some(a)).unwrap(); + let b_uint32 = UInt32::alloc(cs.namespace(|| "alloc b"), Some(b)).unwrap(); + let c_uint32 = UInt32::alloc(cs.namespace(|| "alloc c"), Some(c)).unwrap(); - let r = f4(&mut cs, &a_bit, &b_bit, &c_bit).unwrap(); + let res = $func(&mut cs, &a_uint32, &b_uint32, &c_uint32).unwrap(); - assert!(cs.is_satisfied()); + assert!(cs.is_satisfied()); - for b in r.into_bits().iter() { - match *b { - Boolean::Is(ref b) => { - assert_eq!(b.get_value().unwrap(), expected & 1 == 1); - } - Boolean::Not(ref b) => { - assert_ne!(b.get_value().unwrap(), expected & 1 == 1); - } - Boolean::Constant(b) => { - assert_eq!(b, expected & 1 == 1); + for x in res.into_bits().iter() { + assert_eq!(x.get_value().unwrap(), expected & 1 == 1); + expected >>= 1; } } - - expected >>= 1; } - } + }; } - #[test] - fn test_uint32_f3() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for _ in 0..1000 { - let mut cs = TestConstraintSystem::::new(); - - let a = rng.next_u32(); - let b = rng.next_u32(); - let c = rng.next_u32(); - - let mut expected = ((a) | !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 = f3(&mut cs, &a_bit, &b_bit, &c_bit).unwrap(); - - assert!(cs.is_satisfied()); - - for b in r.into_bits().iter() { - match *b { - Boolean::Is(ref b) => { - assert_eq!(b.get_value().unwrap(), expected & 1 == 1); - } - Boolean::Not(ref b) => { - assert_ne!(b.get_value().unwrap(), expected & 1 == 1); - } - Boolean::Constant(b) => { - assert_eq!(b, expected & 1 == 1); - } - } - - expected >>= 1; - } - } - } - - #[test] - fn test_uint32_f5() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for _ in 0..1000 { - let mut cs = TestConstraintSystem::::new(); - - let a = rng.next_u32(); - let b = rng.next_u32(); - let c = rng.next_u32(); - - let mut expected = a ^ (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 = f5(&mut cs, &a_bit, &b_bit, &c_bit).unwrap(); - - assert!(cs.is_satisfied()); - - for b in r.into_bits().iter() { - match *b { - Boolean::Is(ref b) => { - assert_eq!(b.get_value().unwrap(), expected & 1 == 1); - } - Boolean::Not(ref b) => { - assert_ne!(b.get_value().unwrap(), expected & 1 == 1); - } - Boolean::Constant(b) => { - assert_eq!(b, expected & 1 == 1); - } - } - - expected >>= 1; - } - } - } + test_helper_function!(test_uint32_f1, f1, |a: u32, b: u32, c: u32| { a ^ b ^ c }); + test_helper_function!(test_uint32_f2, f2, |a: u32, b: u32, c: u32| { + (a & b) | ((!a) & c) + }); + test_helper_function!(test_uint32_f3, f3, |a: u32, b: u32, c: u32| { + ((a) | !b) ^ c + }); + test_helper_function!(test_uint32_f4, f4, |a: u32, b: u32, c: u32| { + (a & c) | (b & !c) + }); + test_helper_function!(test_uint32_f5, f5, |a: u32, b: u32, c: u32| { + a ^ (b | !c) + }); } From 7710cd59803f71dee8532108cee480f37f454b38 Mon Sep 17 00:00:00 2001 From: Saravanan Vijayakumaran Date: Fri, 29 Mar 2024 10:25:12 +0530 Subject: [PATCH 31/46] Added tests with constraint counts --- crates/ripemd160/src/ripemd160.rs | 66 ++++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/crates/ripemd160/src/ripemd160.rs b/crates/ripemd160/src/ripemd160.rs index e753d4d..6b1034f 100644 --- a/crates/ripemd160/src/ripemd160.rs +++ b/crates/ripemd160/src/ripemd160.rs @@ -252,9 +252,11 @@ fn half_compression_function( mod test { use super::*; use bellpepper::gadgets::multipack::bytes_to_bits; - use bellpepper_core::test_cs::TestConstraintSystem; + use bellpepper_core::{boolean::AllocatedBit, test_cs::TestConstraintSystem}; use hex_literal::hex; use pasta_curves::Fp; + use rand_core::{RngCore, SeedableRng}; + use rand_xorshift::XorShiftRng; #[test] fn test_blank_hash() { @@ -268,7 +270,10 @@ mod test { .collect::>(); let out_bits = ripemd160(cs.namespace(|| "ripemd160"), &input_bits).unwrap(); + assert!(cs.is_satisfied()); + assert_eq!(cs.num_constraints(), 0); + let expected = hex!("9c1185a5c5e9fc54612808977ee8f548b2258d31"); let mut out = out_bits.iter(); for b in expected.iter() { @@ -291,6 +296,10 @@ mod test { .collect::>(); let out_bits = ripemd160(cs.namespace(|| "ripemd160"), &input_bits).unwrap(); + + assert!(cs.is_satisfied()); + assert_eq!(cs.num_constraints(), 0); + assert!(cs.is_satisfied()); let expected = hex!("8eb208f7e05d987a9b044a8e98c6b087f15a0bfc"); let mut out = out_bits.iter(); @@ -314,7 +323,10 @@ mod test { .collect::>(); let out_bits = ripemd160(cs.namespace(|| "ripemd160"), &input_bits).unwrap(); + assert!(cs.is_satisfied()); + assert_eq!(cs.num_constraints(), 0); + let expected = hex!("12a053384a9c0c88e405a06c27dcf49ada62eb2b"); let mut out = out_bits.iter(); for b in expected.iter() { @@ -324,4 +336,56 @@ mod test { } } } + + #[test] + fn test_one_block_hash() { + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, + 0xbc, 0xe5, + ]); + + let mut cs = TestConstraintSystem::::new(); + let input_bits: Vec<_> = (0..256) + .map(|i| { + Boolean::from( + AllocatedBit::alloc( + cs.namespace(|| format!("input bit {}", i)), + Some(rng.next_u32() % 2 != 0), + ) + .unwrap(), + ) + }) + .collect(); + + ripemd160(cs.namespace(|| "ripemd160"), &input_bits).unwrap(); + + assert!(cs.is_satisfied()); + assert_eq!(cs.num_constraints() - 256, 22893); + } + + #[test] + fn test_two_blocks_hash() { + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, + 0xbc, 0xe5, + ]); + + let mut cs = TestConstraintSystem::::new(); + let input_bits: Vec<_> = (0..512) + .map(|i| { + Boolean::from( + AllocatedBit::alloc( + cs.namespace(|| format!("input bit {}", i)), + Some(rng.next_u32() % 2 != 0), + ) + .unwrap(), + ) + }) + .collect(); + + ripemd160(cs.namespace(|| "ripemd160"), &input_bits).unwrap(); + + assert!(cs.is_satisfied()); + assert_eq!(cs.num_constraints() - 512, 46117); + } } From 023751577cdad9ae91e276a764d93ec499967b33 Mon Sep 17 00:00:00 2001 From: Saravanan Vijayakumaran Date: Fri, 29 Mar 2024 10:28:59 +0530 Subject: [PATCH 32/46] Add author --- crates/ripemd160/Cargo.toml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/ripemd160/Cargo.toml b/crates/ripemd160/Cargo.toml index ca0836f..56fb2cd 100644 --- a/crates/ripemd160/Cargo.toml +++ b/crates/ripemd160/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ripemd160" -authors = ["Shourya Goel ", "Himanshu Raheja "] +authors = ["Shourya Goel ", "Himanshu Raheja ", "Saravanan Vijayakumaran "] version = "0.1.0" edition = "2021" license.workspace = true @@ -16,5 +16,4 @@ ff = { workspace = true } pasta_curves = { workspace = true } hex-literal = "0.4.1" rand_core = { workspace = true } -rand_xorshift = { workspace = true } -sha1 = "0.10.5" +rand_xorshift = { workspace = true } \ No newline at end of file From 3c25ab5dc6f1cccd681d7dfe438c58de217000a2 Mon Sep 17 00:00:00 2001 From: Saravanan Vijayakumaran Date: Fri, 29 Mar 2024 11:17:00 +0530 Subject: [PATCH 33/46] Renaming variables for clarity --- crates/ripemd160/src/ripemd160.rs | 59 +++++++++++++++++-------------- 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/crates/ripemd160/src/ripemd160.rs b/crates/ripemd160/src/ripemd160.rs index 6b1034f..929edf9 100644 --- a/crates/ripemd160/src/ripemd160.rs +++ b/crates/ripemd160/src/ripemd160.rs @@ -145,48 +145,53 @@ fn get_ripemd160_iv() -> [UInt32; 5] { IV.map(UInt32::constant) } -fn compute_f(mut cs: CS, md_val: &mut [UInt32; 5], index: usize, left: bool) -> UInt32 +fn compute_f( + mut cs: CS, + msg_digest: &mut [UInt32; 5], + round_index: usize, + left: bool, +) -> UInt32 where Scalar: PrimeField, CS: ConstraintSystem, { - let f = match (index, left) { + let f = match (round_index, left) { (0, true) | (4, false) => f1( - cs.namespace(|| "f1 in phase {index}. left = {left}"), - &md_val[1], - &md_val[2], - &md_val[3], + cs.namespace(|| "f1 in round {round_index}. left = {left}"), + &msg_digest[1], + &msg_digest[2], + &msg_digest[3], ) .unwrap(), (1, true) | (3, false) => f2( - cs.namespace(|| "f2 in phase {index}. left = {left}"), - &md_val[1], - &md_val[2], - &md_val[3], + cs.namespace(|| "f2 in round {round_index}. left = {left}"), + &msg_digest[1], + &msg_digest[2], + &msg_digest[3], ) .unwrap(), (2, _) => f3( - cs.namespace(|| "f3 in phase {index}. left = {left}"), - &md_val[1], - &md_val[2], - &md_val[3], + cs.namespace(|| "f3 in round {round_index}. left = {left}"), + &msg_digest[1], + &msg_digest[2], + &msg_digest[3], ) .unwrap(), (3, true) | (1, false) => f4( - cs.namespace(|| "f4 in phase {index}. left = {left}"), - &md_val[1], - &md_val[2], - &md_val[3], + cs.namespace(|| "f4 in round {round_index}. left = {left}"), + &msg_digest[1], + &msg_digest[2], + &msg_digest[3], ) .unwrap(), (4, true) | (0, false) => f5( - cs.namespace(|| "f5 in phase {index}. left = {left}"), - &md_val[1], - &md_val[2], - &md_val[3], + cs.namespace(|| "f5 in round {round_index}. left = {left}"), + &msg_digest[1], + &msg_digest[2], + &msg_digest[3], ) .unwrap(), - _ => panic!("Invalid index"), + _ => panic!("Invalid round"), }; f } @@ -202,16 +207,16 @@ fn half_compression_function( M: ConstraintSystem>, { for i in 0..80 { - let phase_index = i / 16; + let round_index = i / 16; let (round_constant, msg_word_index, rotl_amount) = if left { ( - ROUND_CONSTANTS_LEFT[phase_index], + ROUND_CONSTANTS_LEFT[round_index], MSG_SEL_IDX_LEFT[i], ROL_AMOUNT_LEFT[i], ) } else { ( - ROUND_CONSTANTS_RIGHT[phase_index], + ROUND_CONSTANTS_RIGHT[round_index], MSG_SEL_IDX_RIGHT[i], ROL_AMOUNT_RIGHT[i], ) @@ -220,7 +225,7 @@ fn half_compression_function( let f = compute_f( cs.namespace(|| format!("compute_f in round {i}. left = {left}")), msg_digest, - phase_index, + round_index, left, ); let mut t = UInt32::addmany( From 5e67c6e1dcc60a0efec09d2dd6b95bf17bcc0660 Mon Sep 17 00:00:00 2001 From: Saravanan Vijayakumaran Date: Fri, 29 Mar 2024 13:40:51 +0530 Subject: [PATCH 34/46] Improve readability - Moved message block processing into ripemd160_compression_function - Moved comine_left_and_right code into ripemd160_compression_function - Added comments in half_compression_function to associate code with RIPEMD160 spec --- crates/ripemd160/src/ripemd160.rs | 116 +++++++++++++++++------------- 1 file changed, 68 insertions(+), 48 deletions(-) diff --git a/crates/ripemd160/src/ripemd160.rs b/crates/ripemd160/src/ripemd160.rs index 929edf9..eac09f9 100644 --- a/crates/ripemd160/src/ripemd160.rs +++ b/crates/ripemd160/src/ripemd160.rs @@ -4,7 +4,7 @@ //! [RIPEMD-160]: https://homes.esat.kuleuven.be/~bosselae/ripemd160.html use bellpepper::gadgets::{multieq::MultiEq, uint32::UInt32}; -use bellpepper_core::{boolean::Boolean, ConstraintSystem}; +use bellpepper_core::{boolean::Boolean, ConstraintSystem, SynthesisError}; use ff::PrimeField; use std::convert::TryInto; @@ -37,10 +37,7 @@ pub const ROL_AMOUNT_RIGHT: [usize; 80] = [ 6, 14, 6, 9, 12, 9, 12, 5, 15, 8, 8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11, ]; -pub fn ripemd160( - cs: CS, - input: &[Boolean], -) -> Result<[Boolean; 160], bellpepper_core::SynthesisError> +pub fn ripemd160(cs: CS, input: &[Boolean]) -> Result<[Boolean; 160], SynthesisError> where Scalar: PrimeField, CS: ConstraintSystem, @@ -68,42 +65,16 @@ where // Make the bytes little-endian let padded = swap_byte_endianness(&padded); - let mut msg_digest_left = get_ripemd160_iv(); - let mut msg_digest_right = msg_digest_left.clone(); let mut cs = MultiEq::new(cs); + let mut msg_digest = get_ripemd160_iv(); for (i, msg_block) in padded.chunks(512).enumerate() { - let prev_msg_digest_left = msg_digest_left.clone(); - let msg_words = msg_block - .chunks(32) - .map(UInt32::from_bits) - .collect::>(); - - // Process left half of RIPEMD-160 compressesion function - half_compression_function( - cs.namespace(|| format!("left half {}", i)), - &mut msg_digest_left, - msg_words.clone(), - true, - ); - - // Process right half of RIPEMD-160 compressesion function - half_compression_function( - cs.namespace(|| format!("right half {}", i)), - &mut msg_digest_right, - msg_words, - false, - ); - - msg_digest_left = combine_left_and_right( - cs.namespace(|| format!("combine left and right {}", i)), - msg_digest_left, - msg_digest_right, - prev_msg_digest_left, - ) - .unwrap(); - msg_digest_right = msg_digest_left.clone(); + msg_digest = ripemd160_compression_function( + cs.namespace(|| format!("block {i}")), + msg_block, + msg_digest, + )?; } - let result = msg_digest_left + let result = msg_digest .into_iter() .flat_map(|e| e.into_bits()) .collect::>(); @@ -113,24 +84,59 @@ where Ok(result.try_into().unwrap()) } -fn combine_left_and_right( +pub fn ripemd160_compression_function( mut cs: M, - msg_digest_left: [UInt32; 5], - msg_digest_right: [UInt32; 5], - prev_msg_digest_left: [UInt32; 5], -) -> Result<[UInt32; 5], bellpepper_core::SynthesisError> + msg_block: &[Boolean], + curr_msg_digest: [UInt32; 5], +) -> Result<[UInt32; 5], SynthesisError> where Scalar: PrimeField, CS: ConstraintSystem, M: ConstraintSystem>, { + let mut msg_digest_left = curr_msg_digest.clone(); + let mut msg_digest_right = curr_msg_digest.clone(); + + let msg_words = msg_block + .chunks(32) + .map(UInt32::from_bits) + .collect::>(); + + // Process left half of RIPEMD-160 compression function + half_compression_function( + cs.namespace(|| "left half"), + &mut msg_digest_left, + msg_words.clone(), + true, + ); + + // Process right half of RIPEMD-160 compression function + half_compression_function( + cs.namespace(|| "right half"), + &mut msg_digest_right, + msg_words, + false, + ); + + // Combine message digests obtained from left and right halves + // of the compression function + // + // curr_msg_digest = [h0, h1, h2, h3, h4] + // msg_digest_left = [A, B, C, D, E] + // msg_digest_right = [A', B', C', D', E'] + // combined_msg_digest = [h0', h1', h2', h3', h4'] + // h0' = h1 + C + D' + // h1' = h2 + D + E' + // h2' = h3 + E + A' + // h3' = h4 + A + B' + // h4' = h0 + B + C' let mut combined_msg_digest: Vec = vec![]; for i in 0..5 { combined_msg_digest.push( UInt32::addmany( cs.namespace(|| format!("add_many: combined msg digest, index {i}")), &[ - prev_msg_digest_left[(i + 1) % 5].clone(), + curr_msg_digest[(i + 1) % 5].clone(), msg_digest_left[(i + 2) % 5].clone(), msg_digest_right[(i + 3) % 5].clone(), ], @@ -199,7 +205,7 @@ where fn half_compression_function( mut cs: M, msg_digest: &mut [UInt32; 5], - w: Vec, + msg_words: Vec, left: bool, ) where Scalar: PrimeField, @@ -222,29 +228,43 @@ fn half_compression_function( ) }; + // msg_digest = [A, B, C, D, E] + // if left == true + // j = round_index + 1 + // else + // j = 6 - round_index - 1 + // + // f = f_j(B, C, D) let f = compute_f( cs.namespace(|| format!("compute_f in round {i}. left = {left}")), msg_digest, round_index, left, ); + + // t = A + f_j(B, C, D) + msg_words[msg_word_index] + round_constant let mut t = UInt32::addmany( cs.namespace(|| format!("first add_many in compute_f: round {i}, left = {left}",)), &[ msg_digest[0].clone(), f.clone(), - w[msg_word_index].clone(), + msg_words[msg_word_index].clone(), UInt32::constant(round_constant), ], ) .unwrap(); + + // t = rotate_left(t, rotl_amount) t = uint32_rotl(t, rotl_amount); + + // t = t + E t = UInt32::addmany( cs.namespace(|| format!("second add_many in compute_f: round {i}, left = {left}",)), &[t, msg_digest[4].clone()], ) .unwrap(); + // next_msg_digest = [E, t, B, rotate_left(C, 10), D] msg_digest[0] = msg_digest[4].clone(); msg_digest[4] = msg_digest[3].clone(); msg_digest[3] = uint32_rotl(msg_digest[2].clone(), 10); @@ -362,7 +382,7 @@ mod test { }) .collect(); - ripemd160(cs.namespace(|| "ripemd160"), &input_bits).unwrap(); + ripemd160(cs.namespace(|| "one block"), &input_bits).unwrap(); assert!(cs.is_satisfied()); assert_eq!(cs.num_constraints() - 256, 22893); @@ -388,7 +408,7 @@ mod test { }) .collect(); - ripemd160(cs.namespace(|| "ripemd160"), &input_bits).unwrap(); + ripemd160(cs.namespace(|| "two blocks"), &input_bits).unwrap(); assert!(cs.is_satisfied()); assert_eq!(cs.num_constraints() - 512, 46117); From 18bbd18496360abdffea9342cfedfffa8258e609 Mon Sep 17 00:00:00 2001 From: Sh0g0-1758 Date: Tue, 2 Apr 2024 23:05:51 +0530 Subject: [PATCH 35/46] Added space --- crates/ripemd160/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ripemd160/Cargo.toml b/crates/ripemd160/Cargo.toml index 56fb2cd..05bfe94 100644 --- a/crates/ripemd160/Cargo.toml +++ b/crates/ripemd160/Cargo.toml @@ -16,4 +16,4 @@ ff = { workspace = true } pasta_curves = { workspace = true } hex-literal = "0.4.1" rand_core = { workspace = true } -rand_xorshift = { workspace = true } \ No newline at end of file +rand_xorshift = { workspace = true } From 46bc6d55ea38bc571bebe6c38ea7077e8c3ca69a Mon Sep 17 00:00:00 2001 From: Saravanan Vijayakumaran Date: Wed, 3 Apr 2024 10:30:00 +0530 Subject: [PATCH 36/46] Fix xclippy warnings --- crates/ripemd160/src/ripemd160.rs | 14 +++++++------- crates/ripemd160/src/util.rs | 7 ++++--- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/crates/ripemd160/src/ripemd160.rs b/crates/ripemd160/src/ripemd160.rs index eac09f9..96b4b44 100644 --- a/crates/ripemd160/src/ripemd160.rs +++ b/crates/ripemd160/src/ripemd160.rs @@ -71,7 +71,7 @@ where msg_digest = ripemd160_compression_function( cs.namespace(|| format!("block {i}")), msg_block, - msg_digest, + &msg_digest, )?; } let result = msg_digest @@ -87,7 +87,7 @@ where pub fn ripemd160_compression_function( mut cs: M, msg_block: &[Boolean], - curr_msg_digest: [UInt32; 5], + curr_msg_digest: &[UInt32; 5], ) -> Result<[UInt32; 5], SynthesisError> where Scalar: PrimeField, @@ -106,7 +106,7 @@ where half_compression_function( cs.namespace(|| "left half"), &mut msg_digest_left, - msg_words.clone(), + &msg_words, true, ); @@ -114,7 +114,7 @@ where half_compression_function( cs.namespace(|| "right half"), &mut msg_digest_right, - msg_words, + &msg_words, false, ); @@ -205,7 +205,7 @@ where fn half_compression_function( mut cs: M, msg_digest: &mut [UInt32; 5], - msg_words: Vec, + msg_words: &[UInt32], left: bool, ) where Scalar: PrimeField, @@ -255,7 +255,7 @@ fn half_compression_function( .unwrap(); // t = rotate_left(t, rotl_amount) - t = uint32_rotl(t, rotl_amount); + t = uint32_rotl(&t, rotl_amount); // t = t + E t = UInt32::addmany( @@ -267,7 +267,7 @@ fn half_compression_function( // next_msg_digest = [E, t, B, rotate_left(C, 10), D] msg_digest[0] = msg_digest[4].clone(); msg_digest[4] = msg_digest[3].clone(); - msg_digest[3] = uint32_rotl(msg_digest[2].clone(), 10); + msg_digest[3] = uint32_rotl(&msg_digest[2], 10); msg_digest[2] = msg_digest[1].clone(); msg_digest[1] = t; } diff --git a/crates/ripemd160/src/util.rs b/crates/ripemd160/src/util.rs index 88aab5e..416b619 100644 --- a/crates/ripemd160/src/util.rs +++ b/crates/ripemd160/src/util.rs @@ -2,7 +2,7 @@ use bellpepper::gadgets::uint32::UInt32; use bellpepper_core::{boolean::Boolean, ConstraintSystem, SynthesisError}; use ff::PrimeField; -pub fn swap_byte_endianness(bits: &Vec) -> Vec { +pub fn swap_byte_endianness(bits: &[Boolean]) -> Vec { assert!(bits.len() % 8 == 0); let mut modified_bits = vec![]; for i in 0..bits.len() / 8 { @@ -13,7 +13,7 @@ pub fn swap_byte_endianness(bits: &Vec) -> Vec { modified_bits } -pub fn uint32_rotl(a: UInt32, by: usize) -> UInt32 { +pub fn uint32_rotl(a: &UInt32, by: usize) -> UInt32 { assert!(by < 32usize); a.rotr(32 - by) } @@ -175,7 +175,8 @@ mod test { let b = rng.next_u32(); let c = rng.next_u32(); - let mut expected = $expected_res_calculator(a, b, c); + let f = $expected_res_calculator; + let mut expected = f(a, b, c); let a_uint32 = UInt32::alloc(cs.namespace(|| "alloc a"), Some(a)).unwrap(); let b_uint32 = UInt32::alloc(cs.namespace(|| "alloc b"), Some(b)).unwrap(); From e7a4ef9352bed5e0a77b23e6780e98e3dc3ac148 Mon Sep 17 00:00:00 2001 From: Saravanan Vijayakumaran Date: Wed, 3 Apr 2024 11:56:34 +0530 Subject: [PATCH 37/46] Avoid some unwraps --- crates/ripemd160/src/ripemd160.rs | 54 +++++++++++++------------------ 1 file changed, 23 insertions(+), 31 deletions(-) diff --git a/crates/ripemd160/src/ripemd160.rs b/crates/ripemd160/src/ripemd160.rs index 96b4b44..3411728 100644 --- a/crates/ripemd160/src/ripemd160.rs +++ b/crates/ripemd160/src/ripemd160.rs @@ -108,7 +108,7 @@ where &mut msg_digest_left, &msg_words, true, - ); + )?; // Process right half of RIPEMD-160 compression function half_compression_function( @@ -116,7 +116,7 @@ where &mut msg_digest_right, &msg_words, false, - ); + )?; // Combine message digests obtained from left and right halves // of the compression function @@ -132,17 +132,14 @@ where // h4' = h0 + B + C' let mut combined_msg_digest: Vec = vec![]; for i in 0..5 { - combined_msg_digest.push( - UInt32::addmany( - cs.namespace(|| format!("add_many: combined msg digest, index {i}")), - &[ - curr_msg_digest[(i + 1) % 5].clone(), - msg_digest_left[(i + 2) % 5].clone(), - msg_digest_right[(i + 3) % 5].clone(), - ], - ) - .unwrap(), - ); + combined_msg_digest.push(UInt32::addmany( + cs.namespace(|| format!("add_many: combined msg digest, index {i}")), + &[ + curr_msg_digest[(i + 1) % 5].clone(), + msg_digest_left[(i + 2) % 5].clone(), + msg_digest_right[(i + 3) % 5].clone(), + ], + )?); } Ok(combined_msg_digest.try_into().unwrap()) } @@ -156,7 +153,7 @@ fn compute_f( msg_digest: &mut [UInt32; 5], round_index: usize, left: bool, -) -> UInt32 +) -> Result where Scalar: PrimeField, CS: ConstraintSystem, @@ -167,39 +164,34 @@ where &msg_digest[1], &msg_digest[2], &msg_digest[3], - ) - .unwrap(), + )?, (1, true) | (3, false) => f2( cs.namespace(|| "f2 in round {round_index}. left = {left}"), &msg_digest[1], &msg_digest[2], &msg_digest[3], - ) - .unwrap(), + )?, (2, _) => f3( cs.namespace(|| "f3 in round {round_index}. left = {left}"), &msg_digest[1], &msg_digest[2], &msg_digest[3], - ) - .unwrap(), + )?, (3, true) | (1, false) => f4( cs.namespace(|| "f4 in round {round_index}. left = {left}"), &msg_digest[1], &msg_digest[2], &msg_digest[3], - ) - .unwrap(), + )?, (4, true) | (0, false) => f5( cs.namespace(|| "f5 in round {round_index}. left = {left}"), &msg_digest[1], &msg_digest[2], &msg_digest[3], - ) - .unwrap(), + )?, _ => panic!("Invalid round"), }; - f + Ok(f) } fn half_compression_function( @@ -207,7 +199,8 @@ fn half_compression_function( msg_digest: &mut [UInt32; 5], msg_words: &[UInt32], left: bool, -) where +) -> Result<(), SynthesisError> +where Scalar: PrimeField, CS: ConstraintSystem, M: ConstraintSystem>, @@ -240,7 +233,7 @@ fn half_compression_function( msg_digest, round_index, left, - ); + )?; // t = A + f_j(B, C, D) + msg_words[msg_word_index] + round_constant let mut t = UInt32::addmany( @@ -251,8 +244,7 @@ fn half_compression_function( msg_words[msg_word_index].clone(), UInt32::constant(round_constant), ], - ) - .unwrap(); + )?; // t = rotate_left(t, rotl_amount) t = uint32_rotl(&t, rotl_amount); @@ -261,8 +253,7 @@ fn half_compression_function( t = UInt32::addmany( cs.namespace(|| format!("second add_many in compute_f: round {i}, left = {left}",)), &[t, msg_digest[4].clone()], - ) - .unwrap(); + )?; // next_msg_digest = [E, t, B, rotate_left(C, 10), D] msg_digest[0] = msg_digest[4].clone(); @@ -271,6 +262,7 @@ fn half_compression_function( msg_digest[2] = msg_digest[1].clone(); msg_digest[1] = t; } + Ok(()) } #[cfg(test)] From ae39300b1c517e808b2b1c4bafd0c14083a5f858 Mon Sep 17 00:00:00 2001 From: Sh0g0-1758 Date: Wed, 3 Apr 2024 12:02:53 +0530 Subject: [PATCH 38/46] Revert "Fix xclippy warnings" This reverts commit 46bc6d55ea38bc571bebe6c38ea7077e8c3ca69a. --- crates/ripemd160/src/ripemd160.rs | 14 +++++++------- crates/ripemd160/src/util.rs | 7 +++---- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/crates/ripemd160/src/ripemd160.rs b/crates/ripemd160/src/ripemd160.rs index 3411728..eb9e12f 100644 --- a/crates/ripemd160/src/ripemd160.rs +++ b/crates/ripemd160/src/ripemd160.rs @@ -71,7 +71,7 @@ where msg_digest = ripemd160_compression_function( cs.namespace(|| format!("block {i}")), msg_block, - &msg_digest, + msg_digest, )?; } let result = msg_digest @@ -87,7 +87,7 @@ where pub fn ripemd160_compression_function( mut cs: M, msg_block: &[Boolean], - curr_msg_digest: &[UInt32; 5], + curr_msg_digest: [UInt32; 5], ) -> Result<[UInt32; 5], SynthesisError> where Scalar: PrimeField, @@ -106,7 +106,7 @@ where half_compression_function( cs.namespace(|| "left half"), &mut msg_digest_left, - &msg_words, + msg_words.clone(), true, )?; @@ -114,7 +114,7 @@ where half_compression_function( cs.namespace(|| "right half"), &mut msg_digest_right, - &msg_words, + msg_words, false, )?; @@ -197,7 +197,7 @@ where fn half_compression_function( mut cs: M, msg_digest: &mut [UInt32; 5], - msg_words: &[UInt32], + msg_words: Vec, left: bool, ) -> Result<(), SynthesisError> where @@ -247,7 +247,7 @@ where )?; // t = rotate_left(t, rotl_amount) - t = uint32_rotl(&t, rotl_amount); + t = uint32_rotl(t, rotl_amount); // t = t + E t = UInt32::addmany( @@ -258,7 +258,7 @@ where // next_msg_digest = [E, t, B, rotate_left(C, 10), D] msg_digest[0] = msg_digest[4].clone(); msg_digest[4] = msg_digest[3].clone(); - msg_digest[3] = uint32_rotl(&msg_digest[2], 10); + msg_digest[3] = uint32_rotl(msg_digest[2].clone(), 10); msg_digest[2] = msg_digest[1].clone(); msg_digest[1] = t; } diff --git a/crates/ripemd160/src/util.rs b/crates/ripemd160/src/util.rs index 416b619..88aab5e 100644 --- a/crates/ripemd160/src/util.rs +++ b/crates/ripemd160/src/util.rs @@ -2,7 +2,7 @@ use bellpepper::gadgets::uint32::UInt32; use bellpepper_core::{boolean::Boolean, ConstraintSystem, SynthesisError}; use ff::PrimeField; -pub fn swap_byte_endianness(bits: &[Boolean]) -> Vec { +pub fn swap_byte_endianness(bits: &Vec) -> Vec { assert!(bits.len() % 8 == 0); let mut modified_bits = vec![]; for i in 0..bits.len() / 8 { @@ -13,7 +13,7 @@ pub fn swap_byte_endianness(bits: &[Boolean]) -> Vec { modified_bits } -pub fn uint32_rotl(a: &UInt32, by: usize) -> UInt32 { +pub fn uint32_rotl(a: UInt32, by: usize) -> UInt32 { assert!(by < 32usize); a.rotr(32 - by) } @@ -175,8 +175,7 @@ mod test { let b = rng.next_u32(); let c = rng.next_u32(); - let f = $expected_res_calculator; - let mut expected = f(a, b, c); + let mut expected = $expected_res_calculator(a, b, c); let a_uint32 = UInt32::alloc(cs.namespace(|| "alloc a"), Some(a)).unwrap(); let b_uint32 = UInt32::alloc(cs.namespace(|| "alloc b"), Some(b)).unwrap(); From 0434e69d77e2a4bcaff2f05d5ffa240d3180fd5e Mon Sep 17 00:00:00 2001 From: Sh0g0-1758 Date: Wed, 3 Apr 2024 15:01:50 +0530 Subject: [PATCH 39/46] Revert "Revert "Fix xclippy warnings"" This reverts commit ae39300b1c517e808b2b1c4bafd0c14083a5f858. --- crates/ripemd160/src/ripemd160.rs | 14 +++++++------- crates/ripemd160/src/util.rs | 7 ++++--- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/crates/ripemd160/src/ripemd160.rs b/crates/ripemd160/src/ripemd160.rs index eb9e12f..3411728 100644 --- a/crates/ripemd160/src/ripemd160.rs +++ b/crates/ripemd160/src/ripemd160.rs @@ -71,7 +71,7 @@ where msg_digest = ripemd160_compression_function( cs.namespace(|| format!("block {i}")), msg_block, - msg_digest, + &msg_digest, )?; } let result = msg_digest @@ -87,7 +87,7 @@ where pub fn ripemd160_compression_function( mut cs: M, msg_block: &[Boolean], - curr_msg_digest: [UInt32; 5], + curr_msg_digest: &[UInt32; 5], ) -> Result<[UInt32; 5], SynthesisError> where Scalar: PrimeField, @@ -106,7 +106,7 @@ where half_compression_function( cs.namespace(|| "left half"), &mut msg_digest_left, - msg_words.clone(), + &msg_words, true, )?; @@ -114,7 +114,7 @@ where half_compression_function( cs.namespace(|| "right half"), &mut msg_digest_right, - msg_words, + &msg_words, false, )?; @@ -197,7 +197,7 @@ where fn half_compression_function( mut cs: M, msg_digest: &mut [UInt32; 5], - msg_words: Vec, + msg_words: &[UInt32], left: bool, ) -> Result<(), SynthesisError> where @@ -247,7 +247,7 @@ where )?; // t = rotate_left(t, rotl_amount) - t = uint32_rotl(t, rotl_amount); + t = uint32_rotl(&t, rotl_amount); // t = t + E t = UInt32::addmany( @@ -258,7 +258,7 @@ where // next_msg_digest = [E, t, B, rotate_left(C, 10), D] msg_digest[0] = msg_digest[4].clone(); msg_digest[4] = msg_digest[3].clone(); - msg_digest[3] = uint32_rotl(msg_digest[2].clone(), 10); + msg_digest[3] = uint32_rotl(&msg_digest[2], 10); msg_digest[2] = msg_digest[1].clone(); msg_digest[1] = t; } diff --git a/crates/ripemd160/src/util.rs b/crates/ripemd160/src/util.rs index 88aab5e..416b619 100644 --- a/crates/ripemd160/src/util.rs +++ b/crates/ripemd160/src/util.rs @@ -2,7 +2,7 @@ use bellpepper::gadgets::uint32::UInt32; use bellpepper_core::{boolean::Boolean, ConstraintSystem, SynthesisError}; use ff::PrimeField; -pub fn swap_byte_endianness(bits: &Vec) -> Vec { +pub fn swap_byte_endianness(bits: &[Boolean]) -> Vec { assert!(bits.len() % 8 == 0); let mut modified_bits = vec![]; for i in 0..bits.len() / 8 { @@ -13,7 +13,7 @@ pub fn swap_byte_endianness(bits: &Vec) -> Vec { modified_bits } -pub fn uint32_rotl(a: UInt32, by: usize) -> UInt32 { +pub fn uint32_rotl(a: &UInt32, by: usize) -> UInt32 { assert!(by < 32usize); a.rotr(32 - by) } @@ -175,7 +175,8 @@ mod test { let b = rng.next_u32(); let c = rng.next_u32(); - let mut expected = $expected_res_calculator(a, b, c); + let f = $expected_res_calculator; + let mut expected = f(a, b, c); let a_uint32 = UInt32::alloc(cs.namespace(|| "alloc a"), Some(a)).unwrap(); let b_uint32 = UInt32::alloc(cs.namespace(|| "alloc b"), Some(b)).unwrap(); From a67eff98d9c33cfdae1ea13171e35c063bc4d4d5 Mon Sep 17 00:00:00 2001 From: Saravanan Vijayakumaran Date: Wed, 3 Apr 2024 16:49:23 +0530 Subject: [PATCH 40/46] Modified package name, added README and license files --- crates/ripemd160/Cargo.toml | 2 +- crates/ripemd160/LICENSE-APACHE | 1 + crates/ripemd160/LICENSE-MIT | 1 + crates/ripemd160/README.md | 19 +++++++++++++++++++ 4 files changed, 22 insertions(+), 1 deletion(-) create mode 120000 crates/ripemd160/LICENSE-APACHE create mode 120000 crates/ripemd160/LICENSE-MIT create mode 100644 crates/ripemd160/README.md diff --git a/crates/ripemd160/Cargo.toml b/crates/ripemd160/Cargo.toml index 05bfe94..6e2a660 100644 --- a/crates/ripemd160/Cargo.toml +++ b/crates/ripemd160/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "ripemd160" +name = "bellpepper-ripemd160" authors = ["Shourya Goel ", "Himanshu Raheja ", "Saravanan Vijayakumaran "] version = "0.1.0" edition = "2021" diff --git a/crates/ripemd160/LICENSE-APACHE b/crates/ripemd160/LICENSE-APACHE new file mode 120000 index 0000000..1cd601d --- /dev/null +++ b/crates/ripemd160/LICENSE-APACHE @@ -0,0 +1 @@ +../../LICENSE-APACHE \ No newline at end of file diff --git a/crates/ripemd160/LICENSE-MIT b/crates/ripemd160/LICENSE-MIT new file mode 120000 index 0000000..b2cfbdc --- /dev/null +++ b/crates/ripemd160/LICENSE-MIT @@ -0,0 +1 @@ +../../LICENSE-MIT \ No newline at end of file diff --git a/crates/ripemd160/README.md b/crates/ripemd160/README.md new file mode 100644 index 0000000..9212a1f --- /dev/null +++ b/crates/ripemd160/README.md @@ -0,0 +1,19 @@ +# bellpepper-ripemd160 +A [bellpepper](https://github.com/lurk-lab/bellpepper) gadget for the RIPEMD-160 hash function + +## License + +Licensed under either of + + * Apache License, Version 2.0 + ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license + ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +## Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. From 64b6aa1127eed7ccc26f2baec1fa9ad74e4cb0b7 Mon Sep 17 00:00:00 2001 From: Saravanan Vijayakumaran Date: Mon, 8 Apr 2024 11:03:25 +0530 Subject: [PATCH 41/46] Add randomized test using out-of-circuit ripem160 --- crates/ripemd160/Cargo.toml | 1 + crates/ripemd160/src/ripemd160.rs | 44 +++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/crates/ripemd160/Cargo.toml b/crates/ripemd160/Cargo.toml index 6e2a660..764b8e8 100644 --- a/crates/ripemd160/Cargo.toml +++ b/crates/ripemd160/Cargo.toml @@ -17,3 +17,4 @@ pasta_curves = { workspace = true } hex-literal = "0.4.1" rand_core = { workspace = true } rand_xorshift = { workspace = true } +ripemd = "0.1.3" diff --git a/crates/ripemd160/src/ripemd160.rs b/crates/ripemd160/src/ripemd160.rs index 3411728..afe9f37 100644 --- a/crates/ripemd160/src/ripemd160.rs +++ b/crates/ripemd160/src/ripemd160.rs @@ -405,4 +405,48 @@ mod test { assert!(cs.is_satisfied()); assert_eq!(cs.num_constraints() - 512, 46117); } + + #[test] + fn test_against_vectors() { + use ripemd::{Digest, Ripemd160}; + + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, + 0xbc, 0xe5, + ]); + + for input_len in (0..32).chain((32..256).filter(|a| a % 8 == 0)) { + let mut h = Ripemd160::new(); + let data: Vec = (0..input_len).map(|_| rng.next_u32() as u8).collect(); + h.update(&data); + let hash_result = h.finalize(); + + let mut cs = TestConstraintSystem::::new(); + let mut input_bits = vec![]; + + for (byte_i, input_byte) in data.into_iter().enumerate() { + for bit_i in (0..8).rev() { + let cs = cs.namespace(|| format!("input bit {} {}", byte_i, bit_i)); + + input_bits.push( + AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8)) + .unwrap() + .into(), + ); + } + } + + let r = ripemd160(&mut cs, &input_bits).unwrap(); + + assert!(cs.is_satisfied()); + + let mut s = hash_result + .iter() + .flat_map(|&byte| (0..8).rev().map(move |i| (byte >> i) & 1u8 == 1u8)); + + for b in r { + assert_eq!(b.get_value().unwrap(), s.next().unwrap()); + } + } + } } From 8b6dcc4d75201ff5cd90453222c8a8b964f6a2cf Mon Sep 17 00:00:00 2001 From: Saravanan Vijayakumaran Date: Mon, 8 Apr 2024 11:09:06 +0530 Subject: [PATCH 42/46] Change visibility of util functions and inline some --- crates/ripemd160/src/util.rs | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/crates/ripemd160/src/util.rs b/crates/ripemd160/src/util.rs index 416b619..ef373fa 100644 --- a/crates/ripemd160/src/util.rs +++ b/crates/ripemd160/src/util.rs @@ -13,7 +13,8 @@ pub fn swap_byte_endianness(bits: &[Boolean]) -> Vec { modified_bits } -pub fn uint32_rotl(a: &UInt32, by: usize) -> UInt32 { +#[inline] +pub(crate) fn uint32_rotl(a: &UInt32, by: usize) -> UInt32 { assert!(by < 32usize); a.rotr(32 - by) } @@ -43,7 +44,7 @@ where Ok(UInt32::from_bits(&bits)) } -pub fn f1( +pub(crate) fn f1( mut cs: CS, x: &UInt32, y: &UInt32, @@ -72,7 +73,12 @@ where Boolean::or(cs.namespace(|| "(x AND y) OR ((!x) AND z)"), &tmp1, &tmp2) } -pub fn f2(cs: CS, x: &UInt32, y: &UInt32, z: &UInt32) -> Result +pub(crate) fn f2( + cs: CS, + x: &UInt32, + y: &UInt32, + z: &UInt32, +) -> Result where Scalar: PrimeField, CS: ConstraintSystem, @@ -96,7 +102,13 @@ where Boolean::xor(cs.namespace(|| "(x OR !y) XOR z"), &tmp, z) } -pub fn f3(cs: CS, x: &UInt32, y: &UInt32, z: &UInt32) -> Result +#[inline] +pub(crate) fn f3( + cs: CS, + x: &UInt32, + y: &UInt32, + z: &UInt32, +) -> Result where Scalar: PrimeField, CS: ConstraintSystem, @@ -106,7 +118,13 @@ where }) } -pub fn f4(cs: CS, x: &UInt32, y: &UInt32, z: &UInt32) -> Result +#[inline] +pub(crate) fn f4( + cs: CS, + x: &UInt32, + y: &UInt32, + z: &UInt32, +) -> Result where Scalar: PrimeField, CS: ConstraintSystem, @@ -116,7 +134,13 @@ where }) } -pub fn f5(cs: CS, x: &UInt32, y: &UInt32, z: &UInt32) -> Result +#[inline] +pub(crate) fn f5( + cs: CS, + x: &UInt32, + y: &UInt32, + z: &UInt32, +) -> Result where Scalar: PrimeField, CS: ConstraintSystem, From 4c7507f3428ee4dfe4e57d82db9e7daf5a60a108 Mon Sep 17 00:00:00 2001 From: Saravanan Vijayakumaran Date: Mon, 8 Apr 2024 11:10:49 +0530 Subject: [PATCH 43/46] Cleaner way to swap endianness of bytes --- crates/ripemd160/src/util.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/crates/ripemd160/src/util.rs b/crates/ripemd160/src/util.rs index ef373fa..8241305 100644 --- a/crates/ripemd160/src/util.rs +++ b/crates/ripemd160/src/util.rs @@ -4,13 +4,9 @@ use ff::PrimeField; pub fn swap_byte_endianness(bits: &[Boolean]) -> Vec { assert!(bits.len() % 8 == 0); - let mut modified_bits = vec![]; - for i in 0..bits.len() / 8 { - for j in 0..8 { - modified_bits.push(bits[i * 8 + 7 - j].clone()); - } - } - modified_bits + bits.chunks(8) // Process 8 bits (1 byte) at a time + .flat_map(|byte| byte.iter().rev().cloned()) + .collect() } #[inline] From 08e844d05e507b7d6aadc5da0707eaac200415c2 Mon Sep 17 00:00:00 2001 From: Saravanan Vijayakumaran Date: Mon, 8 Apr 2024 11:18:18 +0530 Subject: [PATCH 44/46] Passing inputs to triop by value --- crates/ripemd160/src/util.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/crates/ripemd160/src/util.rs b/crates/ripemd160/src/util.rs index 8241305..da35d7c 100644 --- a/crates/ripemd160/src/util.rs +++ b/crates/ripemd160/src/util.rs @@ -17,9 +17,9 @@ pub(crate) fn uint32_rotl(a: &UInt32, by: usize) -> UInt32 { fn triop( mut cs: CS, - a: &UInt32, - b: &UInt32, - c: &UInt32, + a: UInt32, + b: UInt32, + c: UInt32, circuit_fn: U, ) -> Result where @@ -28,11 +28,10 @@ where U: Fn(&mut CS, usize, &Boolean, &Boolean, &Boolean) -> Result, { let bits: Vec<_> = a - .clone() .into_bits() .iter() - .zip(b.clone().into_bits().iter()) - .zip(c.clone().into_bits().iter()) + .zip(b.into_bits().iter()) + .zip(c.into_bits().iter()) .enumerate() .map(|(i, ((a, b), c))| circuit_fn(&mut cs, i, a, b, c)) .collect::>()?; @@ -79,7 +78,7 @@ where Scalar: PrimeField, CS: ConstraintSystem, { - triop(cs, x, y, z, |cs, i, a, b, c| { + triop(cs, x.clone(), y.clone(), z.clone(), |cs, i, a, b, c| { f2_boolean(cs.namespace(|| format!("f2 {}", i)), a, b, c) }) } @@ -109,7 +108,7 @@ where Scalar: PrimeField, CS: ConstraintSystem, { - triop(cs, x, y, z, |cs, i, a, b, c| { + triop(cs, x.clone(), y.clone(), z.clone(), |cs, i, a, b, c| { f3_boolean(cs.namespace(|| format!("f3 {}", i)), a, b, c) }) } @@ -125,7 +124,7 @@ where Scalar: PrimeField, CS: ConstraintSystem, { - triop(cs, x, y, z, |cs, i, a, b, c| { + triop(cs, x.clone(), y.clone(), z.clone(), |cs, i, a, b, c| { f2_boolean(cs.namespace(|| format!("f4 {}", i)), c, a, b) }) } @@ -141,7 +140,7 @@ where Scalar: PrimeField, CS: ConstraintSystem, { - triop(cs, x, y, z, |cs, i, a, b, c| { + triop(cs, x.clone(), y.clone(), z.clone(), |cs, i, a, b, c| { f3_boolean(cs.namespace(|| format!("f5 {}", i)), b, c, a) }) } From b5348f79e39aec231d59d1fc2bb0347e00c99c9b Mon Sep 17 00:00:00 2001 From: Saravanan Vijayakumaran Date: Mon, 8 Apr 2024 15:53:08 +0530 Subject: [PATCH 45/46] Avoid repeated push into vector --- crates/ripemd160/src/ripemd160.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/crates/ripemd160/src/ripemd160.rs b/crates/ripemd160/src/ripemd160.rs index afe9f37..8cfed32 100644 --- a/crates/ripemd160/src/ripemd160.rs +++ b/crates/ripemd160/src/ripemd160.rs @@ -6,7 +6,7 @@ use bellpepper::gadgets::{multieq::MultiEq, uint32::UInt32}; use bellpepper_core::{boolean::Boolean, ConstraintSystem, SynthesisError}; use ff::PrimeField; -use std::convert::TryInto; +use std::{convert::TryInto, iter}; use crate::util::{f1, f2, f3, f4, f5, swap_byte_endianness, uint32_rotl}; @@ -48,8 +48,11 @@ where let plen = padded.len() as u64; padded.push(Boolean::Constant(true)); - while (padded.len() + 64) % 512 != 0 { - padded.push(Boolean::constant(false)); + let num_zero_bits = 512 - (padded.len() + 64) % 512; + // If num_zero_bits == 512, then (padded.len() + 64) is already a multiple of 512 + // No zero bits need to be appended in this case + if num_zero_bits != 512 { + padded.extend(iter::repeat(Boolean::Constant(false)).take(num_zero_bits)); } for i in (0..64).step_by(8) { From 427b718d8747fc5fdf4ae846a17bb661ac954591 Mon Sep 17 00:00:00 2001 From: Saravanan Vijayakumaran Date: Mon, 8 Apr 2024 16:35:39 +0530 Subject: [PATCH 46/46] Remove the swap_byte_endianness method --- crates/ripemd160/src/ripemd160.rs | 31 +++++++++++++++++++++++++------ crates/ripemd160/src/util.rs | 30 ------------------------------ 2 files changed, 25 insertions(+), 36 deletions(-) diff --git a/crates/ripemd160/src/ripemd160.rs b/crates/ripemd160/src/ripemd160.rs index 8cfed32..c6bc4ad 100644 --- a/crates/ripemd160/src/ripemd160.rs +++ b/crates/ripemd160/src/ripemd160.rs @@ -8,7 +8,7 @@ use bellpepper_core::{boolean::Boolean, ConstraintSystem, SynthesisError}; use ff::PrimeField; use std::{convert::TryInto, iter}; -use crate::util::{f1, f2, f3, f4, f5, swap_byte_endianness, uint32_rotl}; +use crate::util::{f1, f2, f3, f4, f5, uint32_rotl}; const IV: [u32; 5] = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0]; const ROUND_CONSTANTS_LEFT: [u32; 5] = [0x00000000, 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xa953fd4e]; @@ -65,9 +65,6 @@ where assert!(padded.len() % 512 == 0); - // Make the bytes little-endian - let padded = swap_byte_endianness(&padded); - let mut cs = MultiEq::new(cs); let mut msg_digest = get_ripemd160_iv(); for (i, msg_block) in padded.chunks(512).enumerate() { @@ -77,13 +74,21 @@ where &msg_digest, )?; } + + // The UInt32::into_bits function outputs the bit sequence corresponding to + // a 32-bit integer in little-endian order b0, b1, ..., b31 + // But RIPEMD160 outputs each 8 bit chunk in big-endian order. + // So we need to output the sequence + // b7, b6, ..., b0, b15, b14, ..., b8, b23, b22, ..., b16, b31, b30, ..., b24. + // The below code performs this conversion let result = msg_digest .into_iter() .flat_map(|e| e.into_bits()) + .collect::>() + .chunks(8) + .flat_map(|byte| byte.iter().rev().cloned()) .collect::>(); - // Make the bytes big-endian - let result = swap_byte_endianness(&result); Ok(result.try_into().unwrap()) } @@ -100,7 +105,21 @@ where let mut msg_digest_left = curr_msg_digest.clone(); let mut msg_digest_right = curr_msg_digest.clone(); + // Given a chunk of 32 bits b0, b1,..., b31, RIPEMD160 converts them into + // a 32-bit integer as follows: + // - Bits b0 to b7 represent the least significant byte in big-endian order + // - Bits b8 to b15 represent the next most significant byte in big-endian order + // - Bits b16 to b23 represent the next most significant byte in big-endian order + // - Bits b24 to b31 represent the most significant byte in big-endian order + // + // But the UInt32::from_bits function expects the bit sequence corresponding to + // a 32-bit integer to be in little-endian order. So we need to feed it the sequence + // b7, b6, ..., b0, b15, b14, ..., b8, b23, b22, ..., b16, b31, b30, ..., b24. + // The below code performs this conversion let msg_words = msg_block + .chunks(8) + .flat_map(|byte| byte.iter().rev().cloned()) + .collect::>() .chunks(32) .map(UInt32::from_bits) .collect::>(); diff --git a/crates/ripemd160/src/util.rs b/crates/ripemd160/src/util.rs index da35d7c..13945b1 100644 --- a/crates/ripemd160/src/util.rs +++ b/crates/ripemd160/src/util.rs @@ -2,13 +2,6 @@ use bellpepper::gadgets::uint32::UInt32; use bellpepper_core::{boolean::Boolean, ConstraintSystem, SynthesisError}; use ff::PrimeField; -pub fn swap_byte_endianness(bits: &[Boolean]) -> Vec { - assert!(bits.len() % 8 == 0); - bits.chunks(8) // Process 8 bits (1 byte) at a time - .flat_map(|byte| byte.iter().rev().cloned()) - .collect() -} - #[inline] pub(crate) fn uint32_rotl(a: &UInt32, by: usize) -> UInt32 { assert!(by < 32usize); @@ -155,29 +148,6 @@ mod test { use super::*; - #[test] - fn test_swap_byte_endianness() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for _ in 0..50 { - let a = rng.next_u32(); - let a_le_bytes = a.to_le_bytes(); - let a_rev_le_bytes = a_le_bytes.map(|b| b.reverse_bits()); - let a_rev = u32::from_le_bytes(a_rev_le_bytes); - - let a_bits = UInt32::constant(a).into_bits_be(); - let a_rev_bits = UInt32::constant(a_rev).into_bits_be(); - let a_rev_bits_exp = swap_byte_endianness(&a_bits); - - for (x, y) in a_rev_bits.into_iter().zip(a_rev_bits_exp.into_iter()) { - assert_eq!(x.get_value().unwrap(), y.get_value().unwrap()); - } - } - } - macro_rules! test_helper_function { ($test_name:ident, $func:ident, $expected_res_calculator:expr) => { #[test]