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: 6 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "multi-sig"
version = "1.0.2"
version = "1.0.3"
edition = "2021"
authors = ["Dave Grantham <dwg@linuxprogrammer.org>"]
description = "Multisig self-describing multicodec implementation for digital signatures"
Expand All @@ -14,16 +14,21 @@ categories = ["cryptography", "encoding"]
default = ["serde"]

[dependencies]
ciborium = "0.2"
chacha20poly1305 = "0.10"
elliptic-curve = "0.14"
getrandom = { version = "0.2" }
# blsful configured per-target below (blst for native, rust for wasm)
multi-base = "1.0"
multi-codec = "1.0"
multi-key = { version = "1.0", path = "../multi-key" }
multi-trait = "1.0"
multi-util = "1.0"
serde = { version = "1.0", default-features = false, features = ["alloc", "derive"], optional = true }
ssh-encoding = "0.3"
thiserror = { version = "2.0" }
unsigned-varint = { version = "0.8", features = ["std"] }
zeroize = "1"

[target.'cfg(target_arch = "wasm32")'.dependencies]
blsful = { version = "4.0.0-rc1", default-features = false, features = ["rust"] }
Expand Down
563 changes: 466 additions & 97 deletions README.md

Large diffs are not rendered by default.

15 changes: 15 additions & 0 deletions src/attrid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ pub enum AttrId {
ShareIdentifier,
/// codec-specific threshold signature data
ThresholdData,
/// Threshold disclosure mode (varuint u8): 0=Full, 1=Partial, 2=FullConfidentialial.
ThresholdDisclosure,
/// AEAD-encrypted threshold metadata CBOR blob.
EncryptedThresholdMeta,
/// CBOR-encoded cipher info (codec + nonce) for decrypting EncryptedThresholdMeta.
ThresholdMetaCipher,
}

impl AttrId {
Expand All @@ -41,6 +47,9 @@ impl AttrId {
Self::Limit => "limit",
Self::ShareIdentifier => "share-identifier",
Self::ThresholdData => "threshold-data",
Self::ThresholdDisclosure => "threshold-disclosure",
Self::EncryptedThresholdMeta => "encrypted-threshold-meta",
Self::ThresholdMetaCipher => "threshold-meta-cipher",
}
}
}
Expand All @@ -63,6 +72,9 @@ impl TryFrom<u8> for AttrId {
4 => Ok(Self::Limit),
5 => Ok(Self::ShareIdentifier),
6 => Ok(Self::ThresholdData),
7 => Ok(Self::ThresholdDisclosure),
8 => Ok(Self::EncryptedThresholdMeta),
9 => Ok(Self::ThresholdMetaCipher),
_ => Err(AttributesError::InvalidAttributeValue(c).into()),
}
}
Expand Down Expand Up @@ -104,6 +116,9 @@ impl TryFrom<&str> for AttrId {
"limit" => Ok(Self::Limit),
"share-identifier" => Ok(Self::ShareIdentifier),
"threshold-data" => Ok(Self::ThresholdData),
"threshold-disclosure" => Ok(Self::ThresholdDisclosure),
"encrypted-threshold-meta" => Ok(Self::EncryptedThresholdMeta),
"threshold-meta-cipher" => Ok(Self::ThresholdMetaCipher),
_ => Err(AttributesError::InvalidAttributeName(s.to_string()).into()),
}
}
Expand Down
22 changes: 21 additions & 1 deletion src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ pub enum AttributesError {
#[error("Signature missing threshold")]
MissingThreshold,
/// No limit attribute
#[error("Signature missing limi")]
#[error("Signature missing limit")]
MissingLimit,
/// No identifier attribute
#[error("Signature missing identifier")]
Expand Down Expand Up @@ -121,6 +121,26 @@ pub enum SharesError {
/// Not enough shares to reconstruct the siganture
#[error("Not enough shares to reconstruct the signature")]
NotEnoughShares,
/// Threshold metadata encryption/decryption error
#[error("Threshold metadata error: {0}")]
MetaEncryption(String),
/// Missing threshold metadata key for decrypting t/n
#[error("Missing threshold metadata key")]
MissingMetaKey,
/// Threshold disclosure mode mismatch between shares
#[error("Threshold disclosure mode mismatch: expected {expected}, found {found}")]
DisclosureMismatch {
/// Expected disclosure mode code
expected: u8,
/// Found disclosure mode code
found: u8,
},
/// Duplicate share identifier
#[error("Duplicate share identifier")]
DuplicateShare,
/// Invalid or corrupted threshold data
#[error("Invalid threshold data: {0}")]
InvalidThresholdData(String),
}

/// Conversion errors
Expand Down
5 changes: 4 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,10 @@ pub use types::{SignatureBytes, SignatureScheme};

/// Views on the multisig
pub mod views;
pub use views::{AttrView, ConvView, DataView, ThresholdAttrView, ThresholdView, Views};
pub use views::{
AttrView, ConvView, DataView, ThresholdAttrView, ThresholdDisclosureView, ThresholdView,
Views,
};

/// Serde serialization
#[cfg(feature = "serde")]
Expand Down
32 changes: 31 additions & 1 deletion src/ms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::{
views::{
bls12381::{self, SchemeTypeId},
ed25519, ed25519_hybrid, ed25519_mayo2, fn_dsa, mayo, ml_dsa, nist_p, rsa, secp256k1,
slh_dsa,
slh_dsa, threshold_meta, DisclosureView, ThresholdDisclosureView,
},
AttrId, AttrView, ConvView, DataView, Error, ThresholdAttrView, ThresholdView, Views,
};
Expand Down Expand Up @@ -353,6 +353,11 @@ impl Views for Multisig {
_ => Err(AttributesError::UnsupportedCodec(self.codec).into()),
}
}

/// Provide an interface for threshold disclosure mode operations
fn disclosure_view<'a>(&'a self) -> Result<Box<dyn ThresholdDisclosureView + 'a>, Error> {
Ok(Box::new(DisclosureView::new(self)))
}
}

/// Builder for Multisigs
Expand Down Expand Up @@ -564,6 +569,31 @@ impl Builder {
self.with_attribute(AttrId::ThresholdData, &tdata.as_ref().to_vec())
}

/// Set the disclosure mode for a threshold sig share being built.
///
/// In Full mode, t and n are plaintext. In Partial/FullConfidentialial,
/// `meta_key` is required.
pub fn with_disclosure(
self,
mode: multi_key::ThresholdDisclosure,
meta_key: Option<&multi_key::Multikey>,
threshold: usize,
limit: usize,
) -> Self {
let mut attributes = self.attributes.unwrap_or_default();
let _ = threshold_meta::stamp_disclosure_attrs(
&mut attributes,
mode,
threshold,
limit,
meta_key,
);
Self {
attributes: Some(attributes),
..self
}
}

/// add a signature share
pub fn add_signature_share(mut self, share: &Multisig) -> Self {
let mut shares = self.shares.unwrap_or_default();
Expand Down
41 changes: 41 additions & 0 deletions src/views.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
use crate::{Error, Multisig};
use multi_codec::Codec;
use multi_key::ThresholdDisclosure;

/// BLS12 381 G1/G2 signature implementation
pub mod bls12381;
Expand All @@ -24,6 +25,9 @@ pub mod rsa;
pub mod secp256k1;
/// SLH-DSA post-quantum signature implementation; FIPS 205
pub mod slh_dsa;
/// Threshold disclosure modes and encrypted metadata helpers.
pub mod threshold_meta;
pub use threshold_meta::{DisclosureView, disclosure_mode, read_threshold_params, stamp_disclosure_attrs};

///
/// Attributes views let you inquire about the Multisig and retrieve data
Expand Down Expand Up @@ -65,10 +69,45 @@ pub trait ThresholdAttrView {
pub trait ThresholdView {
/// get the signature shares from this multisig
fn shares(&self) -> Result<Vec<Multisig>, Error>;
/// get the signature shares with a specific disclosure mode applied
fn shares_with_disclosure(
&self,
mode: ThresholdDisclosure,
meta_key: Option<&multi_key::Multikey>,
) -> Result<Vec<Multisig>, Error>;
/// add a new share and return the Multisig with the share added
fn add_share(&self, share: &Multisig) -> Result<Multisig, Error>;
/// add a share with a meta_key for decrypting threshold params
fn add_share_with_meta(
&self,
share: &Multisig,
meta_key: Option<&multi_key::Multikey>,
) -> Result<Multisig, Error>;
/// reconstruct the signature from the shares
fn combine(&self) -> Result<Multisig, Error>;
/// combine with a meta_key for decrypting threshold params
fn combine_with_meta(
&self,
meta_key: Option<&multi_key::Multikey>,
) -> Result<Multisig, Error>;
}

/// trait for threshold disclosure mode operations on a Multisig
pub trait ThresholdDisclosureView {
/// Get the current disclosure mode. Returns Full if no mode attribute is present.
fn disclosure_mode(&self) -> Result<ThresholdDisclosure, Error>;
/// Read t and n, decrypting if necessary. Requires `meta_key` for encrypted modes.
fn read_threshold_params(
&self,
meta_key: Option<&multi_key::Multikey>,
) -> Result<(usize, usize), Error>;
/// Convert to a target disclosure mode.
fn to_disclosure(
&self,
target: ThresholdDisclosure,
meta_key: Option<&multi_key::Multikey>,
current_meta_key: Option<&multi_key::Multikey>,
) -> Result<Multisig, Error>;
}

/// trait for getting the other views
Expand All @@ -83,4 +122,6 @@ pub trait Views {
fn threshold_attr_view<'a>(&'a self) -> Result<Box<dyn ThresholdAttrView + 'a>, Error>;
/// Provide the view for adding a share to a multisig
fn threshold_view<'a>(&'a self) -> Result<Box<dyn ThresholdView + 'a>, Error>;
/// Provide an interface for threshold disclosure mode operations
fn disclosure_view<'a>(&'a self) -> Result<Box<dyn ThresholdDisclosureView + 'a>, Error>;
}
Loading
Loading