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