Skip to content

Commit 23e8c12

Browse files
committed
blake2: restore Blake2bVar/Blake2sVar
Restores hashes that previously impl'd the now removed `VariableOutput` trait which were removed in #744. They're used by `argon2`. Note the API has been transition from traits to inherent methods, and the implementations have not been restored for other runtime-variable hash functions, just `blake2` due to the need to use `Blake2bVar` in the `argon2` crate.
1 parent 8af25ee commit 23e8c12

File tree

6 files changed

+247
-27
lines changed

6 files changed

+247
-27
lines changed

Cargo.lock

Lines changed: 24 additions & 24 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

blake2/CHANGELOG.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1717

1818
### Removed
1919
- `std` crate feature ([#678])
20-
- `Blake2bVar` and `Blake2sVar` types ([#744])
2120

2221
[#652]: https://github.com/RustCrypto/hashes/pull/652
2322
[#678]: https://github.com/RustCrypto/hashes/pull/678
24-
[#744]: https://github.com/RustCrypto/hashes/pull/744
2523

2624
## 0.10.6 (2022-12-16)
2725
### Added

blake2/README.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,22 @@ Also, see the [examples section] in the RustCrypto/hashes readme.
4545

4646
### Variable output size
4747

48-
This implementation supports output sizes variable at compile time:
48+
This implementation supports run and compile time variable sizes.
4949

50+
Output size set at run time:
51+
```rust
52+
use blake2::Blake2bVar;
53+
use blake2::digest::Update;
54+
use hex_literal::hex;
55+
56+
let mut hasher = Blake2bVar::new(10).unwrap();
57+
hasher.update(b"my_input");
58+
let mut buf = [0u8; 10];
59+
hasher.finalize_variable(&mut buf).unwrap();
60+
assert_eq!(buf, hex!("2cc55c84e416924e6400"));
61+
```
62+
63+
Output size set at compile time:
5064
```rust
5165
use blake2::{Blake2b, Digest, digest::consts::U10};
5266
use hex_literal::hex;

blake2/src/lib.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,12 @@ where
7979
}
8080
}
8181

82+
buffer_rt_variable!(
83+
/// BLAKE2b which allows to choose output size at runtime.
84+
pub struct Blake2bVar(Blake2bVarCore);
85+
exclude: SerializableState;
86+
);
87+
8288
/// BLAKE2b-128 hasher state.
8389
pub type Blake2b128 = Blake2b<U16>;
8490
/// BLAKE2b-256 hasher state.
@@ -127,6 +133,12 @@ where
127133
}
128134
}
129135

136+
buffer_rt_variable!(
137+
/// BLAKE2s which allows to choose output size at runtime.
138+
pub struct Blake2sVar(Blake2sVarCore);
139+
exclude: SerializableState;
140+
);
141+
130142
/// BLAKE2s-128 hasher state.
131143
pub type Blake2s128 = Blake2s<U16>;
132144
/// BLAKE2s-256 hasher state.

blake2/src/macros.rs

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,3 +472,195 @@ macro_rules! blake2_mac_impl {
472472
}
473473
};
474474
}
475+
476+
/// Creates a buffered wrapper around block-level "core" type which implements variable output size traits
477+
/// with output size selected at run time.
478+
#[macro_export]
479+
macro_rules! buffer_rt_variable {
480+
(
481+
$(#[$attr:meta])*
482+
$vis:vis struct $name:ident($core_ty:ty);
483+
exclude: SerializableState;
484+
) => {
485+
$(#[$attr])*
486+
$vis struct $name {
487+
core: $core_ty,
488+
buffer: digest::block_api::Buffer<$core_ty>,
489+
output_size: u8,
490+
}
491+
492+
impl core::fmt::Debug for $name {
493+
#[inline]
494+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
495+
f.write_str(concat!(stringify!($name), " { ... }"))
496+
}
497+
}
498+
499+
impl digest::crypto_common::AlgorithmName for $name {
500+
#[inline]
501+
fn write_alg_name(f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
502+
<$core_ty as digest::crypto_common::AlgorithmName>::write_alg_name(f)
503+
}
504+
}
505+
506+
impl Clone for $name {
507+
#[inline]
508+
fn clone(&self) -> Self {
509+
Self {
510+
core: Clone::clone(&self.core),
511+
buffer: Clone::clone(&self.buffer),
512+
output_size: self.output_size,
513+
}
514+
}
515+
}
516+
517+
impl digest::Reset for $name {
518+
#[inline]
519+
fn reset(&mut self) {
520+
let size = self.output_size.into();
521+
self.core = <$core_ty as digest::block_api::VariableOutputCore>::new(size).unwrap();
522+
self.buffer.reset();
523+
}
524+
}
525+
526+
impl digest::block_api::BlockSizeUser for $name {
527+
type BlockSize = <$core_ty as digest::crypto_common::BlockSizeUser>::BlockSize;
528+
}
529+
530+
impl digest::HashMarker for $name {}
531+
532+
impl digest::Update for $name {
533+
#[inline]
534+
fn update(&mut self, data: &[u8]) {
535+
let Self { core, buffer, .. } = self;
536+
buffer.digest_blocks(data, |blocks| {
537+
digest::block_api::UpdateCore::update_blocks(core, blocks);
538+
});
539+
}
540+
}
541+
542+
impl $name {
543+
/// Maximum size of output hash in bytes.
544+
pub const MAX_OUTPUT_SIZE: usize = <
545+
<$core_ty as digest::block_api::OutputSizeUser>::OutputSize
546+
as digest::typenum::Unsigned
547+
>::USIZE;
548+
549+
/// Create new hasher instance with the given output size in bytes.
550+
///
551+
/// It will return `Err(InvalidOutputSize)` in case if hasher can not return
552+
/// hash of the specified output size.
553+
#[inline]
554+
pub fn new(output_size: usize) -> Result<Self, digest::InvalidOutputSize> {
555+
let output_size = u8::try_from(output_size).map_err(|_| digest::InvalidOutputSize)?;
556+
let buffer = Default::default();
557+
let core = <$core_ty as digest::block_api::VariableOutputCore>::new(output_size.into())?;
558+
Ok(Self {
559+
core,
560+
buffer,
561+
output_size,
562+
})
563+
}
564+
565+
/// Get output size in bytes of the hasher instance provided to the `new` method
566+
#[inline]
567+
pub fn output_size(&self) -> usize {
568+
self.output_size.into()
569+
}
570+
571+
/// Write result into the output buffer.
572+
///
573+
/// Returns `Err(InvalidOutputSize)` if `out` size is not equal to
574+
/// `self.output_size()`.
575+
#[inline]
576+
pub fn finalize_variable(mut self, out: &mut [u8]) -> Result<(), digest::InvalidBufferSize> {
577+
self.finalize_dirty(out)
578+
}
579+
580+
/// Write result into the output buffer and reset the hasher state.
581+
///
582+
/// Returns `Err(InvalidOutputSize)` if `out` size is not equal to
583+
/// `self.output_size()`.
584+
#[inline]
585+
pub fn finalize_variable_reset(
586+
&mut self,
587+
out: &mut [u8],
588+
) -> Result<(), digest::InvalidBufferSize> {
589+
self.finalize_dirty(out)?;
590+
digest::Reset::reset(self);
591+
Ok(())
592+
}
593+
594+
#[inline]
595+
fn finalize_dirty(&mut self, out: &mut [u8]) -> Result<(), digest::InvalidBufferSize> {
596+
let Self {
597+
core,
598+
buffer,
599+
output_size,
600+
} = self;
601+
let size_u8 = u8::try_from(out.len()).map_err(|_| digest::InvalidBufferSize)?;
602+
603+
let max_size = Self::MAX_OUTPUT_SIZE;
604+
if out.len() > max_size || size_u8 != *output_size {
605+
return Err(digest::InvalidBufferSize);
606+
}
607+
let mut full_res = Default::default();
608+
digest::block_api::VariableOutputCore::finalize_variable_core(core, buffer, &mut full_res);
609+
let n = out.len();
610+
let m = full_res.len() - n;
611+
use digest::block_api::TruncSide::{Left, Right};
612+
let side = <$core_ty as digest::block_api::VariableOutputCore>::TRUNC_SIDE;
613+
match side {
614+
Left => out.copy_from_slice(&full_res[..n]),
615+
Right => out.copy_from_slice(&full_res[m..]),
616+
}
617+
Ok(())
618+
}
619+
}
620+
621+
impl Drop for $name {
622+
#[inline]
623+
fn drop(&mut self) {
624+
#[cfg(feature = "zeroize")]
625+
{
626+
use digest::zeroize::Zeroize;
627+
self.buffer.zeroize();
628+
self.output_size.zeroize();
629+
}
630+
}
631+
}
632+
633+
#[cfg(feature = "zeroize")]
634+
impl digest::zeroize::ZeroizeOnDrop for $name {}
635+
};
636+
637+
(
638+
$(#[$attr:meta])*
639+
$vis:vis struct $name:ident($core_ty:ty);
640+
) => {
641+
buffer_rt_variable!(
642+
$(#[$attr])*
643+
$vis struct $name($core_ty);
644+
exclude: SerializableState;
645+
);
646+
647+
impl digest::crypto_common::hazmat::SerializableState for $name {
648+
type SerializedStateSize = digest::typenum::Add1<digest::typenum::Sum<
649+
<$core_ty as digest::crypto_common::hazmat::SerializableState>::SerializedStateSize,
650+
<$core_ty as digest::block_api::BlockSizeUser>::BlockSize,
651+
>>;
652+
653+
#[inline]
654+
fn serialize(&self) -> digest::crypto_common::hazmat::SerializedState<Self> {
655+
todo!()
656+
}
657+
658+
#[inline]
659+
fn deserialize(
660+
serialized_state: &digest::crypto_common::hazmat::SerializedState<Self>,
661+
) -> Result<Self, digest::crypto_common::hazmat::DeserializeStateError> {
662+
todo!()
663+
}
664+
}
665+
};
666+
}

blake2/tests/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,7 @@ use digest::dev::fixed_test;
55
use digest::new_test;
66

77
new_test!(blake2b_kat, blake2::Blake2b512, fixed_test);
8+
9+
// TODO(tarcieri): port tests over from the `digest` crate
10+
// new_test!(blake2b_variable_kat, blake2::Blake2bVar, variable_test);
11+
// new_test!(blake2s_variable_kat, blake2::Blake2sVar, variable_test);

0 commit comments

Comments
 (0)