sui_crypto/
secp256k1.rs

1use crate::SignatureError;
2use k256::ecdsa::SigningKey;
3use k256::ecdsa::VerifyingKey;
4use k256::elliptic_curve::group::GroupEncoding;
5use signature::Signer;
6use signature::Verifier;
7use sui_sdk_types::Secp256k1PublicKey;
8use sui_sdk_types::Secp256k1Signature;
9use sui_sdk_types::SignatureScheme;
10use sui_sdk_types::SimpleSignature;
11use sui_sdk_types::UserSignature;
12
13#[derive(Clone)]
14pub struct Secp256k1PrivateKey(SigningKey);
15
16impl std::fmt::Debug for Secp256k1PrivateKey {
17    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
18        f.debug_tuple("Secp256k1PrivateKey")
19            .field(&"__elided__")
20            .finish()
21    }
22}
23
24#[cfg(test)]
25impl proptest::arbitrary::Arbitrary for Secp256k1PrivateKey {
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_filter_map("invalid secp256k1 private key", |bytes| {
33                Self::new(bytes).ok()
34            })
35            .boxed()
36    }
37}
38
39impl Secp256k1PrivateKey {
40    /// The length of an secp256k1 private key in bytes.
41    pub const LENGTH: usize = 32;
42
43    pub fn new(bytes: [u8; Self::LENGTH]) -> Result<Self, SignatureError> {
44        SigningKey::from_bytes(&bytes.into()).map(Self)
45    }
46
47    pub fn scheme(&self) -> SignatureScheme {
48        SignatureScheme::Secp256k1
49    }
50
51    pub fn verifying_key(&self) -> Secp256k1VerifyingKey {
52        let verifying_key = self.0.verifying_key();
53        Secp256k1VerifyingKey(*verifying_key)
54    }
55
56    pub fn public_key(&self) -> Secp256k1PublicKey {
57        Secp256k1PublicKey::new(self.0.verifying_key().as_ref().to_bytes().into())
58    }
59
60    pub fn generate<R>(mut rng: R) -> Self
61    where
62        R: rand_core::RngCore + rand_core::CryptoRng,
63    {
64        Self(SigningKey::random(&mut rng))
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        k256::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 k256::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        k256::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_k256(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    /// Secp256k1, has the wrong number of key bytes, or carries bytes that do
121    /// not form a valid secp256k1 scalar.
122    pub fn from_suiprivkey(s: &str) -> Result<Self, SignatureError> {
123        let (scheme, key) = crate::suipriv::decode(s)?;
124        if scheme != SignatureScheme::Secp256k1 {
125            return Err(SignatureError::from_source(format!(
126                "suipriv scheme flag is `{}`, expected `secp256k1`",
127                scheme.name(),
128            )));
129        }
130        let bytes: [u8; Self::LENGTH] = key.try_into().map_err(|_: Vec<u8>| {
131            SignatureError::from_source("suipriv key has invalid length for secp256k1")
132        })?;
133        Self::new(bytes)
134    }
135
136    #[cfg(feature = "bech32")]
137    #[cfg_attr(doc_cfg, doc(cfg(feature = "bech32")))]
138    /// Encode this private key as a Bech32 `suiprivkey` string.
139    pub fn to_suiprivkey(&self) -> Result<String, SignatureError> {
140        let bytes = self.0.to_bytes();
141        crate::suipriv::encode(SignatureScheme::Secp256k1, &bytes)
142    }
143}
144
145impl Signer<Secp256k1Signature> for Secp256k1PrivateKey {
146    fn try_sign(&self, message: &[u8]) -> Result<Secp256k1Signature, SignatureError> {
147        let signature: k256::ecdsa::Signature = self.0.try_sign(message)?;
148        Ok(Secp256k1Signature::new(signature.to_bytes().into()))
149    }
150}
151
152impl Signer<SimpleSignature> for Secp256k1PrivateKey {
153    fn try_sign(&self, msg: &[u8]) -> Result<SimpleSignature, SignatureError> {
154        <Self as Signer<Secp256k1Signature>>::try_sign(self, msg).map(|signature| {
155            SimpleSignature::Secp256k1 {
156                signature,
157                public_key: self.public_key(),
158            }
159        })
160    }
161}
162
163impl Signer<UserSignature> for Secp256k1PrivateKey {
164    fn try_sign(&self, msg: &[u8]) -> Result<UserSignature, SignatureError> {
165        <Self as Signer<SimpleSignature>>::try_sign(self, msg).map(UserSignature::Simple)
166    }
167}
168
169#[derive(Debug, Clone, Eq, PartialEq)]
170pub struct Secp256k1VerifyingKey(VerifyingKey);
171
172impl Secp256k1VerifyingKey {
173    pub fn new(public_key: &Secp256k1PublicKey) -> Result<Self, SignatureError> {
174        VerifyingKey::try_from(public_key.inner().as_ref()).map(Self)
175    }
176
177    pub fn public_key(&self) -> Secp256k1PublicKey {
178        Secp256k1PublicKey::new(self.0.as_ref().to_bytes().into())
179    }
180
181    #[cfg(feature = "pem")]
182    #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))]
183    /// Deserialize public key from ASN.1 DER-encoded data (binary format).
184    pub fn from_der(bytes: &[u8]) -> Result<Self, SignatureError> {
185        k256::pkcs8::DecodePublicKey::from_public_key_der(bytes)
186            .map(Self)
187            .map_err(SignatureError::from_source)
188    }
189
190    #[cfg(feature = "pem")]
191    #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))]
192    /// Serialize this public key as DER-encoded data
193    pub fn to_der(&self) -> Result<Vec<u8>, SignatureError> {
194        use pkcs8::EncodePublicKey;
195
196        self.0
197            .to_public_key_der()
198            .map_err(SignatureError::from_source)
199            .map(|der| der.into_vec())
200    }
201
202    #[cfg(feature = "pem")]
203    #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))]
204    /// Deserialize public key from PEM.
205    pub fn from_pem(s: &str) -> Result<Self, SignatureError> {
206        k256::pkcs8::DecodePublicKey::from_public_key_pem(s)
207            .map(Self)
208            .map_err(SignatureError::from_source)
209    }
210
211    #[cfg(feature = "pem")]
212    #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))]
213    /// Serialize this public key into PEM
214    pub fn to_pem(&self) -> Result<String, SignatureError> {
215        use pkcs8::EncodePublicKey;
216
217        self.0
218            .to_public_key_pem(pkcs8::LineEnding::default())
219            .map_err(SignatureError::from_source)
220    }
221
222    #[cfg(feature = "pem")]
223    pub(crate) fn from_k256(verifying_key: VerifyingKey) -> Self {
224        Self(verifying_key)
225    }
226}
227
228impl Verifier<Secp256k1Signature> for Secp256k1VerifyingKey {
229    fn verify(&self, message: &[u8], signature: &Secp256k1Signature) -> Result<(), SignatureError> {
230        let signature = k256::ecdsa::Signature::from_bytes(signature.inner().into())?;
231        self.0.verify(message, &signature)
232    }
233}
234
235impl Verifier<SimpleSignature> for Secp256k1VerifyingKey {
236    fn verify(&self, message: &[u8], signature: &SimpleSignature) -> Result<(), SignatureError> {
237        let SimpleSignature::Secp256k1 {
238            signature,
239            public_key,
240        } = signature
241        else {
242            return Err(SignatureError::from_source("not a secp256k1 signature"));
243        };
244
245        if public_key.inner() != self.public_key().inner() {
246            return Err(SignatureError::from_source(
247                "public_key in signature does not match",
248            ));
249        }
250
251        <Self as Verifier<Secp256k1Signature>>::verify(self, message, signature)
252    }
253}
254
255impl Verifier<UserSignature> for Secp256k1VerifyingKey {
256    fn verify(&self, message: &[u8], signature: &UserSignature) -> Result<(), SignatureError> {
257        let UserSignature::Simple(signature) = signature else {
258            return Err(SignatureError::from_source("not a secp256k1 signature"));
259        };
260
261        <Self as Verifier<SimpleSignature>>::verify(self, message, signature)
262    }
263}
264
265#[derive(Default, Clone, Debug)]
266pub struct Secp256k1Verifier {}
267
268impl Secp256k1Verifier {
269    pub fn new() -> Self {
270        Self {}
271    }
272}
273
274impl Verifier<SimpleSignature> for Secp256k1Verifier {
275    fn verify(&self, message: &[u8], signature: &SimpleSignature) -> Result<(), SignatureError> {
276        let SimpleSignature::Secp256k1 {
277            signature,
278            public_key,
279        } = signature
280        else {
281            return Err(SignatureError::from_source("not a secp256k1 signature"));
282        };
283
284        let verifying_key = Secp256k1VerifyingKey::new(public_key)?;
285
286        verifying_key.verify(message, signature)
287    }
288}
289
290impl Verifier<UserSignature> for Secp256k1Verifier {
291    fn verify(&self, message: &[u8], signature: &UserSignature) -> Result<(), SignatureError> {
292        let UserSignature::Simple(signature) = signature else {
293            return Err(SignatureError::from_source("not a secp256k1 signature"));
294        };
295
296        <Self as Verifier<SimpleSignature>>::verify(self, message, signature)
297    }
298}
299
300#[cfg(test)]
301mod test {
302    use super::*;
303    use crate::SuiSigner;
304    use crate::SuiVerifier;
305    use sui_sdk_types::PersonalMessage;
306    use test_strategy::proptest;
307
308    #[cfg(target_arch = "wasm32")]
309    use wasm_bindgen_test::wasm_bindgen_test as test;
310
311    // TODO need to export proptest impl from core crate
312    // #[proptest]
313    // fn transaction_signing(signer: Secp256k1PrivateKey, transaction: Transaction) {
314    //     let signature = signer.sign_transaction(&transaction).unwrap();
315    //     let verifier = signer.public_key();
316    //     verifier
317    //         .verify_transaction(&transaction, &signature)
318    //         .unwrap();
319    // }
320
321    #[proptest]
322    fn personal_message_signing(signer: Secp256k1PrivateKey, message: Vec<u8>) {
323        let message = PersonalMessage(message.into());
324        let signature = signer.sign_personal_message(&message).unwrap();
325        let verifying_key = signer.verifying_key();
326        verifying_key
327            .verify_personal_message(&message, &signature)
328            .unwrap();
329
330        let verifier = Secp256k1Verifier::default();
331        verifier
332            .verify_personal_message(&message, &signature)
333            .unwrap();
334    }
335
336    #[test]
337    fn personal_message_signing_fixture() {
338        let key = [
339            172, 12, 96, 180, 207, 143, 111, 151, 81, 57, 242, 89, 74, 5, 150, 51, 56, 111, 245,
340            150, 182, 30, 149, 178, 29, 255, 188, 27, 48, 241, 151, 193,
341        ];
342
343        let signer = Secp256k1PrivateKey::new(key).unwrap();
344
345        let message = PersonalMessage(b"hello".into());
346        let sig = signer.sign_personal_message(&message).unwrap();
347        let external_sig = "AVFAWGjuD8+xUoc6jMC0lKqMtT+4ukln7vz+8Nuv+EbYKl47jwzOWn39maDsqu81kezLPgLzz6o/AfSE0M9+jVwClcrtiuyUggEt/6CEZi8+JQ+NS9TmOhPBZV2X1KjhGCw=";
348        let b64 = sig.to_base64();
349        assert_eq!(external_sig, b64);
350    }
351}