sare_core/hybrid_kem/
mod.rs

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