datex_core/crypto/
crypto_native.rs

1use crate::stdlib::{future::Future, pin::Pin};
2use rand::Rng;
3use std::sync::OnceLock;
4use std::sync::atomic::{AtomicU64, Ordering};
5
6use super::crypto::{CryptoError, CryptoTrait};
7use uuid::Uuid;
8
9use openssl::{
10    aes::{AesKey, unwrap_key, wrap_key},
11    derive::Deriver,
12    pkey::PKey,
13    sign::{Signer, Verifier},
14    symm::{Cipher, Crypter, Mode},
15};
16
17static UUID_COUNTER: OnceLock<AtomicU64> = OnceLock::new();
18
19fn init_counter() -> &'static AtomicU64 {
20    UUID_COUNTER.get_or_init(|| AtomicU64::new(1))
21}
22fn generate_pseudo_uuid() -> String {
23    let counter = init_counter();
24    let count = counter.fetch_add(1, Ordering::Relaxed);
25
26    // Encode counter into last segment, keeping UUID-like structure
27    format!("00000000-0000-0000-0000-{count:012x}")
28}
29
30#[derive(Debug, Clone, PartialEq)]
31pub struct CryptoNative;
32impl CryptoTrait for CryptoNative {
33    fn create_uuid(&self) -> String {
34        // use pseudo-random UUID for testing
35        cfg_if::cfg_if! {
36            if #[cfg(feature = "debug")] {
37                use crate::runtime::global_context::get_global_context;
38                if get_global_context().debug_flags.enable_deterministic_behavior {
39                    generate_pseudo_uuid()
40                }
41                else {
42                    Uuid::new_v4().to_string()
43                }
44            }
45            else {
46                Uuid::new_v4().to_string()
47            }
48        }
49    }
50
51    fn random_bytes(&self, length: usize) -> Vec<u8> {
52        let mut rng = rand::thread_rng();
53        (0..length).map(|_| rng.r#gen()).collect()
54    }
55
56    // EdDSA keygen
57    fn gen_ed25519(
58        &self,
59    ) -> Pin<
60        Box<
61            dyn Future<Output = Result<(Vec<u8>, Vec<u8>), CryptoError>>
62                + 'static,
63        >,
64    > {
65        Box::pin(async move {
66            let key = PKey::generate_ed25519()
67                .map_err(|_| CryptoError::KeyGeneratorFailed)?;
68
69            let public_key: Vec<u8> = key
70                .public_key_to_der()
71                .map_err(|_| CryptoError::KeyGeneratorFailed)?;
72            let private_key: Vec<u8> = key
73                .private_key_to_pkcs8()
74                .map_err(|_| CryptoError::KeyGeneratorFailed)?;
75            Ok((public_key, private_key))
76        })
77    }
78
79    // EdDSA signature
80    fn sig_ed25519<'a>(
81        &'a self,
82        pri_key: &'a [u8],
83        data: &'a [u8],
84    ) -> Pin<Box<dyn Future<Output = Result<[u8; 64], CryptoError>> + 'a>> {
85        Box::pin(async move {
86            let sig_key = PKey::private_key_from_pkcs8(pri_key)
87                .map_err(|_| CryptoError::KeyImportFailed)?;
88            let mut signer = Signer::new_without_digest(&sig_key)
89                .map_err(|_| CryptoError::SigningError)?;
90            let signature = signer
91                .sign_oneshot_to_vec(data)
92                .map_err(|_| CryptoError::SigningError)?;
93            let signature: [u8; 64] =
94                signature.try_into().expect("Invalid signature length");
95            Ok(signature)
96        })
97    }
98
99    // EdDSA verification of signature
100    fn ver_ed25519<'a>(
101        &'a self,
102        pub_key: &'a [u8],
103        sig: &'a [u8],
104        data: &'a [u8],
105    ) -> Pin<Box<dyn Future<Output = Result<bool, CryptoError>> + 'a>> {
106        Box::pin(async move {
107            let public_key = PKey::public_key_from_der(pub_key)
108                .map_err(|_| CryptoError::KeyImportFailed)?;
109            let mut verifier = Verifier::new_without_digest(&public_key)
110                .map_err(|_| CryptoError::KeyImportFailed)?;
111            verifier
112                .verify_oneshot(sig, data)
113                .map_err(|_| CryptoError::VerificationError)
114        })
115    }
116
117    // AES CTR
118    fn aes_ctr_encrypt<'a>(
119        &'a self,
120        key: &'a [u8; 32],
121        iv: &'a [u8; 16],
122        plaintext: &'a [u8],
123    ) -> Pin<Box<dyn Future<Output = Result<Vec<u8>, CryptoError>> + 'a>> {
124        Box::pin(async move {
125            let cipher = Cipher::aes_256_ctr();
126            let mut enc = Crypter::new(cipher, Mode::Encrypt, key, Some(iv))
127                .map_err(|_| CryptoError::EncryptionError)?;
128
129            let mut out = vec![0u8; plaintext.len()];
130            let count = enc
131                .update(plaintext, &mut out)
132                .map_err(|_| CryptoError::EncryptionError)?;
133            out.truncate(count);
134            Ok(out)
135        })
136    }
137
138    fn aes_ctr_decrypt<'a>(
139        &'a self,
140        key: &'a [u8; 32],
141        iv: &'a [u8; 16],
142        ciphertext: &'a [u8],
143    ) -> Pin<Box<dyn Future<Output = Result<Vec<u8>, CryptoError>> + 'a>> {
144        self.aes_ctr_encrypt(key, iv, ciphertext)
145    }
146
147    // AES KW
148    fn key_upwrap<'a>(
149        &'a self,
150        kek_bytes: &'a [u8; 32],
151        rb: &'a [u8; 32],
152    ) -> Pin<Box<dyn Future<Output = Result<[u8; 40], CryptoError>> + 'a>> {
153        Box::pin(async move {
154            // Key encryption key
155            let kek = AesKey::new_encrypt(kek_bytes)
156                .map_err(|_| CryptoError::EncryptionError)?;
157
158            // Key wrap
159            let mut wrapped = [0u8; 40];
160            let _length = wrap_key(&kek, None, &mut wrapped, rb);
161
162            Ok(wrapped)
163        })
164    }
165
166    fn key_unwrap<'a>(
167        &'a self,
168        kek_bytes: &'a [u8; 32],
169        cipher: &'a [u8; 40],
170    ) -> Pin<Box<dyn Future<Output = Result<[u8; 32], CryptoError>> + 'a>> {
171        Box::pin(async move {
172            // Key encryption key
173            let kek = AesKey::new_decrypt(kek_bytes)
174                .map_err(|_| CryptoError::DecryptionError)?;
175
176            // Unwrap key
177            let mut unwrapped: [u8; 32] = [0u8; 32];
178            let _length = unwrap_key(&kek, None, &mut unwrapped, cipher);
179            Ok(unwrapped)
180        })
181    }
182
183    // Generate encryption keypair
184    fn gen_x25519(
185        &self,
186    ) -> Pin<Box<dyn Future<Output = Result<([u8; 44], [u8; 48]), CryptoError>>>>
187    {
188        Box::pin(async move {
189            let key = PKey::generate_x25519()
190                .map_err(|_| CryptoError::KeyGeneratorFailed)?;
191            let public_key: [u8; 44] = key
192                .public_key_to_der()
193                .map_err(|_| CryptoError::KeyGeneratorFailed)?
194                .try_into()
195                .map_err(|_| CryptoError::KeyGeneratorFailed)?;
196            let private_key: [u8; 48] = key
197                .private_key_to_pkcs8()
198                .map_err(|_| CryptoError::KeyGeneratorFailed)?
199                .try_into()
200                .map_err(|_| CryptoError::KeyGeneratorFailed)?;
201            Ok((public_key, private_key))
202        })
203    }
204
205    // Derive shared secret on x255109
206    fn derive_x25519<'a>(
207        &'a self,
208        pri_key: &'a [u8; 48],
209        peer_pub: &'a [u8; 44],
210    ) -> Pin<Box<dyn Future<Output = Result<Vec<u8>, CryptoError>> + 'a>> {
211        Box::pin(async move {
212            let peer_pub = PKey::public_key_from_der(peer_pub)
213                .map_err(|_| CryptoError::KeyImportFailed)?;
214            let my_priv = PKey::private_key_from_pkcs8(pri_key)
215                .map_err(|_| CryptoError::KeyImportFailed)?;
216
217            let mut deriver = Deriver::new(&my_priv)
218                .map_err(|_| CryptoError::KeyGeneratorFailed)?;
219            deriver
220                .set_peer(&peer_pub)
221                .map_err(|_| CryptoError::KeyGeneratorFailed)?;
222            deriver
223                .derive_to_vec()
224                .map_err(|_| CryptoError::KeyGeneratorFailed)
225        })
226    }
227}
228
229#[cfg(test)]
230mod tests {
231    use super::*;
232    static CRYPTO: CryptoNative = CryptoNative {};
233
234    // Signatures
235    #[tokio::test]
236    pub async fn test_dsa_ed2519() {
237        let data = b"Some message to sign".to_vec();
238
239        let (pub_key, pri_key) = CRYPTO.gen_ed25519().await.unwrap();
240
241        let sig: [u8; 64] = CRYPTO.sig_ed25519(&pri_key, &data).await.unwrap();
242        assert!(CRYPTO.ver_ed25519(&pub_key, &sig, &data).await.unwrap());
243    }
244
245    // AES CTR
246    #[tokio::test]
247    pub async fn aes_ctr_roundtrip() {
248        let key = [0u8; 32];
249        let iv = [0u8; 16];
250
251        let data = b"Some message to encrypt".to_vec();
252
253        let ciphered = CRYPTO.aes_ctr_encrypt(&key, &iv, &data).await.unwrap();
254        let deciphered =
255            CRYPTO.aes_ctr_decrypt(&key, &iv, &ciphered).await.unwrap();
256
257        assert_ne!(ciphered, data);
258        assert_eq!(data, deciphered.to_vec());
259    }
260
261    #[tokio::test]
262    pub async fn test_keywrapping() {
263        let kek_bytes = [1u8; 32];
264        let sym_key: [u8; 32] =
265            CRYPTO.random_bytes(32_usize).try_into().unwrap();
266        let arand = CRYPTO.key_upwrap(&kek_bytes, &sym_key).await.unwrap();
267        let brand = CRYPTO.key_unwrap(&kek_bytes, &arand).await.unwrap();
268
269        assert_ne!(arand.to_vec(), brand.to_vec());
270        assert_eq!(arand.len(), brand.len() + 8);
271    }
272
273    #[tokio::test]
274    pub async fn test_keywrapping_more() {
275        // Copy pasta from Web Crypto implementation
276        let kek: [u8; 32] = [
277            176, 213, 29, 202, 131, 45, 220, 153, 250, 120, 219, 65, 177, 117,
278            244, 172, 38, 107, 221, 109, 160, 134, 15, 195, 23, 22, 143, 238,
279            242, 222, 38, 248,
280        ];
281
282        let web_wrapped: [u8; 40] = [
283            140, 223, 207, 46, 9, 105, 205, 24, 174, 238, 109, 5, 96, 4, 51,
284            132, 54, 187, 251, 167, 105, 131, 109, 246, 123, 238, 160, 139,
285            180, 59, 185, 8, 191, 57, 139, 133, 19, 40, 15, 210,
286        ];
287
288        let wrapped = CRYPTO.key_upwrap(&kek, &kek).await.unwrap();
289
290        let unwrapped = CRYPTO.key_unwrap(&kek, &wrapped).await.unwrap();
291        let web_unwrapped =
292            CRYPTO.key_unwrap(&kek, &web_wrapped).await.unwrap();
293
294        assert_eq!(kek, unwrapped);
295        assert_eq!(kek, web_unwrapped);
296    }
297
298    #[tokio::test]
299    async fn test_dh_x25519() {
300        let (ser_pub, ser_pri) = CRYPTO.gen_x25519().await.unwrap();
301        let (cli_pub, cli_pri) = CRYPTO.gen_x25519().await.unwrap();
302
303        let cli_shared =
304            CRYPTO.derive_x25519(&cli_pri, &ser_pub).await.unwrap();
305        let ser_shared =
306            CRYPTO.derive_x25519(&ser_pri, &cli_pub).await.unwrap();
307
308        assert_eq!(cli_shared, ser_shared);
309        assert_eq!(cli_shared.len(), 32);
310    }
311
312    #[tokio::test]
313    pub async fn test_multi_roundtrip() {
314        // Given
315        let mut client_list = Vec::new();
316
317        // Generate symmetric random key
318        let sym_key: [u8; 32] = CRYPTO.random_bytes(32).try_into().unwrap();
319
320        for _ in 0..10 {
321            let (cli_pub, cli_pri) = CRYPTO.gen_x25519().await.unwrap();
322            client_list.push((cli_pri, cli_pub));
323        }
324
325        // Encrypt data with symmetric key
326        let data = b"Some message to encrypt".to_vec();
327        let iv = [0u8; 16];
328        let cipher =
329            CRYPTO.aes_ctr_encrypt(&sym_key, &iv, &data).await.unwrap();
330
331        // Sender (server)
332        let mut payloads = Vec::new();
333        for (_, peer_pub) in client_list.iter().take(10) {
334            let (ser_pub, ser_pri) = CRYPTO.gen_x25519().await.unwrap();
335            let ser_kek_bytes: [u8; 32] = CRYPTO
336                .derive_x25519(&ser_pri, peer_pub)
337                .await
338                .unwrap()
339                .try_into()
340                .unwrap();
341
342            let wrapped =
343                CRYPTO.key_upwrap(&ser_kek_bytes, &sym_key).await.unwrap();
344
345            payloads.push((ser_pub, wrapped));
346        }
347
348        // Receiver (client)
349        for i in 0..10 {
350            // Unwraps key and decrypts
351            let cli_kek_bytes: [u8; 32] = CRYPTO
352                .derive_x25519(&client_list[i].0, &payloads[i].0)
353                .await
354                .unwrap()
355                .try_into()
356                .unwrap();
357            let unwrapped = CRYPTO
358                .key_unwrap(&cli_kek_bytes, &payloads[i].1)
359                .await
360                .unwrap();
361            let plain = CRYPTO
362                .aes_ctr_decrypt(&unwrapped, &iv, &cipher)
363                .await
364                .unwrap();
365
366            // Check key wraps
367            assert_ne!(payloads[i].1.to_vec(), unwrapped.to_vec());
368            assert_eq!(payloads[i].1.len(), unwrapped.len() + 8);
369
370            // Check data, cipher and deciphered
371            assert_ne!(data, cipher);
372            assert_eq!(plain, data);
373        }
374    }
375}