diff --git a/blake2/src/lib.rs b/blake2/src/lib.rs index d2ce5524f..9a205229a 100644 --- a/blake2/src/lib.rs +++ b/blake2/src/lib.rs @@ -16,10 +16,13 @@ use digest::{ AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, OutputSizeUser, TruncSide, UpdateCore, VariableOutputCore, VariableOutputCoreCustomized, }, - block_buffer::{Lazy, LazyBuffer}, - common::{InvalidLength, Key, KeyInit, KeySizeUser}, - consts::{U4, U16, U32, U64, U128}, - typenum::{IsLessOrEqual, True, Unsigned}, + block_buffer::{Lazy, LazyBuffer, SerializedBufferSize}, + common::{ + InvalidLength, Key, KeyInit, KeySizeUser, + hazmat::{DeserializeStateError, SerializableState, SerializedState}, + }, + consts::{U4, U8, U16, U32, U64, U128}, + typenum::{IsLessOrEqual, Sum, True, Unsigned}, }; #[cfg(feature = "reset")] use digest::{FixedOutputReset, Reset}; @@ -56,7 +59,6 @@ blake2_impl!( digest::buffer_ct_variable!( /// BLAKE2b generic over output size. pub struct Blake2b(Blake2bVarCore); - exclude: SerializableState; max_size: U64; ); @@ -104,7 +106,6 @@ blake2_impl!( digest::buffer_ct_variable!( /// BLAKE2s generic over output size. pub struct Blake2s(Blake2sVarCore); - exclude: SerializableState; max_size: U32; ); diff --git a/blake2/src/macros.rs b/blake2/src/macros.rs index ea0f26532..2e2bb2ad9 100644 --- a/blake2/src/macros.rs +++ b/blake2/src/macros.rs @@ -267,6 +267,68 @@ macro_rules! blake2_impl { #[cfg(feature = "zeroize")] impl ZeroizeOnDrop for $name {} + + impl SerializableState for $name { + // Note: using `$bytes` here instead of + // `Prod<<$vec as SerializableState>::SerializedStateSize, U2>` because the latter + // would require $vec to be a public type (but e.g. u64x4 is crate-private). + #[cfg(not(feature = "reset"))] + type SerializedStateSize = Sum<$bytes, U8>; + #[cfg(feature = "reset")] + type SerializedStateSize = Sum, $bytes>; + + fn serialize(&self) -> SerializedState { + let mut serialized_state = SerializedState::::default(); + let vecsz = <$vec as SerializableState>::SerializedStateSize::USIZE; + let tsz = U8::USIZE; + serialized_state[..vecsz].copy_from_slice(&self.h[0].serialize()); + serialized_state[vecsz..(2 * vecsz)].copy_from_slice(&self.h[1].serialize()); + serialized_state[(2 * vecsz)..(2 * vecsz + tsz)] + .copy_from_slice(&self.t.serialize()); + #[cfg(feature = "reset")] + serialized_state[(2 * vecsz + tsz)..(3 * vecsz + tsz)] + .copy_from_slice(&self.h0[0].serialize()); + #[cfg(feature = "reset")] + serialized_state[(3 * vecsz + tsz)..(4 * vecsz + tsz)] + .copy_from_slice(&self.h0[1].serialize()); + serialized_state + } + + fn deserialize( + serialized_state: &SerializedState, + ) -> Result { + let vecsz = <$vec as SerializableState>::SerializedStateSize::USIZE; + let tsz = U8::USIZE; + let h_0 = + SerializableState::deserialize(&serialized_state[..vecsz].try_into().unwrap())?; + let h_1 = SerializableState::deserialize( + &serialized_state[vecsz..(2 * vecsz)].try_into().unwrap(), + )?; + let t = SerializableState::deserialize( + &serialized_state[(2 * vecsz)..(2 * vecsz + tsz)] + .try_into() + .unwrap(), + )?; + #[cfg(feature = "reset")] + let h0_0 = SerializableState::deserialize( + &serialized_state[(2 * vecsz + tsz)..(3 * vecsz + tsz)] + .try_into() + .unwrap(), + )?; + #[cfg(feature = "reset")] + let h0_1 = SerializableState::deserialize( + &serialized_state[(3 * vecsz + tsz)..(4 * vecsz + tsz)] + .try_into() + .unwrap(), + )?; + Ok(Self { + h: [h_0, h_1], + t, + #[cfg(feature = "reset")] + h0: [h0_0, h0_1], + }) + } + } }; } @@ -471,5 +533,100 @@ macro_rules! blake2_mac_impl { OutSize: ArraySize + IsLessOrEqual<$max_size, Output = True> { } + + impl SerializableState for $name + where + OutSize: ArraySize + IsLessOrEqual<$max_size, Output = True>, + { + #[cfg(not(feature = "reset"))] + type SerializedStateSize = Sum< + <$hash as SerializableState>::SerializedStateSize, + SerializedBufferSize<<$hash as BlockSizeUser>::BlockSize, Lazy>, + >; + #[cfg(feature = "reset")] + type SerializedStateSize = Sum< + Sum< + <$hash as SerializableState>::SerializedStateSize, + SerializedBufferSize<<$hash as BlockSizeUser>::BlockSize, Lazy>, + >, + Sum<::KeySize, digest::consts::U1>, + >; + + fn serialize(&self) -> SerializedState { + let mut serialized_state = SerializedState::::default(); + let coresz = <$hash as SerializableState>::SerializedStateSize::USIZE; + let buffersz = + ::BlockSize, Lazy>>::USIZE; + + serialized_state[..coresz].copy_from_slice(&self.core.serialize()); + serialized_state[coresz..(coresz + buffersz)] + .copy_from_slice(&self.buffer.serialize()); + + #[cfg(feature = "reset")] + { + let boolsz = digest::consts::U1::USIZE; + let keyblocksz = ::KeySize::USIZE; + if let Some(key_block) = self.key_block { + serialized_state[(coresz + buffersz)..(coresz + buffersz + boolsz)] + .copy_from_slice(&1u8.serialize()); + serialized_state[(coresz + buffersz + boolsz) + ..(coresz + buffersz + boolsz + keyblocksz)] + .copy_from_slice(&key_block); + } else { + serialized_state[(coresz + buffersz)..(coresz + buffersz + boolsz)] + .copy_from_slice(&0u8.serialize()); + } + } + + serialized_state + } + + fn deserialize( + serialized_state: &SerializedState, + ) -> Result { + let coresz = <$hash as SerializableState>::SerializedStateSize::USIZE; + let buffersz = + ::BlockSize, Lazy>>::USIZE; + + let core = SerializableState::deserialize( + &serialized_state[..coresz].try_into().unwrap(), + )?; + let buffer = LazyBuffer::<<$hash as BlockSizeUser>::BlockSize>::deserialize( + &serialized_state[coresz..(coresz + buffersz)] + .try_into() + .unwrap(), + ) + .map_err(|_| DeserializeStateError)?; + + #[cfg(feature = "reset")] + let key_block = { + let boolsz = digest::consts::U1::USIZE; + let keyblocksz = ::KeySize::USIZE; + let present: u8 = SerializableState::deserialize( + &serialized_state[(coresz + buffersz)..(coresz + buffersz + boolsz)] + .try_into() + .unwrap(), + )?; + if present != 0 { + Some( + serialized_state[(coresz + buffersz + boolsz) + ..(coresz + buffersz + boolsz + keyblocksz)] + .try_into() + .unwrap(), + ) + } else { + None + } + }; + + Ok(Self { + core, + buffer, + #[cfg(feature = "reset")] + key_block, + _out: Default::default(), + }) + } + } }; } diff --git a/blake2/src/simd.rs b/blake2/src/simd.rs index f68f02aa6..99d3d3bbc 100644 --- a/blake2/src/simd.rs +++ b/blake2/src/simd.rs @@ -1,5 +1,7 @@ use core::ops::{Add, BitXor, Shl, Shr}; +use digest::common::hazmat::{DeserializeStateError, SerializableState, SerializedState}; + #[cfg(feature = "zeroize")] use digest::zeroize::Zeroize; @@ -181,6 +183,27 @@ macro_rules! impl_vector4 { self.3.zeroize(); } } + + impl SerializableState for $vec { + type SerializedStateSize = <[$word; 4] as SerializableState>::SerializedStateSize; + + fn serialize(&self) -> SerializedState { + [self.0, self.1, self.2, self.3].serialize() + } + + fn deserialize( + serialized_state: &SerializedState, + ) -> Result { + let deserialized: [$word; 4] = + <[$word; 4] as SerializableState>::deserialize(serialized_state)?; + Ok($vec( + deserialized[0], + deserialized[1], + deserialized[2], + deserialized[3], + )) + } + } }; } diff --git a/blake2/tests/data/blake2b_128_reset_serialization.bin b/blake2/tests/data/blake2b_128_reset_serialization.bin new file mode 100644 index 000000000..6576b795e Binary files /dev/null and b/blake2/tests/data/blake2b_128_reset_serialization.bin differ diff --git a/blake2/tests/data/blake2b_128_serialization.bin b/blake2/tests/data/blake2b_128_serialization.bin new file mode 100644 index 000000000..000c78a53 Binary files /dev/null and b/blake2/tests/data/blake2b_128_serialization.bin differ diff --git a/blake2/tests/data/blake2b_256_reset_serialization.bin b/blake2/tests/data/blake2b_256_reset_serialization.bin new file mode 100644 index 000000000..95172ad15 Binary files /dev/null and b/blake2/tests/data/blake2b_256_reset_serialization.bin differ diff --git a/blake2/tests/data/blake2b_256_serialization.bin b/blake2/tests/data/blake2b_256_serialization.bin new file mode 100644 index 000000000..51cc00361 Binary files /dev/null and b/blake2/tests/data/blake2b_256_serialization.bin differ diff --git a/blake2/tests/data/blake2b_512_reset_serialization.bin b/blake2/tests/data/blake2b_512_reset_serialization.bin new file mode 100644 index 000000000..329c004b9 Binary files /dev/null and b/blake2/tests/data/blake2b_512_reset_serialization.bin differ diff --git a/blake2/tests/data/blake2b_512_serialization.bin b/blake2/tests/data/blake2b_512_serialization.bin new file mode 100644 index 000000000..8f0c8c5a7 Binary files /dev/null and b/blake2/tests/data/blake2b_512_serialization.bin differ diff --git a/blake2/tests/data/blake2b_mac_512_reset_serialization.bin b/blake2/tests/data/blake2b_mac_512_reset_serialization.bin new file mode 100644 index 000000000..203369b44 Binary files /dev/null and b/blake2/tests/data/blake2b_mac_512_reset_serialization.bin differ diff --git a/blake2/tests/data/blake2b_mac_512_serialization.bin b/blake2/tests/data/blake2b_mac_512_serialization.bin new file mode 100644 index 000000000..1fcb813d3 Binary files /dev/null and b/blake2/tests/data/blake2b_mac_512_serialization.bin differ diff --git a/blake2/tests/data/blake2s_128_reset_serialization.bin b/blake2/tests/data/blake2s_128_reset_serialization.bin new file mode 100644 index 000000000..de74eeb15 Binary files /dev/null and b/blake2/tests/data/blake2s_128_reset_serialization.bin differ diff --git a/blake2/tests/data/blake2s_128_serialization.bin b/blake2/tests/data/blake2s_128_serialization.bin new file mode 100644 index 000000000..d70a85f5c Binary files /dev/null and b/blake2/tests/data/blake2s_128_serialization.bin differ diff --git a/blake2/tests/data/blake2s_256_reset_serialization.bin b/blake2/tests/data/blake2s_256_reset_serialization.bin new file mode 100644 index 000000000..6d6acabec Binary files /dev/null and b/blake2/tests/data/blake2s_256_reset_serialization.bin differ diff --git a/blake2/tests/data/blake2s_256_serialization.bin b/blake2/tests/data/blake2s_256_serialization.bin new file mode 100644 index 000000000..8fba7ef19 Binary files /dev/null and b/blake2/tests/data/blake2s_256_serialization.bin differ diff --git a/blake2/tests/data/blake2s_mac_256_reset_serialization.bin b/blake2/tests/data/blake2s_mac_256_reset_serialization.bin new file mode 100644 index 000000000..a3e8a66e7 Binary files /dev/null and b/blake2/tests/data/blake2s_mac_256_reset_serialization.bin differ diff --git a/blake2/tests/data/blake2s_mac_256_serialization.bin b/blake2/tests/data/blake2s_mac_256_serialization.bin new file mode 100644 index 000000000..d884eccc2 Binary files /dev/null and b/blake2/tests/data/blake2s_mac_256_serialization.bin differ diff --git a/blake2/tests/serializable.rs b/blake2/tests/serializable.rs new file mode 100644 index 000000000..926aa2666 --- /dev/null +++ b/blake2/tests/serializable.rs @@ -0,0 +1,150 @@ +#[cfg(not(feature = "reset"))] +mod serialization_tests { + use crate::hash_mac_serialization_test; + use digest::hash_serialization_test; + + hash_serialization_test!(blake2b_128_serialization, blake2::Blake2b128); + hash_serialization_test!(blake2b_256_serialization, blake2::Blake2b256); + hash_serialization_test!(blake2b_512_serialization, blake2::Blake2b512); + hash_mac_serialization_test!(blake2b_mac_512_serialization, blake2::Blake2bMac512, 64); + hash_serialization_test!(blake2s_128_serialization, blake2::Blake2s128); + hash_serialization_test!(blake2s_256_serialization, blake2::Blake2s256); + hash_mac_serialization_test!(blake2s_mac_256_serialization, blake2::Blake2sMac256, 32); +} + +#[cfg(feature = "reset")] +mod serialization_tests { + use crate::hash_mac_serialization_test; + use digest::hash_serialization_test; + + hash_serialization_test!(blake2b_128_reset_serialization, blake2::Blake2b128); + hash_serialization_test!(blake2b_256_reset_serialization, blake2::Blake2b256); + hash_serialization_test!(blake2b_512_reset_serialization, blake2::Blake2b512); + hash_mac_serialization_test!( + blake2b_mac_512_reset_serialization, + blake2::Blake2bMac512, + 64 + ); + hash_serialization_test!(blake2s_128_reset_serialization, blake2::Blake2s128); + hash_serialization_test!(blake2s_256_reset_serialization, blake2::Blake2s256); + hash_mac_serialization_test!( + blake2s_mac_256_reset_serialization, + blake2::Blake2sMac256, + 32 + ); +} + +#[macro_export] +macro_rules! hash_mac_serialization_test { + ($name:ident, $hasher:ty, $keysize:literal $(,)?) => { + #[test] + fn $name() { + use blake2::digest::KeyInit; + use digest::{Mac, array::Array, common::hazmat::SerializableState, typenum::Unsigned}; + + let mut h = <$hasher>::new(&Array::try_from(&[0x42u8; $keysize] as &[u8]).unwrap()); + + // in absence of other sizes we can use as reference (BlockSizeUser can't be accessed + // for blake2 Mac hashers), use the state size + digest::Update::update( + &mut h, + &[0x13; <$hasher as SerializableState>::SerializedStateSize::USIZE + 1], + ); + + let serialized_state = h.serialize(); + let expected = include_bytes!(concat!("data/", stringify!($name), ".bin")); + assert_eq!(serialized_state.as_slice(), expected); + + let mut h = <$hasher>::deserialize(&serialized_state).unwrap(); + + digest::Update::update( + &mut h, + &[0x13; <$hasher as SerializableState>::SerializedStateSize::USIZE + 1], + ); + let output1 = h.finalize(); + + let mut h = <$hasher>::new(&Array::try_from(&[0x42u8; $keysize] as &[u8]).unwrap()); + digest::Update::update( + &mut h, + &[0x13; 2 * (<$hasher as SerializableState>::SerializedStateSize::USIZE + 1)], + ); + let output2 = h.finalize(); + + assert_eq!(output1, output2); + } + }; +} + +macro_rules! gen_test_file { + ($name:ident) => {{ + use blake2::digest::typenum::Unsigned; + use digest::Digest; + use digest::common::{BlockSizeUser, hazmat::SerializableState}; + + let mut a = blake2::$name::new(); + digest::Update::update( + &mut a, + &[0x13; ::BlockSize::USIZE + 1], + ); + let serialized = a.serialize(); + let name = stringify!($name); + std::fs::write( + format!( + "./tests/data/blake2{}_{}{}_serialization.bin", + &name[6..7], + &name[(name.len() - 3)..], + (if cfg!(feature = "reset") { + "_reset" + } else { + "" + }) + ), + serialized, + ) + .unwrap(); + }}; +} + +macro_rules! gen_test_file_mac { + ($name:ident, $keysize:literal) => {{ + use blake2::digest::{KeyInit, typenum::Unsigned}; + use digest::array::Array; + use digest::common::hazmat::SerializableState; + + let mut a = blake2::$name::new(&Array::try_from(&[0x42u8; $keysize] as &[u8]).unwrap()); + digest::Update::update( + &mut a, + &[0x13; ::SerializedStateSize::USIZE + 1], + ); + let serialized = a.serialize(); + let name = stringify!($name); + std::fs::write( + format!( + "./tests/data/blake2{}_mac_{}{}_serialization.bin", + &name[6..7], + &name[(name.len() - 3)..], + (if cfg!(feature = "reset") { + "_reset" + } else { + "" + }) + ), + serialized, + ) + .unwrap(); + }}; +} + +/// Generates the data files used in the tests above. Remove #[ignore] to regenerate the data. +/// Needs to be executed twice, with feature="reset" enabled and disabled. +#[test] +#[ignore] +fn gen_test_files() { + gen_test_file!(Blake2b128); + gen_test_file!(Blake2b256); + gen_test_file!(Blake2b512); + gen_test_file_mac!(Blake2bMac512, 64); + gen_test_file!(Blake2s128); + gen_test_file!(Blake2s256); + gen_test_file_mac!(Blake2sMac256, 32); +}