Skip to content

digest: add newtype macros #1799

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 22 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 0 additions & 19 deletions digest/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,25 +64,6 @@ let hash = Sha256::digest(b"my message");
println!("Result: {:x}", hash);
```

### Hashing `Read`-able objects

If you want to hash data from [`Read`][3] trait (e.g. from file) you can rely on
implementation of [`Write`][4] trait (requires enabled-by-default `std` feature):

```rust
use sha2::{Sha256, Digest};
use std::{fs, io};

let mut file = fs::File::open(&path)?;
let mut hasher = Sha256::new();
let n = io::copy(&mut file, &mut hasher)?;
let hash = hasher.finalize();

println!("Path: {}", path);
println!("Bytes processed: {}", n);
println!("Hash value: {:x}", hash);
```

### Generic code

You can write generic code over `Digest` (or other traits from `digest` crate)
Expand Down
1 change: 1 addition & 0 deletions digest/src/core_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
//! higher-level traits.
use crate::InvalidOutputSize;

pub use block_buffer::{Eager, Lazy};
pub use crypto_common::{AlgorithmName, Block, BlockSizeUser, OutputSizeUser, Reset};

use block_buffer::{BlockBuffer, BufferKind};
Expand Down
1 change: 1 addition & 0 deletions digest/src/core_api/ct_variable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use crypto_common::{
hazmat::{DeserializeStateError, SerializableState, SerializedState, SubSerializedStateSize},
typenum::{IsLess, IsLessOrEqual, Le, LeEq, NonZero, Sum, U1, U256},
};

/// Wrapper around [`VariableOutputCore`] which selects output size
/// at compile time.
#[derive(Clone)]
Expand Down
15 changes: 11 additions & 4 deletions digest/src/core_api/rt_variable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ use crypto_common::{
#[cfg(feature = "zeroize")]
use zeroize::ZeroizeOnDrop;

/// Wrapper around [`VariableOutputCore`] which selects output size
/// at run time.
/// Wrapper around [`VariableOutputCore`] which selects output size at run time.
#[derive(Clone)]
pub struct RtVariableCoreWrapper<T: VariableOutputCore> {
core: T,
Expand Down Expand Up @@ -64,11 +63,19 @@ impl<T: VariableOutputCore> BlockSizeUser for RtVariableCoreWrapper<T> {
type BlockSize = T::BlockSize;
}

impl<T: VariableOutputCore + Reset> Reset for RtVariableCoreWrapper<T> {
// TODO: remove because the hasher may be customized?
impl<T: VariableOutputCore> Reset for RtVariableCoreWrapper<T> {
#[inline]
fn reset(&mut self) {
self.buffer.reset();
self.core.reset();
self.core = T::new(self.output_size as usize).unwrap();
}
}

impl<T: VariableOutputCore + AlgorithmName> AlgorithmName for RtVariableCoreWrapper<T> {
fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
T::write_alg_name(f)?;
f.write_str("Var")
}
}

Expand Down
19 changes: 9 additions & 10 deletions digest/src/core_api/wrapper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,13 @@ impl<T: ExtendableOutputCore + Reset> ExtendableOutputReset for CoreWrapper<T> {
}
}

impl<T: BufferKindUser + AlgorithmName> AlgorithmName for CoreWrapper<T> {
#[inline]
fn write_alg_name(f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
T::write_alg_name(f)
}
}

impl<T: BufferKindUser> Drop for CoreWrapper<T> {
#[inline]
fn drop(&mut self) {
Expand Down Expand Up @@ -214,20 +221,12 @@ where
}
}

/// A proxy trait to a core type implemented by [`CoreWrapper`]
// TODO: replace with an inherent associated type on stabilization:
// https://github.com/rust-lang/rust/issues/8995
pub trait CoreProxy: sealed::Sealed {
/// A proxy trait to a core type.
pub trait CoreProxy {
/// Type wrapped by [`CoreWrapper`].
type Core;
}

mod sealed {
pub trait Sealed {}
}

impl<T: BufferKindUser> sealed::Sealed for CoreWrapper<T> {}

impl<T: BufferKindUser> CoreProxy for CoreWrapper<T> {
type Core = T;
}
1 change: 1 addition & 0 deletions digest/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ pub mod core_api;
mod digest;
#[cfg(feature = "mac")]
mod mac;
mod newtype;
mod xof_fixed;

#[cfg(feature = "core-api")]
Expand Down
4 changes: 4 additions & 0 deletions digest/src/newtype.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
mod fixed;
mod variable_ct;
mod variable_rt;
mod xof;
171 changes: 171 additions & 0 deletions digest/src/newtype/fixed.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
/// Creates a newtype wrapper around another type and
/// delegates implementation of `digest` traits to it.
#[macro_export]
macro_rules! newtype_fixed_hash {
(
$(#[$attr:meta])*
$v:vis struct $name:ident$(<$gp:ident: $bound:ident>)?($core_ty:ty);
) => {
$(#[$attr])*
$v struct $name$(<$gp: $bound>)? {
core: $core_ty,
buffer: $crate::core_api::Buffer<$core_ty>,
}

impl$(<$gp: $bound>)? core::fmt::Debug for $name$(<$gp>)? {
#[inline]
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str(concat!(stringify!($name), " { ... }"))
}
}

impl$(<$gp: $bound>)? $crate::crypto_common::AlgorithmName for $name$(<$gp>)? {
#[inline]
fn write_alg_name(f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
<$core_ty as $crate::crypto_common::AlgorithmName>::write_alg_name(f)
}
}

impl$(<$gp: $bound>)? Clone for $name$(<$gp>)? {
#[inline]
fn clone(&self) -> Self {
Self {
core: Clone::clone(&self.core),
buffer: Clone::clone(&self.buffer),
}
}
}

impl$(<$gp: $bound>)? Default for $name$(<$gp>)? {
#[inline]
fn default() -> Self {
Self {
core: Default::default(),
buffer: Default::default(),
}
}
}

impl$(<$gp: $bound>)? $crate::Reset for $name$(<$gp>)? {
#[inline]
fn reset(&mut self) {
$crate::Reset::reset(&mut self.core);
self.buffer.reset();
}
}

impl$(<$gp: $bound>)? $crate::core_api::BlockSizeUser for $name$(<$gp>)? {
type BlockSize = <$core_ty as $crate::crypto_common::BlockSizeUser>::BlockSize;
}

impl$(<$gp: $bound>)? $crate::OutputSizeUser for $name$(<$gp>)? {
type OutputSize = <$core_ty as $crate::core_api::OutputSizeUser>::OutputSize;
}

impl$(<$gp: $bound>)? $crate::HashMarker for $name$(<$gp>)? {}

// Verify that `$core_ty` implements `HashMarker`
const _: () = {
fn check$(<$gp: $bound>)?(v: &$core_ty) {
v as &dyn $crate::HashMarker;
}
};

impl$(<$gp: $bound>)? $crate::core_api::CoreProxy for $name$(<$gp>)? {
type Core = $core_ty;
}

impl$(<$gp: $bound>)? $crate::Update for $name$(<$gp>)? {
#[inline]
fn update(&mut self, data: &[u8]) {
let Self { core, buffer } = self;
buffer.digest_blocks(data, |blocks| {
$crate::core_api::UpdateCore::update_blocks(core, blocks)
});
}
}

impl$(<$gp: $bound>)? $crate::FixedOutput for $name$(<$gp>)? {
#[inline]
fn finalize_into(mut self, out: &mut $crate::Output<Self>) {
let Self { core, buffer } = &mut self;
$crate::core_api::FixedOutputCore::finalize_fixed_core(core, buffer, out);
}
}

impl$(<$gp: $bound>)? $crate::FixedOutputReset for $name$(<$gp>)? {
#[inline]
fn finalize_into_reset(&mut self, out: &mut $crate::Output<Self>) {
let Self { core, buffer } = self;
$crate::core_api::FixedOutputCore::finalize_fixed_core(core, buffer, out);
$crate::Reset::reset(self);
}
}

impl$(<$gp: $bound>)? $crate::crypto_common::hazmat::SerializableState for $name$(<$gp>)? {
type SerializedStateSize = $crate::typenum::Sum<
<$core_ty as $crate::crypto_common::hazmat::SerializableState>::SerializedStateSize,
$crate::typenum::Add1<
<$core_ty as $crate::core_api::BlockSizeUser>::BlockSize
>
>;

#[inline]
fn serialize(&self) -> $crate::crypto_common::hazmat::SerializedState<Self> {
use $crate::{
array::Array,
consts::U1,
block_buffer::BlockBuffer,
crypto_common::hazmat::SerializableState,
};

let serialized_core = self.core.serialize();
let pos = u8::try_from(self.buffer.get_pos()).unwrap();
let serialized_pos: Array<u8, U1> = Array([pos]);
let serialized_data = self.buffer.clone().pad_with_zeros();

serialized_core
.concat(serialized_pos)
.concat(serialized_data)
}

#[inline]
fn deserialize(
serialized_state: &$crate::crypto_common::hazmat::SerializedState<Self>,
) -> Result<Self, $crate::crypto_common::hazmat::DeserializeStateError> {
use $crate::{
block_buffer::BlockBuffer,
consts::U1,
crypto_common::hazmat::{SerializableState, DeserializeStateError},
};

let (serialized_core, remaining_buffer) = serialized_state
.split_ref::<<$core_ty as SerializableState>::SerializedStateSize>();
let (serialized_pos, serialized_data) = remaining_buffer.split_ref::<U1>();

Ok(Self {
core: <$core_ty as SerializableState>::deserialize(serialized_core)?,
buffer: BlockBuffer::try_new(&serialized_data[..serialized_pos[0].into()])
.map_err(|_| DeserializeStateError)?,
})
}
}
};

(
$(#[$attr:meta])*
$v:vis struct $name:ident$(<$gp:ident: $bound:ident>)?($core_ty:ty);
oid: $oid:literal
) => {
$crate::newtype_fixed_hash!(
$(#[$attr])*
$v struct $name$(<$gp: $bound>)?($core_ty);
);

#[cfg(feature = "oid")]
impl$(<$gp: $bound>)? $crate::const_oid::AssociatedOid for $name$(<$gp>)? {
const OID: $crate::const_oid::ObjectIdentifier =
$crate::const_oid::ObjectIdentifier::new_unwrap($oid);
}
};
}
Loading