diff --git a/Cargo.lock b/Cargo.lock index b657aed..4b2ebd8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.20.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" dependencies = [ "gimli", ] @@ -19,13 +19,14 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "ahash" -version = "0.8.3" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" dependencies = [ "cfg-if", "once_cell", "version_check", + "zerocopy", ] [[package]] @@ -37,18 +38,6 @@ dependencies = [ "backtrace", ] -[[package]] -name = "ark-bls12-381" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c775f0d12169cba7aae4caeb547bb6a50781c7449a8aa53793827c9ec4abf488" -dependencies = [ - "ark-ec", - "ark-ff", - "ark-serialize", - "ark-std", -] - [[package]] name = "ark-crypto-primitives" version = "0.4.0" @@ -216,9 +205,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "backtrace" -version = "0.3.68" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" dependencies = [ "addr2line", "cc", @@ -264,9 +253,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cpufeatures" -version = "0.2.9" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" dependencies = [ "libc", ] @@ -283,9 +272,9 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "4.1.0" +version = "4.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622178105f911d937a42cdb140730ba4a3ed2becd8ae6ce39c7d28b5d75d4588" +checksum = "e89b8c6a2e4b1f45971ad09761aafb85514a84744b67a95e32c3cc1352d1f65c" dependencies = [ "cfg-if", "cpufeatures", @@ -299,13 +288,13 @@ dependencies = [ [[package]] name = "curve25519-dalek-derive" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.31", + "syn 2.0.39", ] [[package]] @@ -348,9 +337,9 @@ dependencies = [ [[package]] name = "fiat-crypto" -version = "0.2.1" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0870c84016d4b481be5c9f323c24f65e31e901ae618f0e80f4308fb00de1d2d" +checksum = "27573eac26f4dd11e2b1916c3fe1baa56407c83c71a773a8ba17ec0bca03b6b7" [[package]] name = "generic-array" @@ -364,9 +353,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" dependencies = [ "cfg-if", "libc", @@ -375,9 +364,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.27.3" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "group" @@ -425,9 +414,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.147" +version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" [[package]] name = "log" @@ -437,9 +426,9 @@ checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "memchr" -version = "2.6.3" +version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] name = "miniz_oxide" @@ -455,7 +444,6 @@ name = "nimue" version = "0.0.1-alpha7" dependencies = [ "anyhow", - "ark-bls12-381", "ark-crypto-primitives", "ark-curve25519", "ark-ec", @@ -498,18 +486,18 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", ] [[package]] name = "object" -version = "0.31.1" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" dependencies = [ "memchr", ] @@ -534,9 +522,9 @@ checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "platforms" -version = "3.1.2" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4503fa043bf02cee09a9582e9554b4c6403b2ef55e4612e96561d294419429f8" +checksum = "14e6ab3f592e6fb464fc9712d8d6e6912de6473954635fd76a589d832cffcbb0" [[package]] name = "ppv-lite86" @@ -546,9 +534,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.66" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] @@ -609,15 +597,15 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.18" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" [[package]] name = "sha2" -version = "0.10.7" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", @@ -643,9 +631,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.31" +version = "2.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "718fa2415bcb8d8bd775917a1bf12a7931b6dfa890753378538118181e0cb398" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" dependencies = [ "proc-macro2", "quote", @@ -654,20 +642,19 @@ dependencies = [ [[package]] name = "tracing" -version = "0.1.37" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "cfg-if", "pin-project-lite", "tracing-core", ] [[package]] name = "tracing-core" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", "valuable", @@ -684,15 +671,15 @@ dependencies = [ [[package]] name = "typenum" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unicode-ident" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "valuable" @@ -712,11 +699,31 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "zerocopy" +version = "0.7.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e97e415490559a91254a2979b4829267a57d2fcd741a98eee8b722fb57289aa0" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd7e48ccf166952882ca8bd778a43502c64f33bf94c12ebe2a7f08e5a0f6689f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + [[package]] name = "zeroize" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" dependencies = [ "zeroize_derive", ] @@ -729,5 +736,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.31", + "syn 2.0.39", ] diff --git a/Cargo.toml b/Cargo.toml index fb18a5d..1b0ec63 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,8 @@ ark-crypto-primitives = {version="0.4.0", optional=true} curve25519-dalek = {version="4.0.0", optional=true} group = {version="0.13.0", optional=true} +# anemoi = {git="https://github.com/mmaker/anemoi"} + [features] default = [] arkworks = ["dep:ark-ff", "dep:ark-ec", "dep:ark-serialize", "dep:ark-crypto-primitives"] @@ -30,7 +32,6 @@ dalek = ["dep:curve25519-dalek"] zkcrypto = ["dep:group"] [dev-dependencies] -ark-bls12-381 = "0.4.0" ark-std = "0.4.0" sha2 = "0.10.7" blake2 = "0.10.6" diff --git a/examples/bulletproof.rs b/examples/bulletproof.rs index 27864c8..5be6ee6 100644 --- a/examples/bulletproof.rs +++ b/examples/bulletproof.rs @@ -185,7 +185,9 @@ fn main() { // the test vectors let a = (0..size).map(|x| F::from(x as u32)).collect::>(); - let b = (0..size).map(|x| F::from(x as u32 + 42)).collect::>(); + let b = (0..size) + .map(|x| F::from(x as u32 + 42)) + .collect::>(); let ab = inner_prod(&a, &b); // the generators to be used for respectively a, b, ip let g = (0..a.len()) diff --git a/scripts/useful_bits_modp.py b/scripts/useful_bits_modp.py index 545809d..d30617a 100644 --- a/scripts/useful_bits_modp.py +++ b/scripts/useful_bits_modp.py @@ -1,10 +1,4 @@ """ -Use this program to find the number of bits that appear uniformly random -in the uniform distribution mod p. - -While this function is trivial for byte-oriented hashes, for algebraic hashes, it requires proper implementation. -Many implementations simply truncate the least-significant bits, but this approach -results in a statistical deviation from uniform randomness. The number of useful bits, denoted as `n`, has a statistical distance from uniformly random given by: p is provided on stdin in any format that python can eval. For example, @@ -12,12 +6,14 @@ $ python3 scripts/useful_bits_modp.py <<< 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab """ + def useful_bits(p): - for n in range(p.bit_length()-1, 0, -1): - alpha = p % 2^n - if n+1 + p.bit_length() - alpha.bit_length() - (2^n-alpha).bit_length() >= 128: - return n + return max( + n for n in range(p.bit_length() - 1, 0, -1) + if n + 1 + p.bit_length() - (alpha := p % 2 ** n).bit_length() - + (2 ** n - alpha).bit_length() >= 128 + ) if __name__ == '__main__': - print(useful_bits(eval(input()))) \ No newline at end of file + print(useful_bits(eval(input()))) \ No newline at end of file diff --git a/src/arthur.rs b/src/arthur.rs index 06177b1..2921548 100644 --- a/src/arthur.rs +++ b/src/arthur.rs @@ -6,8 +6,6 @@ use crate::{IOPattern, Safe}; use super::hash::{DuplexHash, Keccak}; use super::{DefaultHash, DefaultRng, InvalidTag}; - - /// A cryptographically-secure random number generator that is bound to the protocol transcript. /// /// For most public-coin protocols it is *vital* not to have two commitments for the same challenge. diff --git a/src/hash/keccak.rs b/src/hash/keccak.rs index bef2eb6..a3cf3dc 100644 --- a/src/hash/keccak.rs +++ b/src/hash/keccak.rs @@ -31,7 +31,7 @@ impl Sponge for AlignedKeccakState { fn new(tag: [u8; 32]) -> Self { let mut state = Self::default(); - state[..32].copy_from_slice(&tag); + state[Self::RATE..Self::RATE + 32].copy_from_slice(&tag); state } diff --git a/src/hash/legacy.rs b/src/hash/legacy.rs index 53199d8..d9f0c5d 100644 --- a/src/hash/legacy.rs +++ b/src/hash/legacy.rs @@ -2,11 +2,22 @@ //! //! This code is inspired from libsignal's poksho: //! . -//! With the variation that here, squeeze satisfies streaming and the implementation is for any Digest. +//! With the following generalizations: +//! - squeeze satisfies streaming +//! ```text +//! squeeze(1); squeeze(1); squeeze(1) = squeeze(3); +//! ``` +//! - the implementation is for any Digest. +//! +//! Informally, there are three random oracles: +//! - `ABSORB`` oracle, which starts with block `000..00` and takes as input DIGEST_SIZE + arbitrary_length bytes +//! - `SQUEEZE`` oracle, which starts with block `000..01` and takes as input DIGEST_SIZE + sizeof(u64) bytes +//! - `SQUEEZE_END`` oracle, which starts with block `000..02` and takes as input DIGEST_SIZE + sizeof(u64) bytes +//! Using the above, `absorb_unchecked` will use the absorb oracle with some previous `cv` state. +//! `ratchet_unchecked` will store into `cv` the digest of the current digest. +//! `squeeze_unchecked` will use the squeeze oracle to output `output.len()` bytes, +//! and finally `squeeze_end` will set the state `cv` to the current squeeze digest and length. //! -//! ```text -//! squeeze(1); squeeze(1); squeeze(1) = squeeze(3); -//! ``` use core::mem::size_of; @@ -32,33 +43,55 @@ pub struct DigestBridge { #[derive(Clone, PartialEq, Eq)] enum Mode { + Start, Absorb, - Ratcheted(usize), + Squeeze(usize), } impl DigestBridge { const BLOCK_SIZE: usize = D::BlockSize::USIZE; const DIGEST_SIZE: usize = D::OutputSize::USIZE; - fn mask_squeeze(i: usize, cv: &[u8]) -> GenericArray { - assert_eq!(cv.len(), Self::DIGEST_SIZE); - - let squeeze_header_len: usize = - D::block_size() - ::output_size() - size_of::(); - + /// Create a block + /// | start | 0000 0000 | end | + fn pad_block(start: &[u8], end: &[u8]) -> GenericArray { + debug_assert!(start.len() + end.len() < Self::BLOCK_SIZE); let mut mask = GenericArray::default(); - let mutable_mask = mask.as_mut_slice(); - mutable_mask[0] = 0x80; - mutable_mask[squeeze_header_len..squeeze_header_len + Self::DIGEST_SIZE] - .copy_from_slice(cv); - mutable_mask[Self::BLOCK_SIZE - size_of::()..] - .copy_from_slice(i.to_le_bytes().as_slice()); - + mask[..start.len()].copy_from_slice(start); + mask[Self::BLOCK_SIZE - end.len()..].copy_from_slice(end); mask } fn mask_absorb() -> GenericArray { - GenericArray::default() + Self::pad_block(&[], &[0x00]) + } + + fn mask_squeeze() -> GenericArray { + Self::pad_block(&[], &[0x01]) + } + + fn mask_squeeze_end() -> GenericArray { + Self::pad_block(&[], &[0x02]) + } + + fn squeeze_end(&mut self) { + if let Mode::Squeeze(count) = self.mode { + Digest::reset(&mut self.hasher); + + // append to the state the squeeze mask + // with the length of the data read so far + // and the current digest + let byte_count = count * Self::DIGEST_SIZE - self.leftovers.len(); + let mut squeeze_hasher = D::new(); + Digest::update(&mut squeeze_hasher, &Self::mask_squeeze_end()); + Digest::update(&mut squeeze_hasher, &self.cv); + Digest::update(&mut squeeze_hasher, &byte_count.to_be_bytes()); + self.cv = Digest::finalize(squeeze_hasher); + + // set the sponge state in absorb mode + self.mode = Mode::Start; + self.leftovers.clear(); + } } } @@ -77,12 +110,10 @@ impl Drop for DigestBridge { impl Default for DigestBridge { fn default() -> Self { - let mut hasher = D::new(); - Digest::update(&mut hasher, &Self::mask_absorb()); Self { - hasher, + hasher: D::new(), cv: GenericArray::default(), - mode: Mode::Ratcheted(0), + mode: Mode::Start, leftovers: Vec::new(), } } @@ -90,80 +121,148 @@ impl Default for DigestBri impl DuplexHash for DigestBridge { fn new(tag: [u8; 32]) -> Self { + debug_assert!(size_of::() >= 32); let mut bridge = Self::default(); - Digest::update(&mut bridge.hasher, tag); - Digest::update(&mut bridge.hasher, &Self::mask_squeeze(0, &tag)); + bridge.absorb_unchecked(&tag); bridge } fn absorb_unchecked(&mut self, input: &[u8]) -> &mut Self { - if let Mode::Ratcheted(count) = self.mode { - // append to the state the squeeze mask - // with the length of the data read so far - // and the current digest - Digest::update(&mut self.hasher, &Self::mask_squeeze(count, &self.cv)); - // add the absorb mask - Digest::update(&mut self.hasher, &Self::mask_absorb()); - // set the sponge state in absorb mode + self.squeeze_end(); + + if self.mode == Mode::Start { self.mode = Mode::Absorb; - self.leftovers.zeroize(); - self.leftovers.clear(); + Digest::update(&mut self.hasher, &Self::mask_absorb()); + Digest::update(&mut self.hasher, &self.cv); } - // add the input to the hasher + Digest::update(&mut self.hasher, input); self } fn ratchet_unchecked(&mut self) -> &mut Self { - if self.mode == Mode::Absorb { - let digest = self.hasher.finalize_reset(); - // remove all data in `leftovers` - self.leftovers.zeroize(); - self.leftovers.clear(); - self.cv.copy_from_slice(&digest) - } + self.squeeze_end(); + // Double hash + self.cv = ::digest(&self.hasher.finalize_reset()); + // Restart the rest of the data + self.leftovers.zeroize(); + self.leftovers.clear(); + self.mode = Mode::Start; self } - // fn tag(self) -> &'static [u8] { - // self.cv.clone().as_ref() - // } - fn squeeze_unchecked(&mut self, output: &mut [u8]) -> &mut Self { - // Nothing to squeeze - if output.is_empty() { + if self.mode == Mode::Start { + self.mode = Mode::Squeeze(0); + // create the prefix hash + Digest::update(&mut self.hasher, &Self::mask_squeeze()); + Digest::update(&mut self.hasher, &self.cv); + self.squeeze_unchecked(output) + // If Absorbing, ratchet + } else if self.mode == Mode::Absorb { + self.ratchet_unchecked(); + self.squeeze_unchecked(output) + // If we have no more data to squeeze, return + } else if output.is_empty() { self - } // If we still have some digest not yet squeezed // from previous invocations, write it to the output. - else if !self.leftovers.is_empty() { + } else if !self.leftovers.is_empty() { let len = usize::min(output.len(), self.leftovers.len()); self.leftovers[..len].copy_from_slice(&output[..len]); self.leftovers.drain(..len); - // go back to the beginning self.squeeze_unchecked(&mut output[len..]) - } - // If absorbing, change mode and set the state properly - else if let Mode::Absorb = self.mode { - self.mode = Mode::Ratcheted(0); - self.cv.copy_from_slice(&self.hasher.finalize_reset()); - // go back to the beginning - self.squeeze_unchecked(output) // Squeeze another digest - } else if let Mode::Ratcheted(i) = self.mode { - let chunk_len = usize::min(output.len(), Self::DIGEST_SIZE); - // self.hasher is a freshly initialized state. + } else if let Mode::Squeeze(i) = self.mode { // Add the squeeze mask, current digest, and index - Digest::update(&mut self.hasher, &Self::mask_squeeze(i, &self.cv)); - let digest = self.hasher.finalize_reset(); + let mut output_hasher_prefix = self.hasher.clone(); + Digest::update(&mut output_hasher_prefix, &i.to_be_bytes()); + let digest = output_hasher_prefix.finalize(); // Copy the digest into the output, and store the rest for later + let chunk_len = usize::min(output.len(), Self::DIGEST_SIZE); output[..chunk_len].copy_from_slice(&digest[..chunk_len]); - self.leftovers.extend_from_slice(&output[chunk_len..]); + self.leftovers.extend_from_slice(&digest[chunk_len..]); // Update the state - self.mode = Mode::Ratcheted(i + 1); + self.mode = Mode::Squeeze(i + 1); self.squeeze_unchecked(&mut output[chunk_len..]) } else { unreachable!() } } } + +#[test] +fn test_shosha() { + let expected = b"\xEB\xE4\xEF\x29\xE1\x8A\xA5\x41\x37\xED\xD8\x9C\x23\xF8\ + \xBF\xEA\xC2\x73\x1C\x9F\x67\x5D\xA2\x0E\x7C\x67\xD5\xAD\ + \x68\xD7\xEE\x2D\x40\xA4\x52\x32\xB5\x99\x55\x2D\x46\xB5\ + \x20\x08\x2F\xB2\x70\x59\x71\xF0\x7B\x31\x58\xB0\x72\xB6\ + \x3A\xB0\x93\x4A\x05\xE6\xAF\x64"; + let mut sho = DigestBridge::::default(); + let mut got = vec![0u8; 64]; + sho.absorb_unchecked(b"asd"); + sho.ratchet_unchecked(); + // streaming absorb + sho.absorb_unchecked(b"asd"); + sho.absorb_unchecked(b"asd"); + // streaming squeeze + sho.squeeze_unchecked(&mut got[..32]); + sho.squeeze_unchecked(&mut got[32..]); + assert_eq!(got, expected); + + let expected = b"\xEB\xE4\xEF\x29\xE1\x8A\xA5\x41\x37\xED\xD8\x9C\x23\xF8\ + \xBF\xEA\xC2\x73\x1C\x9F\x67\x5D\xA2\x0E\x7C\x67\xD5\xAD\ + \x68\xD7\xEE\x2D\x40\xA4\x52\x32\xB5\x99\x55\x2D\x46\xB5\ + \x20\x08\x2F\xB2\x70\x59\x71\xF0\x7B\x31\x58\xB0\x72\xB6\ + \x3A\xB0\x93\x4A\x05\xE6\xAF\x64\x48"; + let mut sho = DigestBridge::::default(); + let mut got = vec![0u8; 65]; + sho.absorb_unchecked(b"asd"); + sho.ratchet_unchecked(); + sho.absorb_unchecked(b"asdasd"); + sho.squeeze_unchecked(&mut got); + assert_eq!(got, expected); + + let expected = b"\x0D\xDE\xEA\x97\x3F\x32\x10\xF7\x72\x5A\x3C\xDB\x24\x73\ + \xF8\x73\xAE\xAB\x8F\xEB\x32\xB8\x0D\xEE\x67\xF0\xCD\xE7\ + \x95\x4E\x92\x9A\x4E\x78\x7A\xEF\xEE\x6D\xBE\x91\xD3\xFF\ + \xF1\x62\x1A\xAB\x8D\x0D\x29\x19\x4F\x8A\xF9\x86\xD6\xF3\ + \x57\xAD\xD0\x15\x0D\xF7\xD9"; + + let mut sho = DigestBridge::::default(); + let mut got = vec![0u8; 150]; + sho.absorb_unchecked(b""); + sho.ratchet_unchecked(); + sho.absorb_unchecked(b"abc"); + sho.ratchet_unchecked(); + sho.absorb_unchecked(&[0u8; 63]); + sho.ratchet_unchecked(); + sho.absorb_unchecked(&[0u8; 64]); + sho.ratchet_unchecked(); + sho.absorb_unchecked(&[0u8; 65]); + sho.ratchet_unchecked(); + sho.absorb_unchecked(&[0u8; 127]); + sho.ratchet_unchecked(); + sho.absorb_unchecked(&[0u8; 128]); + sho.ratchet_unchecked(); + sho.absorb_unchecked(&[0u8; 129]); + sho.ratchet_unchecked(); + sho.squeeze_unchecked(&mut got[..63]); + // assert_eq!(&got[..63], &hex::decode("5bddc29ac27fd88bf682b07dd5c496b065f6ce11fd7aa77d1e13c609d77b9b2fed21b470f71a7f1fdfbfa895060c51302e782f440305d12ec85a492635dd3a").unwrap()[..]); + sho.squeeze_end(); + sho.squeeze_unchecked(&mut got[..64]); + // assert_eq!(&got[..64], &hex::decode("0ad17fc123d823548447b16ebebc8c21243dc4c59dd95525b7321c3b92a58e30156ec8c8e70987ed1483d2be84e89d2be5813fb1b8ab82119608120a2694a425").unwrap()[..]); + sho.squeeze_end(); + sho.squeeze_unchecked(&mut got[..65]); + sho.squeeze_end(); + sho.squeeze_unchecked(&mut got[..127]); + sho.squeeze_end(); + sho.squeeze_unchecked(&mut got[..128]); + sho.squeeze_end(); + sho.squeeze_unchecked(&mut got[..129]); + assert_eq!(got[0], 0xd0); + sho.absorb_unchecked(b"def"); + sho.ratchet_unchecked(); + sho.squeeze_unchecked(&mut got[..63]); + assert_eq!(&got[..63], expected); +} diff --git a/src/plugins/arkworks/prelude.rs b/src/plugins/arkworks/prelude.rs index b98c6c9..965ea0b 100644 --- a/src/plugins/arkworks/prelude.rs +++ b/src/plugins/arkworks/prelude.rs @@ -14,6 +14,10 @@ pub trait ArkSafe { fn squeeze_scalars(&mut self, output: &mut [G::ScalarField]) -> Result<(), InvalidTag>; } +pub trait ArkFieldMerlin { + fn absorb_scalars(&mut self, input: &[F]) -> Result<(), InvalidTag>; +} + pub trait ArkMerlin { fn absorb_scalars(&mut self) -> Result<[G::ScalarField; N], InvalidTag>; fn absorb_points(&mut self) -> Result<[G; N], InvalidTag>; @@ -33,3 +37,8 @@ pub trait ArkArthur { fn squeeze_scalars(&mut self) -> Result<[G::ScalarField; N], InvalidTag>; } + +pub trait ArkArthur { + fn absorb_field(&mut self, input: &[F]) -> Result<(), InvalidTag>; + fn squeeze_field(&mut self, input: &[F]) -> Result<(), InvalidTag>; +} diff --git a/src/plugins/tests.rs b/src/plugins/tests.rs index 906d9f5..6de362f 100644 --- a/src/plugins/tests.rs +++ b/src/plugins/tests.rs @@ -5,7 +5,6 @@ use super::dalek::prelude::*; #[test] fn compatible_scalars() { - type G = ark_curve25519::EdwardsProjective; let ark_scalar = ark_curve25519::Fr::from(0x42); @@ -14,9 +13,10 @@ fn compatible_scalars() { let dalek_scalar = curve25519_dalek::Scalar::from(0x42u8); // let dalek_point = curve25519_dalek::constants::ED25519_BASEPOINT_POINT * dalek_scalar; - let ark_io = AlgebraicIOPattern::::new("ark-dalek") - .absorb_scalars(1, "scalar") - .squeeze(16, "challenge"); + let ark_io = + AlgebraicIOPattern::::new("ark-dalek") + .absorb_scalars(1, "scalar") + .squeeze(16, "challenge"); let dalek_io = IOPattern::::new("ark-dalek"); let dalek_io = DalekIOPattern::absorb_scalars(dalek_io, 1, "scalar"); let dalek_io = dalek_io.squeeze(16, "challenge"); diff --git a/src/safe.rs b/src/safe.rs index 869f5e9..591135f 100644 --- a/src/safe.rs +++ b/src/safe.rs @@ -123,10 +123,7 @@ impl, U: Unit> IOPattern { let mut stack = VecDeque::new(); // skip the domain separator - for part in io_pattern - .split(|&b| b == SEP_BYTE.as_bytes()[0]) - .skip(1) - { + for part in io_pattern.split(|&b| b == SEP_BYTE.as_bytes()[0]).skip(1) { let next_id = part[0] as char; let next_length = part[1..] .iter()