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

## [2.1.1] - 2026-10-07

### Changed

- Migrated internal `Multisig` view construction from `multi_sig::Views` to `multi_sig::ViewBuilder`. Requirement `multi-sig` raised 1.4 → 1.5. No public API changes.

## [2.1.0] - 2026-10-07

### Added
Expand Down Expand Up @@ -259,6 +265,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Major dependency updates: ed25519-dalek 3, blsful 4, elliptic-curve 0.14, vsss-rs 6, ssh-key 0.7.
- Initial published release on crates.io as `multi-key`.

[2.1.1]: https://github.com/cryptidtech/multi-key/compare/v2.1.0...v2.1.1
[2.1.0]: https://github.com/cryptidtech/multi-key/compare/v2.0.0...v2.1.0
[1.1.1]: https://github.com/cryptidtech/multi-key/compare/v1.1.0...v1.1.1
[1.2.2]: https://github.com/cryptidtech/multi-key/compare/v1.2.1...v1.2.2
Expand Down
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "multi-key"
version = "2.1.0"
version = "2.1.1"
edition = "2024"
rust-version = "1.96"
authors = ["Dave Grantham <dwg@linuxprogrammer.org>"]
Expand Down Expand Up @@ -54,7 +54,7 @@ ml-kem = "0.3"
multi-base = { version = "1.0", default-features = false }
multi-codec = "1.5"
multi-hash = "1.1"
multi-sig = "1.4"
multi-sig = "1.5"
multi-trait = { version = "1.0", default-features = false }
multi-util = "1.1"
p256 = { version = "0.14", features = ["ecdsa", "ecdh"] }
Expand Down
16 changes: 8 additions & 8 deletions src/views/bls12381.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use blsful::{
use elliptic_curve::group::GroupEncoding;
use multi_codec::Codec;
use multi_hash::{Multihash, mh};
use multi_sig::{Multisig, Views as SigViews, ms, views::bls12381::SchemeTypeId};
use multi_sig::{Multisig, ViewBuilder, ms, views::bls12381::SchemeTypeId};
use multi_trait::TryDecodeFrom;
use multi_util::{Varbytes, Varuint};
use rand_core::Rng;
Expand Down Expand Up @@ -1202,7 +1202,7 @@ impl<'a> VerifyView for View<'a> {
};

// get the signature scheme
let av = multisig.attr_view()?;
let av = ViewBuilder::new(multisig).attr().build()?;
let sig_scheme = SchemeTypeId::try_from(av.scheme()?)?;

match pubmk.codec {
Expand All @@ -1212,7 +1212,7 @@ impl<'a> VerifyView for View<'a> {
.map_err(|e| ConversionsError::PublicKeyFailure(e.to_string()))?;

// get the signature data
let sv = multisig.data_view()?;
let sv = ViewBuilder::new(multisig).data().build()?;
let sig = sv.sig_bytes().map_err(|_| VerifyError::MissingSignature)?;

let group_encoding: G1Projective = {
Expand Down Expand Up @@ -1273,11 +1273,11 @@ impl<'a> VerifyView for View<'a> {
);

// get the share identifier
let av = multisig.threshold_attr_view()?;
let av = ViewBuilder::new(multisig).threshold_attr().build()?;
let identifier = bytes_to_identifier(av.identifier()?)?;

// get the signature data
let sv = multisig.data_view()?;
let sv = ViewBuilder::new(multisig).data().build()?;
let value = sv.sig_bytes().map_err(|_| VerifyError::MissingSignature)?;
let bytes: [u8; 48] = value.as_slice().try_into().map_err(|_| {
VerifyError::BadSignature("Invalid signature share bytes".to_string())
Expand Down Expand Up @@ -1316,7 +1316,7 @@ impl<'a> VerifyView for View<'a> {
.map_err(|e| ConversionsError::PublicKeyFailure(e.to_string()))?;

// get the signature data
let sv = multisig.data_view()?;
let sv = ViewBuilder::new(multisig).data().build()?;
let sig = sv.sig_bytes().map_err(|_| VerifyError::MissingSignature)?;

let group_encoding: G2Projective = {
Expand Down Expand Up @@ -1374,11 +1374,11 @@ impl<'a> VerifyView for View<'a> {
);

// get the share identifier
let av = multisig.threshold_attr_view()?;
let av = ViewBuilder::new(multisig).threshold_attr().build()?;
let identifier = bytes_to_identifier(av.identifier()?)?;

// get the signature data
let sv = multisig.data_view()?;
let sv = ViewBuilder::new(multisig).data().build()?;
let value = sv.sig_bytes().map_err(|_| VerifyError::MissingSignature)?;
let bytes: [u8; 96] = value.as_slice().try_into().map_err(|_| {
VerifyError::BadSignature("Invalid signature share bytes".to_string())
Expand Down
4 changes: 2 additions & 2 deletions src/views/bls12381_g1_fndsa512.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use fn_dsa::{
};
use multi_codec::Codec;
use multi_hash::{Multihash, mh};
use multi_sig::{Multisig, Views as SigViews, ms};
use multi_sig::{Multisig, ViewBuilder, ms};
use zeroize::Zeroizing;

const LOGN: u32 = FN_DSA_LOGN_512;
Expand Down Expand Up @@ -211,7 +211,7 @@ impl<'a> VerifyView for View<'a> {
.into());
}

let sv = multisig.data_view()?;
let sv = ViewBuilder::new(multisig).data().build()?;
let sig_bytes = sv.sig_bytes().map_err(|_| VerifyError::MissingSignature)?;
if sig_bytes.len() != bls::BLS_G1_SIG_LEN + signature_size(LOGN) {
return Err(VerifyError::BadSignature("invalid hybrid signature length".into()).into());
Expand Down
4 changes: 2 additions & 2 deletions src/views/bls12381_g1_mayo1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use crate::{
use ml_dsa::signature::{Signer, Verifier};
use multi_codec::Codec;
use multi_hash::{Multihash, mh};
use multi_sig::{Multisig, Views as SigViews, ms};
use multi_sig::{Multisig, ViewBuilder, ms};
use pq_mayo::{KeyPair, Mayo1, Signature, VerifyingKey};
use zeroize::Zeroizing;

Expand Down Expand Up @@ -199,7 +199,7 @@ impl<'a> VerifyView for View<'a> {
.into());
}

let sv = multisig.data_view()?;
let sv = ViewBuilder::new(multisig).data().build()?;
let sig_bytes = sv.sig_bytes().map_err(|_| VerifyError::MissingSignature)?;
if sig_bytes.len() != HYBRID_SIG_LEN {
return Err(VerifyError::BadSignature("invalid hybrid signature length".into()).into());
Expand Down
4 changes: 2 additions & 2 deletions src/views/bls12381_g1_mayo2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use crate::{
use ml_dsa::signature::{Signer, Verifier};
use multi_codec::Codec;
use multi_hash::{Multihash, mh};
use multi_sig::{Multisig, Views as SigViews, ms};
use multi_sig::{Multisig, ViewBuilder, ms};
use pq_mayo::{KeyPair, Mayo2, Signature, VerifyingKey};
use zeroize::Zeroizing;

Expand Down Expand Up @@ -199,7 +199,7 @@ impl<'a> VerifyView for View<'a> {
.into());
}

let sv = multisig.data_view()?;
let sv = ViewBuilder::new(multisig).data().build()?;
let sig_bytes = sv.sig_bytes().map_err(|_| VerifyError::MissingSignature)?;
if sig_bytes.len() != HYBRID_SIG_LEN {
return Err(VerifyError::BadSignature("invalid hybrid signature length".into()).into());
Expand Down
4 changes: 2 additions & 2 deletions src/views/bls12381_g1_mldsa65.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use ml_dsa::{
};
use multi_codec::Codec;
use multi_hash::{Multihash, mh};
use multi_sig::{Multisig, Views as SigViews, ms};
use multi_sig::{Multisig, ViewBuilder, ms};
use zeroize::Zeroizing;

const MLDSA65_SEED_LEN: usize = 32;
Expand Down Expand Up @@ -211,7 +211,7 @@ impl<'a> VerifyView for View<'a> {
.into());
}

let sv = multisig.data_view()?;
let sv = ViewBuilder::new(multisig).data().build()?;
let sig_bytes = sv.sig_bytes().map_err(|_| VerifyError::MissingSignature)?;
if sig_bytes.len() != HYBRID_SIG_LEN {
return Err(VerifyError::BadSignature("invalid hybrid signature length".into()).into());
Expand Down
4 changes: 2 additions & 2 deletions src/views/ed25519.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use ed25519_dalek::{
};
use multi_codec::Codec;
use multi_hash::{Multihash, mh};
use multi_sig::{Multisig, Views as SigViews, ms};
use multi_sig::{Multisig, ViewBuilder, ms};
use multi_trait::TryDecodeFrom;
use multi_util::Varuint;
use zeroize::Zeroizing;
Expand Down Expand Up @@ -318,7 +318,7 @@ impl<'a> VerifyView for View<'a> {
.map_err(|e| ConversionsError::PublicKeyFailure(e.to_string()))?;

// get the signature data
let sv = multisig.data_view()?;
let sv = ViewBuilder::new(multisig).data().build()?;
let sig = sv.sig_bytes().map_err(|_| VerifyError::MissingSignature)?;

// create the signature
Expand Down
4 changes: 2 additions & 2 deletions src/views/ed25519_fndsa512.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use fn_dsa::{
};
use multi_codec::Codec;
use multi_hash::{Multihash, mh};
use multi_sig::{Multisig, Views as SigViews, ms};
use multi_sig::{Multisig, ViewBuilder, ms};
use zeroize::Zeroizing;

const LOGN: u32 = FN_DSA_LOGN_512;
Expand Down Expand Up @@ -223,7 +223,7 @@ impl<'a> VerifyView for View<'a> {
}

// Get signature bytes
let sv = multisig.data_view()?;
let sv = ViewBuilder::new(multisig).data().build()?;
let sig_bytes = sv.sig_bytes().map_err(|_| VerifyError::MissingSignature)?;

if sig_bytes.len() != ED25519_SIG_LEN + signature_size(LOGN) {
Expand Down
4 changes: 2 additions & 2 deletions src/views/ed25519_mayo2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::{
use ed25519_dalek::{Signature as Ed25519Sig, SigningKey, VerifyingKey};
use multi_codec::Codec;
use multi_hash::{Multihash, mh};
use multi_sig::{Multisig, Views as SigViews, ms};
use multi_sig::{Multisig, ViewBuilder, ms};
use pq_mayo::{KeyPair, Mayo2};
use zeroize::Zeroizing;

Expand Down Expand Up @@ -222,7 +222,7 @@ impl<'a> VerifyView for View<'a> {
}

// Get signature bytes
let sv = multisig.data_view()?;
let sv = ViewBuilder::new(multisig).data().build()?;
let sig_bytes = sv.sig_bytes().map_err(|_| VerifyError::MissingSignature)?;

if sig_bytes.len() != HYBRID_SIG_LEN {
Expand Down
4 changes: 2 additions & 2 deletions src/views/ed25519_mldsa65.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use ml_dsa::{
};
use multi_codec::Codec;
use multi_hash::{Multihash, mh};
use multi_sig::{Multisig, Views as SigViews, ms};
use multi_sig::{Multisig, ViewBuilder, ms};
use zeroize::Zeroizing;

const ED25519_SEED_LEN: usize = 32;
Expand Down Expand Up @@ -226,7 +226,7 @@ impl<'a> VerifyView for View<'a> {
}

// Get signature bytes
let sv = multisig.data_view()?;
let sv = ViewBuilder::new(multisig).data().build()?;
let sig_bytes = sv.sig_bytes().map_err(|_| VerifyError::MissingSignature)?;

if sig_bytes.len() != HYBRID_SIG_LEN {
Expand Down
4 changes: 2 additions & 2 deletions src/views/fn_dsa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use fn_dsa::{
};
use multi_codec::Codec;
use multi_hash::{Multihash, mh};
use multi_sig::{Views as _, ms};
use multi_sig::{ViewBuilder, ms};
use ssh_encoding::{Decode, Encode};
use zeroize::Zeroizing;

Expand Down Expand Up @@ -273,7 +273,7 @@ impl<'a> VerifyView for View<'a> {
ConversionsError::PublicKeyFailure("invalid public key length".into()),
)?;

let sv = multisig.data_view()?;
let sv = ViewBuilder::new(multisig).data().build()?;
let sig_bytes = sv.sig_bytes()?;

let expected_sig_len = if key_bytes.len() == vrfy_key_size(FN_DSA_LOGN_512) {
Expand Down
23 changes: 15 additions & 8 deletions src/views/lamport.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use lamport_signature_plus::{
};
use multi_codec::Codec;
use multi_hash::{Multihash, mh};
use multi_sig::{Views as _, ms};
use multi_sig::{ViewBuilder, ms};
use sha2::{Sha256, Sha384, Sha512};
use sha3::{Sha3_256, Sha3_384, Sha3_512};
use shake::{Shake128, Shake256};
Expand Down Expand Up @@ -603,7 +603,7 @@ impl<'a> VerifyView for View<'a> {
let kd = dispatch_data_view(&pubmk)?;
kd.key_bytes()?
};
let sv = multisig.data_view()?;
let sv = ViewBuilder::new(multisig).data().build()?;
let sig = sv.sig_bytes().map_err(|_| VerifyError::MissingSignature)?;
verify_bytes(pubmk.codec, key_bytes.as_slice(), &sig, msg_bytes)
}
Expand Down Expand Up @@ -745,7 +745,7 @@ mod tests {

#[test]
fn test_lamport_threshold_sign_combine() {
use multi_sig::Views as _;
use multi_sig::ViewBuilder as SigViewBuilder;
use multi_util::CodecInfo as _;

let mut rng = rand::rng();
Expand Down Expand Up @@ -796,17 +796,24 @@ mod tests {
let acc = ms::Builder::new(Codec::LamportSha3256Sig)
.try_build()
.unwrap();
let acc = acc
.threshold_view()
let acc = SigViewBuilder::new(&acc)
.threshold()
.build()
.unwrap()
.add_share(&share_sig_a)
.unwrap();
let acc = acc
.threshold_view()
let acc = SigViewBuilder::new(&acc)
.threshold()
.build()
.unwrap()
.add_share(&share_sig_c)
.unwrap();
let combined = acc.threshold_view().unwrap().combine().unwrap();
let combined = SigViewBuilder::new(&acc)
.threshold()
.build()
.unwrap()
.combine()
.unwrap();
assert_eq!(combined.codec(), Codec::LamportSha3256Sig);

// the combined signature verifies under the ORIGINAL public key
Expand Down
19 changes: 15 additions & 4 deletions src/views/lamport_merkle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ use lamport_signature_plus::{
};
use multi_codec::Codec;
use multi_hash::{Multihash, mh};
use multi_sig::{AttrId as MsAttrId, Views as _, ms};
use multi_sig::{AttrId as MsAttrId, ViewBuilder, ms};
use zeroize::Zeroizing;

/// Mt state wire format version byte (lamport_signature_plus 0.5.0).
Expand Down Expand Up @@ -731,7 +731,7 @@ impl<'a> VerifyView for View<'a> {
// wire depth (byte 0) and the signature wire depth (byte 0).
let key_depth = wire_depth_at(key_bytes.as_slice(), 0)?;
check_depth_attribute(&pubmk, key_depth)?;
let sv = multisig.data_view()?;
let sv = ViewBuilder::new(multisig).data().build()?;
let sig = sv.sig_bytes().map_err(|_| VerifyError::MissingSignature)?;
let sig_depth = wire_depth_at(&sig, 0)?;
if sig_depth != key_depth {
Expand Down Expand Up @@ -1017,6 +1017,7 @@ mod tests {
use crate::LAMPORT_MERKLE_KEY_CODECS;
use crate::ViewBuilder;
use multi_sig::AttrId as MsAttrId;
use multi_sig::ViewBuilder as SigViewBuilder;

fn build_priv(codec: Codec, depth: u8) -> Multikey {
Builder::new_from_random_bytes_with_depth(codec, depth, &mut rand::rng())
Expand Down Expand Up @@ -1312,10 +1313,20 @@ mod tests {
.try_build()
.unwrap();
for share_ms in [&s1, &s2] {
let next = acc.threshold_view().unwrap().add_share(share_ms).unwrap();
let next = SigViewBuilder::new(&acc)
.threshold()
.build()
.unwrap()
.add_share(share_ms)
.unwrap();
acc = next;
}
let combined = acc.threshold_view().unwrap().combine().unwrap();
let combined = SigViewBuilder::new(&acc)
.threshold()
.build()
.unwrap()
.combine()
.unwrap();
ViewBuilder::new(&pk)
.verify()
.build()
Expand Down
4 changes: 2 additions & 2 deletions src/views/mayo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::{
};
use multi_codec::Codec;
use multi_hash::{Multihash, mh};
use multi_sig::{Views as _, ms};
use multi_sig::{ViewBuilder, ms};
use pq_mayo::{KeyPair, Mayo1, Mayo2, Mayo3, Mayo5, Signature, VerifyingKey};
use ssh_encoding::{Decode, Encode};
use zeroize::Zeroizing;
Expand Down Expand Up @@ -350,7 +350,7 @@ impl<'a> VerifyView for View<'a> {
kd.key_bytes()?
};

let sv = sig.data_view()?;
let sv = ViewBuilder::new(sig).data().build()?;
let sig_bytes = sv.sig_bytes()?;

match (key_bytes.len(), sig_bytes.len()) {
Expand Down
Loading
Loading