rust_crypto_utils/
keyexchange.rs1use rand::rngs::OsRng;
6use x25519_dalek::{EphemeralSecret, PublicKey, StaticSecret};
7use zeroize::{Zeroize, ZeroizeOnDrop};
8use serde::{Deserialize, Serialize};
9use thiserror::Error;
10
11#[derive(Error, Debug)]
13pub enum KeyExchangeError {
14 #[error("Invalid public key")]
15 InvalidPublicKey,
16
17 #[error("Key exchange failed")]
18 ExchangeFailed,
19
20 #[error("Invalid key length")]
21 InvalidKeyLength,
22}
23
24#[derive(Clone, Serialize, Deserialize)]
26pub struct X25519PublicKey {
27 bytes: [u8; 32],
28}
29
30impl X25519PublicKey {
31 pub fn from_bytes(bytes: &[u8]) -> Result<Self, KeyExchangeError> {
33 if bytes.len() != 32 {
34 return Err(KeyExchangeError::InvalidKeyLength);
35 }
36 let mut arr = [0u8; 32];
37 arr.copy_from_slice(bytes);
38 Ok(Self { bytes: arr })
39 }
40
41 pub fn as_bytes(&self) -> &[u8; 32] {
43 &self.bytes
44 }
45
46 pub fn to_hex(&self) -> String {
48 hex::encode(&self.bytes)
49 }
50
51 pub fn from_hex(hex_str: &str) -> Result<Self, KeyExchangeError> {
53 let bytes = hex::decode(hex_str).map_err(|_| KeyExchangeError::InvalidPublicKey)?;
54 Self::from_bytes(&bytes)
55 }
56
57 fn to_dalek(&self) -> PublicKey {
58 PublicKey::from(self.bytes)
59 }
60}
61
62impl std::fmt::Debug for X25519PublicKey {
63 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64 write!(f, "X25519PublicKey({}...)", &self.to_hex()[..16])
65 }
66}
67
68#[derive(Zeroize, ZeroizeOnDrop)]
70pub struct SharedSecret {
71 bytes: [u8; 32],
72}
73
74impl SharedSecret {
75 pub fn as_bytes(&self) -> &[u8; 32] {
77 &self.bytes
78 }
79
80 pub fn derive_key(&self, info: &[u8]) -> [u8; 32] {
82 use sha2::Sha256;
83 use hkdf::Hkdf;
84
85 let hk = Hkdf::<Sha256>::new(None, &self.bytes);
86 let mut okm = [0u8; 32];
87 hk.expand(info, &mut okm).expect("HKDF expand failed");
88 okm
89 }
90}
91
92pub struct X25519KeyPair {
94 secret: StaticSecret,
95 public: X25519PublicKey,
96}
97
98impl X25519KeyPair {
99 pub fn generate() -> Self {
101 let secret = StaticSecret::random_from_rng(OsRng);
102 let public_key = PublicKey::from(&secret);
103 Self {
104 secret,
105 public: X25519PublicKey {
106 bytes: public_key.to_bytes(),
107 },
108 }
109 }
110
111 pub fn from_secret_bytes(bytes: &[u8]) -> Result<Self, KeyExchangeError> {
113 if bytes.len() != 32 {
114 return Err(KeyExchangeError::InvalidKeyLength);
115 }
116 let mut arr = [0u8; 32];
117 arr.copy_from_slice(bytes);
118 let secret = StaticSecret::from(arr);
119 let public_key = PublicKey::from(&secret);
120 Ok(Self {
121 secret,
122 public: X25519PublicKey {
123 bytes: public_key.to_bytes(),
124 },
125 })
126 }
127
128 pub fn public_key(&self) -> &X25519PublicKey {
130 &self.public
131 }
132
133 pub fn exchange(&self, peer_public: &X25519PublicKey) -> SharedSecret {
135 let shared = self.secret.diffie_hellman(&peer_public.to_dalek());
136 SharedSecret {
137 bytes: shared.to_bytes(),
138 }
139 }
140}
141
142pub struct EphemeralX25519KeyPair {
144 secret: Option<EphemeralSecret>,
145 public: X25519PublicKey,
146}
147
148impl EphemeralX25519KeyPair {
149 pub fn generate() -> Self {
151 let secret = EphemeralSecret::random_from_rng(OsRng);
152 let public_key = PublicKey::from(&secret);
153 Self {
154 secret: Some(secret),
155 public: X25519PublicKey {
156 bytes: public_key.to_bytes(),
157 },
158 }
159 }
160
161 pub fn public_key(&self) -> &X25519PublicKey {
163 &self.public
164 }
165
166 pub fn exchange(mut self, peer_public: &X25519PublicKey) -> Result<SharedSecret, KeyExchangeError> {
168 let secret = self.secret.take().ok_or(KeyExchangeError::ExchangeFailed)?;
169 let shared = secret.diffie_hellman(&peer_public.to_dalek());
170 Ok(SharedSecret {
171 bytes: shared.to_bytes(),
172 })
173 }
174}
175
176#[cfg(test)]
177mod tests {
178 use super::*;
179
180 #[test]
181 fn test_key_exchange() {
182 let alice = X25519KeyPair::generate();
183 let bob = X25519KeyPair::generate();
184
185 let alice_shared = alice.exchange(bob.public_key());
186 let bob_shared = bob.exchange(alice.public_key());
187
188 assert_eq!(alice_shared.as_bytes(), bob_shared.as_bytes());
189 }
190
191 #[test]
192 fn test_ephemeral_key_exchange() {
193 let alice = EphemeralX25519KeyPair::generate();
194 let bob = X25519KeyPair::generate();
195
196 let alice_public = alice.public_key().clone();
197 let alice_shared = alice.exchange(bob.public_key()).unwrap();
198 let bob_shared = bob.exchange(&alice_public);
199
200 assert_eq!(alice_shared.as_bytes(), bob_shared.as_bytes());
201 }
202
203 #[test]
204 fn test_public_key_serialization() {
205 let keypair = X25519KeyPair::generate();
206 let hex = keypair.public_key().to_hex();
207 let restored = X25519PublicKey::from_hex(&hex).unwrap();
208 assert_eq!(keypair.public_key().as_bytes(), restored.as_bytes());
209 }
210
211 #[test]
212 fn test_derive_key() {
213 let alice = X25519KeyPair::generate();
214 let bob = X25519KeyPair::generate();
215
216 let shared = alice.exchange(bob.public_key());
217 let key1 = shared.derive_key(b"encryption");
218 let key2 = shared.derive_key(b"authentication");
219
220 assert_ne!(key1, key2);
221 }
222
223 #[test]
224 fn test_from_secret_bytes() {
225 let original = X25519KeyPair::generate();
226
227 let original_public = original.public_key().clone();
229
230 let test_bytes = [42u8; 32];
233 let restored = X25519KeyPair::from_secret_bytes(&test_bytes).unwrap();
234
235 assert!(restored.public_key().as_bytes().len() == 32);
237 }
238}