1pub mod decrypt;
17pub mod encrypt;
18pub mod errors;
19mod noise;
20mod scrypt;
21
22use orion::hazardous::aead::chacha20poly1305 as chapoly;
23use orion::hazardous::ecc::x25519 as orion_x25519;
24use orion::hazardous::hash::sha2::sha256::Sha256;
25use orion::hazardous::kdf::hkdf::sha256 as hkdf;
26use orion::hazardous::mac::hmac::sha256 as hmac;
27
28use zeroize::{Zeroize, ZeroizeOnDrop};
29
30use errors::{ChaPolyDecryptError, DhError, NoiseError};
31use noise::HandshakeState;
32
33const CHUNK_SIZE: u32 = 65536;
34const SCRYPT_N: u32 = 32768;
35const SCRYPT_R: u32 = 8;
36const SCRYPT_P: u32 = 1;
37const TAG_SIZE: usize = 16;
38
39#[derive(Copy, Clone, PartialEq)]
41#[non_exhaustive]
42pub enum AsymFileFormat {
43 V1,
44}
45
46#[derive(Copy, Clone, PartialEq)]
48#[non_exhaustive]
49pub enum PassFileFormat {
50 V1,
51}
52
53#[derive(Copy, Clone, PartialEq)]
55#[non_exhaustive]
56pub enum FileFormat {
57 AsymV1,
58 PassV1,
59}
60
61#[derive(Clone)]
63pub struct PayloadKey {
64 key: [u8; 32],
65}
66
67impl PayloadKey {
68 pub fn new(key: &[u8]) -> Self {
70 Self {
71 key: key.try_into().expect("Keys must be 32 bytes"),
72 }
73 }
74
75 pub fn as_bytes(&self) -> &[u8] {
77 self.key.as_slice()
78 }
79}
80
81impl Drop for PayloadKey {
82 fn drop(&mut self) {
83 self.zeroize();
84 }
85}
86
87impl Zeroize for PayloadKey {
88 fn zeroize(&mut self) {
89 self.key.zeroize();
90 }
91}
92
93impl ZeroizeOnDrop for PayloadKey {}
94
95#[derive(Clone)]
97pub struct PublicKey {
98 key: Vec<u8>,
99}
100
101#[derive(Clone)]
103pub struct PrivateKey {
104 key: Vec<u8>,
105}
106
107impl PublicKey {
108 pub fn as_bytes(&self) -> &[u8] {
110 self.key.as_ref()
111 }
112}
113
114impl TryFrom<&[u8]> for PublicKey {
116 type Error = &'static str;
117
118 fn try_from(raw_key: &[u8]) -> Result<PublicKey, Self::Error> {
119 if raw_key.len() != 32 {
120 return Err("Public keys must be 32 bytes");
121 }
122 let pk = raw_key.to_vec();
123 Ok(PublicKey { key: pk })
124 }
125}
126
127impl PrivateKey {
128 pub fn generate() -> PrivateKey {
130 let key = secure_random(32);
131 PrivateKey { key }
132 }
133
134 pub fn as_bytes(&self) -> &[u8] {
136 self.key.as_ref()
137 }
138
139 pub fn to_public(&self) -> Result<PublicKey, DhError> {
141 let pk = x25519_derive_public(&self.key)?;
142 Ok(PublicKey::try_from(pk.as_slice()).unwrap())
143 }
144
145 pub fn diffie_hellman(&self, public_key: &PublicKey) -> Result<Vec<u8>, DhError> {
148 x25519(self.as_bytes(), public_key.as_bytes())
149 }
150}
151
152impl TryFrom<&[u8]> for PrivateKey {
154 type Error = &'static str;
155
156 fn try_from(raw_key: &[u8]) -> Result<PrivateKey, Self::Error> {
157 if raw_key.len() != 32 {
158 return Err("Private keys must be 32 bytes");
159 }
160 let sk = raw_key.to_vec();
161 Ok(PrivateKey { key: sk })
162 }
163}
164
165impl Drop for PrivateKey {
166 fn drop(&mut self) {
167 self.zeroize();
168 }
169}
170
171impl Zeroize for PrivateKey {
172 fn zeroize(&mut self) {
173 self.key.as_mut_slice().zeroize();
174 }
175}
176
177impl ZeroizeOnDrop for PrivateKey {}
178
179pub fn x25519(k: &[u8], u: &[u8]) -> Result<Vec<u8>, DhError> {
184 let mut sk: [u8; 32] = k.try_into().expect("Private key must be 32 bytes");
185 let pk: [u8; 32] = u.try_into().expect("Public key must be 32 bytes");
186
187 let private_key = orion_x25519::PrivateKey::from_slice(&sk).unwrap();
188 let public_key: orion_x25519::PublicKey = orion_x25519::PublicKey::from_slice(&pk).unwrap();
189
190 let shared_secret =
191 orion_x25519::key_agreement(&private_key, &public_key).map_err(|_| DhError)?;
192 let res = shared_secret.unprotected_as_bytes().to_vec();
193 sk.zeroize();
194
195 Ok(res)
196}
197
198pub fn x25519_derive_public(private_key: &[u8]) -> Result<Vec<u8>, DhError> {
205 let sk = orion_x25519::PrivateKey::from_slice(private_key).unwrap();
206 let pk = orion_x25519::PublicKey::try_from(&sk).map_err(|_| DhError)?;
207
208 Ok(pk.to_bytes().as_ref().to_vec())
209}
210
211pub struct NoiseEncryptMsg {
213 pub ciphertext: Vec<u8>,
214 pub handshake_hash: [u8; 32],
215}
216
217pub fn noise_encrypt(
230 sender: &PrivateKey,
231 sender_public: &PublicKey,
232 recipient: &PublicKey,
233 ephemeral: Option<&PrivateKey>,
234 ephemeral_public: Option<&PublicKey>,
235 prologue: &[u8],
236 payload_key: &PayloadKey,
237) -> Result<NoiseEncryptMsg, NoiseError> {
238 let mut handshake_state = HandshakeState::init_x(
239 true,
240 prologue,
241 sender.clone(),
242 sender_public.clone(),
243 ephemeral.cloned(),
244 ephemeral_public.cloned(),
245 Some(recipient.clone()),
246 );
247
248 let noise_handshake = handshake_state.write_message(payload_key.as_bytes())?;
249 let handshake_hash = noise_handshake.handshake_hash;
250 let ciphertext = noise_handshake.message;
251
252 Ok(NoiseEncryptMsg {
253 ciphertext,
254 handshake_hash,
255 })
256}
257
258pub struct NoiseDecryptMsg {
261 pub payload_key: PayloadKey,
262 pub public_key: PublicKey,
263 pub handshake_hash: [u8; 32],
264}
265
266pub fn noise_decrypt(
270 recipient: &PrivateKey,
271 recipient_public: &PublicKey,
272 prologue: &[u8],
273 handshake_message: &[u8],
274) -> Result<NoiseDecryptMsg, NoiseError> {
275 let initiator = false;
276 let mut handshake_state = noise::HandshakeState::init_x(
277 initiator,
278 prologue,
279 recipient.clone(),
280 recipient_public.clone(),
281 None,
282 None,
283 None,
284 );
285
286 let noise_handshake = handshake_state.read_message(handshake_message)?;
288 let handshake_hash = noise_handshake.handshake_hash;
289 if noise_handshake.message.len() != 32 {
290 return Err(NoiseError::Other(
291 "Expected payload key to be 32 bytes.".to_string(),
292 ));
293 }
294
295 let payload_key = PayloadKey::new(noise_handshake.message.as_slice());
296 let sender_pubkey = handshake_state
297 .get_pubkey()
298 .expect("Expected to get the sender's public key");
299
300 Ok(NoiseDecryptMsg {
301 payload_key,
302 public_key: sender_pubkey,
303 handshake_hash,
304 })
305}
306
307#[allow(clippy::let_and_return)]
312pub(crate) fn chapoly_encrypt_noise(
313 key: &[u8],
314 nonce: u64,
315 ad: &[u8],
316 plaintext: &[u8],
317) -> Vec<u8> {
318 let nonce_bytes = nonce.to_le_bytes();
321 let mut final_nonce_bytes = [0u8; 12];
322 final_nonce_bytes[4..].copy_from_slice(&nonce_bytes);
323
324 chapoly_encrypt_ietf(key, &final_nonce_bytes, plaintext, ad)
325}
326
327#[allow(clippy::let_and_return, clippy::redundant_field_names)]
332pub fn chapoly_encrypt_ietf(key: &[u8], nonce: &[u8], plaintext: &[u8], aad: &[u8]) -> Vec<u8> {
333 let nonce = chapoly::Nonce::from_slice(nonce).expect("Nonce must be 12 bytes");
334 let mut ct_and_tag = vec![0u8; plaintext.len() + TAG_SIZE];
335 let key = chapoly::SecretKey::from_slice(key).expect("Key must be 32 bytes");
336 chapoly::seal(
337 &key,
338 &nonce,
339 plaintext,
340 Some(aad),
341 ct_and_tag.as_mut_slice(),
342 )
343 .expect("ChaCha20-Poly11305 encryption failed");
344
345 ct_and_tag
346}
347
348pub(crate) fn chapoly_decrypt_noise(
354 key: &[u8],
355 nonce: u64,
356 ad: &[u8],
357 ciphertext: &[u8],
358) -> Result<Vec<u8>, ChaPolyDecryptError> {
359 assert_eq!(key.len(), 32);
360
361 let nonce_bytes = nonce.to_le_bytes();
364 let mut final_nonce_bytes = [0u8; 12];
365 final_nonce_bytes[4..].copy_from_slice(&nonce_bytes);
366
367 chapoly_decrypt_ietf(key, &final_nonce_bytes, ciphertext, ad)
368}
369
370#[allow(clippy::redundant_field_names)]
375pub fn chapoly_decrypt_ietf(
376 key: &[u8],
377 nonce: &[u8],
378 ciphertext: &[u8],
379 aad: &[u8],
380) -> Result<Vec<u8>, ChaPolyDecryptError> {
381 let nonce = chapoly::Nonce::from_slice(nonce).expect("Nonce must be 12 bytes");
382 let key = chapoly::SecretKey::from_slice(key).expect("Key must be 32 bytes");
383 let pt_size = std::cmp::max(ciphertext.len() - TAG_SIZE, 0);
384 let mut plaintext = vec![0u8; pt_size];
385
386 chapoly::open(
387 &key,
388 &nonce,
389 ciphertext,
390 Some(aad),
391 plaintext.as_mut_slice(),
392 )
393 .map_err(|_| ChaPolyDecryptError)?;
394
395 Ok(plaintext)
396}
397
398pub fn sha256(data: &[u8]) -> Vec<u8> {
400 Sha256::digest(data).unwrap().as_ref().to_vec()
401}
402
403pub fn hmac_sha256(key: &[u8], data: &[u8]) -> Vec<u8> {
405 let sk = hmac::SecretKey::from_slice(key).unwrap();
406 hmac::HmacSha256::hmac(&sk, data)
407 .unwrap()
408 .unprotected_as_bytes()
409 .to_vec()
410}
411
412fn hkdf_noise(chaining_key: &[u8], ikm: &[u8]) -> (Vec<u8>, Vec<u8>) {
413 let counter1: [u8; 1] = [0x01];
414 let mut counter2: [u8; 33] = [0u8; 33];
415 let temp_key = hmac_sha256(chaining_key, ikm);
416 let output1 = hmac_sha256(&temp_key, &counter1);
417 counter2[..32].copy_from_slice(&output1);
418 counter2[32..].copy_from_slice(&[0x02]);
419 let output2 = hmac_sha256(&temp_key, &counter2);
420 counter2.zeroize();
421 (output1, output2)
422}
423
424pub fn hkdf_sha256(salt: &[u8], ikm: &[u8], info: &[u8], len: usize) -> Vec<u8> {
427 let mut okm = vec![0u8; len];
428
429 hkdf::derive_key(salt, ikm, Some(info), okm.as_mut_slice()).unwrap();
430
431 okm
432}
433
434pub fn scrypt(password: &[u8], salt: &[u8], n: u32, r: u32, p: u32, dk_len: usize) -> Vec<u8> {
438 scrypt::scrypt(password, salt, n as usize, r as usize, p as usize, dk_len)
439}
440
441pub fn secure_random(len: usize) -> Vec<u8> {
443 let mut data = vec![0u8; len];
444 getrandom::fill(&mut data).expect("CSPRNG gen failed");
445 data
446}
447
448#[cfg(test)]
449mod tests {
450 use super::{
451 chapoly_decrypt_ietf, chapoly_decrypt_noise, chapoly_encrypt_ietf, chapoly_encrypt_noise,
452 hkdf_sha256, hmac_sha256, scrypt, sha256, x25519,
453 };
454 use super::{PrivateKey, PublicKey};
455
456 #[test]
457 fn test_chapoly_encrypt() {
458 let expected =
459 hex::decode("cc459a8b9d29617bb70791e7b158dfaf36585f656aec0ada3899fdcd").unwrap();
460 let pt = b"Hello world!";
461 let key: [u8; 32] = [
462 0x77, 0x07, 0x6d, 0x0a, 0x73, 0x18, 0xa5, 0x7d, 0x3c, 0x16, 0xc1, 0x72, 0x51, 0xb2,
463 0x66, 0x45, 0xdf, 0x4c, 0x2f, 0x87, 0xeb, 0xc0, 0x99, 0x2a, 0xb1, 0x77, 0xfb, 0xa5,
464 0x1d, 0xb9, 0x2c, 0x2a,
465 ];
466 let nonce: u64 = 0;
467 let ad = [0x00, 0x00, 0x00, 0x0C];
468
469 let ct_and_tag = chapoly_encrypt_noise(&key, nonce, &ad, pt);
470
471 assert_eq!(&expected[..], &ct_and_tag[..]);
472 }
473
474 #[test]
475 fn test_chapoly_enc_empty_pt() {
476 let expected_ct = hex::decode("c7a7077a5e9d774b510100904c7dc805").unwrap();
477 let key = hex::decode("68301045a4494999d59ffa818ee5fafc2878bf96c32acf5fa40dbe93e8ac98ce")
478 .unwrap();
479 let nonce = [0u8; 12];
480 let aad: [u8; 1] = [0x01];
481
482 let ct = chapoly_encrypt_ietf(key.as_slice(), &nonce, &[], &aad);
483
484 assert_eq!(expected_ct.as_slice(), ct.as_slice());
485 }
486
487 #[test]
488 fn test_decrypt() {
489 let key = hex::decode("77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a")
490 .unwrap();
491 let nonce: u64 = 0;
492 let ad = hex::decode("0000000C").unwrap();
493 let expected = b"Hello world!";
494 let ct_and_tag =
495 hex::decode("cc459a8b9d29617bb70791e7b158dfaf36585f656aec0ada3899fdcd").unwrap();
496
497 let pt = chapoly_decrypt_noise(&key, nonce, &ad, &ct_and_tag).unwrap();
498
499 assert_eq!(expected, pt.as_slice());
500 }
501
502 #[test]
503 fn test_chapoly_dec_empty_pt() {
504 let ct = hex::decode("c7a7077a5e9d774b510100904c7dc805").unwrap();
505 let key = hex::decode("68301045a4494999d59ffa818ee5fafc2878bf96c32acf5fa40dbe93e8ac98ce")
506 .unwrap();
507 let nonce = [0u8; 12];
508 let aad: [u8; 1] = [0x01];
509
510 let pt = chapoly_decrypt_ietf(key.as_slice(), &nonce, ct.as_slice(), &aad).unwrap();
511
512 let expected_pt: [u8; 0] = [];
513 assert_eq!(&expected_pt, pt.as_slice());
514 }
515
516 #[test]
517 fn test_sha256() {
518 let data = b"hello";
519 let got = sha256(data);
520 let expected =
521 hex::decode("2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824")
522 .unwrap();
523 assert_eq!(&got, expected.as_slice());
524 }
525
526 #[test]
527 fn test_hmac_sha256() {
528 let key = b"yellowsubmarine.yellowsubmarine.";
529 let message = b"Hello, world!";
530 let expected =
531 hex::decode("3cb82dc71c26dfe8be75805f6438027d5170f3fdcd8057f0a55d1c7c1743224c")
532 .unwrap();
533 let result = hmac_sha256(key, message);
534
535 assert_eq!(&expected, &result);
536 }
537
538 #[test]
539 fn test_hkdf_sha256() {
540 let ikm = hex::decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap();
542 let salt = hex::decode("000102030405060708090a0b0c").unwrap();
543 let info = hex::decode("f0f1f2f3f4f5f6f7f8f9").unwrap();
544 let length = 42;
545
546 let expected_okm = hex::decode(
547 "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865",
548 )
549 .unwrap();
550
551 let result_okm = hkdf_sha256(&salt, &ikm, &info, length);
552
553 assert_eq!(&expected_okm, &result_okm);
554
555 let ikm = hex::decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap();
557 let salt = &[];
558 let info = &[];
559 let length = 42;
560
561 let expected_okm = hex::decode(
562 "8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d9d201395faa4b61a96c8",
563 )
564 .unwrap();
565
566 let result_okm = hkdf_sha256(salt, &ikm, info, length);
567 assert_eq!(&expected_okm, &result_okm);
568 }
569
570 #[test]
571 fn test_scrypt() {
572 let password = b"hackme";
573 let salt = b"yellowsubmarine.";
574
575 let expected1 =
576 hex::decode("3ebb9ac0d1da595f755407fe8fc246fe67fe6075730fc6e853351c2834bd6157")
577 .unwrap();
578 let result1 = scrypt(password, salt, 32768, 8, 1, 32);
579 assert_eq!(&expected1, &result1);
580
581 let expected2 = hex::decode("3ebb9ac0d1da595f").unwrap();
582 let result2 = scrypt(password, salt, 32768, 8, 1, 8);
583 assert_eq!(&expected2, &result2);
584
585 let expected3 = hex::decode("87b33dba57a7633a3df7741eabee3de0").unwrap();
586 let result3 = scrypt(password, salt, 1024, 8, 1, 16);
587 assert_eq!(&expected3, &result3);
588 }
589
590 #[test]
591 fn test_rfc7748_diffie_hellman_vectors() {
592 let alice_private_expected =
593 hex::decode("77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a")
594 .unwrap();
595 let alice_public_expected =
596 hex::decode("8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a")
597 .unwrap();
598 let bob_private_expected =
599 hex::decode("5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb")
600 .unwrap();
601 let bob_public_expected =
602 hex::decode("de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f")
603 .unwrap();
604 let expected_shared_secret =
605 hex::decode("4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742")
606 .unwrap();
607
608 let alice_private = PrivateKey::try_from(alice_private_expected.as_slice()).unwrap();
609 let alice_public = PublicKey::try_from(alice_public_expected.as_slice()).unwrap();
610 assert_eq!(
611 &alice_public_expected,
612 &alice_private.to_public().unwrap().as_bytes()
613 );
614
615 let bob_private = PrivateKey::try_from(bob_private_expected.as_slice()).unwrap();
616 let bob_public = PublicKey::try_from(bob_public_expected.as_slice()).unwrap();
617
618 let alice_to_bob = x25519(alice_private.as_bytes(), bob_public.as_bytes()).unwrap();
619 let bob_to_alice = x25519(bob_private.as_bytes(), alice_public.as_bytes()).unwrap();
620 let alice_to_bob2 = alice_private.diffie_hellman(&bob_public).unwrap();
621
622 assert_eq!(&alice_to_bob, &bob_to_alice);
623 assert_eq!(&alice_to_bob, &alice_to_bob2);
624 assert_eq!(&alice_to_bob, expected_shared_secret.as_slice());
625 }
626
627 #[test]
628 fn test_private_to_public() {
629 let alice_private_expected =
630 hex::decode("77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a")
631 .unwrap();
632 let alice_public_expected =
633 hex::decode("8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a")
634 .unwrap();
635 let got_public = PrivateKey::try_from(&alice_private_expected[..])
636 .unwrap()
637 .to_public()
638 .unwrap();
639
640 assert_eq!(&alice_public_expected[..], got_public.as_bytes());
641 }
642}