Skip to content

Commit 0437021

Browse files
committed
bash-prg-hash: fixes according to the review
1 parent 864df08 commit 0437021

7 files changed

Lines changed: 165 additions & 232 deletions

File tree

bash-prg-hash/LICENSE-MIT

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Copyright (c) 2025 The RustCrypto Project Developers
1+
Copyright (c) 2026 The RustCrypto Project Developers
22

33
Permission is hereby granted, free of charge, to any
44
person obtaining a copy of this software and associated

bash-prg-hash/README.md

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

10-
Pure Rust implementation of the bash prg hash function specified in [STB 34.101.77-2020].
11-
12-
## Examples
13-
```rust
14-
use hex_literal::hex;
15-
use bash_prg_hash::{BashPrgHash2561, Digest};
16-
use digest::{Update, ExtendableOutput};
17-
let mut hasher = BashPrgHash2561::default();
18-
hasher.update(b"hello world!");
19-
20-
let mut hash = [0u8; 32];
21-
hasher.finalize_xof_into(&mut hash);
22-
23-
assert_eq!(hash, hex!("0C6B82907AE77386DDF0BA2D7CFDDD99F79A9B0094E545AEF8968A99440F5185"));
24-
25-
// Hex-encode hash using https://docs.rs/base16ct
26-
let hex_hash = base16ct::upper::encode_string(&hash);
27-
assert_eq!(hex_hash, "0C6B82907AE77386DDF0BA2D7CFDDD99F79A9B0094E545AEF8968A99440F5185");
28-
```
29-
30-
Also, see the [examples section] in the RustCrypto/hashes readme.
10+
Pure Rust implementation of the `bash-prg-hash` function specified in [STB 34.101.77-2020].
3111

3212
## License
3313

@@ -46,16 +26,16 @@ dual licensed as above, without any additional terms or conditions.
4626

4727
[//]: # (badges)
4828

49-
[crate-image]: https://img.shields.io/crates/v/belt-hash.svg
50-
[crate-link]: https://crates.io/crates/belt-hash
51-
[docs-image]: https://docs.rs/belt-hash/badge.svg
52-
[docs-link]: https://docs.rs/belt-hash
29+
[crate-image]: https://img.shields.io/crates/v/bash-prg-hash.svg
30+
[crate-link]: https://crates.io/crates/bash-prg-hash
31+
[docs-image]: https://docs.rs/bash-prg-hash/badge.svg
32+
[docs-link]: https://docs.rs/bash-prg-hash
5333
[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg
5434
[rustc-image]: https://img.shields.io/badge/rustc-1.85+-blue.svg
5535
[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg
5636
[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260041-hashes
57-
[build-image]: https://github.com/RustCrypto/hashes/actions/workflows/belt-hash.yml/badge.svg?branch=master
58-
[build-link]: https://github.com/RustCrypto/hashes/actions/workflows/belt-hash.yml?query=branch:master
37+
[build-image]: https://github.com/RustCrypto/hashes/actions/workflows/bash-prg-hash.yml/badge.svg?branch=master
38+
[build-link]: https://github.com/RustCrypto/hashes/actions/workflows/bash-prg-hash.yml?query=branch:master
5939

6040
[//]: # (general links)
6141

bash-prg-hash/src/block_api.rs

Lines changed: 53 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,54 @@
1+
use crate::variants::{Capacity, SecurityLevel};
12
use bash_f::{STATE_WORDS, bash_f};
23
use core::{fmt, marker::PhantomData};
3-
use digest::block_api::BlockSizeUser;
4-
use digest::core_api::AlgorithmName;
5-
6-
use crate::variants::{Cap1, Cap2, Capacity, Level128, Level192, Level256, SecurityLevel};
7-
8-
/// A constant representing the maximum size of a header in bytes.
9-
pub const MAX_HEADER_LEN: usize = 60;
4+
use digest::{
5+
CustomizedInit, block_api::BlockSizeUser, core_api::AlgorithmName, typenum::Unsigned,
6+
};
107

118
/// Data type codes from Table 3 of STB 34.101.77-2020
129
const DATA: u8 = 0b000010;
1310
/// Data type codes from Table 3 of STB 34.101.77-2020
1411
const OUT: u8 = 0b000100;
1512

16-
/// Core bash-prg-hash hasher state generic over security level and capacity.
13+
/// bash-prg-hash hasher state generic over security level and capacity.
1714
///
1815
/// Specified in Section 8.12 of STB 34.101.77-2020.
19-
pub struct BashPrgHashCore<L: SecurityLevel, D: Capacity> {
16+
pub struct BashPrgHashState<L: SecurityLevel, D: Capacity> {
2017
state: [u64; STATE_WORDS],
21-
rate_bytes: usize, // r/8 - buffer size in bytes
22-
offset: usize, // current offset in bytes
23-
header: [u8; MAX_HEADER_LEN], // max header size (480 bits = 60 bytes)
24-
header_len: usize, // header length in bytes
25-
data_committed: bool, // whether commit(DATA) was called in
26-
_level: PhantomData<L>,
27-
_capacity: PhantomData<D>,
18+
/// r/8 - buffer size in bytes
19+
rate_bytes: usize,
20+
/// current offset in bytes
21+
offset: usize,
22+
/// whether commit(DATA) was called
23+
data_committed: bool,
24+
_pd: PhantomData<(L, D)>,
2825
}
2926

3027
macro_rules! impl_block_sizes {
3128
($($level:ty, $cap:ty),* $(,)?) => {
3229
$(
33-
impl BlockSizeUser for BashPrgHashCore<$level, $cap> {
30+
impl BlockSizeUser for BashPrgHashState<$level, $cap> {
3431
type BlockSize = digest::typenum::U<{
35-
(1536 - 2 * <$cap as Capacity>::CAPACITY * <$level as SecurityLevel>::LEVEL) / 8
32+
(1536 - 2 * <$cap as Unsigned>::USIZE * <$level as Unsigned>::USIZE) / 8
3633
}>;
3734
}
3835
)*
3936
};
4037
}
4138

4239
impl_block_sizes! {
43-
Level128, Cap1,
44-
Level192, Cap1,
45-
Level256, Cap1,
46-
Level128, Cap2,
47-
Level192, Cap2,
48-
Level256, Cap2,
40+
digest::typenum::U128, digest::typenum::U1,
41+
digest::typenum::U192, digest::typenum::U1,
42+
digest::typenum::U256, digest::typenum::U1,
43+
digest::typenum::U128, digest::typenum::U2,
44+
digest::typenum::U192, digest::typenum::U2,
45+
digest::typenum::U256, digest::typenum::U2,
4946
}
5047

51-
impl<L: SecurityLevel, D: Capacity> BashPrgHashCore<L, D> {
52-
/// Calculate buffer size r = 1536 - 2dℓ (in bytes)
53-
const fn calculate_rate_bytes() -> usize {
54-
(1536 - 2 * D::CAPACITY * L::LEVEL) / 8
55-
}
48+
impl<L: SecurityLevel, D: Capacity> CustomizedInit for BashPrgHashState<L, D> {
49+
fn new_customized(header: &[u8]) -> Self {
50+
const MAX_HEADER_LEN: usize = 60; // 480 bits = 60 bytes
5651

57-
/// Create a new hasher with an announcement (header).
58-
pub fn new(header: &[u8]) -> Self {
5952
assert!(
6053
header.len() <= MAX_HEADER_LEN,
6154
"Header length must not exceed 480 bits (60 bytes)"
@@ -66,19 +59,24 @@ impl<L: SecurityLevel, D: Capacity> BashPrgHashCore<L, D> {
6659
"Header length must be multiple of 32 bits (4 bytes)"
6760
);
6861

69-
let mut header_buf = [0u8; 60];
70-
header_buf[..header.len()].copy_from_slice(header);
71-
72-
Self {
62+
let mut state = Self {
7363
state: [0u64; STATE_WORDS],
7464
rate_bytes: Self::calculate_rate_bytes(),
7565
offset: 0,
76-
header: header_buf,
77-
header_len: header.len(),
7866
data_committed: false,
79-
_level: PhantomData,
80-
_capacity: PhantomData,
81-
}
67+
_pd: PhantomData,
68+
};
69+
70+
// Immediately consume header during initialization
71+
state.start(header);
72+
state
73+
}
74+
}
75+
76+
impl<L: SecurityLevel, D: Capacity> BashPrgHashState<L, D> {
77+
/// Calculate buffer size r = 1536 - 2dℓ (in bytes)
78+
const fn calculate_rate_bytes() -> usize {
79+
(1536 - 2 * D::USIZE * L::USIZE) / 8
8280
}
8381

8482
/// Helper: modify byte at position in state
@@ -114,9 +112,10 @@ impl<L: SecurityLevel, D: Capacity> BashPrgHashCore<L, D> {
114112
}
115113

116114
/// Execute start command (Section 8.3)
117-
fn start(&mut self) {
115+
fn start(&mut self, header: &[u8]) {
116+
let header_len = header.len();
117+
118118
// Step 3: pos ← 8 + |A| + |K| (in bits) = 1 + header_len (in bytes)
119-
let header_len = self.header_len;
120119
self.offset = 1 + header_len;
121120

122121
// Step 4: S[...pos) ← ⟨|A|/2 + |K|/32⟩_8 || A || K
@@ -125,13 +124,12 @@ impl<L: SecurityLevel, D: Capacity> BashPrgHashCore<L, D> {
125124
self.modify_byte(0, |b| *b = first_byte);
126125

127126
// Copy header bytes
128-
for i in 0..header_len {
129-
let byte = self.header[i];
127+
for (i, &byte) in header.iter().enumerate() {
130128
self.modify_byte(1 + i, |b| *b = byte);
131129
}
132130

133131
// Step 6: S[1472...) ← ⟨ℓ/4 + d⟩_64
134-
self.state[23] = (L::LEVEL / 4 + D::CAPACITY) as u64;
132+
self.state[23] = (L::USIZE / 4 + D::USIZE) as u64;
135133
}
136134

137135
/// Execute commit command (Section 8.4)
@@ -154,17 +152,7 @@ impl<L: SecurityLevel, D: Capacity> BashPrgHashCore<L, D> {
154152

155153
/// Execute absorb command (Section 8.6)
156154
pub(crate) fn absorb(&mut self, data: &[u8]) {
157-
// Check if initialized: state[23] == 0 means not initialized
158-
if self.state[23] == 0 {
159-
self.start();
160-
}
161-
162155
// Step 1: commit(DATA) - only once per absorption session
163-
// We need data_committed because offset == 0 can happen multiple times:
164-
// - After finalize() (need commit(DATA))
165-
// - After commit(DATA) but before absorbing (already did commit)
166-
// - After full block during absorption (offset resets to 0)
167-
// - After empty data calls (offset stays 0)
168156
if !self.data_committed {
169157
self.commit(DATA);
170158
self.data_committed = true;
@@ -188,10 +176,6 @@ impl<L: SecurityLevel, D: Capacity> BashPrgHashCore<L, D> {
188176

189177
/// Prepare for reading output (squeeze)
190178
pub(crate) fn finalize(&mut self) {
191-
if self.state[23] == 0 {
192-
self.start();
193-
}
194-
195179
self.commit(OUT);
196180
self.data_committed = false; // Reset for next absorption session
197181
}
@@ -213,40 +197,37 @@ impl<L: SecurityLevel, D: Capacity> BashPrgHashCore<L, D> {
213197
}
214198
}
215199

216-
impl<L: SecurityLevel, D: Capacity> Clone for BashPrgHashCore<L, D> {
200+
impl<L: SecurityLevel, D: Capacity> Clone for BashPrgHashState<L, D> {
217201
fn clone(&self) -> Self {
218202
Self {
219203
state: self.state,
220204
rate_bytes: self.rate_bytes,
221205
offset: self.offset,
222-
header: self.header,
223-
header_len: self.header_len,
224206
data_committed: self.data_committed,
225-
_level: PhantomData,
226-
_capacity: PhantomData,
207+
_pd: PhantomData,
227208
}
228209
}
229210
}
230211

231-
impl<L: SecurityLevel, D: Capacity> Default for BashPrgHashCore<L, D> {
212+
impl<L: SecurityLevel, D: Capacity> Default for BashPrgHashState<L, D> {
232213
fn default() -> Self {
233-
Self::new(&[])
214+
Self::new_customized(&[])
234215
}
235216
}
236217

237-
impl<L: SecurityLevel, D: Capacity> AlgorithmName for BashPrgHashCore<L, D> {
218+
impl<L: SecurityLevel, D: Capacity> AlgorithmName for BashPrgHashState<L, D> {
238219
fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
239-
write!(f, "BashPrgHash{}-{}", L::LEVEL, D::CAPACITY)
220+
write!(f, "BashPrgHash{}-{}", L::USIZE, D::USIZE)
240221
}
241222
}
242223

243-
impl<L: SecurityLevel, D: Capacity> fmt::Debug for BashPrgHashCore<L, D> {
224+
impl<L: SecurityLevel, D: Capacity> fmt::Debug for BashPrgHashState<L, D> {
244225
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
245-
f.write_str("BashPrgHashCore { ... }")
226+
f.write_str("BashPrgHashState { ... }")
246227
}
247228
}
248229

249-
impl<L: SecurityLevel, D: Capacity> Drop for BashPrgHashCore<L, D> {
230+
impl<L: SecurityLevel, D: Capacity> Drop for BashPrgHashState<L, D> {
250231
fn drop(&mut self) {
251232
#[cfg(feature = "zeroize")]
252233
{
@@ -257,4 +238,4 @@ impl<L: SecurityLevel, D: Capacity> Drop for BashPrgHashCore<L, D> {
257238
}
258239

259240
#[cfg(feature = "zeroize")]
260-
impl<L: SecurityLevel, D: Capacity> digest::zeroize::ZeroizeOnDrop for BashPrgHashCore<L, D> {}
241+
impl<L: SecurityLevel, D: Capacity> digest::zeroize::ZeroizeOnDrop for BashPrgHashState<L, D> {}

0 commit comments

Comments
 (0)