Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ These are non-obvious house rules — follow them when writing or modifying code
- `cargo mutants` is expected to be run on each crate; surviving mutants must be investigated but not all need to die (e.g. XOR/OR equivalences in crypto code are acceptable). Config lives in `.cargo/mutants.toml` (output dir `custom_mutants_output/`).
- Behaviour-critical private functions can use in-file `#[cfg(test)] mod tests` blocks when they can't be exercised from outside the crate.
- For traits in `core`, the canonical tests live in `core-test-framework` and are invoked from each implementor's integration tests — don't duplicate them per-implementation.
- The per-width `impl Condition<W>` blocks in `crypto/utils/src/ct.rs` (and their test modules) are deliberately duplicated rather than macro-generated: `cargo mutants` cannot see into `macro_rules!` bodies, so a macro would hide the mask identities from mutation testing. Do not fold them back into a macro. Any change to one width in a group (i64/i32, u64/u32) must be applied to every width in that group.

## CI

Expand Down
2 changes: 1 addition & 1 deletion cli/src/mldsa_cmd.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! Work in progress.
//! Work in progress.
//! TODO: Use generic macros to eliminate duplicated code.

use crate::helpers::{parse_seed, read_from_file, read_from_file_or_stdin, write_bytes_or_hex};
Expand Down
2 changes: 1 addition & 1 deletion crypto/core/src/key_material.rs
Original file line number Diff line number Diff line change
Expand Up @@ -547,7 +547,7 @@ impl<const KEY_LEN: usize> KeyMaterialTrait for KeyMaterial<KEY_LEN> {
}

self.security_strength = strength;

Ok(())
}

Expand Down
2 changes: 1 addition & 1 deletion crypto/factory/src/rng_factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
//! let output: Vec<u8> = h.hash(data);
//! ```
//! Equivalently, it may be invoked by passing a string instead of using the constant:
//!
//!
//! ```
//! use bouncycastle_factory::AlgorithmFactory;
//! use bouncycastle_core::traits::Hash;
Expand Down
12 changes: 6 additions & 6 deletions crypto/hex/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
//! This one is implemented using constant-time operations in the conversions
//! from Strings to byte values, so it is safe to use on cryptographic secret values.
//!
//! It should just work as expected:
//! encode takes any bytes-like rust type and returns a String,
//! It should just work as expected:
//! encode takes any bytes-like rust type and returns a String,
//! decode takes a String (which can be in any bytes-like container) and returns a `Vec<u8>`.
//!
//! Moreover, the API of this crate is intended to mirror that of the public `hex` crate,
//! so you should generally be able to swap `use hex` for `use bouncycastle_hex` and all the function
//! calls and behaviours should work as expected.
//!
//!
//! ```
//! use bouncycastle_hex as hex;
//!
Expand Down Expand Up @@ -72,7 +72,7 @@ pub fn encode_out<T: AsRef<[u8]>>(input: T, out: &mut [u8]) -> Result<usize, Hex
// let in_09 = Condition::<i64>::is_within_range(c as i64, 0, 9);
let in_af = Condition::<i64>::is_within_range(c as i64, 10, 15);

// TODO: redo this once we have ct::u8 implemented
// TODO: redo this once we have ct::u8 implemented
// The i64 is wasteful

let c_09: i64 = '0' as i64 + (c as i64);
Expand Down Expand Up @@ -111,7 +111,7 @@ pub fn decode_out<T: AsRef<[u8]>>(input: T, out: &mut [u8]) -> Result<usize, Hex
while i < inref.len() {
let c = inref[i];

// first check for whitespace and string null terminators, \x and invalid characters,
// first check for whitespace and string null terminators, \x and invalid characters,
// which unfortunately cannot be done fully constant-time.
match c {
b' ' | b'\t' | b'\n' | b'\r' | 0 => {
Expand Down Expand Up @@ -157,7 +157,7 @@ pub fn decode_out<T: AsRef<[u8]>>(input: T, out: &mut [u8]) -> Result<usize, Hex
#[allow(non_snake_case)]
let in_AF = Condition::<i64>::is_within_range(b as i64, 65, 70);

// TODO: redo this once we have ct::u8 implemented
// TODO: redo this once we have ct::u8 implemented
// The i64 is wasteful

let c_09: i64 = b as i64 - ('0' as i64);
Expand Down
4 changes: 2 additions & 2 deletions crypto/mldsa-lowmemory/src/aux_functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,7 @@ pub(crate) fn sample_in_ball<const LAMBDA_over_4: usize, const TAU: i32>(
let mut j = [0u8];
for i in (N - TAU as usize)..N {
// 7: (ctx, 𝑗) ← H.Squeeze(ctx, 1)
// Note: At first, it might seem to be faster to pre-squeeze a buffer outside the loop.
// Note: At first, it might seem to be faster to pre-squeeze a buffer outside the loop.
// However, after experimentation and testing, the difference is not noticeable.
h.squeeze_out(&mut j);

Expand Down Expand Up @@ -567,7 +567,7 @@ pub(crate) fn rej_bounded_poly<const ETA: usize>(rho: &[u8; 64], nonce: &[u8; 2]
// SHAKE is fairly inefficient if only 3 bytes are squeezed at a time, so the implementation does a block instead.
// size is not a limitation as long as it is a multiple of 3.
// 312 seems to be the sweet spot after some experimentation
// which is possibly also related with the average rejection rate.
// which is possibly also related with the average rejection rate.
// Also, 312 is a multiple of 8 (efficient for SHAKE)
let mut z_arr = [0u8; 312];
h.squeeze_out(&mut z_arr);
Expand Down
2 changes: 1 addition & 1 deletion crypto/mldsa-lowmemory/src/polynomial.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ impl Polynomial {

pub(crate) fn check_norm<const BOUND: i32>(&self) -> bool {
// Fine that this is not constant-time (returns true early) because it is used in a rejection loop.
// IE the early quit here leads to rejection and continuing to the top of the rejection loop, or failing
// IE the early quit here leads to rejection and continuing to the top of the rejection loop, or failing
// the signature validation.
// So the i32 that was just checked in a non-constant-time manner is about to get thrown away.

Expand Down
2 changes: 1 addition & 1 deletion crypto/mldsa/src/hash_mldsa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1097,7 +1097,7 @@ impl<

/// Note that the PH expected here *is not the same* as the `mu` computed by [`MuBuilder`].
/// To make use of this function, the user needs to compute a straight hash of the message using
/// the same hash function as the indicated in the HashML-DSA variant;
/// the same hash function as the indicated in the HashML-DSA variant;
/// for example: SHA256 for HashMDSA44_with_SHA256; SHA512 for HashMLDSA65_with_SHA512; etc.
fn sign_ph_out(
sk: &SK,
Expand Down
3 changes: 1 addition & 2 deletions crypto/mldsa/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,11 +116,10 @@
//!
//! `mu`, `ph`, and `ctx` are binding values that the verifier must reproduce. This means that getting
//! them wrong does not compromise security, it just yields a signature the intended
//! verifier won't accept (a correctness/interoperability failure).
//! verifier won't accept (a correctness/interoperability failure).
//! One caveat: `ctx` can still be security-relevant at the protocol level (domain separation, replay and
//! cross-protocol binding), so choosing it incorrectly can weaken those properties.


#![no_std]
#![forbid(unsafe_code)]
#![forbid(missing_docs)]
Expand Down
12 changes: 6 additions & 6 deletions crypto/mldsa/tests/mldsa_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1083,9 +1083,9 @@ struct Kat {
}

// generated by hand against bc-java
// This is almost tgId=1, tcId=1 from
// This is almost tgId=1, tcId=1 from
// https://raw.githubusercontent.com/bcgit/bc-test-data/refs/heads/main/pqc/crypto/mldsa/ML-DSA-sigGen.txt
// with the difference being that an empty ctx is added, instead of testing sign_internal directly,
// with the difference being that an empty ctx is added, instead of testing sign_internal directly,
// and the signature value is run against bc-java
const MLDSA44_KAT1: Kat = Kat {
_parameter_set: "ML-DSA-44",
Expand All @@ -1096,9 +1096,9 @@ const MLDSA44_KAT1: Kat = Kat {
signature: "2bddcee4a9ac1b9d19bc1531365c5613e48b95a530339c52f5fc0b671ee01b6587fe08b290c191f82eace640fae216cca90f40fa93bb309e5ff53afc5a042050bffeb69d4e0041c34fd334a7b576c6ebae68042b315fe78f84300dc011cdb144252a06f35c9a2b0a275d00552f890361d1e7b7439572097d1f3d5833b98b751412deb3c0df23ee3a30782919444bcbcc9ac25c645180c8d1a9fe693086cba4779d6e31b1e5fd1a0ccaeb055c471ef065a273676d78c6e7feebfc607c5578107ac27765345363af57f77431e306d9407ce365708d813e44d3107c108f8c5f2848913a4099f6a0ce2ebf80c7414236d4edc07fee79efe1ab6fe70217bec958ea48221fb6b87cef6f5762b41a9c698fbb45f5994969bee21e40f20c95f9a18389e8b49d6ecccdae9f568313448b5162c981bc9ff320753aede977b15f7ddc68bba076a07deb68ac36826e70d80752fac0356d507b3e283b350a17b015f76c71314f7c2086e44601c4d9c707c99a36e55716fd34384a218242a69876c8dea9f6ec28c40189ee2b8608040a96a813f38240dc85a511b3cebab1ea893270e730aef2e29406fcc1f6826101f1361168ceb3f1632e8ee505143e0f031e744928c1e41eab923ebbfce5e9f159fd160db737b013759382274a1d9409554ab06eb72b5ce2a710d8b08f163df991c956abc823d21b0a6d9d7ae484d6cdb2e04de7a282ef317f488e20d40e4e67cbf05d1528c0ce261e3a65163ad518661989484bc964da21122a6c95ef7036b3273f93833d068d9a2c7933e19ad2fde8378c286ad57f7e22c8aa4153718f356b46b34ca4a986e6e9ef3ddb206693acf3b08c0aa5118c8efbb6a99a0fc0674f6228e2f6f53e229129d4b6c0fd6455e28587f0f169270d044398e2c377ab6f985f7f2235d68192d031f39251f9f0270451beda2d29b654511b73bce0f30174a606a1100f4fc984959704ba0e2df2bdf642ba3b0241ca9889009ae575540c71747246835018ba8faeb473b39e4ede5a6fbe0165a4db2e90739cb051f3e5c1c0ce159539758569ecc8b67a759a2e786d5b4e96834db0dd460ab1c5c7ac8f3cf19596979108dfbdc6c8798e7342026b7571ba64fa86d68e1d5179af5768a7e22db76f3a319193d04eeea626d03d4be7dd83ed6c46e2149a1e2428e60f6566833726bcc93d0b342ff4245665b54167b013b02165d29fb64055112c5d800e853bf28bf149da2284e49dc1c5f478f490ce1002f16431564534244cd9c1e0bd12ea9209efe572afb7effc0153e5b2066b1af0dd05a2d9da13308c90a0244ad95a33a64d07a75d7596e46cdcfd154e18f7677ef16b979a1f431eb59279c65a3a31c5429ceebfcc869cf538722a82e6e765bd6a3042dc825684ad4163e054c113b47276b894580db17c92f0859c240b6812716e408a557c1d7ac8cfc3abf9b43f759bb2a13c903bba936206496fed37696171717cb8995b837e869052c9c25348698617c70e98966d30f56bb5bf41369c0131de4178637c5684a64992b50f9ab9a16b51752e14ea521d5bd42bacdfcf100085629796d22bcaa6a7830f9b5859d75b290a0db031bd51b77fa7911eb17d46dcf16d45e3b1ce8fb7dc19ba969a724e43ccce9487c143302c79fb208b2ab0ea1060af3809d5397c11cc0a79ed39ab06c0ef7bceb8e094ec0e3ea52937442ccc176a31b07a52d7de84c0599dbe736cecfacbb41cac206ec3dc20603606a14a1db3ccad8037a540f3213ee9337b529f447ed2c2e287760973b8175f70c78423ace6179757c519c14a0d7b81af00646afde573a35e909ef9660623cd3651b63b79be508698888e1d3bc87344067dcc099416616c273d5ea0e41a8723d11c5ec49e0c6019f6576a2e87d0237c3f64d49afbaf09fc818829d5d0fcb4336e08503d570dc3c96fa71fd7b2d4f51d0d1acc515c2c4dad23896273b2616f7d848816c5cc17f7bb2ed5f952a708c38eb13f0fdd8f14ec71893d3f19ccc8aa15fed848583093f4d8246f36db300c9cdd5d63b507641e4d2ba9ec284d17f4a05696be00da371bd5bab0ba56a13451278dbdeed02b4093e482b96269707b09a10dba9ca1dffb4e4185f9e392ebabcea1f9d26c037d9c0dcd71f2bc13faf559d195ef0d8be7baa0cf8aa105a8069356aa2287ec1003e0759f27b02772d4855bf0b7e0b3e97b7f77bd3119669695cc706090991e6a542eb7a28547978ba8a05d9103a90789108fa15898d14982bd0d5d098019700b37939f9f0d66e78af49c6318b886ba2a9101c072bb1e7bdbdb7319eb38e3320b4fbb1fcb28e8e7fac0bd493faf3c0547214465d55ca212dae99dc53addc3378b7e7b93acbdc1c9787149ed0214cd9846852ed7c18b23ab0ae8b7afbb725d44997a38422ff17b1dbeb22e2387094e0bc59496786114d0bb398f36fdfb06c70ff0ce47e9c3eb8c32c22062ccd5306026b606a9e9c628a377f0efd71087c95b3c1ffbdec8a91f311fbba4793d3d3fbf350e6a4a491d74ab7fb0cb66afa0d66177853df464f0175cdee4f97a4e620366aa18c2d04ebab82ff31ec07722fd53e0b4926973dd41d10422747261d8772b18ab55e0bbdcb89e224a5fc2679c2b729aaa4e1a78f95cf68af3562b98f0586d02134464f87dd15b843451a9160c5f4704c994a32259ca623c937431cfe55ee97d736916ac3e7ef831a1b6978539ac6c3304de2f43b3b208f73d071d17fd5cae631f617929468fc59d529deab1c0080a85ded180f9b7029376059c5ca3ba2eab9ee556a74373eadb5983f5990146b04bd255f2380450864e33c478cfe42072eeaabf8032f2c22fbf111407bf2cc41f6cc55596cca62a69303089c3ec231f42a8358d8bce315debce8cd4cea4aa478fba3476b5252e2d64da6b70d6ea0c1b4a99abebbd194e62e25442e7ecffc788710ac6dd1da815a0a5ef8dcef34ebcc90c6f29372875d664c2a06f3485dad8bd00d94837f417412399f9f085d8666fcaf38d38620897e2d06c7399eb28c5db0ffa42a233fdc6732c3e525bd49771aad03348aad068a5e729565c10d343a10c5cd6e530994c9a400354de3af3b39d20cf2b54fc0c9bde4b6f520688694d9b53c3628f1744b61271383d852219d8afae7a284d2f0b042f2fb70778b53d30876b5a031904a241e5a1741ff2e88bf8faa60472ba66111f0560ef127ee86d24f2c502fcff7575696f7f450109776f0f5e1bbd77a48952697fb2f8657516cc061de2bcc6aff9b861356b97e576f78abab1d3acb70d14a84b7858c22b2a0ee17e1231a2da3738018205091b263c42597ea6d3d7d9dae3fa030a13162243494c4e7f8fa1a2d8e2ff11153a41474a63646770717487919da9bac9d2d3e3070f23455d686a7dbcc9de00000000000000000000000000000000000f1f343f",
};

// This is almost tgId=1, tcId=1 from
// This is almost tgId=1, tcId=1 from
// https://raw.githubusercontent.com/bcgit/bc-test-data/refs/heads/main/pqc/crypto/mldsa/ML-DSA-sigGen.txt
// with the difference being that an empty ctx is added, instead of testing sign_internal directly,
// with the difference being that an empty ctx is added, instead of testing sign_internal directly,
// and the signature value is run against bc-java
const MLDSA65_KAT1: Kat = Kat {
_parameter_set: "ML-DSA-65",
Expand All @@ -1110,9 +1110,9 @@ const MLDSA65_KAT1: Kat = Kat {
};

// generated by hand against bc-java
// This is almost tgId=1, tcId=1 from
// This is almost tgId=1, tcId=1 from
// https://raw.githubusercontent.com/bcgit/bc-test-data/refs/heads/main/pqc/crypto/mldsa/ML-DSA-sigGen.txt
// with the difference being that an empty ctx is added, instead of testing sign_internal directly,
// with the difference being that an empty ctx is added, instead of testing sign_internal directly,
// and the signature value is run against bc-java
const MLDSA87_KAT1: Kat = Kat {
_parameter_set: "ML-DSA-87",
Expand Down
2 changes: 1 addition & 1 deletion crypto/mlkem-lowmemory/src/aux_functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ pub(crate) fn byte_decode<const d: usize, const PACK_LEN: usize>(B: &[u8; PACK_L
for j in 0..d {
// select the next bit, according to bitcount, then shift it up by j
// there is supposed to be a `mod m` here, but that shouldn't matter as they are being checked below
F[i] |= (((B[(i * d + j) / 8] >> (i * d + j) % 8) & 1) as i16) << j;
F[i] |= (((B[(i * d + j) / 8] >> (i * d + j) % 8) & 1) as i16) << j;
}
// assert the mod m
// These are relaxed because they are being checked above in MLKEMPublicKey::pk_decode()
Expand Down
8 changes: 4 additions & 4 deletions crypto/mlkem-lowmemory/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,16 +208,16 @@
//! There are, however, a few exceptions worth mentioning.
//!
//! If using a [`MLKEM::keygen_from_seed`], then it is your responsibility to ensure that the seed is
//! cryptographically random and unpredictable at a security strength that matches the MLKEM parameter set.
//! cryptographically random and unpredictable at a security strength that matches the MLKEM parameter set.
//!
//! Also, [`MLKEM::encaps_internal`] requires the encapsulation randomness to be provided, so the ciphertext
//! will only be as strong as the randomness that you provide.
//!
//!
//! A note about cryptographic side-channel attacks: considerable effort has been expended to attempt
//! to make this implementation constant-time, which generally means that the core mathematical algorithm
//! code that handles secret data uses bitshift-and-xor type constructions instead of if-and-loop
//! constructions. That should give this implementation reasonably good resistance to timing and
//! power analysis key extraction attacks, however:
//! power analysis key extraction attacks, however:
//! A) this is a "best-effort" and not formally verified, and
//! B) the Rust compiler does not guarantee constant-time behaviour no matter how good the design is code,
//! so like all Safe Rust code (ie Rust code that does not include inline assembly),
Expand All @@ -227,7 +227,7 @@
#![no_std]
#![forbid(missing_docs)]
#![forbid(unsafe_code)]
// These are because variable names need to be matched exactly against FIPS 204,
// These are because variable names need to be matched exactly against FIPS 204,
// for example both 'K' and 'k', or 'A' and 'a' are used and have specific meanings.
// linter needs to be instructed to ignore these cases
#![allow(non_snake_case)]
Expand Down
5 changes: 2 additions & 3 deletions crypto/mlkem/src/aux_functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,9 +301,8 @@ pub(crate) fn barrett_reduce(a: i16) -> i16 {
a - (((t as i32) * q as i32) as i16)
}


// Not currently used. It is left here as a reference since it's useful for debugging if it's
// necessary to output values that are normalized to [0,q] to compare against intermediate results
// Not currently used. It is left here as a reference since it's useful for debugging if it's
// necessary to output values that are normalized to [0,q] to compare against intermediate results
// from other libraries.
// pub(super) fn cond_sub_q(a: i16) -> i16 {
// let tmp = a - q;
Expand Down
Loading
Loading