sui_crypto/
secp256r1.rs

1use crate::SignatureError;
2use p256::ecdsa::SigningKey;
3use p256::ecdsa::VerifyingKey;
4use p256::elliptic_curve::group::GroupEncoding;
5use signature::Signer;
6use signature::Verifier;
7use sui_sdk_types::Secp256r1PublicKey;
8use sui_sdk_types::Secp256r1Signature;
9use sui_sdk_types::SignatureScheme;
10use sui_sdk_types::SimpleSignature;
11use sui_sdk_types::UserSignature;
12
13#[derive(Clone)]
14pub struct Secp256r1PrivateKey(SigningKey);
15
16impl std::fmt::Debug for Secp256r1PrivateKey {
17    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
18        f.debug_tuple("Secp256r1PrivateKey")
19            .field(&"__elided__")
20            .finish()
21    }
22}
23
24#[cfg(test)]
25impl proptest::arbitrary::Arbitrary for Secp256r1PrivateKey {
26    type Parameters = ();
27    type Strategy = proptest::strategy::BoxedStrategy<Self>;
28    fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
29        use proptest::strategy::Strategy;
30
31        proptest::arbitrary::any::<[u8; Self::LENGTH]>()
32            .prop_map(Self::new)
33            .boxed()
34    }
35}
36
37impl Secp256r1PrivateKey {
38    /// The length of an secp256r1 private key in bytes.
39    pub const LENGTH: usize = 32;
40
41    pub fn new(bytes: [u8; Self::LENGTH]) -> Self {
42        Self(SigningKey::from_bytes(&bytes.into()).unwrap())
43    }
44
45    pub fn scheme(&self) -> SignatureScheme {
46        SignatureScheme::Secp256r1
47    }
48
49    pub fn verifying_key(&self) -> Secp256r1VerifyingKey {
50        let verifying_key = self.0.verifying_key();
51        Secp256r1VerifyingKey(*verifying_key)
52    }
53
54    pub fn public_key(&self) -> Secp256r1PublicKey {
55        Secp256r1PublicKey::new(self.0.verifying_key().as_ref().to_bytes().into())
56    }
57
58    pub fn generate<R>(mut rng: R) -> Self
59    where
60        R: rand_core::RngCore + rand_core::CryptoRng,
61    {
62        let mut buf: [u8; Self::LENGTH] = [0; Self::LENGTH];
63        rng.fill_bytes(&mut buf);
64        Self::new(buf)
65    }
66
67    #[cfg(feature = "pem")]
68    #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))]
69    /// Deserialize PKCS#8 private key from ASN.1 DER-encoded data (binary format).
70    pub fn from_der(bytes: &[u8]) -> Result<Self, SignatureError> {
71        p256::pkcs8::DecodePrivateKey::from_pkcs8_der(bytes)
72            .map(Self)
73            .map_err(SignatureError::from_source)
74    }
75
76    #[cfg(feature = "pem")]
77    #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))]
78    /// Serialize this private key as DER-encoded PKCS#8
79    pub fn to_der(&self) -> Result<Vec<u8>, SignatureError> {
80        use p256::pkcs8::EncodePrivateKey;
81
82        self.0
83            .to_pkcs8_der()
84            .map_err(SignatureError::from_source)
85            .map(|der| der.as_bytes().to_owned())
86    }
87
88    #[cfg(feature = "pem")]
89    #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))]
90    /// Deserialize PKCS#8-encoded private key from PEM.
91    pub fn from_pem(s: &str) -> Result<Self, SignatureError> {
92        p256::pkcs8::DecodePrivateKey::from_pkcs8_pem(s)
93            .map(Self)
94            .map_err(SignatureError::from_source)
95    }
96
97    #[cfg(feature = "pem")]
98    #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))]
99    /// Serialize this private key as PEM-encoded PKCS#8
100    pub fn to_pem(&self) -> Result<String, SignatureError> {
101        use pkcs8::EncodePrivateKey;
102
103        self.0
104            .to_pkcs8_pem(pkcs8::LineEnding::default())
105            .map_err(SignatureError::from_source)
106            .map(|pem| (*pem).to_owned())
107    }
108
109    #[cfg(feature = "pem")]
110    pub(crate) fn from_p256(private_key: SigningKey) -> Self {
111        Self(private_key)
112    }
113
114    #[cfg(feature = "bech32")]
115    #[cfg_attr(doc_cfg, doc(cfg(feature = "bech32")))]
116    /// Decode a Bech32 `suiprivkey` string produced by the Sui CLI.
117    ///
118    /// Returns an error if the string does not have the `suiprivkey` HRP, has
119    /// an invalid Bech32 (BIP-173) checksum, has a flag byte that is not
120    /// Secp256r1, or has the wrong number of key bytes.
121    pub fn from_suiprivkey(s: &str) -> Result<Self, SignatureError> {
122        let (scheme, key) = crate::suipriv::decode(s)?;
123        if scheme != SignatureScheme::Secp256r1 {
124            return Err(SignatureError::from_source(format!(
125                "suipriv scheme flag is `{}`, expected `secp256r1`",
126                scheme.name(),
127            )));
128        }
129        let bytes: [u8; Self::LENGTH] = key.try_into().map_err(|_: Vec<u8>| {
130            SignatureError::from_source("suipriv key has invalid length for secp256r1")
131        })?;
132        Ok(Self::new(bytes))
133    }
134
135    #[cfg(feature = "bech32")]
136    #[cfg_attr(doc_cfg, doc(cfg(feature = "bech32")))]
137    /// Encode this private key as a Bech32 `suiprivkey` string.
138    pub fn to_suiprivkey(&self) -> Result<String, SignatureError> {
139        let bytes = self.0.to_bytes();
140        crate::suipriv::encode(SignatureScheme::Secp256r1, &bytes)
141    }
142}
143
144impl Signer<Secp256r1Signature> for Secp256r1PrivateKey {
145    fn try_sign(&self, message: &[u8]) -> Result<Secp256r1Signature, SignatureError> {
146        let signature: p256::ecdsa::Signature = self.0.try_sign(message)?;
147        Ok(Secp256r1Signature::new(signature.to_bytes().into()))
148    }
149}
150
151impl Signer<SimpleSignature> for Secp256r1PrivateKey {
152    fn try_sign(&self, msg: &[u8]) -> Result<SimpleSignature, SignatureError> {
153        <Self as Signer<Secp256r1Signature>>::try_sign(self, msg).map(|signature| {
154            SimpleSignature::Secp256r1 {
155                signature,
156                public_key: self.public_key(),
157            }
158        })
159    }
160}
161
162impl Signer<UserSignature> for Secp256r1PrivateKey {
163    fn try_sign(&self, msg: &[u8]) -> Result<UserSignature, SignatureError> {
164        <Self as Signer<SimpleSignature>>::try_sign(self, msg).map(UserSignature::Simple)
165    }
166}
167
168#[derive(Debug, Clone, Eq, PartialEq)]
169pub struct Secp256r1VerifyingKey(VerifyingKey);
170
171impl Secp256r1VerifyingKey {
172    pub fn new(public_key: &Secp256r1PublicKey) -> Result<Self, SignatureError> {
173        VerifyingKey::try_from(public_key.inner().as_ref()).map(Self)
174    }
175
176    pub fn public_key(&self) -> Secp256r1PublicKey {
177        Secp256r1PublicKey::new(self.0.as_ref().to_bytes().into())
178    }
179
180    #[cfg(feature = "pem")]
181    #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))]
182    /// Deserialize public key from ASN.1 DER-encoded data (binary format).
183    pub fn from_der(bytes: &[u8]) -> Result<Self, SignatureError> {
184        p256::pkcs8::DecodePublicKey::from_public_key_der(bytes)
185            .map(Self)
186            .map_err(SignatureError::from_source)
187    }
188
189    #[cfg(feature = "pem")]
190    #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))]
191    /// Serialize this public key as DER-encoded data
192    pub fn to_der(&self) -> Result<Vec<u8>, SignatureError> {
193        use pkcs8::EncodePublicKey;
194
195        self.0
196            .to_public_key_der()
197            .map_err(SignatureError::from_source)
198            .map(|der| der.into_vec())
199    }
200
201    #[cfg(feature = "pem")]
202    #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))]
203    /// Deserialize public key from PEM.
204    pub fn from_pem(s: &str) -> Result<Self, SignatureError> {
205        p256::pkcs8::DecodePublicKey::from_public_key_pem(s)
206            .map(Self)
207            .map_err(SignatureError::from_source)
208    }
209
210    #[cfg(feature = "pem")]
211    #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))]
212    /// Serialize this public key into PEM
213    pub fn to_pem(&self) -> Result<String, SignatureError> {
214        use pkcs8::EncodePublicKey;
215
216        self.0
217            .to_public_key_pem(pkcs8::LineEnding::default())
218            .map_err(SignatureError::from_source)
219    }
220
221    #[cfg(feature = "pem")]
222    pub(crate) fn from_p256(verifying_key: VerifyingKey) -> Self {
223        Self(verifying_key)
224    }
225}
226
227impl Verifier<Secp256r1Signature> for Secp256r1VerifyingKey {
228    fn verify(&self, message: &[u8], signature: &Secp256r1Signature) -> Result<(), SignatureError> {
229        let signature = p256::ecdsa::Signature::from_bytes(signature.inner().into())?;
230        self.0.verify(message, &signature)
231    }
232}
233
234impl Verifier<SimpleSignature> for Secp256r1VerifyingKey {
235    fn verify(&self, message: &[u8], signature: &SimpleSignature) -> Result<(), SignatureError> {
236        let SimpleSignature::Secp256r1 {
237            signature,
238            public_key,
239        } = signature
240        else {
241            return Err(SignatureError::from_source("not a secp256r1 signature"));
242        };
243
244        if public_key.inner() != self.public_key().inner() {
245            return Err(SignatureError::from_source(
246                "public_key in signature does not match",
247            ));
248        }
249
250        <Self as Verifier<Secp256r1Signature>>::verify(self, message, signature)
251    }
252}
253
254impl Verifier<UserSignature> for Secp256r1VerifyingKey {
255    fn verify(&self, message: &[u8], signature: &UserSignature) -> Result<(), SignatureError> {
256        let UserSignature::Simple(signature) = signature else {
257            return Err(SignatureError::from_source("not a secp256r1 signature"));
258        };
259
260        <Self as Verifier<SimpleSignature>>::verify(self, message, signature)
261    }
262}
263
264#[derive(Default, Clone, Debug)]
265pub struct Secp256r1Verifier {}
266
267impl Secp256r1Verifier {
268    pub fn new() -> Self {
269        Self {}
270    }
271}
272
273impl Verifier<SimpleSignature> for Secp256r1Verifier {
274    fn verify(&self, message: &[u8], signature: &SimpleSignature) -> Result<(), SignatureError> {
275        let SimpleSignature::Secp256r1 {
276            signature,
277            public_key,
278        } = signature
279        else {
280            return Err(SignatureError::from_source("not a secp256r1 signature"));
281        };
282
283        let verifying_key = Secp256r1VerifyingKey::new(public_key)?;
284
285        verifying_key.verify(message, signature)
286    }
287}
288
289impl Verifier<UserSignature> for Secp256r1Verifier {
290    fn verify(&self, message: &[u8], signature: &UserSignature) -> Result<(), SignatureError> {
291        let UserSignature::Simple(signature) = signature else {
292            return Err(SignatureError::from_source("not a secp256r1 signature"));
293        };
294
295        <Self as Verifier<SimpleSignature>>::verify(self, message, signature)
296    }
297}
298
299#[cfg(test)]
300mod test {
301    use super::*;
302    use crate::SuiSigner;
303    use crate::SuiVerifier;
304    use sui_sdk_types::PersonalMessage;
305    use test_strategy::proptest;
306
307    #[cfg(target_arch = "wasm32")]
308    use wasm_bindgen_test::wasm_bindgen_test as test;
309
310    // TODO need to export proptest impl from core crate
311    // #[proptest]
312    // fn transaction_signing(signer: Secp256r1PrivateKey, transaction: Transaction) {
313    //     let signature = signer.sign_transaction(&transaction).unwrap();
314    //     let verifier = signer.public_key();
315    //     verifier
316    //         .verify_transaction(&transaction, &signature)
317    //         .unwrap();
318    // }
319
320    #[proptest]
321    fn personal_message_signing(signer: Secp256r1PrivateKey, message: Vec<u8>) {
322        let message = PersonalMessage(message.into());
323        let signature = signer.sign_personal_message(&message).unwrap();
324        let verifying_key = signer.verifying_key();
325        verifying_key
326            .verify_personal_message(&message, &signature)
327            .unwrap();
328
329        let verifier = Secp256r1Verifier::default();
330        verifier
331            .verify_personal_message(&message, &signature)
332            .unwrap();
333    }
334
335    #[test]
336    fn personal_message_signing_fixture() {
337        let key = [
338            167, 44, 116, 0, 51, 221, 254, 179, 210, 44, 93, 196, 125, 155, 85, 94, 29, 41, 13, 60,
339            59, 132, 69, 84, 176, 217, 77, 49, 25, 113, 118, 125,
340        ];
341        let signer = Secp256r1PrivateKey::new(key);
342
343        let message = PersonalMessage(b"hello".into());
344        let sig = signer.sign_personal_message(&message).unwrap();
345        let external_sig = "AlqWPdkIE2bZAUquKv2Tdh9i+Ih+rVSQXH/YsgvwkmeOJR0YLjL/kadivoPtiQkvZBQ1ZI8eDZxe8SaLniwoT88Dh+/vAuGf1UrouFTdefpBEWn3apy8x3EexN5c5ESzGDc=";
346        let b64 = sig.to_base64();
347        assert_eq!(external_sig, b64);
348    }
349}