did_utils/crypto/
format.rs

1use crate::{
2    crypto::{
3        ed25519::Ed25519KeyPair,
4        errors::Error as CryptoError,
5        traits::{Generate, KeyMaterial, BYTES_LENGTH_32},
6        x25519::X25519KeyPair,
7    },
8    jwk::{
9        Bytes, Jwk, Key, Parameters, Secret, {Okp, OkpCurves},
10    },
11};
12
13use multibase::Base::Base64Url;
14
15#[derive(Default)]
16pub enum PublicKeyFormat {
17    #[default]
18    Multikey,
19    Jwk,
20}
21
22/// Converts an `Ed25519KeyPair` to a `Jwk`.
23impl TryFrom<Ed25519KeyPair> for Jwk {
24    type Error = CryptoError;
25
26    /// Converts an `Ed25519KeyPair` to a `Jwk`.
27    ///
28    /// # Arguments
29    ///
30    /// * `keypair` - The `Ed25519KeyPair` to convert.
31    ///
32    /// # Returns
33    ///
34    /// A `Result` containing the converted `Jwk` or an `Error`.
35    fn try_from(keypair: Ed25519KeyPair) -> Result<Self, Self::Error> {
36        Ok(Jwk {
37            key: Key::Okp(Okp {
38                crv: OkpCurves::Ed25519,
39                x: Bytes::from(keypair.public_key_bytes()?.to_vec()),
40                d: Some(Secret::from(keypair.private_key_bytes()?.to_vec())),
41            }),
42            prm: Parameters::default(),
43        })
44    }
45}
46
47/// Converts a `Jwk` to an `Ed25519KeyPair`.
48impl TryFrom<Jwk> for Ed25519KeyPair {
49    type Error = CryptoError;
50
51    /// Converts a `Jwk` to an `Ed25519KeyPair`.
52    ///
53    /// # Arguments
54    ///
55    /// * `jwk` - The `Jwk` to convert.
56    ///
57    /// # Returns
58    ///
59    /// A `Result` containing the converted `Ed25519KeyPair` or an `Error`.
60    fn try_from(jwk: Jwk) -> Result<Self, Self::Error> {
61        match jwk.key {
62            Key::Okp(okp) => {
63                if okp.crv != OkpCurves::Ed25519 {
64                    return Err(CryptoError::InvalidCurve);
65                }
66                match okp.d {
67                    Some(secret_key) => {
68                        let secret = secret_key;
69
70                        let secret_key_vec = secret.to_vec();
71
72                        let bytes: [u8; 32] = secret_key_vec.try_into().map_err(|_| CryptoError::InvalidSecretKey)?;
73                        Ed25519KeyPair::from_secret_key(&bytes)
74                    }
75                    None => {
76                        let public_key = okp.x;
77                        let public_key_vec = public_key.to_vec();
78                        Ed25519KeyPair::from_public_key(&public_key_vec.try_into().map_err(|_| CryptoError::InvalidPublicKey)?)
79                    }
80                }
81            }
82            _ => Err(CryptoError::Unsupported),
83        }
84    }
85}
86
87/// Converts an `X25519KeyPair` to a `Jwk`.
88impl TryFrom<X25519KeyPair> for Jwk {
89    type Error = CryptoError;
90
91    /// Converts an `X25519KeyPair` to a `Jwk`.
92    ///
93    /// # Arguments
94    ///
95    /// * `keypair` - The `X25519KeyPair` to convert.
96    ///
97    /// # Returns
98    ///
99    /// A `Result` containing the converted `Jwk` or an `Error`.
100    fn try_from(keypair: X25519KeyPair) -> Result<Self, Self::Error> {
101        Ok(Jwk {
102            key: Key::Okp(Okp {
103                crv: OkpCurves::X25519,
104                x: Bytes::from(keypair.public_key_bytes()?.to_vec()),
105                d: Some(Secret::from(keypair.private_key_bytes()?.to_vec())),
106            }),
107            prm: Parameters::default(),
108        })
109    }
110}
111
112/// Converts a `Jwk` to an `X25519KeyPair`.
113impl TryFrom<Jwk> for X25519KeyPair {
114    type Error = CryptoError;
115
116    /// Converts a `Jwk` to an `X25519KeyPair`.
117    ///
118    /// # Arguments
119    ///
120    /// * `jwk` - The `Jwk` to convert.
121    ///
122    /// # Returns
123    ///
124    /// A `Result` containing the converted `X25519KeyPair` or an `Error`.
125    fn try_from(jwk: Jwk) -> Result<Self, Self::Error> {
126        match jwk.key {
127            Key::Okp(okp) => {
128                if okp.crv != OkpCurves::X25519 {
129                    return Err(CryptoError::InvalidCurve);
130                }
131                match okp.d {
132                    Some(secret_key) => {
133                        let secret = secret_key;
134
135                        let secret_key_vec = secret.to_vec();
136
137                        let bytes: [u8; 32] = secret_key_vec.try_into().map_err(|_| CryptoError::InvalidSecretKey)?;
138                        X25519KeyPair::from_secret_key(&bytes)
139                    }
140                    None => {
141                        let public_key = okp.x;
142                        let public_key_vec = public_key.to_vec();
143                        X25519KeyPair::from_public_key(&public_key_vec.try_into().map_err(|_| CryptoError::InvalidPublicKey)?)
144                    }
145                }
146            }
147            _ => Err(CryptoError::Unsupported),
148        }
149    }
150}
151
152/// Decodes a base64url encoded key string to bytes.
153///
154/// # Arguments
155///
156/// * `key` - The base64url encoded key string.
157///
158/// # Returns
159///
160/// A `Result` containing the decoded key bytes.
161#[allow(dead_code)]
162fn base64url_to_bytes(key: &str) -> Result<[u8; BYTES_LENGTH_32], ()> {
163    let key: Vec<u8> = Base64Url.decode(key).map_err(|_| ())?;
164    let key: [u8; BYTES_LENGTH_32] = key.try_into().map_err(|_| ())?;
165    Ok(key)
166}
167
168#[cfg(test)]
169mod tests {
170    use super::*;
171    use crate::crypto::{CoreSign, Generate, ECDH};
172
173    // Tests conversion between Ed25519KeyPair and Jwk
174    #[test]
175    fn test_conversion_ed25519_jwk() -> Result<(), CryptoError> {
176        let seed = b"TMwLj2p2qhcuVhaFAj3QkkJGhK6pdyKx";
177        let payload = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit.";
178
179        let keypair = Ed25519KeyPair::new_with_seed(seed)?;
180        let signature = keypair.sign(payload).unwrap();
181
182        let jwk: Jwk = keypair.try_into()?;
183        let keypair: Ed25519KeyPair = jwk.try_into()?;
184        assert!(keypair.verify(payload, &signature).is_ok());
185
186        Ok(())
187    }
188
189    // Tests conversion from Jwk to Ed25519KeyPair with external signature
190    #[test]
191    fn test_conversion_ed25519_jwk_with_external_signature() -> Result<(), CryptoError> {
192        let jwk: Jwk = serde_json::from_str(
193            r#"{
194                "kty": "OKP",
195                "crv": "Ed25519",
196                "x": "tjOTPcs4OEMNrmn2ScYZDS-aCCbRFhJgaAmGnRsdmEo"
197            }"#,
198        )
199        .unwrap();
200
201        let payload = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit.";
202        let signature = "2QH7Qrt8clEn4ETh9lgcGUyo26cJj1U8U0CBFQvgCWHe1dwXXXb16SzPTVNVGm-J6m6eALjWrxuJfmbApdoBAQ";
203        let signature = Base64Url.decode(signature).unwrap();
204
205        let keypair: Ed25519KeyPair = jwk.try_into()?;
206        assert!(keypair.verify(payload, &signature).is_ok());
207
208        Ok(())
209    }
210
211    // Tests conversion between X25519KeyPair and Jwk
212    #[test]
213    fn test_conversion_x25519_jwk() -> Result<(), CryptoError> {
214        let alice_seed = b"TMwLj2p2qhcuVhaFAj3QkkJGhK6pdyKx";
215        let bob_seed = b"NWB6DbnIlewWVp5jIJOSgyX8msXNPPAL";
216
217        let alice = X25519KeyPair::new_with_seed(alice_seed)?;
218        let bob = X25519KeyPair::new_with_seed(bob_seed)?;
219
220        let alice_shared_secret = alice.key_exchange(&bob);
221
222        let alice_jwk: Jwk = alice.try_into()?;
223        let alice: X25519KeyPair = alice_jwk.try_into()?;
224        let bob_jwk: Jwk = bob.try_into()?;
225        let bob: X25519KeyPair = bob_jwk.try_into()?;
226
227        let bob_shared_secret = bob.key_exchange(&alice);
228
229        assert_eq!(alice_shared_secret, bob_shared_secret);
230        Ok(())
231    }
232}