Skip to content

Commit 9e3b1cc

Browse files
authored
Tackling the 0.1.2alpha TODO list (#16)
Various small refactoring tasks: * removed nightly feature from mldsa * Split the Signature trait into Signer and SignatureVerifier * Split the KEM trait into KEMEncapsulator and KEMDecapsulator * Touched up some documentation
1 parent e7eafba commit 9e3b1cc

45 files changed

Lines changed: 1085 additions & 540 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

alpha_0.1.2_release_notes.md

Lines changed: 10 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,8 @@
66
* Check the crate release checklist and run claude against the style guide (maybe Francis could cross-check me)
77
* Run Crucible testing
88
* Add factories for ML-DSA and ML-KEM (if we are keeping factories, see below)
9-
* Split the Signature trait into a Signer and a Verifier so that, for example, we can implement the verifier for MTC in
10-
a different struct from the signer; or so that you can get FIPS compliance on old algorithms that are currently only
11-
FIPS-allowed for verification of existing signatures but not for creation of new ones.
129
* Check out Megan's email May 13 about KeyMaterial: "I was wondering if there might be scope for a closure based
13-
approach that could
14-
guarantee encapsulation of the state change from safe to hazardous back to safe again."
10+
approach that could guarantee encapsulation of the state change from safe to hazardous back to safe again."
1511
* Go back to previous algs and apply memory optimization tricks like internal functions. And add a docs section "Memory
1612
Usage" that measures with valgrind.
1713
* Ensure that all crates have `#![forbid(missing_docs)]`
@@ -21,40 +17,27 @@
2117
appropriate.
2218
* Probably it makes sense to leave Hex and Base64 as requiring std; ... or maybe add a no_std version that uses
2319
fixed-sized blocks?
20+
* Make this build on the stable compiler. IE Remove the rust-toolchain.toml file that builds with nightly. Will require
21+
some refactoring.
2422
* Create a cargo feature #[cfg(feature='rng')] and put it around things like keygen that takes an rng so that the build
2523
dependency on bouncycastle_rng is optional.
26-
* Enhance the default HashDRBG instantiation to take in NIST-compatible CPU jitter entropy? Or not? Maybe this is the
27-
problem of the caller to properly seed the RNG?
2824
* Factories ... Are they worth it? Michael Richardson says Very Yes. If we are keeping them, then we need a serious
2925
re-engineering of them because I really dislike that currently they make it hard for the underlying primitive to have
3026
static one-shot APIs.
31-
* Add back the Memoable trait from nursery (maybe under a different name) that lets you serialize out the
32-
intermediate state, especially important for SHA2, SHA3, and HMAC because TLS needs to be able to fork a state,
33-
finalize() a copy and then keep feeding the other copy.
34-
* Do some science about perf impacts of acting on a local hard-copy vs acting in-place on some specific bit of
35-
memory
36-
* Change the tone of the documentation (both the crate docs and the inline comments) to be less individual ("I"
37-
statements) and be more factual ("it is", or "the project", or "the bc-rust library" as appropriate).
38-
* Relax the requirement on XOF that once you start squeezing, you can't absorb anymore. This will likely need to be an
39-
exposed "bell & whistle" because it is an obvious way to do something like the TLS handshake transcript where you need
40-
to periodically spit out hashes and then continue absorbing more input. We'll need to study the SHA3 / SHAKE FIPS
41-
documents because it might be that this is forbidden as part of the definition of SHAKE, but is allowed if you use the
42-
KECCAK primitive raw. We need to make a decision on how to handle this, and provide some sample code in crate docs.
43-
* Need a rust expert: I use a bunch of #![feature(_)]'s that are only available in nightly. ... what should I do
44-
about that?
45-
* Research task: no_std means that everything is on the stack, which can cause you to blow your stack limit. Research
46-
how an application that itself is not no_std can put our large structs (like key objects) on the heap. Is this what
47-
Box is for?
4827
* Deal with as many of the inline TODOs as possible
4928
* Close all open github issues and document them in this file.
5029
* After everything is merged, circle back to crucible, and make sure that the harness still works (and maybe remove the
5130
nightly build toolchain)
5231

5332
# 0.1.2 Features / Changelog
5433

55-
* ML-DSA
56-
* Low-Memory ML-DSA -- runs in about 1/10th of the usual memory (~ 30 kb of stack) with only minor performance impact.
34+
* New algorithms added to crypto/ :
35+
* mldsa (FIPS 204)
36+
* mldsa-lowmemory -- runs in about 1/10th of the usual memory (~ 30 kb of stack) with comparable performance impact.
37+
* mlkem (FIPS 203)
38+
* mlkem-lowmemory -- runs in about 1/4th of the usual memory (~ 12 kb of stack) with comparable performance impact.
5739
* All public `*_out(.., out: &mut [u8])` functions now begin by zeroizing the entire output buffer with `.fill(0)`,
5840
preventing exposure of stale data in oversized output buffers or on early error returns.
5941
* Github issues resolved:
60-
* #2, or whatever
42+
* #6: https://github.com/bcgit/bc-rust/issues/6, thanks to Q. T. Felix (github: @Quant-TheodoreFelix)
43+
* #10: https://github.com/bcgit/bc-rust/issues/10, thanks to Nicola Tuveri (github: @romen)

cli/src/mldsa_cmd.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
//! by using generics or macros. I just, haven't ... yet.
33
44
use crate::helpers::{parse_seed, read_from_file, read_from_file_or_stdin, write_bytes_or_hex};
5-
use bouncycastle::core::traits::{Signature, SignaturePrivateKey, SignaturePublicKey};
5+
use bouncycastle::core::traits::{
6+
SignaturePrivateKey, SignaturePublicKey, SignatureVerifier, Signer,
7+
};
68
use bouncycastle::hex;
79
use bouncycastle::mldsa::{
810
HashMLDSA44_with_SHA512, HashMLDSA65_with_SHA512, HashMLDSA87_with_SHA512, MLDSA_SEED_LEN,

cli/src/mlkem_cmd.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::helpers::{
66
write_bytes_or_hex_to_file,
77
};
88
use bouncycastle::core::key_material::KeyMaterialTrait;
9-
use bouncycastle::core::traits::{KEM, KEMPrivateKey, KEMPublicKey};
9+
use bouncycastle::core::traits::{KEMDecapsulator, KEMEncapsulator, KEMPrivateKey, KEMPublicKey};
1010
use bouncycastle::hex;
1111
use bouncycastle::mlkem::{
1212
MLKEM512, MLKEM512_CT_LEN, MLKEM512_PK_LEN, MLKEM512_SK_LEN, MLKEM512PrivateKey,

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

Lines changed: 29 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use bouncycastle_core::errors::KEMError;
2-
use bouncycastle_core::traits::{KEM, KEMPrivateKey, KEMPublicKey};
2+
use bouncycastle_core::traits::{KEMDecapsulator, KEMEncapsulator, KEMPrivateKey, KEMPublicKey};
33

44
pub struct TestFrameworkKEM {
55
// Put any config options here
@@ -16,43 +16,48 @@ impl TestFrameworkKEM {
1616
Self { alg_is_deterministic, is_implicitly_rejecting }
1717
}
1818

19-
/// Test all the members of trait Hash against the given input-output pair.
19+
/// Test all the members of traits [KEMEncapsulator] and [KEMDecapsulator] against the given input-output pair.
2020
/// This gives good baseline test coverage, but is not exhaustive.
21+
///
22+
/// Since key generation is not part of either KEM trait, the caller supplies a
23+
/// `keygen` function pointer (the inherent `keygen` associated function on the algorithm struct).
2124
pub fn test_kem<
2225
PK: KEMPublicKey<PK_LEN>,
2326
SK: KEMPrivateKey<SK_LEN>,
24-
KEMAlg: KEM<PK, SK, PK_LEN, SK_LEN, CT_LEN, SS_LEN>,
27+
ENCAPSULATOR: KEMEncapsulator<PK, PK_LEN, CT_LEN, SS_LEN>,
28+
DECAPSULATOR: KEMDecapsulator<SK, SK_LEN, CT_LEN, SS_LEN>,
2529
const PK_LEN: usize,
2630
const SK_LEN: usize,
2731
const CT_LEN: usize,
2832
const SS_LEN: usize,
2933
>(
3034
&self,
35+
keygen: fn() -> Result<(PK, SK), KEMError>,
3136
run_full_bitflipping_tests: bool,
3237
) {
3338
// Basic test
34-
let (pk, sk) = KEMAlg::keygen().unwrap();
35-
let (ss, ct) = KEMAlg::encaps(&pk).unwrap();
36-
let ss1 = KEMAlg::decaps(&sk, &ct).unwrap();
39+
let (pk, sk) = keygen().unwrap();
40+
let (ss, ct) = ENCAPSULATOR::encaps(&pk).unwrap();
41+
let ss1 = DECAPSULATOR::decaps(&sk, &ct).unwrap();
3742
assert_eq!(ss, ss1);
3843

3944
// Test non-determinism
4045
if !self.alg_is_deterministic {
41-
let (ss1, ct1) = KEMAlg::encaps(&pk).unwrap();
42-
let (ss2, ct2) = KEMAlg::encaps(&pk).unwrap();
46+
let (ss1, ct1) = ENCAPSULATOR::encaps(&pk).unwrap();
47+
let (ss2, ct2) = ENCAPSULATOR::encaps(&pk).unwrap();
4348
assert_ne!(ss1, ss2);
4449
assert_ne!(ct1, ct2);
4550
}
4651

4752
// Test that decaps fails for broken ct value
48-
let (pk, sk) = KEMAlg::keygen().unwrap();
49-
let (ss, mut ct) = KEMAlg::encaps(&pk).unwrap();
53+
let (pk, sk) = keygen().unwrap();
54+
let (ss, mut ct) = ENCAPSULATOR::encaps(&pk).unwrap();
5055
ct[17] ^= 0xFF;
5156
if self.is_implicitly_rejecting {
52-
let ss2 = KEMAlg::decaps(&sk, &ct).unwrap();
57+
let ss2 = DECAPSULATOR::decaps(&sk, &ct).unwrap();
5358
assert_ne!(ss, ss2);
5459
} else {
55-
match KEMAlg::decaps(&sk, &ct) {
60+
match DECAPSULATOR::decaps(&sk, &ct) {
5661
Err(KEMError::DecapsulationFailed) =>
5762
/* good */
5863
{
@@ -71,10 +76,10 @@ impl TestFrameworkKEM {
7176

7277
// should throw an Err
7378
if self.is_implicitly_rejecting {
74-
let ss2 = KEMAlg::decaps(&sk, &ct_copy).unwrap();
79+
let ss2 = DECAPSULATOR::decaps(&sk, &ct_copy).unwrap();
7580
assert_ne!(ss, ss2);
7681
} else {
77-
match KEMAlg::decaps(&sk, &ct) {
82+
match DECAPSULATOR::decaps(&sk, &ct) {
7883
Err(KEMError::DecapsulationFailed) =>
7984
/* good */
8085
{
@@ -88,19 +93,18 @@ impl TestFrameworkKEM {
8893
}
8994

9095
// test ct the wrong length
91-
let (pk, sk) = KEMAlg::keygen().unwrap();
92-
let (_ss, ct) = KEMAlg::encaps(&pk).unwrap();
93-
96+
let (pk, sk) = keygen().unwrap();
97+
let (_ss, ct) = ENCAPSULATOR::encaps(&pk).unwrap();
9498
// too short
95-
match KEMAlg::decaps(&sk, &ct[..CT_LEN - 1]) {
99+
match DECAPSULATOR::decaps(&sk, &ct[..CT_LEN - 1]) {
96100
Err(KEMError::LengthError(_)) => { /* good */ }
97101
_ => panic!("This should have thrown an error but it didn't."),
98102
};
99103

100104
// too long
101105
let mut long_ct = vec![1u8; CT_LEN + 2];
102106
long_ct.as_mut_slice()[..CT_LEN].copy_from_slice(&ct);
103-
match KEMAlg::decaps(&sk, &long_ct) {
107+
match DECAPSULATOR::decaps(&sk, &long_ct) {
104108
Err(KEMError::LengthError(_)) => { /* good */ }
105109
_ => panic!("This should have thrown an error but it didn't."),
106110
};
@@ -114,33 +118,31 @@ impl TestFrameworkKEMKeys {
114118
Self {}
115119
}
116120

121+
/// Since key generation is not part of either KEM trait, the caller supplies a
122+
/// `keygen` function pointer (the inherent `keygen` associated function on the algorithm struct).
117123
pub fn test_keys<
118124
PK: KEMPublicKey<PK_LEN>,
119125
SK: KEMPrivateKey<SK_LEN>,
120-
KEMAlg: KEM<PK, SK, PK_LEN, SK_LEN, CT_LEN, SS_LEN>,
121126
const PK_LEN: usize,
122127
const SK_LEN: usize,
123-
const CT_LEN: usize,
124-
const SS_LEN: usize,
125128
>(
126129
&self,
130+
keygen: fn() -> Result<(PK, SK), KEMError>,
127131
) {
128-
self.test_boundary_conditions::<PK, SK, KEMAlg, PK_LEN, SK_LEN, CT_LEN, SS_LEN>();
132+
self.test_boundary_conditions::<PK, SK, PK_LEN, SK_LEN>(keygen);
129133
}
130134

131135
/// Tests the correct behaviour on buffers too large / too small.
132136
fn test_boundary_conditions<
133137
PK: KEMPublicKey<PK_LEN>,
134138
SK: KEMPrivateKey<SK_LEN>,
135-
KEMAlg: KEM<PK, SK, PK_LEN, SK_LEN, CT_LEN, SS_LEN>,
136139
const PK_LEN: usize,
137140
const SK_LEN: usize,
138-
const CT_LEN: usize,
139-
const SS_LEN: usize,
140141
>(
141142
&self,
143+
keygen: fn() -> Result<(PK, SK), KEMError>,
142144
) {
143-
let (pk, sk) = KEMAlg::keygen().unwrap();
145+
let (pk, sk) = keygen().unwrap();
144146

145147
let pk_bytes = pk.encode();
146148
assert_eq!(pk_bytes.len(), PK_LEN);

0 commit comments

Comments
 (0)