Skip to content

digest: add newtype! macro #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 12 commits into
base: master
Choose a base branch
from
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
7 changes: 7 additions & 0 deletions digest/src/core_api/wrapper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,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
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;

#[cfg(feature = "core-api")]
pub use block_buffer;
Expand Down
266 changes: 266 additions & 0 deletions digest/src/newtype.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,266 @@
/// Creates a newtype wrapper around another type and
/// delegates implementation of `digest` traits to it.
#[macro_export]
macro_rules! newtype {
(
$(#[$attr:meta])*
$v:vis struct $name:ident($wrapped_ty:ty);
$(delegate_template: $template_name:ident)?
$(delegate: $($trait_name:ident)*)?
$(oid: $oid:literal)?
) => {
$(#[$attr])*
$v struct $name($wrapped_ty);

$(
$crate::newtype!(template_impl: $template_name $name($wrapped_ty));
)?

$(
$crate::newtype!(delegate_impls: $name($wrapped_ty) $($trait_name)*);
)?

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

(template_impl: FixedOutputHash $name:ident($wrapped_ty:ty)) => {
$crate::newtype!(
delegate_impls: $name($wrapped_ty)
Debug Clone Default
AlgorithmName SerializableState
BlockSizeUser OutputSizeUser
HashMarker Reset Update
FixedOutput FixedOutputReset
);
};

(template_impl: ExtendableOutputHash $name:ident($wrapped_ty:ty)) => {
$crate::newtype!(
delegate_impls: $name($wrapped_ty)
Debug Clone Default
AlgorithmName SerializableState
BlockSizeUser
HashMarker Reset Update
ExtendableOutput ExtendableOutputReset
);
};

(delegate_impls: $name:ident($wrapped_ty:ty) $($trait_name:ident)*) => {
$(
$crate::newtype!(delegate_impl: $name($wrapped_ty) $trait_name);
)*
};

(delegate_impl: $name:ident($wrapped_ty:ty) Debug) => {
impl core::fmt::Debug for $name {
#[inline]
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str(concat!(stringify!($name), " { ... }"))
}
}
};

(delegate_impl: $name:ident($wrapped_ty:ty) AlgorithmName) => {
impl $crate::crypto_common::AlgorithmName for $name {
#[inline]
fn write_alg_name(f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
<$wrapped_ty as $crate::crypto_common::AlgorithmName>::write_alg_name(f)
}
}
};

(delegate_impl: $name:ident($wrapped_ty:ty) Clone) => {
impl Clone for $name {
#[inline]
fn clone(&self) -> Self {
Self(<$wrapped_ty as Clone>::clone(&self.0))
}
}
};

(delegate_impl: $name:ident($wrapped_ty:ty) Default) => {
impl Default for $name {
#[inline]
fn default() -> Self {
Self(<$wrapped_ty as Default>::default())
}
}
};

(delegate_impl: $name:ident($wrapped_ty:ty) InnerInit) => {
impl $crate::InnerInit for $name {
#[inline]
fn new(inner: Self::Inner) -> Self {
Self(<$wrapped_ty as $crate::InnerInit>::new(inner))
}
}
};

(delegate_impl: $name:ident($wrapped_ty:ty) KeyInit) => {
impl $crate::KeyInit for $name {
#[inline]
fn new(key: &$crate::Key<Self>) -> Self {
Self(<$wrapped_ty as $crate::KeyInit>::new(key))
}
}
};

(delegate_impl: $name:ident($wrapped_ty:ty) CustomizedInit) => {
impl $crate::CustomizedInit for $name {
#[inline]
fn new_customized(customization: &[u8]) -> Self {
Self(<$wrapped_ty as $crate::CustomizedInit>::new_customized(customization))
}
}
};

(delegate_impl: $name:ident($wrapped_ty:ty) Reset) => {
impl $crate::Reset for $name {
#[inline]
fn reset(&mut self) {
<$wrapped_ty as $crate::Reset>::reset(&mut self.0);
}
}
};

(delegate_impl: $name:ident($wrapped_ty:ty) BlockSizeUser) => {
impl $crate::core_api::BlockSizeUser for $name {
type BlockSize = <$wrapped_ty as $crate::crypto_common::BlockSizeUser>::BlockSize;
}
};

(delegate_impl: $name:ident($wrapped_ty:ty) OutputSizeUser) => {
impl $crate::OutputSizeUser for $name {
type OutputSize = <$wrapped_ty as $crate::core_api::OutputSizeUser>::OutputSize;
}
};

(delegate_impl: $name:ident($wrapped_ty:ty) KeySizeUser) => {
impl $crate::crypto_common::KeySizeUser for $name {
type KeySize = <$wrapped_ty as $crate::crypto_common::KeySizeUser>::KeySize;
}
};

(delegate_impl: $name:ident($wrapped_ty:ty) HashMarker) => {
impl $crate::HashMarker for $name {}

// TODO: assert that `$wrapped_ty` impls `HashMarker`?
};

(delegate_impl: $name:ident($wrapped_ty:ty) MacMarker) => {
impl $crate::MacMarker for $name {}

// TODO: assert that `$wrapped_ty` impls `MacMarker`?
};

(delegate_impl: $name:ident($wrapped_ty:ty) Update) => {
impl $crate::Update for $name {
#[inline]
fn update(&mut self, data: &[u8]) {
<$wrapped_ty as $crate::Update>::update(&mut self.0, data)
}
}
};

(delegate_impl: $name:ident($wrapped_ty:ty) CoreProxy) => {
impl $crate::core_api::CoreProxy for $name {
type Core = <$wrapped_ty as $crate::core_api::CoreProxy>::Core;
}
};

(delegate_impl: $name:ident($wrapped_ty:ty) FixedOutput) => {
impl $crate::FixedOutput for $name {
#[inline]
fn finalize_into(self, out: &mut $crate::Output<Self>) {
<$wrapped_ty as $crate::FixedOutput>::finalize_into(self.0, out)
}
}
};

(delegate_impl: $name:ident($wrapped_ty:ty) FixedOutputReset) => {
impl $crate::FixedOutputReset for $name {
#[inline]
fn finalize_into_reset(&mut self, out: &mut $crate::Output<Self>) {
<$wrapped_ty as $crate::FixedOutputReset>::finalize_into_reset(&mut self.0, out);
}
}
};

(delegate_impl: $name:ident($wrapped_ty:ty) VariableOutput) => {
impl $crate::VariableOutput for $name {
const MAX_OUTPUT_SIZE: usize = <$wrapped_ty as $crate::VariableOutput>::MAX_OUTPUT_SIZE;

#[inline]
fn new(output_size: usize) -> Result<Self, $crate::InvalidOutputSize> {
<$wrapped_ty as $crate::VariableOutput>::new(output_size)
}

#[inline]
fn output_size(&self) -> usize {
<$wrapped_ty as $crate::VariableOutput>::output_size(&self.0)
}

#[inline]
fn finalize_variable(self, out: &mut [u8]) -> Result<(), $crate::InvalidBufferSize> {
<$wrapped_ty as $crate::VariableOutput>::finalize_variable(self.0, out)
}
}
};

(delegate_impl: $name:ident($wrapped_ty:ty) VariableOutputReset) => {
impl $crate::VariableOutputReset for $name {
#[inline]
fn finalize_variable_reset(
&mut self,
out: &mut [u8],
) -> Result<(), $crate::InvalidBufferSize> {
<$wrapped_ty as $crate::VariableOutputReset>::finalize_variable_reset(&mut self.0, out)
}
}
};

(delegate_impl: $name:ident($wrapped_ty:ty) ExtendableOutput) => {
impl $crate::ExtendableOutput for $name {
// TODO: use a newtype wrapper?
type Reader = <$wrapped_ty as $crate::ExtendableOutput>::Reader;

#[inline]
fn finalize_xof(self) -> Self::Reader {
<$wrapped_ty as $crate::ExtendableOutput>::finalize_xof(self.0)
}
}
};

(delegate_impl: $name:ident($wrapped_ty:ty) ExtendableOutputReset) => {
impl $crate::ExtendableOutputReset for $name {
#[inline]
fn finalize_xof_reset(&mut self) -> Self::Reader {
<$wrapped_ty as $crate::ExtendableOutputReset>::finalize_xof_reset(&mut self.0)
}
}
};

(delegate_impl: $name:ident($wrapped_ty:ty) SerializableState) => {
impl $crate::crypto_common::hazmat::SerializableState for $name {
type SerializedStateSize = <$wrapped_ty as $crate::crypto_common::hazmat::SerializableState>::SerializedStateSize;

#[inline]
fn serialize(&self) -> $crate::crypto_common::hazmat::SerializedState<Self> {
<$wrapped_ty as $crate::crypto_common::hazmat::SerializableState>::serialize(&self.0)
}

#[inline]
fn deserialize(
serialized_state: &$crate::crypto_common::hazmat::SerializedState<Self>,
) -> Result<Self, $crate::crypto_common::hazmat::DeserializeStateError> {
<$wrapped_ty as $crate::crypto_common::hazmat::SerializableState>::deserialize(serialized_state).map(Self)
}
}
};
}
82 changes: 82 additions & 0 deletions digest/tests/dummy_fixed.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#![cfg(feature = "core-api")]

use core::fmt;
use digest::{
HashMarker, Output, OutputSizeUser, Reset,
consts::U8,
core_api::{
AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, CoreWrapper, FixedOutputCore,
UpdateCore,
},
crypto_common::hazmat::{DeserializeStateError, SerializableState, SerializedState},
};

/// Core of primitive XOR hasher for testing purposes
#[derive(Clone, Default, Debug)]
pub struct FixedHashCore {
state: u64,
}

impl AlgorithmName for FixedHashCore {
fn write_alg_name(f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.write_str("FixedHash")
}
}

impl BlockSizeUser for FixedHashCore {
type BlockSize = U8;
}

impl BufferKindUser for FixedHashCore {
type BufferKind = block_buffer::Eager;
}

impl Reset for FixedHashCore {
fn reset(&mut self) {
self.state = 0;
}
}

impl UpdateCore for FixedHashCore {
fn update_blocks(&mut self, blocks: &[Block<Self>]) {
for block in blocks {
self.state ^= u64::from_le_bytes(block.0)
}
}
}

impl HashMarker for FixedHashCore {}

impl OutputSizeUser for FixedHashCore {
type OutputSize = U8;
}

impl FixedOutputCore for FixedHashCore {
fn finalize_fixed_core(&mut self, buffer: &mut Buffer<Self>, out: &mut Output<Self>) {
let block = buffer.pad_with_zeros();
self.state ^= u64::from_le_bytes(block.0);
out.copy_from_slice(&self.state.to_le_bytes());
}
}

impl SerializableState for FixedHashCore {
type SerializedStateSize = U8;

fn serialize(&self) -> SerializedState<Self> {
self.state.to_le_bytes().into()
}

fn deserialize(
serialized_state: &SerializedState<Self>,
) -> Result<Self, DeserializeStateError> {
Ok(Self {
state: u64::from_le_bytes(serialized_state.0),
})
}
}

digest::newtype!(
/// Primitive XOR hasher for testing purposes
pub struct FixedHash(CoreWrapper<FixedHashCore>);
delegate_template: FixedOutputHash
);