Skip to main content

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    /// Build a key from the scheme flag and key bytes of a decoded
115    /// `flag || private_key` payload.
116    fn from_flagged_key_bytes(
117        scheme: SignatureScheme,
118        key: Vec<u8>,
119    ) -> Result<Self, SignatureError> {
120        if scheme != SignatureScheme::Secp256k1 {
121            return Err(SignatureError::from_source(format!(
122                "private key scheme flag is `{}`, expected `secp256k1`",
123                scheme.name(),
124            )));
125        }
126        let bytes: [u8; Self::LENGTH] = key.try_into().map_err(|_: Vec<u8>| {
127            SignatureError::from_source("private key has invalid length for secp256k1")
128        })?;
129        Self::new(bytes)
130    }
131
132    #[cfg(feature = "bech32")]
133    #[cfg_attr(doc_cfg, doc(cfg(feature = "bech32")))]
134    /// Decode a Bech32 `suiprivkey` string produced by the Sui CLI.
135    ///
136    /// Returns an error if the string does not have the `suiprivkey` HRP, has
137    /// an invalid Bech32 (BIP-173) checksum, has a flag byte that is not
138    /// Secp256k1, has the wrong number of key bytes, or carries bytes that do
139    /// not form a valid secp256k1 scalar.
140    pub fn from_suiprivkey(s: &str) -> Result<Self, SignatureError> {
141        let (scheme, key) = crate::suipriv::decode(s)?;
142        Self::from_flagged_key_bytes(scheme, key)
143    }
144
145    #[cfg(feature = "bech32")]
146    #[cfg_attr(doc_cfg, doc(cfg(feature = "bech32")))]
147    /// Encode this private key as a Bech32 `suiprivkey` string.
148    pub fn to_suiprivkey(&self) -> Result<String, SignatureError> {
149        let bytes = self.0.to_bytes();
150        crate::suipriv::encode(SignatureScheme::Secp256k1, &bytes)
151    }
152
153    /// Decode a Base64 `flag || private_key` string, the legacy keystore
154    /// format used for entries of the Sui CLI's `sui.keystore` file.
155    ///
156    /// Returns an error if the string is not valid Base64, has a flag byte
157    /// that is not Secp256k1, has the wrong number of key bytes, or carries
158    /// bytes that do not form a valid secp256k1 scalar.
159    pub fn from_base64(s: &str) -> Result<Self, SignatureError> {
160        let (scheme, key) = crate::suipriv::decode_base64(s)?;
161        Self::from_flagged_key_bytes(scheme, key)
162    }
163
164    /// Encode this private key as a Base64 `flag || private_key` string, the
165    /// legacy keystore format used for entries of the Sui CLI's
166    /// `sui.keystore` file.
167    pub fn to_base64(&self) -> String {
168        let bytes = self.0.to_bytes();
169        crate::suipriv::encode_base64(SignatureScheme::Secp256k1, &bytes)
170    }
171}
172
173impl Signer<Secp256k1Signature> for Secp256k1PrivateKey {
174    fn try_sign(&self, message: &[u8]) -> Result<Secp256k1Signature, SignatureError> {
175        let signature: k256::ecdsa::Signature = self.0.try_sign(message)?;
176        Ok(Secp256k1Signature::new(signature.to_bytes().into()))
177    }
178}
179
180impl Signer<SimpleSignature> for Secp256k1PrivateKey {
181    fn try_sign(&self, msg: &[u8]) -> Result<SimpleSignature, SignatureError> {
182        <Self as Signer<Secp256k1Signature>>::try_sign(self, msg).map(|signature| {
183            SimpleSignature::Secp256k1 {
184                signature,
185                public_key: self.public_key(),
186            }
187        })
188    }
189}
190
191impl Signer<UserSignature> for Secp256k1PrivateKey {
192    fn try_sign(&self, msg: &[u8]) -> Result<UserSignature, SignatureError> {
193        <Self as Signer<SimpleSignature>>::try_sign(self, msg).map(UserSignature::Simple)
194    }
195}
196
197#[derive(Debug, Clone, Eq, PartialEq)]
198pub struct Secp256k1VerifyingKey(VerifyingKey);
199
200impl Secp256k1VerifyingKey {
201    pub fn new(public_key: &Secp256k1PublicKey) -> Result<Self, SignatureError> {
202        VerifyingKey::try_from(public_key.inner().as_ref()).map(Self)
203    }
204
205    pub fn public_key(&self) -> Secp256k1PublicKey {
206        Secp256k1PublicKey::new(self.0.as_ref().to_bytes().into())
207    }
208
209    #[cfg(feature = "pem")]
210    #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))]
211    /// Deserialize public key from ASN.1 DER-encoded data (binary format).
212    pub fn from_der(bytes: &[u8]) -> Result<Self, SignatureError> {
213        k256::pkcs8::DecodePublicKey::from_public_key_der(bytes)
214            .map(Self)
215            .map_err(SignatureError::from_source)
216    }
217
218    #[cfg(feature = "pem")]
219    #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))]
220    /// Serialize this public key as DER-encoded data
221    pub fn to_der(&self) -> Result<Vec<u8>, SignatureError> {
222        use pkcs8::EncodePublicKey;
223
224        self.0
225            .to_public_key_der()
226            .map_err(SignatureError::from_source)
227            .map(|der| der.into_vec())
228    }
229
230    #[cfg(feature = "pem")]
231    #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))]
232    /// Deserialize public key from PEM.
233    pub fn from_pem(s: &str) -> Result<Self, SignatureError> {
234        k256::pkcs8::DecodePublicKey::from_public_key_pem(s)
235            .map(Self)
236            .map_err(SignatureError::from_source)
237    }
238
239    #[cfg(feature = "pem")]
240    #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))]
241    /// Serialize this public key into PEM
242    pub fn to_pem(&self) -> Result<String, SignatureError> {
243        use pkcs8::EncodePublicKey;
244
245        self.0
246            .to_public_key_pem(pkcs8::LineEnding::default())
247            .map_err(SignatureError::from_source)
248    }
249
250    #[cfg(feature = "pem")]
251    pub(crate) fn from_k256(verifying_key: VerifyingKey) -> Self {
252        Self(verifying_key)
253    }
254}
255
256impl Verifier<Secp256k1Signature> for Secp256k1VerifyingKey {
257    fn verify(&self, message: &[u8], signature: &Secp256k1Signature) -> Result<(), SignatureError> {
258        let signature = k256::ecdsa::Signature::from_bytes(signature.inner().into())?;
259        self.0.verify(message, &signature)
260    }
261}
262
263impl Verifier<SimpleSignature> for Secp256k1VerifyingKey {
264    fn verify(&self, message: &[u8], signature: &SimpleSignature) -> Result<(), SignatureError> {
265        let SimpleSignature::Secp256k1 {
266            signature,
267            public_key,
268        } = signature
269        else {
270            return Err(SignatureError::from_source("not a secp256k1 signature"));
271        };
272
273        if public_key.inner() != self.public_key().inner() {
274            return Err(SignatureError::from_source(
275                "public_key in signature does not match",
276            ));
277        }
278
279        <Self as Verifier<Secp256k1Signature>>::verify(self, message, signature)
280    }
281}
282
283impl Verifier<UserSignature> for Secp256k1VerifyingKey {
284    fn verify(&self, message: &[u8], signature: &UserSignature) -> Result<(), SignatureError> {
285        let UserSignature::Simple(signature) = signature else {
286            return Err(SignatureError::from_source("not a secp256k1 signature"));
287        };
288
289        <Self as Verifier<SimpleSignature>>::verify(self, message, signature)
290    }
291}
292
293#[derive(Default, Clone, Debug)]
294pub struct Secp256k1Verifier {}
295
296impl Secp256k1Verifier {
297    pub fn new() -> Self {
298        Self {}
299    }
300}
301
302impl Verifier<SimpleSignature> for Secp256k1Verifier {
303    fn verify(&self, message: &[u8], signature: &SimpleSignature) -> Result<(), SignatureError> {
304        let SimpleSignature::Secp256k1 {
305            signature,
306            public_key,
307        } = signature
308        else {
309            return Err(SignatureError::from_source("not a secp256k1 signature"));
310        };
311
312        let verifying_key = Secp256k1VerifyingKey::new(public_key)?;
313
314        verifying_key.verify(message, signature)
315    }
316}
317
318impl Verifier<UserSignature> for Secp256k1Verifier {
319    fn verify(&self, message: &[u8], signature: &UserSignature) -> Result<(), SignatureError> {
320        let UserSignature::Simple(signature) = signature else {
321            return Err(SignatureError::from_source("not a secp256k1 signature"));
322        };
323
324        <Self as Verifier<SimpleSignature>>::verify(self, message, signature)
325    }
326}
327
328#[cfg(test)]
329mod test {
330    use super::*;
331    use crate::SuiSigner;
332    use crate::SuiVerifier;
333    use sui_sdk_types::PersonalMessage;
334    use test_strategy::proptest;
335
336    #[cfg(target_arch = "wasm32")]
337    use wasm_bindgen_test::wasm_bindgen_test as test;
338
339    // TODO need to export proptest impl from core crate
340    // #[proptest]
341    // fn transaction_signing(signer: Secp256k1PrivateKey, transaction: Transaction) {
342    //     let signature = signer.sign_transaction(&transaction).unwrap();
343    //     let verifier = signer.public_key();
344    //     verifier
345    //         .verify_transaction(&transaction, &signature)
346    //         .unwrap();
347    // }
348
349    #[proptest]
350    fn personal_message_signing(signer: Secp256k1PrivateKey, message: Vec<u8>) {
351        let message = PersonalMessage(message.into());
352        let signature = signer.sign_personal_message(&message).unwrap();
353        let verifying_key = signer.verifying_key();
354        verifying_key
355            .verify_personal_message(&message, &signature)
356            .unwrap();
357
358        let verifier = Secp256k1Verifier::default();
359        verifier
360            .verify_personal_message(&message, &signature)
361            .unwrap();
362    }
363
364    #[test]
365    fn personal_message_signing_fixture() {
366        let key = [
367            172, 12, 96, 180, 207, 143, 111, 151, 81, 57, 242, 89, 74, 5, 150, 51, 56, 111, 245,
368            150, 182, 30, 149, 178, 29, 255, 188, 27, 48, 241, 151, 193,
369        ];
370
371        let signer = Secp256k1PrivateKey::new(key).unwrap();
372
373        let message = PersonalMessage(b"hello".into());
374        let sig = signer.sign_personal_message(&message).unwrap();
375        let external_sig = "AVFAWGjuD8+xUoc6jMC0lKqMtT+4ukln7vz+8Nuv+EbYKl47jwzOWn39maDsqu81kezLPgLzz6o/AfSE0M9+jVwClcrtiuyUggEt/6CEZi8+JQ+NS9TmOhPBZV2X1KjhGCw=";
376        let b64 = sig.to_base64();
377        assert_eq!(external_sig, b64);
378    }
379}