Blog

November 18, 2025 · Release · 7 min read

OpenCrypt 2.0: ML-DSA and ML-KEM in pure Rust

OpenCrypt 2.0 adds ML-DSA and ML-KEM implementations that outperform existing solutions by up to 2x. The benchmarks, the design decisions behind them, and how we would approach a migration.

FIPS 203, 204, and 205 are final

NIST finalized FIPS 203, 204, and 205 in August 2024. It is the biggest change to cryptography since public-key algorithms arrived in the 1970s, and organizations everywhere are now racing to deploy the new algorithms before large quantum computers exist.

OpenCrypt 2.0 took years of research and engineering. It is production-ready post-quantum cryptography, and it is fast.

ML-DSA and ML-KEM performance

ML-DSA signatures

ML-DSA (Module-Lattice Digital Signature Algorithm, standardized as FIPS 204) replaces classical signature schemes such as RSA and ECDSA. Our implementation is fast at every security level:

Security LevelKey GenerationSigningVerification
ML-DSA-4422.5μs52.3μs28.9μs
ML-DSA-6538.7μs74.4μs40.9μs
ML-DSA-8761.2μs108.2μs58.3μs

That is a 2x improvement over other Rust implementations. It comes from careful algorithm design and architecture-specific optimization.

ML-KEM key exchange

ML-KEM (Module-Lattice Key Encapsulation Mechanism, standardized as FIPS 203) establishes keys in a way that resists quantum attacks. We focused on the encapsulation/decapsulation round trip, which is the critical path:

Security LevelEncapsulationDecapsulationRound-Trip
ML-KEM-51218.7μs19.2μs37.9μs
ML-KEM-76829.1μs30.5μs59.6μs
ML-KEM-102449.0μs51.3μs100.3μs

Design decisions

Pure Rust, no unsafe code

OpenCrypt 2.0 is written entirely in safe Rust, which eliminates whole classes of memory-safety vulnerabilities. We reach competitive performance without unsafe blocks through:

  • careful optimization of memory layout
  • deliberate use of const generics
  • compile-time loop unrolling

Constant-time execution

Every operation in OpenCrypt runs in constant time, which prevents timing side-channel attacks:

use opencrypt::ml_dsa::{MlDsa65, SigningKey, VerifyingKey};

// Key generation is constant-time
let signing_key = SigningKey::<MlDsa65>::generate(&mut rng);
let verifying_key = signing_key.verifying_key();

// Signing and verification are constant-time
let signature = signing_key.sign(message);
verifying_key.verify(message, &signature)?;

Platform-specific optimizations

OpenCrypt automatically detects and uses platform-specific intrinsics:

  • AVX2/AVX-512: vectorized NTT operations on x86_64
  • NEON: polynomial arithmetic optimized for ARM
  • Portable: an efficient pure Rust scalar fallback for all platforms

Migration plan

For organizations planning their post-quantum migration, we recommend doing it in phases:

  1. Inventory: catalog all cryptographic usage in your systems.
  2. Hybrid mode: deploy ML-KEM alongside classical ECDH for key exchange.
  3. Testing: validate performance under production workloads.
  4. Full migration: replace the classical algorithms entirely.

In OpenCrypt 2.0, hybrid modes are supported out of the box, so you can migrate gradually without service disruption.

Next on our list is a WebAssembly target for browser-native post-quantum crypto. OpenCrypt 2.0 is available now on GitHub, with full documentation, migration guides and benchmarking tools.

Questions about this post, or working on something similar? Book a meeting.