Skip to main content

aptos_sdk/crypto/
multi_key.rs

1//! `MultiKey` signature scheme implementation.
2//!
3//! `MultiKey` enables M-of-N threshold signatures with mixed key types.
4//! Unlike `MultiEd25519`, each key can be a different signature scheme
5//! (Ed25519, Secp256k1, Secp256r1, etc.).
6
7use crate::error::{AptosError, AptosResult};
8use serde::{Deserialize, Serialize};
9use std::fmt;
10
11/// Maximum number of keys in a multi-key account.
12pub const MAX_NUM_OF_KEYS: usize = 32;
13
14// Compile-time assertion: MAX_NUM_OF_KEYS must fit in u8 for bitmap operations
15const _: () = assert!(MAX_NUM_OF_KEYS <= u8::MAX as usize);
16
17/// Minimum threshold (at least 1 signature required).
18pub const MIN_THRESHOLD: u8 = 1;
19
20/// Supported signature schemes for multi-key.
21#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
22#[repr(u8)]
23pub enum AnyPublicKeyVariant {
24    /// Ed25519 public key.
25    Ed25519 = 0,
26    /// Secp256k1 ECDSA public key.
27    Secp256k1 = 1,
28    /// Secp256r1 (P-256) ECDSA public key.
29    Secp256r1 = 2,
30    /// Keyless public key.
31    Keyless = 3,
32}
33
34impl AnyPublicKeyVariant {
35    /// Get the variant from a byte.
36    ///
37    /// # Errors
38    ///
39    /// Returns [`AptosError::InvalidPublicKey`] if the byte value is not a valid variant (0-3).
40    pub fn from_byte(byte: u8) -> AptosResult<Self> {
41        match byte {
42            0 => Ok(Self::Ed25519),
43            1 => Ok(Self::Secp256k1),
44            2 => Ok(Self::Secp256r1),
45            3 => Ok(Self::Keyless),
46            _ => Err(AptosError::InvalidPublicKey(format!(
47                "unknown public key variant: {byte}"
48            ))),
49        }
50    }
51
52    /// Get the byte representation.
53    pub fn as_byte(&self) -> u8 {
54        *self as u8
55    }
56}
57
58/// A public key that can be any supported signature scheme.
59#[derive(Clone, PartialEq, Eq)]
60pub struct AnyPublicKey {
61    /// The signature scheme variant.
62    pub variant: AnyPublicKeyVariant,
63    /// The raw public key bytes.
64    pub bytes: Vec<u8>,
65}
66
67impl AnyPublicKey {
68    /// Creates a new `AnyPublicKey`.
69    pub fn new(variant: AnyPublicKeyVariant, bytes: Vec<u8>) -> Self {
70        Self { variant, bytes }
71    }
72
73    /// Creates an Ed25519 public key.
74    #[cfg(feature = "ed25519")]
75    pub fn ed25519(public_key: &crate::crypto::Ed25519PublicKey) -> Self {
76        Self {
77            variant: AnyPublicKeyVariant::Ed25519,
78            bytes: public_key.to_bytes().to_vec(),
79        }
80    }
81
82    /// Creates a Secp256k1 public key.
83    /// Uses the 65-byte SEC1 uncompressed encoding so the chain's
84    /// `bcs::to_bytes(&AnyPublicKey::Secp256k1Ecdsa)` re-serialisation produces
85    /// the same bytes during auth-key derivation. The SDK and chain therefore
86    /// agree on the address.
87    #[cfg(feature = "secp256k1")]
88    pub fn secp256k1(public_key: &crate::crypto::Secp256k1PublicKey) -> Self {
89        Self {
90            variant: AnyPublicKeyVariant::Secp256k1,
91            bytes: public_key.to_uncompressed_bytes(),
92        }
93    }
94
95    /// Creates a Secp256r1 public key.
96    /// Uses 65-byte SEC1 uncompressed encoding (see `secp256k1` for rationale).
97    #[cfg(feature = "secp256r1")]
98    pub fn secp256r1(public_key: &crate::crypto::Secp256r1PublicKey) -> Self {
99        Self {
100            variant: AnyPublicKeyVariant::Secp256r1,
101            bytes: public_key.to_uncompressed_bytes(),
102        }
103    }
104
105    /// Serializes to BCS format: `variant_byte` || ULEB128(length) || bytes
106    ///
107    /// This is the correct BCS serialization format for `AnyPublicKey` used
108    /// in authentication key derivation: `SHA3-256(BCS(AnyPublicKey) || scheme_id)`
109    pub fn to_bcs_bytes(&self) -> Vec<u8> {
110        let mut result = Vec::with_capacity(1 + 1 + self.bytes.len());
111        result.push(self.variant.as_byte());
112        // BCS uses ULEB128 for vector lengths
113        result.extend(uleb128_encode(self.bytes.len()));
114        result.extend_from_slice(&self.bytes);
115        result
116    }
117
118    /// Parses BCS `AnyPublicKey` bytes (`variant` || `ULEB128(len)` || raw key bytes).
119    ///
120    /// # Errors
121    ///
122    /// Returns [`AptosError::InvalidPublicKey`] if the slice is empty, the variant byte is
123    /// unknown, the length prefix is malformed, the payload is truncated, or trailing bytes
124    /// remain after the declared key bytes.
125    pub fn from_bcs_bytes(bytes: &[u8]) -> AptosResult<Self> {
126        if bytes.is_empty() {
127            return Err(AptosError::InvalidPublicKey(
128                "AnyPublicKey BCS empty".into(),
129            ));
130        }
131        let variant = AnyPublicKeyVariant::from_byte(bytes[0])?;
132        let (len, len_bytes) = uleb128_decode(&bytes[1..]).ok_or_else(|| {
133            AptosError::InvalidPublicKey("AnyPublicKey BCS invalid length prefix".into())
134        })?;
135        let start = 1 + len_bytes;
136        let end = start.checked_add(len).ok_or_else(|| {
137            AptosError::InvalidPublicKey("AnyPublicKey BCS length overflow".into())
138        })?;
139        if end > bytes.len() {
140            return Err(AptosError::InvalidPublicKey(
141                "AnyPublicKey BCS truncated payload".into(),
142            ));
143        }
144        if end != bytes.len() {
145            return Err(AptosError::InvalidPublicKey(
146                "AnyPublicKey BCS trailing bytes".into(),
147            ));
148        }
149        Ok(Self::new(variant, bytes[start..end].to_vec()))
150    }
151
152    /// Verifies a signature against a message.
153    ///
154    /// # Errors
155    ///
156    /// This function will return an error if:
157    /// - The signature variant doesn't match the public key variant
158    /// - The public key bytes are invalid for the variant
159    /// - The signature bytes are invalid for the variant
160    /// - Signature verification fails
161    /// - Verification is not supported for the variant
162    #[allow(unused_variables)]
163    pub fn verify(&self, message: &[u8], signature: &AnySignature) -> AptosResult<()> {
164        if signature.variant != self.variant {
165            return Err(AptosError::InvalidSignature(format!(
166                "signature variant {:?} doesn't match public key variant {:?}",
167                signature.variant, self.variant
168            )));
169        }
170
171        match self.variant {
172            #[cfg(feature = "ed25519")]
173            AnyPublicKeyVariant::Ed25519 => {
174                let pk = crate::crypto::Ed25519PublicKey::from_bytes(&self.bytes)?;
175                let sig = crate::crypto::Ed25519Signature::from_bytes(&signature.bytes)?;
176                pk.verify(message, &sig)
177            }
178            #[cfg(feature = "secp256k1")]
179            AnyPublicKeyVariant::Secp256k1 => {
180                // Public key can be either compressed (33 bytes) or uncompressed (65 bytes)
181                let pk = crate::crypto::Secp256k1PublicKey::from_bytes(&self.bytes)?;
182                let sig = crate::crypto::Secp256k1Signature::from_bytes(&signature.bytes)?;
183                pk.verify(message, &sig)
184            }
185            #[cfg(feature = "secp256r1")]
186            AnyPublicKeyVariant::Secp256r1 => {
187                // Public key can be either compressed (33 bytes) or uncompressed (65 bytes)
188                let pk = crate::crypto::Secp256r1PublicKey::from_bytes(&self.bytes)?;
189                let sig = crate::crypto::Secp256r1Signature::from_bytes(&signature.bytes)?;
190                pk.verify(message, &sig)
191            }
192            #[allow(unreachable_patterns)]
193            _ => Err(AptosError::InvalidPublicKey(format!(
194                "verification not supported for variant {:?}",
195                self.variant
196            ))),
197        }
198    }
199}
200
201impl fmt::Debug for AnyPublicKey {
202    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
203        write!(
204            f,
205            "AnyPublicKey({:?}, {})",
206            self.variant,
207            const_hex::encode_prefixed(&self.bytes)
208        )
209    }
210}
211
212impl fmt::Display for AnyPublicKey {
213    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
214        write!(
215            f,
216            "{:?}:{}",
217            self.variant,
218            const_hex::encode_prefixed(&self.bytes)
219        )
220    }
221}
222
223/// A signature that can be any supported signature scheme.
224#[derive(Clone, PartialEq, Eq)]
225pub struct AnySignature {
226    /// The signature scheme variant.
227    pub variant: AnyPublicKeyVariant,
228    /// The raw signature bytes.
229    pub bytes: Vec<u8>,
230}
231
232impl AnySignature {
233    /// Creates a new `AnySignature`.
234    pub fn new(variant: AnyPublicKeyVariant, bytes: Vec<u8>) -> Self {
235        Self { variant, bytes }
236    }
237
238    /// Creates an Ed25519 signature.
239    #[cfg(feature = "ed25519")]
240    pub fn ed25519(signature: &crate::crypto::Ed25519Signature) -> Self {
241        Self {
242            variant: AnyPublicKeyVariant::Ed25519,
243            bytes: signature.to_bytes().to_vec(),
244        }
245    }
246
247    /// Creates a Secp256k1 signature.
248    #[cfg(feature = "secp256k1")]
249    pub fn secp256k1(signature: &crate::crypto::Secp256k1Signature) -> Self {
250        Self {
251            variant: AnyPublicKeyVariant::Secp256k1,
252            bytes: signature.to_bytes().to_vec(),
253        }
254    }
255
256    /// Creates a Secp256r1 signature.
257    #[cfg(feature = "secp256r1")]
258    pub fn secp256r1(signature: &crate::crypto::Secp256r1Signature) -> Self {
259        Self {
260            variant: AnyPublicKeyVariant::Secp256r1,
261            bytes: signature.to_bytes().to_vec(),
262        }
263    }
264
265    /// Serializes to BCS `AnySignature` bytes.
266    ///
267    /// Most variants use `variant_byte` || `ULEB128(len)` || `payload`.
268    ///
269    /// On Aptos, discriminant `2` is shared by bare `Secp256r1Ecdsa` signatures (64-byte
270    /// payload) and the `WebAuthn` variant, which embeds a `PartialAuthenticatorAssertionResponse`
271    /// struct **without** an outer `ULEB128` length prefix after the enum tag. When this struct
272    /// is present, the first payload byte is `0x00` (`AssertionSignature::Secp256r1Ecdsa`).
273    /// For that shape we serialize as `0x02 || payload` to match the chain and
274    /// [`crate::account::WebAuthnAccount`].
275    pub fn to_bcs_bytes(&self) -> Vec<u8> {
276        if self.variant == AnyPublicKeyVariant::Secp256r1 && self.bytes.len() != 64 {
277            let mut result = Vec::with_capacity(1 + self.bytes.len());
278            result.push(self.variant.as_byte());
279            result.extend_from_slice(&self.bytes);
280            return result;
281        }
282        let mut result = Vec::with_capacity(1 + 1 + self.bytes.len());
283        result.push(self.variant.as_byte());
284        // BCS uses ULEB128 for vector lengths
285        result.extend(uleb128_encode(self.bytes.len()));
286        result.extend_from_slice(&self.bytes);
287        result
288    }
289
290    /// Parses BCS `AnySignature` bytes from the wire.
291    ///
292    /// For variants other than `Secp256r1` (`2`), this expects
293    /// `variant` || `ULEB128(len)` || `payload`.
294    ///
295    /// For discriminant `2`, Aptos uses two layouts:
296    ///
297    /// - Bare ECDSA: `0x02` || `ULEB128(64)` || 64-byte signature. The length prefix always
298    ///   begins with `0x40` when `64` is encoded as a single ULEB128 byte.
299    /// - WebAuthn: `0x02` || BCS(`PartialAuthenticatorAssertionResponse`), which begins with
300    ///   `0x00` (`AssertionSignature::Secp256r1Ecdsa`) — **no** outer `ULEB128` wrapping the
301    ///   struct.
302    ///
303    /// # Errors
304    ///
305    /// Returns [`AptosError::InvalidSignature`] if the slice is empty, the variant byte is
306    /// unknown, the length prefix is malformed, the payload is truncated, or trailing bytes
307    /// remain after the declared signature bytes.
308    pub fn from_bcs_bytes(bytes: &[u8]) -> AptosResult<Self> {
309        if bytes.is_empty() {
310            return Err(AptosError::InvalidSignature(
311                "AnySignature BCS empty".into(),
312            ));
313        }
314        let variant = AnyPublicKeyVariant::from_byte(bytes[0]).map_err(|e| {
315            AptosError::InvalidSignature(format!("AnySignature BCS bad variant: {e}"))
316        })?;
317        if variant == AnyPublicKeyVariant::Secp256r1 && bytes.len() > 1 && bytes[1] == 0x00 {
318            return Ok(Self::new(variant, bytes[1..].to_vec()));
319        }
320        let (len, len_bytes) = uleb128_decode(&bytes[1..]).ok_or_else(|| {
321            AptosError::InvalidSignature("AnySignature BCS invalid length prefix".into())
322        })?;
323        let start = 1 + len_bytes;
324        let end = start.checked_add(len).ok_or_else(|| {
325            AptosError::InvalidSignature("AnySignature BCS length overflow".into())
326        })?;
327        if end > bytes.len() {
328            return Err(AptosError::InvalidSignature(
329                "AnySignature BCS truncated payload".into(),
330            ));
331        }
332        if end != bytes.len() {
333            return Err(AptosError::InvalidSignature(
334                "AnySignature BCS trailing bytes".into(),
335            ));
336        }
337        Ok(Self::new(variant, bytes[start..end].to_vec()))
338    }
339}
340
341/// Encodes a value as `ULEB128` (unsigned `LEB128`).
342/// BCS uses `ULEB128` for encoding vector/sequence lengths.
343/// For typical sizes (< 128), this returns a single byte.
344#[allow(clippy::cast_possible_truncation)] // value & 0x7F is always <= 127
345#[inline]
346pub(crate) fn uleb128_encode(mut value: usize) -> Vec<u8> {
347    // Pre-allocate for common case: values < 128 need 1 byte, < 16384 need 2 bytes
348    let mut result = Vec::with_capacity(if value < 128 { 1 } else { 2 });
349    loop {
350        let byte = (value & 0x7F) as u8;
351        value >>= 7;
352        if value == 0 {
353            result.push(byte);
354            break;
355        }
356        result.push(byte | 0x80);
357    }
358    result
359}
360
361/// Decodes a `ULEB128` value from bytes, returning `(value, bytes_consumed)`.
362fn uleb128_decode(bytes: &[u8]) -> Option<(usize, usize)> {
363    let mut result: usize = 0;
364    let mut shift = 0;
365    for (i, &byte) in bytes.iter().enumerate() {
366        result |= ((byte & 0x7F) as usize) << shift;
367        if byte & 0x80 == 0 {
368            return Some((result, i + 1));
369        }
370        shift += 7;
371        if shift >= 64 {
372            return None; // Overflow
373        }
374    }
375    None
376}
377
378impl fmt::Debug for AnySignature {
379    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
380        write!(
381            f,
382            "AnySignature({:?}, {} bytes)",
383            self.variant,
384            self.bytes.len()
385        )
386    }
387}
388
389/// A multi-key public key supporting mixed signature schemes.
390///
391/// This allows M-of-N threshold signing where each key can be a different type.
392#[derive(Clone, PartialEq, Eq)]
393pub struct MultiKeyPublicKey {
394    /// The individual public keys.
395    public_keys: Vec<AnyPublicKey>,
396    /// The required threshold (M in M-of-N).
397    threshold: u8,
398}
399
400impl MultiKeyPublicKey {
401    /// Creates a new multi-key public key.
402    ///
403    /// # Arguments
404    ///
405    /// * `public_keys` - The individual public keys (can be mixed types)
406    /// * `threshold` - The number of signatures required (M in M-of-N)
407    ///
408    /// # Errors
409    ///
410    /// Returns [`AptosError::InvalidPublicKey`] if:
411    /// - No public keys are provided
412    /// - More than 32 public keys are provided
413    /// - Threshold is 0
414    /// - Threshold exceeds the number of keys
415    pub fn new(public_keys: Vec<AnyPublicKey>, threshold: u8) -> AptosResult<Self> {
416        if public_keys.is_empty() {
417            return Err(AptosError::InvalidPublicKey(
418                "multi-key requires at least one public key".into(),
419            ));
420        }
421        if public_keys.len() > MAX_NUM_OF_KEYS {
422            return Err(AptosError::InvalidPublicKey(format!(
423                "multi-key supports at most {} keys, got {}",
424                MAX_NUM_OF_KEYS,
425                public_keys.len()
426            )));
427        }
428        if threshold < MIN_THRESHOLD {
429            return Err(AptosError::InvalidPublicKey(
430                "threshold must be at least 1".into(),
431            ));
432        }
433        if threshold as usize > public_keys.len() {
434            return Err(AptosError::InvalidPublicKey(format!(
435                "threshold {} exceeds number of keys {}",
436                threshold,
437                public_keys.len()
438            )));
439        }
440        Ok(Self {
441            public_keys,
442            threshold,
443        })
444    }
445
446    /// Returns the number of public keys.
447    pub fn num_keys(&self) -> usize {
448        self.public_keys.len()
449    }
450
451    /// Returns the threshold.
452    pub fn threshold(&self) -> u8 {
453        self.threshold
454    }
455
456    /// Returns the individual public keys.
457    pub fn public_keys(&self) -> &[AnyPublicKey] {
458        &self.public_keys
459    }
460
461    /// Returns the key at the given index.
462    pub fn get(&self, index: usize) -> Option<&AnyPublicKey> {
463        self.public_keys.get(index)
464    }
465
466    /// Serializes to bytes for authentication key derivation.
467    ///
468    /// Format: `num_keys` || `pk1_bcs` || `pk2_bcs` || ... || threshold
469    #[allow(clippy::cast_possible_truncation)] // public_keys.len() <= MAX_NUM_OF_KEYS (32)
470    pub fn to_bytes(&self) -> Vec<u8> {
471        // Pre-allocate: 1 byte num_keys + estimated key size (avg ~35 bytes per key) + 1 byte threshold
472        let estimated_size = 2 + self.public_keys.len() * 36;
473        let mut bytes = Vec::with_capacity(estimated_size);
474
475        // Number of keys (1 byte, validated in new())
476        bytes.push(self.public_keys.len() as u8);
477
478        // Each public key in BCS format
479        for pk in &self.public_keys {
480            bytes.extend(pk.to_bcs_bytes());
481        }
482
483        // Threshold (1 byte)
484        bytes.push(self.threshold);
485
486        bytes
487    }
488
489    /// Creates from bytes.
490    ///
491    /// # Errors
492    ///
493    /// Returns [`AptosError::InvalidPublicKey`] if:
494    /// - The bytes are empty
495    /// - The number of keys is invalid (0 or > 32)
496    /// - The bytes are too short for the expected structure
497    /// - Any public key variant byte is invalid
498    /// - Any public key length or data is invalid
499    /// - The threshold is invalid
500    pub fn from_bytes(bytes: &[u8]) -> AptosResult<Self> {
501        // SECURITY: Limit individual key size to prevent DoS via large allocations
502        // Largest supported key is uncompressed secp256k1/secp256r1 at 65 bytes
503        const MAX_KEY_SIZE: usize = 128;
504
505        if bytes.is_empty() {
506            return Err(AptosError::InvalidPublicKey("empty bytes".into()));
507        }
508
509        let num_keys = bytes[0] as usize;
510        if num_keys == 0 || num_keys > MAX_NUM_OF_KEYS {
511            return Err(AptosError::InvalidPublicKey(format!(
512                "invalid number of keys: {num_keys}"
513            )));
514        }
515
516        let mut offset = 1;
517        let mut public_keys = Vec::with_capacity(num_keys);
518
519        for _ in 0..num_keys {
520            if offset >= bytes.len() {
521                return Err(AptosError::InvalidPublicKey("bytes too short".into()));
522            }
523
524            let variant = AnyPublicKeyVariant::from_byte(bytes[offset])?;
525            offset += 1;
526
527            // Decode ULEB128 length
528            let (len, len_bytes) = uleb128_decode(&bytes[offset..]).ok_or_else(|| {
529                AptosError::InvalidPublicKey("invalid ULEB128 length encoding".into())
530            })?;
531            offset += len_bytes;
532
533            if len > MAX_KEY_SIZE {
534                return Err(AptosError::InvalidPublicKey(format!(
535                    "key size {len} exceeds maximum {MAX_KEY_SIZE}"
536                )));
537            }
538
539            if offset + len > bytes.len() {
540                return Err(AptosError::InvalidPublicKey(
541                    "bytes too short for key".into(),
542                ));
543            }
544
545            let key_bytes = bytes[offset..offset + len].to_vec();
546            offset += len;
547
548            public_keys.push(AnyPublicKey::new(variant, key_bytes));
549        }
550
551        if offset >= bytes.len() {
552            return Err(AptosError::InvalidPublicKey(
553                "bytes too short for threshold".into(),
554            ));
555        }
556
557        let threshold = bytes[offset];
558
559        Self::new(public_keys, threshold)
560    }
561
562    /// Derives the account address for this multi-key public key.
563    pub fn to_address(&self) -> crate::types::AccountAddress {
564        crate::crypto::derive_address(&self.to_bytes(), crate::crypto::MULTI_KEY_SCHEME)
565    }
566
567    /// Derives the authentication key for this public key.
568    pub fn to_authentication_key(&self) -> [u8; 32] {
569        crate::crypto::derive_authentication_key(&self.to_bytes(), crate::crypto::MULTI_KEY_SCHEME)
570    }
571
572    /// Verifies a multi-key signature against a message.
573    ///
574    /// # Errors
575    ///
576    /// This function will return an error if:
577    /// - The number of signatures is less than the threshold
578    /// - Any individual signature verification fails
579    /// - A signer index is out of bounds
580    pub fn verify(&self, message: &[u8], signature: &MultiKeySignature) -> AptosResult<()> {
581        // Check that we have enough signatures
582        if signature.num_signatures() < self.threshold as usize {
583            return Err(AptosError::SignatureVerificationFailed);
584        }
585
586        // Verify each signature
587        for (index, sig) in signature.signatures() {
588            if *index as usize >= self.public_keys.len() {
589                return Err(AptosError::InvalidSignature(format!(
590                    "signer index {} out of bounds (max {})",
591                    index,
592                    self.public_keys.len() - 1
593                )));
594            }
595            let pk = &self.public_keys[*index as usize];
596            pk.verify(message, sig)?;
597        }
598
599        Ok(())
600    }
601}
602
603impl fmt::Debug for MultiKeyPublicKey {
604    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
605        write!(
606            f,
607            "MultiKeyPublicKey({}-of-{} keys)",
608            self.threshold,
609            self.public_keys.len()
610        )
611    }
612}
613
614impl fmt::Display for MultiKeyPublicKey {
615    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
616        f.write_str(&const_hex::encode_prefixed(self.to_bytes()))
617    }
618}
619
620/// A multi-key signature containing signatures from multiple signers.
621#[derive(Clone, PartialEq, Eq)]
622pub struct MultiKeySignature {
623    /// Individual signatures with their signer index.
624    signatures: Vec<(u8, AnySignature)>,
625    /// Bitmap indicating which keys signed (little-endian, up to 4 bytes for 32 keys).
626    bitmap: [u8; 4],
627}
628
629impl MultiKeySignature {
630    /// Creates a new multi-key signature from individual signatures.
631    ///
632    /// # Arguments
633    ///
634    /// * `signatures` - Vec of (`signer_index`, signature) pairs
635    ///
636    /// # Errors
637    ///
638    /// Returns [`AptosError::InvalidSignature`] if:
639    /// - No signatures are provided
640    /// - More than 32 signatures are provided
641    /// - A signer index is out of bounds (>= 32)
642    /// - Duplicate signer indices are present
643    pub fn new(mut signatures: Vec<(u8, AnySignature)>) -> AptosResult<Self> {
644        if signatures.is_empty() {
645            return Err(AptosError::InvalidSignature(
646                "multi-key signature requires at least one signature".into(),
647            ));
648        }
649        if signatures.len() > MAX_NUM_OF_KEYS {
650            return Err(AptosError::InvalidSignature(format!(
651                "too many signatures: {} (max {})",
652                signatures.len(),
653                MAX_NUM_OF_KEYS
654            )));
655        }
656
657        // Sort by index
658        signatures.sort_by_key(|(idx, _)| *idx);
659
660        // Check for duplicates and bounds, build bitmap
661        let mut bitmap = [0u8; 4];
662        let mut last_index: Option<u8> = None;
663
664        for (index, _) in &signatures {
665            if *index as usize >= MAX_NUM_OF_KEYS {
666                return Err(AptosError::InvalidSignature(format!(
667                    "signer index {} out of bounds (max {})",
668                    index,
669                    MAX_NUM_OF_KEYS - 1
670                )));
671            }
672            if last_index == Some(*index) {
673                return Err(AptosError::InvalidSignature(format!(
674                    "duplicate signer index {index}"
675                )));
676            }
677            last_index = Some(*index);
678
679            // Set bit in bitmap
680            // Aptos uses MSB-first ordering within each bitmap byte
681            // (matches `aptos_bitvec::BitVec::set`: `0b1000_0000 >> pos`).
682            // index 0 -> bit 7 of byte 0, index 7 -> bit 0 of byte 0, etc.
683            let byte_index = (index / 8) as usize;
684            let bit_in_byte = index % 8;
685            bitmap[byte_index] |= 0b1000_0000u8 >> bit_in_byte;
686        }
687
688        Ok(Self { signatures, bitmap })
689    }
690
691    /// Returns the number of signatures.
692    pub fn num_signatures(&self) -> usize {
693        self.signatures.len()
694    }
695
696    /// Returns the individual signatures with their indices.
697    pub fn signatures(&self) -> &[(u8, AnySignature)] {
698        &self.signatures
699    }
700
701    /// Returns the signer bitmap.
702    pub fn bitmap(&self) -> &[u8; 4] {
703        &self.bitmap
704    }
705
706    /// Checks if a particular index signed.
707    pub fn has_signature(&self, index: u8) -> bool {
708        if index as usize >= MAX_NUM_OF_KEYS {
709            return false;
710        }
711        let byte_index = (index / 8) as usize;
712        let bit_in_byte = index % 8;
713        (self.bitmap[byte_index] & (0b1000_0000u8 >> bit_in_byte)) != 0
714    }
715
716    /// Serializes to bytes, matching the on-chain BCS layout of
717    /// `MultiKeyAuthenticator { signatures: Vec<AnySignature>, signatures_bitmap: BitVec }`.
718    ///
719    /// Wire layout: `ULEB128(num_sigs) || BCS(AnySignature)... || BCS(BitVec)`
720    /// where `BCS(BitVec) = ULEB128(num_bitmap_bytes) || bitmap_bytes`.
721    #[allow(clippy::cast_possible_truncation)] // signatures.len() <= MAX_NUM_OF_KEYS (32)
722    pub fn to_bytes(&self) -> Vec<u8> {
723        // Pre-allocate: 1 byte num_sigs + estimated sig size (~68 bytes per sig)
724        // + 1 byte bitmap length prefix + 4 bytes bitmap.
725        let estimated_size = 1 + self.signatures.len() * 68 + 1 + 4;
726        let mut bytes = Vec::with_capacity(estimated_size);
727
728        // ULEB128(num_signatures). num_sigs <= 32 fits in a single byte.
729        bytes.push(self.signatures.len() as u8);
730
731        // Each AnySignature in BCS format (ordered by signer index).
732        for (_, sig) in &self.signatures {
733            bytes.extend(sig.to_bcs_bytes());
734        }
735
736        // BitVec BCS prefix: ULEB128(4).
737        bytes.push(4);
738        // Bitmap bytes.
739        bytes.extend_from_slice(&self.bitmap);
740
741        bytes
742    }
743
744    /// Creates from bytes.
745    ///
746    /// # Errors
747    ///
748    /// Returns [`AptosError::InvalidSignature`] if:
749    /// - The bytes are too short (less than 5 bytes for `num_sigs` + bitmap)
750    /// - The number of signatures is invalid (0 or > 32)
751    /// - The bitmap doesn't match the number of signatures
752    /// - The bytes are too short for the expected structure
753    /// - Any signature variant byte is invalid
754    /// - Any signature length or data is invalid
755    pub fn from_bytes(bytes: &[u8]) -> AptosResult<Self> {
756        // SECURITY: Limit individual signature size to prevent DoS via large allocations
757        // Largest supported signature is ~72 bytes for ECDSA DER format
758        const MAX_SIGNATURE_SIZE: usize = 128;
759
760        // Wire layout (matches `to_bytes`):
761        //   ULEB128(num_sigs) || sigs... || ULEB128(4) || 4 bitmap bytes
762        // Minimum length is 1 (num_sigs) + 1 (BitVec length prefix) + 4 (bitmap) = 6.
763        if bytes.len() < 6 {
764            return Err(AptosError::InvalidSignature("bytes too short".into()));
765        }
766
767        let num_sigs = bytes[0] as usize;
768        if num_sigs == 0 || num_sigs > MAX_NUM_OF_KEYS {
769            return Err(AptosError::InvalidSignature(format!(
770                "invalid number of signatures: {num_sigs}"
771            )));
772        }
773
774        // Read bitmap from the end. Last 4 bytes are the bitmap bytes; the byte
775        // just before is the ULEB128(4) length prefix from BCS(BitVec).
776        let bitmap_start = bytes.len() - 4;
777        let mut bitmap = [0u8; 4];
778        bitmap.copy_from_slice(&bytes[bitmap_start..]);
779        let bitvec_prefix_idx = bitmap_start.checked_sub(1).ok_or_else(|| {
780            AptosError::InvalidSignature("MultiKeySignature too short for BitVec prefix".into())
781        })?;
782        if bytes[bitvec_prefix_idx] != 4 {
783            return Err(AptosError::InvalidSignature(
784                "MultiKeySignature: expected BCS BitVec length prefix = 4".into(),
785            ));
786        }
787
788        // Parse signatures
789        let mut offset = 1;
790        let mut signatures = Vec::with_capacity(num_sigs);
791
792        // MSB-first bit order (matches aptos-core's `aptos_bitvec::BitVec`).
793        let mut signer_indices = Vec::new();
794        #[allow(clippy::cast_possible_truncation)]
795        for bit_pos in 0..(MAX_NUM_OF_KEYS as u8) {
796            let byte_idx = (bit_pos / 8) as usize;
797            let bit_in_byte = bit_pos % 8;
798            if (bitmap[byte_idx] & (0b1000_0000u8 >> bit_in_byte)) != 0 {
799                signer_indices.push(bit_pos);
800            }
801        }
802
803        if signer_indices.len() != num_sigs {
804            return Err(AptosError::InvalidSignature(
805                "bitmap doesn't match number of signatures".into(),
806            ));
807        }
808
809        // Signatures occupy the range `[1, bitvec_prefix_idx)` -- everything
810        // between the leading num_sigs byte and the BCS BitVec length prefix.
811        let sigs_end = bitvec_prefix_idx;
812        for &index in &signer_indices {
813            if offset >= sigs_end {
814                return Err(AptosError::InvalidSignature("bytes too short".into()));
815            }
816
817            let variant = AnyPublicKeyVariant::from_byte(bytes[offset])?;
818            offset += 1;
819
820            // Decode ULEB128 length
821            let (len, len_bytes) = uleb128_decode(&bytes[offset..sigs_end]).ok_or_else(|| {
822                AptosError::InvalidSignature("invalid ULEB128 length encoding".into())
823            })?;
824            offset += len_bytes;
825
826            if len > MAX_SIGNATURE_SIZE {
827                return Err(AptosError::InvalidSignature(format!(
828                    "signature size {len} exceeds maximum {MAX_SIGNATURE_SIZE}"
829                )));
830            }
831
832            if offset + len > sigs_end {
833                return Err(AptosError::InvalidSignature(
834                    "bytes too short for signature".into(),
835                ));
836            }
837
838            let sig_bytes = bytes[offset..offset + len].to_vec();
839            offset += len;
840
841            signatures.push((index, AnySignature::new(variant, sig_bytes)));
842        }
843
844        Ok(Self { signatures, bitmap })
845    }
846}
847
848impl fmt::Debug for MultiKeySignature {
849    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
850        write!(
851            f,
852            "MultiKeySignature({} signatures, bitmap={:?})",
853            self.signatures.len(),
854            self.bitmap
855        )
856    }
857}
858
859impl fmt::Display for MultiKeySignature {
860    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
861        f.write_str(&const_hex::encode_prefixed(self.to_bytes()))
862    }
863}
864
865#[cfg(test)]
866mod tests {
867    use super::*;
868
869    #[test]
870    fn test_any_public_key_variant_from_byte() {
871        assert_eq!(
872            AnyPublicKeyVariant::from_byte(0).unwrap(),
873            AnyPublicKeyVariant::Ed25519
874        );
875        assert_eq!(
876            AnyPublicKeyVariant::from_byte(1).unwrap(),
877            AnyPublicKeyVariant::Secp256k1
878        );
879        assert_eq!(
880            AnyPublicKeyVariant::from_byte(2).unwrap(),
881            AnyPublicKeyVariant::Secp256r1
882        );
883        assert_eq!(
884            AnyPublicKeyVariant::from_byte(3).unwrap(),
885            AnyPublicKeyVariant::Keyless
886        );
887        assert!(AnyPublicKeyVariant::from_byte(4).is_err());
888        assert!(AnyPublicKeyVariant::from_byte(255).is_err());
889    }
890
891    #[test]
892    fn test_any_public_key_variant_as_byte() {
893        assert_eq!(AnyPublicKeyVariant::Ed25519.as_byte(), 0);
894        assert_eq!(AnyPublicKeyVariant::Secp256k1.as_byte(), 1);
895        assert_eq!(AnyPublicKeyVariant::Secp256r1.as_byte(), 2);
896        assert_eq!(AnyPublicKeyVariant::Keyless.as_byte(), 3);
897    }
898
899    #[test]
900    fn test_any_public_key_new() {
901        let pk = AnyPublicKey::new(AnyPublicKeyVariant::Ed25519, vec![0x11; 32]);
902        assert_eq!(pk.variant, AnyPublicKeyVariant::Ed25519);
903        assert_eq!(pk.bytes.len(), 32);
904        assert_eq!(pk.bytes[0], 0x11);
905    }
906
907    #[test]
908    fn test_any_public_key_to_bcs_bytes() {
909        let pk = AnyPublicKey::new(AnyPublicKeyVariant::Ed25519, vec![0xaa; 32]);
910        let bcs = pk.to_bcs_bytes();
911
912        // Format: variant_byte || ULEB128(length) || bytes
913        assert_eq!(bcs[0], 0); // Ed25519 variant
914        assert_eq!(bcs[1], 32); // ULEB128(32) = 0x20 (since 32 < 128)
915        assert_eq!(bcs[2], 0xaa); // first byte of key
916        assert_eq!(bcs.len(), 1 + 1 + 32); // variant + length + bytes
917    }
918
919    #[test]
920    fn test_any_public_key_debug() {
921        let pk = AnyPublicKey::new(AnyPublicKeyVariant::Secp256k1, vec![0xbb; 33]);
922        let debug = format!("{pk:?}");
923        assert!(debug.contains("Secp256k1"));
924        assert!(debug.contains("0x"));
925    }
926
927    #[test]
928    fn test_any_signature_new() {
929        let sig = AnySignature::new(AnyPublicKeyVariant::Ed25519, vec![0xcc; 64]);
930        assert_eq!(sig.variant, AnyPublicKeyVariant::Ed25519);
931        assert_eq!(sig.bytes.len(), 64);
932    }
933
934    #[test]
935    fn test_any_signature_to_bcs_bytes() {
936        let sig = AnySignature::new(AnyPublicKeyVariant::Ed25519, vec![0xdd; 64]);
937        let bcs = sig.to_bcs_bytes();
938
939        // Format: variant_byte || ULEB128(length) || bytes
940        assert_eq!(bcs[0], 0); // Ed25519 variant
941        assert_eq!(bcs[1], 64); // ULEB128(64) = 0x40 (since 64 < 128)
942        assert_eq!(bcs[2], 0xdd); // first byte of signature
943        assert_eq!(bcs.len(), 1 + 1 + 64); // variant + length + bytes
944    }
945
946    #[test]
947    fn test_any_signature_secp256r1_bare_bcs_roundtrip() {
948        let inner = vec![0x42u8; 64];
949        let mut wire = vec![0x02, 0x40];
950        wire.extend_from_slice(&inner);
951        let sig = AnySignature::from_bcs_bytes(&wire).expect("parse bare Secp256r1");
952        assert_eq!(sig.variant, AnyPublicKeyVariant::Secp256r1);
953        assert_eq!(sig.bytes, inner);
954        assert_eq!(sig.to_bcs_bytes(), wire);
955    }
956
957    #[test]
958    fn test_any_signature_webauthn_style_bcs_roundtrip() {
959        // Synthetic PAAR-shaped payload: starts with AssertionSignature::Secp256r1Ecdsa (0x00)
960        // and is not exactly 64 bytes so `to_bcs_bytes` uses WebAuthn framing.
961        let mut paar = vec![0x00u8, 0x40];
962        paar.extend(vec![0x11u8; 64]);
963        let mut wire = vec![0x02];
964        wire.extend_from_slice(&paar);
965        let sig = AnySignature::from_bcs_bytes(&wire).expect("parse WebAuthn-style");
966        assert_eq!(sig.variant, AnyPublicKeyVariant::Secp256r1);
967        assert_eq!(sig.bytes, paar);
968        assert_eq!(sig.to_bcs_bytes(), wire);
969    }
970
971    #[test]
972    fn test_any_signature_debug() {
973        let sig = AnySignature::new(AnyPublicKeyVariant::Secp256r1, vec![0xee; 64]);
974        let debug = format!("{sig:?}");
975        assert!(debug.contains("Secp256r1"));
976        assert!(debug.contains("64 bytes"));
977    }
978
979    #[test]
980    fn test_any_public_key_verify_mismatched_variant() {
981        let pk = AnyPublicKey::new(AnyPublicKeyVariant::Ed25519, vec![0; 32]);
982        let sig = AnySignature::new(AnyPublicKeyVariant::Secp256k1, vec![0; 64]);
983
984        let result = pk.verify(b"message", &sig);
985        assert!(result.is_err());
986        assert!(result.unwrap_err().to_string().contains("variant"));
987    }
988
989    #[test]
990    fn test_multi_key_signature_insufficient_sigs() {
991        // Empty signatures should fail
992        let result = MultiKeySignature::new(vec![]);
993        assert!(result.is_err());
994    }
995
996    #[test]
997    fn test_multi_key_signature_duplicate_indices() {
998        let sig1 = AnySignature::new(AnyPublicKeyVariant::Ed25519, vec![0; 64]);
999        let sig2 = AnySignature::new(AnyPublicKeyVariant::Ed25519, vec![1; 64]);
1000
1001        // Duplicate index should fail
1002        let result = MultiKeySignature::new(vec![(0, sig1.clone()), (0, sig2)]);
1003        assert!(result.is_err());
1004    }
1005
1006    #[test]
1007    fn test_multi_key_signature_index_out_of_range() {
1008        let sig = AnySignature::new(AnyPublicKeyVariant::Ed25519, vec![0; 64]);
1009
1010        // Index 32 is out of range (0-31)
1011        let result = MultiKeySignature::new(vec![(32, sig)]);
1012        assert!(result.is_err());
1013    }
1014
1015    #[test]
1016    fn test_multi_key_signature_basic() {
1017        let sig1 = AnySignature::new(AnyPublicKeyVariant::Ed25519, vec![0xaa; 64]);
1018        let sig2 = AnySignature::new(AnyPublicKeyVariant::Ed25519, vec![0xbb; 64]);
1019
1020        let multi_sig = MultiKeySignature::new(vec![(0, sig1), (5, sig2)]).unwrap();
1021
1022        assert_eq!(multi_sig.num_signatures(), 2);
1023        assert!(multi_sig.has_signature(0));
1024        assert!(!multi_sig.has_signature(1));
1025        assert!(multi_sig.has_signature(5));
1026    }
1027
1028    #[test]
1029    fn test_multi_key_signature_debug_display() {
1030        let sig = AnySignature::new(AnyPublicKeyVariant::Ed25519, vec![0; 64]);
1031        let multi_sig = MultiKeySignature::new(vec![(0, sig)]).unwrap();
1032
1033        let debug = format!("{multi_sig:?}");
1034        let display = format!("{multi_sig}");
1035
1036        assert!(debug.contains("MultiKeySignature"));
1037        assert!(display.starts_with("0x"));
1038    }
1039
1040    #[test]
1041    #[cfg(feature = "ed25519")]
1042    fn test_multi_key_public_key_creation() {
1043        use crate::crypto::Ed25519PrivateKey;
1044
1045        let keys: Vec<_> = (0..3)
1046            .map(|_| AnyPublicKey::ed25519(&Ed25519PrivateKey::generate().public_key()))
1047            .collect();
1048
1049        // Valid 2-of-3
1050        let multi_pk = MultiKeyPublicKey::new(keys.clone(), 2).unwrap();
1051        assert_eq!(multi_pk.num_keys(), 3);
1052        assert_eq!(multi_pk.threshold(), 2);
1053
1054        // Invalid: threshold > num_keys
1055        assert!(MultiKeyPublicKey::new(keys.clone(), 4).is_err());
1056
1057        // Invalid: threshold = 0
1058        assert!(MultiKeyPublicKey::new(keys.clone(), 0).is_err());
1059
1060        // Invalid: empty keys
1061        assert!(MultiKeyPublicKey::new(vec![], 1).is_err());
1062    }
1063
1064    #[test]
1065    #[cfg(all(feature = "ed25519", feature = "secp256k1"))]
1066    fn test_multi_key_mixed_types() {
1067        use crate::crypto::{Ed25519PrivateKey, Secp256k1PrivateKey};
1068
1069        // Create mixed key types
1070        let ed_key = AnyPublicKey::ed25519(&Ed25519PrivateKey::generate().public_key());
1071        let secp_key = AnyPublicKey::secp256k1(&Secp256k1PrivateKey::generate().public_key());
1072
1073        let multi_pk = MultiKeyPublicKey::new(vec![ed_key, secp_key], 2).unwrap();
1074        assert_eq!(multi_pk.num_keys(), 2);
1075        assert_eq!(
1076            multi_pk.get(0).unwrap().variant,
1077            AnyPublicKeyVariant::Ed25519
1078        );
1079        assert_eq!(
1080            multi_pk.get(1).unwrap().variant,
1081            AnyPublicKeyVariant::Secp256k1
1082        );
1083    }
1084
1085    #[test]
1086    #[cfg(feature = "ed25519")]
1087    fn test_multi_key_sign_verify() {
1088        use crate::crypto::Ed25519PrivateKey;
1089
1090        let private_keys: Vec<_> = (0..3).map(|_| Ed25519PrivateKey::generate()).collect();
1091        let public_keys: Vec<_> = private_keys
1092            .iter()
1093            .map(|k| AnyPublicKey::ed25519(&k.public_key()))
1094            .collect();
1095
1096        let multi_pk = MultiKeyPublicKey::new(public_keys, 2).unwrap();
1097        let message = b"test message";
1098
1099        // Sign with keys 0 and 2 (2-of-3)
1100        let sig0 = AnySignature::ed25519(&private_keys[0].sign(message));
1101        let sig2 = AnySignature::ed25519(&private_keys[2].sign(message));
1102
1103        let multi_sig = MultiKeySignature::new(vec![(0, sig0), (2, sig2)]).unwrap();
1104
1105        // Verify should succeed
1106        assert!(multi_pk.verify(message, &multi_sig).is_ok());
1107
1108        // Wrong message should fail
1109        assert!(multi_pk.verify(b"wrong message", &multi_sig).is_err());
1110    }
1111
1112    #[test]
1113    #[cfg(feature = "ed25519")]
1114    fn test_multi_key_bytes_roundtrip() {
1115        use crate::crypto::Ed25519PrivateKey;
1116
1117        let keys: Vec<_> = (0..3)
1118            .map(|_| AnyPublicKey::ed25519(&Ed25519PrivateKey::generate().public_key()))
1119            .collect();
1120        let multi_pk = MultiKeyPublicKey::new(keys, 2).unwrap();
1121
1122        let bytes = multi_pk.to_bytes();
1123        let restored = MultiKeyPublicKey::from_bytes(&bytes).unwrap();
1124
1125        assert_eq!(multi_pk.threshold(), restored.threshold());
1126        assert_eq!(multi_pk.num_keys(), restored.num_keys());
1127    }
1128
1129    #[test]
1130    #[cfg(feature = "ed25519")]
1131    fn test_multi_key_signature_bytes_roundtrip() {
1132        use crate::crypto::Ed25519PrivateKey;
1133
1134        let private_keys: Vec<_> = (0..3).map(|_| Ed25519PrivateKey::generate()).collect();
1135        let message = b"test";
1136
1137        let sig0 = AnySignature::ed25519(&private_keys[0].sign(message));
1138        let sig2 = AnySignature::ed25519(&private_keys[2].sign(message));
1139
1140        let multi_sig = MultiKeySignature::new(vec![(0, sig0), (2, sig2)]).unwrap();
1141
1142        let bytes = multi_sig.to_bytes();
1143        let restored = MultiKeySignature::from_bytes(&bytes).unwrap();
1144
1145        assert_eq!(multi_sig.num_signatures(), restored.num_signatures());
1146        assert_eq!(multi_sig.bitmap(), restored.bitmap());
1147    }
1148
1149    #[test]
1150    #[cfg(feature = "ed25519")]
1151    fn test_signature_bitmap() {
1152        use crate::crypto::Ed25519PrivateKey;
1153
1154        let private_keys: Vec<_> = (0..5).map(|_| Ed25519PrivateKey::generate()).collect();
1155        let message = b"test";
1156
1157        // Sign with indices 1, 3, 4
1158        let signatures: Vec<_> = [1, 3, 4]
1159            .iter()
1160            .map(|&i| {
1161                (
1162                    i,
1163                    AnySignature::ed25519(&private_keys[i as usize].sign(message)),
1164                )
1165            })
1166            .collect();
1167
1168        let multi_sig = MultiKeySignature::new(signatures).unwrap();
1169
1170        assert!(!multi_sig.has_signature(0));
1171        assert!(multi_sig.has_signature(1));
1172        assert!(!multi_sig.has_signature(2));
1173        assert!(multi_sig.has_signature(3));
1174        assert!(multi_sig.has_signature(4));
1175        assert!(!multi_sig.has_signature(5));
1176    }
1177
1178    #[test]
1179    fn test_multi_key_public_key_empty_keys() {
1180        let result = MultiKeyPublicKey::new(vec![], 1);
1181        assert!(result.is_err());
1182        assert!(result.unwrap_err().to_string().contains("at least one"));
1183    }
1184
1185    #[test]
1186    #[cfg(feature = "ed25519")]
1187    fn test_multi_key_public_key_threshold_zero() {
1188        use crate::crypto::Ed25519PrivateKey;
1189
1190        let keys: Vec<_> = (0..2)
1191            .map(|_| AnyPublicKey::ed25519(&Ed25519PrivateKey::generate().public_key()))
1192            .collect();
1193        let result = MultiKeyPublicKey::new(keys, 0);
1194        assert!(result.is_err());
1195        assert!(result.unwrap_err().to_string().contains("at least 1"));
1196    }
1197
1198    #[test]
1199    #[cfg(feature = "ed25519")]
1200    fn test_multi_key_public_key_threshold_exceeds() {
1201        use crate::crypto::Ed25519PrivateKey;
1202
1203        let keys: Vec<_> = (0..2)
1204            .map(|_| AnyPublicKey::ed25519(&Ed25519PrivateKey::generate().public_key()))
1205            .collect();
1206        let result = MultiKeyPublicKey::new(keys, 5);
1207        assert!(result.is_err());
1208        assert!(result.unwrap_err().to_string().contains("exceed"));
1209    }
1210
1211    #[test]
1212    #[cfg(feature = "ed25519")]
1213    fn test_multi_key_signature_empty() {
1214        let result = MultiKeySignature::new(vec![]);
1215        assert!(result.is_err());
1216        assert!(result.unwrap_err().to_string().contains("at least one"));
1217    }
1218
1219    #[test]
1220    #[cfg(feature = "ed25519")]
1221    fn test_multi_key_signature_duplicate_index() {
1222        use crate::crypto::Ed25519PrivateKey;
1223
1224        let private_key = Ed25519PrivateKey::generate();
1225        let sig = AnySignature::ed25519(&private_key.sign(b"test"));
1226
1227        let result = MultiKeySignature::new(vec![(0, sig.clone()), (0, sig)]);
1228        assert!(result.is_err());
1229        assert!(result.unwrap_err().to_string().contains("duplicate"));
1230    }
1231
1232    #[test]
1233    #[cfg(feature = "ed25519")]
1234    fn test_multi_key_public_key_accessors() {
1235        use crate::crypto::Ed25519PrivateKey;
1236
1237        let keys: Vec<_> = (0..3)
1238            .map(|_| AnyPublicKey::ed25519(&Ed25519PrivateKey::generate().public_key()))
1239            .collect();
1240        let multi_pk = MultiKeyPublicKey::new(keys, 2).unwrap();
1241
1242        assert_eq!(multi_pk.threshold(), 2);
1243        assert_eq!(multi_pk.num_keys(), 3);
1244        assert_eq!(multi_pk.public_keys().len(), 3);
1245    }
1246
1247    #[test]
1248    #[cfg(feature = "ed25519")]
1249    fn test_multi_key_signature_accessors() {
1250        use crate::crypto::Ed25519PrivateKey;
1251
1252        let private_key = Ed25519PrivateKey::generate();
1253        let sig0 = AnySignature::ed25519(&private_key.sign(b"test"));
1254        let sig2 = AnySignature::ed25519(&private_key.sign(b"test"));
1255
1256        let multi_sig = MultiKeySignature::new(vec![(0, sig0), (2, sig2)]).unwrap();
1257
1258        assert_eq!(multi_sig.num_signatures(), 2);
1259        assert_eq!(multi_sig.signatures().len(), 2);
1260    }
1261
1262    #[test]
1263    #[cfg(feature = "ed25519")]
1264    fn test_multi_key_public_key_debug() {
1265        use crate::crypto::Ed25519PrivateKey;
1266
1267        let keys: Vec<_> = (0..2)
1268            .map(|_| AnyPublicKey::ed25519(&Ed25519PrivateKey::generate().public_key()))
1269            .collect();
1270        let multi_pk = MultiKeyPublicKey::new(keys, 2).unwrap();
1271
1272        let debug = format!("{multi_pk:?}");
1273        assert!(debug.contains("MultiKeyPublicKey"));
1274    }
1275
1276    #[test]
1277    #[cfg(feature = "ed25519")]
1278    fn test_multi_key_signature_debug() {
1279        use crate::crypto::Ed25519PrivateKey;
1280
1281        let private_key = Ed25519PrivateKey::generate();
1282        let sig = AnySignature::ed25519(&private_key.sign(b"test"));
1283        let multi_sig = MultiKeySignature::new(vec![(0, sig)]).unwrap();
1284
1285        let debug = format!("{multi_sig:?}");
1286        assert!(debug.contains("MultiKeySignature"));
1287    }
1288
1289    #[test]
1290    #[cfg(feature = "ed25519")]
1291    fn test_multi_key_public_key_display() {
1292        use crate::crypto::Ed25519PrivateKey;
1293
1294        let keys: Vec<_> = (0..2)
1295            .map(|_| AnyPublicKey::ed25519(&Ed25519PrivateKey::generate().public_key()))
1296            .collect();
1297        let multi_pk = MultiKeyPublicKey::new(keys, 2).unwrap();
1298
1299        let display = format!("{multi_pk}");
1300        assert!(display.starts_with("0x"));
1301    }
1302
1303    #[test]
1304    #[cfg(feature = "ed25519")]
1305    fn test_multi_key_signature_display() {
1306        use crate::crypto::Ed25519PrivateKey;
1307
1308        let private_key = Ed25519PrivateKey::generate();
1309        let sig = AnySignature::ed25519(&private_key.sign(b"test"));
1310        let multi_sig = MultiKeySignature::new(vec![(0, sig)]).unwrap();
1311
1312        let display = format!("{multi_sig}");
1313        assert!(display.starts_with("0x"));
1314    }
1315
1316    #[test]
1317    #[cfg(feature = "ed25519")]
1318    fn test_multi_key_public_key_to_address() {
1319        use crate::crypto::Ed25519PrivateKey;
1320
1321        let keys: Vec<_> = (0..2)
1322            .map(|_| AnyPublicKey::ed25519(&Ed25519PrivateKey::generate().public_key()))
1323            .collect();
1324        let multi_pk = MultiKeyPublicKey::new(keys, 2).unwrap();
1325
1326        let address = multi_pk.to_address();
1327        assert!(!address.is_zero());
1328    }
1329
1330    #[test]
1331    fn test_any_public_key_variant_debug() {
1332        let variant = AnyPublicKeyVariant::Ed25519;
1333        let debug = format!("{variant:?}");
1334        assert!(debug.contains("Ed25519"));
1335    }
1336
1337    #[test]
1338    #[cfg(feature = "ed25519")]
1339    fn test_any_public_key_ed25519_debug() {
1340        use crate::crypto::Ed25519PrivateKey;
1341
1342        let pk = AnyPublicKey::ed25519(&Ed25519PrivateKey::generate().public_key());
1343        let debug = format!("{pk:?}");
1344        assert!(debug.contains("Ed25519"));
1345    }
1346
1347    #[test]
1348    #[cfg(feature = "ed25519")]
1349    fn test_any_signature_ed25519_debug() {
1350        use crate::crypto::Ed25519PrivateKey;
1351
1352        let private_key = Ed25519PrivateKey::generate();
1353        let sig = AnySignature::ed25519(&private_key.sign(b"test"));
1354        let debug = format!("{sig:?}");
1355        assert!(debug.contains("Ed25519"));
1356    }
1357
1358    #[test]
1359    #[cfg(feature = "ed25519")]
1360    fn test_multi_key_insufficient_signatures() {
1361        use crate::crypto::Ed25519PrivateKey;
1362
1363        let private_keys: Vec<_> = (0..3).map(|_| Ed25519PrivateKey::generate()).collect();
1364        let public_keys: Vec<_> = private_keys
1365            .iter()
1366            .map(|k| AnyPublicKey::ed25519(&k.public_key()))
1367            .collect();
1368
1369        let multi_pk = MultiKeyPublicKey::new(public_keys, 2).unwrap();
1370        let message = b"test message";
1371
1372        // Only provide 1 signature when threshold is 2
1373        let sig0 = AnySignature::ed25519(&private_keys[0].sign(message));
1374        let multi_sig = MultiKeySignature::new(vec![(0, sig0)]).unwrap();
1375
1376        // Verify should fail
1377        let result = multi_pk.verify(message, &multi_sig);
1378        assert!(result.is_err());
1379    }
1380}