Skip to content

Commit

Permalink
sha2: Implement HashCoreSerializableState for Sha256 and Sha512 cores
Browse files Browse the repository at this point in the history
The internal state of SHA-2 hashes can be used for serialization.
  • Loading branch information
Ruslan Piasetskyi committed Aug 19, 2022
1 parent e5d9356 commit 0ddab78
Show file tree
Hide file tree
Showing 5 changed files with 265 additions and 36 deletions.
55 changes: 37 additions & 18 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions sha2/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ keywords = ["crypto", "sha2", "hash", "digest"]
categories = ["cryptography", "no-std"]

[dependencies]
digest = "0.10.3"
digest = { git = "https://github.com/RustCrypto/traits", rev = "refs/pull/1078/head" } # TODO change back to #digest = "0.10.3"
cfg-if = "1.0"
sha2-asm = { version = "0.6.1", optional = true }

[target.'cfg(any(target_arch = "aarch64", target_arch = "x86_64", target_arch = "x86"))'.dependencies]
cpufeatures = "0.2"

[dev-dependencies]
digest = { version = "0.10.3", features = ["dev"] }
digest = { git = "https://github.com/RustCrypto/traits", rev = "refs/pull/1078/head", features = ["dev"] } # TODO change back to #digest = { version = "0.10.3", features = ["dev"] }
hex-literal = "0.2.2"

[features]
Expand Down
133 changes: 129 additions & 4 deletions sha2/src/core_api.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,37 @@
use crate::{consts, sha256::compress256, sha512::compress512};
use core::{fmt, slice::from_ref};
use core::{
convert::{TryFrom, TryInto},
fmt,
mem::size_of,
slice::from_ref,
};
use digest::{
block_buffer::Eager,
core_api::{
AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, OutputSizeUser, TruncSide,
UpdateCore, VariableOutputCore,
AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, HashCoreSerializableState,
OutputSizeUser, TruncSide, UpdateCore, VariableOutputCore,
},
typenum::{Unsigned, U128, U32, U64},
generic_array::GenericArray,
typenum::{Unsigned, U128, U32, U40, U64, U80},
HashMarker, InvalidOutputSize, Output,
};

#[cfg(feature = "std")]
extern crate std;

/// The error type returned when deserialization of the hash core fails.
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub struct HashCoreStateDeserializationError;

impl fmt::Display for HashCoreStateDeserializationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.write_str("HashCoreStateDeserializationError")
}
}

#[cfg(feature = "std")]
impl std::error::Error for HashCoreStateDeserializationError {}

/// Core block-level SHA-256 hasher with variable output size.
///
/// Supports initialization only for 28 and 32 byte output sizes,
Expand Down Expand Up @@ -82,6 +104,57 @@ impl fmt::Debug for Sha256VarCore {
}
}

impl HashCoreSerializableState for Sha256VarCore {
type SerializedStateSize = U40;
}

impl Sha256VarCore {
const STATE_SIZE: usize = size_of::<consts::State256>();
const STATE_ELEMENT_SIZE: usize = size_of::<u32>();
}

impl TryFrom<GenericArray<u8, U40>> for Sha256VarCore {
type Error = HashCoreStateDeserializationError;

fn try_from(serialized_state: GenericArray<u8, U40>) -> Result<Self, Self::Error> {
let buffer = serialized_state.as_slice();

let mut state = [0; consts::STATE_LEN];
for (i, bytes) in buffer[..Self::STATE_SIZE]
.as_ref()
.chunks(Self::STATE_ELEMENT_SIZE)
.enumerate()
{
state[i] = u32::from_be_bytes(bytes.try_into().unwrap());
}

let block_len = u64::from_be_bytes(buffer[Self::STATE_SIZE..].as_ref().try_into().unwrap());

Ok(Self { state, block_len })
}
}

impl From<Sha256VarCore> for GenericArray<u8, U40> {
fn from(core: Sha256VarCore) -> Self {
let mut serialized_state = Self::default();
let buffer = serialized_state.as_mut_slice();

for (i, bytes) in buffer[..Sha256VarCore::STATE_SIZE]
.as_mut()
.chunks_mut(Sha256VarCore::STATE_ELEMENT_SIZE)
.enumerate()
{
bytes.copy_from_slice(core.state[i].to_be_bytes().as_slice());
}

buffer[Sha256VarCore::STATE_SIZE..]
.as_mut()
.copy_from_slice(core.block_len.to_be_bytes().as_slice());

serialized_state
}
}

/// Core block-level SHA-512 hasher with variable output size.
///
/// Supports initialization only for 28, 32, 48, and 64 byte output sizes,
Expand Down Expand Up @@ -155,3 +228,55 @@ impl fmt::Debug for Sha512VarCore {
f.write_str("Sha512VarCore { ... }")
}
}

impl HashCoreSerializableState for Sha512VarCore {
type SerializedStateSize = U80;
}

impl Sha512VarCore {
const STATE_SIZE: usize = size_of::<consts::State512>();
const STATE_ELEMENT_SIZE: usize = size_of::<u64>();
}

impl TryFrom<GenericArray<u8, U80>> for Sha512VarCore {
type Error = HashCoreStateDeserializationError;

fn try_from(serialized_state: GenericArray<u8, U80>) -> Result<Self, Self::Error> {
let buffer = serialized_state.as_slice();

let mut state = [0; consts::STATE_LEN];
for (i, bytes) in buffer[..Self::STATE_SIZE]
.as_ref()
.chunks(Self::STATE_ELEMENT_SIZE)
.enumerate()
{
state[i] = u64::from_be_bytes(bytes.try_into().unwrap());
}

let block_len =
u128::from_be_bytes(buffer[Self::STATE_SIZE..].as_ref().try_into().unwrap());

Ok(Self { state, block_len })
}
}

impl From<Sha512VarCore> for GenericArray<u8, U80> {
fn from(core: Sha512VarCore) -> Self {
let mut serialized_state = Self::default();
let buffer = serialized_state.as_mut_slice();

for (i, bytes) in buffer[..Sha512VarCore::STATE_SIZE]
.as_mut()
.chunks_mut(Sha512VarCore::STATE_ELEMENT_SIZE)
.enumerate()
{
bytes.copy_from_slice(core.state[i].to_be_bytes().as_slice());
}

buffer[Sha512VarCore::STATE_SIZE..]
.as_mut()
.copy_from_slice(core.block_len.to_be_bytes().as_slice());

serialized_state
}
}
Loading

0 comments on commit 0ddab78

Please sign in to comment.