From d7868c586364153214e06ccbc3ba4f9d92782acf Mon Sep 17 00:00:00 2001 From: Menko Date: Wed, 24 Apr 2024 09:40:42 +0200 Subject: [PATCH 1/8] feat: add rpx random coin (#307) --- src/rand/mod.rs | 2 + src/rand/rpx.rs | 292 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 294 insertions(+) create mode 100644 src/rand/rpx.rs diff --git a/src/rand/mod.rs b/src/rand/mod.rs index cc846056..6e371a2d 100644 --- a/src/rand/mod.rs +++ b/src/rand/mod.rs @@ -7,7 +7,9 @@ pub use winter_utils::Randomizable; use crate::{Felt, FieldElement, Word, ZERO}; mod rpo; +mod rpx; pub use rpo::RpoRandomCoin; +pub use rpx::RpxRandomCoin; /// Pseudo-random element generator. /// diff --git a/src/rand/rpx.rs b/src/rand/rpx.rs new file mode 100644 index 00000000..a2a0641d --- /dev/null +++ b/src/rand/rpx.rs @@ -0,0 +1,292 @@ +use super::{Felt, FeltRng, FieldElement, RandomCoin, RandomCoinError, RngCore, Word, ZERO}; +use crate::{ + hash::rpx::{Rpx256, RpxDigest}, + utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable}, +}; +use alloc::{string::ToString, vec::Vec}; +use rand_core::impls; + +// CONSTANTS +// ================================================================================================ + +const STATE_WIDTH: usize = Rpx256::STATE_WIDTH; +const RATE_START: usize = Rpx256::RATE_RANGE.start; +const RATE_END: usize = Rpx256::RATE_RANGE.end; +const HALF_RATE_WIDTH: usize = (Rpx256::RATE_RANGE.end - Rpx256::RATE_RANGE.start) / 2; + +// RPX RANDOM COIN +// ================================================================================================ +/// A simplified version of the `SPONGE_PRG` reseedable pseudo-random number generator algorithm +/// described in . +/// +/// The simplification is related to the following facts: +/// 1. A call to the reseed method implies one and only one call to the permutation function. +/// This is possible because in our case we never reseed with more than 4 field elements. +/// 2. As a result of the previous point, we don't make use of an input buffer to accumulate seed +/// material. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct RpxRandomCoin { + state: [Felt; STATE_WIDTH], + current: usize, +} + +impl RpxRandomCoin { + /// Returns a new [RpxRandomCoin] initialize with the specified seed. + pub fn new(seed: Word) -> Self { + let mut state = [ZERO; STATE_WIDTH]; + + for i in 0..HALF_RATE_WIDTH { + state[RATE_START + i] += seed[i]; + } + + // Absorb + Rpx256::apply_permutation(&mut state); + + RpxRandomCoin { state, current: RATE_START } + } + + /// Returns an [RpxRandomCoin] instantiated from the provided components. + /// + /// # Panics + /// Panics if `current` is smaller than 4 or greater than or equal to 12. + pub fn from_parts(state: [Felt; STATE_WIDTH], current: usize) -> Self { + assert!( + (RATE_START..RATE_END).contains(¤t), + "current value outside of valid range" + ); + Self { state, current } + } + + /// Returns components of this random coin. + pub fn into_parts(self) -> ([Felt; STATE_WIDTH], usize) { + (self.state, self.current) + } + + /// Fills `dest` with random data. + pub fn fill_bytes(&mut self, dest: &mut [u8]) { + ::fill_bytes(self, dest) + } + + fn draw_basefield(&mut self) -> Felt { + if self.current == RATE_END { + Rpx256::apply_permutation(&mut self.state); + self.current = RATE_START; + } + + self.current += 1; + self.state[self.current - 1] + } +} + +// RANDOM COIN IMPLEMENTATION +// ------------------------------------------------------------------------------------------------ + +impl RandomCoin for RpxRandomCoin { + type BaseField = Felt; + type Hasher = Rpx256; + + fn new(seed: &[Self::BaseField]) -> Self { + let digest: Word = Rpx256::hash_elements(seed).into(); + Self::new(digest) + } + + fn reseed(&mut self, data: RpxDigest) { + // Reset buffer + self.current = RATE_START; + + // Add the new seed material to the first half of the rate portion of the RPX state + let data: Word = data.into(); + + self.state[RATE_START] += data[0]; + self.state[RATE_START + 1] += data[1]; + self.state[RATE_START + 2] += data[2]; + self.state[RATE_START + 3] += data[3]; + + // Absorb + Rpx256::apply_permutation(&mut self.state); + } + + fn check_leading_zeros(&self, value: u64) -> u32 { + let value = Felt::new(value); + let mut state_tmp = self.state; + + state_tmp[RATE_START] += value; + + Rpx256::apply_permutation(&mut state_tmp); + + let first_rate_element = state_tmp[RATE_START].as_int(); + first_rate_element.trailing_zeros() + } + + fn draw>(&mut self) -> Result { + let ext_degree = E::EXTENSION_DEGREE; + let mut result = vec![ZERO; ext_degree]; + for r in result.iter_mut().take(ext_degree) { + *r = self.draw_basefield(); + } + + let result = E::slice_from_base_elements(&result); + Ok(result[0]) + } + + fn draw_integers( + &mut self, + num_values: usize, + domain_size: usize, + nonce: u64, + ) -> Result, RandomCoinError> { + assert!(domain_size.is_power_of_two(), "domain size must be a power of two"); + assert!(num_values < domain_size, "number of values must be smaller than domain size"); + + // absorb the nonce + let nonce = Felt::new(nonce); + self.state[RATE_START] += nonce; + Rpx256::apply_permutation(&mut self.state); + + // reset the buffer + self.current = RATE_START; + + // determine how many bits are needed to represent valid values in the domain + let v_mask = (domain_size - 1) as u64; + + // draw values from PRNG until we get as many unique values as specified by num_queries + let mut values = Vec::new(); + for _ in 0..1000 { + // get the next pseudo-random field element + let value = self.draw_basefield().as_int(); + + // use the mask to get a value within the range + let value = (value & v_mask) as usize; + + values.push(value); + if values.len() == num_values { + break; + } + } + + if values.len() < num_values { + return Err(RandomCoinError::FailedToDrawIntegers(num_values, values.len(), 1000)); + } + + Ok(values) + } +} + +// FELT RNG IMPLEMENTATION +// ------------------------------------------------------------------------------------------------ + +impl FeltRng for RpxRandomCoin { + fn draw_element(&mut self) -> Felt { + self.draw_basefield() + } + + fn draw_word(&mut self) -> Word { + let mut output = [ZERO; 4]; + for o in output.iter_mut() { + *o = self.draw_basefield(); + } + output + } +} + +// RNGCORE IMPLEMENTATION +// ------------------------------------------------------------------------------------------------ + +impl RngCore for RpxRandomCoin { + fn next_u32(&mut self) -> u32 { + self.draw_basefield().as_int() as u32 + } + + fn next_u64(&mut self) -> u64 { + impls::next_u64_via_u32(self) + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + impls::fill_bytes_via_next(self, dest) + } + + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> { + self.fill_bytes(dest); + Ok(()) + } +} + +// SERIALIZATION +// ------------------------------------------------------------------------------------------------ + +impl Serializable for RpxRandomCoin { + fn write_into(&self, target: &mut W) { + self.state.iter().for_each(|v| v.write_into(target)); + // casting to u8 is OK because `current` is always between 4 and 12. + target.write_u8(self.current as u8); + } +} + +impl Deserializable for RpxRandomCoin { + fn read_from(source: &mut R) -> Result { + let state = [ + Felt::read_from(source)?, + Felt::read_from(source)?, + Felt::read_from(source)?, + Felt::read_from(source)?, + Felt::read_from(source)?, + Felt::read_from(source)?, + Felt::read_from(source)?, + Felt::read_from(source)?, + Felt::read_from(source)?, + Felt::read_from(source)?, + Felt::read_from(source)?, + Felt::read_from(source)?, + ]; + let current = source.read_u8()? as usize; + if !(RATE_START..RATE_END).contains(¤t) { + return Err(DeserializationError::InvalidValue( + "current value outside of valid range".to_string(), + )); + } + Ok(Self { state, current }) + } +} + +// TESTS +// ================================================================================================ + +#[cfg(test)] +mod tests { + use super::{Deserializable, FeltRng, RpxRandomCoin, Serializable, ZERO}; + use crate::ONE; + + #[test] + fn test_feltrng_felt() { + let mut rpxcoin = RpxRandomCoin::new([ZERO; 4]); + let output = rpxcoin.draw_element(); + + let mut rpxcoin = RpxRandomCoin::new([ZERO; 4]); + let expected = rpxcoin.draw_basefield(); + + assert_eq!(output, expected); + } + + #[test] + fn test_feltrng_word() { + let mut rpxcoin = RpxRandomCoin::new([ZERO; 4]); + let output = rpxcoin.draw_word(); + + let mut rpocoin = RpxRandomCoin::new([ZERO; 4]); + let mut expected = [ZERO; 4]; + for o in expected.iter_mut() { + *o = rpocoin.draw_basefield(); + } + + assert_eq!(output, expected); + } + + #[test] + fn test_feltrng_serialization() { + let coin1 = RpxRandomCoin::from_parts([ONE; 12], 5); + + let bytes = coin1.to_bytes(); + let coin2 = RpxRandomCoin::read_from_bytes(&bytes).unwrap(); + assert_eq!(coin1, coin2); + } +} From 2b6d8d11aeb41fa4958df5292a5feb00c092a0e8 Mon Sep 17 00:00:00 2001 From: Bobbin Threadbare Date: Wed, 24 Apr 2024 00:50:38 -0700 Subject: [PATCH 2/8] chore: increment crate version to v0.9.3 and update changelog --- CHANGELOG.md | 4 ++++ Cargo.lock | 38 ++++++++------------------------------ Cargo.toml | 4 ++-- README.md | 3 ++- 4 files changed, 16 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d244600..091bd2a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.3 (2024-04-24) + +* Added `RpxRandomCoin` struct (#307). + ## 0.9.2 (2024-04-21) * Implemented serialization for the `Smt` struct (#304). diff --git a/Cargo.lock b/Cargo.lock index a7185b55..03b6600d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -443,9 +443,9 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jobserver" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "685a7d121ee3f65ae4fddd72b25a04bb36b6af81bc0828f7d5434c0fe60fa3a2" +checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" dependencies = [ "libc", ] @@ -506,7 +506,7 @@ checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "miden-crypto" -version = "0.9.2" +version = "0.9.3" dependencies = [ "blake3", "cc", @@ -786,9 +786,9 @@ checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "rustix" -version = "0.38.32" +version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ "bitflags", "errno", @@ -1029,37 +1029,15 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - [[package]] name = "winapi-util" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +checksum = "134306a13c5647ad6453e8deaec55d3a44d6021970129e6188735e74bf546697" dependencies = [ - "winapi", + "windows-sys", ] -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - [[package]] name = "windows-sys" version = "0.52.0" diff --git a/Cargo.toml b/Cargo.toml index ce34b1b6..e0c86526 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,12 @@ [package] name = "miden-crypto" -version = "0.9.2" +version = "0.9.3" description = "Miden Cryptographic primitives" authors = ["miden contributors"] readme = "README.md" license = "MIT" repository = "https://github.com/0xPolygonMiden/crypto" -documentation = "https://docs.rs/miden-crypto/0.9.2" +documentation = "https://docs.rs/miden-crypto/0.9.3" categories = ["cryptography", "no-std"] keywords = ["miden", "crypto", "hash", "merkle"] edition = "2021" diff --git a/README.md b/README.md index d82af815..f5a554f1 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,8 @@ For the above signatures, key generation, signing, and signature verification ar [Pseudo random element generator module](./src/rand/) provides a set of traits and data structures that facilitate generating pseudo-random elements in the context of Miden VM and Miden rollup. The module currently includes: * `FeltRng`: a trait for generating random field elements and random 4 field elements. -* `RpoRandomCoin`: a struct implementing `FeltRng` as well as the [`RandomCoin`](https://github.com/facebook/winterfell/blob/main/crypto/src/random/mod.rs) trait. +* `RpoRandomCoin`: a struct implementing `FeltRng` as well as the [`RandomCoin`](https://github.com/facebook/winterfell/blob/main/crypto/src/random/mod.rs) trait using RPO hash function. +* `RpxRandomCoin`: a struct implementing `FeltRng` as well as the [`RandomCoin`](https://github.com/facebook/winterfell/blob/main/crypto/src/random/mod.rs) trait using RPX hash function. ## Crate features This crate can be compiled with the following features: From ae22edcc6ac76f0e34fa2e208a78a49985e9e871 Mon Sep 17 00:00:00 2001 From: Augusto Hack Date: Mon, 6 May 2024 23:58:25 +0200 Subject: [PATCH 3/8] Rpo256: Add RpoDigest conversions (#311) --- src/hash/rescue/rpo/digest.rs | 292 +++++++++++++++++++++++++------- src/hash/rescue/rpx/digest.rs | 310 ++++++++++++++++++++++++++-------- src/merkle/smt/full/tests.rs | 15 +- 3 files changed, 474 insertions(+), 143 deletions(-) diff --git a/src/hash/rescue/rpo/digest.rs b/src/hash/rescue/rpo/digest.rs index 6feb9f68..545b68dc 100644 --- a/src/hash/rescue/rpo/digest.rs +++ b/src/hash/rescue/rpo/digest.rs @@ -118,26 +118,106 @@ impl Randomizable for RpoDigest { // CONVERSIONS: FROM RPO DIGEST // ================================================================================================ -impl From<&RpoDigest> for [Felt; DIGEST_SIZE] { - fn from(value: &RpoDigest) -> Self { - value.0 +#[derive(Copy, Clone, Debug)] +pub enum RpoDigestError { + InvalidInteger, +} + +impl TryFrom<&RpoDigest> for [bool; DIGEST_SIZE] { + type Error = RpoDigestError; + + fn try_from(value: &RpoDigest) -> Result { + (*value).try_into() } } -impl From for [Felt; DIGEST_SIZE] { - fn from(value: RpoDigest) -> Self { - value.0 +impl TryFrom for [bool; DIGEST_SIZE] { + type Error = RpoDigestError; + + fn try_from(value: RpoDigest) -> Result { + fn to_bool(v: u64) -> Option { + if v <= 1 { + Some(v == 1) + } else { + None + } + } + + Ok([ + to_bool(value.0[0].as_int()).ok_or(RpoDigestError::InvalidInteger)?, + to_bool(value.0[1].as_int()).ok_or(RpoDigestError::InvalidInteger)?, + to_bool(value.0[2].as_int()).ok_or(RpoDigestError::InvalidInteger)?, + to_bool(value.0[3].as_int()).ok_or(RpoDigestError::InvalidInteger)?, + ]) + } +} + +impl TryFrom<&RpoDigest> for [u8; DIGEST_SIZE] { + type Error = RpoDigestError; + + fn try_from(value: &RpoDigest) -> Result { + (*value).try_into() + } +} + +impl TryFrom for [u8; DIGEST_SIZE] { + type Error = RpoDigestError; + + fn try_from(value: RpoDigest) -> Result { + Ok([ + value.0[0].as_int().try_into().map_err(|_| RpoDigestError::InvalidInteger)?, + value.0[1].as_int().try_into().map_err(|_| RpoDigestError::InvalidInteger)?, + value.0[2].as_int().try_into().map_err(|_| RpoDigestError::InvalidInteger)?, + value.0[3].as_int().try_into().map_err(|_| RpoDigestError::InvalidInteger)?, + ]) + } +} + +impl TryFrom<&RpoDigest> for [u16; DIGEST_SIZE] { + type Error = RpoDigestError; + + fn try_from(value: &RpoDigest) -> Result { + (*value).try_into() + } +} + +impl TryFrom for [u16; DIGEST_SIZE] { + type Error = RpoDigestError; + + fn try_from(value: RpoDigest) -> Result { + Ok([ + value.0[0].as_int().try_into().map_err(|_| RpoDigestError::InvalidInteger)?, + value.0[1].as_int().try_into().map_err(|_| RpoDigestError::InvalidInteger)?, + value.0[2].as_int().try_into().map_err(|_| RpoDigestError::InvalidInteger)?, + value.0[3].as_int().try_into().map_err(|_| RpoDigestError::InvalidInteger)?, + ]) + } +} + +impl TryFrom<&RpoDigest> for [u32; DIGEST_SIZE] { + type Error = RpoDigestError; + + fn try_from(value: &RpoDigest) -> Result { + (*value).try_into() + } +} + +impl TryFrom for [u32; DIGEST_SIZE] { + type Error = RpoDigestError; + + fn try_from(value: RpoDigest) -> Result { + Ok([ + value.0[0].as_int().try_into().map_err(|_| RpoDigestError::InvalidInteger)?, + value.0[1].as_int().try_into().map_err(|_| RpoDigestError::InvalidInteger)?, + value.0[2].as_int().try_into().map_err(|_| RpoDigestError::InvalidInteger)?, + value.0[3].as_int().try_into().map_err(|_| RpoDigestError::InvalidInteger)?, + ]) } } impl From<&RpoDigest> for [u64; DIGEST_SIZE] { fn from(value: &RpoDigest) -> Self { - [ - value.0[0].as_int(), - value.0[1].as_int(), - value.0[2].as_int(), - value.0[3].as_int(), - ] + (*value).into() } } @@ -152,9 +232,21 @@ impl From for [u64; DIGEST_SIZE] { } } +impl From<&RpoDigest> for [Felt; DIGEST_SIZE] { + fn from(value: &RpoDigest) -> Self { + (*value).into() + } +} + +impl From for [Felt; DIGEST_SIZE] { + fn from(value: RpoDigest) -> Self { + value.0 + } +} + impl From<&RpoDigest> for [u8; DIGEST_BYTES] { fn from(value: &RpoDigest) -> Self { - value.as_bytes() + (*value).into() } } @@ -164,6 +256,13 @@ impl From for [u8; DIGEST_BYTES] { } } +impl From<&RpoDigest> for String { + /// The returned string starts with `0x`. + fn from(value: &RpoDigest) -> Self { + (*value).into() + } +} + impl From for String { /// The returned string starts with `0x`. fn from(value: RpoDigest) -> Self { @@ -171,20 +270,76 @@ impl From for String { } } -impl From<&RpoDigest> for String { - /// The returned string starts with `0x`. - fn from(value: &RpoDigest) -> Self { +// CONVERSIONS: TO RPO DIGEST +// ================================================================================================ + +impl From<&[bool; DIGEST_SIZE]> for RpoDigest { + fn from(value: &[bool; DIGEST_SIZE]) -> Self { (*value).into() } } -// CONVERSIONS: TO RPO DIGEST -// ================================================================================================ +impl From<[bool; DIGEST_SIZE]> for RpoDigest { + fn from(value: [bool; DIGEST_SIZE]) -> Self { + [value[0] as u32, value[1] as u32, value[2] as u32, value[3] as u32].into() + } +} -#[derive(Copy, Clone, Debug)] -pub enum RpoDigestError { - /// The provided u64 integer does not fit in the field's moduli. - InvalidInteger, +impl From<&[u8; DIGEST_SIZE]> for RpoDigest { + fn from(value: &[u8; DIGEST_SIZE]) -> Self { + (*value).into() + } +} + +impl From<[u8; DIGEST_SIZE]> for RpoDigest { + fn from(value: [u8; DIGEST_SIZE]) -> Self { + Self([value[0].into(), value[1].into(), value[2].into(), value[3].into()]) + } +} + +impl From<&[u16; DIGEST_SIZE]> for RpoDigest { + fn from(value: &[u16; DIGEST_SIZE]) -> Self { + (*value).into() + } +} + +impl From<[u16; DIGEST_SIZE]> for RpoDigest { + fn from(value: [u16; DIGEST_SIZE]) -> Self { + Self([value[0].into(), value[1].into(), value[2].into(), value[3].into()]) + } +} + +impl From<&[u32; DIGEST_SIZE]> for RpoDigest { + fn from(value: &[u32; DIGEST_SIZE]) -> Self { + (*value).into() + } +} + +impl From<[u32; DIGEST_SIZE]> for RpoDigest { + fn from(value: [u32; DIGEST_SIZE]) -> Self { + Self([value[0].into(), value[1].into(), value[2].into(), value[3].into()]) + } +} + +impl TryFrom<&[u64; DIGEST_SIZE]> for RpoDigest { + type Error = RpoDigestError; + + fn try_from(value: &[u64; DIGEST_SIZE]) -> Result { + (*value).try_into() + } +} + +impl TryFrom<[u64; DIGEST_SIZE]> for RpoDigest { + type Error = RpoDigestError; + + fn try_from(value: [u64; DIGEST_SIZE]) -> Result { + Ok(Self([ + value[0].try_into().map_err(|_| RpoDigestError::InvalidInteger)?, + value[1].try_into().map_err(|_| RpoDigestError::InvalidInteger)?, + value[2].try_into().map_err(|_| RpoDigestError::InvalidInteger)?, + value[3].try_into().map_err(|_| RpoDigestError::InvalidInteger)?, + ])) + } } impl From<&[Felt; DIGEST_SIZE]> for RpoDigest { @@ -199,6 +354,14 @@ impl From<[Felt; DIGEST_SIZE]> for RpoDigest { } } +impl TryFrom<&[u8; DIGEST_BYTES]> for RpoDigest { + type Error = HexParseError; + + fn try_from(value: &[u8; DIGEST_BYTES]) -> Result { + (*value).try_into() + } +} + impl TryFrom<[u8; DIGEST_BYTES]> for RpoDigest { type Error = HexParseError; @@ -218,14 +381,6 @@ impl TryFrom<[u8; DIGEST_BYTES]> for RpoDigest { } } -impl TryFrom<&[u8; DIGEST_BYTES]> for RpoDigest { - type Error = HexParseError; - - fn try_from(value: &[u8; DIGEST_BYTES]) -> Result { - (*value).try_into() - } -} - impl TryFrom<&[u8]> for RpoDigest { type Error = HexParseError; @@ -234,33 +389,12 @@ impl TryFrom<&[u8]> for RpoDigest { } } -impl TryFrom<[u64; DIGEST_SIZE]> for RpoDigest { - type Error = RpoDigestError; - - fn try_from(value: [u64; DIGEST_SIZE]) -> Result { - Ok(Self([ - value[0].try_into().map_err(|_| RpoDigestError::InvalidInteger)?, - value[1].try_into().map_err(|_| RpoDigestError::InvalidInteger)?, - value[2].try_into().map_err(|_| RpoDigestError::InvalidInteger)?, - value[3].try_into().map_err(|_| RpoDigestError::InvalidInteger)?, - ])) - } -} - -impl TryFrom<&[u64; DIGEST_SIZE]> for RpoDigest { - type Error = RpoDigestError; - - fn try_from(value: &[u64; DIGEST_SIZE]) -> Result { - (*value).try_into() - } -} - impl TryFrom<&str> for RpoDigest { type Error = HexParseError; /// Expects the string to start with `0x`. fn try_from(value: &str) -> Result { - hex_to_bytes(value).and_then(|v| v.try_into()) + hex_to_bytes::(value).and_then(RpoDigest::try_from) } } @@ -373,43 +507,71 @@ mod tests { Felt::new(rand_value()), ]); - let v: [Felt; DIGEST_SIZE] = digest.into(); + // BY VALUE + // ---------------------------------------------------------------------------------------- + let v: [bool; DIGEST_SIZE] = [true, false, true, true]; let v2: RpoDigest = v.into(); - assert_eq!(digest, v2); + assert_eq!(v, <[bool; DIGEST_SIZE]>::try_from(v2).unwrap()); - let v: [Felt; DIGEST_SIZE] = (&digest).into(); + let v: [u8; DIGEST_SIZE] = [0_u8, 1_u8, 2_u8, 3_u8]; let v2: RpoDigest = v.into(); - assert_eq!(digest, v2); + assert_eq!(v, <[u8; DIGEST_SIZE]>::try_from(v2).unwrap()); + + let v: [u16; DIGEST_SIZE] = [0_u16, 1_u16, 2_u16, 3_u16]; + let v2: RpoDigest = v.into(); + assert_eq!(v, <[u16; DIGEST_SIZE]>::try_from(v2).unwrap()); + + let v: [u32; DIGEST_SIZE] = [0_u32, 1_u32, 2_u32, 3_u32]; + let v2: RpoDigest = v.into(); + assert_eq!(v, <[u32; DIGEST_SIZE]>::try_from(v2).unwrap()); let v: [u64; DIGEST_SIZE] = digest.into(); let v2: RpoDigest = v.try_into().unwrap(); assert_eq!(digest, v2); - let v: [u64; DIGEST_SIZE] = (&digest).into(); - let v2: RpoDigest = v.try_into().unwrap(); + let v: [Felt; DIGEST_SIZE] = digest.into(); + let v2: RpoDigest = v.into(); assert_eq!(digest, v2); let v: [u8; DIGEST_BYTES] = digest.into(); let v2: RpoDigest = v.try_into().unwrap(); assert_eq!(digest, v2); - let v: [u8; DIGEST_BYTES] = (&digest).into(); + let v: String = digest.into(); let v2: RpoDigest = v.try_into().unwrap(); assert_eq!(digest, v2); - let v: String = digest.into(); - let v2: RpoDigest = v.try_into().unwrap(); + // BY REF + // ---------------------------------------------------------------------------------------- + let v: [bool; DIGEST_SIZE] = [true, false, true, true]; + let v2: RpoDigest = (&v).into(); + assert_eq!(v, <[bool; DIGEST_SIZE]>::try_from(&v2).unwrap()); + + let v: [u8; DIGEST_SIZE] = [0_u8, 1_u8, 2_u8, 3_u8]; + let v2: RpoDigest = (&v).into(); + assert_eq!(v, <[u8; DIGEST_SIZE]>::try_from(&v2).unwrap()); + + let v: [u16; DIGEST_SIZE] = [0_u16, 1_u16, 2_u16, 3_u16]; + let v2: RpoDigest = (&v).into(); + assert_eq!(v, <[u16; DIGEST_SIZE]>::try_from(&v2).unwrap()); + + let v: [u32; DIGEST_SIZE] = [0_u32, 1_u32, 2_u32, 3_u32]; + let v2: RpoDigest = (&v).into(); + assert_eq!(v, <[u32; DIGEST_SIZE]>::try_from(&v2).unwrap()); + + let v: [u64; DIGEST_SIZE] = (&digest).into(); + let v2: RpoDigest = (&v).try_into().unwrap(); assert_eq!(digest, v2); - let v: String = (&digest).into(); - let v2: RpoDigest = v.try_into().unwrap(); + let v: [Felt; DIGEST_SIZE] = (&digest).into(); + let v2: RpoDigest = (&v).into(); assert_eq!(digest, v2); - let v: [u8; DIGEST_BYTES] = digest.into(); + let v: [u8; DIGEST_BYTES] = (&digest).into(); let v2: RpoDigest = (&v).try_into().unwrap(); assert_eq!(digest, v2); - let v: [u8; DIGEST_BYTES] = (&digest).into(); + let v: String = (&digest).into(); let v2: RpoDigest = (&v).try_into().unwrap(); assert_eq!(digest, v2); } diff --git a/src/hash/rescue/rpx/digest.rs b/src/hash/rescue/rpx/digest.rs index 7d5fcb8e..d104d4f9 100644 --- a/src/hash/rescue/rpx/digest.rs +++ b/src/hash/rescue/rpx/digest.rs @@ -118,26 +118,106 @@ impl Randomizable for RpxDigest { // CONVERSIONS: FROM RPX DIGEST // ================================================================================================ -impl From<&RpxDigest> for [Felt; DIGEST_SIZE] { - fn from(value: &RpxDigest) -> Self { - value.0 +#[derive(Copy, Clone, Debug)] +pub enum RpxDigestError { + InvalidInteger, +} + +impl TryFrom<&RpxDigest> for [bool; DIGEST_SIZE] { + type Error = RpxDigestError; + + fn try_from(value: &RpxDigest) -> Result { + (*value).try_into() } } -impl From for [Felt; DIGEST_SIZE] { - fn from(value: RpxDigest) -> Self { - value.0 +impl TryFrom for [bool; DIGEST_SIZE] { + type Error = RpxDigestError; + + fn try_from(value: RpxDigest) -> Result { + fn to_bool(v: u64) -> Option { + if v <= 1 { + Some(v == 1) + } else { + None + } + } + + Ok([ + to_bool(value.0[0].as_int()).ok_or(RpxDigestError::InvalidInteger)?, + to_bool(value.0[1].as_int()).ok_or(RpxDigestError::InvalidInteger)?, + to_bool(value.0[2].as_int()).ok_or(RpxDigestError::InvalidInteger)?, + to_bool(value.0[3].as_int()).ok_or(RpxDigestError::InvalidInteger)?, + ]) + } +} + +impl TryFrom<&RpxDigest> for [u8; DIGEST_SIZE] { + type Error = RpxDigestError; + + fn try_from(value: &RpxDigest) -> Result { + (*value).try_into() + } +} + +impl TryFrom for [u8; DIGEST_SIZE] { + type Error = RpxDigestError; + + fn try_from(value: RpxDigest) -> Result { + Ok([ + value.0[0].as_int().try_into().map_err(|_| RpxDigestError::InvalidInteger)?, + value.0[1].as_int().try_into().map_err(|_| RpxDigestError::InvalidInteger)?, + value.0[2].as_int().try_into().map_err(|_| RpxDigestError::InvalidInteger)?, + value.0[3].as_int().try_into().map_err(|_| RpxDigestError::InvalidInteger)?, + ]) + } +} + +impl TryFrom<&RpxDigest> for [u16; DIGEST_SIZE] { + type Error = RpxDigestError; + + fn try_from(value: &RpxDigest) -> Result { + (*value).try_into() + } +} + +impl TryFrom for [u16; DIGEST_SIZE] { + type Error = RpxDigestError; + + fn try_from(value: RpxDigest) -> Result { + Ok([ + value.0[0].as_int().try_into().map_err(|_| RpxDigestError::InvalidInteger)?, + value.0[1].as_int().try_into().map_err(|_| RpxDigestError::InvalidInteger)?, + value.0[2].as_int().try_into().map_err(|_| RpxDigestError::InvalidInteger)?, + value.0[3].as_int().try_into().map_err(|_| RpxDigestError::InvalidInteger)?, + ]) + } +} + +impl TryFrom<&RpxDigest> for [u32; DIGEST_SIZE] { + type Error = RpxDigestError; + + fn try_from(value: &RpxDigest) -> Result { + (*value).try_into() + } +} + +impl TryFrom for [u32; DIGEST_SIZE] { + type Error = RpxDigestError; + + fn try_from(value: RpxDigest) -> Result { + Ok([ + value.0[0].as_int().try_into().map_err(|_| RpxDigestError::InvalidInteger)?, + value.0[1].as_int().try_into().map_err(|_| RpxDigestError::InvalidInteger)?, + value.0[2].as_int().try_into().map_err(|_| RpxDigestError::InvalidInteger)?, + value.0[3].as_int().try_into().map_err(|_| RpxDigestError::InvalidInteger)?, + ]) } } impl From<&RpxDigest> for [u64; DIGEST_SIZE] { fn from(value: &RpxDigest) -> Self { - [ - value.0[0].as_int(), - value.0[1].as_int(), - value.0[2].as_int(), - value.0[3].as_int(), - ] + (*value).into() } } @@ -152,6 +232,18 @@ impl From for [u64; DIGEST_SIZE] { } } +impl From<&RpxDigest> for [Felt; DIGEST_SIZE] { + fn from(value: &RpxDigest) -> Self { + value.0 + } +} + +impl From for [Felt; DIGEST_SIZE] { + fn from(value: RpxDigest) -> Self { + value.0 + } +} + impl From<&RpxDigest> for [u8; DIGEST_BYTES] { fn from(value: &RpxDigest) -> Self { value.as_bytes() @@ -164,6 +256,13 @@ impl From for [u8; DIGEST_BYTES] { } } +impl From<&RpxDigest> for String { + /// The returned string starts with `0x`. + fn from(value: &RpxDigest) -> Self { + (*value).into() + } +} + impl From for String { /// The returned string starts with `0x`. fn from(value: RpxDigest) -> Self { @@ -171,20 +270,76 @@ impl From for String { } } -impl From<&RpxDigest> for String { - /// The returned string starts with `0x`. - fn from(value: &RpxDigest) -> Self { +// CONVERSIONS: TO RPX DIGEST +// ================================================================================================ + +impl From<&[bool; DIGEST_SIZE]> for RpxDigest { + fn from(value: &[bool; DIGEST_SIZE]) -> Self { (*value).into() } } -// CONVERSIONS: TO RPX DIGEST -// ================================================================================================ +impl From<[bool; DIGEST_SIZE]> for RpxDigest { + fn from(value: [bool; DIGEST_SIZE]) -> Self { + [value[0] as u32, value[1] as u32, value[2] as u32, value[3] as u32].into() + } +} -#[derive(Copy, Clone, Debug)] -pub enum RpxDigestError { - /// The provided u64 integer does not fit in the field's moduli. - InvalidInteger, +impl From<&[u8; DIGEST_SIZE]> for RpxDigest { + fn from(value: &[u8; DIGEST_SIZE]) -> Self { + (*value).into() + } +} + +impl From<[u8; DIGEST_SIZE]> for RpxDigest { + fn from(value: [u8; DIGEST_SIZE]) -> Self { + Self([value[0].into(), value[1].into(), value[2].into(), value[3].into()]) + } +} + +impl From<&[u16; DIGEST_SIZE]> for RpxDigest { + fn from(value: &[u16; DIGEST_SIZE]) -> Self { + (*value).into() + } +} + +impl From<[u16; DIGEST_SIZE]> for RpxDigest { + fn from(value: [u16; DIGEST_SIZE]) -> Self { + Self([value[0].into(), value[1].into(), value[2].into(), value[3].into()]) + } +} + +impl From<&[u32; DIGEST_SIZE]> for RpxDigest { + fn from(value: &[u32; DIGEST_SIZE]) -> Self { + (*value).into() + } +} + +impl From<[u32; DIGEST_SIZE]> for RpxDigest { + fn from(value: [u32; DIGEST_SIZE]) -> Self { + Self([value[0].into(), value[1].into(), value[2].into(), value[3].into()]) + } +} + +impl TryFrom<&[u64; DIGEST_SIZE]> for RpxDigest { + type Error = RpxDigestError; + + fn try_from(value: &[u64; DIGEST_SIZE]) -> Result { + (*value).try_into() + } +} + +impl TryFrom<[u64; DIGEST_SIZE]> for RpxDigest { + type Error = RpxDigestError; + + fn try_from(value: [u64; DIGEST_SIZE]) -> Result { + Ok(Self([ + value[0].try_into().map_err(|_| RpxDigestError::InvalidInteger)?, + value[1].try_into().map_err(|_| RpxDigestError::InvalidInteger)?, + value[2].try_into().map_err(|_| RpxDigestError::InvalidInteger)?, + value[3].try_into().map_err(|_| RpxDigestError::InvalidInteger)?, + ])) + } } impl From<&[Felt; DIGEST_SIZE]> for RpxDigest { @@ -199,6 +354,14 @@ impl From<[Felt; DIGEST_SIZE]> for RpxDigest { } } +impl TryFrom<&[u8; DIGEST_BYTES]> for RpxDigest { + type Error = HexParseError; + + fn try_from(value: &[u8; DIGEST_BYTES]) -> Result { + (*value).try_into() + } +} + impl TryFrom<[u8; DIGEST_BYTES]> for RpxDigest { type Error = HexParseError; @@ -218,14 +381,6 @@ impl TryFrom<[u8; DIGEST_BYTES]> for RpxDigest { } } -impl TryFrom<&[u8; DIGEST_BYTES]> for RpxDigest { - type Error = HexParseError; - - fn try_from(value: &[u8; DIGEST_BYTES]) -> Result { - (*value).try_into() - } -} - impl TryFrom<&[u8]> for RpxDigest { type Error = HexParseError; @@ -234,50 +389,29 @@ impl TryFrom<&[u8]> for RpxDigest { } } -impl TryFrom<[u64; DIGEST_SIZE]> for RpxDigest { - type Error = RpxDigestError; - - fn try_from(value: [u64; DIGEST_SIZE]) -> Result { - Ok(Self([ - value[0].try_into().map_err(|_| RpxDigestError::InvalidInteger)?, - value[1].try_into().map_err(|_| RpxDigestError::InvalidInteger)?, - value[2].try_into().map_err(|_| RpxDigestError::InvalidInteger)?, - value[3].try_into().map_err(|_| RpxDigestError::InvalidInteger)?, - ])) - } -} - -impl TryFrom<&[u64; DIGEST_SIZE]> for RpxDigest { - type Error = RpxDigestError; - - fn try_from(value: &[u64; DIGEST_SIZE]) -> Result { - (*value).try_into() - } -} - impl TryFrom<&str> for RpxDigest { type Error = HexParseError; /// Expects the string to start with `0x`. fn try_from(value: &str) -> Result { - hex_to_bytes(value).and_then(|v| v.try_into()) + hex_to_bytes::(value).and_then(RpxDigest::try_from) } } -impl TryFrom for RpxDigest { +impl TryFrom<&String> for RpxDigest { type Error = HexParseError; /// Expects the string to start with `0x`. - fn try_from(value: String) -> Result { + fn try_from(value: &String) -> Result { value.as_str().try_into() } } -impl TryFrom<&String> for RpxDigest { +impl TryFrom for RpxDigest { type Error = HexParseError; /// Expects the string to start with `0x`. - fn try_from(value: &String) -> Result { + fn try_from(value: String) -> Result { value.as_str().try_into() } } @@ -308,6 +442,17 @@ impl Deserializable for RpxDigest { } } +// ITERATORS +// ================================================================================================ +impl IntoIterator for RpxDigest { + type Item = Felt; + type IntoIter = <[Felt; 4] as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + // TESTS // ================================================================================================ @@ -338,7 +483,6 @@ mod tests { assert_eq!(d1, d2); } - #[cfg(feature = "std")] #[test] fn digest_encoding() { let digest = RpxDigest([ @@ -363,43 +507,71 @@ mod tests { Felt::new(rand_value()), ]); - let v: [Felt; DIGEST_SIZE] = digest.into(); + // BY VALUE + // ---------------------------------------------------------------------------------------- + let v: [bool; DIGEST_SIZE] = [true, false, true, true]; let v2: RpxDigest = v.into(); - assert_eq!(digest, v2); + assert_eq!(v, <[bool; DIGEST_SIZE]>::try_from(v2).unwrap()); - let v: [Felt; DIGEST_SIZE] = (&digest).into(); + let v: [u8; DIGEST_SIZE] = [0_u8, 1_u8, 2_u8, 3_u8]; let v2: RpxDigest = v.into(); - assert_eq!(digest, v2); + assert_eq!(v, <[u8; DIGEST_SIZE]>::try_from(v2).unwrap()); + + let v: [u16; DIGEST_SIZE] = [0_u16, 1_u16, 2_u16, 3_u16]; + let v2: RpxDigest = v.into(); + assert_eq!(v, <[u16; DIGEST_SIZE]>::try_from(v2).unwrap()); + + let v: [u32; DIGEST_SIZE] = [0_u32, 1_u32, 2_u32, 3_u32]; + let v2: RpxDigest = v.into(); + assert_eq!(v, <[u32; DIGEST_SIZE]>::try_from(v2).unwrap()); let v: [u64; DIGEST_SIZE] = digest.into(); let v2: RpxDigest = v.try_into().unwrap(); assert_eq!(digest, v2); - let v: [u64; DIGEST_SIZE] = (&digest).into(); - let v2: RpxDigest = v.try_into().unwrap(); + let v: [Felt; DIGEST_SIZE] = digest.into(); + let v2: RpxDigest = v.into(); assert_eq!(digest, v2); let v: [u8; DIGEST_BYTES] = digest.into(); let v2: RpxDigest = v.try_into().unwrap(); assert_eq!(digest, v2); - let v: [u8; DIGEST_BYTES] = (&digest).into(); + let v: String = digest.into(); let v2: RpxDigest = v.try_into().unwrap(); assert_eq!(digest, v2); - let v: String = digest.into(); - let v2: RpxDigest = v.try_into().unwrap(); + // BY REF + // ---------------------------------------------------------------------------------------- + let v: [bool; DIGEST_SIZE] = [true, false, true, true]; + let v2: RpxDigest = (&v).into(); + assert_eq!(v, <[bool; DIGEST_SIZE]>::try_from(&v2).unwrap()); + + let v: [u8; DIGEST_SIZE] = [0_u8, 1_u8, 2_u8, 3_u8]; + let v2: RpxDigest = (&v).into(); + assert_eq!(v, <[u8; DIGEST_SIZE]>::try_from(&v2).unwrap()); + + let v: [u16; DIGEST_SIZE] = [0_u16, 1_u16, 2_u16, 3_u16]; + let v2: RpxDigest = (&v).into(); + assert_eq!(v, <[u16; DIGEST_SIZE]>::try_from(&v2).unwrap()); + + let v: [u32; DIGEST_SIZE] = [0_u32, 1_u32, 2_u32, 3_u32]; + let v2: RpxDigest = (&v).into(); + assert_eq!(v, <[u32; DIGEST_SIZE]>::try_from(&v2).unwrap()); + + let v: [u64; DIGEST_SIZE] = (&digest).into(); + let v2: RpxDigest = (&v).try_into().unwrap(); assert_eq!(digest, v2); - let v: String = (&digest).into(); - let v2: RpxDigest = v.try_into().unwrap(); + let v: [Felt; DIGEST_SIZE] = (&digest).into(); + let v2: RpxDigest = (&v).into(); assert_eq!(digest, v2); - let v: [u8; DIGEST_BYTES] = digest.into(); + let v: [u8; DIGEST_BYTES] = (&digest).into(); let v2: RpxDigest = (&v).try_into().unwrap(); assert_eq!(digest, v2); - let v: [u8; DIGEST_BYTES] = (&digest).into(); + let v: String = (&digest).into(); let v2: RpxDigest = (&v).try_into().unwrap(); assert_eq!(digest, v2); } diff --git a/src/merkle/smt/full/tests.rs b/src/merkle/smt/full/tests.rs index 1c066bd7..e852811c 100644 --- a/src/merkle/smt/full/tests.rs +++ b/src/merkle/smt/full/tests.rs @@ -287,8 +287,7 @@ fn test_empty_leaf_hash() { #[test] fn test_smt_get_value() { let key_1: RpoDigest = RpoDigest::from([ONE, ONE, ONE, ONE]); - let key_2: RpoDigest = - RpoDigest::from([2_u32.into(), 2_u32.into(), 2_u32.into(), 2_u32.into()]); + let key_2: RpoDigest = RpoDigest::from([2_u32, 2_u32, 2_u32, 2_u32]); let value_1 = [ONE; WORD_SIZE]; let value_2 = [2_u32.into(); WORD_SIZE]; @@ -302,8 +301,7 @@ fn test_smt_get_value() { assert_eq!(value_2, returned_value_2); // Check that a key with no inserted value returns the empty word - let key_no_value = - RpoDigest::from([42_u32.into(), 42_u32.into(), 42_u32.into(), 42_u32.into()]); + let key_no_value = RpoDigest::from([42_u32, 42_u32, 42_u32, 42_u32]); assert_eq!(EMPTY_WORD, smt.get_value(&key_no_value)); } @@ -312,8 +310,7 @@ fn test_smt_get_value() { #[test] fn test_smt_entries() { let key_1: RpoDigest = RpoDigest::from([ONE, ONE, ONE, ONE]); - let key_2: RpoDigest = - RpoDigest::from([2_u32.into(), 2_u32.into(), 2_u32.into(), 2_u32.into()]); + let key_2: RpoDigest = RpoDigest::from([2_u32, 2_u32, 2_u32, 2_u32]); let value_1 = [ONE; WORD_SIZE]; let value_2 = [2_u32.into(); WORD_SIZE]; @@ -347,7 +344,7 @@ fn test_empty_smt_leaf_serialization() { #[test] fn test_single_smt_leaf_serialization() { let single_leaf = SmtLeaf::new_single( - RpoDigest::from([10_u32.into(), 11_u32.into(), 12_u32.into(), 13_u32.into()]), + RpoDigest::from([10_u32, 11_u32, 12_u32, 13_u32]), [1_u32.into(), 2_u32.into(), 3_u32.into(), 4_u32.into()], ); @@ -363,11 +360,11 @@ fn test_single_smt_leaf_serialization() { fn test_multiple_smt_leaf_serialization_success() { let multiple_leaf = SmtLeaf::new_multiple(vec![ ( - RpoDigest::from([10_u32.into(), 11_u32.into(), 12_u32.into(), 13_u32.into()]), + RpoDigest::from([10_u32, 11_u32, 12_u32, 13_u32]), [1_u32.into(), 2_u32.into(), 3_u32.into(), 4_u32.into()], ), ( - RpoDigest::from([100_u32.into(), 101_u32.into(), 102_u32.into(), 13_u32.into()]), + RpoDigest::from([100_u32, 101_u32, 102_u32, 13_u32]), [11_u32.into(), 12_u32.into(), 13_u32.into(), 14_u32.into()], ), ]) From 754a301dd497e193efdf9e2cb5193ec63d071ea0 Mon Sep 17 00:00:00 2001 From: Augusto Hack Date: Tue, 7 May 2024 12:08:48 +0200 Subject: [PATCH 4/8] rpo/rpx: export digest error enum (#313) --- src/hash/mod.rs | 4 ++-- src/hash/rescue/mod.rs | 4 ++-- src/hash/rescue/rpo/mod.rs | 2 +- src/hash/rescue/rpx/mod.rs | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/hash/mod.rs b/src/hash/mod.rs index ea068339..630a6f57 100644 --- a/src/hash/mod.rs +++ b/src/hash/mod.rs @@ -6,11 +6,11 @@ pub mod blake; mod rescue; pub mod rpo { - pub use super::rescue::{Rpo256, RpoDigest}; + pub use super::rescue::{Rpo256, RpoDigest, RpoDigestError}; } pub mod rpx { - pub use super::rescue::{Rpx256, RpxDigest}; + pub use super::rescue::{Rpx256, RpxDigest, RpxDigestError}; } // RE-EXPORTS diff --git a/src/hash/rescue/mod.rs b/src/hash/rescue/mod.rs index 448fe899..b22c111b 100644 --- a/src/hash/rescue/mod.rs +++ b/src/hash/rescue/mod.rs @@ -11,10 +11,10 @@ mod mds; use mds::{apply_mds, MDS}; mod rpo; -pub use rpo::{Rpo256, RpoDigest}; +pub use rpo::{Rpo256, RpoDigest, RpoDigestError}; mod rpx; -pub use rpx::{Rpx256, RpxDigest}; +pub use rpx::{Rpx256, RpxDigest, RpxDigestError}; #[cfg(test)] mod tests; diff --git a/src/hash/rescue/rpo/mod.rs b/src/hash/rescue/rpo/mod.rs index 8d71467c..2909bc6d 100644 --- a/src/hash/rescue/rpo/mod.rs +++ b/src/hash/rescue/rpo/mod.rs @@ -8,7 +8,7 @@ use super::{ }; mod digest; -pub use digest::RpoDigest; +pub use digest::{RpoDigest, RpoDigestError}; #[cfg(test)] mod tests; diff --git a/src/hash/rescue/rpx/mod.rs b/src/hash/rescue/rpx/mod.rs index aa2e8d14..356f71de 100644 --- a/src/hash/rescue/rpx/mod.rs +++ b/src/hash/rescue/rpx/mod.rs @@ -9,7 +9,7 @@ use super::{ }; mod digest; -pub use digest::RpxDigest; +pub use digest::{RpxDigest, RpxDigestError}; pub type CubicExtElement = CubeExtension; From 042126e53aa6489f67adf24e669278b733bdfcaa Mon Sep 17 00:00:00 2001 From: Al-Kindi-0 <82364884+Al-Kindi-0@users.noreply.github.com> Date: Mon, 3 Jun 2024 10:06:47 +0200 Subject: [PATCH 5/8] doc: update RPO docs on domain separation --- src/hash/rescue/rpo/mod.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/hash/rescue/rpo/mod.rs b/src/hash/rescue/rpo/mod.rs index 2909bc6d..5642f358 100644 --- a/src/hash/rescue/rpo/mod.rs +++ b/src/hash/rescue/rpo/mod.rs @@ -22,9 +22,10 @@ mod tests; /// [specifications](https://eprint.iacr.org/2022/1577) /// /// The parameters used to instantiate the function are: -/// * Field: 64-bit prime field with modulus 2^64 - 2^32 + 1. +/// * Field: 64-bit prime field with modulus p = 2^64 - 2^32 + 1. /// * State width: 12 field elements. -/// * Capacity size: 4 field elements. +/// * Rate size: r = 8 field elements. +/// * Capacity size: c = 4 field elements. /// * Number of founds: 7. /// * S-Box degree: 7. /// @@ -52,6 +53,17 @@ mod tests; /// to deserialize them into field elements and then hash them using /// [hash_elements()](Rpo256::hash_elements) function rather then hashing the serialized bytes /// using [hash()](Rpo256::hash) function. +/// +/// ## Domain separation +/// [merge_in_domain()](Rpo256::merge_in_domain) hashes two digests into one given some domain +/// identifier and the current implementation sets the second capacity element to the value of +/// the domain identifier. Using a similar argument as the one formulated for domain separation +/// of the RPX hash function in Appendix C of [specifications](https://eprint.iacr.org/2023/1045), +/// one sees that doing so degrades only pre-image resistance, from its initial bound of c.log_2(p), +/// by as much as the log_2 of the size of the domain identifier space. Since pre-image resistance +/// becomes the bottleneck for the security bound of the sponge in overwrite-mode only when it is +/// smaller than 2^128, we see that the target 128-bit security level is maintained as long as +/// the size of the domain identifier space, including for padding, is less than 2^128. #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub struct Rpo256(); From 50b5bf1d875e995ae129eec54d55b752a8586c37 Mon Sep 17 00:00:00 2001 From: Al-Kindi-0 <82364884+Al-Kindi-0@users.noreply.github.com> Date: Mon, 3 Jun 2024 10:57:06 +0200 Subject: [PATCH 6/8] chore: minor edits --- src/hash/rescue/rpo/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/hash/rescue/rpo/mod.rs b/src/hash/rescue/rpo/mod.rs index 5642f358..9288d707 100644 --- a/src/hash/rescue/rpo/mod.rs +++ b/src/hash/rescue/rpo/mod.rs @@ -53,16 +53,16 @@ mod tests; /// to deserialize them into field elements and then hash them using /// [hash_elements()](Rpo256::hash_elements) function rather then hashing the serialized bytes /// using [hash()](Rpo256::hash) function. -/// +/// /// ## Domain separation -/// [merge_in_domain()](Rpo256::merge_in_domain) hashes two digests into one given some domain +/// [merge_in_domain()](Rpo256::merge_in_domain) hashes two digests into one digest with some domain /// identifier and the current implementation sets the second capacity element to the value of -/// the domain identifier. Using a similar argument as the one formulated for domain separation +/// this domain identifier. Using a similar argument as the one formulated for domain separation /// of the RPX hash function in Appendix C of [specifications](https://eprint.iacr.org/2023/1045), /// one sees that doing so degrades only pre-image resistance, from its initial bound of c.log_2(p), /// by as much as the log_2 of the size of the domain identifier space. Since pre-image resistance /// becomes the bottleneck for the security bound of the sponge in overwrite-mode only when it is -/// smaller than 2^128, we see that the target 128-bit security level is maintained as long as +/// lower than 2^128, we see that the target 128-bit security level is maintained as long as /// the size of the domain identifier space, including for padding, is less than 2^128. #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub struct Rpo256(); From a6d008a6d45bab57fdcbc798af20b99578bb8714 Mon Sep 17 00:00:00 2001 From: Al-Kindi-0 <82364884+Al-Kindi-0@users.noreply.github.com> Date: Wed, 5 Jun 2024 07:34:43 +0200 Subject: [PATCH 7/8] doc: add doc to rpx --- CHANGELOG.md | 5 +++++ src/hash/rescue/rpo/mod.rs | 4 ++-- src/hash/rescue/rpx/mod.rs | 11 +++++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 091bd2a3..bed68635 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.10.0 (TBD) + +* Added more `RpoDigest` and `RpxDigest` conversions (#311). +* Migrated to Winterfell v0.9.0 (#315) + ## 0.9.3 (2024-04-24) * Added `RpxRandomCoin` struct (#307). diff --git a/src/hash/rescue/rpo/mod.rs b/src/hash/rescue/rpo/mod.rs index 9288d707..82be3dc1 100644 --- a/src/hash/rescue/rpo/mod.rs +++ b/src/hash/rescue/rpo/mod.rs @@ -57,8 +57,8 @@ mod tests; /// ## Domain separation /// [merge_in_domain()](Rpo256::merge_in_domain) hashes two digests into one digest with some domain /// identifier and the current implementation sets the second capacity element to the value of -/// this domain identifier. Using a similar argument as the one formulated for domain separation -/// of the RPX hash function in Appendix C of [specifications](https://eprint.iacr.org/2023/1045), +/// this domain identifier. Using a similar argument to the one formulated for domain separation of +/// the RPX hash function in Appendix C of its [specification](https://eprint.iacr.org/2023/1045), /// one sees that doing so degrades only pre-image resistance, from its initial bound of c.log_2(p), /// by as much as the log_2 of the size of the domain identifier space. Since pre-image resistance /// becomes the bottleneck for the security bound of the sponge in overwrite-mode only when it is diff --git a/src/hash/rescue/rpx/mod.rs b/src/hash/rescue/rpx/mod.rs index 356f71de..c8540250 100644 --- a/src/hash/rescue/rpx/mod.rs +++ b/src/hash/rescue/rpx/mod.rs @@ -55,6 +55,17 @@ pub type CubicExtElement = CubeExtension; /// to deserialize them into field elements and then hash them using /// [hash_elements()](Rpx256::hash_elements) function rather then hashing the serialized bytes /// using [hash()](Rpx256::hash) function. +/// +/// ## Domain separation +/// [merge_in_domain()](Rpx256::merge_in_domain) hashes two digests into one digest with some domain +/// identifier and the current implementation sets the second capacity element to the value of +/// this domain identifier. Using a similar argument to the one formulated for domain separation +/// in Appendix C of the [specifications](https://eprint.iacr.org/2023/1045), one sees that doing +/// so degrades only pre-image resistance, from its initial bound of c.log_2(p), by as much as +/// the log_2 of the size of the domain identifier space. Since pre-image resistance becomes +/// the bottleneck for the security bound of the sponge in overwrite-mode only when it is +/// lower than 2^128, we see that the target 128-bit security level is maintained as long as +/// the size of the domain identifier space, including for padding, is less than 2^128. #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub struct Rpx256(); From 1fabf571e2cbb4cd66cc0c198465a1a17a4ce6d0 Mon Sep 17 00:00:00 2001 From: Al-Kindi-0 <82364884+Al-Kindi-0@users.noreply.github.com> Date: Wed, 5 Jun 2024 07:37:36 +0200 Subject: [PATCH 8/8] fix: fmt --- src/hash/rescue/rpx/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hash/rescue/rpx/mod.rs b/src/hash/rescue/rpx/mod.rs index c8540250..c3aca3cd 100644 --- a/src/hash/rescue/rpx/mod.rs +++ b/src/hash/rescue/rpx/mod.rs @@ -55,7 +55,7 @@ pub type CubicExtElement = CubeExtension; /// to deserialize them into field elements and then hash them using /// [hash_elements()](Rpx256::hash_elements) function rather then hashing the serialized bytes /// using [hash()](Rpx256::hash) function. -/// +/// /// ## Domain separation /// [merge_in_domain()](Rpx256::merge_in_domain) hashes two digests into one digest with some domain /// identifier and the current implementation sets the second capacity element to the value of