Skip to content
12 changes: 9 additions & 3 deletions alpha_0.1.2_release_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
appropriate.
* Probably it makes sense to leave Hex and Base64 as requiring std; ... or maybe add a no_std version that uses
fixed-sized blocks?
* Make this build on the stable compiler. IE Remove the rust-toolchain.toml file that builds with nightly. Will require
some refactoring.
* Create a cargo feature #[cfg(feature='rng')] and put it around things like keygen that takes an rng so that the build
dependency on bouncycastle_rng is optional.
* Enhance the default HashDRBG instantiation to take in NIST-compatible CPU jitter entropy? Or not? Maybe this is the
Expand Down Expand Up @@ -52,9 +54,13 @@

# 0.1.2 Features / Changelog

* ML-DSA
* Low-Memory ML-DSA -- runs in about 1/10th of the usual memory (~ 30 kb of stack) with only minor performance impact.
* New algorithms added to crypto/ :
* mldsa (FIPS 204)
* mldsa-lowmemory -- runs in about 1/10th of the usual memory (~ 30 kb of stack) with comparable performance impact.
* mlkem (FIPS 203)
* mlkem-lowmemory -- runs in about 1/4th of the usual memory (~ 12 kb of stack) with comparable performance impact.
* All public `*_out(.., out: &mut [u8])` functions now begin by zeroizing the entire output buffer with `.fill(0)`,
preventing exposure of stale data in oversized output buffers or on early error returns.
* Github issues resolved:
* #2, or whatever
* #6: https://github.com/bcgit/bc-rust/issues/6, thanks to Q. T. Felix (github: @Quant-TheodoreFelix)
* #10: https://github.com/bcgit/bc-rust/issues/10, thanks to Nicola Tuveri (github: @romen)
2 changes: 1 addition & 1 deletion cli/src/mldsa_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//! by using generics or macros. I just, haven't ... yet.

use crate::helpers::{parse_seed, read_from_file, read_from_file_or_stdin, write_bytes_or_hex};
use bouncycastle::core::traits::{Signature, SignaturePrivateKey, SignaturePublicKey};
use bouncycastle::core::traits::{SignatureVerifier, Signer, SignaturePrivateKey, SignaturePublicKey};
use bouncycastle::hex;
use bouncycastle::mldsa::{
HashMLDSA44_with_SHA512, HashMLDSA65_with_SHA512, HashMLDSA87_with_SHA512, MLDSA_SEED_LEN,
Expand Down
2 changes: 1 addition & 1 deletion cli/src/mlkem_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::helpers::{
write_bytes_or_hex_to_file,
};
use bouncycastle::core::key_material::KeyMaterialTrait;
use bouncycastle::core::traits::{KEM, KEMPrivateKey, KEMPublicKey};
use bouncycastle::core::traits::{KEMDecapsulator, KEMEncapsulator, KEMPrivateKey, KEMPublicKey};
use bouncycastle::hex;
use bouncycastle::mlkem::{
MLKEM512, MLKEM512_CT_LEN, MLKEM512_PK_LEN, MLKEM512_SK_LEN, MLKEM512PrivateKey,
Expand Down
56 changes: 29 additions & 27 deletions crypto/core-test-framework/src/kem.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use bouncycastle_core::errors::KEMError;
use bouncycastle_core::traits::{KEM, KEMPrivateKey, KEMPublicKey};
use bouncycastle_core::traits::{KEMDecapsulator, KEMEncapsulator, KEMPrivateKey, KEMPublicKey};

pub struct TestFrameworkKEM {
// Put any config options here
Expand All @@ -16,43 +16,48 @@ impl TestFrameworkKEM {
Self { alg_is_deterministic, is_implicitly_rejecting }
}

/// Test all the members of trait Hash against the given input-output pair.
/// Test all the members of traits [KEMEncapsulator] and [KEMDecapsulator] against the given input-output pair.
/// This gives good baseline test coverage, but is not exhaustive.
///
/// Since key generation is not part of either KEM trait, the caller supplies a
/// `keygen` function pointer (the inherent `keygen` associated function on the algorithm struct).
pub fn test_kem<
PK: KEMPublicKey<PK_LEN>,
SK: KEMPrivateKey<SK_LEN>,
KEMAlg: KEM<PK, SK, PK_LEN, SK_LEN, CT_LEN, SS_LEN>,
ENCAPSULATOR: KEMEncapsulator<PK, PK_LEN, CT_LEN, SS_LEN>,
DECAPSULATOR: KEMDecapsulator<SK, SK_LEN, CT_LEN, SS_LEN>,
const PK_LEN: usize,
const SK_LEN: usize,
const CT_LEN: usize,
const SS_LEN: usize,
>(
&self,
keygen: fn() -> Result<(PK, SK), KEMError>,
run_full_bitflipping_tests: bool,
) {
// Basic test
let (pk, sk) = KEMAlg::keygen().unwrap();
let (ss, ct) = KEMAlg::encaps(&pk).unwrap();
let ss1 = KEMAlg::decaps(&sk, &ct).unwrap();
let (pk, sk) = keygen().unwrap();
let (ss, ct) = ENCAPSULATOR::encaps(&pk).unwrap();
let ss1 = DECAPSULATOR::decaps(&sk, &ct).unwrap();
assert_eq!(ss, ss1);

// Test non-determinism
if !self.alg_is_deterministic {
let (ss1, ct1) = KEMAlg::encaps(&pk).unwrap();
let (ss2, ct2) = KEMAlg::encaps(&pk).unwrap();
let (ss1, ct1) = ENCAPSULATOR::encaps(&pk).unwrap();
let (ss2, ct2) = ENCAPSULATOR::encaps(&pk).unwrap();
assert_ne!(ss1, ss2);
assert_ne!(ct1, ct2);
}

// Test that decaps fails for broken ct value
let (pk, sk) = KEMAlg::keygen().unwrap();
let (ss, mut ct) = KEMAlg::encaps(&pk).unwrap();
let (pk, sk) = keygen().unwrap();
let (ss, mut ct) = ENCAPSULATOR::encaps(&pk).unwrap();
ct[17] ^= 0xFF;
if self.is_implicitly_rejecting {
let ss2 = KEMAlg::decaps(&sk, &ct).unwrap();
let ss2 = DECAPSULATOR::decaps(&sk, &ct).unwrap();
assert_ne!(ss, ss2);
} else {
match KEMAlg::decaps(&sk, &ct) {
match DECAPSULATOR::decaps(&sk, &ct) {
Err(KEMError::DecapsulationFailed) =>
/* good */
{
Expand All @@ -71,10 +76,10 @@ impl TestFrameworkKEM {

// should throw an Err
if self.is_implicitly_rejecting {
let ss2 = KEMAlg::decaps(&sk, &ct_copy).unwrap();
let ss2 = DECAPSULATOR::decaps(&sk, &ct_copy).unwrap();
assert_ne!(ss, ss2);
} else {
match KEMAlg::decaps(&sk, &ct) {
match DECAPSULATOR::decaps(&sk, &ct) {
Err(KEMError::DecapsulationFailed) =>
/* good */
{
Expand All @@ -88,19 +93,18 @@ impl TestFrameworkKEM {
}

// test ct the wrong length
let (pk, sk) = KEMAlg::keygen().unwrap();
let (_ss, ct) = KEMAlg::encaps(&pk).unwrap();

let (pk, sk) = keygen().unwrap();
let (_ss, ct) = ENCAPSULATOR::encaps(&pk).unwrap();
// too short
match KEMAlg::decaps(&sk, &ct[..CT_LEN - 1]) {
match DECAPSULATOR::decaps(&sk, &ct[..CT_LEN - 1]) {
Err(KEMError::LengthError(_)) => { /* good */ }
_ => panic!("This should have thrown an error but it didn't."),
};

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

/// Since key generation is not part of either KEM trait, the caller supplies a
/// `keygen` function pointer (the inherent `keygen` associated function on the algorithm struct).
pub fn test_keys<
PK: KEMPublicKey<PK_LEN>,
SK: KEMPrivateKey<SK_LEN>,
KEMAlg: KEM<PK, SK, PK_LEN, SK_LEN, CT_LEN, SS_LEN>,
const PK_LEN: usize,
const SK_LEN: usize,
const CT_LEN: usize,
const SS_LEN: usize,
>(
&self,
keygen: fn() -> Result<(PK, SK), KEMError>,
) {
self.test_boundary_conditions::<PK, SK, KEMAlg, PK_LEN, SK_LEN, CT_LEN, SS_LEN>();
self.test_boundary_conditions::<PK, SK, PK_LEN, SK_LEN>(keygen);
}

/// Tests the correct behaviour on buffers too large / too small.
fn test_boundary_conditions<
PK: KEMPublicKey<PK_LEN>,
SK: KEMPrivateKey<SK_LEN>,
KEMAlg: KEM<PK, SK, PK_LEN, SK_LEN, CT_LEN, SS_LEN>,
const PK_LEN: usize,
const SK_LEN: usize,
const CT_LEN: usize,
const SS_LEN: usize,
>(
&self,
keygen: fn() -> Result<(PK, SK), KEMError>,
) {
let (pk, sk) = KEMAlg::keygen().unwrap();
let (pk, sk) = keygen().unwrap();

let pk_bytes = pk.encode();
assert_eq!(pk_bytes.len(), PK_LEN);
Expand Down
Loading
Loading