1pub mod error;
2
3use crate::seed::Seed;
4
5use ed25519_compact::x25519;
6use safe_pqc_kyber as pqc_kyber;
7use secrecy::{ExposeSecret, SecretVec};
8
9use serde::{Deserialize, Serialize};
10use std::string::ToString;
11
12use crate::hybrid_kem::error::*;
13
14const X25519_MAGIC_BYTES: [u8; 4] = [25, 85, 2, 0]; const KYBER768_MAGIC_BYTES: [u8; 4] = [104, 7, 0, 0]; #[derive(Clone, Copy, Debug, Serialize, Deserialize)]
18pub enum DHAlgorithm {
19 X25519,
20}
21
22impl ToString for DHAlgorithm {
23 fn to_string(&self) -> String {
24 match self {
25 Self::X25519 => String::from("X25519"),
26 }
27 }
28}
29
30pub struct DHKeyPair {
31 pub public_key: Vec<u8>,
32 pub secret_key: SecretVec<u8>,
33 pub algorithm: DHAlgorithm,
34}
35
36impl DHKeyPair {
37 pub fn from_secret_key(
38 secret_key: &SecretVec<u8>,
39 dh_algorithm: DHAlgorithm,
40 ) -> Result<Self, HybridKEMError> {
41 match dh_algorithm {
42 DHAlgorithm::X25519 => {
43 let secret_key = x25519::SecretKey::from_slice(secret_key.expose_secret())?;
44 let public_key = secret_key.recover_public_key()?;
45
46 Ok(DHKeyPair {
47 public_key: public_key.to_vec(),
48 secret_key: SecretVec::from(secret_key.to_vec()),
49 algorithm: dh_algorithm,
50 })
51 }
52 }
53 }
54
55 pub fn from_seed(seed: &Seed, dh_algorithm: DHAlgorithm) -> Self {
56 match dh_algorithm {
57 DHAlgorithm::X25519 => {
58 let child_seed = &seed.derive_32bytes_child_seed(Some(&X25519_MAGIC_BYTES));
59
60 let secret_key =
62 x25519::SecretKey::from_slice(child_seed.expose_secret().as_slice()).unwrap();
63 let public_key = secret_key.recover_public_key().unwrap();
64
65 DHKeyPair {
66 public_key: public_key.to_vec(),
67 secret_key: SecretVec::from(secret_key.to_vec()),
68 algorithm: dh_algorithm,
69 }
70 }
71 }
72 }
73}
74
75pub struct DiffieHellman<'a> {
76 pub sender_keypair: &'a DHKeyPair,
77 pub reciever_public_key: &'a Vec<u8>,
78}
79
80impl<'a> DiffieHellman<'a> {
81 pub fn new(sender_keypair: &'a DHKeyPair, reciever_public_key: &'a Vec<u8>) -> Self {
82 DiffieHellman {
83 sender_keypair,
84 reciever_public_key,
85 }
86 }
87
88 pub fn calculate_shared_key(&self) -> Result<SecretVec<u8>, HybridKEMError> {
89 let dh_algorithm = &self.sender_keypair.algorithm;
90
91 match dh_algorithm {
92 DHAlgorithm::X25519 => {
93 let sender_key =
94 x25519::SecretKey::from_slice(self.sender_keypair.secret_key.expose_secret())?;
95 let reciever_key = x25519::PublicKey::from_slice(self.reciever_public_key)?;
96
97 Ok(reciever_key.dh(&sender_key)?.as_slice().to_vec().into())
98 }
99 }
100 }
101}
102
103#[derive(Clone, Debug, Copy, Serialize, Deserialize)]
104pub enum KEMAlgorithm {
105 Kyber768,
106}
107
108impl ToString for KEMAlgorithm {
109 fn to_string(&self) -> String {
110 match self {
111 Self::Kyber768 => String::from("Kyber768"),
112 }
113 }
114}
115
116pub struct KEMKeyPair {
117 pub public_key: Vec<u8>,
118 pub secret_key: SecretVec<u8>,
119 pub algorithm: KEMAlgorithm,
120}
121
122impl KEMKeyPair {
123 pub fn from_seed(seed: &Seed, kem_algorithm: KEMAlgorithm) -> Self {
124 match kem_algorithm {
125 KEMAlgorithm::Kyber768 => {
126 let child_seed = seed.derive_64bytes_child_seed(Some(&KYBER768_MAGIC_BYTES));
127 let keypair = pqc_kyber::derive(child_seed.expose_secret()).unwrap();
129
130 KEMKeyPair {
131 public_key: keypair.public.to_vec(),
132 secret_key: SecretVec::from(keypair.secret.to_vec()),
133 algorithm: kem_algorithm,
134 }
135 }
136 }
137 }
138}
139
140pub struct EncapsulatedSecret {
141 pub shared_secret: SecretVec<u8>,
142 pub cipher_text: Vec<u8>,
143}
144
145pub struct Encapsulation {
146 public_key: Vec<u8>,
147 algorithm: KEMAlgorithm,
148}
149
150impl Encapsulation {
151 pub fn new(public_key: &[u8], algorithm: KEMAlgorithm) -> Self {
152 Encapsulation {
153 public_key: public_key.to_vec(),
154 algorithm,
155 }
156 }
157
158 pub fn encapsulate(&self) -> Result<EncapsulatedSecret, HybridKEMError> {
159 let mut random_generator = rand::thread_rng();
160
161 let (cipher_text, shared_secret) = match self.algorithm {
162 KEMAlgorithm::Kyber768 => {
163 pqc_kyber::encapsulate(&self.public_key, &mut random_generator).unwrap()
164 }
165 };
166
167 Ok(EncapsulatedSecret {
168 shared_secret: SecretVec::from(shared_secret.to_vec()),
169 cipher_text: cipher_text.to_vec(),
170 })
171 }
172}
173
174pub struct DecapsulatedSecret {
175 shared_secret: SecretVec<u8>,
176}
177
178pub struct Decapsulation<'a> {
179 secret_key: &'a SecretVec<u8>,
180 algorithm: &'a KEMAlgorithm,
181}
182
183impl<'a> Decapsulation<'a> {
184 pub fn new(secret_key: &'a SecretVec<u8>, algorithm: &'a KEMAlgorithm) -> Self {
185 Decapsulation {
186 secret_key,
187 algorithm,
188 }
189 }
190
191 pub fn decapsulate(&self, cipher_text: &[u8]) -> Result<DecapsulatedSecret, HybridKEMError> {
192 let shared_secret = match self.algorithm {
193 KEMAlgorithm::Kyber768 => {
194 pqc_kyber::decapsulate(cipher_text, self.secret_key.expose_secret()).unwrap()
195 }
196 };
197
198 Ok(DecapsulatedSecret {
199 shared_secret: SecretVec::from(shared_secret.to_vec()),
200 })
201 }
202}
203
204pub struct HybridKEM {
205 pub dh_keypair: DHKeyPair,
206 pub kem_keypair: KEMKeyPair,
207}
208
209impl HybridKEM {
210 pub fn new(dh_keypair: DHKeyPair, kem_keypair: KEMKeyPair) -> Self {
211 HybridKEM {
212 dh_keypair,
213 kem_keypair,
214 }
215 }
216
217 pub fn calculate_raw_shared_key(
218 &self,
219 kem_cipher_text: &[u8],
220 dh_sender_public_key: &[u8],
221 ) -> Result<(SecretVec<u8>, SecretVec<u8>), HybridKEMError> {
222 let binding = dh_sender_public_key.to_vec();
223 let diffie_hellman = DiffieHellman::new(&self.dh_keypair, &binding);
224 let kem_decapsulation =
225 Decapsulation::new(&self.kem_keypair.secret_key, &self.kem_keypair.algorithm);
226
227 let dh_shared_secret = diffie_hellman.calculate_shared_key()?;
228 let kem_shared_secret = kem_decapsulation.decapsulate(kem_cipher_text)?;
229
230 Ok((dh_shared_secret, kem_shared_secret.shared_secret))
231 }
232}
233
234#[cfg(test)]
235mod tests {
236 use super::*;
237 use base64::prelude::*;
238
239 const TEST_SEED: [u8; 128] = [
240 198, 44, 204, 124, 44, 49, 54, 122, 236, 122, 174, 6, 50, 107, 65, 214, 47, 51, 12, 251,
241 107, 231, 10, 176, 23, 212, 180, 156, 17, 59, 207, 193, 239, 137, 69, 61, 25, 4, 0, 233,
242 97, 31, 94, 200, 222, 243, 222, 181, 63, 225, 246, 49, 233, 246, 206, 13, 147, 85, 137, 5,
243 165, 80, 188, 150, 198, 44, 204, 124, 44, 49, 54, 122, 236, 122, 174, 6, 50, 107, 65, 214,
244 47, 51, 12, 251, 107, 231, 10, 176, 23, 212, 180, 156, 17, 59, 207, 193, 239, 137, 69, 61,
245 25, 4, 0, 233, 97, 31, 94, 200, 222, 243, 222, 181, 63, 225, 246, 49, 233, 246, 206, 13,
246 147, 85, 137, 5, 165, 80, 188, 150,
247 ];
248
249 const KYBER_SECRET_KEY: &str = "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";
250
251 const KYBER_PUBLIC_KEY: &str = "eoMnONCcnYSjbkNQ1oO6KJYfNwsj3LaX+keuMEGz05COI8oqzAVEQFVmtzY/sgJrg9sIG9RylBfLicnCzEet2WStYPyoTPwIyYBg6vyYTnCZfuYr/uO40FQCqXop7ihdTMe5Q+ltmQzM61wpX4t/oRK6+kG9KtBueOCerhdAJhKpo1yUgNiQAehJuesXCqdko1amBEMHq/gCDPGVNUmO5zE+NlNP0RaJT5qpdNcGh7AcFriTezAsj4QJboZ4kfB6HZl0z3OWkuzGR3ekBYdEOcC8JopoHayvcje6CYA0F5NpBSe5vSSErUwoOEEaX7By6rzIVBBMrjtVUPalICsp32W7pyBC9fOk6IVCVXlRTRu+BqEoQeVJmCtET9wTguo1hCgHNPiCpQEdcbjGJJnPeqQlN0Su8eXNzyODA5pDAYi+MqC7tFtD7SCJ71xgHCI4FuZAn6BIS1ykBnpai0tzd5xiexuf1/w5i6SjV2sQbRGEpQzIJlzHyFa/5vArfSh6xnCTg3QvkOxraGBH3zV2KilnHJOM2lFLJSc+Zkl7tmPCEblC0SAD4gdyYto+yxYD3yN3klGKhtCtrvROpdwu97yQ9jmYveZ8hhxFSHhcT3dxs4pSX5kGmLutsmsASGSqw1i+9JhOSkFx71ViM8sLUNStPSwKjkPAhqghIyAYffZ7wwKVUkwoZacxw2h8myEbdux14rt70pzDrhY2S6gRbDYVKwiwnSVtDoECcaATjjPL3mO9Y/IdTCSmi4gno9Gx4BVoMNcM92xE1HC4IagZ0RhwA7ZVZFBfpDyObnugEYDJEVscpsKZKjdbhWRWucwdGbOjsXkq8Uk+J5Eb42mrvZMEoFspSwJrN8ayWBMh8VdNzMA+UAttPyoh0aOJqPdlZWZyIEcnJ8cBWJACdFmHO4XPq9p5+kmPUPAVJcKq+lQB+2NZ79JpZXm248auAqVebAadWzJ1WiJ4rgKR7qZzCvxJHpQuprs0npoM8wJ/FrSfqLuRpzxSVmzLh6E5x/Q3huRjxSQXCAw0Csud5zuBEKLMimyrq2nH5Xi0alSkzbFBLwlWLlnDiGcKYzuQm3PDnsLFbZFi9HeF02uhWjO80RGG6jolZjpd95VgAZB5oMBW2WcM8leYvHYM/IMQODlyfmGZU2Q6igtylYt4mMQQlRAUAYWM4fopdSe/7huWK0iEUtEXO6qUVTEA4Ywv8wJMwFhYn1U/VXN/+qOtMIR65bgFzCitSsE542Eg4Xm1ZJbLF3USobtAi5CXaRaLRrNWALWTO+gNu1wFb7eZMySoHyU+03u/Lzux2ZfGPEVA/IhVJ/Qr27x4hrqY66agtno0QfUB4oxsp+IMBpS7TjieeTwdhoPFynkd93CfTEIOfJi6A1W2UsqUWUKsB5sHXuhK8kjHZxl/haa9lxMvAMO2dAp6P0gTi6qNF5JCsjvM8aNrQEiNodmeO3SPfRS3D7tTx3HBOTObmPwuEegI6IRUoAiiMsuxPLEMOpV2bihNHpRKXGtihatuo9opj3ekqxlglF/TNc/qSn3cGowhuUiBum8kK0lqA9jonDRuuUaCG1A=";
252
253 const KYBER_CIPHER_TEXT: &str = "dcZCy2qBW9KURkV2YmYvg3v35MAX2TsNMEA5m8GHws7D+m7IfOw6NWskDojrglG0s1j3pC4HGoOp7urS5SDqvsZlmW0vae//ZHMqIHStG14vRM23lIl1ElZejtkm0AWfGhYs4ZkAzeRfdPjuaYbm3UzbEm8bng0yTyifMigqEc9whQp2WKu0z6bArcFftrQsxkR2wLaK8qN+w7ARuL4infcRHSeexzyt3NvP65KK7/8lRqjMvp4rbjcyucSKr6UPaIwgIQntuRn3y90QOXbuijWduGulCK4z2eS1dFsrYnuc66JZZPz7OJTDdHyFpYMxdulNv0KFFgbC6yef7zXu984iGmdNPdRQRK5tmqdQld4eKKxQ9MZFOVB0R0xJ6FLRcImsiIMSDbjBo+C2aGyZir156XeFcIeXbSz/APxucTtciFE5sNLOYVBAz3ci7t1gnZvWr9Blw8Ew7kb0Mk1l1sh4ZONSrmpG/qQeOfyBGMiUgiszqPuMAQNwB/8I64t3QO72JMzW9jQMUIa/q4isfc3wScPFHW4Hbnmk5HpTsqbjogdxTsDYa7orcKCTkRz80+Fa0bf2n2sQW0mieph0gntlEgw6Mk/wNOZPUwljj7t+B3qy1MS2RYCALvLFCBQ06cMdyE6EJrgPUqjFh0npmFV6sdAigVYhEStWNdKftVw8A4jfHKQhR+fGcyZm3zY7ZjQailZbOGBDmZDhWnyzKajYKovvYkyNDI/6H4qtMKEmBXJ/QIOu2VvaJAeyVGFtv1kmPzfgag/Y0obOZfVyarkfwUqRsK/fi2DWURwrAVwDI+k+4+e/6Bpe4ERf5crxaYVE/THfwLh0cj8ShLR8h59FjrWYutHFqHwzE8WY6JaV01kebROK9tbbvtZn79GNuwfxrCOiB8LEM1usSbj8bdBLFgTrbsujB8XhOnSn5RJmg6BoE4Bb7wPQUujrjuF3G4x74miChX/LUjc34nJDERng2kUT0vqC2+A7uDxDqjoc48LP/muCuKqD5xJZAjXE8FH9hN2WMEdrvcj986WefCGLJCWHLkMvCzUq3WPpTcH6xwQorgR4ZoLuR+wwWRMlIJfzVsbkxQedJONGBvjKRI8jf50dldQUGVN52eV5dRW6X0hUQkhTToWSFCxw22WQF18s9iun4BMWW3pp0AjuhtptjcSnQGAW+YBw1WDtRv1OT8VFAY5O0rFUJ1rtaMEE1bd0g0NrZ1WK+F68d60xP+sGmYyEGxyx1A2Yc5lOccG5mEZRnxPOsEgGygyWM9KePFN/MO5fu09jJUSTSUuelI7UbM1r0v44ns+Wm1wWpsNfXthkLmvbdVHjXPjvTZnWp6Nr0J5pYZKdZEleLxLjuUzQRHt7obhpQX+kBlAHFrEzDCdeYrlL1HFAFYP5n5WdVDYZn0rgypan3KpCx5YS+bhSYkqYAekUub+CzW46Xek=";
254
255 const KYBER_SHARED_SECRET: [u8; 32] = [
256 185, 127, 166, 22, 6, 250, 76, 23, 7, 18, 145, 156, 212, 31, 147, 145, 238, 220, 219, 29,
257 89, 115, 86, 138, 173, 153, 147, 40, 206, 131, 51, 127,
258 ];
259
260 const X25519_SECRET_KEY: &str = "Hce1WL1kC3XHHSH+5EyDzoyraj9+bWZTX1R4A7ZougM=";
261 const X25519_PUBLIC_KEY: &str = "P07nNmiMAgt4hSh8YMwQ58yRBxErKp9VXrh74RHz7Sw=";
262
263 const X25519_RECV_SECRET_KEY: &str = "OCsYVt7xwPf11E8chbtRa+IRYgYsoEpbfRY8+R0hcEc=";
264 const X25519_RECV_PUBLIC_KEY: &str = "3Yic3lphqfixI+rSbOiB91SCpEh0vPCrWu6n2YxzMn0=";
265
266 const _X25519_SHARED_SECRET: [u8; 32] = [
267 71, 141, 13, 166, 215, 13, 144, 138, 183, 233, 237, 240, 88, 255, 7, 135, 238, 98, 67, 21,
268 233, 9, 99, 125, 193, 122, 201, 224, 41, 51, 100, 25,
269 ];
270
271 #[test]
272 fn kyber_keypair_from_seed() {
273 let keypair = KEMKeyPair::from_seed(
274 &Seed::new(SecretVec::from(TEST_SEED.to_vec())),
275 KEMAlgorithm::Kyber768,
276 );
277
278 assert_eq!(
279 KYBER_SECRET_KEY,
280 BASE64_STANDARD.encode(keypair.secret_key.expose_secret())
281 );
282
283 assert_eq!(KYBER_PUBLIC_KEY, BASE64_STANDARD.encode(keypair.public_key));
284 }
285
286 #[test]
287 fn x25519_keypair_from_seed() {
288 let keypair = DHKeyPair::from_seed(
289 &Seed::new(SecretVec::from(TEST_SEED.to_vec())),
290 DHAlgorithm::X25519,
291 );
292
293 assert_eq!(
294 X25519_SECRET_KEY,
295 BASE64_STANDARD.encode(keypair.secret_key.expose_secret())
296 );
297
298 assert_eq!(
299 X25519_PUBLIC_KEY,
300 BASE64_STANDARD.encode(keypair.public_key)
301 );
302 }
303
304 #[test]
305 fn kyber_encapsulate() {
306 let kem = Encapsulation::new(
307 &BASE64_STANDARD.decode(KYBER_PUBLIC_KEY).unwrap(),
308 KEMAlgorithm::Kyber768,
309 );
310
311 assert!(kem.encapsulate().is_ok());
312 }
313
314 #[test]
315 fn kyber_decapsulate() {
316 let binding = SecretVec::from(BASE64_STANDARD.decode(KYBER_SECRET_KEY).unwrap());
317 let kem = Decapsulation::new(&binding, &KEMAlgorithm::Kyber768);
318
319 let decapsulated_secret = kem
320 .decapsulate(&BASE64_STANDARD.decode(KYBER_CIPHER_TEXT).unwrap())
321 .unwrap();
322
323 assert_eq!(
324 decapsulated_secret.shared_secret.expose_secret().as_slice(),
325 KYBER_SHARED_SECRET
326 );
327 }
328
329 #[test]
330 fn diffie_hellman_x25519() {
331 let sender_keypair = DHKeyPair::from_seed(
332 &Seed::new(SecretVec::from(TEST_SEED.to_vec())),
333 DHAlgorithm::X25519,
334 );
335
336 let binding = BASE64_STANDARD.decode(X25519_RECV_PUBLIC_KEY).unwrap();
337
338 let sender_dh = DiffieHellman::new(&sender_keypair, &binding);
339
340 let sender_shared_secret = sender_dh.calculate_shared_key().unwrap();
341
342 let reciever_keypair = DHKeyPair::from_secret_key(
343 &SecretVec::from(BASE64_STANDARD.decode(X25519_RECV_SECRET_KEY).unwrap()),
344 DHAlgorithm::X25519,
345 )
346 .unwrap();
347
348 let binding = BASE64_STANDARD.decode(X25519_PUBLIC_KEY).unwrap();
349 let reciever_dh = DiffieHellman::new(&reciever_keypair, &binding);
350
351 let reciever_shared_secret = reciever_dh.calculate_shared_key().unwrap();
352
353 assert_eq!(
354 reciever_shared_secret.expose_secret(),
355 sender_shared_secret.expose_secret()
356 );
357 }
358}