sare_core/hybrid_kem/
mod.rs

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]; // 0x25519 in LittleEndian
15const KYBER768_MAGIC_BYTES: [u8; 4] = [104, 7, 0, 0]; // 0x768 in LittleEndian
16
17#[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                // Because we make sure the key is 32bytes it won't return errors
61                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                // Because the size of the child_seed is fixed it won't return errors
128                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}