A Rust implementation of the multiformats multisig specification. The published crate is multi-sig. Depend on it as multi-sig = "1.0" in Cargo.toml. Import it as multi_sig in Rust, for example use multi_sig::Builder;.
This crate gives self-describing digital signature containers (Multisig) for 35 signature codecs. The codecs span classical, post-quantum, and hybrid schemes. It supports BLS12-381 threshold signatures with share accumulation and combination. It supports SSH signature interoperability for all classical schemes plus BLS12-381 combined and share signatures.
Supported signature families:
- Classical: Ed25519, secp256k1 (ECDSA), NIST P-256/P-384/P-521 (ECDSA), RSA-SHA256, BLS12-381 G1/G2
- Post-quantum: ML-DSA (65/87), FN-DSA (512/1024), MAYO (1/2/3/5), SLH-DSA (all 12 parameter sets)
- Hybrid: Ed25519+MAYO-2, Ed25519+ML-DSA-65, Ed25519+FN-DSA-512, BLS12-381-G1+ML-DSA-65, BLS12-381-G1+FN-DSA-512, BLS12-381-G1+MAYO-1, BLS12-381-G1+MAYO-2
- Threshold: BLS12-381 G1/G2 combined and share signatures with threshold disclosure modes
SSH interoperability: Ed25519, secp256k1, NIST P-256/P-384/P-521, RSA-SHA256, and BLS12-381 G1/G2 (combined and share signatures) convert to and from OpenSSH format with the ssh-key crate. Non-standard algorithms use RFC 4251 "additional algorithms" names with the @multisig domain suffix, for example secp256k1@multisig and bls12_381-g1-share@multisig.
This is a Rust implementation of a multicodec container format for digital signatures. The design is intentionally abstract. It supports any kind of digital signature data for any protocol. The format is a container of signature data with abstract, protocol-specific views backed by a generic, self-describing data storage format.
Every piece of data in a serialized Multisig object either has a known fixed size or a self-describing variable size via Varuint and Varbytes. Software that processes these objects does not need to support all digital signature protocols to calculate the size of the serialized object and skip over it.
The only operations on a Multisig object are those that return attribute data and the threshold signature operations for accumulating and combining signature shares. Any operation that involves a cryptographic key (for example, signing or verifying) is in the companion Multi-Key crate.
A Multisig is serialized as:
SIGIL (0x1239) | signature_codec | Varbytes(message) | Varuint(num_attributes) |
[ AttrId | Varbytes(attribute_value) ] * num_attributes
- SIGIL — the multicodec
0x1239(Multisig) distinguishes this format from the older Varsig (0x34). - signature_codec — a varuint-encoded multicodec tag that identifies the signature algorithm.
- message —
Varbytes(length-prefixed). If non-empty, the signature is combined (it carries the signed message in-band). If empty, the signature is detached (the message must be supplied out-of-band for verification). - attributes — a counted list of
(AttrId, Varbytes)pairs. Attribute IDs are u8 enum values. The decoder rejects duplicate IDs at decode time. Attributes are emitted inBTreeMaporder (sorted by ID) for deterministic encoding.
The preferred base encoding for Multisig strings is Base16Lower (lowercase hex).
| Codec | Multicodec name | SSH algorithm | Threshold | Notes |
|---|---|---|---|---|
EddsaMsig |
eddsa-msig |
ssh-ed25519 |
no | Ed25519 signatures |
Es256KMsig |
es256k-msig |
secp256k1@multisig |
no | ECDSA over secp256k1 |
Es256Msig |
es256-msig |
ecdsa-sha2-nistp256@multisig |
no | ECDSA over NIST P-256 |
Es384Msig |
es384-msig |
ecdsa-sha2-nistp384@multisig |
no | ECDSA over NIST P-384 |
Es521Msig |
es521-msig |
ecdsa-sha2-nistp521@multisig |
no | ECDSA over NIST P-521 |
Rs256Msig |
rs256-msig |
rsa-sha256@multisig |
no | RSA-SHA256 signatures |
Bls12381G1Msig |
bls12_381-g1-msig |
bls12_381-g1@multisig |
yes | BLS signatures on G1 (48-byte sig) |
Bls12381G2Msig |
bls12_381-g2-msig |
bls12_381-g2@multisig |
yes | BLS signatures on G2 (96-byte sig) |
| Codec | Multicodec name | SSH | Notes |
|---|---|---|---|
Mldsa65Msig |
mldsa-65-msig |
no | ML-DSA (Dilithium) security level 65; FIPS 204 |
Mldsa87Msig |
mldsa-87-msig |
no | ML-DSA security level 87; FIPS 204 |
FnDsa512Msig |
fn-dsa-512-msig |
no | FN-DSA (Falcon) 512; FIPS 206 (draft) |
FnDsa1024Msig |
fn-dsa-1024-msig |
no | FN-DSA (Falcon) 1024; FIPS 206 (draft) |
Mayo1Msig |
mayo-1-msig |
no | MAYO-1 |
Mayo2Msig |
mayo-2-msig |
no | MAYO-2 |
Mayo3Msig |
mayo-3-msig |
no | MAYO-3 |
Mayo5Msig |
mayo-5-msig |
no | MAYO-5 |
SlhdsaSha2128FMsig |
slhdsa-sha2-128f-msig |
no | SLH-DSA (SPHINCS+) SHA-2 128f; FIPS 205 |
SlhdsaSha2128SMsig |
slhdsa-sha2-128s-msig |
no | SLH-DSA SHA-2 128s |
SlhdsaSha2192FMsig |
slhdsa-sha2-192f-msig |
no | SLH-DSA SHA-2 192f |
SlhdsaSha2192SMsig |
slhdsa-sha2-192s-msig |
no | SLH-DSA SHA-2 192s |
SlhdsaSha2256FMsig |
slhdsa-sha2-256f-msig |
no | SLH-DSA SHA-2 256f |
SlhdsaSha2256SMsig |
slhdsa-sha2-256s-msig |
no | SLH-DSA SHA-2 256s |
SlhdsaShake128FMsig |
slhdsa-shake-128f-msig |
no | SLH-DSA SHAKE 128f |
SlhdsaShake128SMsig |
slhdsa-shake-128s-msig |
no | SLH-DSA SHAKE 128s |
SlhdsaShake192FMsig |
slhdsa-shake-192f-msig |
no | SLH-DSA SHAKE 192f |
SlhdsaShake192SMsig |
slhdsa-shake-192s-msig |
no | SLH-DSA SHAKE 192s |
SlhdsaShake256FMsig |
slhdsa-shake-256f-msig |
no | SLH-DSA SHAKE 256f |
SlhdsaShake256SMsig |
slhdsa-shake-256s-msig |
no | SLH-DSA SHAKE 256s |
Hybrid signatures use a nested combiner construction. The classical component signs the message. The PQ component then signs message || classical_signature. Verification requires both components to pass.
| Codec | Multicodec name | Components | SSH |
|---|---|---|---|
Ed25519Mayo2Msig |
ed25519-mayo2-msig |
Ed25519 + MAYO-2 | no |
Ed25519Mldsa65Msig |
ed25519-mldsa65-msig |
Ed25519 + ML-DSA-65 | no |
Ed25519Fndsa512Msig |
ed25519-fndsa512-msig |
Ed25519 + FN-DSA-512 | no |
Bls12381G1Mldsa65Msig |
bls12381-g1-mldsa65-msig |
BLS12-381 G1 + ML-DSA-65 | no |
Bls12381G1Fndsa512Msig |
bls12381-g1-fndsa512-msig |
BLS12-381 G1 + FN-DSA-512 | no |
Bls12381G1Mayo1Msig |
bls12381-g1-mayo1-msig |
BLS12-381 G1 + MAYO-1 | no |
Bls12381G1Mayo2Msig |
bls12381-g1-mayo2-msig |
BLS12-381 G1 + MAYO-2 | no |
| Codec | Multicodec name | SSH algorithm | Notes |
|---|---|---|---|
Bls12381G1ShareMsig |
bls12_381-g1-share-msig |
bls12_381-g1-share@multisig |
A BLS G1 partial signature from a threshold share |
Bls12381G2ShareMsig |
bls12_381-g2-share-msig |
bls12_381-g2-share@multisig |
A BLS G2 partial signature from a threshold share |
Each Multisig carries a set of attributes identified by a u8 code:
| Code | Name | Used by | Description |
|---|---|---|---|
| 0 | sig-data |
all | The raw signature bytes |
| 1 | payload-encoding |
all (optional) | The multicodec encoding of the signed payload |
| 2 | scheme |
BLS | BLS scheme type: 0=Basic, 1=MessageAugmentation, 2=ProofOfPossession |
| 3 | threshold |
BLS shares | The threshold t (plaintext, Full disclosure mode) |
| 4 | limit |
BLS shares | The share count n (plaintext, Full/Partial disclosure modes) |
| 5 | share-identifier |
BLS shares | 32-byte BLS scalar identifier for this share |
| 6 | threshold-data |
BLS combined | Serialized ThresholdData — the accumulated share map |
| 7 | threshold-disclosure |
BLS (optional) | Disclosure mode: 0=Full, 1=Partial, 2=FullConfidentialial |
| 8 | encrypted-threshold-meta |
BLS (optional) | AEAD-encrypted CBOR blob containing t and/or n |
| 9 | threshold-meta-cipher |
BLS (optional) | CBOR-encoded cipher info (codec + nonce) for decrypting #8 |
To provide an abstract interface to digital signatures of all schemes, this crate gives "views" on the Multisig data. These are read-only or copy-on-write abstract interfaces with implementations for different supporting signature protocols.
Create views through the builder-pattern multi_sig::ViewBuilder (re-exported at the crate root and in the prelude). Select a view kind fluently, then build:
use multi_sig::{Builder, ViewBuilder};
use multi_codec::Codec;
let sig_data = vec![0u8; 64];
let ms = Builder::new(Codec::EddsaMsig)
.with_signature_bytes(&sig_data)
.try_build()
.unwrap();
// a data view reads the signature bytes
let data = ViewBuilder::new(&ms).data().build().unwrap();
assert_eq!(data.sig_bytes().unwrap(), sig_data);
// an attributes view reads the payload encoding and the scheme
let attrs = ViewBuilder::new(&ms).attr().build().unwrap();
assert_eq!(attrs.scheme().unwrap(), 0);The six kinds are attr, data, conv, threshold_attr, threshold, and disclosure. Select each with .attr(), .data(), .conv(), .threshold_attr(), .threshold(), or .disclosure(). A view borrows its Multisig, so keep the Multisig alive while the view is in use. Built-in codecs dispatch through the crate-internal views::dispatch core. A built-in view is identical to the view the deprecated Views trait method returns for that codec.
ViewBuilder is Send + Sync when its registered factories are. The view trait objects it returns carry no Send/Sync supertrait, so they cannot be shared across threads at the type level.
A custom scheme is a signature scheme this crate does not standardize yet. It carries a codec outside SIG_CODECS. The convention uses the Codec::Identity codec slot (code 0), which no built-in dispatch table covers. Construct the Multisig with the existing public Builder setters. Builder::new accepts any codec. with_signature_bytes, with_payload_encoding, and with_scheme store their attributes generically. A custom scheme has no built-in views, so ViewBuilder dispatches it to caller-supplied local-codec factories registered with with_local_codec:
use multi_sig::{AttrId, AttrView, Builder, Error, Multisig, ViewBuilder};
use multi_codec::Codec;
use multi_util::Varuint;
// a custom scheme uses the Codec::Identity slot. The public Builder
// setters store its attributes generically
let ms = Builder::new(Codec::Identity)
.with_signature_bytes(b"my-custom-signature".as_slice())
.with_scheme(0x2A)
.try_build()
.unwrap();
struct CustomAttrsView {
scheme: u8,
}
impl AttrView for CustomAttrsView {
fn payload_encoding(&self) -> Result<Codec, Error> {
Ok(Codec::Identity)
}
fn scheme(&self) -> Result<u8, Error> {
Ok(self.scheme)
}
}
// the factory reads the AttrId::Scheme attribute (stored as a varuint)
// and serves its own scheme. Schemes that share the Identity codec slot
// branch on this attribute inside the factory.
let attrs = ViewBuilder::new(&ms)
.attr()
.with_local_codec(Codec::Identity, Box::new(|ms: &Multisig| {
let scheme = ms
.attributes
.get(&AttrId::Scheme)
.and_then(|bytes| Varuint::<u8>::try_from(bytes.as_slice()).ok())
.map(|decoded| *decoded)
.unwrap_or(0);
let view: Box<dyn AttrView> = Box::new(CustomAttrsView { scheme });
Ok(view)
}))
.build()
.unwrap();
assert_eq!(attrs.scheme().unwrap(), 0x2A);Built-in views always win: a factory registered for a codec that has a built-in view is never consulted. A repeat with_local_codec call for the same kind replaces the earlier factory: the last registration wins. Factory errors propagate unchanged. The disclosure view is codec-agnostic, so a factory registered for that kind is never consulted. The wire encoder carries arbitrary attributes, so the built Multisig roundtrips through encode and decode unchanged. The tests in tests/custom_codec_tests.rs exercise this flow end to end.
| Trait | Methods | Purpose |
|---|---|---|
AttrView |
payload_encoding(), scheme() |
Access the payload encoding codec and signing scheme |
DataView |
sig_bytes() |
Access the raw signature bytes |
ConvView |
to_ssh_signature() |
Convert to an OpenSSH ssh_key::Signature |
ThresholdAttrView |
threshold(), limit(), identifier(), threshold_data() |
Read threshold parameters (BLS only) |
ThresholdView |
shares(), shares_with_disclosure(), add_share(), add_share_with_meta(), combine(), combine_with_meta() |
Accumulate and combine threshold signature shares (BLS only) |
ThresholdDisclosureView |
disclosure_mode(), read_threshold_params(), to_disclosure() |
Read or convert the threshold disclosure mode (all codecs) |
Views (deprecated) |
attr_view(), data_view(), conv_view(), threshold_attr_view(), threshold_view(), disclosure_view() |
Dispatcher trait — obtain any view from a Multisig. Deprecated since 1.5.0: use ViewBuilder (removal planned for 2.0.0) |
| Codec family | AttrView |
DataView |
ConvView |
ThresholdAttrView |
ThresholdView |
|---|---|---|---|---|---|
| BLS G1/G2 (combined + share) | bls12381::View |
bls12381::View |
bls12381::View |
bls12381::View |
bls12381::View (combined only) |
| Ed25519 | ed25519::View |
ed25519::View |
ed25519::View |
— | — |
| secp256k1 | secp256k1::View |
secp256k1::View |
secp256k1::View |
— | — |
| NIST P-256/384/521 | nist_p::View |
nist_p::View |
nist_p::View |
— | — |
| RSA | rsa::View |
rsa::View |
rsa::View |
— | — |
| ML-DSA 65/87 | ml_dsa::View |
ml_dsa::View |
ml_dsa::View |
— | — |
| FN-DSA 512/1024 | fn_dsa::View |
fn_dsa::View |
fn_dsa::View |
— | — |
| MAYO 1/2/3/5 | mayo::View |
mayo::View |
mayo::View |
— | — |
| SLH-DSA (all 12) | slh_dsa::View |
slh_dsa::View |
slh_dsa::View |
— | — |
| Ed25519-MAYO2 | ed25519_mayo2::View |
ed25519_mayo2::View |
ed25519_mayo2::View |
— | — |
| Other hybrids | ed25519_hybrid::View |
ed25519_hybrid::View |
ed25519_hybrid::View |
— | — |
Dispatch runs inside ViewBuilder through the crate-internal views::dispatch functions. The table content is unchanged from earlier releases. The disclosure view is codec-agnostic. It is available on all codecs.
Operations that appear to mutate the Multisig (add_share, combine, to_disclosure) in fact perform a copy-on-write (CoW) operation. They return a new Multisig. The original is unchanged. This is most visible in Builder::try_build():
let mut ms = Builder::new(Codec::Bls12381G2Msig).try_build()?;
for share in &shares {
ms = {
let tv = ViewBuilder::new(&ms).threshold().build()?;
// CoW — returns a new Multisig with the share added
tv.add_share(share)?
};
}The Builder constructs Multisig objects. It is a different type from ViewBuilder (Views on the Multisig Data), which builds views over an existing Multisig:
| Method | Description |
|---|---|
Builder::new(codec) |
Create a builder for the given signature codec |
Builder::new_from_ssh_signature(&sig) |
Construct from an OpenSSH ssh_key::Signature |
Builder::new_from_bls_signature(&sig) |
Construct from a blsful::Signature (infers G1/G2 by byte length) |
Builder::new_from_bls_signature_share(t, n, &share) |
Construct from a blsful::SignatureShare |
.with_message_bytes(&msg) |
Set the message payload (makes a combined signature) |
.with_signature_bytes(&data) |
Set the raw signature bytes (AttrId::SigData) |
.with_payload_encoding(codec) |
Set the payload encoding codec |
.with_scheme(scheme_u8) |
Set the BLS scheme type (0/1/2) |
.with_threshold(t) |
Set the threshold value (plaintext) |
.with_limit(n) |
Set the limit value (plaintext) |
.with_identifier(&id) |
Set the share identifier (32-byte BLS scalar) |
.with_threshold_data(&data) |
Set the accumulated threshold data blob |
.with_disclosure(mode, meta_key, t, n) |
Set t/n with a specific disclosure mode (see Threshold Confidentiality) |
.add_signature_share(&share) |
Accumulate a share for try_build() to fold in |
.try_build() |
Build the Multisig (folds in accumulated shares) |
.try_build_encoded() |
Build and wrap in EncodedMultisig (base-encoded string) |
Signature generation and verification are in the companion Multi-Key crate. They use the SignView and VerifyView views on a Multikey, built with multi_key::ViewBuilder. The sign view produces a Multisig. The verify view verifies a Multisig against an optional message.
use multi_key::{Builder, ViewBuilder};
use multi_codec::Codec;
// Generate an Ed25519 key and sign a message
let mk = Builder::new_from_random_bytes(Codec::Ed25519Priv, &mut rand::rng())?
.try_build()?;
// Combined signature (carries the message in-band)
let multisig = ViewBuilder::new(&mk)
.sign()
.build()?
.sign(b"hello world", true, None)?;
// Detached signature (message supplied out-of-band for verification)
let detached = ViewBuilder::new(&mk)
.sign()
.build()?
.sign(b"hello world", false, None)?;use multi_key::ViewBuilder;
// Verify a combined signature (message is carried in the Multisig)
ViewBuilder::new(&mk)
.verify()
.build()?
.verify(&multisig, None)?;
// Verify a detached signature (message supplied separately)
ViewBuilder::new(&mk)
.verify()
.build()?
.verify(&detached, Some(b"hello world"))?;A Multisig is combined if the message field is non-empty. The signed message is carried in-band. No external message is needed for verification. A Multisig is detached if the message field is empty. The verifier must supply the original message out-of-band.
The combined parameter on SignView::sign(msg, combined, scheme) controls this:
combined = true→ the message is stored in theMultisig(combined signature)combined = false→ the message is not stored (detached signature)
For verification, VerifyView::verify(sig, msg):
msg = None→ uses the message stored in the Multisig (combined)msg = Some(bytes)→ uses the externally supplied message (detached)
BLS12-381 is the only signature family that supports threshold signatures in this crate. A threshold BLS signature is produced by multiple parties. Each party signs with their key share. The partial signatures are then combined into a single combined signature. The combined signature verifies against the group public key.
BLS12-381 supports three signature schemes, stored as AttrId::Scheme:
| Scheme | Code | Description |
|---|---|---|
Basic |
0 | Raw BLS. It is vulnerable to rogue-key attacks without PoP checking. |
MessageAugmentation |
1 | Prepends a domain tag to the message before signing. |
ProofOfPossession |
2 | Requires a separate PoP signature over the public key. This is the default and the strongest rogue-key defence. |
- A BLS secret key is split into
nshares with thresholdtusing theMulti-Keycrate'sThresholdView::split(t, n)orsplit_with_disclosure(t, n, mode, meta_key). - Each shareholder signs the message with their key share. This produces a partial signature (
Bls12381G1ShareMsigorBls12381G2ShareMsig). - The partial signatures are accumulated into a combined
MultisigwithThresholdView::add_share()(CoW) oradd_share_with_meta(). - Once at least
tshares are accumulated,ThresholdView::combine()(orcombine_with_meta()) reconstructs the combined BLS signature via Lagrange interpolation in the group.
use multi_key::Builder;
use multi_sig::ViewBuilder;
use multi_codec::Codec;
// Split a BLS G2 key into 3-of-5 shares
let mk = Builder::new_from_random_bytes(Codec::Bls12381G2Priv, &mut rand::rng())?
.try_build()?;
// the companion crate has its own ViewBuilder, so this snippet qualifies it
let shares = multi_key::ViewBuilder::new(&mk).threshold().build()?.split(3, 5)?;
// Each share signs the message (done by the shareholder)
let partial_sigs: Vec<_> = shares.iter()
.map(|s| multi_key::ViewBuilder::new(&s).sign().build()?.sign(b"message", true, Some(2))?) // scheme 2 = PoP
.collect();
// Accumulate shares into a combined Multisig
let mut ms = partial_sigs[0].clone();
for ps in &partial_sigs[1..] {
ms = ViewBuilder::new(&ms).threshold().build()?.add_share(ps)?;
}
// Combine into the final signature
let combined = ViewBuilder::new(&ms).threshold().build()?.combine()?;BLS share signatures can convert to and from SSH format. The SSH algorithm names are bls12_381-g1-share@multisig and bls12_381-g2-share@multisig. The share identifier, threshold, and limit are carried inside the SSH signature blob.
By default, threshold t and share count n are stored as plaintext attributes on every share. Any observer of a share learns the threshold parameters. This crate supports three configurable disclosure modes that control the confidentiality of t and n:
| Mode | t (threshold) |
n (limit) |
Who sees t |
Who sees n |
|---|---|---|---|---|
Full (default, 0) |
plaintext attribute | plaintext attribute | everyone | everyone |
Partial (1) |
encrypted (AEAD) | plaintext attribute | key-holder only | everyone (auditable) |
FullConfidentialial (2) |
encrypted (AEAD) | encrypted (AEAD) | key-holder only | key-holder only |
The encrypted values are sealed with ChaCha20-Poly1305 AEAD. They are stored as a CBOR-encoded ThresholdMetadata blob in AttrId::EncryptedThresholdMeta. The cipher parameters (codec and nonce) are recorded in AttrId::ThresholdMetaCipher so the blob is self-describing for decryption. A separate meta key (a 32-byte symmetric Multikey with Codec::Chacha20Poly1305) is required to encrypt and decrypt the metadata.
-
Full— Use when t and n are not sensitive. This is the default. It is backward-compatible with all existing shares. It is appropriate for open governance systems where the threshold structure is public knowledge. -
Partial— Use when the total number of participantsnshould be auditable (for example, for governance transparency) but the thresholdtshould be hidden from share holders and observers. Hidingtmeans an adversary who compromises some shares does not know how many more they need to reconstruct. Themeta_keyis required to readt. The valuenis freely readable. -
FullConfidentialial— Use when bothtandnmust be kept secret. An observer who sees a share cannot determine the group size or how many shares are needed. This is the strongest confidentiality mode. Themeta_keyis required to read bothtandn.
| Consideration | Full | Partial | FullConfidentialial |
|---|---|---|---|
| Backward compatible | yes | yes (attribute defaults to Full if absent) | yes |
Observer learns t |
yes | no | no |
Observer learns n |
yes | yes | no |
Requires meta_key |
no | for reading t |
for reading t and n |
Auditable n |
yes | yes | no |
Risk if meta_key lost |
n/a | t irrecoverable |
t and n irrecoverable |
| Performance overhead | none | negligible (AEAD on ~10 bytes) | negligible |
Key management risk. Losing the meta_key makes t (Partial) or both t and n (FullConfidentialial) irrecoverable. This prevents share combination. The meta_key should be stored or backed up with the existing at-rest encryption mechanisms. You can always convert back to Full mode (with the meta_key) before you lose it.
There are three ways to produce shares in a given disclosure mode:
1. Direct creation via split_with_disclosure():
use multi_key::{Builder, ThresholdDisclosure, ViewBuilder};
let meta_key = multi_key::generate_meta_key();
let meta_mk = Builder::new(Codec::Chacha20Poly1305)
.with_key_bytes(&meta_key.as_slice())
.try_build()?;
let shares = ViewBuilder::new(&mk).threshold().build()?.split_with_disclosure(3, 5,
ThresholdDisclosure::FullConfidentialial, Some(&meta_mk))?;2. Builder construction:
let share = Builder::new(Codec::Bls12381G2ShareMsig)
.with_disclosure(ThresholdDisclosure::Partial, Some(&meta_mk), 3, 5)
.with_identifier(&identifier)
.with_signature_bytes(&sig_bytes)
.try_build()?;3. Convert an existing share:
let encrypted = ViewBuilder::new(&share)
.disclosure()
.build()?
.to_disclosure(ThresholdDisclosure::FullConfidentialial, Some(&meta_mk), None)?;Use read_threshold_params() with the meta_key to decrypt t and n:
let (t, n) = ViewBuilder::new(&encrypted)
.disclosure()
.build()?
.read_threshold_params(Some(&meta_mk))?;let combined = ViewBuilder::new(&ms)
.threshold()
.build()?
.combine_with_meta(Some(&meta_mk))?;The to_disclosure() method converts between any pair of modes. It reads the current t and n (decrypting if needed with current_meta_key), then re-stamps the attributes in the target mode (encrypting if needed with meta_key):
// Full -> Partial
let partial = ViewBuilder::new(&full)
.disclosure()
.build()?
.to_disclosure(ThresholdDisclosure::Partial, Some(&meta_mk), None)?;
// Partial -> FullConfidentialial
let confidential = ViewBuilder::new(&partial)
.disclosure()
.build()?
.to_disclosure(ThresholdDisclosure::FullConfidentialial, Some(&meta_mk), Some(&meta_mk))?;
// FullConfidentialial -> Full
let full_again = ViewBuilder::new(&confidential)
.disclosure()
.build()?
.to_disclosure(ThresholdDisclosure::Full, None, Some(&meta_mk))?;With the serde feature (default), Multisig supports dual-form serialization:
- Human-readable (JSON, etc.): a struct
{ "codec": "...", "message": "...", "attributes": [...] }wherecodecis the multicodec name,messageis a base-encodedVarbytes, andattributesis a list of(name, base-encoded-value)tuples. - Compact (binary formats): the raw wire-format bytes via
serialize_bytes.
EncodedMultisig serializes as a single base-encoded string in readable form and as raw bytes in compact form. AttrId round-trips as either a name string or a u8.
The types module gives newtypes for type safety:
SignatureBytes(Vec<u8>)— wraps raw signature bytes withDisplay(hex),AsRef<[u8]>, and safe conversions.SignatureScheme(Codec)— wraps aCodecas a signature scheme identifier. It isCopy. It givesname()andcode()accessors.
A multicodec signature format called Varsig (0x34) already exists. It has serious design deficiencies. It relies on out-of-band context for signature-specific values. This makes it impossible to decode without supporting every key codec. Multisig uses a new multicodec sigil 0x1239 to distinguish the two formats. To convert from Varsig to Multisig, pull the relevant data out of the Varsig and use the Builder to construct a Multisig.
| Feature | Default | Description |
|---|---|---|
serde |
yes | Serde serialization for Multisig and AttrId |