Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions .github/workflows/manual-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on:
workflow_dispatch:
inputs:
tag:
description: 'Release tag to publish (for example, v0.3.0)'
description: 'Release tag to publish (for example, v0.4.0)'
required: true
type: string
dry_run:
Expand All @@ -27,7 +27,7 @@ jobs:
RELEASE_TAG: ${{ inputs.tag }}
run: |
if [[ ! "$RELEASE_TAG" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$ ]]; then
echo "::error::Release tag '$RELEASE_TAG' is not a valid SemVer tag (for example, v0.3.0)."
echo "::error::Release tag '$RELEASE_TAG' is not a valid SemVer tag (for example, v0.4.0)."
exit 1
fi

Expand Down
16 changes: 7 additions & 9 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
# Generated by Cargo
# will have compiled files and executables
# Generated by Cargo; contains compiled files and executables.
debug
target

Expand All @@ -14,15 +13,14 @@ target
**/mutants.out*/

# RustRover
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
# The JetBrains-specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a broader
# option (not recommended), uncomment the following line to ignore the entire `.idea` folder.
.idea/


# Added by cargo
# Added by Cargo.

/target
.vscode/
.DS_Store
.DS_Store
30 changes: 21 additions & 9 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ All notable changes to this crate will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## v0.3.0 - 2026-08-06
## v0.4.0 - 2026-08-07

### Added

Expand All @@ -15,23 +15,35 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
then pass the complete 32-byte child key to `hqc-kem` as deterministic key-generation
input. This release also adds the KEM-aware HHD wallet constructors and
`derive_hqc128_keypair`, `derive_hqc192_keypair`, and `derive_hqc256_keypair`.
- Hierarchical deterministic (HD) wallet key derivation for MAYO-1, MAYO-2, and MAYO-3.
This release adds the
`SignatureScheme::Mayo1/Mayo2/Mayo3` and `SignatureSeed::Mayo1/Mayo2/Mayo3` variants,
the `HHDWallet::derive_mayo1_keypair` / `derive_mayo2_keypair` / `derive_mayo3_keypair`
methods, and BIP-85 child indices `7'`/`8'`/`9'`. The 32-byte SLIP-0010 child key is
truncated (never expanded) to each parameter set's key-generation seed size—24 bytes for
MAYO-1/2, 32 bytes for MAYO-3. MAYO-5 is intentionally not supported in HHD because its
40-byte seed cannot be sourced from a 32-byte SLIP-0010 child key without expansion.

### Changed

- Replaced the Classic McEliece backend with `pq-mceliece`, added the ISO-standardized
460896, 6688128, 6960119, and 8192128 parameter sets, and made 460896 the default when
Classic McEliece is the only enabled KEM family. ClassicMcEliece-348864 remains
available for legacy interoperability.
- Kept all-feature CI within its five-minute budget by limiting expensive XMSS
cryptographic behavior tests to `XMSS-SHA2_10_256` and reusing a fixed test-only
key plus two safely cached signatures and serialized states. Full-tree key
generation remains available as an ignored manual smoke test. Heights 16/20 and
the remaining hash/width variants retain dispatch, size, serialization, name, and
wire-contract coverage without building full Merkle trees.
- Updated the transitive `keccak` dependency used by Falcon compatibility tests from
the yanked 0.1.5 release to 0.1.6.

## v0.3.0 - 2026-07-28

### Added

- Hierarchical deterministic (HD) wallet key derivation for MAYO-1, MAYO-2, and MAYO-3.
This release adds the
`SignatureScheme::Mayo1/Mayo2/Mayo3` and `SignatureSeed::Mayo1/Mayo2/Mayo3` variants,
the `HHDWallet::derive_mayo1_keypair` / `derive_mayo2_keypair` / `derive_mayo3_keypair`
methods, and BIP-85 child indices `7'`/`8'`/`9'`. The 32-byte SLIP-0010 child key is
truncated (never expanded) to each parameter set's key-generation seed size—24 bytes for
MAYO-1 and MAYO-2; 32 bytes for MAYO-3. MAYO-5 is intentionally not supported in HHD
because its 40-byte seed cannot be sourced from a 32-byte SLIP-0010 child key without
expansion.

### Removed

Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ categories = ["cryptography"]
description = "Tectonic's common cryptography library"
documentation = "https://docs.rs/tectonic-bedrock"
edition = "2021"
exclude = ["CLAUDE.md"]
homepage = "https://github.com/tectonic-labs/bedrock"
keywords = ["post-quantum", "crypto", "signature", "kem"]
license = "MIT OR Apache-2.0"
name = "tectonic-bedrock"
readme = "README.md"
repository = "https://github.com/tectonic-labs/bedrock"
version = "0.3.0"
version = "0.4.0"

[features]
default = ["eth_falcon", "falcon", "ml-dsa", "slh-dsa", "mayo", "decp", "encp", "kgen", "sign", "vrfy", "hhd"]
Expand Down
24 changes: 12 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ Two security levels plus Ethereum variant:

### MAYO (Digital Signatures)

MAYO provides multivariate "oil and vinegar" signatures with compact public keys through
MAYO provides multivariate oil-and-vinegar signatures with compact public keys through
the `mayo` feature. It offers four parameter sets at three NIST security levels:

- **MAYO-1** (NIST Level 1) - Default
Expand All @@ -52,9 +52,9 @@ the `mayo` feature. It offers four parameter sets at three NIST security levels:
- **MAYO-5** (NIST Level 5)

HHD key derivation is supported for MAYO-1, MAYO-2, and MAYO-3. The 32-byte
SLIP-0010 child key is truncated to the parameter set's key-generation seed size (24 bytes for
MAYO-1/2, 32 bytes for MAYO-3). MAYO-5 is not offered in HHD because its 40-byte seed
would require expanding, rather than truncating, the child key.
SLIP-0010 child key is truncated to the parameter set's key-generation seed size (24 bytes
for MAYO-1 and MAYO-2; 32 bytes for MAYO-3). MAYO-5 is not offered in HHD because its
40-byte seed would require expanding, rather than truncating, the child key.

### ML-KEM (Key Encapsulation)

Expand Down Expand Up @@ -128,7 +128,7 @@ it through the `frodo` feature. Six parameter sets are available:
- **FrodoKEM-1344-AES** / **FrodoKEM-1344-SHAKE** (NIST Level 5)

The AES and SHAKE variants of a given `n` differ only in how the matrix **A** is derived
and are byte-identical in every key, ciphertext, and shared-secret length. They are
and have identical key, ciphertext, and shared-secret lengths. They are
therefore distinguished exclusively by the scheme stored alongside the key material —
never by encoding length.

Expand Down Expand Up @@ -162,7 +162,7 @@ post-quantum signature. It follows ePrint 2025/1844 and Section 4 of
- **Ed25519-ML-DSA-65**
- **Ed25519-FN-DSA-512**

The Ed25519 commitment `R` is recovered at verification rather than transmitted, so the
The Ed25519 commitment `R` is recovered during verification rather than transmitted, so the
classical half of the signature is a single 32-byte scalar instead of a full 64-byte EdDSA
signature. The classical component is serialized first in both keys and signatures.

Expand Down Expand Up @@ -353,7 +353,7 @@ All key types, signatures, ciphertexts, and shared secrets implement `serde::Ser
and `serde::Deserialize`:

- **Human-readable formats** (JSON, for example): Serialized as hex strings.
- **Binary formats** (postcard and bincode, for example): Serialized as compact byte arrays.
- **Binary formats** (postcard and CBOR, for example): Serialized as compact byte arrays.

Schemes implement the `Display` and `FromStr` traits for string parsing:

Expand Down Expand Up @@ -526,7 +526,7 @@ Control which algorithms and operations are enabled:
- `encp` - Enable encapsulation operations (default)
- `decp` - Enable decapsulation operations (default)

### Features
### Default Features

Bedrock is designed to allow selective features to minimize the dependency list.
The default feature set is:
Expand All @@ -540,25 +540,25 @@ default = ["eth_falcon", "falcon", "ml-dsa", "slh-dsa", "mayo", "decp", "encp",
Verification only (no key generation or signing):

```toml
tectonic-bedrock = { version = "0.3", default-features = false, features = ["ml-dsa", "vrfy"] }
tectonic-bedrock = { version = "0.4", default-features = false, features = ["ml-dsa", "vrfy"] }
```

ML-KEM only:

```toml
tectonic-bedrock = { version = "0.3", default-features = false, features = ["ml-kem", "kgen", "encp", "decp"] }
tectonic-bedrock = { version = "0.4", default-features = false, features = ["ml-kem", "kgen", "encp", "decp"] }
```

X-Wing hybrid KEM only:

```toml
tectonic-bedrock = { version = "0.3", default-features = false, features = ["ml-kem", "xwing", "kgen", "encp", "decp"] }
tectonic-bedrock = { version = "0.4", default-features = false, features = ["ml-kem", "xwing", "kgen", "encp", "decp"] }
```

## Error Handling

All fallible operations return `Result<T, tectonic_bedrock::error::Error>`. The `Error`
enum includes:
enum includes, among others:

- `McElieceError(String)` - Errors from the Classic McEliece KEM.
- `InvalidScheme(u8)` / `InvalidSchemeStr(String)` - Invalid scheme identifiers.
Expand Down
2 changes: 1 addition & 1 deletion src/bird_of_prey.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
//! following the strong-unforgeability-preserving construction of "Bird of
//! Prey" (ePrint 2025/1844) and Section 4 of `draft-prabel-cfrg-suf-hybrid-sigs`.
//!
//! The Ed25519 commitment `R` is recovered at verification
//! The Ed25519 commitment `R` is recovered during verification
//! (`R = rsp*B - chl*A`) rather than transmitted, so the classical part of the
//! signature is a single 32-byte scalar instead of a full 64-byte EdDSA
//! signature. The classical component is serialized first in both keys and
Expand Down
2 changes: 1 addition & 1 deletion src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ pub enum Error {
///
/// Byte length is never a reliable discriminator between parameter sets — MAYO-1
/// and MAYO-2 share a 24-byte compact secret key, and FrodoKEM's AES and SHAKE
/// variants are byte-identical in every dimension — so every dispatch method binds
/// variants have identical lengths in every dimension — so every dispatch method binds
/// to the stored scheme and reports this error on mismatch.
#[error("scheme mismatch: expected '{expected}', got '{actual}'")]
SchemeMismatch {
Expand Down
4 changes: 2 additions & 2 deletions src/hhd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -631,7 +631,7 @@ impl HHDWallet {
&self,
address_index: u32,
) -> Result<(MlDsaSigningKey, MlDsaVerificationKey), WalletError> {
// 1. Extract child seed for the ML-DSA 65 scheme
// 1. Extract the child seed for the ML-DSA-65 scheme.
let signature_seed = self
.master_seeds
.get(&SignatureScheme::MlDsa65)
Expand Down Expand Up @@ -680,7 +680,7 @@ impl HHDWallet {
&self,
address_index: u32,
) -> Result<(MlDsaSigningKey, MlDsaVerificationKey), WalletError> {
// 1. Extract child seed for the ML-DSA 87 scheme
// 1. Extract the child seed for the ML-DSA-87 scheme.
let signature_seed = self
.master_seeds
.get(&SignatureScheme::MlDsa87)
Expand Down
4 changes: 2 additions & 2 deletions src/hhd/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ uses [BIP-32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki),
[SLIP-0010](https://slips.readthedocs.io/en/latest/slip-0010/) to post-quantum schemes.

In summary, BIP-39 generates a master seed from a mnemonic, and BIP-85 generates an
independent child seed for each configured scheme. BIP-85 uses BIP-32 hardened child key
independent child seed for each configured scheme. BIP-85 uses hardened BIP-32 child key
derivation (CKD) with the path `m/83696968'/{app_no}'/{index}'`. The ECDSA branch
(`index = 1`) can use hardened or non-hardened BIP-32 derivation. Falcon (`index = 2`),
ML-DSA (`index = 4, 5, 6`), MAYO (`index = 7, 8, 9`), and HQC
Expand Down Expand Up @@ -279,7 +279,7 @@ not supported because deriving its 40-byte seed would require expansion rather t
truncation. HQC uses all 32 child-key bytes directly for each parameter set.

The original Falcon and ML-DSA proposals do not provide an out-of-the-box key
rerandomization technique, so they cannot use a non-hardened derivation path.
rerandomization technique, so their keychains cannot use a non-hardened derivation path.

```
└─> HMAC-SHA-512("bip-entropy-from-k", k) = child_master_seed
Expand Down
6 changes: 3 additions & 3 deletions src/hhd/bip85.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,9 @@ impl Bip85 {
///
/// - ECDSA secp256k1: `1`
/// - Falcon-512: `2`
/// - ML-DSA 44: `4`
/// - ML-DSA 65: `5`
/// - ML-DSA 87: `6`
/// - ML-DSA-44: `4`
/// - ML-DSA-65: `5`
/// - ML-DSA-87: `6`
/// - MAYO-1: `7`
/// - MAYO-2: `8`
/// - MAYO-3: `9`
Expand Down
4 changes: 2 additions & 2 deletions src/hhd/keys/mldsa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
//!
//! # Key Specifications
//!
//! - **Private key**: 2560 bytes (ML-DSA 44), 4032 bytes (ML-DSA 65), 4896 bytes (ML-DSA 87)
//! - **Public key**: 1312 bytes (ML-DSA 44), 1952 bytes (ML-DSA 65), 2592 bytes (ML-DSA 87)
//! - **Private key**: 2560 bytes (ML-DSA-44), 4032 bytes (ML-DSA-65), 4896 bytes (ML-DSA-87)
//! - **Public key**: 1312 bytes (ML-DSA-44), 1952 bytes (ML-DSA-65), 2592 bytes (ML-DSA-87)
//!
//! # Derivation Path
//!
Expand Down
Loading