Tectonic's common cryptography library
Bedrock provides post-quantum cryptographic primitives including digital signatures and key encapsulation mechanisms (KEMs). The library supports NIST-standardized algorithms (ML-DSA, ML-KEM) as well as other post-quantum schemes (Falcon/FN-DSA, Classic McEliece).
- ML-DSA (FIPS 204): Module-Lattice-Based Digital Signature Algorithm
- Falcon/FN-DSA: Fast Fourier Lattice-based Compact Signatures
- MAYO: Multivariate oil-and-vinegar signatures with compact public keys
- ML-KEM (FIPS 203): Module-Lattice-Based Key-Encapsulation Mechanism
- Classic McEliece: Code-based Key Encapsulation Mechanism
- ETHFALCON: Ethereum-compatible Falcon variant with Keccak-256 XOF
- X-Wing: Hybrid KEM combining X25519 with ML-KEM or Classic McEliece
Two security levels following NIST standards:
- ML-DSA-65 (NIST Level 3) - Default
- ML-DSA-87 (NIST Level 5)
Two security levels plus Ethereum variant:
- FN-DSA-512 (NIST Level 1) - Default
- FN-DSA-1024 (NIST Level 5)
- ETHFALCON (Ethereum-compatible Falcon-512 with Keccak-256)
Multivariate "oil and vinegar" signatures with compact public keys, via the mayo feature. Four NIST parameter sets:
- MAYO-1 (NIST Level 1) - Default
- MAYO-2 (NIST Level 1) - Smallest signatures
- MAYO-3 (NIST Level 3)
- MAYO-5 (NIST Level 5)
Two security levels following NIST standards:
- ML-KEM-768 (NIST Level 3) - Default when
ml-kemfeature enabled - ML-KEM-1024 (NIST Level 5)
- ClassicMcEliece-348864 (NIST Level 1) - Default when only
mceliecefeature enabled
Hybrid KEM combining X25519 with post-quantum KEMs:
- X25519-ML-KEM-768 (X25519 + ML-KEM-768) - Default
- X25519-ML-KEM-1024 (X25519 + ML-KEM-1024)
- X25519-ClassicMcEliece348864 (X25519 + Classic McEliece)
The weakest ML-KEM and ML-DSA parameter sets are intentionally not offered — they are considered too weak for new deployments, so the library provides no way to select them:
| Scheme | NIST Level | Status | Notes |
|---|---|---|---|
| ML-KEM-512 | Level 1 | Removed | Use ML-KEM-768 (the new KemScheme default) or higher. |
| ML-DSA-44 | Level 2 | Removed | Use ML-DSA-65 (the new MlDsaScheme default) or higher. |
| X25519-ML-KEM-512 | — | Removed | Hybrid built on ML-KEM-512; use X25519-ML-KEM-768 or higher. |
Removing these dropped the KemScheme::MlKem512, MlDsaScheme::Dsa44, and
XwingScheme::X25519MlKem512 variants, the SignatureScheme::MlDsa44 /
SignatureSeed::MlDsa44 HD-wallet variants, the HHDWallet::derive_mldsa44_keypair
method, and all ML_DSA_44_* constants. See the CHANGELOG for the
full breaking-change entry.
The serde discriminants and BIP-85 child indices of the surviving schemes are unchanged, so existing serialized keys and derivation paths for the stronger schemes remain valid.
Deprecation path for existing data. The wire discriminants (1) and name strings
("ML-KEM-512", "ML-DSA-44", "X25519-ML-KEM-512") of the removed schemes stay
reserved: deserializing or parsing data produced by an older version of the library
returns a specific Error::DeprecatedScheme { scheme, replacement } — naming the removed
scheme and its recommended replacement — rather than a generic InvalidScheme. This gives
anything that may already have used these schemes a clear, actionable migration error
instead of a silent or confusing failure. The discriminants are never reassigned to other
schemes.
Note: exclusion applies only to the weakest ML-KEM / ML-DSA lattice sets. Level 1 parameter sets from other families — FN-DSA-512, MAYO-1, MAYO-2, and ClassicMcEliece-348864 — remain available, since their security margins and intended use cases differ.
Key Generation:
keypair() -> Result<(MlDsaVerificationKey, MlDsaSigningKey)>Generate a new ML-DSA signing and verification key pair (requireskgenfeature)
Signing:
sign(message: &[u8], signing_key: &MlDsaSigningKey) -> Result<MlDsaSignature>Sign a message with the specified signing key (requiressignfeature)
Verification:
verify(message: &[u8], signature: &MlDsaSignature, verification_key: &MlDsaVerificationKey) -> Result<()>Verify a signature (requiresvrfyfeature)
Common methods for all types:
scheme() -> MlDsaScheme- Get the scheme used by this key/signatureto_raw_bytes() -> Vec<u8>- Convert to raw byte representationfrom_raw_bytes(scheme: MlDsaScheme, bytes: &[u8]) -> Result<Self>- Create from raw bytesas_ref() -> &[u8]- Get byte slice reference
Key Generation:
keypair() -> Result<(FalconVerificationKey, FalconSigningKey)>Generate a new Falcon signing and verification key pair (requireskgenfeature)keypair_from_seed(seed: &[u8]) -> Result<(FalconVerificationKey, FalconSigningKey)>Generate a key pair from a seed (32-64 bytes, requireskgenfeature)
Signing:
sign(message: &[u8], signing_key: &FalconSigningKey) -> Result<FalconSignature>Sign a message with the specified signing key (requiressignfeature)
Verification:
verify(message: &[u8], signature: &FalconSignature, verification_key: &FalconVerificationKey) -> Result<()>Verify a signature (requiresvrfyfeature)
Common methods for all types:
scheme() -> FalconScheme- Get the scheme used by this key/signatureto_raw_bytes() -> Vec<u8>- Convert to raw byte representationfrom_raw_bytes(scheme: FalconScheme, bytes: &[u8]) -> Result<Self>- Create from raw bytesas_ref() -> &[u8]- Get byte slice reference
When eth_falcon feature is enabled:
into_ethereum(self) -> Result<Self>- Convert FN-DSA-512 signing key to ETHFALCON schemeinto_dsa512(self) -> Result<Self>- Convert ETHFALCON signing key to FN-DSA-512 scheme
When eth_falcon feature is enabled:
EthFalconVerifyingKey::try_from(FalconVerificationKey) -> Result<EthFalconVerifyingKey>Convert Falcon public key to ETHFALCON Solidity format (abi.encodePacked, NTT form, 1024 bytes)EthFalconSignature::try_from(FalconSignature) -> Result<EthFalconSignature>Convert Falcon signature to ETHFALCON Solidity format (abi.encodePacked, 1024 bytes)
Key Generation:
keypair() -> Result<(KemEncapsulationKey, KemDecapsulationKey)>Generate a new encapsulation/decapsulation key pair (requireskgenfeature)keypair_from_seed(seed: &[u8]) -> Result<(KemEncapsulationKey, KemDecapsulationKey)>Generate a key pair from a seed (requireskgenfeature)
Encapsulation:
encapsulate(encapsulation_key: &KemEncapsulationKey) -> Result<(KemCiphertext, KemSharedSecret)>Encapsulate to the provided public key (requiresencpfeature)
Decapsulation:
decapsulate(ciphertext: &KemCiphertext, decapsulation_key: &KemDecapsulationKey) -> Result<KemSharedSecret>Decapsulate the provided ciphertext (requiresdecpfeature)
Common methods for all types:
scheme() -> KemScheme- Get the scheme used by this key/ciphertext/secretto_raw_bytes() -> Vec<u8>- Convert to raw byte representationfrom_raw_bytes(scheme: KemScheme, bytes: &[u8]) -> Result<Self>- Create from raw bytesas_ref() -> &[u8]- Get byte slice reference
Key Generation:
keypair() -> Result<(EncapsulationKey, DecapsulationKey)>Generate a new X-Wing encapsulation/decapsulation key pair (requiresxwingfeature)keypair_from_seed(seed: &[u8]) -> Result<(EncapsulationKey, DecapsulationKey)>Generate a key pair from a seed (requiresxwingfeature)
encapsulate() -> Result<(Ciphertext, SharedSecret)>Create a ciphertext and shared secret for the encapsulation key holderto_raw_bytes() -> Vec<u8>- Convert to raw byte representationfrom_raw_bytes(scheme: XwingScheme, bytes: &[u8]) -> Result<Self>- Create from raw bytes
decapsulate(ciphertext: &Ciphertext) -> Result<SharedSecret>Decapsulate to recover the shared secretto_seed() -> Vec<u8>- Get the seed bytesfrom_seed(scheme: XwingScheme, bytes: &[u8]) -> Self- Create from seed bytesexpand() -> Result<ExpandedDecapsulationKey>- Expand the seed into full key material
decapsulate(ciphertext: &Ciphertext) -> Result<SharedSecret>- Decapsulate using expanded keyencapsulation_key() -> EncapsulationKey- Get the associated encapsulation key
to_raw_bytes() -> Vec<u8>- Convert to raw byte representationfrom_raw_bytes(scheme: XwingScheme, bytes: &[u8]) -> Result<Self>- Create from raw bytes
All key types, signatures, ciphertexts, and shared secrets implement serde::Serialize and serde::Deserialize:
- Human-readable formats (JSON, etc.): Serialized as hex strings and human-readable
- Binary formats (postcard, bincode, etc.): Serialized as compact byte arrays
Schemes implement the [Display] and [FromStr] traits for string parsing:
to_string()- Convert scheme to string representation (e.g., "ML-DSA-65")from_str(s: &str) -> Result<Self>orparse()- Parse scheme from string- Conversion to/from
u8for compact storage
use bedrock::ml_dsa::MlDsaScheme;
// Generate a keypair
let scheme = MlDsaScheme::Dsa65;
let (verification_key, signing_key) = scheme.keypair()?;
// Sign a message
let message = b"Hello, world!";
let signature = scheme.sign(message, &signing_key)?;
// Verify the signature
scheme.verify(message, &signature, &verification_key)?;
// Serialize keys
let vk_json = serde_json::to_string(&verification_key)?;
let vk_binary = postcard::to_stdvec(&verification_key)?;use bedrock::falcon::FalconScheme;
// Generate a keypair with deterministic seed
let scheme = FalconScheme::Dsa512;
let seed = [1u8; 48];
let (verification_key, signing_key) = scheme.keypair_from_seed(&seed)?;
// Sign and verify
let message = b"Sign this message";
let signature = scheme.sign(message, &signing_key)?;
scheme.verify(message, &signature, &verification_key)?;use bedrock::falcon::{FalconScheme, EthFalconVerifyingKey, EthFalconSignature};
// Generate ETHFALCON keypair
let scheme = FalconScheme::Ethereum;
let (verification_key, signing_key) = scheme.keypair()?;
// Sign with ETHFALCON
let message = b"Transaction data";
let signature = scheme.sign(message, &signing_key)?;
// Convert to Solidity-compatible formats
let eth_vk: EthFalconVerifyingKey = verification_key.try_into()?;
let eth_sig: EthFalconSignature = signature.try_into()?;
// Verify
scheme.verify(message, &signature, &verification_key)?;
// Convert between schemes
let signing_key_512 = signing_key.into_dsa512()?;use bedrock::kem::KemScheme;
// Generate a keypair
let scheme = KemScheme::MlKem768;
let (encapsulation_key, decapsulation_key) = scheme.keypair()?;
// Encapsulate to create shared secret
let (ciphertext, shared_secret_sender) = scheme.encapsulate(&encapsulation_key)?;
// Decapsulate to recover shared secret
let shared_secret_receiver = scheme.decapsulate(&ciphertext, &decapsulation_key)?;
assert_eq!(shared_secret_sender.as_ref(), shared_secret_receiver.as_ref());use bedrock::kem::KemScheme;
// Use Classic McEliece for code-based KEM
let scheme = KemScheme::ClassicMcEliece348864;
let (ek, dk) = scheme.keypair()?;
let (ct, ss) = scheme.encapsulate(&ek)?;
let ss2 = scheme.decapsulate(&ct, &dk)?;
assert_eq!(ss.as_ref(), ss2.as_ref());use bedrock::xwing::XwingScheme;
// Generate X-Wing keypair (combines X25519 with ML-KEM-768)
let scheme = XwingScheme::X25519MlKem768;
let (encapsulation_key, decapsulation_key) = scheme.keypair()?;
// Encapsulate to create shared secret
let (ciphertext, shared_secret_sender) = encapsulation_key.encapsulate()?;
// Decapsulate to recover shared secret
let shared_secret_receiver = decapsulation_key.decapsulate(&ciphertext)?;
assert_eq!(shared_secret_sender, shared_secret_receiver);Control which algorithms and operations are enabled:
ml-dsa- Enable ML-DSA signature schemes (default)falcon- Enable Falcon/FN-DSA signature schemes (default)eth_falcon- Enable ETHFALCON Ethereum-compatible variant (default, requiresfalcon)ml-kem- Enable ML-KEM key encapsulation (default)mceliece- Enable Classic McEliece key encapsulation (default)xwing- Enable X-Wing hybrid KEM (default, requiresml-kemormceliece)
kgen- Enable key generation (default)sign- Enable signing operations (default)vrfy- Enable verification operations (default)encp- Enable encapsulation operations (default)decp- Enable decapsulation operations (default)
Bedrock is designed to allow selective features to minimize the dependency list. The default is
default = ["eth_falcon", "falcon", "mceliece", "ml-dsa", "ml-kem", "decp", "encp", "kgen", "sign", "vrfy", "hhd", "xwing"]Verification only (no key generation or signing):
tectonic-bedrock = { version = "0.1", default-features = false, features = ["ml-dsa", "vrfy"] }ML-KEM only:
tectonic-bedrock = { version = "0.1", default-features = false, features = ["ml-kem", "kgen", "encp", "decp"] }X-Wing hybrid KEM only:
tectonic-bedrock = { version = "0.1", default-features = false, features = ["ml-kem", "xwing", "kgen", "encp", "decp"] }All fallible operations return Result<T, bedrock::error::Error>. The Error enum includes:
McElieceError(String)- Errors from the Classic McEliece KEMInvalidScheme(u8)/InvalidSchemeStr(String)- Invalid scheme identifiersInvalidSeedLength(usize)- Seed length out of valid range (32-64 bytes)InvalidLength(usize)- Invalid data lengthFnDsaError(String)- ETHFALCON-specific errors
- All algorithms are quantum-resistant
- ML-DSA and ML-KEM are NIST-standardized (FIPS 204, FIPS 203)
- Falcon provides smaller signatures than ML-DSA with similar security
- ETHFALCON enables post-quantum signatures in Ethereum smart contracts
- Classic McEliece offers conservative code-based security
- Use deterministic key generation (
keypair_from_seed) only when necessary - Protect private keys and seeds with appropriate key management practices
Licensed under either of
- Apache License, Version 2.0, (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.