did_utils/crypto/
ed25519.rs

1use curve25519_dalek::edwards::CompressedEdwardsY;
2use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey};
3use multibase::Base::Base58Btc;
4use sha2::{Digest, Sha512};
5
6use super::{
7    alg::Algorithm,
8    errors::Error,
9    traits::{CoreSign, Generate, KeyMaterial, ToMultikey, BYTES_LENGTH_32},
10    utils::{clone_slice_to_array, generate_seed},
11    x25519::X25519KeyPair,
12    AsymmetricKey,
13};
14
15pub type Ed25519KeyPair = AsymmetricKey<VerifyingKey, SigningKey>;
16
17impl std::fmt::Debug for Ed25519KeyPair {
18    /// Returns a string representation of the public key.
19    ///
20    /// This function is used to implement the `fmt::Debug` trait.
21    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22        f.write_fmt(format_args!("{:?}", self.public_key))
23    }
24}
25
26impl KeyMaterial for Ed25519KeyPair {
27    /// Returns the bytes of the public key.
28    ///
29    /// # Returns
30    ///
31    /// A `Result` containing the bytes of the public key or an `Error`.
32    fn public_key_bytes(&self) -> Result<[u8; BYTES_LENGTH_32], Error> {
33        Ok(clone_slice_to_array(self.public_key.as_bytes()))
34    }
35
36    /// Returns the bytes of the private key.
37    ///
38    /// # Returns
39    ///
40    /// A `Result` containing the bytes of the private key or an `Error`.
41    fn private_key_bytes(&self) -> Result<[u8; BYTES_LENGTH_32], Error> {
42        match &self.secret_key {
43            Some(sk) => Ok(clone_slice_to_array(&sk.to_bytes())),
44            None => Err(Error::InvalidSecretKey),
45        }
46    }
47}
48
49impl Generate for Ed25519KeyPair {
50    /// Generates a new Ed25519 key pair.
51    ///
52    /// If the initial seed is empty or invalid, a random seed will be generated.
53    ///
54    /// # Returns
55    ///
56    /// A new `Ed25519KeyPair` instance or an `Error`.
57    fn new() -> Result<Ed25519KeyPair, Error> {
58        Self::new_with_seed(vec![].as_slice())
59    }
60
61    /// Generates a new Ed25519 key pair with a seed.
62    ///
63    /// If the seed is empty or invalid, generates a new seed.
64    ///
65    /// # Arguments
66    ///
67    /// * `seed` - The initial seed to use.
68    ///
69    /// # Returns
70    ///
71    /// A new `Ed25519KeyPair` instance.
72    fn new_with_seed(seed: &[u8]) -> Result<Ed25519KeyPair, Error> {
73        match generate_seed(seed) {
74            Ok(secret_seed) => {
75                let sk: SigningKey = SigningKey::from_bytes(&secret_seed);
76                Ok(Ed25519KeyPair {
77                    public_key: sk.verifying_key(),
78                    secret_key: Some(sk),
79                })
80            }
81            Err(_) => Err(Error::InvalidSeed),
82        }
83    }
84
85    /// Creates a new `Ed25519KeyPair` from a public key.
86    ///
87    /// # Arguments
88    ///
89    /// * `public_key` - The bytes of the public key.
90    ///
91    /// # Returns
92    ///
93    /// A new `Ed25519KeyPair` instance.
94    fn from_public_key(public_key: &[u8; BYTES_LENGTH_32]) -> Result<Ed25519KeyPair, Error> {
95        match public_key.len() {
96            BYTES_LENGTH_32 => Ok(Ed25519KeyPair {
97                public_key: match VerifyingKey::from_bytes(&clone_slice_to_array(public_key)) {
98                    Ok(vk) => vk,
99                    Err(_) => return Err(Error::InvalidPublicKey),
100                },
101                secret_key: None,
102            }),
103            _ => Err(Error::InvalidKeyLength),
104        }
105    }
106
107    /// Creates a new `Ed25519KeyPair` from a secret key.
108    ///
109    /// A public key will be derived from the secret key.
110    ///
111    /// # Arguments
112    ///
113    /// * `secret_key` - The bytes of the secret key.
114    ///
115    /// # Returns
116    ///
117    /// A new `Ed25519KeyPair` instance.
118    fn from_secret_key(secret_key: &[u8; BYTES_LENGTH_32]) -> Result<Ed25519KeyPair, Error> {
119        match secret_key.len() {
120            BYTES_LENGTH_32 => {
121                let sk: SigningKey = SigningKey::from_bytes(&clone_slice_to_array(secret_key));
122                Ok(Ed25519KeyPair {
123                    public_key: sk.verifying_key(),
124                    secret_key: Some(sk),
125                })
126            }
127            _ => Err(Error::InvalidKeyLength),
128        }
129    }
130}
131
132impl CoreSign for Ed25519KeyPair {
133    /// Signs a payload using the secret key of the `Ed25519KeyPair`.
134    ///
135    /// # Arguments
136    ///
137    /// * `payload` - The payload to sign.
138    ///
139    /// # Returns
140    ///
141    /// A `Result` containing the signature as bytes or an `Error`.
142    fn sign(&self, payload: &[u8]) -> Result<Vec<u8>, Error> {
143        // Check if the secret key is present
144        match &self.secret_key {
145            Some(sk) => {
146                // Try to sign the payload
147                match sk.try_sign(payload) {
148                    Ok(signature) => {
149                        // Convert the signature to bytes and return it
150                        Ok(signature.to_bytes().to_vec())
151                    }
152                    Err(_) => Err(Error::SignatureError),
153                }
154            }
155            None => Err(Error::InvalidSecretKey),
156        }
157    }
158
159    /// Verifies a payload using the public key of the `Ed25519KeyPair`.
160    ///
161    /// # Arguments
162    ///
163    /// * `payload` - The payload to verify.
164    /// * `signature` - The signature to verify against the payload.
165    ///
166    /// # Returns
167    ///
168    /// A `Result` containing `()`, or an `Error` if the verification fails.
169    fn verify(&self, payload: &[u8], signature: &[u8]) -> Result<(), Error> {
170        // Try to convert the signature to a `Signature` instance
171        // This conversion is necessary because the `signature` argument is represented as bytes
172        match Signature::try_from(signature) {
173            Ok(sig) => match self.public_key.verify(payload, &sig) {
174                Ok(_) => Ok(()),
175                _ => Err(Error::VerificationError),
176            },
177            Err(_) => Err(Error::CanNotRetrieveSignature),
178        }
179    }
180}
181
182impl ToMultikey for Ed25519KeyPair {
183    fn to_multikey(&self) -> String {
184        let prefix = &Algorithm::Ed25519.muticodec_prefix();
185        let bytes = &self.public_key.as_bytes()[..];
186        multibase::encode(Base58Btc, [prefix, bytes].concat())
187    }
188}
189
190impl Ed25519KeyPair {
191    /// Returns the X25519 key pair corresponding to the Ed25519 key pair.
192    ///
193    /// # Returns
194    ///
195    /// A `Result` containing the X25519 key pair as `X25519KeyPair` or an `Error`.
196    pub fn get_x25519(&self) -> Result<X25519KeyPair, Error> {
197        // Check if the secret key is present
198        match &self.secret_key {
199            Some(sk) => {
200                let bytes: [u8; BYTES_LENGTH_32] = sk.to_bytes();
201                // Compute the SHA-512 hash of the secret key
202                let mut hasher = Sha512::new();
203                hasher.update(bytes);
204                let hash = hasher.finalize();
205                // Copy the first 32 bytes of the hash to the output buffer
206                let mut output = [0u8; BYTES_LENGTH_32];
207                output.copy_from_slice(&hash[..BYTES_LENGTH_32]);
208                // Adjust the first byte and the last byte of the output buffer
209                output[0] &= 248;
210                output[31] &= 127;
211                output[31] |= 64;
212
213                // Create a new X25519 key pair using the output buffer
214                X25519KeyPair::new_with_seed(&output)
215            }
216            None => {
217                // Get the bytes of the public key
218                match self.public_key_bytes() {
219                    Ok(pk_bytes) => {
220                        // Decompress the compressed Ed25519 point
221                        match CompressedEdwardsY(pk_bytes).decompress() {
222                            Some(point) => {
223                                // Convert the point to Montgomery form and create a new X25519 key pair
224                                let montgomery = point.to_montgomery();
225                                X25519KeyPair::from_public_key(montgomery.as_bytes())
226                            }
227                            None => Err(Error::InvalidPublicKey),
228                        }
229                    }
230                    Err(_) => Err(Error::InvalidPublicKey),
231                }
232            }
233        }
234    }
235}
236
237#[cfg(test)]
238mod tests {
239    use super::*;
240    use crate::crypto::traits::{CoreSign, Generate, KeyMaterial, BYTES_LENGTH_32};
241    use crate::jwk::Jwk;
242
243    // A test to create a new Ed25519KeyPair and check that bytes of both private and public key from
244    // key material is 32 bytes long.
245    #[test]
246    fn test_new() {
247        let keypair = Ed25519KeyPair::new().unwrap();
248        assert_eq!(keypair.public_key_bytes().unwrap().len(), BYTES_LENGTH_32);
249        assert_eq!(keypair.private_key_bytes().unwrap().len(), BYTES_LENGTH_32);
250    }
251
252    // Generate a new Ed25519KeyPair with a seed and check that bytes of both private and public key
253    // are equals to the given bytes pub_key_hex and pri_key_hex.
254    #[test]
255    fn test_new_with_seed() {
256        // generate seed bytes from the the string "Sample seed bytes of thirtytwo!b"
257        // Beware that you need a seed of 32 bytes to produce the deterministic key pair.
258        let my_string = String::from("Sample seed bytes of thirtytwo!b");
259        let seed: &[u8] = my_string.as_bytes();
260        let keypair = Ed25519KeyPair::new_with_seed(seed).unwrap();
261        let pub_key_hex = hex::encode(keypair.public_key_bytes().unwrap());
262        let pri_key_hex = hex::encode(keypair.private_key_bytes().unwrap());
263        assert_eq!(pub_key_hex, "412328b0201b71d0144a27d028057b6fdf58d22e0f3baaebaa5388140e57bbbd");
264        assert_eq!(pri_key_hex, "53616d706c652073656564206279746573206f662074686972747974776f2162");
265    }
266
267    // Creat a test that:
268    // - Generate a key pair
269    // - load the file test_resources/crypto_ed25519_test_sign_verify.json
270    // - sign the content of the file wiht the key pair
271    // - Verify the signature
272    #[test]
273    fn test_sign_verify() {
274        let keypair = Ed25519KeyPair::new().unwrap();
275
276        let json_file = "test_resources/crypto_ed25519_test_sign_verify.json";
277        let json_data = std::fs::read_to_string(json_file).unwrap();
278
279        let signature = keypair.sign(json_data.as_bytes());
280
281        // Verify the signature
282        let verified = keypair.verify(json_data.as_bytes(), &signature.unwrap());
283        assert!(verified.is_ok());
284    }
285
286    #[test]
287    fn test_ed25519_keypair_to_multikey() {
288        let jwk: Jwk = serde_json::from_str(
289            r#"{
290                "kty": "OKP",
291                "crv": "Ed25519",
292                "x": "O2onvM62pC1io6jQKm8Nc2UyFXcd4kOmOsBIoYtZ2ik"
293            }"#,
294        )
295        .unwrap();
296
297        let keypair: Ed25519KeyPair = jwk.try_into().unwrap();
298        let multikey = keypair.to_multikey();
299
300        assert_eq!(&multikey, "z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp");
301    }
302}