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 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 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 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 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 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 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 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 let kek = AesKey::new_encrypt(kek_bytes).map_err(|_| {
268 KeyWrapError::Backend(BackendError::Unavailable(
269 "openssl aes-kw",
270 ))
271 })?;
272
273 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 let kek = AesKey::new_decrypt(kek_bytes).map_err(|_| {
288 KeyUnwrapError::Backend(BackendError::Unavailable(
289 "openssl aes-kw",
290 ))
291 })?;
292
293 let mut unwrapped: [u8; 32] = [0u8; 32];
295 let _length = unwrap_key(&kek, None, &mut unwrapped, cipher);
296 Ok(unwrapped)
297 })
298 }
299
300 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 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 assert_eq!(uuid1.len(), 36);
396 assert_eq!(uuid2.len(), 36);
397
398 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 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 let bad_peer = [0u8; 44]; let err = CryptoNative::derive_x25519(&pri, &bad_peer)
554 .await
555 .unwrap_err();
556
557 assert_eq!(err, X25519DeriveError::InvalidPeerPublicKey);
558 }
559}