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
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,20 @@ 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).

## [Unreleased]

## [1.4.0] - 2026-09-03

### Added

- The three XMSS msig codecs (`XmssSha210256Msig`, `XmssSha216256Msig`, `XmssSha220256Msig`) and the eleven one-time Lamport sig codecs (SHA3-512/384/256, SHA2-512/384/256, BLAKE2b-512, BLAKE2s-256, BLAKE3-256, SHAKE-128/256) added to `SIG_CODECS`. All fourteen codecs were dispatched by the view tables but absent from the list.
- `SIG_CODECS` now has 60 entries with the `lamport` feature (up from 47) and 35 without (up from 34).
- Invariant tests `test_sig_codecs_unique` (no duplicate codecs in the list) and `test_sig_codecs_dispatch` (every listed codec dispatches to a data view).

### Fixed

- Removed the duplicate `SlhDsaSha2192FMsig` row from both `SIG_CODECS` variants (the codec was listed twice in each list).

## [1.3.0] - 2026-09-01

### Added
Expand Down
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "multi-sig"
version = "1.3.1"
version = "1.4.0"
edition = "2024"
rust-version = "1.96"
authors = ["Dave Grantham <dwg@linuxprogrammer.org>"]
Expand All @@ -21,15 +21,15 @@ serde = []
lamport = ["dep:lamport_signature_plus", "dep:sha3", "dep:sha2", "dep:blake2", "dep:shake", "dep:blake3"]
xmss = ["dep:xmss"]

# Local multi-codec 1.3.0 until it is published (remove at release).
# Local multi-codec 1.4.0 until it is published (remove at release).
[dependencies]
ciborium = "0.2"
chacha20poly1305 = "0.11"
elliptic-curve = "0.14"
getrandom = { version = "0.4" }
# blsful configured per-target below (blst for native, rust for wasm)
multi-base = { version = "1.0", default-features = false }
multi-codec = "1.3"
multi-codec = "1.5"
multi-trait = { version = "1.0", default-features = false }
multi-util = "1.1"
serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] }
Expand Down
68 changes: 62 additions & 6 deletions src/ms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ use std::{collections::BTreeMap, fmt};

/// the list of signature codecs currently supported
#[cfg(feature = "lamport")]
pub const SIG_CODECS: [Codec; 47] = [
pub const SIG_CODECS: [Codec; 60] = [
Codec::Bls12381G1Msig,
Codec::Bls12381G2Msig,
Codec::EddsaMsig,
Expand All @@ -45,7 +45,6 @@ pub const SIG_CODECS: [Codec; 47] = [
Codec::SlhDsaSha2256SMsig,
Codec::SlhDsaShake128FMsig,
Codec::SlhDsaShake128SMsig,
Codec::SlhDsaSha2192FMsig,
Codec::SlhDsaShake192FMsig,
Codec::SlhDsaShake192SMsig,
Codec::SlhDsaShake256FMsig,
Expand Down Expand Up @@ -76,6 +75,34 @@ pub const SIG_CODECS: [Codec; 47] = [
Codec::LamportMerkleBlake3256Sig,
Codec::LamportMerkleShake128Sig,
Codec::LamportMerkleShake256Sig,
#[cfg(feature = "xmss")]
Codec::XmssSha210256Msig,
#[cfg(feature = "xmss")]
Codec::XmssSha216256Msig,
#[cfg(feature = "xmss")]
Codec::XmssSha220256Msig,
#[cfg(feature = "lamport")]
Codec::LamportSha3512Sig,
#[cfg(feature = "lamport")]
Codec::LamportSha3384Sig,
#[cfg(feature = "lamport")]
Codec::LamportSha3256Sig,
#[cfg(feature = "lamport")]
Codec::LamportSha2512Sig,
#[cfg(feature = "lamport")]
Codec::LamportSha2384Sig,
#[cfg(feature = "lamport")]
Codec::LamportSha2256Sig,
#[cfg(feature = "lamport")]
Codec::LamportBlake2B512Sig,
#[cfg(feature = "lamport")]
Codec::LamportBlake2S256Sig,
#[cfg(feature = "lamport")]
Codec::LamportBlake3256Sig,
#[cfg(feature = "lamport")]
Codec::LamportShake128Sig,
#[cfg(feature = "lamport")]
Codec::LamportShake256Sig,
];

/// the list of signature codecs currently supported
Expand All @@ -97,7 +124,6 @@ pub const SIG_CODECS: [Codec; 35] = [
Codec::SlhDsaSha2256SMsig,
Codec::SlhDsaShake128FMsig,
Codec::SlhDsaShake128SMsig,
Codec::SlhDsaSha2192FMsig,
Codec::SlhDsaShake192FMsig,
Codec::SlhDsaShake192SMsig,
Codec::SlhDsaShake256FMsig,
Expand Down Expand Up @@ -358,7 +384,7 @@ impl Views for Multisig {
| Codec::Bls12381G2Msig
| Codec::Bls12381G1ShareMsig
| Codec::Bls12381G2ShareMsig => Ok(Box::new(bls12381::View::try_from(self)?)),
Codec::EddsaMsig => Ok(Box::new(ed25519::View::try_from(self)?)),
Codec::EddsaMsig | Codec::XeddsaMsig => Ok(Box::new(ed25519::View::try_from(self)?)),
Codec::Es256KMsig => Ok(Box::new(secp256k1::View::try_from(self)?)),
Codec::Es256Msig | Codec::Es384Msig | Codec::Es521Msig => {
Ok(Box::new(nist_p::View::try_from(self)?))
Expand Down Expand Up @@ -452,7 +478,7 @@ impl Views for Multisig {
| Codec::Bls12381G2Msig
| Codec::Bls12381G1ShareMsig
| Codec::Bls12381G2ShareMsig => Ok(Box::new(bls12381::View::try_from(self)?)),
Codec::EddsaMsig => Ok(Box::new(ed25519::View::try_from(self)?)),
Codec::EddsaMsig | Codec::XeddsaMsig => Ok(Box::new(ed25519::View::try_from(self)?)),
Codec::Es256KMsig => Ok(Box::new(secp256k1::View::try_from(self)?)),
Codec::Es256Msig | Codec::Es384Msig | Codec::Es521Msig => {
Ok(Box::new(nist_p::View::try_from(self)?))
Expand Down Expand Up @@ -546,7 +572,7 @@ impl Views for Multisig {
| Codec::Bls12381G2Msig
| Codec::Bls12381G1ShareMsig
| Codec::Bls12381G2ShareMsig => Ok(Box::new(bls12381::View::try_from(self)?)),
Codec::EddsaMsig => Ok(Box::new(ed25519::View::try_from(self)?)),
Codec::EddsaMsig | Codec::XeddsaMsig => Ok(Box::new(ed25519::View::try_from(self)?)),
Codec::Es256KMsig => Ok(Box::new(secp256k1::View::try_from(self)?)),
Codec::Es256Msig | Codec::Es384Msig | Codec::Es521Msig => {
Ok(Box::new(nist_p::View::try_from(self)?))
Expand Down Expand Up @@ -1185,6 +1211,36 @@ mod tests {
assert_eq!(ms1, ms2);
}

#[test]
fn test_sig_codecs_unique() {
let mut seen = std::collections::BTreeSet::new();
for codec in SIG_CODECS {
assert!(
seen.insert(codec),
"duplicate codec in SIG_CODECS: {codec:?}"
);
}
}

#[test]
fn test_sig_codecs_dispatch() {
for codec in SIG_CODECS {
let ms = Builder::new(codec)
.with_signature_bytes(&[0u8; 64])
.try_build()
.unwrap();
// Every listed codec must produce a working sign-data view. A
// codec in SIG_CODECS that fails to dispatch here has drifted
// from the view dispatch tables.
let result = ms.data_view();
assert!(
result.is_ok(),
"SIG_CODECS entry {codec:?} does not dispatch to a data view (error kind {:?})",
result.err().map(|e| e.to_string())
);
}
}

#[test]
fn test_eddsa() {
let ms = Builder::new(Codec::EddsaMsig)
Expand Down
Loading