Skip to main content

sui_crypto/
ed25519.rs

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