Skip to content

Commit e048918

Browse files
authored
bash-hash: minor refactor (#746)
1 parent 65f8551 commit e048918

File tree

8 files changed

+164
-175
lines changed

8 files changed

+164
-175
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Additionally all crates do not require the standard library (i.e. `no_std` capab
1414
| Algorithm | Crate | Crates.io | Documentation | MSRV | [Security] |
1515
|-----------|-------|:---------:|:-------------:|:----:|:----------:|
1616
| [Ascon] hash | [`ascon‑hash`] | [![crates.io](https://img.shields.io/crates/v/ascon-hash.svg)](https://crates.io/crates/ascon-hash) | [![Documentation](https://docs.rs/ascon-hash/badge.svg)](https://docs.rs/ascon-hash) | 1.85 | :green_heart: |
17+
| [`bash-hash`][bash_hash_stb] | [`belt‑hash`] | [![crates.io](https://img.shields.io/crates/v/bash-hash.svg)](https://crates.io/crates/bash-hash) | [![Documentation](https://docs.rs/bash-hash/badge.svg)](https://docs.rs/bash-hash) | 1.85 | :green_heart: |
1718
| [BelT] hash | [`belt‑hash`] | [![crates.io](https://img.shields.io/crates/v/belt-hash.svg)](https://crates.io/crates/belt-hash) | [![Documentation](https://docs.rs/belt-hash/badge.svg)](https://docs.rs/belt-hash) | 1.85 | :green_heart: |
1819
| [BLAKE2] | [`blake2`] | [![crates.io](https://img.shields.io/crates/v/blake2.svg)](https://crates.io/crates/blake2) | [![Documentation](https://docs.rs/blake2/badge.svg)](https://docs.rs/blake2) | 1.85 | :green_heart: |
1920
| [FSB] | [`fsb`] | [![crates.io](https://img.shields.io/crates/v/fsb.svg)](https://crates.io/crates/fsb) | [![Documentation](https://docs.rs/fsb/badge.svg)](https://docs.rs/fsb) | 1.85 | :green_heart: |
@@ -235,6 +236,7 @@ Unless you explicitly state otherwise, any contribution intentionally submitted
235236
[//]: # (crates)
236237

237238
[`ascon‑hash`]: ./ascon-hash
239+
[`bash‑hash`]: ./bash-hash
238240
[`belt‑hash`]: ./belt-hash
239241
[`blake2`]: ./blake2
240242
[`fsb`]: ./fsb
@@ -278,6 +280,7 @@ Unless you explicitly state otherwise, any contribution intentionally submitted
278280
[//]: # (algorithms)
279281

280282
[Ascon]: https://ascon.iaik.tugraz.at
283+
[bash_hash_stb]: https://apmi.bsu.by/assets/files/std/bash-spec241.pdf
281284
[BelT]: https://ru.wikipedia.org/wiki/BelT
282285
[BLAKE2]: https://en.wikipedia.org/wiki/BLAKE_(hash_function)#BLAKE2
283286
[FSB]: https://en.wikipedia.org/wiki/Fast_syndrome-based_hash

bash-hash/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ categories = ["cryptography", "no-std"]
1414

1515
[dependencies]
1616
digest = "0.11.0-rc.3"
17-
bash-f = { version = "0.1.0" }
17+
bash-f = "0.1"
1818

1919
[dev-dependencies]
2020
digest = { version = "0.11.0-rc.3", features = ["dev"] }

bash-hash/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
![Rust Version][rustc-image]
88
[![Project Chat][chat-image]][chat-link]
99

10-
Pure Rust implementation of the bash hash function specified in [STB 34.101.31-2020].
10+
Pure Rust implementation of the bash hash function specified in [STB 34.101.77-2020].
1111

1212
## Examples
1313
```rust

bash-hash/src/block_api.rs

Lines changed: 54 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
use core::fmt;
1+
use core::{fmt, marker::PhantomData};
22
use digest::{
33
HashMarker, Output,
4-
array::Array,
54
block_api::{
65
AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, Eager, FixedOutputCore,
76
OutputSizeUser, Reset, UpdateCore,
@@ -10,48 +9,33 @@ use digest::{
109
typenum::U192,
1110
};
1211

13-
use crate::variants::{Bash256, Bash384, Bash512, Variant};
12+
use crate::OutputSize;
1413
use bash_f::{STATE_WORDS, bash_f};
15-
use digest::typenum::Unsigned;
1614

17-
/// Core Bash hasher state with generic security level.
15+
/// Core `bash-hash` hasher generic over output size.
1816
///
19-
/// Implements bash-hash[ℓ] algorithm according to section 7 of STB 34.101.77-2020.
20-
/// Parameters:
21-
/// - BlockSize: block size r = (1536 - 4ℓ) / 8 bytes
22-
/// - OutputSize: output size 2ℓ / 8 bytes
23-
#[derive(Clone)]
24-
pub struct BashHashCore<V: Variant> {
17+
/// Specified in Section 7 of STB 34.101.77-2020.
18+
pub struct BashHashCore<OS: OutputSize> {
2519
state: [u64; STATE_WORDS],
26-
_variant: core::marker::PhantomData<V>,
20+
_pd: PhantomData<OS>,
2721
}
2822

29-
impl<V> BashHashCore<V>
30-
where
31-
V: Variant,
32-
{
33-
/// Calculate security level ℓ
34-
///
35-
/// According to section 5.3: ℓ = OutputSize * 8 / 2 = OutputSize * 4
23+
impl<OS: OutputSize> Clone for BashHashCore<OS> {
3624
#[inline]
37-
const fn get_level() -> usize {
38-
// 3. ℓ ← OutSize * 8 / 2
39-
V::OutputSize::USIZE * 4
40-
}
41-
42-
/// Calculate buffer size r in bytes
43-
#[inline]
44-
const fn get_r_bytes() -> usize {
45-
V::BlockSize::USIZE
25+
fn clone(&self) -> Self {
26+
Self {
27+
state: self.state,
28+
_pd: PhantomData,
29+
}
4630
}
31+
}
4732

33+
impl<OS: OutputSize> BashHashCore<OS> {
4834
/// Compress one data block
4935
fn compress_block(&mut self, block: &Block<Self>) {
50-
let r_bytes = Self::get_r_bytes();
51-
debug_assert_eq!(r_bytes % 8, 0);
52-
5336
// 4.1: S[...1536 - 4ℓ) ← Xi
54-
for (dst, chunk) in self.state.iter_mut().zip(block[..r_bytes].chunks_exact(8)) {
37+
// TODO: use `as_chunks` after MSRV is bumped to 1.88+
38+
for (dst, chunk) in self.state.iter_mut().zip(block.chunks_exact(8)) {
5539
// `chunk` is guaranteed to be 8 bytes long due to `r_bytes` being a multiple of 8
5640
*dst = u64::from_le_bytes(chunk.try_into().unwrap());
5741
}
@@ -61,33 +45,21 @@ where
6145
}
6246
}
6347

64-
impl<V> HashMarker for BashHashCore<V> where V: Variant {}
48+
impl<OS: OutputSize> HashMarker for BashHashCore<OS> {}
6549

66-
impl<V> BlockSizeUser for BashHashCore<V>
67-
where
68-
V: Variant,
69-
{
70-
type BlockSize = V::BlockSize;
50+
impl<OS: OutputSize> BlockSizeUser for BashHashCore<OS> {
51+
type BlockSize = OS::BlockSize;
7152
}
7253

73-
impl<V> BufferKindUser for BashHashCore<V>
74-
where
75-
V: Variant,
76-
{
54+
impl<OS: OutputSize> BufferKindUser for BashHashCore<OS> {
7755
type BufferKind = Eager;
7856
}
7957

80-
impl<V> OutputSizeUser for BashHashCore<V>
81-
where
82-
V: Variant,
83-
{
84-
type OutputSize = V::OutputSize;
58+
impl<OS: OutputSize> OutputSizeUser for BashHashCore<OS> {
59+
type OutputSize = OS;
8560
}
8661

87-
impl<V> UpdateCore for BashHashCore<V>
88-
where
89-
V: Variant,
90-
{
62+
impl<OS: OutputSize> UpdateCore for BashHashCore<OS> {
9163
#[inline]
9264
fn update_blocks(&mut self, blocks: &[Block<Self>]) {
9365
for block in blocks {
@@ -96,85 +68,62 @@ where
9668
}
9769
}
9870

99-
impl<V> FixedOutputCore for BashHashCore<V>
100-
where
101-
V: Variant,
102-
{
71+
impl<OS: OutputSize> FixedOutputCore for BashHashCore<OS> {
10372
fn finalize_fixed_core(&mut self, buffer: &mut Buffer<Self>, out: &mut Output<Self>) {
104-
let pos = buffer.get_pos();
105-
10673
// 1. Split(X || 01, r) - split message with appended 01
10774
// 2: Xn ← Xn || 0^(1536-4ℓ-|Xn|) - pad last block with zeros
108-
let mut padding_block = Array::<u8, V::BlockSize>::default();
109-
let block = buffer.pad_with_zeros();
110-
padding_block.copy_from_slice(&block);
111-
padding_block[pos] = 0x40;
75+
let pos = buffer.get_pos();
76+
let mut block = buffer.pad_with_zeros();
77+
block[pos] = 0x40;
11278

11379
// 4. for i = 1, 2, ..., n, do:
114-
self.compress_block(&padding_block);
115-
116-
//5. Y ← S[...2ℓ)
117-
self.state
118-
.iter()
119-
.flat_map(|w| w.to_le_bytes())
120-
.take(V::OutputSize::USIZE)
121-
.zip(out.iter_mut())
122-
.for_each(|(src, dst)| *dst = src);
80+
self.compress_block(&block);
81+
82+
// 5. Y ← S[...2ℓ)
83+
// TODO: use `as_chunks` after MSRV is bumped to 1.88+
84+
for (src, dst) in self.state.iter().zip(out.chunks_exact_mut(8)) {
85+
dst.copy_from_slice(&src.to_le_bytes());
86+
}
12387
}
12488
}
12589

126-
impl<V> Default for BashHashCore<V>
127-
where
128-
V: Variant,
129-
{
90+
impl<OS: OutputSize> Default for BashHashCore<OS> {
13091
#[inline]
13192
fn default() -> Self {
13293
let mut state = [0u64; STATE_WORDS];
13394

95+
// 3. ℓ ← OutSize * 8 / 2
96+
let level = OS::USIZE * 4;
13497
// 3. S ← 0^1472 || ⟨ℓ/4⟩_64
135-
let level = Self::get_level();
13698
state[23] = (level / 4) as u64;
13799

138100
Self {
139101
state,
140-
_variant: core::marker::PhantomData,
102+
_pd: PhantomData,
141103
}
142104
}
143105
}
144106

145-
impl<V> Reset for BashHashCore<V>
146-
where
147-
V: Variant,
148-
{
107+
impl<OS: OutputSize> Reset for BashHashCore<OS> {
149108
#[inline]
150109
fn reset(&mut self) {
151110
*self = Default::default();
152111
}
153112
}
154113

155-
impl<V> AlgorithmName for BashHashCore<V>
156-
where
157-
V: Variant,
158-
{
114+
impl<OS: OutputSize> AlgorithmName for BashHashCore<OS> {
159115
fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
160-
let level = Self::get_level();
161-
write!(f, "Bash{}", level * 2)
116+
write!(f, "BashHash{}", OS::USIZE)
162117
}
163118
}
164119

165-
impl<V> fmt::Debug for BashHashCore<V>
166-
where
167-
V: Variant,
168-
{
120+
impl<OS: OutputSize> fmt::Debug for BashHashCore<OS> {
169121
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
170122
f.write_str("BashHashCore { ... }")
171123
}
172124
}
173125

174-
impl<V> Drop for BashHashCore<V>
175-
where
176-
V: Variant,
177-
{
126+
impl<OS: OutputSize> Drop for BashHashCore<OS> {
178127
fn drop(&mut self) {
179128
#[cfg(feature = "zeroize")]
180129
{
@@ -185,48 +134,31 @@ where
185134
}
186135

187136
#[cfg(feature = "zeroize")]
188-
impl<V> digest::zeroize::ZeroizeOnDrop for BashHashCore<V> where V: Variant {}
137+
impl<OS: OutputSize> digest::zeroize::ZeroizeOnDrop for BashHashCore<OS> {}
189138

190-
impl<V> SerializableState for BashHashCore<V>
191-
where
192-
V: Variant,
193-
{
139+
impl<OS: OutputSize> SerializableState for BashHashCore<OS> {
194140
type SerializedStateSize = U192;
195141

196142
fn serialize(&self) -> SerializedState<Self> {
197-
let mut dst = SerializedState::<Self>::default();
198-
199-
for (word, chunk) in self.state.iter().zip(dst.chunks_exact_mut(8)) {
200-
// `word` is guaranteed to be 8 bytes long due to `STATE_WORDS` being a multiple of 8
201-
// and `chunk` being a slice of 8 bytes
202-
chunk.copy_from_slice(&word.to_le_bytes());
143+
let mut res = SerializedState::<Self>::default();
144+
// TODO: use `as_chunks` after MSRV is bumped to 1.88+
145+
for (src, dst) in self.state.iter().zip(res.chunks_exact_mut(8)) {
146+
dst.copy_from_slice(&src.to_le_bytes());
203147
}
204-
205-
dst
148+
res
206149
}
207150

208151
fn deserialize(
209152
serialized_state: &SerializedState<Self>,
210153
) -> Result<Self, DeserializeStateError> {
211154
let mut state = [0u64; STATE_WORDS];
212-
213-
for (dst, chunk) in state.iter_mut().zip(serialized_state.chunks_exact(8)) {
214-
// `chunk` is guaranteed to be 8 bytes long due to `STATE_WORDS` being a multiple of 8
215-
// and `dst` being a slice of 8 bytes
216-
*dst = u64::from_le_bytes(chunk.try_into().map_err(|_| DeserializeStateError)?);
155+
// TODO: use `as_chunks` after MSRV is bumped to 1.88+
156+
for (src, dst) in serialized_state.chunks_exact(8).zip(state.iter_mut()) {
157+
*dst = u64::from_le_bytes(src.try_into().unwrap());
217158
}
218-
219159
Ok(Self {
220160
state,
221-
_variant: core::marker::PhantomData,
161+
_pd: PhantomData,
222162
})
223163
}
224164
}
225-
226-
// Standard Bash hash variants according to section 5.3 and 7.1
227-
// Bash256: ℓ = 128, output = 2ℓ = 256 bits, block = (1536 - 4×128)/8 = 128 bytes
228-
// Bash384: ℓ = 192, output = 2ℓ = 384 bits, block = (1536 - 4×192)/8 = 96 bytes
229-
// Bash512: ℓ = 256, output = 2ℓ = 512 bits, block = (1536 - 4×256)/8 = 64 bytes
230-
pub(crate) type Bash256Core = BashHashCore<Bash256>;
231-
pub(crate) type Bash384Core = BashHashCore<Bash384>;
232-
pub(crate) type Bash512Core = BashHashCore<Bash512>;

bash-hash/src/lib.rs

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,29 +8,30 @@
88
#![warn(missing_docs, unreachable_pub)]
99
#![forbid(unsafe_code)]
1010

11+
use digest::typenum::{U32, U48, U64};
1112
pub use digest::{self, Digest};
1213

1314
/// Block-level types
1415
pub mod block_api;
16+
#[cfg(feature = "oid")]
17+
mod oids;
18+
mod serialize;
1519
mod variants;
1620

17-
digest::buffer_fixed!(
18-
/// BASH256 hasher state.
19-
pub struct BashHash256(block_api::Bash256Core);
20-
oid: "1.2.112.0.2.0.34.101.77.11";
21-
impl: FixedHashTraits;
22-
);
21+
pub use variants::OutputSize;
2322

2423
digest::buffer_fixed!(
25-
/// BASH384 hasher state.
26-
pub struct BashHash384(block_api::Bash384Core);
27-
oid: "1.2.112.0.2.0.34.101.77.12";
28-
impl: FixedHashTraits;
24+
/// `bash-hash` hasher state generic over output size.
25+
pub struct BashHash<OS: OutputSize>(block_api::BashHashCore<OS>);
26+
// note: `SerializableState` is implemented in the `serialize` module
27+
// to work around issues with complex trait bounds
28+
impl: BaseFixedTraits AlgorithmName Default Clone HashMarker
29+
Reset FixedOutputReset ZeroizeOnDrop;
2930
);
3031

31-
digest::buffer_fixed!(
32-
/// BASH512 hasher state.
33-
pub struct BashHash512(block_api::Bash512Core);
34-
oid: "1.2.112.0.2.0.34.101.77.13";
35-
impl: FixedHashTraits;
36-
);
32+
/// `bash-hash-256` hasher state.
33+
pub type BashHash256 = BashHash<U32>;
34+
/// `bash-hash-384` hasher state.
35+
pub type BashHash384 = BashHash<U48>;
36+
/// `bash-hash-512` hasher state.
37+
pub type BashHash512 = BashHash<U64>;

bash-hash/src/oids.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
use digest::const_oid::{AssociatedOid, ObjectIdentifier};
2+
3+
impl AssociatedOid for super::BashHash256 {
4+
const OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.112.0.2.0.34.101.77.11");
5+
}
6+
7+
impl AssociatedOid for super::BashHash384 {
8+
const OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.112.0.2.0.34.101.77.12");
9+
}
10+
11+
impl AssociatedOid for super::BashHash512 {
12+
const OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.112.0.2.0.34.101.77.13");
13+
}

0 commit comments

Comments
 (0)