Skip to content

0.1.2alpha Is Released!

Mike Ounsworth edited this page Jul 15, 2026 · 1 revision

Release 0.1.2alpha 🥳 🚀

Big progress since the 0.1.1alpha release in February! The project is really picking up steam with a larger team and more ambitious set of feature goals!

We're still in ALPHA, meaning that we are still making sweeping (and often breaking) changes as we figure out what works and what doesn't, especially in response to the excellent feedback we've recieved since the 0.1.1 release.

The Team

bc-rust grew out of a straightforward port of bc-java into the rust language 2024 - 2025 done by the Legion of the Bouncy Castle team. In October 2025 Mike Ounsworth came on as full-time maintainer of bc-rust resulting in the 0.1.0 and 0.1.1 alpha releases in February. Since then, it's really picked up steam and we now have a healthy team of contributors backed by OpenSSL Corporation and KeyFactor who want accelerate the production-quality availability of the bc-rust project.

The current core team is:

  • 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)
  • Francis Mendoza (Keyfactor)
  • Nikola Pajkovský (OpenSSL Corporation)
  • Jason Kurczak (Cryptic Forest)
  • Luis Ruiz (Individual)

As such, with the 0.1.2 release we have developed and implemented a number of workflow improvements to move from a single-contributor to a multiple-contributor model, including moving the list of open TODO items from a file on Mike's computer into a public GitHub Issues tracker; creating a shared release/0.1.2alpha branch and a process for pull requests, code reviews, and clean merges into the shared branch. This all results in a much more public process where all in-progress features and changes can be found on the GitHub Pull Requests page.

Major New Features

ML-KEM and ML-DSA and their low-memory modes

The primary feature in this release is implementations of the post-quantum Module Lattice Key Encapsulation Algorithm (ML-KEM, FIPS 203) and Module Lattice Digital Signature Algorithm (ML-DSA, FIPS 204). These algorithms allow for quantum-safe data protection now, and set the pattern for asymmetric key establishment and signature algorithms across the bc-rust library.

We are extremely proud of our ML-KEM and ML-DSA implementation which boast the following design elements:

  • Implemented against the FIPS specifications and carefully commented where we are in direct correspondence with the sample algorithms in the FIPS and where we make design deviations. This is to support easy code review for certification and research purposes.
  • All the bells & whistles allowed by FIPS 203 and 204 are exposed, including:
    • Full support for full and seed-based private keys.
    • A sign_from_seed() which optimizes both performance and memory usage for a single signature operation from a seed-based private key compared to calling keygen() then sign() by optimizing reuse of intermediate values.
    • Full support for randomized and deterministic sign() and encaps() operations as well as the ML-DSA ctx parameter.
    • Full support for HashML-DSA as well as the "external mu" mode of ML-DSA.
    • Support for pre-expanding the public keys for faster encaps, decaps, sign and verify operations. Upon careful study, expanding the public matrix A_hat from the compressed seed that's contained in the public key can be up to 40% of the runtime of a verify or encaps operation (depending on parameter set). Using the provided MLKEMPublicKeyExpanded and MLDSAPublicKeyExpanded objects front-loads this computation at the cost of increased memory footprint and is particularly effective when doing multiple operations against the same key.
    • Our ML-KEM and ML-DSA implementations fully pass several rigorous test suites, including bc-test-data, project wycheproof, and the Symbolicself Crucible project.
    • And last but most certainly not least, we have parallel crates mlkem-lowmemory and mldsa-lowmemory which are built to aggressively optimize for small memory footprint by only holding the private keys as seeds and only ever expanding one entry at a time of matrices and vectors. For ML-KEM thing gives a 2-3x reduction in memory footprint in exchange for a slight decrease in performance speed. ML-KEM-1024.decaps runs in 20kb of peak memory usage (compared to 60kb for the non-optimized implementation). For ML-DSA the results are even more dramatic, giving a 6-10x reduction in memory footprint in exchange for a 5x decrease in performance speed on the sign() operation and only a slight decrease for keygen() and verify(). ML-DSA-87.sign runs in 32kb of peak memory usage (compared to 240kb for the non-optimized implementation). See the docs for the mlkem-lowmemory and mldsa-lowmemory for details, including performance figures.

Suspendable

All objects with a stateful streaming API (ie that implement the .do_update() -> .do_update() -> .. -> do_final() pattern) can now be suspended via either the Suspendable or SuspendableKeyed traits which allow them to serialize their internal state to a small byte array that can be safely stored in a cache and resumed later. We handle keyed algorithms (ie MACs, block ciphers, etc) differently from un-keyed algorithms (hashes) to ensure that the long-term secret is not contained in the serialized state, but can be properly re-supplied at resumption.

Every serialized state embeds the version of the library that produced it so that we can gracefully handle resuming into a different version of the library. Resuming from data produced by an older version is generally ok and the version tag allows us to apply the correct version of the parser or reject if the serialized state is so old as to be completely incompatible. Resuming from data produced by a newer version of the library is ok as long as it is on the same patch stream (MAJOR.MINOR.x) since semantic versioning promises that breaking changes cannot happen on a patch.

dyn RNG

Anywhere that consumes randomness (such as keygen and non-deterministic sign / encaps functions) can now be handed an instance of an object that impl's bouncycastle-core::traits::RNG. That means that if you have a hardware RNG or even want to read entropy from a file or other system device, now you just need to write a wrapper that impl's the RNG trait and it will behave as a first-class RNG across the library.

This is still a bit of an experiment because all those same places also currently accept the random value directly as a &[u8] (be it a seed or a random message). We may end up leaving both, or we may consolidate down to one interface for injecting custom randomness.

Rework of Secrets handling

Rework of the Secret system for protecting secret data against leakage via returning to the memory pool unzeroized, or being logged in debug messages, stack traces, and crash dumps. Now properly uses core::mem::write_volatile to prevent compiler from eliding writes on drop, and introduced a new type system Secret<T> that is used across the library to give more fine-grained control over which objects (and which fields within objects) get this extra protection. Bonus: this is a public type that you can use to protect your application data as well!

As an example, the MLDSAPrivateKey object can now be defined as:

pub struct MLDSAPrivateKey<..> {
    rho: [u8; 32],
    K: Secret<[u8; 32]>,
    tr: [u8; 64],
    s1_hat: Secret<Vector<l>>,
    s2_hat: Secret<Vector<k>>,
    t0_hat: Vector<k>,
    seed: Option<KeyMaterial<32>>,
}

which clearly indicates to a human reader which values are secret (ie the actual private key components) and which are not (ie part of the public key). This also gives developers more fine-grained control over which data pays the extra performance penalty associated with zeroization-on-drop.

no_std

A lot of progress has been made towards getting the crypto crates to build in no_std mode, in order to make them compatible with embedded devices that cannot support the Rust standard library. This work is nearing completion, but needed more attention than was available before the release deadline. This milestone is expected to be reached in the near future, and further optimizations to the internal cryptographic algorithms are planned for future versions.

Minor New Features and Refactoring

Build system

Removed the dependence on nightly / experimental compiler features; the library now builds on stable (github issue #28). Although, we had been making great use of the generic_const_exprs feature in nightly, and not having access to that forced some additional ugliness in our type system, so we've left // TODO comments to revert those changes whenever generic_const_exprs lands in rust stable.

Trait system

  • Split the Signature trait into a Signer and a Verifier trait. This is for two reasons: 1) some of the future signature algorithms (like hash-based signatures) the verifier code is substantially lighter than the signer code, or we may not even want to implement a signer in software, and 2) NIST likes to soft-deprecate algorithms by disallowing generation of new signatures, but still allowing verification of existing signatures.
  • Added traits for symmetric ciphers in the block cipher, stream cipher, and AEAD families. We don't have any of these algorithms implemented yet, but they're coming!

The KeyMaterial object

  • Reworked the way KeyMaterial hazardous operations work; instead of a stateful .allow_hazardous_operations() / .drop_hazardous_operations(), it now uses a closure-based do_hazardous_operations(). Github issue #39.
  • Renamed KeyMaterial::KeyType's and deleted KeyMaterial::concatenate in order to give a better intuition and FIPS-alignment.
  • Tightened up the entropy-tracking behaviour of the KeyMaterial object, thanks to Q. T. Felix (github: @Quant-TheodoreFelix, github issue #6)

Docs

  • Major overhaul of the docs (public crate docs and inline comments) for a more neutral and professional tone - Special thanks to Luis Ruiz (github: laruizlo) for this big effort!
  • All crypto algorithm crates now have Memory Usage docs that list the stack memory usage of the implementation.
  • All crypto algorithm crates now have #![forbid(missing_docs)] to ensure that they have a fully-documented public API.

Other miscellaneous issues resolved

All public *_out(.., out: &mut [u8]) functions now begin by zeroizing the entire provided output buffer with .fill(0), preventing exposure of stale data in oversized output buffers or on early error returns. Thanks to Q. T. Felix (github: @Quant-TheodoreFelix, github issue #18)

"SHAKE absorb-after-squeeze": clarified and hardened the behaviour of SHAKE as defined in FIPS 202 with respect to absorbing more input after having been squeezed. (github issue #27)

Effort to reduce the number of .unwrap() and .expect() calls within the library code. (github issue #46)

Feedback Welcome

As this is still an ALPHA release, we are extremely interested in community feedback of any kind. We are particularly interested in feedback on the following.

Factories

A "factory" (often called a "provider" in crypto libraries) is a pattern that provides a cryptographic agility layer where you can ask the library for an instance of, say, a signature algorithm and write all the appropriate code for that, but defer to runtime configuration which signature algorithm you are going to use. This is a gold standard for cryptographic agility because it allows dynamic and evolving configuration of crypto policy by application operators without requiring re-building applications.

This pattern is the default way to interact with a crypto library in Java via the Java Cryptography Architecture (JCA) provider interface, and it is becoming the standard way to interact with OpenSSL since OpenSSL 3.0.0 via its EVP API. But it turns out to be rather complicated to implement in Rust since this is fundamentally an object-oriented design pattern being implemented in a non-OOP language. We have a basic implementation in the crypto/factory crate, but it needs a design overhaul because several aspects of it are fairly clunky at the moment. If anyone has experience designing this sort of agility layer in Rust, we'd love to hear ideas.

Acknowledgements

We also want to acknowledge several community members who provided valuable feedback and contributions during this release cycle:

  • Nicola Tuveri (Tampere University, github: romen) who provides ongoing academic assessment of various design tradeoffs.
  • Daniel Heinrich (Cryptsoft, github: omegashadow112) who provided the use case for and initial implementation of the ML-DSA and ML-KEM Low Memory implementations.
  • Tamish Dahiya (github: tad-fr) who has provided contributions and PRs reviews particularly relating to zeroization of secret data.
  • Q. T. Felix (github: Quant-TheodoreFelix) who has provided PRs addressing a number of open issues.

Roadmap

See the Bouncy Castle Rust Roadmap page for an up-to-date view of what's coming up next for the project.