Skip to main content

sui_sdk_types/crypto/
signature.rs

1use super::Ed25519PublicKey;
2use super::Ed25519Signature;
3use super::MlDsa65PublicKey;
4use super::MlDsa65Signature;
5use super::MultisigAggregatedSignature;
6use super::PasskeyAuthenticator;
7use super::Secp256k1PublicKey;
8use super::Secp256k1Signature;
9use super::Secp256r1PublicKey;
10use super::Secp256r1Signature;
11use super::ZkLoginAuthenticator;
12
13/// A basic signature
14///
15/// This enumeration defines the set of simple or basic signature schemes supported by Sui. Most
16/// signature schemes supported by Sui end up comprising of a at least one simple signature scheme.
17///
18/// # BCS
19///
20/// The BCS serialized form for this type is defined by the following ABNF:
21///
22/// ```text
23/// simple-signature-bcs = bytes ; where the contents of the bytes are defined by <simple-signature>
24/// simple-signature = (ed25519-flag ed25519-signature ed25519-public-key) /
25///                    (secp256k1-flag secp256k1-signature secp256k1-public-key) /
26///                    (secp256r1-flag secp256r1-signature secp256r1-public-key) /
27///                    (mldsa65-flag mldsa65-signature mldsa65-public-key)
28/// ```
29///
30/// Note: Due to historical reasons, signatures are serialized slightly different from the majority
31/// of the types in Sui. In particular if a signature is ever embedded in another structure it
32/// generally is serialized as `bytes` meaning it has a length prefix that defines the length of
33/// the completely serialized signature.
34#[derive(Clone, Debug, PartialEq, Eq, Hash)]
35#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
36#[non_exhaustive]
37pub enum SimpleSignature {
38    Ed25519 {
39        signature: Ed25519Signature,
40        public_key: Ed25519PublicKey,
41    },
42    Secp256k1 {
43        signature: Secp256k1Signature,
44        public_key: Secp256k1PublicKey,
45    },
46    Secp256r1 {
47        signature: Secp256r1Signature,
48        public_key: Secp256r1PublicKey,
49    },
50    // Boxed: 5,261 bytes would otherwise size every enum carrying a signature.
51    MlDsa65 {
52        signature: Box<MlDsa65Signature>,
53        public_key: Box<MlDsa65PublicKey>,
54    },
55}
56
57impl SimpleSignature {
58    /// Return the flag for this signature scheme
59    pub fn scheme(&self) -> SignatureScheme {
60        match self {
61            SimpleSignature::Ed25519 { .. } => SignatureScheme::Ed25519,
62            SimpleSignature::Secp256k1 { .. } => SignatureScheme::Secp256k1,
63            SimpleSignature::Secp256r1 { .. } => SignatureScheme::Secp256r1,
64            SimpleSignature::MlDsa65 { .. } => SignatureScheme::MlDsa65,
65        }
66    }
67}
68
69impl SimpleSignature {
70    #[cfg(feature = "serde")]
71    #[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
72    fn to_bytes(&self) -> Vec<u8> {
73        let mut buf = Vec::new();
74        match self {
75            SimpleSignature::Ed25519 {
76                signature,
77                public_key,
78            } => {
79                buf.push(SignatureScheme::Ed25519 as u8);
80                buf.extend_from_slice(signature.as_ref());
81                buf.extend_from_slice(public_key.as_ref());
82            }
83            SimpleSignature::Secp256k1 {
84                signature,
85                public_key,
86            } => {
87                buf.push(SignatureScheme::Secp256k1 as u8);
88                buf.extend_from_slice(signature.as_ref());
89                buf.extend_from_slice(public_key.as_ref());
90            }
91            SimpleSignature::Secp256r1 {
92                signature,
93                public_key,
94            } => {
95                buf.push(SignatureScheme::Secp256r1 as u8);
96                buf.extend_from_slice(signature.as_ref());
97                buf.extend_from_slice(public_key.as_ref());
98            }
99            SimpleSignature::MlDsa65 {
100                signature,
101                public_key,
102            } => {
103                buf.push(SignatureScheme::MlDsa65 as u8);
104                buf.extend_from_slice(signature.as_bytes());
105                buf.extend_from_slice(public_key.as_bytes());
106            }
107        }
108
109        buf
110    }
111
112    #[cfg(feature = "serde")]
113    #[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
114    fn from_serialized_bytes<T: AsRef<[u8]>, E: serde::de::Error>(bytes: T) -> Result<Self, E> {
115        let bytes = bytes.as_ref();
116        let flag = SignatureScheme::from_byte(
117            *bytes
118                .first()
119                .ok_or_else(|| serde::de::Error::custom("missing signature scheme flag"))?,
120        )
121        .map_err(serde::de::Error::custom)?;
122        match flag {
123            SignatureScheme::Ed25519 => {
124                let expected_length = 1 + Ed25519Signature::LENGTH + Ed25519PublicKey::LENGTH;
125
126                if bytes.len() != expected_length {
127                    return Err(serde::de::Error::custom("invalid ed25519 signature"));
128                }
129
130                let mut signature = [0; Ed25519Signature::LENGTH];
131                signature.copy_from_slice(&bytes[1..(1 + Ed25519Signature::LENGTH)]);
132
133                let mut public_key = [0; Ed25519PublicKey::LENGTH];
134                public_key.copy_from_slice(&bytes[(1 + Ed25519Signature::LENGTH)..]);
135
136                Ok(SimpleSignature::Ed25519 {
137                    signature: Ed25519Signature::new(signature),
138                    public_key: Ed25519PublicKey::new(public_key),
139                })
140            }
141            SignatureScheme::Secp256k1 => {
142                let expected_length = 1 + Secp256k1Signature::LENGTH + Secp256k1PublicKey::LENGTH;
143
144                if bytes.len() != expected_length {
145                    return Err(serde::de::Error::custom("invalid secp25k1 signature"));
146                }
147
148                let mut signature = [0; Secp256k1Signature::LENGTH];
149                signature.copy_from_slice(&bytes[1..(1 + Secp256k1Signature::LENGTH)]);
150
151                let mut public_key = [0; Secp256k1PublicKey::LENGTH];
152                public_key.copy_from_slice(&bytes[(1 + Secp256k1Signature::LENGTH)..]);
153
154                Ok(SimpleSignature::Secp256k1 {
155                    signature: Secp256k1Signature::new(signature),
156                    public_key: Secp256k1PublicKey::new(public_key),
157                })
158            }
159            SignatureScheme::Secp256r1 => {
160                let expected_length = 1 + Secp256r1Signature::LENGTH + Secp256r1PublicKey::LENGTH;
161
162                if bytes.len() != expected_length {
163                    return Err(serde::de::Error::custom("invalid secp25r1 signature"));
164                }
165
166                let mut signature = [0; Secp256r1Signature::LENGTH];
167                signature.copy_from_slice(&bytes[1..(1 + Secp256r1Signature::LENGTH)]);
168
169                let mut public_key = [0; Secp256r1PublicKey::LENGTH];
170                public_key.copy_from_slice(&bytes[(1 + Secp256r1Signature::LENGTH)..]);
171
172                Ok(SimpleSignature::Secp256r1 {
173                    signature: Secp256r1Signature::new(signature),
174                    public_key: Secp256r1PublicKey::new(public_key),
175                })
176            }
177            SignatureScheme::MlDsa65 => {
178                let expected_length = 1 + MlDsa65Signature::LENGTH + MlDsa65PublicKey::LENGTH;
179                if bytes.len() != expected_length {
180                    return Err(serde::de::Error::custom("invalid mldsa65 signature"));
181                }
182                let signature =
183                    MlDsa65Signature::from_bytes(&bytes[1..(1 + MlDsa65Signature::LENGTH)])
184                        .map_err(serde::de::Error::custom)?;
185                let public_key =
186                    MlDsa65PublicKey::from_bytes(&bytes[(1 + MlDsa65Signature::LENGTH)..])
187                        .map_err(serde::de::Error::custom)?;
188                Ok(SimpleSignature::MlDsa65 {
189                    signature: Box::new(signature),
190                    public_key: Box::new(public_key),
191                })
192            }
193            SignatureScheme::Multisig
194            | SignatureScheme::Bls12381
195            | SignatureScheme::ZkLogin
196            | SignatureScheme::Passkey => Err(serde::de::Error::custom("invalid signature scheme")),
197        }
198    }
199}
200
201#[cfg(feature = "serde")]
202#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
203impl serde::Serialize for SimpleSignature {
204    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
205    where
206        S: serde::Serializer,
207    {
208        #[derive(serde_derive::Serialize)]
209        #[serde(tag = "scheme")]
210        #[serde(rename_all = "lowercase")]
211        enum Sig<'a> {
212            Ed25519 {
213                signature: &'a Ed25519Signature,
214                public_key: &'a Ed25519PublicKey,
215            },
216            Secp256k1 {
217                signature: &'a Secp256k1Signature,
218                public_key: &'a Secp256k1PublicKey,
219            },
220            Secp256r1 {
221                signature: &'a Secp256r1Signature,
222                public_key: &'a Secp256r1PublicKey,
223            },
224            MlDsa65 {
225                signature: &'a MlDsa65Signature,
226                public_key: &'a MlDsa65PublicKey,
227            },
228        }
229
230        if serializer.is_human_readable() {
231            let sig = match self {
232                SimpleSignature::Ed25519 {
233                    signature,
234                    public_key,
235                } => Sig::Ed25519 {
236                    signature,
237                    public_key,
238                },
239                SimpleSignature::Secp256k1 {
240                    signature,
241                    public_key,
242                } => Sig::Secp256k1 {
243                    signature,
244                    public_key,
245                },
246                SimpleSignature::Secp256r1 {
247                    signature,
248                    public_key,
249                } => Sig::Secp256r1 {
250                    signature,
251                    public_key,
252                },
253                SimpleSignature::MlDsa65 {
254                    signature,
255                    public_key,
256                } => Sig::MlDsa65 {
257                    signature,
258                    public_key,
259                },
260            };
261
262            sig.serialize(serializer)
263        } else {
264            match self {
265                SimpleSignature::Ed25519 {
266                    signature,
267                    public_key,
268                } => {
269                    let mut buf = [0; 1 + Ed25519Signature::LENGTH + Ed25519PublicKey::LENGTH];
270                    buf[0] = SignatureScheme::Ed25519 as u8;
271                    buf[1..(1 + Ed25519Signature::LENGTH)].copy_from_slice(signature.as_ref());
272                    buf[(1 + Ed25519Signature::LENGTH)..].copy_from_slice(public_key.as_ref());
273
274                    serializer.serialize_bytes(&buf)
275                }
276                SimpleSignature::Secp256k1 {
277                    signature,
278                    public_key,
279                } => {
280                    let mut buf = [0; 1 + Secp256k1Signature::LENGTH + Secp256k1PublicKey::LENGTH];
281                    buf[0] = SignatureScheme::Secp256k1 as u8;
282                    buf[1..(1 + Secp256k1Signature::LENGTH)].copy_from_slice(signature.as_ref());
283                    buf[(1 + Secp256k1Signature::LENGTH)..].copy_from_slice(public_key.as_ref());
284
285                    serializer.serialize_bytes(&buf)
286                }
287                SimpleSignature::Secp256r1 {
288                    signature,
289                    public_key,
290                } => {
291                    let mut buf = [0; 1 + Secp256r1Signature::LENGTH + Secp256r1PublicKey::LENGTH];
292                    buf[0] = SignatureScheme::Secp256r1 as u8;
293                    buf[1..(1 + Secp256r1Signature::LENGTH)].copy_from_slice(signature.as_ref());
294                    buf[(1 + Secp256r1Signature::LENGTH)..].copy_from_slice(public_key.as_ref());
295
296                    serializer.serialize_bytes(&buf)
297                }
298                SimpleSignature::MlDsa65 { .. } => serializer.serialize_bytes(&self.to_bytes()),
299            }
300        }
301    }
302}
303
304#[cfg(feature = "serde")]
305#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
306impl<'de> serde::Deserialize<'de> for SimpleSignature {
307    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
308    where
309        D: serde::Deserializer<'de>,
310    {
311        #[derive(serde_derive::Deserialize)]
312        #[serde(tag = "scheme")]
313        #[serde(rename_all = "lowercase")]
314        enum Sig {
315            Ed25519 {
316                signature: Ed25519Signature,
317                public_key: Ed25519PublicKey,
318            },
319            Secp256k1 {
320                signature: Secp256k1Signature,
321                public_key: Secp256k1PublicKey,
322            },
323            Secp256r1 {
324                signature: Secp256r1Signature,
325                public_key: Secp256r1PublicKey,
326            },
327            MlDsa65 {
328                signature: Box<MlDsa65Signature>,
329                public_key: Box<MlDsa65PublicKey>,
330            },
331        }
332
333        if deserializer.is_human_readable() {
334            let sig = Sig::deserialize(deserializer)?;
335            Ok(match sig {
336                Sig::Ed25519 {
337                    signature,
338                    public_key,
339                } => SimpleSignature::Ed25519 {
340                    signature,
341                    public_key,
342                },
343                Sig::Secp256k1 {
344                    signature,
345                    public_key,
346                } => SimpleSignature::Secp256k1 {
347                    signature,
348                    public_key,
349                },
350                Sig::Secp256r1 {
351                    signature,
352                    public_key,
353                } => SimpleSignature::Secp256r1 {
354                    signature,
355                    public_key,
356                },
357                Sig::MlDsa65 {
358                    signature,
359                    public_key,
360                } => SimpleSignature::MlDsa65 {
361                    signature,
362                    public_key,
363                },
364            })
365        } else {
366            let bytes: std::borrow::Cow<'de, [u8]> = std::borrow::Cow::deserialize(deserializer)?;
367            Self::from_serialized_bytes(bytes)
368        }
369    }
370}
371
372/// Flag use to disambiguate the signature schemes supported by Sui.
373///
374/// # BCS
375///
376/// The BCS serialized form for this type is defined by the following ABNF:
377///
378/// ```text
379/// signature-scheme = ed25519-flag / secp256k1-flag / secp256r1-flag /
380///                    multisig-flag / bls-flag / zklogin-flag / passkey-flag /
381///                    mldsa65-flag
382/// ed25519-flag     = %x00
383/// secp256k1-flag   = %x01
384/// secp256r1-flag   = %x02
385/// multisig-flag    = %x03
386/// bls-flag         = %x04
387/// zklogin-flag     = %x05
388/// passkey-flag     = %x06
389/// mldsa65-flag     = %x07
390/// ```
391#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
392#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
393#[non_exhaustive]
394#[repr(u8)]
395pub enum SignatureScheme {
396    Ed25519 = 0x00,
397    Secp256k1 = 0x01,
398    Secp256r1 = 0x02,
399    Multisig = 0x03,
400    Bls12381 = 0x04, // This is currently not supported for user addresses
401    ZkLogin = 0x05,
402    Passkey = 0x06,
403    MlDsa65 = 0x07,
404}
405
406impl SignatureScheme {
407    /// Return the name of this signature scheme
408    pub fn name(self) -> &'static str {
409        match self {
410            SignatureScheme::Ed25519 => "ed25519",
411            SignatureScheme::Secp256k1 => "secp256k1",
412            SignatureScheme::Secp256r1 => "secp256r1",
413            SignatureScheme::Multisig => "multisig",
414            SignatureScheme::Bls12381 => "bls12381",
415            SignatureScheme::ZkLogin => "zklogin",
416            SignatureScheme::Passkey => "passkey",
417            SignatureScheme::MlDsa65 => "mldsa65",
418        }
419    }
420
421    /// Try constructing from a byte flag
422    pub fn from_byte(flag: u8) -> Result<Self, InvalidSignatureScheme> {
423        match flag {
424            0x00 => Ok(Self::Ed25519),
425            0x01 => Ok(Self::Secp256k1),
426            0x02 => Ok(Self::Secp256r1),
427            0x03 => Ok(Self::Multisig),
428            0x04 => Ok(Self::Bls12381),
429            0x05 => Ok(Self::ZkLogin),
430            0x06 => Ok(Self::Passkey),
431            0x07 => Ok(Self::MlDsa65),
432            invalid => Err(InvalidSignatureScheme(invalid)),
433        }
434    }
435
436    /// Convert to a byte flag
437    pub fn to_u8(self) -> u8 {
438        self as u8
439    }
440}
441
442impl Ed25519PublicKey {
443    /// Return the flag for this signature scheme
444    pub fn scheme(&self) -> SignatureScheme {
445        SignatureScheme::Ed25519
446    }
447}
448
449impl Secp256k1PublicKey {
450    /// Return the flag for this signature scheme
451    pub fn scheme(&self) -> SignatureScheme {
452        SignatureScheme::Secp256k1
453    }
454}
455
456impl Secp256r1PublicKey {
457    /// Return the flag for this signature scheme
458    pub fn scheme(&self) -> SignatureScheme {
459        SignatureScheme::Secp256r1
460    }
461}
462
463impl MlDsa65PublicKey {
464    /// Return the flag for this signature scheme
465    pub fn scheme(&self) -> SignatureScheme {
466        SignatureScheme::MlDsa65
467    }
468}
469
470impl super::ZkLoginPublicIdentifier {
471    /// Return the flag for this signature scheme
472    pub fn scheme(&self) -> SignatureScheme {
473        SignatureScheme::ZkLogin
474    }
475}
476
477impl super::PasskeyPublicKey {
478    /// Return the flag for this signature scheme
479    pub fn scheme(&self) -> SignatureScheme {
480        SignatureScheme::Passkey
481    }
482}
483
484#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
485pub struct InvalidSignatureScheme(u8);
486
487impl std::fmt::Display for InvalidSignatureScheme {
488    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
489        write!(f, "invalid signature scheme: {:02x}", self.0)
490    }
491}
492
493/// A signature from a user
494///
495/// A `UserSignature` is most commonly used to authorize the execution and inclusion of a
496/// transaction to the blockchain.
497///
498/// # BCS
499///
500/// The BCS serialized form for this type is defined by the following ABNF:
501///
502/// ```text
503/// user-signature-bcs = bytes ; where the contents of the bytes are defined by <user-signature>
504/// user-signature = simple-signature / multisig / multisig-legacy / zklogin / passkey
505/// ```
506///
507/// Note: Due to historical reasons, signatures are serialized slightly different from the majority
508/// of the types in Sui. In particular if a signature is ever embedded in another structure it
509/// generally is serialized as `bytes` meaning it has a length prefix that defines the length of
510/// the completely serialized signature.
511#[derive(Clone, Debug, PartialEq, Eq)]
512#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
513#[non_exhaustive]
514pub enum UserSignature {
515    Simple(SimpleSignature),
516    Multisig(MultisigAggregatedSignature),
517    ZkLogin(Box<ZkLoginAuthenticator>),
518    Passkey(PasskeyAuthenticator),
519}
520
521impl UserSignature {
522    /// Return the flag for this signature scheme
523    pub fn scheme(&self) -> SignatureScheme {
524        match self {
525            UserSignature::Simple(simple) => simple.scheme(),
526            UserSignature::Multisig(_) => SignatureScheme::Multisig,
527            UserSignature::ZkLogin(_) => SignatureScheme::ZkLogin,
528            UserSignature::Passkey(_) => SignatureScheme::Passkey,
529        }
530    }
531}
532
533#[cfg(feature = "serde")]
534#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
535mod serialization {
536    use super::*;
537
538    impl UserSignature {
539        pub fn to_bytes(&self) -> Vec<u8> {
540            match self {
541                UserSignature::Simple(s) => s.to_bytes(),
542                UserSignature::Multisig(m) => m.to_bytes(),
543                UserSignature::ZkLogin(z) => z.to_bytes(),
544                UserSignature::Passkey(p) => p.to_bytes(),
545            }
546        }
547
548        pub fn to_base64(&self) -> String {
549            use base64ct::Encoding;
550
551            base64ct::Base64::encode_string(&self.to_bytes())
552        }
553
554        fn from_serialized_bytes<T: AsRef<[u8]>, E: serde::de::Error>(bytes: T) -> Result<Self, E> {
555            let bytes = bytes.as_ref();
556
557            let flag = SignatureScheme::from_byte(
558                *bytes
559                    .first()
560                    .ok_or_else(|| serde::de::Error::custom("missing signature scheme flag"))?,
561            )
562            .map_err(serde::de::Error::custom)?;
563            match flag {
564                SignatureScheme::Ed25519
565                | SignatureScheme::Secp256k1
566                | SignatureScheme::Secp256r1
567                | SignatureScheme::MlDsa65 => {
568                    let simple = SimpleSignature::from_serialized_bytes(bytes)?;
569                    Ok(Self::Simple(simple))
570                }
571                SignatureScheme::Multisig => {
572                    let multisig = MultisigAggregatedSignature::from_serialized_bytes(bytes)?;
573                    Ok(Self::Multisig(multisig))
574                }
575                SignatureScheme::Bls12381 => Err(serde::de::Error::custom(
576                    "bls not supported for user signatures",
577                )),
578                SignatureScheme::ZkLogin => {
579                    let zklogin = ZkLoginAuthenticator::from_serialized_bytes(bytes)?;
580                    Ok(Self::ZkLogin(Box::new(zklogin)))
581                }
582                SignatureScheme::Passkey => {
583                    let passkey = PasskeyAuthenticator::from_serialized_bytes(bytes)?;
584                    Ok(Self::Passkey(passkey))
585                }
586            }
587        }
588
589        pub fn from_bytes(bytes: &[u8]) -> Result<Self, bcs::Error> {
590            Self::from_serialized_bytes(bytes)
591        }
592
593        pub fn from_base64(s: &str) -> Result<Self, bcs::Error> {
594            use base64ct::Encoding;
595            use serde::de::Error;
596
597            let bytes = base64ct::Base64::decode_vec(s).map_err(bcs::Error::custom)?;
598            Self::from_bytes(&bytes)
599        }
600    }
601
602    #[derive(serde_derive::Serialize)]
603    #[serde(tag = "scheme", rename_all = "lowercase")]
604    enum ReadableUserSignatureRef<'a> {
605        Ed25519 {
606            signature: &'a Ed25519Signature,
607            public_key: &'a Ed25519PublicKey,
608        },
609        Secp256k1 {
610            signature: &'a Secp256k1Signature,
611            public_key: &'a Secp256k1PublicKey,
612        },
613        Secp256r1 {
614            signature: &'a Secp256r1Signature,
615            public_key: &'a Secp256r1PublicKey,
616        },
617        MlDsa65 {
618            signature: &'a MlDsa65Signature,
619            public_key: &'a MlDsa65PublicKey,
620        },
621        Multisig(&'a MultisigAggregatedSignature),
622        ZkLogin(&'a ZkLoginAuthenticator),
623        Passkey(&'a PasskeyAuthenticator),
624    }
625
626    #[derive(serde_derive::Deserialize)]
627    #[serde(tag = "scheme", rename_all = "lowercase")]
628    #[serde(rename = "UserSignature")]
629    enum ReadableUserSignature {
630        Ed25519 {
631            signature: Ed25519Signature,
632            public_key: Ed25519PublicKey,
633        },
634        Secp256k1 {
635            signature: Secp256k1Signature,
636            public_key: Secp256k1PublicKey,
637        },
638        Secp256r1 {
639            signature: Secp256r1Signature,
640            public_key: Secp256r1PublicKey,
641        },
642        MlDsa65 {
643            signature: Box<MlDsa65Signature>,
644            public_key: Box<MlDsa65PublicKey>,
645        },
646        Multisig(MultisigAggregatedSignature),
647        ZkLogin(Box<ZkLoginAuthenticator>),
648        Passkey(PasskeyAuthenticator),
649    }
650
651    impl serde::Serialize for UserSignature {
652        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
653        where
654            S: serde::Serializer,
655        {
656            if serializer.is_human_readable() {
657                let readable = match self {
658                    UserSignature::Simple(SimpleSignature::Ed25519 {
659                        signature,
660                        public_key,
661                    }) => ReadableUserSignatureRef::Ed25519 {
662                        signature,
663                        public_key,
664                    },
665                    UserSignature::Simple(SimpleSignature::Secp256k1 {
666                        signature,
667                        public_key,
668                    }) => ReadableUserSignatureRef::Secp256k1 {
669                        signature,
670                        public_key,
671                    },
672                    UserSignature::Simple(SimpleSignature::Secp256r1 {
673                        signature,
674                        public_key,
675                    }) => ReadableUserSignatureRef::Secp256r1 {
676                        signature,
677                        public_key,
678                    },
679                    UserSignature::Multisig(multisig) => {
680                        ReadableUserSignatureRef::Multisig(multisig)
681                    }
682                    UserSignature::ZkLogin(zklogin) => ReadableUserSignatureRef::ZkLogin(zklogin),
683                    UserSignature::Passkey(passkey) => ReadableUserSignatureRef::Passkey(passkey),
684                    UserSignature::Simple(SimpleSignature::MlDsa65 {
685                        signature,
686                        public_key,
687                    }) => ReadableUserSignatureRef::MlDsa65 {
688                        signature,
689                        public_key,
690                    },
691                };
692                readable.serialize(serializer)
693            } else {
694                match self {
695                    UserSignature::Simple(simple) => simple.serialize(serializer),
696                    UserSignature::Multisig(multisig) => multisig.serialize(serializer),
697                    UserSignature::ZkLogin(zklogin) => zklogin.serialize(serializer),
698                    UserSignature::Passkey(passkey) => passkey.serialize(serializer),
699                }
700            }
701        }
702    }
703
704    impl<'de> serde::Deserialize<'de> for UserSignature {
705        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
706        where
707            D: serde::Deserializer<'de>,
708        {
709            if deserializer.is_human_readable() {
710                // Try to deserialize as either a plain base64 string OR the tagged enum format
711                #[derive(serde_derive::Deserialize)]
712                #[serde(untagged)]
713                enum UserSignatureHelper {
714                    Base64String(String),
715                    Tagged(ReadableUserSignature),
716                }
717
718                let helper = UserSignatureHelper::deserialize(deserializer)?;
719                match helper {
720                    UserSignatureHelper::Base64String(s) => {
721                        UserSignature::from_base64(&s).map_err(serde::de::Error::custom)
722                    }
723                    UserSignatureHelper::Tagged(readable) => Ok(match readable {
724                        ReadableUserSignature::Ed25519 {
725                            signature,
726                            public_key,
727                        } => Self::Simple(SimpleSignature::Ed25519 {
728                            signature,
729                            public_key,
730                        }),
731                        ReadableUserSignature::Secp256k1 {
732                            signature,
733                            public_key,
734                        } => Self::Simple(SimpleSignature::Secp256k1 {
735                            signature,
736                            public_key,
737                        }),
738                        ReadableUserSignature::Secp256r1 {
739                            signature,
740                            public_key,
741                        } => Self::Simple(SimpleSignature::Secp256r1 {
742                            signature,
743                            public_key,
744                        }),
745                        ReadableUserSignature::Multisig(multisig) => Self::Multisig(multisig),
746                        ReadableUserSignature::ZkLogin(zklogin) => Self::ZkLogin(zklogin),
747                        ReadableUserSignature::Passkey(passkey) => Self::Passkey(passkey),
748                        ReadableUserSignature::MlDsa65 {
749                            signature,
750                            public_key,
751                        } => Self::Simple(SimpleSignature::MlDsa65 {
752                            signature,
753                            public_key,
754                        }),
755                    }),
756                }
757            } else {
758                use serde_with::DeserializeAs;
759
760                let bytes: std::borrow::Cow<'de, [u8]> =
761                    serde_with::Bytes::deserialize_as(deserializer)?;
762                Self::from_serialized_bytes(bytes)
763            }
764        }
765    }
766
767    #[cfg(test)]
768    mod test {
769        use super::*;
770        use base64ct::Base64;
771        use base64ct::Encoding;
772        use test_strategy::proptest;
773
774        #[cfg(target_arch = "wasm32")]
775        use wasm_bindgen_test::wasm_bindgen_test as test;
776
777        #[proptest]
778        fn roundtrip_signature_scheme(scheme: SignatureScheme) {
779            assert_eq!(Ok(scheme), SignatureScheme::from_byte(scheme.to_u8()));
780        }
781
782        #[test]
783        fn mldsa65_fixture() {
784            // Signed by Sui's signer over personal message "hello", seed
785            // [2; 32]; the address is Sui's golden for that seed.
786            const SIGNATURE: &str = include_str!("fixtures/mldsa65-personal-message-signature");
787            let signature = UserSignature::from_base64(SIGNATURE.trim()).unwrap();
788            let UserSignature::Simple(simple @ SimpleSignature::MlDsa65 { .. }) = &signature else {
789                panic!("expected an ML-DSA-65 simple signature: {signature:?}");
790            };
791            assert_eq!(simple.scheme(), SignatureScheme::MlDsa65);
792            assert_eq!(
793                simple.derive_address().to_string(),
794                "0xa44576e02f83a9e1bddac6fd742a77931d1689d9a61122eb3125dee425f6dd36"
795            );
796            assert_eq!(signature.to_base64(), SIGNATURE.trim());
797
798            let json = serde_json::to_string(&signature).unwrap();
799            assert_eq!(signature, serde_json::from_str(&json).unwrap());
800            let bcs = bcs::to_bytes(&signature).unwrap();
801            assert_eq!(signature, bcs::from_bytes(&bcs).unwrap());
802
803            // Truncated or over-long envelopes are rejected.
804            let bytes = signature.to_bytes();
805            assert!(UserSignature::from_bytes(&bytes[..bytes.len() - 1]).is_err());
806            let mut long = bytes.clone();
807            long.push(0);
808            assert!(UserSignature::from_bytes(&long).is_err());
809        }
810
811        #[test]
812        fn simple_fixtures() {
813            const FIXTURES: &[(SignatureScheme, &str)] = &[
814                (
815                    SignatureScheme::Ed25519,
816                    "YQDaeO4w2ULMy5eqHBzP0oalr1YhDX/9uJS9MntKnW3d55q4aqZYYnoEloaBmXKc6FoD5bTwONdwS9CwdMQGhIcPDX2rNYyNrapO+gBJp1sHQ2VVsQo2ghm7aA9wVxNJ13U=",
817                ),
818                (
819                    SignatureScheme::Secp256k1,
820                    "YgErcT6WUSQXGD1DaIwls5rWq648akDMlvL41ugUUhyIPWnqURl+daQLG+ILNemARKHYVNOikKJJ8jqu+HzlRa5rAg4XzVk55GsZZkGWjNdZkQuiV34n+nP944dtub7FvOsr",
821                ),
822                (
823                    SignatureScheme::Secp256r1,
824                    "YgLp1p4K9dSQTt2AeR05yK1MkXmtLm6Sieb9yfkpW1gOBiqnO9ZKZiWUrLJQav2Mxw64zM37g3IVdsB/To6qfl8IA0f7ryPwOKvEwwiicRF6Kkz/rt28X/gcdRe8bHSn7bQw",
825                ),
826            ];
827
828            for (scheme, fixture) in FIXTURES {
829                let bcs = Base64::decode_vec(fixture).unwrap();
830
831                let sig: UserSignature = bcs::from_bytes(&bcs).unwrap();
832                assert_eq!(*scheme, sig.scheme());
833                let bytes = bcs::to_bytes(&sig).unwrap();
834                assert_eq!(bcs, bytes);
835
836                let json = serde_json::to_string_pretty(&sig).unwrap();
837                println!("{json}");
838                assert_eq!(sig, serde_json::from_str(&json).unwrap());
839            }
840        }
841
842        #[test]
843        fn legacy_multisig_fixtures() {
844            const FIXTURE1: &str = "rgIDAgAnwUSyrALP8m0eEPZE6aPggBELk72n1u3LU+i4nx5kqzhahcICbskEYzHJrbarvFr/RQITgDMoorqpDhN8dgsKATyrN3CD8g37D60dYiGW6sOBqIcf3E1mdMsKvX2pbOZsYQv8VNL+2Jz3vnMXcwEZF32PplKjcnmyUGRhV11M7n4UOjAAAAEAAAAAAAEAEAAAAAAAAQADLEFBMTlxeldNamEycVR2b0FTYWRiQjBObFZiRUtOb0ladTJnUGNGY1RTZGQxATBBUUlPRjgxWk9lUnJHV1pCbG96WFdaRUxvbGQrSi9wei9lT0hiYm0reGJ6ckt3PT0BMEFnTkgrNjhqOERpcnhNTUlvbkVSZWlwTS82N2R2Ri80SEhVWHZHeDBwKzIwTUE9PQECAA==";
845
846            const FIXTURE2: &str = "8QIDAwDYAAra4KQGp2Oq1TCOgWfH8IxC4UA5wJB/NqOcNmMh54Y5d5pnVQfTlqgq4J17a8+W+y3+jk9h4YMB9LzPDYcLAaJJBH+WLPfPaQ7T3Cv8nqpZ1TbPrT8E61FrSgeIbN4OTJeijjguv1pd3ImvTeo4AMYZczf5OH6+5yBaur7R6YACiooT5J36agjUk0TpVcTKMGwykIwD7NBkZ0gbinHxuVJwdi1tSbqhMpqvNgP+CFO6F7FSTe+xiHh0MDOKyYQItxY6MAAAAQAAAAAAAgAQAAAAAAABAAIAAyxBQTE5cXpXTWphMnFUdm9BU2FkYkIwTmxWYkVLTm9JWnUyZ1BjRmNUU2RkMQEwQVFJT0Y4MVpPZVJyR1daQmxvelhXWkVMb2xkK0ovcHovZU9IYmJtK3hienJLdz09ATBBZ05IKzY4ajhEaXJ4TU1Jb25FUmVpcE0vNjdkdkYvNEhIVVh2R3gwcCsyME1BPT0BAgA=";
847
848            for fixture in [FIXTURE1, FIXTURE2] {
849                let bcs = Base64::decode_vec(fixture).unwrap();
850
851                let sig: UserSignature = bcs::from_bytes(&bcs).unwrap();
852                assert_eq!(SignatureScheme::Multisig, sig.scheme());
853                let bytes = bcs::to_bytes(&sig).unwrap();
854                assert_eq!(bcs, bytes);
855
856                let json = serde_json::to_string_pretty(&sig).unwrap();
857                println!("{json}");
858                assert_eq!(sig, serde_json::from_str(&json).unwrap());
859            }
860        }
861
862        #[test]
863        fn multisig_fixtures() {
864            const FIXTURE1: &str = "sgIDAwCTLgVngjC4yeuvpAGKVkgcvIKVFUJnL1r6oFZScQVE5DNIz6kfxAGDRcVUczE9CUb7/sN/EuFJ8ot86Sdb8pAFASoQ91stRHXdW5dLy0BQ6v+7XWptawy2ItMyPk508p+PHdtZcm2aKl3lZGIvXe6MPY73E+1Hakv/xJbTYsw5SPMC5dx3gBwxds2GV12c7VUSqkyXamliSF1W/QBMufqrlmdIOZ1ox9gbsvIPtXYahfvKm8ozA7rsZWwRv8atsnyfYgcAAwANfas1jI2tqk76AEmnWwdDZVWxCjaCGbtoD3BXE0nXdQEBAg4XzVk55GsZZkGWjNdZkQuiV34n+nP944dtub7FvOsrAQIDR/uvI/A4q8TDCKJxEXoqTP+u3bxf+Bx1F7xsdKfttDABAgA=";
865
866            const FIXTURE2: &str = "8QEDAgBMW4Oq7XMjO5c6HLgTBJrWDZsCEcZF2EPOf68fdf1aY3e3pvA3cmk0tjMmXFB9+A6J2NohCpTFb/CsXEBjtCcMAfraaMMOMzG815145jlrY44Rbp0d1JQJOJ3hjgEe2xVBFP3QR94IVZk6ssyYxsecpBA+re5eqVRacvZGSobNPkMDAAMADX2rNYyNrapO+gBJp1sHQ2VVsQo2ghm7aA9wVxNJ13UBAQIOF81ZOeRrGWZBlozXWZELold+J/pz/eOHbbm+xbzrKwECA0f7ryPwOKvEwwiicRF6Kkz/rt28X/gcdRe8bHSn7bQwAQIA";
867
868            for fixture in [FIXTURE1, FIXTURE2] {
869                let bcs = Base64::decode_vec(fixture).unwrap();
870
871                let sig: UserSignature = bcs::from_bytes(&bcs).unwrap();
872                assert_eq!(SignatureScheme::Multisig, sig.scheme());
873                let bytes = bcs::to_bytes(&sig).unwrap();
874                assert_eq!(bcs, bytes);
875
876                let json = serde_json::to_string_pretty(&sig).unwrap();
877                println!("{json}");
878                assert_eq!(sig, serde_json::from_str(&json).unwrap());
879            }
880        }
881
882        #[test]
883        fn mutisig_with_zklogin() {
884            const FIXTURE: &str = "xwgDAQOWBwUDTDYyNzM5OTI5NDQyODI2NTYyNDE2NDMyNDk5Njc1NDY0MzY0MTU2ODM1MzgzMzAwNzMzMDkwMzgyOTUwOTAwMjA0MzcwMzI3MzQ0MTdNMTM5MjUxMzE5MTUzODM4NDMwOTkzODU1NTU2MjYyNzIwMzI5NjE5MjM3MjY1ODAyMjY2OTcwMTUzMTkxOTcxMzIxODkxMTg2MDUyNTcBMQMCTDc3MDQwMDc2MTQxMjAyNDQ0MjgyMDMyMzQwMzI3NzQ2ODkwODEwNzg2Mzg4NjkzMTcyNDM1NTEyNTMwMTA3MjYzMzg1MTA0MzYxMjdMNDczMzY5MTU2NjAwODE5MTIwNDAxMjcwNTc5OTA2MjI3NTk2MjY0NzMwMTUyNDU3MDIxMjM0MzczMjc1MTE3MTQyMjY2MzEzNTc1MgJMOTYzMjExNjUzNzQxMTQ3Mjk4MTQ2NDE0NzY0MDM5MzkxNzQxODQyMDI4NzgwOTUxODYyMTk5OTM0MjIwNjc2ODgzMjg0NzY5NTg5Nkw4NDM2Mjg3MTUwMzIxMjE2NTUwOTIxNTQ4ODg0MjI2MDM4MjczMTk0MjAyNDQwNTc0NzI5MDM4MTk2NDAxNDAzMDI0Mzg0MTEzODk4AgExATADTDUxNzIxODQyMDU0ODkxNDg4MzEwNTgxODkxNDIwNjI3Njc1NTM3MjMxMDkzNzIyMDk4NzI0NDAxMzA1MTg0MzYzODQxNzUxMjY1MjRMNTE1MTQzMjA2NjEzODc0NzIwOTEyMDY4NzUyMjIwODU1NDA0MTU2NDgyNzA4MDc5NzA1MDcxNjkyNzc2OTY0MzQ0NzQyMjEyMzI3MAExMXdpYVhOeklqb2lhSFIwY0hNNkx5OXBaQzUwZDJsMFkyZ3VkSFl2YjJGMWRHZ3lJaXcCMmV5SmhiR2NpT2lKU1V6STFOaUlzSW5SNWNDSTZJa3BYVkNJc0ltdHBaQ0k2SWpFaWZRTTIwNjg3NjQyNTE3NjMwNzMzMjczNjg3Nzk1NDc2MjQ3NDM3NzQzODM0NjAxMTAxNTY2Njg0OTY3OTY3NzA1ODA5OTg1MjQxMDY1NTM5CgAAAAAAAABhANFnRWP0VWDZA6kp8ltYtndCLMp70+CQMkW4CKPMOF5fGqTuUIKzqHJysBK8jS3rgHHBc5ZDqn0YUG0W2SH5gQC5xu4WMO8+cRFEpkjbBruyKE9ydM++5T/87lA8waSSAAgABAANfas1jI2tqk76AEmnWwdDZVWxCjaCGbtoD3BXE0nXdQEBAg4XzVk55GsZZkGWjNdZkQuiV34n+nP944dtub7FvOsrAQIDR/uvI/A4q8TDCKJxEXoqTP+u3bxf+Bx1F7xsdKfttDABAzwbaHR0cHM6Ly9pZC50d2l0Y2gudHYvb2F1dGgyLbzKbLI5Mq4c7y9X5gf73CthASNbTN9llO2Okr5TqEMBAQA=";
885
886            let bcs = Base64::decode_vec(FIXTURE).unwrap();
887
888            let sig: UserSignature = bcs::from_bytes(&bcs).unwrap();
889            assert_eq!(SignatureScheme::Multisig, sig.scheme());
890            let bytes = bcs::to_bytes(&sig).unwrap();
891            assert_eq!(bcs, bytes);
892
893            let json = serde_json::to_string_pretty(&sig).unwrap();
894            println!("{json}");
895            assert_eq!(sig, serde_json::from_str(&json).unwrap());
896        }
897
898        #[test]
899        fn zklogin_fixtures() {
900            const FIXTURES: &[&str] = &[
901                "mAcFA00yMTM0MzA3MTg2NDQ3ODc4NTU1OTU1OTY2Njg3NDQ3Njg0MzYyODQxNjA4OTQ4ODEyMTk4MjQ0OTY0ODk4OTg3OTkxMTI1MTY2OTA2N0w1MzYyMzAzOTQxMzk3NzQ1MTk2MTQxNjgxNjA5MDk0MDI4MTg3NzgxMzY2ODc3ODA5NTA2NTU0NzA3MjQ4MzcwNzM4OTcwOTI5MzYwATEDAk0xOTAzMjkyNDMyMDAxODEyNjcyNzEyMDYzMjYzMzM2OTE1NTg2MDc4NDA0NjY2MDcyMzIzMjU0MTAwMjQyODAxODA4ODQ4MTI3MzA5N0sxOTM0MDEzODQwOTcyNjc5OTM0MzgxMTI2ODg3OTQ2MTk1NDk5NTczMjY3NTE5ODAxNDA4MzQ2MzA3NDA2NzI3NjIxNzI0MTA4ODUCTDQxMTc0OTU3NjIwNzc2NjE4OTk2Njk5ODU1MTUzMzc2MDcwMTkzNTgwMjc2MjUxNTc4MDQwMTc0NTI2OTM1MTY5ODY1MDU1NDcyMTdNMTI3MDM0MzkzNTYyNTQ3NTM4NDA5NzAxMjA3MDAxMjM5MjcxOTU1OTI4OTE0MDgxMzY5NzQ0ODkwMzkzMzgyOTgzODYwODQxODYyNzYCATEBMANMNjAyNTg2MDg4MjI2OTUxNTE2NDY3MjY1NjU3OTU4MDE1OTMyMTI2ODY4MDM1NjU0NTkxOTA1NDkwNzkzNTM4MzY1NDYwNzA5MTIyOE0xNTUxNzY4ODA2NDc3NTgzMDI3NzAwNjY2NzE2OTM2NzAxNjU4Nzk5NDIyNjc1MTQ0Nzg5ODMzNjg0MDk5NjU4MDczNzg0NDY0NDExNQExMXdpYVhOeklqb2lhSFIwY0hNNkx5OXBaQzUwZDJsMFkyZ3VkSFl2YjJGMWRHZ3lJaXcCMmV5SmhiR2NpT2lKU1V6STFOaUlzSW5SNWNDSTZJa3BYVkNJc0ltdHBaQ0k2SWpFaWZRTDIwMjIzNjc3MTc2ODYwNzEyNzk5MTIwNjA0MTY1NTc3MDEyMjMyOTk3NTc0MjQwNjg2MDUwMzc1OTc1MTI1NzUzNDA0NDU0MTY4MTAKAAAAAAAAAGICUv1c+GW7/G0rKAO5QgQrqs3ujZALi4GWTkqgEjfTqMs53H1MeeRJdWzJW104/97F1VJyjm01ozKRasbRJGSsRwKi14vTFJZpnOkPlaKtNt4cGpY4PpaoeXb289IzLDx1Gg==",
902                "mwcFA00xMDE2MjI2MTYwOTg0NDYxNDI1OTY3MjA3OTg1MTYyNzUxNTQyNjEzMzM1OTEzMTc5NDQ3ODc4MDgzNDA3NTkxNDc5ODcxNzMzNzUzNU0xNjUwMTEzNTg2OTk2NDUwMDk1Njk2MDE2NzI0NzgwMzY3MzkyNDI4NDI0NTU3MDIyODMyODc4MDYxNjE4NzE0MzY2MzgzNzA0MjMyNAExAwJMNjAyMjIxMDk3ODA0MDA5MTgyMjQ1MDM2NjM2NjkyMzE1Mjg2NDAzMDQzNjY2ODg5NTUzNzYwNTM5MTM4MDA3OTAxMzIzMjE5OTk2NU0xNjEwNjE0MDY4NzEwMDc3MzQyNDIyNTI0NjEyNzM3ODIyNTgwOTIxMTQxMTYwMjQzMTIwMzI3NDM2MjM1NjEwOTI5NDk5Mjg2MjM4NgJMNzQwNDE3NTg3NDgyOTU3NDM0NTk1NDk1MTU0NDkxODY2ODI5ODQ0OTYxNjMzMDAyMzE4MzE4MzcwMTgxNjEwOTg3OTAwOTY5MTcxMUw3MzAwNzMwODk0MDQzNjM0NjI0NzIwNzkzNDIxNTM1NTUxODI3NDU4NjE4NzU5NjE2OTEzMjU0ODY4MzUzODE1MzM5ODg3MjIzMTA5AgExATADTTExNDA2NTA2NzUyNTkyODQ5NDk4MzcyNzYxODIyNzM4MjA2NTY0ODc4ODM3NTE3NzkxNTY2MzQ3NDk0NDkyNDQyMTI4MDExMTQwMzU3TTE1Njk5MzYzODA5ODg4MDc3MDcxNjM1NTg1MTA2NzA2MjE0NTcxMDI3NDU3ODE5MTE4NTE1NTk2MjA4MDgzODUzODcyOTM3NzQxNDczATExd2lhWE56SWpvaWFIUjBjSE02THk5cFpDNTBkMmwwWTJndWRIWXZiMkYxZEdneUlpdwIyZXlKaGJHY2lPaUpTVXpJMU5pSXNJblI1Y0NJNklrcFhWQ0lzSW10cFpDSTZJakVpZlFNMTc1NzI2MTY4NDgyNzU4OTMzODIyMTc0ODE3OTM5MTkwMDYzNjYzNzY4NTk4MTcwNDA1NDYwNDk4MzU5NTgxODc0NjEyOTg2NjkyNzAKAAAAAAAAAGICl9lwjktCQkH7GqGGV6EdbjHv4Go6MIDmr6EIvtg/2h5IuXKJF5GoVLuykxWwkSdNr9iRUZaz3Z0p/Z9nPJlW/gNaiwdVCMdfShJHSZgqfSH4DZpfaJPkGp6VX+TIIeDevg==",
903                "mgcFA00xOTUwNDI1NDE5MzgxMzM3OTA5NDA1MzkyODkyNTQwMjAxMjIxMTg4ODY5MDAzOTQ3ODM0MjYzOTk4OTcwMjA4MjAxNjY2MDkyNzg4MU0xODEwMjYxODU0NjY0NjY3MDgyMjI5MjczMjIwMDgzMzU0OTk4NDAxMTkxMDI1MDY2MjQ4Mjk5MjMzODgzNjA1NTc1MzMyNTUyMTUzMwExAwJMNTI0MzA1OTQ2MTI1NDQxOTM0NzgzOTMxMjI4ODQ5NjY4OTI0Njk4NzIyMTMyMDcxMDcyNzc2NzgzNzc3NDc4ODI4Mzc1NjgzMTAyOE0xMjA3MDIwMzk2MzAzNjY0NTY2NjAyMzUwNDMyNDM3NDY1OTYwMTY1NzY2NDAzOTU4MzE0MDU2Njc2MDExOTcwMTA3MjI5MjA0NzkxMQJNMTYzNjc2NDUxMTMxNzA1OTkxMTgwNzc1NjgxOTUyMjA5ODY1NjcxNjE0ODk2MDcyNDI1NzQ2ODg5ODQ0NzI4NTk0MzE2Mzk4MzQxNzhMNTg5MTQ4MzY3MjI1MTQyMzgzODE5NTQxNDg0NjEwNTY0Nzk4MDE2NjAyODIyNjcwMzE2ODE1Njg2MzkzNjUxNjk1OTkzMjE4MzExNQIBMQEwA0w2NDc2MTA0MzAwODgxNTQ2NTk3NjUwODk0NjEzNTUyMDc1NDg4Mjk5NjA4NjM5MTY4MzE3MjgzNTg2ODI3MDA3MTUzODg5MjI1MTI2TDQ3NjgzNjQxMTE1NjM0NzI0MDI1NzA4NDE0ODEyMDMzMTgzMDQzMTQ1MDQ4NjcxMzk1NzQ0MzAzODI2NzA4MDcwMTkwNDgxMTQyNzEBMTF3aWFYTnpJam9pYUhSMGNITTZMeTlwWkM1MGQybDBZMmd1ZEhZdmIyRjFkR2d5SWl3AjJleUpoYkdjaU9pSlNVekkxTmlJc0luUjVjQ0k2SWtwWFZDSXNJbXRwWkNJNklqRWlmUU0xMDc0MzE4MDg0MjY5ODE2Mzk0ODQ5NzAyMjkwMDE0Mzc4NjI0MTEwOTYyMzMyMDgzNzYxNzUzMDY5NzUxNDA1MzIwODA1NjEwNzgzNAoAAAAAAAAAYgLL7Jn3QV4USqVbuv97w4LqA12BAwU95fsUrvymgAUPtiepsG6kCVnX903PFZBusNM07tgWJ4/5ypb5mbJQhijJA+3BG7HM6kM2jZ0NPldx4AR5zvu+l4ZXRC4lo39h/K5s",
904                "mgcFA0wxNjY4NTEwOTQ1NDY2OTQ2OTYyODUxODAzOTg1MTA3Mjk2NTM1OTM3MzI1NzI5OTMzNDE1MzAzOTcwNjI2MjE3NzAwOTM0NjE4MjY1TDc5ODAxMjUwNTYxMTA2NzczMjY0NjA1MjI0MjgyNTk1OTM4NzQzOTg5MjE3Nzg1Mzk1MTUxODY3MTkwMzk3OTc1MTQ0NDA4ODQ1NTEBMQMCTTEyODA2ODY2NjkyMTUxODMxMTI1MzExMTk3Nzc1NTAxNDU1MTIwNzQwNjg2Mzk2OTQ4NDIxMjAyODI0MjkwODMwNzQzMzM4NTE1MjMxTTE4NzY2Mzc4MTcwNzE4MDMzMzk0ODQxMTYxMTU0MjA0NDA2ODc0MDM0ODk4NjA1NTk4MTgzMDM5NjM2NjQ1NjU3Mjk3NTIzMTU4ODU1Ak0xNzYyNTIzNTA0NzgxNDg2NDg1OTY1NTA1MTkyNTUyMzYxNjkwNzg2MDk3MjM3ODE1OTU1NjA4NDMyNDM5NTQxODk5MzI4NzQwMzk0M00xNjQ3MTA2MDIyOTUzMDIyNjEwOTk0Mzc3MTU3NzQ1NjIzMDM2NTM5NTMxNzM0NDk3OTAwNjAwMTE1NTgxNzM5ODE1NjczMjIwMjcxMwIBMQEwA00xODU1NzU4NTE1MjgxMTk5Mzk1MTY2NDY4NDA1ODg4MTg0NDE2MjY4NTk1NzAxMDY1MzIyNjg5ODkyOTgwNjA1Njc4NjMyMzg5MzA0M0wyNDQ2NDI2NDg4NTQwNzcwNzE5NTIyMjk1NTY3Njc2OTU3MzYzNjIxNTQ0MDUwMTg5OTAxMTk0MTY3NDY1NDE3OTA0Njk2NDQ3NDA1ATExd2lhWE56SWpvaWFIUjBjSE02THk5cFpDNTBkMmwwWTJndWRIWXZiMkYxZEdneUlpdwIyZXlKaGJHY2lPaUpTVXpJMU5pSXNJblI1Y0NJNklrcFhWQ0lzSW10cFpDSTZJakVpZlFMODQ2ODk2NzMyOTAyOTgzNjU0MjM3MzIzMjc4Njc3NzgyNDA5MTgyOTM1OTA4MjczNzA5MjQ3MjM1NDEwODEzNTkwOTE0MTM4MjEwNAoAAAAAAAAAYgEaWZZP7C934LS3vgsXYQk85BBiG6E285TY0C6U59qaUxlCUQWACVbxyEej193U4uIIP71lZ6KwvfT7lqOsUUIvAoa8xwWZ68Qgs7iXfsxg5ZS7VnSb6qVi1/gKm9//yqod",
905                "lgcFA0w2MjczOTkyOTQ0MjgyNjU2MjQxNjQzMjQ5OTY3NTQ2NDM2NDE1NjgzNTM4MzMwMDczMzA5MDM4Mjk1MDkwMDIwNDM3MDMyNzM0NDE3TTEzOTI1MTMxOTE1MzgzODQzMDk5Mzg1NTU1NjI2MjcyMDMyOTYxOTIzNzI2NTgwMjI2Njk3MDE1MzE5MTk3MTMyMTg5MTE4NjA1MjU3ATEDAkw3NzA0MDA3NjE0MTIwMjQ0NDI4MjAzMjM0MDMyNzc0Njg5MDgxMDc4NjM4ODY5MzE3MjQzNTUxMjUzMDEwNzI2MzM4NTEwNDM2MTI3TDQ3MzM2OTE1NjYwMDgxOTEyMDQwMTI3MDU3OTkwNjIyNzU5NjI2NDczMDE1MjQ1NzAyMTIzNDM3MzI3NTExNzE0MjI2NjMxMzU3NTICTDk2MzIxMTY1Mzc0MTE0NzI5ODE0NjQxNDc2NDAzOTM5MTc0MTg0MjAyODc4MDk1MTg2MjE5OTkzNDIyMDY3Njg4MzI4NDc2OTU4OTZMODQzNjI4NzE1MDMyMTIxNjU1MDkyMTU0ODg4NDIyNjAzODI3MzE5NDIwMjQ0MDU3NDcyOTAzODE5NjQwMTQwMzAyNDM4NDExMzg5OAIBMQEwA0w1MTcyMTg0MjA1NDg5MTQ4ODMxMDU4MTg5MTQyMDYyNzY3NTUzNzIzMTA5MzcyMjA5ODcyNDQwMTMwNTE4NDM2Mzg0MTc1MTI2NTI0TDUxNTE0MzIwNjYxMzg3NDcyMDkxMjA2ODc1MjIyMDg1NTQwNDE1NjQ4MjcwODA3OTcwNTA3MTY5Mjc3Njk2NDM0NDc0MjIxMjMyNzABMTF3aWFYTnpJam9pYUhSMGNITTZMeTlwWkM1MGQybDBZMmd1ZEhZdmIyRjFkR2d5SWl3AjJleUpoYkdjaU9pSlNVekkxTmlJc0luUjVjQ0k2SWtwWFZDSXNJbXRwWkNJNklqRWlmUU0yMDY4NzY0MjUxNzYzMDczMzI3MzY4Nzc5NTQ3NjI0NzQzNzc0MzgzNDYwMTEwMTU2NjY4NDk2Nzk2NzcwNTgwOTk4NTI0MTA2NTUzOQoAAAAAAAAAYQBn1v6x7RD9EyaiubLQ8qQkJSNI2Mr1GFHXZyOUJ+eCphFkwjYKBo44TMAbryd405BY+MHYTFLZOD06UTycKHgKucbuFjDvPnERRKZI2wa7sihPcnTPvuU//O5QPMGkkgA=",
906            ];
907
908            for fixture in FIXTURES {
909                let bcs = Base64::decode_vec(fixture).unwrap();
910
911                let sig: UserSignature = bcs::from_bytes(&bcs).unwrap();
912                assert_eq!(SignatureScheme::ZkLogin, sig.scheme());
913                let bytes = bcs::to_bytes(&sig).unwrap();
914                assert_eq!(bcs, bytes);
915
916                let json = serde_json::to_string_pretty(&sig).unwrap();
917                println!("{json}");
918                assert_eq!(sig, serde_json::from_str(&json).unwrap());
919            }
920        }
921
922        #[test]
923        fn passkey_fixtures() {
924            const FIXTURES: &[&str] = &[
925                "lgIGJUmWDeWIDoxodDQXD2R2YFuP5K65ooYyx5lc87qDHZdjHQAAAACKAXsidHlwZSI6IndlYmF1dGhuLmdldCIsImNoYWxsZW5nZSI6IkFBQUF0X21qSUIxdmJWcFlNNldWNllfb2l4Nko4YU5fOXNiOFNLRmJ1a0JmaVF3Iiwib3JpZ2luIjoiaHR0cDovL2xvY2FsaG9zdDo1MTczIiwiY3Jvc3NPcmlnaW4iOmZhbHNlfWICmOyQv1fJ+inKD0C/sxKtxyFKl9aoBign6p9Ih3iA2ahDVg2CPZqUOlEhur2S2GbIZjbn6TbgWtbXXg8SjLkL7wM9Fw4JO0AKLdnLC1nhQguHBX5K6Hv2ta1sqoOqEFDDEw==",
926            ];
927
928            for fixture in FIXTURES {
929                let bcs = Base64::decode_vec(fixture).unwrap();
930
931                let sig: UserSignature = bcs::from_bytes(&bcs).unwrap();
932                assert_eq!(SignatureScheme::Passkey, sig.scheme());
933                let bytes = bcs::to_bytes(&sig).unwrap();
934                assert_eq!(bcs, bytes);
935
936                let json = serde_json::to_string_pretty(&sig).unwrap();
937                println!("{json}");
938                assert_eq!(sig, serde_json::from_str(&json).unwrap());
939            }
940        }
941
942        #[test]
943        fn mutisig_with_passkey() {
944            const FIXTURE: &str = "4wMDAQSPAgYlWA5npzp6kvrYZs+ZuUq2Z1mil2S1cYIfq64uix17NuQdAAAAAIMBeyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoieEtoSGhUU1o4TVJTNjAtLTZNbjBreC1vSW1RWUs1RjNFMmtzQm9iRkk3OCIsIm9yaWdpbiI6Imh0dHBzOi8vd3d3LnN1aS5pbyIsImNyb3NzT3JpZ2luIjpmYWxzZX1iAj6cOZNgD7buVIny/mQVv0gt87gEpo/V6yFusFHc0ED0XBdBXIRO8cDcoKDOPeKpNOwyMn4JvCwh/bA9Z27vBcID4FVY2uPg1vTXoKTq74rVS0uaUzafqn5emXJEfpzp9osQAAUADX2rNYyNrapO+gBJp1sHQ2VVsQo2ghm7aA9wVxNJ13UBAQIOF81ZOeRrGWZBlozXWZELold+J/pz/eOHbbm+xbzrKwECA0f7ryPwOKvEwwiicRF6Kkz/rt28X/gcdRe8bHSn7bQwAQM8G2h0dHBzOi8vaWQudHdpdGNoLnR2L29hdXRoMgVuAvSJovCe0hhp6n7cBfcDd20d7RhhkODYMqLqqMIDAQQD4FVY2uPg1vTXoKTq74rVS0uaUzafqn5emXJEfpzp9osBAQA=";
945
946            let bcs = Base64::decode_vec(FIXTURE).unwrap();
947
948            let sig: UserSignature = bcs::from_bytes(&bcs).unwrap();
949            assert_eq!(SignatureScheme::Multisig, sig.scheme());
950
951            let committee = match &sig {
952                UserSignature::Multisig(multisig) => multisig.committee(),
953                _ => panic!("not a multisig"),
954            };
955            assert_eq!(
956                committee.derive_address(),
957                crate::Address::from_hex(
958                    "0x10f58251fa90d7fd5510f99787ac5c5874fe29c033524ba0275f1ed793ed8598"
959                )
960                .unwrap()
961            );
962
963            let bytes = bcs::to_bytes(&sig).unwrap();
964            assert_eq!(bcs, bytes);
965
966            let json = serde_json::to_string_pretty(&sig).unwrap();
967            println!("{json}");
968            assert_eq!(sig, serde_json::from_str(&json).unwrap());
969        }
970
971        #[test]
972        fn test_base64_string_deserialization() {
973            const ED25519_BASE64: &str = "ANp47jDZQszLl6ocHM/ShqWvViENf/24lL0ye0qdbd3nmrhqplhiegSWhoGZcpzoWgPltPA413BL0LB0xAaEhw8Nfas1jI2tqk76AEmnWwdDZVWxCjaCGbtoD3BXE0nXdQ==";
974            let compact_json = format!(r#""{}""#, ED25519_BASE64);
975            let sig_from_compact: UserSignature =
976                serde_json::from_str(&compact_json).expect("Should deserialize from base64 string");
977
978            #[derive(serde::Serialize, serde::Deserialize, PartialEq, Debug)]
979            struct TestCertificate {
980                signature: UserSignature,
981            }
982            let cert_compact = format!(r#"{{"signature": "{}"}}"#, ED25519_BASE64);
983            let cert: TestCertificate = serde_json::from_str(&cert_compact)
984                .expect("Should deserialize with base64 signature");
985
986            assert_eq!(cert.signature, sig_from_compact);
987        }
988    }
989}