Skip to main content

sui_crypto/
simple.rs

1use crate::SignatureError;
2use signature::Verifier;
3use sui_sdk_types::SimpleSignature;
4use sui_sdk_types::UserSignature;
5
6pub struct SimpleVerifier;
7
8impl Verifier<SimpleSignature> for SimpleVerifier {
9    #[allow(unused_variables)]
10    fn verify(&self, message: &[u8], signature: &SimpleSignature) -> Result<(), SignatureError> {
11        match signature {
12            #[cfg(feature = "ed25519")]
13            SimpleSignature::Ed25519 {
14                signature,
15                public_key,
16            } => {
17                let verifying_key = crate::ed25519::Ed25519VerifyingKey::new(public_key)?;
18                verifying_key.verify(message, signature)
19            }
20            #[cfg(not(feature = "ed25519"))]
21            SimpleSignature::Ed25519 { .. } => Err(SignatureError::from_source(
22                "support for ed25519 is not enabled",
23            )),
24            #[cfg(feature = "secp256k1")]
25            SimpleSignature::Secp256k1 {
26                signature,
27                public_key,
28            } => {
29                let verifying_key = crate::secp256k1::Secp256k1VerifyingKey::new(public_key)?;
30                verifying_key.verify(message, signature)
31            }
32            #[cfg(not(feature = "secp256k1"))]
33            SimpleSignature::Secp256k1 { .. } => Err(SignatureError::from_source(
34                "support for secp256k1 is not enabled",
35            )),
36            #[cfg(feature = "secp256r1")]
37            SimpleSignature::Secp256r1 {
38                signature,
39                public_key,
40            } => {
41                let verifying_key = crate::secp256r1::Secp256r1VerifyingKey::new(public_key)?;
42                verifying_key.verify(message, signature)
43            }
44            #[cfg(not(feature = "secp256r1"))]
45            SimpleSignature::Secp256r1 { .. } => Err(SignatureError::from_source(
46                "support for secp256r1 is not enabled",
47            )),
48            #[cfg(feature = "mldsa65")]
49            SimpleSignature::MlDsa65 {
50                signature,
51                public_key,
52            } => {
53                let verifying_key = crate::mldsa65::MlDsa65VerifyingKey::new(public_key)?;
54                verifying_key.verify(message, signature.as_ref())
55            }
56            #[cfg(not(feature = "mldsa65"))]
57            SimpleSignature::MlDsa65 { .. } => Err(SignatureError::from_source(
58                "support for mldsa65 is not enabled",
59            )),
60            _ => Err(SignatureError::from_source("unknown signature scheme")),
61        }
62    }
63}
64
65impl Verifier<UserSignature> for SimpleVerifier {
66    fn verify(&self, message: &[u8], signature: &UserSignature) -> Result<(), SignatureError> {
67        let UserSignature::Simple(signature) = signature else {
68            return Err(SignatureError::from_source("not a simple signature"));
69        };
70
71        <Self as Verifier<SimpleSignature>>::verify(self, message, signature)
72    }
73}
74
75#[cfg(any(
76    feature = "ed25519",
77    feature = "secp256r1",
78    feature = "secp256k1",
79    feature = "mldsa65",
80))]
81#[cfg_attr(
82    doc_cfg,
83    doc(cfg(any(
84        feature = "ed25519",
85        feature = "secp256r1",
86        feature = "secp256k1",
87        feature = "mldsa65",
88    )))
89)]
90#[rustfmt::skip]
91pub use keypair::{SimpleKeypair, SimpleVerifiyingKey};
92
93#[cfg(any(
94    feature = "ed25519",
95    feature = "secp256r1",
96    feature = "secp256k1",
97    feature = "mldsa65",
98))]
99#[cfg_attr(
100    doc_cfg,
101    doc(cfg(any(
102        feature = "ed25519",
103        feature = "secp256r1",
104        feature = "secp256k1",
105        feature = "mldsa65",
106    )))
107)]
108mod keypair {
109    use crate::SignatureError;
110    use signature::Signer;
111    use signature::Verifier;
112    use sui_sdk_types::MultisigMemberPublicKey;
113    use sui_sdk_types::SignatureScheme;
114    use sui_sdk_types::SimpleSignature;
115    use sui_sdk_types::UserSignature;
116
117    #[derive(Debug, Clone)]
118    pub struct SimpleKeypair {
119        inner: InnerKeypair,
120    }
121
122    #[derive(Debug, Clone)]
123    enum InnerKeypair {
124        #[cfg(feature = "ed25519")]
125        Ed25519(crate::ed25519::Ed25519PrivateKey),
126        #[cfg(feature = "secp256k1")]
127        Secp256k1(crate::secp256k1::Secp256k1PrivateKey),
128        #[cfg(feature = "secp256r1")]
129        Secp256r1(crate::secp256r1::Secp256r1PrivateKey),
130        #[cfg(feature = "mldsa65")]
131        MlDsa65(crate::mldsa65::MlDsa65PrivateKey),
132    }
133
134    impl SimpleKeypair {
135        pub fn scheme(&self) -> SignatureScheme {
136            match &self.inner {
137                #[cfg(feature = "ed25519")]
138                InnerKeypair::Ed25519(private_key) => private_key.scheme(),
139                #[cfg(feature = "secp256k1")]
140                InnerKeypair::Secp256k1(private_key) => private_key.scheme(),
141                #[cfg(feature = "secp256r1")]
142                InnerKeypair::Secp256r1(private_key) => private_key.scheme(),
143                #[cfg(feature = "mldsa65")]
144                InnerKeypair::MlDsa65(private_key) => private_key.scheme(),
145            }
146        }
147
148        pub fn verifying_key(&self) -> SimpleVerifiyingKey {
149            let verifying_key = match &self.inner {
150                #[cfg(feature = "ed25519")]
151                InnerKeypair::Ed25519(private_key) => {
152                    InnerVerifyingKey::Ed25519(private_key.verifying_key())
153                }
154                #[cfg(feature = "secp256k1")]
155                InnerKeypair::Secp256k1(private_key) => {
156                    InnerVerifyingKey::Secp256k1(private_key.verifying_key())
157                }
158                #[cfg(feature = "secp256r1")]
159                InnerKeypair::Secp256r1(private_key) => {
160                    InnerVerifyingKey::Secp256r1(private_key.verifying_key())
161                }
162                #[cfg(feature = "mldsa65")]
163                InnerKeypair::MlDsa65(private_key) => {
164                    InnerVerifyingKey::MlDsa65(private_key.verifying_key())
165                }
166            };
167
168            SimpleVerifiyingKey {
169                inner: verifying_key,
170            }
171        }
172
173        pub fn public_key(&self) -> MultisigMemberPublicKey {
174            self.verifying_key().public_key()
175        }
176
177        #[cfg(feature = "pem")]
178        #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))]
179        /// Deserialize PKCS#8 private key from ASN.1 DER-encoded data (binary format).
180        pub fn from_der(bytes: &[u8]) -> Result<Self, SignatureError> {
181            let private_key =
182                pkcs8::PrivateKeyInfo::try_from(bytes).map_err(SignatureError::from_source)?;
183
184            match private_key
185                .algorithm
186                .oids()
187                .map_err(SignatureError::from_source)?
188            {
189                #[cfg(feature = "ed25519")]
190                (ed25519_dalek::pkcs8::ALGORITHM_OID, None) => private_key
191                    .try_into()
192                    .map(crate::ed25519::Ed25519PrivateKey::from_dalek)
193                    .map(InnerKeypair::Ed25519)
194                    .map_err(SignatureError::from_source),
195
196                #[cfg(feature = "secp256r1")]
197                (
198                    p256::elliptic_curve::ALGORITHM_OID,
199                    Some(<p256::NistP256 as pkcs8::AssociatedOid>::OID),
200                ) => private_key
201                    .try_into()
202                    .map(crate::secp256r1::Secp256r1PrivateKey::from_p256)
203                    .map(InnerKeypair::Secp256r1)
204                    .map_err(SignatureError::from_source),
205
206                #[cfg(feature = "secp256k1")]
207                (
208                    k256::elliptic_curve::ALGORITHM_OID,
209                    Some(<k256::Secp256k1 as pkcs8::AssociatedOid>::OID),
210                ) => private_key
211                    .try_into()
212                    .map(crate::secp256k1::Secp256k1PrivateKey::from_k256)
213                    .map(InnerKeypair::Secp256k1)
214                    .map_err(SignatureError::from_source),
215
216                _ => Err(SignatureError::from_source(
217                    "unsupported or invalid private key type",
218                )),
219            }
220            .map(|inner| Self { inner })
221        }
222
223        #[cfg(feature = "pem")]
224        #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))]
225        /// Serialize this private key as DER-encoded PKCS#8
226        pub fn to_der(&self) -> Result<Vec<u8>, SignatureError> {
227            match &self.inner {
228                #[cfg(feature = "ed25519")]
229                InnerKeypair::Ed25519(private_key) => private_key.to_der(),
230                #[cfg(feature = "secp256k1")]
231                InnerKeypair::Secp256k1(private_key) => private_key.to_der(),
232                #[cfg(feature = "secp256r1")]
233                InnerKeypair::Secp256r1(private_key) => private_key.to_der(),
234                #[cfg(feature = "mldsa65")]
235                InnerKeypair::MlDsa65(_) => Err(SignatureError::from_source(
236                    "pem encoding is not supported for mldsa65",
237                )),
238            }
239        }
240
241        #[cfg(feature = "pem")]
242        #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))]
243        /// Deserialize PKCS#8-encoded private key from PEM.
244        pub fn from_pem(s: &str) -> Result<Self, SignatureError> {
245            use pkcs8::der::pem::PemLabel;
246
247            let (label, doc) =
248                pkcs8::SecretDocument::from_pem(s).map_err(SignatureError::from_source)?;
249            pkcs8::PrivateKeyInfo::validate_pem_label(label)
250                .map_err(SignatureError::from_source)?;
251            Self::from_der(doc.as_bytes())
252        }
253
254        #[cfg(feature = "pem")]
255        #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))]
256        /// Serialize this private key as DER-encoded PKCS#8
257        pub fn to_pem(&self) -> Result<String, SignatureError> {
258            match &self.inner {
259                #[cfg(feature = "ed25519")]
260                InnerKeypair::Ed25519(private_key) => private_key.to_pem(),
261                #[cfg(feature = "secp256k1")]
262                InnerKeypair::Secp256k1(private_key) => private_key.to_pem(),
263                #[cfg(feature = "secp256r1")]
264                InnerKeypair::Secp256r1(private_key) => private_key.to_pem(),
265                #[cfg(feature = "mldsa65")]
266                InnerKeypair::MlDsa65(_) => Err(SignatureError::from_source(
267                    "pem encoding is not supported for mldsa65",
268                )),
269            }
270        }
271
272        /// Build a keypair from the scheme flag and key bytes of a decoded
273        /// `flag || private_key` payload.
274        ///
275        /// Only the simple schemes (Ed25519, Secp256k1, Secp256r1, ML-DSA-65) are
276        /// accepted, matching the upstream `sui-types` parser.
277        fn from_flagged_key_bytes(
278            scheme: SignatureScheme,
279            key: Vec<u8>,
280        ) -> Result<Self, SignatureError> {
281            let inner = match scheme {
282                #[cfg(feature = "ed25519")]
283                SignatureScheme::Ed25519 => InnerKeypair::Ed25519(
284                    crate::ed25519::Ed25519PrivateKey::from_flagged_key_bytes(scheme, key)?,
285                ),
286                #[cfg(feature = "secp256k1")]
287                SignatureScheme::Secp256k1 => InnerKeypair::Secp256k1(
288                    crate::secp256k1::Secp256k1PrivateKey::from_flagged_key_bytes(scheme, key)?,
289                ),
290                #[cfg(feature = "secp256r1")]
291                SignatureScheme::Secp256r1 => InnerKeypair::Secp256r1(
292                    crate::secp256r1::Secp256r1PrivateKey::from_flagged_key_bytes(scheme, key)?,
293                ),
294                #[cfg(feature = "mldsa65")]
295                SignatureScheme::MlDsa65 => InnerKeypair::MlDsa65(
296                    crate::mldsa65::MlDsa65PrivateKey::from_flagged_key_bytes(scheme, key)?,
297                ),
298                other => {
299                    return Err(SignatureError::from_source(format!(
300                        "unsupported scheme `{}` in private key encoding",
301                        other.name(),
302                    )));
303                }
304            };
305            Ok(Self { inner })
306        }
307
308        #[cfg(feature = "bech32")]
309        #[cfg_attr(doc_cfg, doc(cfg(feature = "bech32")))]
310        /// Decode a Bech32 `suiprivkey` string produced by the Sui CLI.
311        ///
312        /// The leading flag byte selects the scheme. Only the simple schemes
313        /// (Ed25519, Secp256k1, Secp256r1) are accepted, matching the
314        /// upstream `sui-types` parser.
315        pub fn from_suiprivkey(s: &str) -> Result<Self, SignatureError> {
316            let (scheme, key) = crate::suipriv::decode(s)?;
317            Self::from_flagged_key_bytes(scheme, key)
318        }
319
320        #[cfg(feature = "bech32")]
321        #[cfg_attr(doc_cfg, doc(cfg(feature = "bech32")))]
322        /// Encode this private key as a Bech32 `suiprivkey` string.
323        pub fn to_suiprivkey(&self) -> Result<String, SignatureError> {
324            match &self.inner {
325                #[cfg(feature = "ed25519")]
326                InnerKeypair::Ed25519(private_key) => private_key.to_suiprivkey(),
327                #[cfg(feature = "secp256k1")]
328                InnerKeypair::Secp256k1(private_key) => private_key.to_suiprivkey(),
329                #[cfg(feature = "secp256r1")]
330                InnerKeypair::Secp256r1(private_key) => private_key.to_suiprivkey(),
331                #[cfg(feature = "mldsa65")]
332                InnerKeypair::MlDsa65(private_key) => private_key.to_suiprivkey(),
333            }
334        }
335
336        /// Decode a Base64 `flag || private_key` string, the legacy keystore
337        /// format used for entries of the Sui CLI's `sui.keystore` file.
338        ///
339        /// The leading flag byte selects the scheme. Only the simple schemes
340        /// (Ed25519, Secp256k1, Secp256r1) are accepted, matching the
341        /// upstream `sui-types` parser.
342        pub fn from_base64(s: &str) -> Result<Self, SignatureError> {
343            let (scheme, key) = crate::suipriv::decode_base64(s)?;
344            Self::from_flagged_key_bytes(scheme, key)
345        }
346
347        /// Encode this private key as a Base64 `flag || private_key` string,
348        /// the legacy keystore format used for entries of the Sui CLI's
349        /// `sui.keystore` file.
350        pub fn to_base64(&self) -> String {
351            match &self.inner {
352                #[cfg(feature = "ed25519")]
353                InnerKeypair::Ed25519(private_key) => private_key.to_base64(),
354                #[cfg(feature = "secp256k1")]
355                InnerKeypair::Secp256k1(private_key) => private_key.to_base64(),
356                #[cfg(feature = "secp256r1")]
357                InnerKeypair::Secp256r1(private_key) => private_key.to_base64(),
358                #[cfg(feature = "mldsa65")]
359                InnerKeypair::MlDsa65(private_key) => private_key.to_base64(),
360            }
361        }
362    }
363
364    impl Signer<SimpleSignature> for SimpleKeypair {
365        /// Signs with the underlying classical scheme.
366        ///
367        /// ML-DSA-65 cannot use this interface because signing requires fresh
368        /// per-signature randomness. Since `SimpleKeypair` hides the concrete
369        /// scheme, this is checked at runtime and ML-DSA users must use
370        /// [`signature::RandomizedSigner`] instead.
371        //
372        // When only `mldsa65` is enabled, `message` is unused because the only
373        // match arm returns the error above.
374        #[cfg_attr(
375            not(any(feature = "ed25519", feature = "secp256k1", feature = "secp256r1")),
376            allow(unused_variables)
377        )]
378        fn try_sign(&self, message: &[u8]) -> Result<SimpleSignature, SignatureError> {
379            match &self.inner {
380                #[cfg(feature = "ed25519")]
381                InnerKeypair::Ed25519(private_key) => private_key.try_sign(message),
382                #[cfg(feature = "secp256k1")]
383                InnerKeypair::Secp256k1(private_key) => private_key.try_sign(message),
384                #[cfg(feature = "secp256r1")]
385                InnerKeypair::Secp256r1(private_key) => private_key.try_sign(message),
386                #[cfg(feature = "mldsa65")]
387                InnerKeypair::MlDsa65(_) => Err(SignatureError::from_source(
388                    "mldsa65 signing requires randomness; sign with RandomizedSigner",
389                )),
390            }
391        }
392    }
393
394    /// Signs with any scheme; the deterministic ones ignore `rng`. Use this
395    /// when the scheme is only known at run time, e.g. a key loaded from
396    /// `suiprivkey`.
397    #[cfg(feature = "mldsa65")]
398    #[cfg_attr(doc_cfg, doc(cfg(feature = "mldsa65")))]
399    impl signature::RandomizedSigner<SimpleSignature> for SimpleKeypair {
400        fn try_sign_with_rng(
401            &self,
402            rng: &mut impl rand_core::CryptoRngCore,
403            message: &[u8],
404        ) -> Result<SimpleSignature, SignatureError> {
405            match &self.inner {
406                #[cfg(feature = "ed25519")]
407                InnerKeypair::Ed25519(private_key) => private_key.try_sign(message),
408                #[cfg(feature = "secp256k1")]
409                InnerKeypair::Secp256k1(private_key) => private_key.try_sign(message),
410                #[cfg(feature = "secp256r1")]
411                InnerKeypair::Secp256r1(private_key) => private_key.try_sign(message),
412                InnerKeypair::MlDsa65(private_key) => {
413                    signature::RandomizedSigner::try_sign_with_rng(private_key, rng, message)
414                }
415            }
416        }
417    }
418
419    #[cfg(feature = "mldsa65")]
420    #[cfg_attr(doc_cfg, doc(cfg(feature = "mldsa65")))]
421    impl signature::RandomizedSigner<UserSignature> for SimpleKeypair {
422        fn try_sign_with_rng(
423            &self,
424            rng: &mut impl rand_core::CryptoRngCore,
425            msg: &[u8],
426        ) -> Result<UserSignature, SignatureError> {
427            <Self as signature::RandomizedSigner<SimpleSignature>>::try_sign_with_rng(
428                self, rng, msg,
429            )
430            .map(UserSignature::Simple)
431        }
432    }
433
434    impl Signer<UserSignature> for SimpleKeypair {
435        fn try_sign(&self, msg: &[u8]) -> Result<UserSignature, SignatureError> {
436            <Self as Signer<SimpleSignature>>::try_sign(self, msg).map(UserSignature::Simple)
437        }
438    }
439
440    #[cfg(feature = "ed25519")]
441    #[cfg_attr(doc_cfg, doc(cfg(feature = "ed25519")))]
442    impl From<crate::ed25519::Ed25519PrivateKey> for SimpleKeypair {
443        fn from(private_key: crate::ed25519::Ed25519PrivateKey) -> Self {
444            Self {
445                inner: InnerKeypair::Ed25519(private_key),
446            }
447        }
448    }
449
450    #[cfg(feature = "secp256r1")]
451    #[cfg_attr(doc_cfg, doc(cfg(feature = "secp256r1")))]
452    impl From<crate::secp256r1::Secp256r1PrivateKey> for SimpleKeypair {
453        fn from(private_key: crate::secp256r1::Secp256r1PrivateKey) -> Self {
454            Self {
455                inner: InnerKeypair::Secp256r1(private_key),
456            }
457        }
458    }
459
460    #[cfg(feature = "secp256k1")]
461    #[cfg_attr(doc_cfg, doc(cfg(feature = "secp256k1")))]
462    impl From<crate::secp256k1::Secp256k1PrivateKey> for SimpleKeypair {
463        fn from(private_key: crate::secp256k1::Secp256k1PrivateKey) -> Self {
464            Self {
465                inner: InnerKeypair::Secp256k1(private_key),
466            }
467        }
468    }
469
470    #[cfg(feature = "mldsa65")]
471    #[cfg_attr(doc_cfg, doc(cfg(feature = "mldsa65")))]
472    impl From<crate::mldsa65::MlDsa65PrivateKey> for SimpleKeypair {
473        fn from(private_key: crate::mldsa65::MlDsa65PrivateKey) -> Self {
474            Self {
475                inner: InnerKeypair::MlDsa65(private_key),
476            }
477        }
478    }
479
480    #[derive(Debug, Clone, Eq, PartialEq)]
481    pub struct SimpleVerifiyingKey {
482        inner: InnerVerifyingKey,
483    }
484
485    #[derive(Debug, Clone, Eq, PartialEq)]
486    enum InnerVerifyingKey {
487        #[cfg(feature = "ed25519")]
488        Ed25519(crate::ed25519::Ed25519VerifyingKey),
489        #[cfg(feature = "secp256k1")]
490        Secp256k1(crate::secp256k1::Secp256k1VerifyingKey),
491        #[cfg(feature = "secp256r1")]
492        Secp256r1(crate::secp256r1::Secp256r1VerifyingKey),
493        #[cfg(feature = "mldsa65")]
494        MlDsa65(crate::mldsa65::MlDsa65VerifyingKey),
495    }
496
497    impl SimpleVerifiyingKey {
498        pub fn scheme(&self) -> SignatureScheme {
499            match &self.inner {
500                #[cfg(feature = "ed25519")]
501                InnerVerifyingKey::Ed25519(verifying_key) => verifying_key.public_key().scheme(),
502                #[cfg(feature = "secp256k1")]
503                InnerVerifyingKey::Secp256k1(verifying_key) => verifying_key.public_key().scheme(),
504                #[cfg(feature = "secp256r1")]
505                InnerVerifyingKey::Secp256r1(verifying_key) => verifying_key.public_key().scheme(),
506                #[cfg(feature = "mldsa65")]
507                InnerVerifyingKey::MlDsa65(verifying_key) => verifying_key.public_key().scheme(),
508            }
509        }
510
511        pub fn public_key(&self) -> MultisigMemberPublicKey {
512            match &self.inner {
513                #[cfg(feature = "ed25519")]
514                InnerVerifyingKey::Ed25519(verifying_key) => {
515                    MultisigMemberPublicKey::Ed25519(verifying_key.public_key())
516                }
517                #[cfg(feature = "secp256k1")]
518                InnerVerifyingKey::Secp256k1(verifying_key) => {
519                    MultisigMemberPublicKey::Secp256k1(verifying_key.public_key())
520                }
521                #[cfg(feature = "secp256r1")]
522                InnerVerifyingKey::Secp256r1(verifying_key) => {
523                    MultisigMemberPublicKey::Secp256r1(verifying_key.public_key())
524                }
525                #[cfg(feature = "mldsa65")]
526                InnerVerifyingKey::MlDsa65(verifying_key) => {
527                    MultisigMemberPublicKey::MlDsa65(Box::new(verifying_key.public_key()))
528                }
529            }
530        }
531
532        /// Derive the `Address` that corresponds to this public key.
533        pub fn derive_address(&self) -> sui_sdk_types::Address {
534            match &self.inner {
535                #[cfg(feature = "ed25519")]
536                InnerVerifyingKey::Ed25519(verifying_key) => {
537                    verifying_key.public_key().derive_address()
538                }
539                #[cfg(feature = "secp256k1")]
540                InnerVerifyingKey::Secp256k1(verifying_key) => {
541                    verifying_key.public_key().derive_address()
542                }
543                #[cfg(feature = "secp256r1")]
544                InnerVerifyingKey::Secp256r1(verifying_key) => {
545                    verifying_key.public_key().derive_address()
546                }
547                #[cfg(feature = "mldsa65")]
548                InnerVerifyingKey::MlDsa65(verifying_key) => {
549                    verifying_key.public_key().derive_address()
550                }
551            }
552        }
553
554        #[cfg(feature = "pem")]
555        #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))]
556        /// Deserialize public key from ASN.1 DER-encoded data (binary format).
557        pub fn from_der(bytes: &[u8]) -> Result<Self, SignatureError> {
558            let public_key = pkcs8::SubjectPublicKeyInfoRef::try_from(bytes)
559                .map_err(SignatureError::from_source)?;
560
561            match public_key
562                .algorithm
563                .oids()
564                .map_err(SignatureError::from_source)?
565            {
566                #[cfg(feature = "ed25519")]
567                (ed25519_dalek::pkcs8::ALGORITHM_OID, None) => public_key
568                    .try_into()
569                    .map(crate::ed25519::Ed25519VerifyingKey::from_dalek)
570                    .map(InnerVerifyingKey::Ed25519)
571                    .map_err(SignatureError::from_source),
572
573                #[cfg(feature = "secp256r1")]
574                (
575                    p256::elliptic_curve::ALGORITHM_OID,
576                    Some(<p256::NistP256 as pkcs8::AssociatedOid>::OID),
577                ) => public_key
578                    .try_into()
579                    .map(crate::secp256r1::Secp256r1VerifyingKey::from_p256)
580                    .map(InnerVerifyingKey::Secp256r1)
581                    .map_err(SignatureError::from_source),
582
583                #[cfg(feature = "secp256k1")]
584                (
585                    k256::elliptic_curve::ALGORITHM_OID,
586                    Some(<k256::Secp256k1 as pkcs8::AssociatedOid>::OID),
587                ) => public_key
588                    .try_into()
589                    .map(crate::secp256k1::Secp256k1VerifyingKey::from_k256)
590                    .map(InnerVerifyingKey::Secp256k1)
591                    .map_err(SignatureError::from_source),
592
593                _ => Err(SignatureError::from_source(
594                    "unsupported or invalid public key type",
595                )),
596            }
597            .map(|inner| Self { inner })
598        }
599
600        #[cfg(feature = "pem")]
601        #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))]
602        /// Serialize this public key as DER-encoded data
603        pub fn to_der(&self) -> Result<Vec<u8>, SignatureError> {
604            match &self.inner {
605                #[cfg(feature = "ed25519")]
606                InnerVerifyingKey::Ed25519(verifying_key) => verifying_key.to_der(),
607                #[cfg(feature = "secp256k1")]
608                InnerVerifyingKey::Secp256k1(verifying_key) => verifying_key.to_der(),
609                #[cfg(feature = "secp256r1")]
610                InnerVerifyingKey::Secp256r1(verifying_key) => verifying_key.to_der(),
611                #[cfg(feature = "mldsa65")]
612                InnerVerifyingKey::MlDsa65(_) => Err(SignatureError::from_source(
613                    "pem encoding is not supported for mldsa65",
614                )),
615            }
616        }
617
618        #[cfg(feature = "pem")]
619        #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))]
620        /// Deserialize public key from PEM.
621        pub fn from_pem(s: &str) -> Result<Self, SignatureError> {
622            use pkcs8::der::pem::PemLabel;
623
624            let (label, doc) = pkcs8::Document::from_pem(s).map_err(SignatureError::from_source)?;
625            pkcs8::SubjectPublicKeyInfoRef::validate_pem_label(label)
626                .map_err(SignatureError::from_source)?;
627            Self::from_der(doc.as_bytes())
628        }
629
630        #[cfg(feature = "pem")]
631        #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))]
632        /// Serialize this public key as PEM
633        pub fn to_pem(&self) -> Result<String, SignatureError> {
634            match &self.inner {
635                #[cfg(feature = "ed25519")]
636                InnerVerifyingKey::Ed25519(verifying_key) => verifying_key.to_pem(),
637                #[cfg(feature = "secp256k1")]
638                InnerVerifyingKey::Secp256k1(verifying_key) => verifying_key.to_pem(),
639                #[cfg(feature = "secp256r1")]
640                InnerVerifyingKey::Secp256r1(verifying_key) => verifying_key.to_pem(),
641                #[cfg(feature = "mldsa65")]
642                InnerVerifyingKey::MlDsa65(_) => Err(SignatureError::from_source(
643                    "pem encoding is not supported for mldsa65",
644                )),
645            }
646        }
647    }
648
649    impl Verifier<SimpleSignature> for SimpleVerifiyingKey {
650        fn verify(
651            &self,
652            message: &[u8],
653            signature: &SimpleSignature,
654        ) -> Result<(), SignatureError> {
655            match &self.inner {
656                #[cfg(feature = "ed25519")]
657                InnerVerifyingKey::Ed25519(verifying_key) => {
658                    verifying_key.verify(message, signature)
659                }
660                #[cfg(feature = "secp256k1")]
661                InnerVerifyingKey::Secp256k1(verifying_key) => {
662                    verifying_key.verify(message, signature)
663                }
664                #[cfg(feature = "secp256r1")]
665                InnerVerifyingKey::Secp256r1(verifying_key) => {
666                    verifying_key.verify(message, signature)
667                }
668                #[cfg(feature = "mldsa65")]
669                InnerVerifyingKey::MlDsa65(verifying_key) => {
670                    verifying_key.verify(message, signature)
671                }
672            }
673        }
674    }
675
676    impl Verifier<UserSignature> for SimpleVerifiyingKey {
677        fn verify(&self, message: &[u8], signature: &UserSignature) -> Result<(), SignatureError> {
678            let UserSignature::Simple(signature) = signature else {
679                return Err(SignatureError::from_source("not a simple signature"));
680            };
681
682            <Self as Verifier<SimpleSignature>>::verify(self, message, signature)
683        }
684    }
685
686    #[cfg(feature = "ed25519")]
687    #[cfg_attr(doc_cfg, doc(cfg(feature = "ed25519")))]
688    impl From<crate::ed25519::Ed25519VerifyingKey> for SimpleVerifiyingKey {
689        fn from(verifying_key: crate::ed25519::Ed25519VerifyingKey) -> Self {
690            Self {
691                inner: InnerVerifyingKey::Ed25519(verifying_key),
692            }
693        }
694    }
695
696    #[cfg(feature = "secp256r1")]
697    #[cfg_attr(doc_cfg, doc(cfg(feature = "secp256r1")))]
698    impl From<crate::secp256r1::Secp256r1VerifyingKey> for SimpleVerifiyingKey {
699        fn from(verifying_key: crate::secp256r1::Secp256r1VerifyingKey) -> Self {
700            Self {
701                inner: InnerVerifyingKey::Secp256r1(verifying_key),
702            }
703        }
704    }
705
706    #[cfg(feature = "secp256k1")]
707    #[cfg_attr(doc_cfg, doc(cfg(feature = "secp256k1")))]
708    impl From<crate::secp256k1::Secp256k1VerifyingKey> for SimpleVerifiyingKey {
709        fn from(verifying_key: crate::secp256k1::Secp256k1VerifyingKey) -> Self {
710            Self {
711                inner: InnerVerifyingKey::Secp256k1(verifying_key),
712            }
713        }
714    }
715
716    #[cfg(feature = "mldsa65")]
717    #[cfg_attr(doc_cfg, doc(cfg(feature = "mldsa65")))]
718    impl From<crate::mldsa65::MlDsa65VerifyingKey> for SimpleVerifiyingKey {
719        fn from(verifying_key: crate::mldsa65::MlDsa65VerifyingKey) -> Self {
720            Self {
721                inner: InnerVerifyingKey::MlDsa65(verifying_key),
722            }
723        }
724    }
725}
726
727#[cfg(test)]
728mod test {
729    use super::*;
730    use crate::ed25519::Ed25519PrivateKey;
731    use crate::ed25519::Ed25519VerifyingKey;
732    use crate::mldsa65::MlDsa65PrivateKey;
733    use crate::secp256k1::Secp256k1PrivateKey;
734    use crate::secp256k1::Secp256k1VerifyingKey;
735    use crate::secp256r1::Secp256r1PrivateKey;
736    use crate::secp256r1::Secp256r1VerifyingKey;
737    use test_strategy::proptest;
738
739    #[cfg(target_arch = "wasm32")]
740    use wasm_bindgen_test::wasm_bindgen_test as test;
741
742    #[proptest]
743    fn ed25519_pem_der(signer: Ed25519PrivateKey) {
744        //
745        // Private Key
746        //
747        let public_key = signer.public_key();
748        let ed25519_der = signer.to_der().unwrap();
749        let ed25519_pem = signer.to_pem().unwrap();
750
751        // der and pem round-trip
752        let from_der = Ed25519PrivateKey::from_der(&ed25519_der).unwrap();
753        assert_eq!(from_der.public_key(), public_key);
754        let from_pem = Ed25519PrivateKey::from_pem(&ed25519_pem).unwrap();
755        assert_eq!(from_pem.public_key(), public_key);
756
757        // der and pem bytes don't convert to secp256r1 or secp256k1
758        Secp256r1PrivateKey::from_der(&ed25519_der).unwrap_err();
759        Secp256r1PrivateKey::from_pem(&ed25519_pem).unwrap_err();
760        Secp256k1PrivateKey::from_der(&ed25519_der).unwrap_err();
761        Secp256k1PrivateKey::from_pem(&ed25519_pem).unwrap_err();
762
763        // SimpleKeypair parses
764        let keypair_from_der = SimpleKeypair::from_der(&ed25519_der).unwrap();
765        assert_eq!(ed25519_der, keypair_from_der.to_der().unwrap());
766        let keypair_from_pem = SimpleKeypair::from_pem(&ed25519_pem).unwrap();
767        assert_eq!(ed25519_pem, keypair_from_pem.to_pem().unwrap());
768
769        //
770        // Verifying Key
771        //
772        let verifying_key = signer.verifying_key();
773        let der = verifying_key.to_der().unwrap();
774        let pem = verifying_key.to_pem().unwrap();
775
776        // der and pem round-trip
777        let from_der = Ed25519VerifyingKey::from_der(&der).unwrap();
778        assert_eq!(from_der.public_key(), public_key);
779        let from_pem = Ed25519VerifyingKey::from_pem(&pem).unwrap();
780        assert_eq!(from_pem.public_key(), public_key);
781
782        // der and pem bytes don't convert to secp256r1 or secp256k1
783        Secp256r1VerifyingKey::from_der(&der).unwrap_err();
784        Secp256r1VerifyingKey::from_pem(&pem).unwrap_err();
785        Secp256k1VerifyingKey::from_der(&der).unwrap_err();
786        Secp256k1VerifyingKey::from_pem(&pem).unwrap_err();
787
788        // SimpleKeypair parses
789        let from_der = SimpleVerifiyingKey::from_der(&der).unwrap();
790        assert_eq!(der, from_der.to_der().unwrap());
791        let from_pem = SimpleVerifiyingKey::from_pem(&pem).unwrap();
792        assert_eq!(pem, from_pem.to_pem().unwrap());
793    }
794
795    #[proptest]
796    fn secp256r1_pem_der(signer: Secp256r1PrivateKey) {
797        //
798        // Private Key
799        //
800        let public_key = signer.public_key();
801        let secp256r1_der = signer.to_der().unwrap();
802        let secp256r1_pem = signer.to_pem().unwrap();
803
804        // der and pem round-trip
805        let from_der = Secp256r1PrivateKey::from_der(&secp256r1_der).unwrap();
806        assert_eq!(from_der.public_key(), public_key);
807        let from_pem = Secp256r1PrivateKey::from_pem(&secp256r1_pem).unwrap();
808        assert_eq!(from_pem.public_key(), public_key);
809
810        // der and pem bytes don't convert to ed25519 or secp256k1
811        Ed25519PrivateKey::from_der(&secp256r1_der).unwrap_err();
812        Ed25519PrivateKey::from_pem(&secp256r1_pem).unwrap_err();
813        Secp256k1PrivateKey::from_der(&secp256r1_der).unwrap_err();
814        Secp256k1PrivateKey::from_pem(&secp256r1_pem).unwrap_err();
815
816        // SimpleKeypair parses
817        let keypair_from_der = SimpleKeypair::from_der(&secp256r1_der).unwrap();
818        assert_eq!(secp256r1_der, keypair_from_der.to_der().unwrap());
819        let keypair_from_pem = SimpleKeypair::from_pem(&secp256r1_pem).unwrap();
820        assert_eq!(secp256r1_pem, keypair_from_pem.to_pem().unwrap());
821
822        //
823        // Verifying Key
824        //
825        let verifying_key = signer.verifying_key();
826        let der = verifying_key.to_der().unwrap();
827        let pem = verifying_key.to_pem().unwrap();
828
829        // der and pem round-trip
830        let from_der = Secp256r1VerifyingKey::from_der(&der).unwrap();
831        assert_eq!(from_der.public_key(), public_key);
832        let from_pem = Secp256r1VerifyingKey::from_pem(&pem).unwrap();
833        assert_eq!(from_pem.public_key(), public_key);
834
835        // der and pem bytes don't convert to ed25519 or secp256k1
836        Ed25519VerifyingKey::from_der(&der).unwrap_err();
837        Ed25519VerifyingKey::from_pem(&pem).unwrap_err();
838        Secp256k1VerifyingKey::from_der(&der).unwrap_err();
839        Secp256k1VerifyingKey::from_pem(&pem).unwrap_err();
840
841        // SimpleKeypair parses
842        let from_der = SimpleVerifiyingKey::from_der(&der).unwrap();
843        assert_eq!(der, from_der.to_der().unwrap());
844        let from_pem = SimpleVerifiyingKey::from_pem(&pem).unwrap();
845        assert_eq!(pem, from_pem.to_pem().unwrap());
846    }
847
848    #[proptest]
849    fn secp256k1_pem_der(signer: Secp256k1PrivateKey) {
850        //
851        // Private Key
852        //
853        let public_key = signer.public_key();
854        let secp256k1_der = signer.to_der().unwrap();
855        let secp256k1_pem = signer.to_pem().unwrap();
856
857        // der and pem round-trip
858        let from_der = Secp256k1PrivateKey::from_der(&secp256k1_der).unwrap();
859        assert_eq!(from_der.public_key(), public_key);
860        let from_pem = Secp256k1PrivateKey::from_pem(&secp256k1_pem).unwrap();
861        assert_eq!(from_pem.public_key(), public_key);
862
863        // der and pem bytes don't convert to secp256r1 or ed25519
864        Ed25519PrivateKey::from_der(&secp256k1_der).unwrap_err();
865        Ed25519PrivateKey::from_pem(&secp256k1_pem).unwrap_err();
866        Secp256r1PrivateKey::from_der(&secp256k1_der).unwrap_err();
867        Secp256r1PrivateKey::from_pem(&secp256k1_pem).unwrap_err();
868
869        // SimpleKeypair parses
870        let keypair_from_der = SimpleKeypair::from_der(&secp256k1_der).unwrap();
871        assert_eq!(secp256k1_der, keypair_from_der.to_der().unwrap());
872        let keypair_from_pem = SimpleKeypair::from_pem(&secp256k1_pem).unwrap();
873        assert_eq!(secp256k1_pem, keypair_from_pem.to_pem().unwrap());
874
875        //
876        // Verifying Key
877        //
878        let verifying_key = signer.verifying_key();
879        let der = verifying_key.to_der().unwrap();
880        let pem = verifying_key.to_pem().unwrap();
881
882        // der and pem round-trip
883        let from_der = Secp256k1VerifyingKey::from_der(&der).unwrap();
884        assert_eq!(from_der.public_key(), public_key);
885        let from_pem = Secp256k1VerifyingKey::from_pem(&pem).unwrap();
886        assert_eq!(from_pem.public_key(), public_key);
887
888        // der and pem bytes don't convert to ed25519 or secp256r1
889        Ed25519VerifyingKey::from_der(&der).unwrap_err();
890        Ed25519VerifyingKey::from_pem(&pem).unwrap_err();
891        Secp256r1VerifyingKey::from_der(&der).unwrap_err();
892        Secp256r1VerifyingKey::from_pem(&pem).unwrap_err();
893
894        // SimpleKeypair parses
895        let from_der = SimpleVerifiyingKey::from_der(&der).unwrap();
896        assert_eq!(der, from_der.to_der().unwrap());
897        let from_pem = SimpleVerifiyingKey::from_pem(&pem).unwrap();
898        assert_eq!(pem, from_pem.to_pem().unwrap());
899    }
900
901    #[test]
902    fn mldsa65_pem_der_unsupported() {
903        let keypair = SimpleKeypair::from(MlDsa65PrivateKey::new([2; 32]));
904        keypair.to_der().unwrap_err();
905        keypair.to_pem().unwrap_err();
906
907        let verifying_key = keypair.verifying_key();
908        verifying_key.to_der().unwrap_err();
909        verifying_key.to_pem().unwrap_err();
910    }
911
912    #[proptest]
913    fn mldsa65_sign_and_verify(signer: MlDsa65PrivateKey, message: Vec<u8>, seed: u64) {
914        use crate::test_util::TestRng;
915        use signature::RandomizedSigner;
916        use signature::Signer;
917
918        let keypair = SimpleKeypair::from(signer.clone());
919        assert_eq!(keypair.scheme(), sui_sdk_types::SignatureScheme::MlDsa65);
920        assert_eq!(
921            keypair.verifying_key(),
922            SimpleVerifiyingKey::from(signer.verifying_key())
923        );
924
925        // `Signer` takes no rng, so ML-DSA-65 keys must sign through
926        // `RandomizedSigner` instead.
927        Signer::<SimpleSignature>::try_sign(&keypair, &message).unwrap_err();
928        Signer::<UserSignature>::try_sign(&keypair, &message).unwrap_err();
929
930        let signature: UserSignature = keypair
931            .try_sign_with_rng(&mut TestRng(seed), &message)
932            .unwrap();
933        keypair
934            .verifying_key()
935            .verify(&message, &signature)
936            .unwrap();
937        SimpleVerifier.verify(&message, &signature).unwrap();
938
939        // The signature is bound to both the message and the key.
940        let mut other_message = message.clone();
941        other_message.push(0);
942        keypair
943            .verifying_key()
944            .verify(&other_message, &signature)
945            .unwrap_err();
946        let mut other_seed = *signer.seed();
947        other_seed[0] ^= 1;
948        SimpleKeypair::from(MlDsa65PrivateKey::new(other_seed))
949            .verifying_key()
950            .verify(&message, &signature)
951            .unwrap_err();
952    }
953
954    // The classical schemes sign deterministically, so signing a
955    // `SimpleKeypair` through `RandomizedSigner` must ignore the rng and
956    // produce the same signature as `Signer`.
957    #[proptest]
958    fn randomized_signer_matches_signer_for_deterministic_schemes(
959        ed25519: Ed25519PrivateKey,
960        secp256k1: Secp256k1PrivateKey,
961        secp256r1: Secp256r1PrivateKey,
962        message: Vec<u8>,
963        seed: u64,
964    ) {
965        use crate::test_util::TestRng;
966        use signature::RandomizedSigner;
967        use signature::Signer;
968
969        for keypair in [
970            SimpleKeypair::from(ed25519),
971            SimpleKeypair::from(secp256k1),
972            SimpleKeypair::from(secp256r1),
973        ] {
974            let expected: UserSignature = keypair.try_sign(&message).unwrap();
975            let signature: UserSignature = keypair
976                .try_sign_with_rng(&mut TestRng(seed), &message)
977                .unwrap();
978            assert_eq!(signature, expected);
979        }
980    }
981
982    #[proptest]
983    fn simple_verifying_key_derives_matching_address(
984        ed25519: Ed25519PrivateKey,
985        secp256k1: Secp256k1PrivateKey,
986        secp256r1: Secp256r1PrivateKey,
987        mldsa65: MlDsa65PrivateKey,
988    ) {
989        // SimpleVerifiyingKey::derive_address must agree with the address
990        // derived from the scheme-specific public key.
991        let expected = ed25519.public_key().derive_address();
992        let keypair = SimpleKeypair::from(ed25519);
993        assert_eq!(keypair.verifying_key().derive_address(), expected);
994
995        let expected = secp256k1.public_key().derive_address();
996        let keypair = SimpleKeypair::from(secp256k1);
997        assert_eq!(keypair.verifying_key().derive_address(), expected);
998
999        let expected = secp256r1.public_key().derive_address();
1000        let keypair = SimpleKeypair::from(secp256r1);
1001        assert_eq!(keypair.verifying_key().derive_address(), expected);
1002
1003        let expected = mldsa65.public_key().derive_address();
1004        let keypair = SimpleKeypair::from(mldsa65);
1005        assert_eq!(keypair.verifying_key().derive_address(), expected);
1006    }
1007
1008    // Round-trip and rejection tests for the suiprivkey Bech32 format.
1009    //
1010    // These mirror the format produced by the Sui CLI and the upstream
1011    // `sui-types` crate. `EXPECTED_ED25519_VECTOR` is taken directly from
1012    // `crates/sui-types/src/unit_tests/crypto_tests.rs` in the main sui repo
1013    // and is included as a regression vector against any future encoding
1014    // drift.
1015    #[cfg(feature = "bech32")]
1016    mod bech32 {
1017        use super::*;
1018        use sui_sdk_types::SignatureScheme;
1019
1020        #[cfg(target_arch = "wasm32")]
1021        use wasm_bindgen_test::wasm_bindgen_test as test;
1022
1023        // Upstream test vector: `Ed25519KeyPair::generate(&mut StdRng::from_seed([0; 32]))`
1024        // encoded with `SuiKeyPair::encode()` produces this string. The leading
1025        // flag byte is 0x00 (Ed25519); the remaining 32 bytes are the private
1026        // key.
1027        const UPSTREAM_ED25519_SUIPRIVKEY: &str =
1028            "suiprivkey1qzdlfxn2qa2lj5uprl8pyhexs02sg2wrhdy7qaq50cqgnffw4c2477kg9h3";
1029
1030        #[proptest]
1031        fn ed25519_round_trip(signer: Ed25519PrivateKey) {
1032            let encoded = signer.to_suiprivkey().unwrap();
1033            let decoded = Ed25519PrivateKey::from_suiprivkey(&encoded).unwrap();
1034            assert_eq!(decoded.public_key(), signer.public_key());
1035
1036            // SimpleKeypair dispatch agrees.
1037            let keypair = SimpleKeypair::from_suiprivkey(&encoded).unwrap();
1038            assert_eq!(keypair.scheme(), signer.scheme());
1039            assert_eq!(encoded, keypair.to_suiprivkey().unwrap());
1040        }
1041
1042        #[proptest]
1043        fn secp256k1_round_trip(signer: Secp256k1PrivateKey) {
1044            let encoded = signer.to_suiprivkey().unwrap();
1045            let decoded = Secp256k1PrivateKey::from_suiprivkey(&encoded).unwrap();
1046            assert_eq!(decoded.public_key(), signer.public_key());
1047
1048            let keypair = SimpleKeypair::from_suiprivkey(&encoded).unwrap();
1049            assert_eq!(keypair.scheme(), signer.scheme());
1050            assert_eq!(encoded, keypair.to_suiprivkey().unwrap());
1051        }
1052
1053        #[proptest]
1054        fn secp256r1_round_trip(signer: Secp256r1PrivateKey) {
1055            let encoded = signer.to_suiprivkey().unwrap();
1056            let decoded = Secp256r1PrivateKey::from_suiprivkey(&encoded).unwrap();
1057            assert_eq!(decoded.public_key(), signer.public_key());
1058
1059            let keypair = SimpleKeypair::from_suiprivkey(&encoded).unwrap();
1060            assert_eq!(keypair.scheme(), signer.scheme());
1061            assert_eq!(encoded, keypair.to_suiprivkey().unwrap());
1062        }
1063
1064        #[proptest]
1065        fn mldsa65_round_trip(signer: MlDsa65PrivateKey) {
1066            let encoded = signer.to_suiprivkey().unwrap();
1067            let decoded = MlDsa65PrivateKey::from_suiprivkey(&encoded).unwrap();
1068            assert_eq!(decoded.public_key(), signer.public_key());
1069
1070            let keypair = SimpleKeypair::from_suiprivkey(&encoded).unwrap();
1071            assert_eq!(keypair.scheme(), signer.scheme());
1072            assert_eq!(
1073                keypair.verifying_key(),
1074                SimpleVerifiyingKey::from(signer.verifying_key())
1075            );
1076            assert_eq!(encoded, keypair.to_suiprivkey().unwrap());
1077
1078            // Wrong-scheme per-scheme decoders reject it.
1079            Ed25519PrivateKey::from_suiprivkey(&encoded).unwrap_err();
1080            Secp256k1PrivateKey::from_suiprivkey(&encoded).unwrap_err();
1081            Secp256r1PrivateKey::from_suiprivkey(&encoded).unwrap_err();
1082        }
1083
1084        #[test]
1085        fn upstream_ed25519_vector_round_trips() {
1086            let keypair = SimpleKeypair::from_suiprivkey(UPSTREAM_ED25519_SUIPRIVKEY).unwrap();
1087            assert_eq!(keypair.scheme(), SignatureScheme::Ed25519);
1088            assert_eq!(
1089                keypair.to_suiprivkey().unwrap(),
1090                UPSTREAM_ED25519_SUIPRIVKEY
1091            );
1092
1093            // Per-scheme decoder accepts it too.
1094            Ed25519PrivateKey::from_suiprivkey(UPSTREAM_ED25519_SUIPRIVKEY).unwrap();
1095            // Wrong-scheme per-scheme decoders reject it.
1096            Secp256k1PrivateKey::from_suiprivkey(UPSTREAM_ED25519_SUIPRIVKEY).unwrap_err();
1097            Secp256r1PrivateKey::from_suiprivkey(UPSTREAM_ED25519_SUIPRIVKEY).unwrap_err();
1098            MlDsa65PrivateKey::from_suiprivkey(UPSTREAM_ED25519_SUIPRIVKEY).unwrap_err();
1099        }
1100
1101        #[test]
1102        fn rejects_wrong_hrp() {
1103            // Same payload as the upstream Ed25519 vector but encoded with a
1104            // different HRP — must fail.
1105            let bytes = ::bech32::primitives::decode::CheckedHrpstring::new::<::bech32::Bech32>(
1106                UPSTREAM_ED25519_SUIPRIVKEY,
1107            )
1108            .unwrap()
1109            .byte_iter()
1110            .collect::<Vec<_>>();
1111            let wrong_hrp = ::bech32::Hrp::parse("notsui").unwrap();
1112            let encoded = ::bech32::encode::<::bech32::Bech32>(wrong_hrp, &bytes).unwrap();
1113
1114            SimpleKeypair::from_suiprivkey(&encoded).unwrap_err();
1115            Ed25519PrivateKey::from_suiprivkey(&encoded).unwrap_err();
1116        }
1117
1118        #[test]
1119        fn rejects_bech32m_checksum() {
1120            // Re-encode the upstream Ed25519 payload using the Bech32m
1121            // checksum variant. A correctly-implemented decoder must reject
1122            // this, since the suipriv format uses BIP-173 Bech32 only.
1123            let bytes = ::bech32::primitives::decode::CheckedHrpstring::new::<::bech32::Bech32>(
1124                UPSTREAM_ED25519_SUIPRIVKEY,
1125            )
1126            .unwrap()
1127            .byte_iter()
1128            .collect::<Vec<_>>();
1129            let hrp = ::bech32::Hrp::parse("suiprivkey").unwrap();
1130            let bech32m = ::bech32::encode::<::bech32::Bech32m>(hrp, &bytes).unwrap();
1131            assert_ne!(bech32m, UPSTREAM_ED25519_SUIPRIVKEY);
1132
1133            SimpleKeypair::from_suiprivkey(&bech32m).unwrap_err();
1134            Ed25519PrivateKey::from_suiprivkey(&bech32m).unwrap_err();
1135        }
1136    }
1137
1138    // Round-trip and rejection tests for the legacy Base64 keystore format.
1139    //
1140    // The payload is the same `flag || private_key` bytes as the Bech32
1141    // `suiprivkey` format, encoded as plain Base64 instead. This is the
1142    // encoding used for entries of the Sui CLI's `sui.keystore` file.
1143    mod base64 {
1144        use super::*;
1145        use sui_sdk_types::SignatureScheme;
1146
1147        #[cfg(target_arch = "wasm32")]
1148        use wasm_bindgen_test::wasm_bindgen_test as test;
1149
1150        // The same payload as the bech32 module's upstream Ed25519 vector
1151        // (`Ed25519KeyPair::generate(&mut StdRng::from_seed([0; 32]))`),
1152        // Base64-encoded: 0x00 flag byte followed by the 32 private key
1153        // bytes.
1154        const UPSTREAM_ED25519_BASE64: &str = "AJv0mmoHVflTgR/OEl8mg9UEKcO7SeB0FH4AiaUurhVf";
1155
1156        #[proptest]
1157        fn ed25519_round_trip(signer: Ed25519PrivateKey) {
1158            let encoded = signer.to_base64();
1159            let decoded = Ed25519PrivateKey::from_base64(&encoded).unwrap();
1160            assert_eq!(decoded.public_key(), signer.public_key());
1161
1162            // SimpleKeypair dispatch agrees.
1163            let keypair = SimpleKeypair::from_base64(&encoded).unwrap();
1164            assert_eq!(keypair.scheme(), signer.scheme());
1165            assert_eq!(encoded, keypair.to_base64());
1166        }
1167
1168        #[proptest]
1169        fn secp256k1_round_trip(signer: Secp256k1PrivateKey) {
1170            let encoded = signer.to_base64();
1171            let decoded = Secp256k1PrivateKey::from_base64(&encoded).unwrap();
1172            assert_eq!(decoded.public_key(), signer.public_key());
1173
1174            let keypair = SimpleKeypair::from_base64(&encoded).unwrap();
1175            assert_eq!(keypair.scheme(), signer.scheme());
1176            assert_eq!(encoded, keypair.to_base64());
1177        }
1178
1179        #[proptest]
1180        fn secp256r1_round_trip(signer: Secp256r1PrivateKey) {
1181            let encoded = signer.to_base64();
1182            let decoded = Secp256r1PrivateKey::from_base64(&encoded).unwrap();
1183            assert_eq!(decoded.public_key(), signer.public_key());
1184
1185            let keypair = SimpleKeypair::from_base64(&encoded).unwrap();
1186            assert_eq!(keypair.scheme(), signer.scheme());
1187            assert_eq!(encoded, keypair.to_base64());
1188        }
1189
1190        #[proptest]
1191        fn mldsa65_round_trip(signer: MlDsa65PrivateKey) {
1192            let encoded = signer.to_base64();
1193            let decoded = MlDsa65PrivateKey::from_base64(&encoded).unwrap();
1194            assert_eq!(decoded.public_key(), signer.public_key());
1195
1196            let keypair = SimpleKeypair::from_base64(&encoded).unwrap();
1197            assert_eq!(keypair.scheme(), signer.scheme());
1198            assert_eq!(
1199                keypair.verifying_key(),
1200                SimpleVerifiyingKey::from(signer.verifying_key())
1201            );
1202            assert_eq!(encoded, keypair.to_base64());
1203
1204            // Wrong-scheme per-scheme decoders reject it.
1205            Ed25519PrivateKey::from_base64(&encoded).unwrap_err();
1206            Secp256k1PrivateKey::from_base64(&encoded).unwrap_err();
1207            Secp256r1PrivateKey::from_base64(&encoded).unwrap_err();
1208        }
1209
1210        #[test]
1211        fn upstream_ed25519_vector_round_trips() {
1212            let keypair = SimpleKeypair::from_base64(UPSTREAM_ED25519_BASE64).unwrap();
1213            assert_eq!(keypair.scheme(), SignatureScheme::Ed25519);
1214            assert_eq!(keypair.to_base64(), UPSTREAM_ED25519_BASE64);
1215
1216            // Per-scheme decoder accepts it too.
1217            Ed25519PrivateKey::from_base64(UPSTREAM_ED25519_BASE64).unwrap();
1218            // Wrong-scheme per-scheme decoders reject it.
1219            Secp256k1PrivateKey::from_base64(UPSTREAM_ED25519_BASE64).unwrap_err();
1220            Secp256r1PrivateKey::from_base64(UPSTREAM_ED25519_BASE64).unwrap_err();
1221            MlDsa65PrivateKey::from_base64(UPSTREAM_ED25519_BASE64).unwrap_err();
1222        }
1223
1224        #[cfg(feature = "bech32")]
1225        #[test]
1226        fn agrees_with_suiprivkey_encoding() {
1227            // Both encodings carry the same payload, so converting between
1228            // them must be lossless.
1229            let keypair = SimpleKeypair::from_base64(UPSTREAM_ED25519_BASE64).unwrap();
1230            let suiprivkey = keypair.to_suiprivkey().unwrap();
1231            let round_tripped = SimpleKeypair::from_suiprivkey(&suiprivkey).unwrap();
1232            assert_eq!(round_tripped.to_base64(), UPSTREAM_ED25519_BASE64);
1233        }
1234
1235        #[test]
1236        fn rejects_invalid_input() {
1237            // Not Base64 at all.
1238            SimpleKeypair::from_base64("not base64!").unwrap_err();
1239            Ed25519PrivateKey::from_base64("not base64!").unwrap_err();
1240
1241            // Valid Base64 but an empty payload (no flag byte).
1242            SimpleKeypair::from_base64("").unwrap_err();
1243
1244            // Valid Base64 but an unknown scheme flag (0xff).
1245            use base64ct::Encoding;
1246            let payload = base64ct::Base64::decode_vec(UPSTREAM_ED25519_BASE64).unwrap();
1247            let mut bad_flag_payload = payload.clone();
1248            bad_flag_payload[0] = 0xff;
1249            let bad_flag = base64ct::Base64::encode_string(&bad_flag_payload);
1250            SimpleKeypair::from_base64(&bad_flag).unwrap_err();
1251
1252            // Valid Base64 and flag but truncated key bytes.
1253            let truncated = base64ct::Base64::encode_string(&payload[..16]);
1254            SimpleKeypair::from_base64(&truncated).unwrap_err();
1255
1256            // The ML-DSA-65 flag with a seed one byte short.
1257            let mut short_seed = vec![SignatureScheme::MlDsa65.to_u8()];
1258            short_seed.extend_from_slice(&[0; MlDsa65PrivateKey::LENGTH - 1]);
1259            let short_seed = base64ct::Base64::encode_string(&short_seed);
1260            SimpleKeypair::from_base64(&short_seed).unwrap_err();
1261            MlDsa65PrivateKey::from_base64(&short_seed).unwrap_err();
1262        }
1263
1264        #[test]
1265        fn rejects_invalid_ecdsa_scalar() {
1266            // Zero is not a valid secp256k1 or secp256r1 private key. The
1267            // payload is untrusted input, so decoding must return an error
1268            // rather than panic.
1269            use base64ct::Encoding;
1270            for scheme in [SignatureScheme::Secp256k1, SignatureScheme::Secp256r1] {
1271                let mut payload = vec![scheme.to_u8()];
1272                payload.extend_from_slice(&[0; 32]);
1273                let encoded = base64ct::Base64::encode_string(&payload);
1274
1275                SimpleKeypair::from_base64(&encoded).unwrap_err();
1276            }
1277        }
1278    }
1279}