Skip to content

Commit 1f43b9b

Browse files
Adapt test framework, tests and CLI to the trait split, update release notes
1 parent e0df0e6 commit 1f43b9b

15 files changed

Lines changed: 176 additions & 164 deletions

File tree

alpha_0.1.2_release_notes.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@
1717
* Ensure that all crates have `#![forbid(missing_docs)]`
1818
* Apply Secret trait consistently across the library --> study the `Zeroize` trait in RustCrypto
1919
* Change all "[u8;0]" to "[]" throughout the code and docs ... or better yet, change the APIs to take an Option<>
20-
* Change all `-> Vec<u8>` to `-> [u8; CONST_LEN]`, and the `output: &mut [u8]` to `output: &mut [u8; CONST_LEN]` where
21-
appropriate.
20+
* Change the `output: &mut [u8]` to `output: &mut [u8; CONST_LEN]` where appropriate. (The `-> Vec<u8>` half of this
21+
item is done, see changelog. The `*_out` slice parameters were deliberately left as `&mut [u8]` because the
22+
documented truncation / oversized-buffer semantics depend on them; revisit per-API.)
2223
* Probably it makes sense to leave Hex and Base64 as requiring std; ... or maybe add a no_std version that uses
2324
fixed-sized blocks?
2425
* Create a cargo feature #[cfg(feature='rng')] and put it around things like keygen that takes an rng so that the build
@@ -52,6 +53,15 @@
5253

5354
* ML-DSA
5455
* Low-Memory ML-DSA -- runs in about 1/10th of the usual memory (~ 30 kb of stack) with only minor performance impact.
56+
* (Breaking, progress on #14) Removed all heap-allocating `-> Vec<u8>` functions from the `Hash` and `MAC` traits
57+
(`hash`, `do_final`, `do_final_partial_bits`, `mac`). They are replaced by the new `HashFixedOutput<const OUTPUT_LEN>`
58+
and `MACFixedOutput<const OUTPUT_LEN>` traits which return `[u8; OUTPUT_LEN]` stack arrays, following the same
59+
const-generic pattern as the `KEM` and `Signature` traits. All concrete algorithms (SHA2, SHA3, HMAC) implement the
60+
new traits; the factory enums keep only the `*_out` functions because their output length is a runtime property.
61+
The XOF functions (`hash_xof`, `squeeze`) keep returning `Vec<u8>` because their output length is inherently a
62+
runtime parameter.
63+
* HMAC no longer heap-allocates its intermediate inner digest (was an internal `vec!`, now a fixed stack buffer),
64+
and the inner digest buffer is zeroized after use.
5565
* All public `*_out(.., out: &mut [u8])` functions now begin by zeroizing the entire output buffer with `.fill(0)`,
5666
preventing exposure of stale data in oversized output buffers or on early error returns.
5767
* Github issues resolved:

cli/src/mac_cmd.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,14 +63,17 @@ fn do_mac(mut mac: impl MAC, verify_val: &Option<String>, output_hex: bool) {
6363

6464
if verify_val.is_none() {
6565
// compute a MAC value
66-
let out = mac.do_final();
66+
let mut out = [0u8; 64];
67+
let bytes_written =
68+
mac.do_final_out(&mut out).expect("Failed to compute the MAC value");
69+
let out = &out[..bytes_written];
6770

6871
if output_hex {
6972
for b in out.iter() {
7073
print!("{b:02x}");
7174
}
7275
} else {
73-
io::stdout().write(&out).unwrap();
76+
io::stdout().write(out).unwrap();
7477
}
7578
println!();
7679
} else {

cli/src/sha2_cmd.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,14 @@ fn do_sha2(mut sha2: impl Hash, output_hex: bool) {
2424
bytes_read = io::stdin().read(&mut buf).expect("Failed to read from stdin");
2525
}
2626

27-
let out = sha2.do_final();
27+
let mut out = [0u8; 64];
28+
let bytes_written = sha2.do_final_out(&mut out);
29+
let out = &out[..bytes_written];
2830

2931
if output_hex {
3032
for b in out.iter() {
3133
print!("{b:02x}");
3234
}
33-
} else { io::stdout().write(&out).unwrap(); }
35+
} else { io::stdout().write(out).unwrap(); }
3436
println!();
3537
}

cli/src/sha3_cmd.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,15 @@ fn do_sha3(mut sha3: impl Hash, output_hex: bool) {
2525
bytes_read = io::stdin().read(&mut buf).expect("Failed to read from stdin");
2626
}
2727

28-
let out = sha3.do_final();
28+
let mut out = [0u8; 64];
29+
let bytes_written = sha3.do_final_out(&mut out);
30+
let out = &out[..bytes_written];
2931

3032
if output_hex {
3133
for b in out.iter() {
3234
print!("{b:02x}");
3335
}
34-
} else { io::stdout().write(&out).unwrap(); }
36+
} else { io::stdout().write(out).unwrap(); }
3537
println!();
3638
}
3739

crypto/core-test-framework/src/hash.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use bouncycastle_core::traits::{Hash, HashAlgParams};
1+
use bouncycastle_core::traits::{HashAlgParams, HashFixedOutput};
22

33
pub struct TestFrameworkHash {
44
pub enable_partial_final_input_tests: bool,
@@ -13,30 +13,31 @@ impl TestFrameworkHash {
1313
/// This gives good baseline test coverage, but is not exhaustive; for example it does not test
1414
/// do_final_partial_bits() or do_final_partial_bits_out()
1515
/// because those require different input-output pairs.
16-
pub fn test_hash<H: Hash + HashAlgParams + Default>(
16+
pub fn test_hash<H: HashFixedOutput<N> + HashAlgParams + Default, const N: usize>(
1717
&self,
1818
input: &[u8],
1919
expected_output: &[u8],
2020
) {
2121
/*** fn result_len() -> usize ***/
2222
assert_eq!(H::default().output_len(), H::OUTPUT_LEN);
23+
assert_eq!(N, H::OUTPUT_LEN);
2324

24-
/*** fn hash(self, data: &[u8]) -> Vec<u8> **/
25-
let output_vec = H::default().hash(input);
26-
assert_eq!(output_vec, expected_output);
25+
/*** fn hash(self, data: &[u8]) -> [u8; N] **/
26+
let output_arr = H::default().hash(input);
27+
assert_eq!(&output_arr[..], expected_output);
2728

2829
/*** fn hash_out(self, data: &[u8], output: &mut [u8]) -> Result<usize, HashError> ***/
2930
let mut output_buf = vec![0_u8; H::OUTPUT_LEN];
3031
H::default().hash_out(input, &mut output_buf);
3132
assert_eq!(output_buf, expected_output);
3233

3334
/*** fn do_update(&mut self, data: &[u8]) -> Result<(), HashError> ***/
34-
/*** fn do_final(self) -> Result<Vec<u8>, HashError> **/
35+
/*** fn do_final(self) -> [u8; N] **/
3536

3637
let mut message_digest = H::default();
3738
message_digest.do_update(input);
3839
let output_buf = message_digest.do_final();
39-
assert_eq!(expected_output, output_buf, "Incorrect output for input (update_bytes)");
40+
assert_eq!(expected_output, &output_buf[..], "Incorrect output for input (update_bytes)");
4041

4142
for length in 1..output_buf.len() {
4243
let mut truncated = vec![0_u8; length];
@@ -58,7 +59,7 @@ impl TestFrameworkHash {
5859
message_digest.do_update(chunk);
5960
}
6061
let output_buf = message_digest.do_final();
61-
assert_eq!(expected_output, output_buf, "Incorrect output for input (update_bytes)");
62+
assert_eq!(expected_output, &output_buf[..], "Incorrect output for input (update_bytes)");
6263

6364
/*** fn do_update(&mut self, data: &[u8]) -> Result<(), HashError> ***/
6465
/*** fn do_final_out(self, output: &mut [u8]) -> Result<usize, HashError> ***/

crypto/core-test-framework/src/mac.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::DUMMY_SEED_512;
22
use bouncycastle_core::errors::{KeyMaterialError, MACError};
33
use bouncycastle_core::key_material::{KeyMaterial512, KeyType, KeyMaterialTrait};
4-
use bouncycastle_core::traits::MAC;
4+
use bouncycastle_core::traits::MACFixedOutput;
55
use bouncycastle_core::traits::{SecurityStrength};
66

77
pub struct TestFrameworkMAC {
@@ -15,15 +15,15 @@ impl TestFrameworkMAC {
1515

1616
/// Test all the members of trait Hash against the given input-output pair.
1717
/// This gives good baseline test coverage, but is not exhaustive.
18-
pub fn test_mac<M: MAC>(
18+
pub fn test_mac<M: MACFixedOutput<N>, const N: usize>(
1919
&self,
2020
key: &impl KeyMaterialTrait,
2121
input: &[u8],
2222
expected_output: &[u8],
2323
) {
2424
// Test ::mac()
2525
let out = M::new_allow_weak_key(key).unwrap().mac(input);
26-
assert_eq!(out, expected_output);
26+
assert_eq!(&out[..], expected_output);
2727

2828
// Test ::mac_out
2929
let mut out = vec![0u8; expected_output.len()];
@@ -53,7 +53,7 @@ impl TestFrameworkMAC {
5353
let output_len = mac.output_len();
5454
mac.do_update(input);
5555
let out = mac.do_final();
56-
assert_eq!(out, expected_output);
56+
assert_eq!(&out[..], expected_output);
5757

5858
// Test .output_len()
5959
assert_eq!(output_len, out.len());

crypto/core-test-framework/src/signature.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -254,14 +254,16 @@ impl TestFrameworkSignature {
254254

255255
// sign_ph
256256
let (pk, sk) = SigAlg::keygen().unwrap();
257-
let ph: [u8; PH_LEN] = HASH::default().hash(msg)[..PH_LEN].try_into().unwrap();
257+
let mut ph = [0u8; PH_LEN];
258+
HASH::default().hash_out(msg, &mut ph);
258259
let sig_val = SigAlg::sign_ph(&sk, &ph, None).unwrap();
259260
SigAlg::verify(&pk, msg, None, &sig_val).unwrap();
260261
SigAlg::verify_ph(&pk, &ph, None, &sig_val).unwrap();
261262

262263
// sign_ph_out
263264
let (pk, sk) = SigAlg::keygen().unwrap();
264-
let ph: [u8; PH_LEN] = HASH::default().hash(msg)[..PH_LEN].try_into().unwrap();
265+
let mut ph = [0u8; PH_LEN];
266+
HASH::default().hash_out(msg, &mut ph);
265267
let mut sig_val = [0u8; SIG_LEN];
266268
let bytes_written = SigAlg::sign_ph_out(&sk, &ph, None, &mut sig_val).unwrap();
267269
assert_eq!(bytes_written, SIG_LEN);

0 commit comments

Comments
 (0)