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#[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 MlDsa65 {
52 signature: Box<MlDsa65Signature>,
53 public_key: Box<MlDsa65PublicKey>,
54 },
55}
56
57impl SimpleSignature {
58 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#[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, ZkLogin = 0x05,
402 Passkey = 0x06,
403 MlDsa65 = 0x07,
404}
405
406impl SignatureScheme {
407 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 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 pub fn to_u8(self) -> u8 {
438 self as u8
439 }
440}
441
442impl Ed25519PublicKey {
443 pub fn scheme(&self) -> SignatureScheme {
445 SignatureScheme::Ed25519
446 }
447}
448
449impl Secp256k1PublicKey {
450 pub fn scheme(&self) -> SignatureScheme {
452 SignatureScheme::Secp256k1
453 }
454}
455
456impl Secp256r1PublicKey {
457 pub fn scheme(&self) -> SignatureScheme {
459 SignatureScheme::Secp256r1
460 }
461}
462
463impl MlDsa65PublicKey {
464 pub fn scheme(&self) -> SignatureScheme {
466 SignatureScheme::MlDsa65
467 }
468}
469
470impl super::ZkLoginPublicIdentifier {
471 pub fn scheme(&self) -> SignatureScheme {
473 SignatureScheme::ZkLogin
474 }
475}
476
477impl super::PasskeyPublicKey {
478 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#[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 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 #[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 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 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}