did_utils/crypto/
x25519.rs1use multibase::Base::Base58Btc;
2use x25519_dalek::{PublicKey, StaticSecret};
3
4use super::{
5 alg::Algorithm,
6 errors::Error,
7 traits::{Generate, KeyMaterial, ToMultikey, BYTES_LENGTH_32, ECDH},
8 utils::{clone_slice_to_array, generate_seed},
9 AsymmetricKey,
10};
11
12pub type X25519KeyPair = AsymmetricKey<PublicKey, StaticSecret>;
13
14impl std::fmt::Debug for X25519KeyPair {
15 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19 f.write_fmt(format_args!("{:?}", self.public_key))
20 }
21}
22
23impl KeyMaterial for X25519KeyPair {
24 fn public_key_bytes(&self) -> Result<[u8; BYTES_LENGTH_32], Error> {
30 Ok(clone_slice_to_array(self.public_key.as_bytes()))
31 }
32
33 fn private_key_bytes(&self) -> Result<[u8; BYTES_LENGTH_32], Error> {
39 match &self.secret_key {
40 Some(sk) => Ok(clone_slice_to_array(sk.as_bytes())),
41 None => Err(Error::InvalidSecretKey),
42 }
43 }
44}
45
46impl Generate for X25519KeyPair {
47 fn new() -> Result<X25519KeyPair, Error> {
59 Self::new_with_seed(vec![].as_slice())
60 }
61
62 fn new_with_seed(seed: &[u8]) -> Result<X25519KeyPair, Error> {
74 match generate_seed(seed) {
75 Ok(secret_seed) => {
76 let sk = StaticSecret::from(secret_seed);
77 Ok(X25519KeyPair {
78 public_key: PublicKey::from(&sk),
79 secret_key: Some(sk),
80 })
81 }
82 Err(_) => Err(Error::InvalidSeed),
83 }
84 }
85
86 fn from_public_key(public_key: &[u8; BYTES_LENGTH_32]) -> Result<X25519KeyPair, Error> {
96 match public_key.len() {
97 BYTES_LENGTH_32 => {
98 let pk = clone_slice_to_array(public_key);
99 Ok(X25519KeyPair {
100 public_key: PublicKey::from(pk),
101 secret_key: None,
102 })
103 }
104 _ => Err(Error::InvalidKeyLength),
105 }
106 }
107
108 fn from_secret_key(secret_key: &[u8; BYTES_LENGTH_32]) -> Result<X25519KeyPair, Error> {
118 match secret_key.len() {
119 BYTES_LENGTH_32 => {
120 let sk_bytes = clone_slice_to_array(secret_key);
121 let sk = StaticSecret::from(sk_bytes);
122 Ok(X25519KeyPair {
123 public_key: PublicKey::from(&sk),
124 secret_key: Some(sk),
125 })
126 }
127 _ => Err(Error::InvalidKeyLength),
128 }
129 }
130}
131
132impl ECDH for X25519KeyPair {
133 fn key_exchange(&self, key: &Self) -> Option<Vec<u8>> {
144 (self.secret_key).as_ref().map(|x| x.diffie_hellman(&key.public_key).as_bytes().to_vec())
145 }
146}
147
148impl ToMultikey for X25519KeyPair {
149 fn to_multikey(&self) -> String {
150 let prefix = &Algorithm::X25519.muticodec_prefix();
151 let bytes = &self.public_key.as_bytes()[..];
152 multibase::encode(Base58Btc, [prefix, bytes].concat())
153 }
154}
155
156#[cfg(test)]
157pub mod tests {
158 use crate::jwk::Jwk;
161 use x25519_dalek::{EphemeralSecret, PublicKey};
162
163 use super::*;
164 use crate::crypto::{
165 traits::{Generate, KeyMaterial, BYTES_LENGTH_32, ECDH},
166 utils::clone_slice_to_array,
167 };
168
169 #[test]
172 fn test_new() {
173 let keypair = X25519KeyPair::new().unwrap();
174 assert_eq!(keypair.public_key_bytes().unwrap().len(), BYTES_LENGTH_32);
175 assert_eq!(keypair.private_key_bytes().unwrap().len(), BYTES_LENGTH_32);
176 }
177
178 #[test]
181 fn test_new_with_seed() {
182 let my_string = String::from("Sample seed bytes of thirtytwo!b");
185 let seed: &[u8] = my_string.as_bytes();
186 let keypair = X25519KeyPair::new_with_seed(seed).unwrap();
187 let pub_key_hex = hex::encode(keypair.public_key_bytes().unwrap());
188 let pri_key_hex = hex::encode(keypair.private_key_bytes().unwrap());
189
190 assert_eq!(pub_key_hex, "2879534e09045c99580051db0cc7c0eac622a649b55893798fb62159f4134159");
191 assert_eq!(pri_key_hex, "53616d706c652073656564206279746573206f662074686972747974776f2162");
192 }
193
194 #[test]
199 fn test_encrypt_decrypt() {
200 let decryption_keypair_at_recipient = X25519KeyPair::new().unwrap();
203 let encryption_public_key_bytes_at_recipient = decryption_keypair_at_recipient.public_key_bytes().unwrap();
204
205 let encryption_public_key_bytes_at_sender = clone_slice_to_array(&encryption_public_key_bytes_at_recipient);
207 let eph_secret_at_sender = EphemeralSecret::random();
209 let eph_public_key_in_transport = PublicKey::from(&eph_secret_at_sender);
211 let encryption_public_key_at_sender = X25519KeyPair::from_public_key(&encryption_public_key_bytes_at_sender).unwrap();
213 let shared_secret_at_sender = &eph_secret_at_sender.diffie_hellman(&encryption_public_key_at_sender.public_key);
214 let symmetric_key_at_sender = shared_secret_at_sender.as_bytes();
216
217 let shared_secret_at_recipient = decryption_keypair_at_recipient
225 .secret_key
226 .as_ref()
227 .unwrap()
228 .diffie_hellman(&eph_public_key_in_transport);
229
230 let eph_public_key_at_recipient = X25519KeyPair::from_public_key(eph_public_key_in_transport.as_bytes()).unwrap();
231 decryption_keypair_at_recipient.key_exchange(&eph_public_key_at_recipient);
232 let symmetric_key_at_recipient = shared_secret_at_recipient.as_bytes();
233
234 assert_eq!(symmetric_key_at_sender, symmetric_key_at_recipient);
236 }
237
238 #[test]
239 fn test_x25519_keypair_to_multikey() {
240 let jwk: Jwk = serde_json::from_str(
241 r#"{
242 "kty": "OKP",
243 "crv": "X25519",
244 "x": "A2gufB762KKDkbTX0usDbekRJ-_PPBeVhc2gNgjpswU"
245 }"#,
246 )
247 .unwrap();
248
249 let keypair: X25519KeyPair = jwk.try_into().unwrap();
250 let multikey = keypair.to_multikey();
251
252 assert_eq!(&multikey, "z6LSbuUXWSgPfpiDBjUK6E7yiCKMN2eKJsXn5b55ZgqGz6Mr");
253 }
254}