Repository navigation
0.1.3alpha Is Released
Four months on from 0.1.2alpha, this release fills in the symmetric side of the library and settles the shape of the API for anything that takes a key. It is the biggest change to the public API we have made so far, and it is also the first release to go through a crate-by-crate review before it was cut.
We are still in ALPHA. Breaking changes are still fine, and there are plenty here; see the release notes for the full changelog.
The core team for this release:
- Mike Ounsworth (Legion of the Bouncy Castle / Cryptic Forest)
- David Hook (Legion of the Bouncy Castle / Keyfactor)
- Megan Woods (Legion of the Bouncy Castle / Keyfactor)
- Roy Basmacier (Legion of the Bouncy Castle / Keyfactor)
- Francis Mendoza (Keyfactor)
- Nikola Pajkovský (OpenSSL Corporation)
- Jason Kurczak (Cryptic Forest)
- Luis Ruiz (Individual)
The work is tracked in the issues and pull requests on GitHub as before. One new thing in the process this time: before release/0.1.3alpha was merged, every crate was reviewed against QUALITY_AND_STYLE.md, and the fixes landed as one commit per crate (PR #190). The most consequential find was in Hash_DRBG, which now matches SP 800-90A Rev. 1 and passes all 960 CAVP vectors; the AES round-key expansion also gained a barrier, with a CI job that checks the Cortex-M0 and RISC-V assembly, so the compiler cannot fold it into a multiply.
The 0.1.2 release added the cipher traits with nothing behind them. They now have:
-
AES (FIPS 197): AES-128/192/256 in ECB, CBC, CFB, CFB8, CTR, CCM and GCM, plus CMAC and GMAC. The block cipher is a bit-sliced Boolean circuit with no lookup table, so it is constant-time by construction; the modes and padding live in a new
ciphercrate that is generic over the block cipher, ready for the ciphers that follow. - Ascon (NIST SP 800-232): Ascon-AEAD128, Ascon-Hash256, Ascon-XOF128 and Ascon-CXOF128.
- SM3 (GB/T 32905-2016), with HMAC-SM3 and HKDF-SM3.
-
SP 800-185: cSHAKE, KMAC, TupleHash and ParallelHash, in the
sha3crate. - HMAC and HKDF are generic over the hash, so HMAC over SHA-3, HMAC-SHA512/224 and /256 and HKDF-SHA384 come for free.
All of them are tested against bc-test-data and Project Wycheproof, and every one has a bc-rust CLI subcommand.
Anything keyed is now a key object, built once from KeyMaterial, that validates and expands the key, hands out streaming workers that borrow it, and provides the one-shots. The mode is part of the key's type, so an AES-GCM key cannot be used for CBC, and a worker cannot outlive its key:
let key = AES256Key::<GCM>::from_key_material(&material)?;
// one-shot: the nonce is drawn from the caller's RNG and returned
let (nonce, ciphertext, tag) = key.encrypt_detached(&mut rng, aad, message)?;
let recovered = key.decrypt_detached(&nonce, aad, &ciphertext, &tag)?;
// streaming
let (mut encryptor, nonce) = key.create_encryptor(&mut rng)?;The same shape applies to HmacKey<H>, KMAC128Key, AsconAEAD128Key and the MAC factory. The worker traits keep only the streaming methods; do_*_init and the key-taking one-shots are gone, and resuming a suspended worker takes the key object.
Signatures follow suit. SigningKey, DeterministicSigningKey and VerifyingKey carry create_signer(rng, ctx), sign, create_verifier and verify, and the pre-hash forms take the hash as a type parameter (create_ph_signer::<SHA512>). ML-DSA and HashML-DSA are on these traits; the ML-DSA external-mu interface moved onto the keys with them.
Mostly by removing copies, the lattice crates got smaller again, with a slight throughput gain. Peak stack for the largest parameter sets:
| 0.1.3 | |
|---|---|
| ML-DSA-87 sign / verify | 118 kB / 212 kB |
| ML-DSA-87 low-memory sign / verify | 25 kB / 21 kB |
| ML-KEM-1024 encaps / decaps | 44 kB / 58 kB |
| ML-KEM-1024 low-memory encaps / decaps | 11 kB / 21 kB |
Both crates now share their arithmetic with their low-memory twins through mldsa-common and mlkem-common, and ML-KEM gained expanded public and private keys to match ML-DSA's.
- The
dyn RNGexperiment from 0.1.2 is settled: anything that draws randomness takes the caller's&mut dyn RNG, and thekeygen()that built its own DRBG is gone. -
AlgorithmInfois the run-time form ofAlgorithm, so adyn Hashordyn MACcan still tell you what it is; the hash, XOF, cipher and MAC worker traits are all usable asdyn, andHashno longer requiresClone. -
HashOneShotgives every hashSHA256::hash(msg). - XOFs: absorb-after-squeeze is now a type error rather than a runtime rule, via
into_squeezer(). -
hazmatmodules hold what is only safe when composed correctly: the raw block permutation, ECB, the CTR keystream,do_hazardous_operations, ML-KEMencaps_with_randomnessand the uninitialised DRBG. - Error enums are
#[non_exhaustive], and the constant-time barrier usesread_volatile/write_volatilerather than the best-effortblack_box.
- Fixed an
i32overflow in ML-DSA-87 verification from a missing reduction, found with Wycheproof (PR #137). - Parameter sets are sealed traits rather than const-generic turbofish, so key types carry only
Pand the encoded lengths are inferred. - HashML-DSA drops the SHA-256 pairings for ML-DSA-65 and -87, since SHA-256 is below those sets' strength.
- SHA-2 gains SHA512/224, SHA512/256 and partial-bit finalisation.
- A first-squeeze bug in SHA-3
squeeze_partial_byte_final()is fixed, and Keccak now absorbs whole blocks straight from the caller's slice, which is 14-31% faster on long inputs.
- QUALITY_AND_STYLE.md now covers key objects,
hazmat,dyn-compatibility and the_out/_inplace/_noncesuffixes; CONTRIBUTING.md and the new ISSUES_STYLE_GUIDE.md state the submission bar and the AI policy. - GitHub Actions build, test, doc and format-check every pull request.
- A
mutantscargo profile makescargo mutantson the lattice crates take seconds per mutant rather than minutes, which is what made running it on every crate practical.
As before, we want feedback of any kind, and on two things in particular.
This is the first release of the key-object API, and it is the pattern every keyed algorithm from here on will follow. If the shape of from_key_material → create_* → do_* → do_*_final does not fit how you want to use a cipher or a MAC, now is the time to say so.
The factory question from 0.1.2 is still open, and the symmetric ciphers made it harder: the cipher traits are const-generic over the key, nonce and tag lengths, which the enum-per-trait pattern used for hashes and MACs cannot express. We have a few designs sketched and would like to hear from anyone who has built an agility layer over const-generic traits in Rust.
Thanks to the community members who contributed to this release:
- Tamish Dahiya, for making the crate versions inherit from the workspace.
- Polly Labs, for linking the factory docs to the core traits.
- Michael Richardson, for the clarified build instructions.
- The Wycheproof project, whose ML-DSA vectors caught the ML-DSA-87 overflow.
See the Bouncy Castle Rust Roadmap page for what is coming next. The short version: ChaCha20-Poly1305, Camellia, ARIA, SM4 and Triple-DES are already in review on the cipher traits above, with ECDSA, RSA, ECDH and X25519 behind them on the signature and agreement key traits.