Skip to main content

datex_crypto_native/
lib.rs

1use datex_crypto_facade::{
2    crypto::{AsyncCryptoResult, Crypto},
3    error::{
4        AesCtrError, BackendError, Ed25519GenError, Ed25519SignError,
5        Ed25519VerifyError, HkdfError, KeyUnwrapError, KeyWrapError,
6        X25519DeriveError, X25519GenError,
7    },
8};
9use openssl::{
10    aes::{AesKey, unwrap_key, wrap_key},
11    derive::Deriver,
12    md::Md,
13    pkey::{Id, PKey},
14    pkey_ctx::{HkdfMode, PkeyCtx},
15    sha::sha256,
16    sign::{Signer, Verifier},
17    symm::{Cipher, Crypter, Mode},
18};
19use rand::{TryRngCore, rngs::OsRng};
20use uuid::Uuid;
21
22#[derive(Debug, Clone, PartialEq)]
23pub struct CryptoNative;
24impl Crypto for CryptoNative {
25    fn create_uuid() -> String {
26        Uuid::new_v4().to_string()
27    }
28
29    fn random_bytes(length: usize) -> Vec<u8> {
30        let mut out = vec![0u8; length];
31        OsRng
32            .try_fill_bytes(&mut out)
33            .expect("CryptoNative random_bytes failed");
34        out
35    }
36    type Sha256Error = ();
37
38    fn hash_sha256<'a>(
39        to_digest: &'a [u8],
40    ) -> AsyncCryptoResult<'a, [u8; 32], Self::Sha256Error> {
41        Box::pin(async move {
42            let hash = sha256(to_digest);
43            Ok(hash)
44        })
45    }
46
47    fn hkdf_sha256<'a>(
48        ikm: &'a [u8],
49        salt: &'a [u8],
50    ) -> AsyncCryptoResult<'a, [u8; 32], Self::HkdfError> {
51        Box::pin(async move {
52            let info = b"";
53            let mut ctx = PkeyCtx::new_id(Id::HKDF).map_err(|_| {
54                HkdfError::Backend(BackendError::Unavailable(
55                    "openssl hkdf ctx",
56                ))
57            })?;
58            ctx.derive_init().map_err(|_| {
59                HkdfError::Backend(BackendError::Unavailable(
60                    "openssl hkdf init",
61                ))
62            })?;
63            ctx.set_hkdf_mode(HkdfMode::EXTRACT_THEN_EXPAND)
64                .map_err(|_| {
65                    HkdfError::Backend(BackendError::Unavailable(
66                        "openssl hkdf mode",
67                    ))
68                })?;
69            ctx.set_hkdf_md(Md::sha256()).map_err(|_| {
70                HkdfError::Backend(BackendError::Unavailable("openssl hkdf md"))
71            })?;
72            ctx.set_hkdf_salt(salt).map_err(|_| {
73                HkdfError::Backend(BackendError::Unavailable(
74                    "openssl hkdf salt",
75                ))
76            })?;
77            ctx.set_hkdf_key(ikm).map_err(|_| {
78                HkdfError::Backend(BackendError::Unavailable(
79                    "openssl hkdf key",
80                ))
81            })?;
82            ctx.add_hkdf_info(info).map_err(|_| {
83                HkdfError::Backend(BackendError::Unavailable(
84                    "openssl hkdf info",
85                ))
86            })?;
87
88            let mut okm = [0u8; 32];
89            ctx.derive(Some(&mut okm)).map_err(|_| {
90                HkdfError::Backend(BackendError::Unavailable(
91                    "openssl hkdf derive",
92                ))
93            })?;
94            Ok(okm)
95        })
96    }
97
98    // EdDSA keygen
99    fn gen_ed25519<'a>()
100    -> AsyncCryptoResult<'a, (Vec<u8>, Vec<u8>), Self::Ed25519GenError> {
101        Box::pin(async move {
102            let key = PKey::generate_ed25519().map_err(|_| {
103                Ed25519GenError::Backend(BackendError::Unavailable(
104                    "openssl ed25519 gen",
105                ))
106            })?;
107
108            // Keep your DER/PKCS8 formats (portable).
109            let public_key = key.public_key_to_der().map_err(|_| {
110                Ed25519GenError::Backend(BackendError::Unavailable(
111                    "ed25519 pub der",
112                ))
113            })?;
114            let private_key = key.private_key_to_pkcs8().map_err(|_| {
115                Ed25519GenError::Backend(BackendError::Unavailable(
116                    "ed25519 priv pkcs8",
117                ))
118            })?;
119
120            Ok((public_key, private_key))
121        })
122    }
123
124    // EdDSA signature
125    fn sig_ed25519<'a>(
126        pri_key: &'a [u8],
127        data: &'a [u8],
128    ) -> AsyncCryptoResult<'a, [u8; 64], Self::Ed25519SignError> {
129        Box::pin(async move {
130            let sig_key = PKey::private_key_from_pkcs8(pri_key)
131                .map_err(|_| Ed25519SignError::InvalidPrivateKey)?;
132
133            let mut signer =
134                Signer::new_without_digest(&sig_key).map_err(|_| {
135                    Ed25519SignError::Backend(BackendError::Unavailable(
136                        "ed25519 signer",
137                    ))
138                })?;
139
140            let signature = signer.sign_oneshot_to_vec(data).map_err(|_| {
141                Ed25519SignError::Backend(BackendError::Unavailable(
142                    "ed25519 sign",
143                ))
144            })?;
145
146            let sig: [u8; 64] =
147                signature.as_slice().try_into().map_err(|_| {
148                    Ed25519SignError::Backend(BackendError::Unavailable(
149                        "ed25519 sig len",
150                    ))
151                })?;
152
153            Ok(sig)
154        })
155    }
156
157    // EdDSA verification of signature
158    fn ver_ed25519<'a>(
159        pub_key: &'a [u8],
160        sig: &'a [u8],
161        data: &'a [u8],
162    ) -> AsyncCryptoResult<'a, bool, Self::Ed25519VerifyError> {
163        Box::pin(async move {
164            let public_key = PKey::public_key_from_der(pub_key)
165                .map_err(|_| Ed25519VerifyError::InvalidPublicKey)?;
166
167            // OpenSSL expects signature to be exactly 64 bytes for Ed25519.
168            if sig.len() != 64 {
169                return Err(Ed25519VerifyError::InvalidSignature);
170            }
171
172            let mut verifier = Verifier::new_without_digest(&public_key)
173                .map_err(|_| {
174                    Ed25519VerifyError::Backend(BackendError::Unavailable(
175                        "ed25519 verifier",
176                    ))
177                })?;
178
179            let ok = verifier.verify_oneshot(sig, data).map_err(|_| {
180                Ed25519VerifyError::Backend(BackendError::Unavailable(
181                    "ed25519 verify",
182                ))
183            })?;
184
185            Ok(ok)
186        })
187    }
188
189    // AES CTR
190    fn aes_ctr_encrypt<'a>(
191        key: &'a [u8; 32],
192        iv: &'a [u8; 16],
193        plaintext: &'a [u8],
194    ) -> AsyncCryptoResult<'a, Vec<u8>, Self::AesCtrError> {
195        Box::pin(async move {
196            let cipher = Cipher::aes_256_ctr();
197            let mut crypter =
198                Crypter::new(cipher, Mode::Encrypt, key, Some(iv)).map_err(
199                    |_| {
200                        AesCtrError::Backend(BackendError::Unavailable(
201                            "openssl aes-ctr",
202                        ))
203                    },
204                )?;
205
206            let mut out = vec![0u8; plaintext.len() + cipher.block_size()];
207            let mut count =
208                crypter.update(plaintext, &mut out).map_err(|_| {
209                    AesCtrError::Backend(BackendError::Unavailable(
210                        "aes-ctr update",
211                    ))
212                })?;
213
214            count += crypter.finalize(&mut out[count..]).map_err(|_| {
215                AesCtrError::Backend(BackendError::Unavailable(
216                    "aes-ctr finalize",
217                ))
218            })?;
219
220            out.truncate(count);
221            Ok(out)
222        })
223    }
224
225    fn aes_ctr_decrypt<'a>(
226        key: &'a [u8; 32],
227        iv: &'a [u8; 16],
228        cipher_text: &'a [u8],
229    ) -> AsyncCryptoResult<'a, Vec<u8>, Self::AesCtrError> {
230        Box::pin(async move {
231            let cipher = Cipher::aes_256_ctr();
232            let mut crypter =
233                Crypter::new(cipher, Mode::Decrypt, key, Some(iv)).map_err(
234                    |_| {
235                        AesCtrError::Backend(BackendError::Unavailable(
236                            "openssl aes-ctr",
237                        ))
238                    },
239                )?;
240
241            let mut out = vec![0u8; cipher_text.len() + cipher.block_size()];
242            let mut count =
243                crypter.update(cipher_text, &mut out).map_err(|_| {
244                    AesCtrError::Backend(BackendError::Unavailable(
245                        "aes-ctr update",
246                    ))
247                })?;
248
249            count += crypter.finalize(&mut out[count..]).map_err(|_| {
250                AesCtrError::Backend(BackendError::Unavailable(
251                    "aes-ctr finalize",
252                ))
253            })?;
254
255            out.truncate(count);
256            Ok(out)
257        })
258    }
259
260    // AES KW
261    fn key_wrap_rfc3394<'a>(
262        kek_bytes: &'a [u8; 32],
263        rb: &'a [u8; 32],
264    ) -> AsyncCryptoResult<'a, [u8; 40], Self::KeyWrapError> {
265        Box::pin(async move {
266            // Key encryption key
267            let kek = AesKey::new_encrypt(kek_bytes).map_err(|_| {
268                KeyWrapError::Backend(BackendError::Unavailable(
269                    "openssl aes-kw",
270                ))
271            })?;
272
273            // Key wrap
274            let mut wrapped = [0u8; 40];
275            let _length = wrap_key(&kek, None, &mut wrapped, rb);
276
277            Ok(wrapped)
278        })
279    }
280
281    fn key_unwrap_rfc3394<'a>(
282        kek_bytes: &'a [u8; 32],
283        cipher: &'a [u8; 40],
284    ) -> AsyncCryptoResult<'a, [u8; 32], Self::KeyUnwrapError> {
285        Box::pin(async move {
286            // Key encryption key
287            let kek = AesKey::new_decrypt(kek_bytes).map_err(|_| {
288                KeyUnwrapError::Backend(BackendError::Unavailable(
289                    "openssl aes-kw",
290                ))
291            })?;
292
293            // Unwrap key
294            let mut unwrapped: [u8; 32] = [0u8; 32];
295            let _length = unwrap_key(&kek, None, &mut unwrapped, cipher);
296            Ok(unwrapped)
297        })
298    }
299
300    // Generate encryption keypair
301    fn gen_x25519<'a>()
302    -> AsyncCryptoResult<'a, ([u8; 44], [u8; 48]), Self::X25519GenError> {
303        Box::pin(async move {
304            let key = PKey::generate_x25519().map_err(|_| {
305                X25519GenError::Backend(BackendError::Unavailable(
306                    "openssl x25519 gen",
307                ))
308            })?;
309
310            let public_key: [u8; 44] = key
311                .public_key_to_der()
312                .map_err(|_| {
313                    X25519GenError::Backend(BackendError::Unavailable(
314                        "openssl x25519 gen",
315                    ))
316                })?
317                .try_into()
318                .map_err(|_| {
319                    X25519GenError::Backend(BackendError::Unavailable(
320                        "openssl x25519 gen",
321                    ))
322                })?;
323            let private_key: [u8; 48] = key
324                .private_key_to_pkcs8()
325                .map_err(|_| {
326                    X25519GenError::Backend(BackendError::Unavailable(
327                        "openssl x25519 gen",
328                    ))
329                })?
330                .try_into()
331                .map_err(|_| {
332                    X25519GenError::Backend(BackendError::Unavailable(
333                        "openssl x25519 gen",
334                    ))
335                })?;
336            Ok((public_key, private_key))
337        })
338    }
339
340    // Derive shared secret on x255109
341    fn derive_x25519<'a>(
342        pri_key: &'a [u8; 48],
343        peer_raw: &'a [u8; 44],
344    ) -> AsyncCryptoResult<'a, [u8; 32], Self::X25519DeriveError> {
345        Box::pin(async move {
346            let my_priv = PKey::private_key_from_pkcs8(pri_key)
347                .map_err(|_| X25519DeriveError::InvalidPrivateKey)?;
348            let peer_pub = PKey::public_key_from_der(peer_raw)
349                .map_err(|_| X25519DeriveError::InvalidPeerPublicKey)?;
350
351            let mut deriver = Deriver::new(&my_priv).map_err(|_| {
352                X25519DeriveError::Backend(BackendError::Unavailable(
353                    "x25519 deriver",
354                ))
355            })?;
356            deriver.set_peer(&peer_pub).map_err(|_| {
357                X25519DeriveError::Backend(BackendError::Unavailable(
358                    "x25519 set_peer",
359                ))
360            })?;
361
362            let shared = deriver.derive_to_vec().map_err(|_| {
363                X25519DeriveError::Backend(BackendError::Unavailable(
364                    "x25519 derive",
365                ))
366            })?;
367
368            if shared.len() != 32 {
369                return Err(X25519DeriveError::Backend(
370                    BackendError::Unavailable("x25519 shared len"),
371                ));
372            }
373
374            let mut out = [0u8; 32];
375            out.copy_from_slice(&shared);
376            Ok(out)
377        })
378    }
379}
380
381#[cfg(test)]
382mod tests {
383    use super::CryptoNative;
384    use datex_crypto_facade::{
385        crypto::Crypto,
386        error::{Ed25519VerifyError, KeyUnwrapError, X25519DeriveError},
387    };
388    #[test]
389    fn test_uuid() {
390        let uuid1 = CryptoNative::create_uuid();
391        let uuid2 = CryptoNative::create_uuid();
392        assert_ne!(uuid1, uuid2);
393
394        // 8-4-4-4-12 = 36 chars
395        assert_eq!(uuid1.len(), 36);
396        assert_eq!(uuid2.len(), 36);
397
398        // Basic dash positions check
399        assert_eq!(&uuid1[8..9], "-");
400        assert_eq!(&uuid1[13..14], "-");
401        assert_eq!(&uuid1[18..19], "-");
402        assert_eq!(&uuid1[23..24], "-");
403    }
404
405    #[test]
406    fn test_random_bytes() {
407        let bytes1 = CryptoNative::random_bytes(16);
408        let bytes2 = CryptoNative::random_bytes(16);
409        assert_eq!(bytes1.len(), 16);
410        assert_eq!(bytes2.len(), 16);
411        assert_ne!(bytes1, bytes2);
412    }
413
414    #[tokio::test]
415    async fn test_sha256_matches_openssl() {
416        let msg = b"hello world";
417        let got = CryptoNative::hash_sha256(msg).await.expect("sha256");
418        let expected = openssl::sha::sha256(msg);
419        assert_eq!(got, expected);
420    }
421
422    #[tokio::test]
423    async fn test_hkdf_deterministic_and_changes_with_inputs() {
424        let ikm = b"input key material";
425        let salt1 = b"salt one";
426        let salt2 = b"salt two";
427
428        let out1 = CryptoNative::hkdf_sha256(ikm, salt1).await.expect("hkdf");
429        let out1b = CryptoNative::hkdf_sha256(ikm, salt1).await.expect("hkdf");
430        let out2 = CryptoNative::hkdf_sha256(ikm, salt2).await.expect("hkdf");
431        let out3 = CryptoNative::hkdf_sha256(b"other ikm", salt1)
432            .await
433            .expect("hkdf");
434
435        assert_eq!(out1, out1b, "HKDF must be deterministic for same inputs");
436        assert_ne!(out1, out2, "Changing salt should change output");
437        assert_ne!(out1, out3, "Changing ikm should change output");
438    }
439
440    #[tokio::test]
441    async fn test_ed25519_sign_verify_ok_and_mismatch() {
442        let (pub_key, pri_key) =
443            CryptoNative::gen_ed25519().await.expect("gen ed25519");
444        let msg = b"Hello DATEX";
445
446        let sig = CryptoNative::sig_ed25519(&pri_key, msg)
447            .await
448            .expect("sign");
449        let ok = CryptoNative::ver_ed25519(&pub_key, &sig, msg)
450            .await
451            .expect("verify");
452        assert!(ok);
453
454        let ok2 = CryptoNative::ver_ed25519(&pub_key, &sig, b"goodbye DATEX")
455            .await
456            .expect("verify");
457        assert!(!ok2);
458    }
459
460    #[tokio::test]
461    async fn test_ed25519_verify_rejects_wrong_sig_length() {
462        let (pub_key, _pri_key) =
463            CryptoNative::gen_ed25519().await.expect("gen ed25519");
464        let msg = b"msg";
465        let bad_sig = [0u8; 63];
466
467        let err = CryptoNative::ver_ed25519(&pub_key, &bad_sig, msg)
468            .await
469            .unwrap_err();
470
471        assert_eq!(err, Ed25519VerifyError::InvalidSignature);
472    }
473
474    #[tokio::test]
475    async fn test_aes_ctr_roundtrip_and_wrong_key_changes_output() {
476        let key = [7u8; 32];
477        let iv = [9u8; 16];
478        let pt = b"Hello DATEX";
479
480        let ct = CryptoNative::aes_ctr_encrypt(&key, &iv, pt)
481            .await
482            .expect("encrypt");
483        assert_ne!(ct, pt);
484
485        let got = CryptoNative::aes_ctr_decrypt(&key, &iv, &ct)
486            .await
487            .expect("decrypt");
488        assert_eq!(got, pt);
489
490        let wrong_key = [8u8; 32];
491        let got2 = CryptoNative::aes_ctr_decrypt(&wrong_key, &iv, &ct)
492            .await
493            .expect("decrypt");
494        assert_ne!(got2, pt);
495    }
496
497    #[tokio::test]
498    async fn test_rfc3394_wrap_unwrap_roundtrip() {
499        let kek = [1u8; 32];
500        let key_to_wrap = [2u8; 32];
501
502        let wrapped = CryptoNative::key_wrap_rfc3394(&kek, &key_to_wrap)
503            .await
504            .expect("wrap");
505
506        let unwrapped = CryptoNative::key_unwrap_rfc3394(&kek, &wrapped)
507            .await
508            .expect("unwrap");
509
510        assert_eq!(unwrapped, key_to_wrap);
511    }
512
513    #[tokio::test]
514    #[ignore = "Integrity check is not implemented"]
515    async fn test_rfc3394_unwrap_integrity_failure_on_tamper() {
516        let kek = [3u8; 32];
517        let key_to_wrap = [4u8; 32];
518
519        let mut wrapped = CryptoNative::key_wrap_rfc3394(&kek, &key_to_wrap)
520            .await
521            .expect("wrap");
522
523        // flip one bit
524        wrapped[0] ^= 0x01;
525
526        let err = CryptoNative::key_unwrap_rfc3394(&kek, &wrapped)
527            .await
528            .unwrap_err();
529        assert_eq!(err, KeyUnwrapError::IntegrityCheckFailed);
530    }
531
532    #[tokio::test]
533    async fn test_x25519_derive_same_secret_both_sides() {
534        let (a_pub, a_pri) = CryptoNative::gen_x25519().await.expect("gen a");
535        let (b_pub, b_pri) = CryptoNative::gen_x25519().await.expect("gen b");
536
537        let a_shared = CryptoNative::derive_x25519(&a_pri, &b_pub)
538            .await
539            .expect("derive a");
540        let b_shared = CryptoNative::derive_x25519(&b_pri, &a_pub)
541            .await
542            .expect("derive b");
543
544        assert_eq!(a_shared, b_shared);
545    }
546
547    #[tokio::test]
548    async fn test_x25519_invalid_peer_key_errors() {
549        let (_pub, pri) = CryptoNative::gen_x25519().await.expect("gen");
550
551        // peer key with wrong bytes should error
552        let bad_peer = [0u8; 44]; // not a valid DER public key usually, but your impl uses raw_bytes
553        let err = CryptoNative::derive_x25519(&pri, &bad_peer)
554            .await
555            .unwrap_err();
556
557        assert_eq!(err, X25519DeriveError::InvalidPeerPublicKey);
558    }
559}