diff --git a/Cargo.lock b/Cargo.lock index 6d42be8f..5bf6e521 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -96,8 +96,7 @@ dependencies = [ [[package]] name = "digest" version = "0.11.0-pre.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "065d93ead7c220b85d5b4be4795d8398eac4ff68b5ee63895de0a3c1fb6edf25" +source = "git+https://github.com/RustCrypto/traits.git#7c7a0d2a2caa60e286835051e6ad5fd10a9a9554" dependencies = [ "blobby", "block-buffer", @@ -157,9 +156,9 @@ checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" [[package]] name = "hybrid-array" -version = "0.2.0-rc.4" +version = "0.2.0-rc.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18e63b66aee2df5599ba69b17a48113dfc68d2e143ea387ef836509e433bbd7e" +checksum = "53668f5da5a41d9eaf4bf7064be46d1ebe6a4e1ceed817f387587b18f2b51047" dependencies = [ "typenum", ] diff --git a/Cargo.toml b/Cargo.toml index 26637761..f3a9df35 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,3 +26,7 @@ members = [ [profile.dev] opt-level = 2 + +[patch.crates-io] +# https://github.com/RustCrypto/traits/pull/1537 - Unreleased +digest = { git = "https://github.com/RustCrypto/traits.git" } diff --git a/blake2/tests/mac.rs b/blake2/tests/mac.rs index 75710284..7a166a04 100644 --- a/blake2/tests/mac.rs +++ b/blake2/tests/mac.rs @@ -12,7 +12,7 @@ fn blake2b_new_test() { fn run(key: &[u8]) { const DATA: &[u8] = &[42; 300]; - let res1 = T::new(Array::from_slice(key)) + let res1 = T::new(&Array::try_from(key).unwrap()) .chain_update(DATA) .finalize() .into_bytes(); diff --git a/fsb/src/lib.rs b/fsb/src/lib.rs index 141468fd..8bf52722 100644 --- a/fsb/src/lib.rs +++ b/fsb/src/lib.rs @@ -12,7 +12,7 @@ #[macro_use] mod macros; -use core::fmt; +use core::{fmt, ops::Add}; pub use digest::{self, Digest}; // Double check this contains all values in the reference implementation @@ -24,7 +24,8 @@ use digest::{ AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, CoreWrapper, FixedOutputCore, OutputSizeUser, Reset, UpdateCore, }, - typenum::Unsigned, + crypto_common::hazmat::{DeserializeStateError, SerializableState, SerializedState}, + typenum::{Unsigned, U8}, HashMarker, Output, }; @@ -38,6 +39,7 @@ fsb_impl!( 160, U60, U20, + U80, 5 << 18, 80, 640, @@ -54,6 +56,7 @@ fsb_impl!( 224, U84, U28, + U112, 7 << 18, 112, 896, @@ -70,6 +73,7 @@ fsb_impl!( 256, U96, U32, + U128, 1 << 21, 128, 1024, @@ -86,6 +90,7 @@ fsb_impl!( 384, U115, U48, + U184, 23 << 16, 184, 1472, @@ -102,6 +107,7 @@ fsb_impl!( 512, U155, U64, + U248, 31 << 16, 248, 1984, diff --git a/fsb/src/macros.rs b/fsb/src/macros.rs index 2204b747..59845f79 100644 --- a/fsb/src/macros.rs +++ b/fsb/src/macros.rs @@ -1,9 +1,9 @@ macro_rules! fsb_impl { ( - $full_state:ident, $state:ident, $state_num:expr, $blocksize:ident, $outputsize:ident, $n:expr, $w:expr, - $r:expr, $p:expr, $s:expr, $full_doc:expr, $doc:expr, + $full_state:ident, $state:ident, $state_num:expr, $blocksize:ident, $outputsize:ident, $statesize:ident, + $n:expr, $w:expr, $r:expr, $p:expr, $s:expr, $full_doc:expr, $doc:expr, ) => { - use digest::consts::{$blocksize, $outputsize}; + use digest::consts::{$blocksize, $outputsize, $statesize}; #[derive(Clone)] #[doc=$doc] @@ -95,6 +95,33 @@ macro_rules! fsb_impl { #[cfg(feature = "zeroize")] impl ZeroizeOnDrop for $state {} + impl SerializableState for $state { + type SerializedStateSize = <$statesize as Add>::Output; + + fn serialize(&self) -> SerializedState { + let mut serialized_state = SerializedState::::default(); + + serialized_state[..self.state.len()].copy_from_slice(&self.state[..]); + serialized_state[self.state.len()..] + .copy_from_slice(&self.blocks_len.to_le_bytes()); + + serialized_state + } + + fn deserialize( + serialized_state: &SerializedState, + ) -> Result { + let (serialized_state, serialized_block_len) = + serialized_state.split::<$statesize>(); + + let mut state = [0; $r / 8]; + state.copy_from_slice(serialized_state.as_ref()); + + let blocks_len = u64::from_le_bytes(*serialized_block_len.as_ref()); + Ok(Self { state, blocks_len }) + } + } + impl $state { const SIZE_OUTPUT_COMPRESS: usize = $r / 8; const SIZE_INPUT_COMPRESS: usize = $s / 8; diff --git a/fsb/tests/mod.rs b/fsb/tests/mod.rs index 6a9bddea..4a967c85 100644 --- a/fsb/tests/mod.rs +++ b/fsb/tests/mod.rs @@ -1,5 +1,7 @@ -use digest::dev::{feed_rand_16mib, fixed_reset_test}; -use digest::new_test; +use digest::{ + dev::{feed_rand_16mib, fixed_reset_test}, + hash_serialization_test, new_test, +}; use fsb::{Digest, Fsb160, Fsb224, Fsb256, Fsb384, Fsb512}; use hex_literal::hex; @@ -9,6 +11,129 @@ new_test!(fsb256_main, "fsb256", Fsb256, fixed_reset_test); new_test!(fsb384_main, "fsb384", Fsb384, fixed_reset_test); new_test!(fsb512_main, "fsb512", Fsb512, fixed_reset_test); +#[rustfmt::skip] +hash_serialization_test!( + fsb160_serialization, + Fsb160, + hex!(" + e269a086505e9493fa92ed509f6cdce8 + 51dd58654160a8c8a499a953a479c169 + d46c0576d8e7b262341087f58eb3dc9d + 3002451f8f0d484cbdc8b342afef13e5 + 4f2fce12e400eca0a6bc0b8837f999c3 + 01000000000000000113000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 0000000000 + ") +); +#[rustfmt::skip] +hash_serialization_test!( + fsb224_serialization, + Fsb224, + hex!(" + bfba3bbd79050b4428d239ec4eb25277 + b228898bd26c04ccf11e052944e72b61 + aae3f1a0a6cdb862d87fac21fefb1dc1 + 4074cfc45d8994087dc70d1d5308b6b1 + f68f6eea5d886904dfcab198e62f6c97 + 67ae365fc648b1bb7d00f65ff276373a + 7a1b4d80efdd7af5fce3b0e93371172a + 01000000000000000113000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000 + ") +); + +#[rustfmt::skip] +hash_serialization_test!( + fsb256_serialization, + Fsb256, + hex!(" + 6c4fef5401baa1825e74fe2a150dd746 + 55ba10d8fa2db4ee3e6925de2cf4a83a + 5121e2ded528f92613ec858045c1bdd1 + 5a11ce8bd4df1a3f409dfc9d1025d333 + 360f30a342f417018fcf0ff1c5dddb04 + 2a18453d707d27721e57fd182d932945 + 89a1c3ef007e6bb3b59f2a361094e21d + 6c72d213545a6612a2adc547968a03e9 + 01000000000000000113000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 000000000000000000 + ") +); + +#[rustfmt::skip] +hash_serialization_test!( + fsb384_serialization, + Fsb384, + hex!(" + 41825b73ae6b5cdc91b8b70723dc1f92 + 97fec62f09c17c75a2326e3d7664efb5 + df1104db5c711016d161187f3174ef77 + f5e0545c917d01375537d15cf90c838d + 2f5fd5a294c7012d80a0f6a6240f90f9 + a6976812ec60fdd35bbc1a064287308e + 1d916cb4d59c02b19ab2d20e1b9b2acb + e826c4d0022db322e3314fe0cf232cdd + 75b61c653bf30569ca76dd11bd032d03 + bc83a0e59964eb5dd77a22d0a459de63 + ab5ff6ce1207a9daed690c36399f7306 + 43a1628e0f33650a0100000000000000 + 01130000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000 + ") +); + +#[rustfmt::skip] +hash_serialization_test!( + fsb512_serialization, + Fsb512, + hex!(" + 4feff733b532b0767d0bbe8804f60ebc + bbf33aa6796e608d37e6e24dcf216636 + 31312286c6efa794b237f05df2838526 + cb5120291a53566bb784ff32d2ea5464 + 693cd68fc52a37375160c0a4f4b8dae8 + 06703a98720180c4abaa2c195a6ede59 + ed68fc5caae6172003ad9195d7ae7747 + 10d7a0c46772a7216e553a39dbeac282 + fa2848e7038eec7c78f7da35db4cf8ea + d35f2f140ec49203f1d3afe24fe4100a + 9d0cc5fdb1e964ed48fe786e2bfdabe4 + 70c148f65c67c21cc6794b8e1eb90e6a + 39800334a2016e2081f5a458fcd348d8 + 778dc4090066f3906b835a1283c97569 + 4e1dc38fef18dd35d2d4f283d0bc1502 + db72a91871a23bc40100000000000000 + 01130000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 000000000000000000000000 + ") +); + #[test] fn fsb160_rand() { let mut h = Fsb160::new(); diff --git a/gost94/src/gost94_core.rs b/gost94/src/gost94_core.rs index bc8c383a..ff76f2b8 100644 --- a/gost94/src/gost94_core.rs +++ b/gost94/src/gost94_core.rs @@ -1,12 +1,14 @@ #![allow(clippy::many_single_char_names)] use core::fmt; use digest::{ + array::Array, block_buffer::Eager, core_api::{ AlgorithmName, Block as TBlock, BlockSizeUser, Buffer, BufferKindUser, FixedOutputCore, OutputSizeUser, Reset, UpdateCore, }, - typenum::{Unsigned, U32}, + crypto_common::hazmat::{DeserializeStateError, SerializableState, SerializedState}, + typenum::{Unsigned, U32, U96}, HashMarker, Output, }; @@ -290,3 +292,47 @@ impl Drop for Gost94Core

{ #[cfg(feature = "zeroize")] impl ZeroizeOnDrop for Gost94Core

{} + +impl SerializableState for Gost94Core

{ + type SerializedStateSize = U96; + + fn serialize(&self) -> SerializedState { + let serialized_h = Array::<_, U32>::from(self.h); + + let mut serialized_n = Array::<_, U32>::default(); + for (val, chunk) in self.n.iter().zip(serialized_n.chunks_exact_mut(8)) { + chunk.copy_from_slice(&val.to_le_bytes()); + } + + let mut serialized_sigma = Array::<_, U32>::default(); + for (val, chunk) in self.sigma.iter().zip(serialized_sigma.chunks_exact_mut(8)) { + chunk.copy_from_slice(&val.to_le_bytes()); + } + + serialized_h.concat(serialized_n).concat(serialized_sigma) + } + + fn deserialize( + serialized_state: &SerializedState, + ) -> Result { + let (serialized_h, remaining_buffer) = serialized_state.split::(); + + let (serialized_n, serialized_sigma) = remaining_buffer.split::(); + let mut n = [0; 4]; + for (val, chunk) in n.iter_mut().zip(serialized_n.chunks_exact(8)) { + *val = u64::from_le_bytes(chunk.try_into().unwrap()); + } + + let mut sigma = [0; 4]; + for (val, chunk) in sigma.iter_mut().zip(serialized_sigma.chunks_exact(8)) { + *val = u64::from_le_bytes(chunk.try_into().unwrap()); + } + + Ok(Self { + h: serialized_h.into(), + n, + sigma, + _m: core::marker::PhantomData, + }) + } +} diff --git a/gost94/tests/mod.rs b/gost94/tests/mod.rs index a31e4beb..ead5c3f7 100644 --- a/gost94/tests/mod.rs +++ b/gost94/tests/mod.rs @@ -1,8 +1,8 @@ #[cfg(feature = "oid")] use digest::const_oid::{AssociatedOid, ObjectIdentifier}; use digest::dev::{feed_rand_16mib, fixed_reset_test}; -use digest::new_test; -use gost94::{Digest, Gost94CryptoPro, Gost94Test, Gost94UA}; +use digest::{hash_serialization_test, new_test}; +use gost94::{Digest, Gost94CryptoPro, Gost94Test, Gost94UA, Gost94s2015}; use hex_literal::hex; new_test!(gost94_test_main, "test", Gost94Test, fixed_reset_test); @@ -13,6 +13,71 @@ new_test!( fixed_reset_test ); +#[rustfmt::skip] +hash_serialization_test!( + gost94_crypto_pro_serialization, + Gost94CryptoPro, + hex!(" + 51aeb30f746350e15ef31472e3914b1b + 4b9198e0272881ff2401cea8490e5ab2 + 00010000000000000000000000000000 + 00000000000000000000000000000000 + 13131313131313131313131313131313 + 13131313131313131313131313131313 + 01130000000000000000000000000000 + 00000000000000000000000000000000 + 00 + ") +); +#[rustfmt::skip] +hash_serialization_test!( + gost94_test_serialization, + Gost94Test, + hex!(" + 81bba4e852b20165ac12b2151cd38b47 + 821cfd45ad739fb03018021a77750754 + 00010000000000000000000000000000 + 00000000000000000000000000000000 + 13131313131313131313131313131313 + 13131313131313131313131313131313 + 01130000000000000000000000000000 + 00000000000000000000000000000000 + 00 + ") +); +#[rustfmt::skip] +hash_serialization_test!( + gost94_ua_serialization, + Gost94UA, + hex!(" + 7755aa3d77c2026677adf176fd722741 + 742a184862f353ec99b1f7928ff0eaa4 + 00010000000000000000000000000000 + 00000000000000000000000000000000 + 13131313131313131313131313131313 + 13131313131313131313131313131313 + 01130000000000000000000000000000 + 00000000000000000000000000000000 + 00 + ") +); +#[rustfmt::skip] +hash_serialization_test!( + gost94_s_2015_serialization, + Gost94s2015, + hex!(" + d29b34011a22a27037ea42d36a512910 + 913482fdc2349ab02ca1087a50745d5b + 00010000000000000000000000000000 + 00000000000000000000000000000000 + 13131313131313131313131313131313 + 13131313131313131313131313131313 + 01130000000000000000000000000000 + 00000000000000000000000000000000 + 00 + ") +); + #[test] fn gost94_test_rand() { let mut h = Gost94Test::new(); diff --git a/groestl/src/lib.rs b/groestl/src/lib.rs index dc7899d7..df7eacc9 100644 --- a/groestl/src/lib.rs +++ b/groestl/src/lib.rs @@ -10,7 +10,7 @@ pub use digest::{self, Digest}; -use core::fmt; +use core::{convert::TryInto, fmt}; use digest::{ block_buffer::Eager, core_api::{ @@ -18,7 +18,8 @@ use digest::{ CtVariableCoreWrapper, OutputSizeUser, RtVariableCoreWrapper, TruncSide, UpdateCore, VariableOutputCore, }, - typenum::{Unsigned, U128, U28, U32, U48, U64}, + crypto_common::hazmat::{DeserializeStateError, SerializableState, SerializedState}, + typenum::{Unsigned, U128, U136, U28, U32, U48, U64, U72}, HashMarker, InvalidOutputSize, Output, }; @@ -127,6 +128,36 @@ impl Drop for GroestlShortVarCore { } } +impl SerializableState for GroestlShortVarCore { + type SerializedStateSize = U72; + + fn serialize(&self) -> SerializedState { + let mut serialized_state = SerializedState::::default(); + + for (val, chunk) in self.state.iter().zip(serialized_state.chunks_exact_mut(8)) { + chunk.copy_from_slice(&val.to_le_bytes()); + } + + serialized_state[64..].copy_from_slice(&self.blocks_len.to_le_bytes()); + serialized_state + } + + fn deserialize( + serialized_state: &SerializedState, + ) -> Result { + let (serialized_state, serialized_block_len) = serialized_state.split::(); + + let mut state = [0; compress512::COLS]; + for (val, chunk) in state.iter_mut().zip(serialized_state.chunks_exact(8)) { + *val = u64::from_le_bytes(chunk.try_into().unwrap()); + } + + let blocks_len = u64::from_le_bytes(*serialized_block_len.as_ref()); + + Ok(Self { state, blocks_len }) + } +} + #[cfg(feature = "zeroize")] impl ZeroizeOnDrop for GroestlShortVarCore {} @@ -228,5 +259,35 @@ impl Drop for GroestlLongVarCore { } } +impl SerializableState for GroestlLongVarCore { + type SerializedStateSize = U136; + + fn serialize(&self) -> SerializedState { + let mut serialized_state = SerializedState::::default(); + + for (val, chunk) in self.state.iter().zip(serialized_state.chunks_exact_mut(8)) { + chunk.copy_from_slice(&val.to_le_bytes()); + } + + serialized_state[128..].copy_from_slice(&self.blocks_len.to_le_bytes()); + serialized_state + } + + fn deserialize( + serialized_state: &SerializedState, + ) -> Result { + let (serialized_state, serialized_blocks_len) = serialized_state.split::(); + + let mut state = [0; compress1024::COLS]; + for (val, chunk) in state.iter_mut().zip(serialized_state.chunks_exact(8)) { + *val = u64::from_le_bytes(chunk.try_into().unwrap()); + } + + let blocks_len = u64::from_le_bytes(*serialized_blocks_len.as_ref()); + + Ok(Self { state, blocks_len }) + } +} + #[cfg(feature = "zeroize")] impl ZeroizeOnDrop for GroestlLongVarCore {} diff --git a/groestl/tests/mod.rs b/groestl/tests/mod.rs index 6b23220e..b73fd483 100755 --- a/groestl/tests/mod.rs +++ b/groestl/tests/mod.rs @@ -1,6 +1,8 @@ use digest::dev::{feed_rand_16mib, fixed_reset_test}; -use digest::new_test; -use groestl::{Digest, Groestl224, Groestl256, Groestl384, Groestl512}; +use digest::{hash_rt_outsize_serialization_test, hash_serialization_test, new_test}; +use groestl::{ + Digest, Groestl224, Groestl256, Groestl384, Groestl512, GroestlLongVar, GroestlShortVar, +}; use hex_literal::hex; new_test!(groestl_224_main, "groestl224", Groestl224, fixed_reset_test); @@ -8,6 +10,127 @@ new_test!(groestl_256_main, "groestl256", Groestl256, fixed_reset_test); new_test!(groestl_384_main, "groestl384", Groestl384, fixed_reset_test); new_test!(groestl_512_main, "groestl512", Groestl512, fixed_reset_test); +#[rustfmt::skip] +hash_serialization_test!( + groestl_224_serialization, + Groestl224, + hex!(" + 22b4f94ab7689d6434484d06ae89401b + 722a240fb27e61ec2b16bcd4a7356a5b + cbd38299d0bf12e0aed4e157e66f3936 + 2470e38d59247c03b911539fdf1c590f + 01000000000000001c01130000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000 + ") +); +#[rustfmt::skip] +hash_serialization_test!( + groestl_256_serialization, + Groestl256, + hex!(" + d52426ab8dbd5022f8c30afca94d27d8 + 37e2407b311f65ee2926b33d0ea59209 + 0db3463c0f6e272d40ad97ad88a5ffe6 + 444e3bbf31165508937a71b9b5e4c690 + 01000000000000002001130000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000 + ") +); +#[rustfmt::skip] +hash_serialization_test!( + groestl_384_serialization, + Groestl384, + hex!(" + 7b6e411adea4d26a564b6c1717001184 + cb8ca1d57c461635e98ef0d4acc02e00 + cab78a23a12ae339f70e612a4aad386a + 5f8fe9024262e3ef35fda0e0aecf819c + d274889e334b330ce0f116e0c74c5df1 + 1085f942d65089d7e0d6494a83e6bdea + fa03861e95cc5c13e1afb8312332f79d + 70ee8dc1e6bbdb5644bfaa0bfc7e3674 + 01000000000000003001130000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000 + ") +); +#[rustfmt::skip] +hash_serialization_test!( + groestl_512_serialization, + Groestl512, + hex!(" + 503f2a766eed3a15b43ecbf90f840447 + 588fe26f14cc63fd7d79a375c920b776 + e3a443149c735f4161ef31ff8ccb0afb + dba6ce50239411294623568f43e8d337 + 5f236f50e6aad6409661bb5348ef0451 + b6f470a42a63fa3613e7091ab0044014 + e3535f6ece66f3a19ac53d98f60bd896 + 2c879ab89e4990e1a39418d8a94bde45 + 01000000000000004001130000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000 + ") +); +#[rustfmt::skip] +hash_rt_outsize_serialization_test!( + groestl_short_var_serialization, + GroestlShortVar, + hex!(" + d549d951b61b5caa7ad964c6595b4473 + 0161feb244d70c75c562b63374224a2f + 228bffde579c78368b8b507021cdacb9 + a17412ae9027f8f4cb061c9c9d94a77e + 01000000000000000113000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 0000000000000000001f + ") +); +#[rustfmt::skip] +hash_rt_outsize_serialization_test!( + groestl_long_var_serialization, + GroestlLongVar, + hex!(" + d2038487f42a7bc6ac0c172db0aa20a4 + f878e618ffefd63b11517a039b374088 + 2ce6345f0eb746fa8abd6446f4d52d13 + 3395872ae812d0c10a7569c03872eb59 + 22a38a10f240cc6c2b62c60b95461bc6 + 80e0a2e2452561a28edcd59a1ca4bf7f + 7237d1395d84e76a2061218d084d9112 + 9e4ae07a2dc86b2c67e1acc188eceba4 + 01000000000000000113000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 0000000000000000003f + ") +); + #[test] fn groestl224_rand() { let mut h = Groestl224::new(); diff --git a/md2/src/lib.rs b/md2/src/lib.rs index fde870a3..9313baca 100644 --- a/md2/src/lib.rs +++ b/md2/src/lib.rs @@ -12,12 +12,14 @@ pub use digest::{self, Digest}; use core::fmt; use digest::{ + array::Array, block_buffer::Eager, - consts::U16, + consts::{U16, U48, U64}, core_api::{ AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, CoreWrapper, FixedOutputCore, OutputSizeUser, Reset, UpdateCore, }, + crypto_common::hazmat::{DeserializeStateError, SerializableState, SerializedState}, HashMarker, Output, }; @@ -31,13 +33,15 @@ mod consts; /// Core MD2 hasher state. #[derive(Clone)] pub struct Md2Core { - x: [u8; 48], + x: [u8; STATE_LEN], checksum: Block, } /// MD2 hasher state. pub type Md2 = CoreWrapper; +const STATE_LEN: usize = 48; + impl Md2Core { fn compress(&mut self, block: &Block) { // Update state @@ -48,7 +52,7 @@ impl Md2Core { let mut t = 0u8; for j in 0..18u8 { - for k in 0..48 { + for k in 0..STATE_LEN { self.x[k] ^= consts::S[t as usize]; t = self.x[k]; } @@ -106,7 +110,7 @@ impl Default for Md2Core { #[inline] fn default() -> Self { Self { - x: [0; 48], + x: [0; STATE_LEN], checksum: Default::default(), } } @@ -149,3 +153,22 @@ impl Drop for Md2Core { #[cfg(feature = "zeroize")] impl ZeroizeOnDrop for Md2Core {} + +impl SerializableState for Md2Core { + type SerializedStateSize = U64; + + fn serialize(&self) -> SerializedState { + Array::<_, U48>::from(self.x).concat(self.checksum) + } + + fn deserialize( + serialized_state: &SerializedState, + ) -> Result { + let (serialized_x, serialized_checksum) = serialized_state.split::(); + + Ok(Self { + x: *serialized_x.as_ref(), + checksum: serialized_checksum, + }) + } +} diff --git a/md2/tests/mod.rs b/md2/tests/mod.rs index 21e4cc14..0050fbeb 100644 --- a/md2/tests/mod.rs +++ b/md2/tests/mod.rs @@ -10,3 +10,17 @@ fn md2_rand() { feed_rand_16mib(&mut h); assert_eq!(h.finalize(), hex!("f9638c7be725f4d0b5ac342560af1a5b")); } + +#[rustfmt::skip] +digest::hash_serialization_test!( + md2_serialization, + Md2, + hex!(" + 30a6b6fb5560099020a61f1535a51a4b + 228e7e945ef919b6c670486ffde72dd6 + 764728b6ce8222562c9ceae0cbbdaf01 + f3f2ef9fe6ed831b3de51fec14010573 + 01130000000000000000000000000000 + 00 + ") +); diff --git a/md4/src/lib.rs b/md4/src/lib.rs index 04d04749..f41b891a 100644 --- a/md4/src/lib.rs +++ b/md4/src/lib.rs @@ -17,7 +17,8 @@ use digest::{ AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, CoreWrapper, FixedOutputCore, OutputSizeUser, Reset, UpdateCore, }, - typenum::{Unsigned, U16, U64}, + crypto_common::hazmat::{DeserializeStateError, SerializableState, SerializedState}, + typenum::{Unsigned, U16, U24, U64}, HashMarker, Output, }; @@ -40,12 +41,14 @@ const K2: Wu32 = W(0x6ED9_EBA1); #[derive(Clone)] pub struct Md4Core { block_len: W, - state: [Wu32; 4], + state: [Wu32; STATE_LEN], } /// MD4 hasher state pub type Md4 = CoreWrapper; +const STATE_LEN: usize = 4; + impl HashMarker for Md4Core {} impl BlockSizeUser for Md4Core { @@ -134,7 +137,7 @@ impl Drop for Md4Core { #[cfg(feature = "zeroize")] impl ZeroizeOnDrop for Md4Core {} -fn compress(state: &mut [Wu32; 4], input: &Block) { +fn compress(state: &mut [Wu32; STATE_LEN], input: &Block) { fn f(x: Wu32, y: Wu32, z: Wu32) -> Wu32 { z ^ (x & (y ^ z)) } @@ -195,3 +198,33 @@ fn compress(state: &mut [Wu32; 4], input: &Block) { state[2] += c; state[3] += d; } + +impl SerializableState for Md4Core { + type SerializedStateSize = U24; + + fn serialize(&self) -> SerializedState { + let mut serialized_state = SerializedState::::default(); + + for (val, chunk) in self.state.iter().zip(serialized_state.chunks_exact_mut(4)) { + chunk.copy_from_slice(&val.0.to_le_bytes()); + } + + serialized_state[16..].copy_from_slice(&self.block_len.0.to_le_bytes()); + serialized_state + } + + fn deserialize( + serialized_state: &SerializedState, + ) -> Result { + let (serialized_state, serialized_block_len) = serialized_state.split::(); + + let mut state = [W(0); STATE_LEN]; + for (val, chunk) in state.iter_mut().zip(serialized_state.chunks_exact(4)) { + *val = W(u32::from_le_bytes(chunk.try_into().unwrap())); + } + + let block_len = W(u64::from_le_bytes(*serialized_block_len.as_ref())); + + Ok(Self { block_len, state }) + } +} diff --git a/md4/tests/mod.rs b/md4/tests/mod.rs index d7681e2f..63cf5441 100644 --- a/md4/tests/mod.rs +++ b/md4/tests/mod.rs @@ -10,3 +10,17 @@ fn md4_rand() { feed_rand_16mib(&mut h); assert_eq!(h.finalize(), hex!("07345abfb6192d85bf6a211381926120")); } + +#[rustfmt::skip] +digest::hash_serialization_test!( + md4_serialization, + Md4, + hex!(" + b87358479b721dbccae8eefb4831451d + 01000000000000000113000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 000000000000000000 + ") +); diff --git a/md5/src/lib.rs b/md5/src/lib.rs index a4156b02..223db46e 100644 --- a/md5/src/lib.rs +++ b/md5/src/lib.rs @@ -12,7 +12,7 @@ pub use digest::{self, Digest}; mod compress; pub(crate) mod consts; -use core::{fmt, slice::from_ref}; +use core::{convert::TryInto, fmt, slice::from_ref}; use digest::{ array::Array, block_buffer::Eager, @@ -20,7 +20,8 @@ use digest::{ AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, CoreWrapper, FixedOutputCore, OutputSizeUser, Reset, UpdateCore, }, - typenum::{Unsigned, U16, U64}, + crypto_common::hazmat::{DeserializeStateError, SerializableState, SerializedState}, + typenum::{Unsigned, U16, U24, U64}, HashMarker, Output, }; @@ -33,12 +34,14 @@ use digest::zeroize::{Zeroize, ZeroizeOnDrop}; #[derive(Clone)] pub struct Md5Core { block_len: u64, - state: [u32; 4], + state: [u32; STATE_LEN], } /// MD5 hasher state. pub type Md5 = CoreWrapper; +const STATE_LEN: usize = 4; + impl HashMarker for Md5Core {} impl BlockSizeUser for Md5Core { @@ -123,5 +126,35 @@ impl Drop for Md5Core { } } +impl SerializableState for Md5Core { + type SerializedStateSize = U24; + + fn serialize(&self) -> SerializedState { + let mut serialized_state = SerializedState::::default(); + + for (val, chunk) in self.state.iter().zip(serialized_state.chunks_exact_mut(4)) { + chunk.copy_from_slice(&val.to_le_bytes()); + } + + serialized_state[16..].copy_from_slice(&self.block_len.to_le_bytes()); + serialized_state + } + + fn deserialize( + serialized_state: &SerializedState, + ) -> Result { + let (serialized_state, serialized_block_len) = serialized_state.split::(); + + let mut state = [0; STATE_LEN]; + for (val, chunk) in state.iter_mut().zip(serialized_state.chunks_exact(4)) { + *val = u32::from_le_bytes(chunk.try_into().unwrap()); + } + + let block_len = u64::from_le_bytes(*serialized_block_len.as_ref()); + + Ok(Self { state, block_len }) + } +} + #[cfg(feature = "zeroize")] impl ZeroizeOnDrop for Md5Core {} diff --git a/md5/tests/mod.rs b/md5/tests/mod.rs index 175d5060..bec9c19f 100644 --- a/md5/tests/mod.rs +++ b/md5/tests/mod.rs @@ -10,3 +10,17 @@ fn md5_rand() { feed_rand_16mib(&mut h); assert_eq!(h.finalize(), hex!("61aec26f1b909578ef638ae02dac0977")); } + +#[rustfmt::skip] +digest::hash_serialization_test!( + md5_serialization, + Md5, + hex!(" + 9522cae5ddd693db0f99ab079e21d2ca + 01000000000000000113000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 000000000000000000 + ") +); diff --git a/ripemd/src/c128.rs b/ripemd/src/c128.rs index 432613a0..5c1d5919 100644 --- a/ripemd/src/c128.rs +++ b/ripemd/src/c128.rs @@ -1,5 +1,9 @@ +use core::convert::TryInto; +use digest::typenum::U16; + pub const DIGEST_BUF_LEN: usize = 4; pub const WORK_BUF_LEN: usize = 16; +pub type DigestBufByteLen = U16; pub const H0: [u32; DIGEST_BUF_LEN] = [0x6745_2301, 0xefcd_ab89, 0x98ba_dcfe, 0x1032_5476]; diff --git a/ripemd/src/c160.rs b/ripemd/src/c160.rs index f3e14a09..3956900d 100644 --- a/ripemd/src/c160.rs +++ b/ripemd/src/c160.rs @@ -1,5 +1,9 @@ +use core::convert::TryInto; +use digest::typenum::U20; + pub const DIGEST_BUF_LEN: usize = 5; pub const WORK_BUF_LEN: usize = 16; +pub type DigestBufByteLen = U20; pub const H0: [u32; DIGEST_BUF_LEN] = [ 0x6745_2301, diff --git a/ripemd/src/c256.rs b/ripemd/src/c256.rs index cd54e8af..3579c156 100644 --- a/ripemd/src/c256.rs +++ b/ripemd/src/c256.rs @@ -1,8 +1,10 @@ -use core::mem::swap; +use core::{convert::TryInto, mem::swap}; +use digest::typenum::U32; pub const DIGEST_BUF_LEN: usize = 8; pub const HALF_DIGEST_BUF_LEN: usize = DIGEST_BUF_LEN / 2; pub const WORK_BUF_LEN: usize = 16; +pub type DigestBufByteLen = U32; pub const H0: [u32; DIGEST_BUF_LEN] = [ 0x6745_2301, diff --git a/ripemd/src/c320.rs b/ripemd/src/c320.rs index d962f54f..a69e2891 100644 --- a/ripemd/src/c320.rs +++ b/ripemd/src/c320.rs @@ -1,8 +1,10 @@ -use core::mem::swap; +use core::{convert::TryInto, mem::swap}; +use digest::typenum::U40; pub const HALF_DIGEST_BUF_LEN: usize = 5; pub const DIGEST_BUF_LEN: usize = 10; pub const WORK_BUF_LEN: usize = 16; +pub type DigestBufByteLen = U40; pub const H0: [u32; DIGEST_BUF_LEN] = [ 0x6745_2301, diff --git a/ripemd/src/lib.rs b/ripemd/src/lib.rs index 0dfa357b..24687616 100644 --- a/ripemd/src/lib.rs +++ b/ripemd/src/lib.rs @@ -10,14 +10,15 @@ pub use digest::{self, Digest}; -use core::fmt; +use core::{convert::TryInto, fmt}; use digest::{ block_buffer::Eager, core_api::{ AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, CoreWrapper, FixedOutputCore, OutputSizeUser, Reset, UpdateCore, }, - typenum::{Unsigned, U16, U20, U32, U40, U64}, + crypto_common::hazmat::{DeserializeStateError, SerializableState, SerializedState}, + typenum::{Sum, Unsigned, U16, U20, U32, U40, U64, U8}, HashMarker, Output, }; @@ -131,6 +132,38 @@ macro_rules! impl_ripemd { #[cfg(feature = "zeroize")] impl ZeroizeOnDrop for $name {} + + impl SerializableState for $name { + type SerializedStateSize = Sum<$mod::DigestBufByteLen, U8>; + + fn serialize(&self) -> SerializedState { + let mut serialized_h = SerializedState::::default(); + + for (val, chunk) in self.h.iter().zip(serialized_h.chunks_exact_mut(4)) { + chunk.copy_from_slice(&val.to_le_bytes()); + } + + serialized_h[$mod::DigestBufByteLen::USIZE..] + .copy_from_slice(&self.block_len.to_le_bytes()); + serialized_h + } + + fn deserialize( + serialized_state: &SerializedState, + ) -> Result { + let (serialized_h, serialized_block_len) = + serialized_state.split::<$mod::DigestBufByteLen>(); + + let mut h = [0; $mod::DIGEST_BUF_LEN]; + for (val, chunk) in h.iter_mut().zip(serialized_h.chunks_exact(4)) { + *val = u32::from_le_bytes(chunk.try_into().unwrap()); + } + + let block_len = u64::from_le_bytes(*serialized_block_len.as_ref()); + + Ok(Self { h, block_len }) + } + } }; } diff --git a/ripemd/tests/mod.rs b/ripemd/tests/mod.rs index fe2a78e2..3edb3888 100644 --- a/ripemd/tests/mod.rs +++ b/ripemd/tests/mod.rs @@ -1,5 +1,5 @@ use digest::dev::{feed_rand_16mib, fixed_reset_test}; -use digest::new_test; +use digest::{hash_serialization_test, new_test}; use hex_literal::hex; use ripemd::{Digest, Ripemd128, Ripemd160, Ripemd256, Ripemd320}; @@ -11,6 +11,65 @@ new_test!(ripemd160_main, "ripemd160", Ripemd160, fixed_reset_test); new_test!(ripemd256_main, "ripemd256", Ripemd256, fixed_reset_test); new_test!(ripemd320_main, "ripemd320", Ripemd320, fixed_reset_test); +#[rustfmt::skip] +hash_serialization_test!( + ripemd128_serialization, + Ripemd128, + hex!(" + 4be201c3c174ca6a87dbc274d2a372eb + 01000000000000000113000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 000000000000000000 + ") +); + +#[rustfmt::skip] +hash_serialization_test!( + ripemd160_serialization, + Ripemd160, + hex!(" + fe8fe28142dc4785457085ad85ac4a20 + 4127b66d010000000000000001130000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000 + ") +); + +#[rustfmt::skip] +hash_serialization_test!( + ripemd256_serialization, + Ripemd256, + hex!(" + adaa3eee80b1ebe8f5a2363a13ad15c6 + 1cac8a0942a88e8b075d9cdc788dd59e + 01000000000000000113000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 000000000000000000 + ") +); + +#[rustfmt::skip] +hash_serialization_test!( + ripemd320_serialization, + Ripemd320, + hex!(" + b964693501716183244dc2f66453fe04 + 333677dadfc04919f1aa6b2e66684cce + 665028f0f1253e090100000000000000 + 01130000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00 + ") +); + #[test] fn ripemd128_1mil_a() { let mut h = Ripemd128::new(); diff --git a/sha1/src/lib.rs b/sha1/src/lib.rs index 32a9fce9..e77e9bfd 100644 --- a/sha1/src/lib.rs +++ b/sha1/src/lib.rs @@ -9,7 +9,7 @@ pub use digest::{self, Digest}; -use core::{fmt, slice::from_ref}; +use core::{convert::TryInto, fmt, slice::from_ref}; use digest::{ array::Array, block_buffer::Eager, @@ -17,7 +17,8 @@ use digest::{ AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, CoreWrapper, FixedOutputCore, OutputSizeUser, Reset, UpdateCore, }, - typenum::{Unsigned, U20, U64}, + crypto_common::hazmat::{DeserializeStateError, SerializableState, SerializedState}, + typenum::{Unsigned, U20, U28, U64}, HashMarker, Output, }; @@ -127,3 +128,33 @@ impl Drop for Sha1Core { #[cfg(feature = "zeroize")] impl ZeroizeOnDrop for Sha1Core {} + +impl SerializableState for Sha1Core { + type SerializedStateSize = U28; + + fn serialize(&self) -> SerializedState { + let mut serialized_h = SerializedState::::default(); + + for (val, chunk) in self.h.iter().zip(serialized_h.chunks_exact_mut(4)) { + chunk.copy_from_slice(&val.to_le_bytes()); + } + + serialized_h[20..].copy_from_slice(&self.block_len.to_le_bytes()); + serialized_h + } + + fn deserialize( + serialized_state: &SerializedState, + ) -> Result { + let (serialized_h, serialized_block_len) = serialized_state.split::(); + + let mut h = [0; STATE_LEN]; + for (val, chunk) in h.iter_mut().zip(serialized_h.chunks_exact(4)) { + *val = u32::from_le_bytes(chunk.try_into().unwrap()); + } + + let block_len = u64::from_le_bytes(*serialized_block_len.as_ref()); + + Ok(Self { h, block_len }) + } +} diff --git a/sha1/tests/mod.rs b/sha1/tests/mod.rs index 6f26dabc..f5fe1b0a 100644 --- a/sha1/tests/mod.rs +++ b/sha1/tests/mod.rs @@ -13,3 +13,17 @@ fn sha1_rand() { hex!("7e565a25a8b123e9881addbcedcd927b23377a78"), ); } + +#[rustfmt::skip] +digest::hash_serialization_test!( + sha1_serialization, + Sha1, + hex!(" + ad0eca12d0aaa48af970c03f1420a28d + 260b50df010000000000000001130000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000 + ") +); diff --git a/sha2/src/core_api.rs b/sha2/src/core_api.rs index 94ace502..acd23541 100644 --- a/sha2/src/core_api.rs +++ b/sha2/src/core_api.rs @@ -1,5 +1,5 @@ use crate::{consts, sha256::compress256, sha512::compress512}; -use core::{fmt, slice::from_ref}; +use core::{convert::TryInto, fmt, slice::from_ref}; use digest::{ array::Array, block_buffer::Eager, @@ -7,7 +7,8 @@ use digest::{ AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, OutputSizeUser, TruncSide, UpdateCore, VariableOutputCore, }, - typenum::{Unsigned, U128, U32, U64}, + crypto_common::hazmat::{DeserializeStateError, SerializableState, SerializedState}, + typenum::{Unsigned, U128, U32, U40, U64, U80}, HashMarker, InvalidOutputSize, Output, }; @@ -100,6 +101,36 @@ impl Drop for Sha256VarCore { #[cfg(feature = "zeroize")] impl ZeroizeOnDrop for Sha256VarCore {} +impl SerializableState for Sha256VarCore { + type SerializedStateSize = U40; + + fn serialize(&self) -> SerializedState { + let mut serialized_state = SerializedState::::default(); + + for (val, chunk) in self.state.iter().zip(serialized_state.chunks_exact_mut(4)) { + chunk.copy_from_slice(&val.to_le_bytes()); + } + + serialized_state[32..].copy_from_slice(&self.block_len.to_le_bytes()); + serialized_state + } + + fn deserialize( + serialized_state: &SerializedState, + ) -> Result { + let (serialized_state, serialized_block_len) = serialized_state.split::(); + + let mut state = consts::State256::default(); + for (val, chunk) in state.iter_mut().zip(serialized_state.chunks_exact(4)) { + *val = u32::from_le_bytes(chunk.try_into().unwrap()); + } + + let block_len = u64::from_le_bytes(*serialized_block_len.as_ref()); + + Ok(Self { state, block_len }) + } +} + /// Core block-level SHA-512 hasher with variable output size. /// /// Supports initialization only for 28, 32, 48, and 64 byte output sizes, @@ -186,3 +217,34 @@ impl Drop for Sha512VarCore { } #[cfg(feature = "zeroize")] impl ZeroizeOnDrop for Sha512VarCore {} + +impl SerializableState for Sha512VarCore { + type SerializedStateSize = U80; + + fn serialize(&self) -> SerializedState { + let mut serialized_state = SerializedState::::default(); + + for (val, chunk) in self.state.iter().zip(serialized_state.chunks_exact_mut(8)) { + chunk.copy_from_slice(&val.to_le_bytes()); + } + + serialized_state[64..].copy_from_slice(&self.block_len.to_le_bytes()); + + serialized_state + } + + fn deserialize( + serialized_state: &SerializedState, + ) -> Result { + let (serialized_state, serialized_block_len) = serialized_state.split::(); + + let mut state = consts::State512::default(); + for (val, chunk) in state.iter_mut().zip(serialized_state.chunks_exact(8)) { + *val = u64::from_le_bytes(chunk.try_into().unwrap()); + } + + let block_len = u128::from_le_bytes(*serialized_block_len.as_ref()); + + Ok(Self { state, block_len }) + } +} diff --git a/sha2/tests/mod.rs b/sha2/tests/mod.rs index 91104229..e77bf846 100644 --- a/sha2/tests/mod.rs +++ b/sha2/tests/mod.rs @@ -1,14 +1,133 @@ -use digest::dev::{feed_rand_16mib, fixed_reset_test}; -use digest::new_test; +use digest::{ + dev::{feed_rand_16mib, fixed_reset_test}, + hash_serialization_test, new_test, +}; use hex_literal::hex; use sha2::{Digest, Sha224, Sha256, Sha384, Sha512, Sha512_224, Sha512_256}; new_test!(sha224_main, "sha224", Sha224, fixed_reset_test); +#[rustfmt::skip] +hash_serialization_test!( + sha224_serializable_state, + Sha224, + hex!(" + 347517c7066151018ff10a846ceff19c + 34cfae254e3efe284a6a92019582e270 + 01000000000000001c01130000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000 + ") +); + new_test!(sha256_main, "sha256", Sha256, fixed_reset_test); +#[rustfmt::skip] +hash_serialization_test!( + sha256_serializable_state, + Sha256, + hex!(" + ca01b8b161490f9889c64c64f9036cb7 + 3322c40ec8c6a2d7da3ee8877247c253 + 01000000000000002001130000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000 + ") +); + new_test!(sha512_224_main, "sha512_224", Sha512_224, fixed_reset_test); +#[rustfmt::skip] +hash_serialization_test!( + sha512_224_serializable_state, + Sha512_224, + hex!(" + d9c911b44e2fe9546e6de18d6d4d7b40 + 741b68eae6e6ad9d495d8ffbf3dec827 + 66e8ef2f248f79b4a654fd4b04310088 + 07b82e6616da4d4c7dc2fc45965c7381 + 01000000000000000000000000000000 + 1c011300000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 0000 + ") +); + new_test!(sha512_256_main, "sha512_256", Sha512_256, fixed_reset_test); +#[rustfmt::skip] +hash_serialization_test!( + sha512_256_serializable_state, + Sha512_256, + hex!(" + 7a37a8bf68012e540d93b77075bfff38 + af47e388f0be8293b1a2f112b8ca9f5c + 893499ddb32159a444c3c72e3b7194f5 + 59d5625b73328dd3e8159273b77e7a98 + 01000000000000000000000000000000 + 20011300000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 0000 + ") +); + new_test!(sha384_main, "sha384", Sha384, fixed_reset_test); +#[rustfmt::skip] +hash_serialization_test!( + sha384_serializable_state, + Sha384, + hex!(" + 2a5aeaf34b33d4f3a8ba1a13b0ee48e7 + a3378b66f0bca73b4a576e84e7d1db0b + 43494cbd9a6b2e8c80e0703b605b8f82 + 33ded6d375b39f7c18c80d3c0e9c7029 + 01000000000000000000000000000000 + 30011300000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 0000 + ") +); + new_test!(sha512_main, "sha512", Sha512, fixed_reset_test); +#[rustfmt::skip] +hash_serialization_test!( + sha512_serializable_state, + Sha512, + hex!(" + eee700aeb8c1952696e5fb2c0701a728 + 96937bc6a82e054e91e06fe939db2fa7 + e45494b888dc5090d16689284664e865 + 0d06e4c18f4abb177d69d4fec184f8a4 + 01000000000000000000000000000000 + 40011300000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 0000 + ") +); #[test] fn sha256_rand() { diff --git a/sha3/src/lib.rs b/sha3/src/lib.rs index 68c519a0..2db64efa 100644 --- a/sha3/src/lib.rs +++ b/sha3/src/lib.rs @@ -20,6 +20,7 @@ use digest::{ ExtendableOutputCore, FixedOutputCore, OutputSizeUser, Reset, UpdateCore, XofReaderCore, XofReaderCoreWrapper, }, + crypto_common::hazmat::{DeserializeStateError, SerializableState, SerializedState}, HashMarker, Output, }; diff --git a/sha3/src/macros.rs b/sha3/src/macros.rs index 63970311..a5ee3e43 100644 --- a/sha3/src/macros.rs +++ b/sha3/src/macros.rs @@ -93,6 +93,38 @@ macro_rules! impl_sha3 { #[cfg(feature = "zeroize")] impl ZeroizeOnDrop for $name {} + + impl SerializableState for $name { + type SerializedStateSize = U200; + + fn serialize(&self) -> SerializedState { + let mut serialized_state = SerializedState::::default(); + + for (val, chunk) in self + .state + .state + .iter() + .zip(serialized_state.chunks_exact_mut(8)) + { + chunk.copy_from_slice(&val.to_le_bytes()); + } + + serialized_state + } + + fn deserialize( + serialized_state: &SerializedState, + ) -> Result { + use core::convert::TryInto; + + let mut state = Sha3State::default(); + for (val, chunk) in state.state.iter_mut().zip(serialized_state.chunks_exact(8)) { + *val = u64::from_le_bytes(chunk.try_into().unwrap()); + } + + Ok(Self { state }) + } + } }; ( $name:ident, $full_name:ident, $output_size:ident, diff --git a/sha3/tests/mod.rs b/sha3/tests/mod.rs index 120ff41e..5b3184b0 100644 --- a/sha3/tests/mod.rs +++ b/sha3/tests/mod.rs @@ -1,24 +1,282 @@ #![no_std] use digest::dev::{fixed_reset_test, xof_reset_test}; -use digest::new_test; +use digest::{hash_serialization_test, new_test}; +use hex_literal::hex; + +// new_test values are from https://github.com/kazcw/yellowsun/blob/test-keccak/src/lib.rs#L171 new_test!(keccak_224, "keccak_224", sha3::Keccak224, fixed_reset_test); +#[rustfmt::skip] +hash_serialization_test!( + keccak_224_serialization, + sha3::Keccak224, + hex!(" + 44fd24cba7fce860dc98a80dc7fb7d47 + bf139f3230ded727d6f9ef31809bbf97 + 31d84d0b9c2329188e172173b6bdf162 + 110beca9038378bff2438eb8564d8d72 + e7a82b9bb891918e71ed71bc7b0669d3 + 5cf60adf4a9239ad8da020500b1061b1 + cb60c04da0d36e4c1174776188f10b17 + dbff4ba813efe418a669a64b6637d400 + d245105d585b3e9968db9031e3552d2a + f11c65f70c1ccfebc2b56fcc6cc01fb5 + 172ba2421f7421e615276188334e72fe + 314abd05cf754d381e6e37056fa59208 + 0051cc0117f141730113000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 000000000000000000 + ") +); + new_test!(keccak_256, "keccak_256", sha3::Keccak256, fixed_reset_test); +#[rustfmt::skip] +hash_serialization_test!( + keccak_256_serialization, + sha3::Keccak256, + hex!(" + fc47e7fccd77882a725bb285776a86bb + 894158aff4b1b7b1dac04e5fbf46eb54 + 71d660b46236f53e01fa72ad115bfecc + 5f612b2f2ff5f01bf7dd6b6ad0d3b3c4 + f938a789505134417873fb5c459f7948 + fa79ee740848afb6195c58b160ae1d44 + 4023d4e0308935a8bd27f7254189a3bf + b2c192bcb9a46607e919ef89dfeb7cc2 + 2a4e9b68a597eaf59e337dd807e1abfd + 7e447e24024806d9108ac859e70592c5 + f1262b322d2f66eb79be7011fc093a8e + 77944fd80e7874194cf4139e6dd2db21 + 457ece6d445a2a740113000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00 + ") +); + new_test!(keccak_384, "keccak_384", sha3::Keccak384, fixed_reset_test); +#[rustfmt::skip] +hash_serialization_test!( + keccak_384_serialization, + sha3::Keccak384, + hex!(" + 4c0e2029fe5337ba52489efa9b16fd56 + 444067cb57201b34d41b895f7cd8ed55 + f158738fb7bed087678916c410578be3 + b72d861fc340c2b38226bc7d87198295 + d0b99e50fac033d5ca13f18d67887530 + 7301c500850fa7c7a3cfab39e1207cfa + b0a75522a0c062283ecd6f505d10a057 + 75129005b9badbcf37f64b37d8a97a1e + d6d4637908eaf29862f464c079e4238a + 0f97a0cd0653a2ee5ae43299319e3c13 + 7a96672c6b0fc76eab1982ed7df52181 + 5629f1440977df882d4fff69e5882524 + 0204c9dfaeff9bc50113000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00 + ") +); + new_test!(keccak_512, "keccak_512", sha3::Keccak512, fixed_reset_test); -// tests are from https://github.com/kazcw/yellowsun/blob/test-keccak/src/lib.rs#L171 +#[rustfmt::skip] +hash_serialization_test!( + keccak_512_serialization, + sha3::Keccak512, + hex!(" + e79e949fb144d267375cca9fa29f47f4 + 5faca4ac7d7b70fa3b892587ca64d28f + 1f3b4baf1d7f4192cc29d1656bca631c + 05492ddaaf3b767c63ce45f36d03eead + f4e86127f0221230b964ceb0a97387df + eab91a107657ea46087148ae1bc8bee2 + 9e5a6dc3869a616b68dcadf67ea2745c + 31ffc101f31192ae40dd44ca1025b2ca + 96f9c0a3891b1f870115ef6c15af3bcc + 81958eac548089b535342c542108a510 + 366d728b0daf6030a1ae7f1428d8a322 + 0d3501a6e36eb272b7d59b69bfe7f71f + 4978bbae6553ed8d0113000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00 + ") +); + new_test!( keccak_256_full, "keccak_256_full", sha3::Keccak256Full, fixed_reset_test ); +#[rustfmt::skip] +hash_serialization_test!( + keccak_256_full_serialization, + sha3::Keccak256Full, + hex!(" + fc47e7fccd77882a725bb285776a86bb + 894158aff4b1b7b1dac04e5fbf46eb54 + 71d660b46236f53e01fa72ad115bfecc + 5f612b2f2ff5f01bf7dd6b6ad0d3b3c4 + f938a789505134417873fb5c459f7948 + fa79ee740848afb6195c58b160ae1d44 + 4023d4e0308935a8bd27f7254189a3bf + b2c192bcb9a46607e919ef89dfeb7cc2 + 2a4e9b68a597eaf59e337dd807e1abfd + 7e447e24024806d9108ac859e70592c5 + f1262b322d2f66eb79be7011fc093a8e + 77944fd80e7874194cf4139e6dd2db21 + 457ece6d445a2a740113000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00 + ") +); new_test!(sha3_224, "sha3_224", sha3::Sha3_224, fixed_reset_test); +#[rustfmt::skip] +hash_serialization_test!( + sha3_224_serialization, + sha3::Sha3_224, + hex!(" + 44fd24cba7fce860dc98a80dc7fb7d47 + bf139f3230ded727d6f9ef31809bbf97 + 31d84d0b9c2329188e172173b6bdf162 + 110beca9038378bff2438eb8564d8d72 + e7a82b9bb891918e71ed71bc7b0669d3 + 5cf60adf4a9239ad8da020500b1061b1 + cb60c04da0d36e4c1174776188f10b17 + dbff4ba813efe418a669a64b6637d400 + d245105d585b3e9968db9031e3552d2a + f11c65f70c1ccfebc2b56fcc6cc01fb5 + 172ba2421f7421e615276188334e72fe + 314abd05cf754d381e6e37056fa59208 + 0051cc0117f141730113000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 000000000000000000 + ") +); + new_test!(sha3_256, "sha3_256", sha3::Sha3_256, fixed_reset_test); +#[rustfmt::skip] +hash_serialization_test!( + sha3_256_serialization, + sha3::Sha3_256, + hex!(" + fc47e7fccd77882a725bb285776a86bb + 894158aff4b1b7b1dac04e5fbf46eb54 + 71d660b46236f53e01fa72ad115bfecc + 5f612b2f2ff5f01bf7dd6b6ad0d3b3c4 + f938a789505134417873fb5c459f7948 + fa79ee740848afb6195c58b160ae1d44 + 4023d4e0308935a8bd27f7254189a3bf + b2c192bcb9a46607e919ef89dfeb7cc2 + 2a4e9b68a597eaf59e337dd807e1abfd + 7e447e24024806d9108ac859e70592c5 + f1262b322d2f66eb79be7011fc093a8e + 77944fd80e7874194cf4139e6dd2db21 + 457ece6d445a2a740113000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00 + ") +); + new_test!(sha3_384, "sha3_384", sha3::Sha3_384, fixed_reset_test); +#[rustfmt::skip] +hash_serialization_test!( + sha3_384_serialization, + sha3::Sha3_384, + hex!(" + 4c0e2029fe5337ba52489efa9b16fd56 + 444067cb57201b34d41b895f7cd8ed55 + f158738fb7bed087678916c410578be3 + b72d861fc340c2b38226bc7d87198295 + d0b99e50fac033d5ca13f18d67887530 + 7301c500850fa7c7a3cfab39e1207cfa + b0a75522a0c062283ecd6f505d10a057 + 75129005b9badbcf37f64b37d8a97a1e + d6d4637908eaf29862f464c079e4238a + 0f97a0cd0653a2ee5ae43299319e3c13 + 7a96672c6b0fc76eab1982ed7df52181 + 5629f1440977df882d4fff69e5882524 + 0204c9dfaeff9bc50113000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00 + ") +); + new_test!(sha3_512, "sha3_512", sha3::Sha3_512, fixed_reset_test); +#[rustfmt::skip] +hash_serialization_test!( + sha3_512_serialization, + sha3::Sha3_512, + hex!(" + e79e949fb144d267375cca9fa29f47f4 + 5faca4ac7d7b70fa3b892587ca64d28f + 1f3b4baf1d7f4192cc29d1656bca631c + 05492ddaaf3b767c63ce45f36d03eead + f4e86127f0221230b964ceb0a97387df + eab91a107657ea46087148ae1bc8bee2 + 9e5a6dc3869a616b68dcadf67ea2745c + 31ffc101f31192ae40dd44ca1025b2ca + 96f9c0a3891b1f870115ef6c15af3bcc + 81958eac548089b535342c542108a510 + 366d728b0daf6030a1ae7f1428d8a322 + 0d3501a6e36eb272b7d59b69bfe7f71f + 4978bbae6553ed8d0113000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00 + ") +); new_test!(shake128, "shake128", sha3::Shake128, xof_reset_test); new_test!(shake256, "shake256", sha3::Shake256, xof_reset_test); diff --git a/shabal/src/core_api.rs b/shabal/src/core_api.rs index 2ee07ec3..a49a7d7e 100644 --- a/shabal/src/core_api.rs +++ b/shabal/src/core_api.rs @@ -3,11 +3,12 @@ use core::{fmt, mem, num::Wrapping}; use digest::{ array::Array, block_buffer::Eager, - consts::U64, + consts::{U184, U48, U64, U8}, core_api::{ AlgorithmName, BlockSizeUser, Buffer, BufferKindUser, OutputSizeUser, TruncSide, UpdateCore, VariableOutputCore, }, + crypto_common::hazmat::{DeserializeStateError, SerializableState, SerializedState}, HashMarker, InvalidOutputSize, Output, }; @@ -259,3 +260,58 @@ impl Drop for ShabalVarCore { #[cfg(feature = "zeroize")] impl ZeroizeOnDrop for ShabalVarCore {} + +impl SerializableState for ShabalVarCore { + type SerializedStateSize = U184; + + fn serialize(&self) -> SerializedState { + let mut serialized_a = Array::<_, U48>::default(); + for (val, chunk) in self.a.iter().zip(serialized_a.chunks_exact_mut(4)) { + chunk.copy_from_slice(&val.0.to_le_bytes()); + } + + let mut serialized_b = Array::<_, U64>::default(); + for (val, chunk) in self.b.iter().zip(serialized_b.chunks_exact_mut(4)) { + chunk.copy_from_slice(&val.0.to_le_bytes()); + } + + let mut serialized_c = Array::<_, U64>::default(); + for (val, chunk) in self.c.iter().zip(serialized_c.chunks_exact_mut(4)) { + chunk.copy_from_slice(&val.0.to_le_bytes()); + } + + let mut serialized_w = Array::<_, U8>::default(); + serialized_w.copy_from_slice(&self.w.0.to_le_bytes()); + + serialized_a + .concat(serialized_b) + .concat(serialized_c) + .concat(serialized_w) + } + + fn deserialize( + serialized_state: &SerializedState, + ) -> Result { + let (serialized_a, remaining_buffer) = serialized_state.split::(); + let mut a = [Wrapping(0); 12]; + for (val, chunk) in a.iter_mut().zip(serialized_a.chunks_exact(4)) { + *val = Wrapping(u32::from_le_bytes(chunk.try_into().unwrap())); + } + + let (serialized_b, remaining_buffer) = remaining_buffer.split::(); + let mut b = [Wrapping(0); 16]; + for (val, chunk) in b.iter_mut().zip(serialized_b.chunks_exact(4)) { + *val = Wrapping(u32::from_le_bytes(chunk.try_into().unwrap())); + } + + let (serialized_c, serialized_w) = remaining_buffer.split::(); + let mut c = [Wrapping(0); 16]; + for (val, chunk) in c.iter_mut().zip(serialized_c.chunks_exact(4)) { + *val = Wrapping(u32::from_le_bytes(chunk.try_into().unwrap())); + } + + let w = Wrapping(u64::from_le_bytes(*serialized_w.as_ref())); + + Ok(Self { a, b, c, w }) + } +} diff --git a/shabal/tests/mod.rs b/shabal/tests/mod.rs index 08ff3109..27a5a73f 100644 --- a/shabal/tests/mod.rs +++ b/shabal/tests/mod.rs @@ -1,7 +1,7 @@ #![no_std] use digest::dev::{feed_rand_16mib, fixed_reset_test}; -use digest::new_test; +use digest::{hash_serialization_test, new_test}; use hex_literal::hex; use shabal::{Digest, Shabal192, Shabal224, Shabal256, Shabal384, Shabal512}; @@ -11,6 +11,126 @@ new_test!(shabal256_main, "shabal256", Shabal256, fixed_reset_test); new_test!(shabal384_main, "shabal384", Shabal384, fixed_reset_test); new_test!(shabal512_main, "shabal512", Shabal512, fixed_reset_test); +#[rustfmt::skip] +hash_serialization_test!( + shabal192_serialization, + Shabal192, + hex!(" + 5039204617bc74833d85392827f8a4dc + 9ac8eda6c9de1b56f61879157d15deac + 9b8c2170e578116fa9464c2269a06be6 + 65f5414e6218dc7633f9528e4872e06b + 79a2845f8064b30810b49e6c2db35ca3 + 09a43507045a84ddf7d479f5e0da4191 + e158f608380f63990965023f56134aba + f3cf6558c25b0a72e9c247dd2eb57f91 + 6fb115aac5d870af0bf646f4e663cd11 + 7fb0af3bbce91a4a40876cdabe7b95c9 + bfe92dd7ae31110726fb1b33d2183477 + 02000000000000001801130000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000 + ") +); + +#[rustfmt::skip] +hash_serialization_test!( + shabal224_serialization, + Shabal224, + hex!(" + 9d466b087ab69391e8e9c3d92409d6d9 + 5b49b1a1124a021722c149eeff1507ae + 598db0e5fcf9def16798a1841b286ba8 + 8e60a2d16df506393e697d0bea6b6df1 + d2bac527efff9e0340193eef05b8f10e + 1a4c2d868e35a3d2300a985daf12f98d + f298de0343d8a8254dc9ee87705af69d + e57d96abc34fc92e5d8194f3b4438399 + 3bc21d38311f40fb6717051629a8f2b8 + 61d3f46aa9cdb7cf37981781f31aee26 + f3e2686c3a18a94ced2612722e6c03b5 + 02000000000000001c01130000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000 + ") +); + +#[rustfmt::skip] +hash_serialization_test!( + shabal256_serialization, + Shabal256, + hex!(" + b3d27330c9676be3d9b6200924583db5 + bee08342ab935cc068781eac890bd7cc + cf71dfaf4158eb717291c661377f4841 + 1eddf2a0a72b10b1662660a0428acaad + 9b1509b2cea51490544eb24320314eda + 4d8aa275a7bbcf4d783878626c17d570 + 157583a9e4f8ccd3428b70a74d093688 + 121b31571bb00244209b8157f6b10b59 + fa1c06007633ca58d530851c06fe878e + a7c3f69e3d9dc271dc0bb5f2e0d14182 + ea242c80ee16e72c6ca9d3ac0fd76356 + 02000000000000002001130000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000 + ") +); + +#[rustfmt::skip] +hash_serialization_test!( + shabal384_serialization, + Shabal384, + hex!(" + 355cab8881202f5f3a6cdebbf4fa985c + bfa232bb4567c140cc4ede6f47eb9e96 + 3b21275bf636a63cbdbb0b71ad10098a + 079e51e4330e04633a80e3bb8670bfb3 + 4e4cf6eaa1054d4be2d9353d4e1f223e + c9235b5b9afaff4fc388b396f9d67d0b + 28f421695a4bc615fafa20975ddb2ab8 + 4d9f20d8d28824c1bf1551a48aac281a + f10046f41d03f02d5eb1406272a1443b + 2f1b1986f2d98581bb590d3e310bd0bc + 9e43aa352f9129bea46e1c77e3039eda + 02000000000000003001130000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000 + ") +); + +#[rustfmt::skip] +hash_serialization_test!( + shabal512_serialization, + Shabal512, + hex!(" + e569f34e6d01c4b1a10d664ad63b9a4c + 651175399c30ea4cd7a115c6911035cb + c56fdb4940d150277318888b7ca64bde + be55acc63dc4a7459f79ef4246e0216e + c556c1a2af7907815b17782e6d14f2f0 + 74c4f46b7c22813e52c34d2987c484ab + 2121f9815a8dc69b7ac924125646c869 + 9b4141fbad6679751f8c1624a15aebc4 + 27de6fa08d6fdee2c94ae0567c0e5f46 + 578da939e824cf529f094aa979e92354 + 992b0863d7dcc6e911766049be4af617 + 02000000000000004001130000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000 + ") +); + #[test] fn shabal192_rand() { let mut h = Shabal192::new(); diff --git a/sm3/src/consts.rs b/sm3/src/consts.rs index 792bdfbe..7b2a671a 100644 --- a/sm3/src/consts.rs +++ b/sm3/src/consts.rs @@ -11,6 +11,8 @@ pub(crate) const T32: [u32; 64] = [ 0x8a7a879d, 0x14f50f3b, 0x29ea1e76, 0x53d43cec, 0xa7a879d8, 0x4f50f3b1, 0x9ea1e762, 0x3d43cec5, ]; -pub(crate) static H0: [u32; 8] = [ +pub(crate) static H0: [u32; H_LEN] = [ 0x7380166f, 0x4914b2b9, 0x172442d7, 0xda8a0600, 0xa96f30bc, 0x163138aa, 0xe38dee4d, 0xb0fb0e4e, ]; + +pub(crate) const H_LEN: usize = 8; diff --git a/sm3/src/lib.rs b/sm3/src/lib.rs index f0a31a81..7ed965b9 100644 --- a/sm3/src/lib.rs +++ b/sm3/src/lib.rs @@ -10,14 +10,15 @@ pub use digest::{self, Digest}; -use core::{fmt, slice::from_ref}; +use core::{convert::TryInto, fmt, slice::from_ref}; use digest::{ block_buffer::Eager, core_api::{ AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, CoreWrapper, FixedOutputCore, OutputSizeUser, Reset, UpdateCore, }, - typenum::{Unsigned, U32, U64}, + crypto_common::hazmat::{DeserializeStateError, SerializableState, SerializedState}, + typenum::{Unsigned, U32, U40, U64}, HashMarker, Output, }; @@ -33,7 +34,7 @@ use compress::compress; #[derive(Clone)] pub struct Sm3Core { block_len: u64, - h: [u32; 8], + h: [u32; consts::H_LEN], } /// Sm3 hasher state. @@ -116,3 +117,33 @@ impl Drop for Sm3Core { #[cfg(feature = "zeroize")] impl ZeroizeOnDrop for Sm3Core {} + +impl SerializableState for Sm3Core { + type SerializedStateSize = U40; + + fn serialize(&self) -> SerializedState { + let mut serialized_h = SerializedState::::default(); + + for (val, chunk) in self.h.iter().zip(serialized_h.chunks_exact_mut(4)) { + chunk.copy_from_slice(&val.to_le_bytes()); + } + + serialized_h[32..].copy_from_slice(&self.block_len.to_le_bytes()); + serialized_h + } + + fn deserialize( + serialized_state: &SerializedState, + ) -> Result { + let (serialized_h, serialized_block_len) = serialized_state.split::(); + + let mut h = [0; consts::H_LEN]; + for (val, chunk) in h.iter_mut().zip(serialized_h.chunks_exact(4)) { + *val = u32::from_le_bytes(chunk.try_into().unwrap()); + } + + let block_len = u64::from_le_bytes(*serialized_block_len.as_ref()); + + Ok(Self { block_len, h }) + } +} diff --git a/sm3/tests/mod.rs b/sm3/tests/mod.rs index 54c91ab0..2ed80d97 100644 --- a/sm3/tests/mod.rs +++ b/sm3/tests/mod.rs @@ -1,10 +1,25 @@ use digest::dev::{feed_rand_16mib, fixed_reset_test}; -use digest::new_test; +use digest::{hash_serialization_test, new_test}; use hex_literal::hex; use sm3::{Digest, Sm3}; new_test!(sm3_main, "sm3", Sm3, fixed_reset_test); +#[rustfmt::skip] +hash_serialization_test!( + sm3_serialization, + Sm3, + hex!(" + ca87204f0aac075dbfa7088e245ff9f9 + 6e941eb2b5b63e57fdedfa1d2e1f5a27 + 01000000000000000113000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 000000000000000000 + ") +); + #[test] fn sm3_rand() { let mut h = Sm3::new(); diff --git a/streebog/src/core_api.rs b/streebog/src/core_api.rs index 826a8a14..cd581eaa 100644 --- a/streebog/src/core_api.rs +++ b/streebog/src/core_api.rs @@ -1,11 +1,13 @@ use core::fmt; use digest::{ + array::Array, block_buffer::Eager, - consts::U64, + consts::{U192, U64}, core_api::{ AlgorithmName, Block as GenBlock, BlockSizeUser, Buffer, BufferKindUser, OutputSizeUser, TruncSide, UpdateCore, VariableOutputCore, }, + crypto_common::hazmat::{DeserializeStateError, SerializableState, SerializedState}, HashMarker, InvalidOutputSize, Output, }; @@ -179,6 +181,49 @@ impl Drop for StreebogVarCore { #[cfg(feature = "zeroize")] impl ZeroizeOnDrop for StreebogVarCore {} +impl SerializableState for StreebogVarCore { + type SerializedStateSize = U192; + + fn serialize(&self) -> SerializedState { + let serialized_h = Array::<_, U64>::from(self.h); + + let mut serialized_n = Array::<_, U64>::default(); + for (val, chunk) in self.n.iter().zip(serialized_n.chunks_exact_mut(8)) { + chunk.copy_from_slice(&val.to_le_bytes()); + } + + let mut serialized_sigma = Array::<_, U64>::default(); + for (val, chunk) in self.sigma.iter().zip(serialized_sigma.chunks_exact_mut(8)) { + chunk.copy_from_slice(&val.to_le_bytes()); + } + + serialized_h.concat(serialized_n).concat(serialized_sigma) + } + + fn deserialize( + serialized_state: &SerializedState, + ) -> Result { + let (serialized_h, remaining_buffer) = serialized_state.split::(); + + let (serialized_n, serialized_sigma) = remaining_buffer.split::(); + let mut n = [0; 8]; + for (val, chunk) in n.iter_mut().zip(serialized_n.chunks_exact(8)) { + *val = u64::from_le_bytes(chunk.try_into().unwrap()); + } + + let mut sigma = [0; 8]; + for (val, chunk) in sigma.iter_mut().zip(serialized_sigma.chunks_exact(8)) { + *val = u64::from_le_bytes(chunk.try_into().unwrap()); + } + + Ok(Self { + h: serialized_h.into(), + n, + sigma, + }) + } +} + #[inline(always)] fn adc(a: &mut u64, b: u64, carry: &mut u64) { let ret = (*a as u128) + (b as u128) + (*carry as u128); diff --git a/streebog/tests/mod.rs b/streebog/tests/mod.rs index 1cba79d9..ea9dc90e 100644 --- a/streebog/tests/mod.rs +++ b/streebog/tests/mod.rs @@ -1,5 +1,5 @@ use digest::dev::{feed_rand_16mib, fixed_reset_test}; -use digest::new_test; +use digest::{hash_serialization_test, new_test}; use hex_literal::hex; use streebog::{Digest, Streebog256, Streebog512}; @@ -18,6 +18,55 @@ new_test!( fixed_reset_test, ); +#[rustfmt::skip] +hash_serialization_test!( + streebog256_serialization, + Streebog256, + hex!(" + 99898325f3be80da21901d9933ff8b95 + f173fc451da491ff0343ef663bc45fac + 789823fc6dc56b2d34e13ba6e495d641 + 6de8bcb4065353a2d1cbf7c4fcdee6ba + 00020000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 13131313131313131313131313131313 + 13131313131313131313131313131313 + 13131313131313131313131313131313 + 13131313131313131313131313131313 + 20011300000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 0000 + ") +); +#[rustfmt::skip] +hash_serialization_test!( + streebog512_serialization, + Streebog512, + hex!(" + 7a2034d872a6cb930f19285661c55315 + 12dc8528e7ba213196a4f628a38db435 + 6e8706ffda276f9bce4c13421bff6c2c + f9661f638eed08fe091d82f1f6d2dd54 + 00020000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 13131313131313131313131313131313 + 13131313131313131313131313131313 + 13131313131313131313131313131313 + 13131313131313131313131313131313 + 40011300000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 0000 + ") +); + /// Test vectors from: /// https://github.com/gost-engine/engine/blob/master/test/01-digest.t #[test] diff --git a/tiger/src/lib.rs b/tiger/src/lib.rs index c92e3636..5f42675b 100644 --- a/tiger/src/lib.rs +++ b/tiger/src/lib.rs @@ -9,14 +9,15 @@ pub use digest::{self, Digest}; -use core::fmt; +use core::{convert::TryInto, fmt}; use digest::{ block_buffer::Eager, core_api::{ AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, CoreWrapper, FixedOutputCore, OutputSizeUser, Reset, UpdateCore, }, - typenum::{Unsigned, U24, U64}, + crypto_common::hazmat::{DeserializeStateError, SerializableState, SerializedState}, + typenum::{Unsigned, U24, U32, U64}, HashMarker, Output, }; @@ -27,7 +28,8 @@ mod compress; mod tables; use compress::compress; -type State = [u64; 3]; +type State = [u64; STATE_LEN]; +const STATE_LEN: usize = 3; const S0: State = [ 0x0123_4567_89AB_CDEF, 0xFEDC_BA98_7654_3210, @@ -108,6 +110,36 @@ impl Reset for TigerCore { } } +impl SerializableState for TigerCore { + type SerializedStateSize = U32; + + fn serialize(&self) -> SerializedState { + let mut serialized_state = SerializedState::::default(); + + for (val, chunk) in self.state.iter().zip(serialized_state.chunks_exact_mut(8)) { + chunk.copy_from_slice(&val.to_le_bytes()); + } + + serialized_state[24..].copy_from_slice(&self.block_len.to_le_bytes()); + serialized_state + } + + fn deserialize( + serialized_state: &SerializedState, + ) -> Result { + let (serialized_state, serialized_block_len) = serialized_state.split::(); + + let mut state = [0; STATE_LEN]; + for (val, chunk) in state.iter_mut().zip(serialized_state.chunks_exact(8)) { + *val = u64::from_le_bytes(chunk.try_into().unwrap()); + } + + let block_len = u64::from_le_bytes(*serialized_block_len.as_ref()); + + Ok(Self { state, block_len }) + } +} + impl AlgorithmName for TigerCore { #[inline] fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/tiger/tests/mod.rs b/tiger/tests/mod.rs index 6adf7156..54e0d008 100644 --- a/tiger/tests/mod.rs +++ b/tiger/tests/mod.rs @@ -1,11 +1,40 @@ use digest::dev::{feed_rand_16mib, fixed_reset_test}; -use digest::new_test; +use digest::{hash_serialization_test, new_test}; use hex_literal::hex; use tiger::{Digest, Tiger, Tiger2}; new_test!(tiger, "tiger", tiger::Tiger, fixed_reset_test); new_test!(tiger2, "tiger2", tiger::Tiger2, fixed_reset_test); +#[rustfmt::skip] +hash_serialization_test!( + tiger_serialization, + Tiger, + hex!(" + eb0b98618cfb93dd8d0e27b22312c64c + 54528976ae32041f0100000000000000 + 01130000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00 + ") +); +#[rustfmt::skip] +hash_serialization_test!( + tiger2_serialization, + Tiger2, + hex!(" + eb0b98618cfb93dd8d0e27b22312c64c + 54528976ae32041f0100000000000000 + 01130000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00 + ") +); + #[test] fn tiger_rand() { let mut h = Tiger::new(); diff --git a/whirlpool/src/lib.rs b/whirlpool/src/lib.rs index 9911e213..a4d4c9cc 100644 --- a/whirlpool/src/lib.rs +++ b/whirlpool/src/lib.rs @@ -10,7 +10,7 @@ pub use digest::{self, Digest}; -use core::fmt; +use core::{convert::TryInto, fmt}; use digest::{ array::Array, block_buffer::Eager, @@ -18,7 +18,8 @@ use digest::{ AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, CoreWrapper, FixedOutputCore, OutputSizeUser, Reset, UpdateCore, }, - typenum::U64, + crypto_common::hazmat::{DeserializeStateError, SerializableState, SerializedState}, + typenum::{U32, U64, U96}, HashMarker, Output, }; @@ -33,9 +34,11 @@ use compress::compress; /// Core Whirlpool hasher state. #[derive(Clone)] pub struct WhirlpoolCore { - bit_len: [u64; 4], - state: [u64; 8], + bit_len: [u64; BITLEN_LEN], + state: [u64; STATE_LEN], } +const STATE_LEN: usize = 8; +const BITLEN_LEN: usize = 4; /// Whirlpool hasher state. pub type Whirlpool = CoreWrapper; @@ -144,5 +147,46 @@ impl Drop for WhirlpoolCore { } } +impl SerializableState for WhirlpoolCore { + type SerializedStateSize = U96; + + fn serialize(&self) -> SerializedState { + let mut serialized_state = Array::<_, U64>::default(); + + for (val, chunk) in self.state.iter().zip(serialized_state.chunks_exact_mut(8)) { + chunk.copy_from_slice(&val.to_le_bytes()); + } + + let mut serialized_bit_len = Array::<_, U32>::default(); + for (val, chunk) in self + .bit_len + .iter() + .zip(serialized_bit_len.chunks_exact_mut(8)) + { + chunk.copy_from_slice(&val.to_le_bytes()); + } + + serialized_state.concat(serialized_bit_len) + } + + fn deserialize( + serialized_state: &SerializedState, + ) -> Result { + let (serialized_state, serialized_bit_len) = serialized_state.split::(); + + let mut state = [0; STATE_LEN]; + for (val, chunk) in state.iter_mut().zip(serialized_state.chunks_exact(8)) { + *val = u64::from_le_bytes(chunk.try_into().unwrap()); + } + + let mut bit_len = [0; BITLEN_LEN]; + for (val, chunk) in bit_len.iter_mut().zip(serialized_bit_len.chunks_exact(8)) { + *val = u64::from_le_bytes(chunk.try_into().unwrap()); + } + + Ok(Self { state, bit_len }) + } +} + #[cfg(feature = "zeroize")] impl ZeroizeOnDrop for WhirlpoolCore {} diff --git a/whirlpool/tests/mod.rs b/whirlpool/tests/mod.rs index e0980f39..c963a324 100644 --- a/whirlpool/tests/mod.rs +++ b/whirlpool/tests/mod.rs @@ -4,6 +4,25 @@ use whirlpool::{Digest, Whirlpool}; digest::new_test!(whirlpool_main, "whirlpool", Whirlpool, fixed_reset_test); +#[rustfmt::skip] +digest::hash_serialization_test!( + whirlpool_serialization, + Whirlpool, + hex!(" + 44b95aeb60cdf5910f83d556a3382cd8 + 58f03d791dfb7675125d6ede083dc917 + 47be004f1982289c065eb53491e06729 + f5935532c376541ca78e23ed572516a9 + 00000000000000000000000000000000 + 00000000000000000002000000000000 + 01130000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00 + ") +); + #[test] fn whirlpool_rand() { let mut h = Whirlpool::new();