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
6 changes: 3 additions & 3 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ env:

jobs:
msrv:
name: Verify MSRV (1.95)
name: Verify MSRV (1.96)
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Install Rust Toolchain (1.95)
uses: dtolnay/rust-toolchain@1.95.0
- name: Install Rust Toolchain (1.96)
uses: dtolnay/rust-toolchain@1.96.0

- name: Check
run: cargo check --all-features
Expand Down
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,28 @@ All notable changes to this project are documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.2.0] - 2026-09-01

### Added

- Merkle-tree Lamport multikey view (`views::lamport_merkle`, behind the default `lamport` feature) wrapping the `lamport_signature_plus` 0.5.0 `Mt*` API. Supports 11 digest variants (SHA3-512/384/256, SHA2-512/384/256, BLAKE2b-512, BLAKE2s-256, BLAKE3-256, SHAKE-128/256) with 55 new codecs (`LamportMerkle*Pub/Priv/PrivShare`, codes `0x1a64`-`0x1a8e`).
- Depth attribute (`AttrId::Depth`, code 29) stamped on every merkle artifact and strictly cross-checked against the depth byte embedded in the `Mt*` wire data; mismatches return `AttributesError::DepthMismatch`.
- `MerkleStateView` trait and `Views::merkle_state_view()`: `depth`, `capacity`, `next_index`, and `remaining_signatures` introspection parsed from the tree state.
- `Builder::new_from_random_bytes_with_depth(codec, depth, rng)` (depth 1..=3 validated and stamped) and `Builder::with_depth(depth)`.
- `LAMPORT_MERKLE_KEY_CODECS` and `LAMPORT_MERKLE_KEY_SHARE_CODECS` codec lists; the 11 `LamportMerkle*PrivShare` codecs added to `KEY_SHARE_CODECS`.
- Threshold support mirroring the one-time Lamport view: `split(t, n)` produces `LamportMerkle*PrivShare` multikeys (GF(256) shares of the whole tree state); share signing produces `LamportMerkle*SigShare` multisigs; accumulation/combination happens in multi-sig. Keys are never reconstructed.
- `sign_advance` method on the `SignView` trait. Stateful signature schemes (XMSS, merkle-tree Lamport) override it to return the `Multisig` AND the advanced `Multikey` (codec, comment, and attributes preserved; only `KeyData` replaced). The caller must persist the advanced key so the consumed one-time slot is never reused. The default implementation returns `UnsupportedAlgorithm`, so stateless schemes and existing external trait impls stay source-compatible.
- `AttrId::Depth` attribute (code 29, name `depth`, one raw byte) for merkle-tree signature schemes.
- `AttributesError::DepthMismatch { expected, found }` error variant.

### Changed

- **Breaking:** removed the free function `multi_key::views::xmss::sign_advance`. Use the `SignView::sign_advance` trait method instead. The trait method returns the advanced key as a full `Multikey` rather than raw secret-key bytes.
- Bumped `lamport_signature_plus` from `0.5.0-rc2` to `0.5.0` (adds the merkle-tree API used by the next release).
- Raised `rust-version` from `1.95` to `1.96` (required by `lamport_signature_plus` 0.5.0).
- `Builder::with_key_bytes` now accepts unsized byte slices (`&(impl AsRef<[u8]> + ?Sized)`).
- `multi-sig` dependency raised to `1.3` (adds `AttrId::Depth`, `Builder::with_depth`, and `Multisig::depth()`).

## [1.1.1] - 2026-08-18

### Fixed
Expand Down
14 changes: 10 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
[package]
name = "multi-key"
version = "1.1.1"
version = "1.2.0"
edition = "2024"
rust-version = "1.95"
rust-version = "1.96"
authors = ["Dave Grantham <dwg@linuxprogrammer.org>"]
description = "Multikey self-describing cryptographic key data"
repository = "https://github.com/cryptidtech/multi-key.git"
Expand Down Expand Up @@ -47,7 +47,7 @@ mceliece348864 = { version = "1.0" }
ml-dsa = "0.1.1"
ml-kem = "0.3"
multi-base = { version = "1.0", default-features = false }
multi-codec = "1.2"
multi-codec = "1.3"
multi-hash = "1.1"
multi-sig = "1.2"
multi-trait = { version = "1.0", default-features = false }
Expand Down Expand Up @@ -77,13 +77,19 @@ vsss-rs = "6.0"
x25519-dalek = { version = "3.0", features = ["static_secrets", "getrandom"] }
zeroize = "1"
# Lamport support (feature-gated, default-enabled)
lamport_signature_plus = { version = "0.5.0-rc2", optional = true }
lamport_signature_plus = { version = "0.5.0", optional = true }
sha3 = { version = "0.12", optional = true }
blake2 = { version = "0.11.0-rc.6", optional = true }
shake = { version = "0.1", optional = true }
# XMSS support (feature-gated, default-enabled)
xmss = { version = "0.1.0-pre.0", optional = true }

# Local multi-codec 1.3.0 and multi-sig with `AttrId::Depth` until published
# (remove at release).
[patch.crates-io]
multi-codec = { path = "../multi-codec" }
multi-sig = { path = "../multi-sig" }

[target.'cfg(target_arch = "wasm32")'.dependencies]
blsful = { version = "4.0.0", default-features = false, features = ["rust"] }
getrandom_02 = { package = "getrandom", version = "0.2", features = ["js"] }
Expand Down
7 changes: 7 additions & 0 deletions src/attrid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ pub enum AttrId {
AlgorithmName,
/// An arbitrary key type for non-standard keys (optional)
KeyType,
/// Merkle-tree signature scheme depth (one raw byte; the tree holds
/// 2^depth one-time leaves). Cross-checked against the depth byte embedded
/// in the key/signature wire data.
Depth,
}

impl AttrId {
Expand Down Expand Up @@ -122,6 +126,7 @@ impl AttrId {
AttrId::ThresholdMetaCipher => "threshold-meta-cipher",
AttrId::AlgorithmName => "algorithm-name",
AttrId::KeyType => "key-type",
AttrId::Depth => "depth",
}
}
}
Expand Down Expand Up @@ -166,6 +171,7 @@ impl TryFrom<u8> for AttrId {
26 => Ok(AttrId::ThresholdMetaCipher),
27 => Ok(AttrId::AlgorithmName),
28 => Ok(AttrId::KeyType),
29 => Ok(AttrId::Depth),
_ => Err(AttributesError::InvalidAttributeValue(c).into()),
}
}
Expand Down Expand Up @@ -230,6 +236,7 @@ impl TryFrom<&str> for AttrId {
"threshold-meta-cipher" => Ok(AttrId::ThresholdMetaCipher),
"algorithm-name" => Ok(AttrId::AlgorithmName),
"key-type" => Ok(AttrId::KeyType),
"depth" => Ok(AttrId::Depth),
_ => Err(AttributesError::InvalidAttributeName(s.to_string()).into()),
}
}
Expand Down
9 changes: 9 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,15 @@ pub enum AttributesError {
/// The threshold marker signature failed verification (tampered marker).
#[error("Threshold marker signature verification failed")]
ThresholdMarkerSigInvalid,
/// A merkle-tree depth attribute does not match the depth byte embedded in
/// the key or signature wire data.
#[error("Depth mismatch: attribute says {expected}, wire data says {found}")]
DepthMismatch {
/// depth declared by the `depth` attribute
expected: u8,
/// depth byte embedded in the wire data
found: u8,
},
}

/// Conversions errors created by this library
Expand Down
6 changes: 4 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,8 @@ pub use views::threshold_meta::{
};
pub use views::{
AttrView, CipherAttrView, CipherView, ConvView, DataView, FingerprintView, KdfAttrView,
KdfView, OpenView, SealView, SignView, ThresholdAttrView, ThresholdDisclosureView,
ThresholdKeyView, ThresholdView, VerifyView, Views,
KdfView, MerkleStateView, OpenView, SealView, SignView, ThresholdAttrView,
ThresholdDisclosureView, ThresholdKeyView, ThresholdView, VerifyView, Views,
};

/// Key splitting / recombination (verifiable threshold shares)
Expand All @@ -155,6 +155,8 @@ pub use mk::{
MAX_DECODED_SIZE, ML_DSA_KEY_CODECS, ML_KEM_KEY_CODECS, Multikey, SLH_DSA_KEY_CODECS,
X25519_KEY_CODECS,
};
#[cfg(feature = "lamport")]
pub use mk::{LAMPORT_MERKLE_KEY_CODECS, LAMPORT_MERKLE_KEY_SHARE_CODECS};

/// Nonce type
pub mod nonce;
Expand Down
Loading
Loading