1mod error;
2
3pub use error::{DecryptionError, EncryptionError};
4pub use ps_buffer::Buffer;
5
6use chacha20poly1305::aead::{Aead, KeyInit};
7use chacha20poly1305::ChaCha20Poly1305;
8use ps_compress::{compress, decompress_bounded};
9use ps_ecc::{decode, encode, Codeword, DecodeError};
10use ps_hash::{Hash, PARITY_SIZE};
11use ps_util::subarray;
12use std::ops::Deref;
13
14#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
15pub struct Encrypted {
16 pub bytes: Buffer,
17 pub hash: Hash,
18 pub key: Hash,
19}
20
21const KSIZE: usize = 32;
22const NSIZE: usize = 12;
23
24const PARITY: u8 = 12;
25
26#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
27pub struct ParsedKey {
28 key: [u8; KSIZE],
29 nonce: [u8; NSIZE],
30 length: usize,
31}
32
33impl std::fmt::Debug for ParsedKey {
34 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35 f.debug_struct("ParsedKey")
36 .field("key", &"<REDACTED>")
37 .field("nonce", &self.nonce)
38 .field("length", &self.length)
39 .finish()
40 }
41}
42
43impl From<&Hash> for ParsedKey {
44 fn from(value: &Hash) -> Self {
45 Self {
46 key: *value.digest(),
47 length: value.data_max_len().to_usize(),
48 nonce: *subarray(value.parity(), PARITY_SIZE - NSIZE),
49 }
50 }
51}
52
53pub fn encrypt(data: &[u8]) -> Result<Encrypted, EncryptionError> {
59 let compressed_data = compress(data)?;
60 let hash_of_raw_data = ps_hash::hash(data)?;
61
62 let ParsedKey {
63 key: encryption_key,
64 length: _,
65 nonce,
66 } = (&hash_of_raw_data).into();
67
68 let chacha = ChaCha20Poly1305::new(&encryption_key.into());
69 let encrypted_data = chacha
70 .encrypt(&nonce.into(), compressed_data.as_ref())
71 .map_err(|_| EncryptionError::ChaCha)?;
72
73 let bytes = encode(&encrypted_data, PARITY)?;
74 let hash = Hash::hash(&bytes)?;
75
76 let encrypted = Encrypted {
77 bytes,
78 hash,
79 key: hash_of_raw_data,
80 };
81
82 Ok(encrypted)
83}
84
85pub fn decrypt(data: &[u8], key: &Hash) -> Result<Buffer, DecryptionError> {
90 let ParsedKey {
91 key: encryption_key,
92 length: out_size,
93 nonce,
94 } = key.into();
95
96 let ecc_decoded = extract_encrypted(data)?;
97 let chacha = ChaCha20Poly1305::new(&encryption_key.into());
98 let compressed_data = chacha
99 .decrypt(&nonce.into(), &ecc_decoded[..])
100 .map_err(|_| DecryptionError::ChaCha)?;
101
102 Ok(decompress_bounded(&compressed_data, out_size)?)
103}
104
105#[inline]
106pub fn extract_encrypted(data: &[u8]) -> Result<Codeword<'_>, DecodeError> {
110 decode(data, PARITY)
111}
112
113#[inline]
114#[must_use]
115pub fn validate_ecc(data: &[u8]) -> bool {
130 ps_ecc::validate(data, PARITY)
131}
132
133impl AsRef<[u8]> for Encrypted {
134 fn as_ref(&self) -> &[u8] {
135 self
136 }
137}
138
139impl Deref for Encrypted {
140 type Target = [u8];
141
142 fn deref(&self) -> &Self::Target {
143 &self.bytes
144 }
145}
146
147#[cfg(test)]
148#[allow(clippy::expect_used)]
149#[allow(clippy::unwrap_used)]
150mod tests {
151 use ps_buffer::ToBuffer;
152 use ps_hash::hash;
153
154 use super::*;
155
156 #[test]
157 fn test_encrypt_and_decrypt() {
158 let original_data = b"Hello, World!";
159
160 let encrypted_data = encrypt(original_data).expect("encryption should succeed");
161
162 let decrypted_data =
163 decrypt(&encrypted_data.bytes, &encrypted_data.key).expect("decryption should succeed");
164
165 assert_ne!(
166 original_data
167 .to_buffer()
168 .expect("conversion to buffer should succeed"),
169 encrypted_data.bytes,
170 "Encryption should modify the data"
171 );
172
173 let ecc_payload = extract_encrypted(&encrypted_data.bytes)
174 .expect("extracting ECC payload should succeed");
175
176 assert_eq!(
177 encrypted_data.bytes.len(),
178 ecc_payload.len() + 2 * usize::from(PARITY),
179 "ECC encoding should add parity bytes"
180 );
181
182 assert_eq!(
183 original_data,
184 &decrypted_data[..],
185 "Decryption should reverse encryption"
186 );
187 }
188
189 fn create_test_key() -> Hash {
191 hash("Hello, world!").expect("hashing test key should succeed")
192 }
193
194 #[test]
195 fn test_parse_key() {
196 let key = &create_test_key();
197
198 let ParsedKey {
199 key: encryption_key,
200 length: _,
201 nonce,
202 } = key.into();
203
204 assert_eq!(encryption_key.len(), 32);
205 assert_eq!(nonce.len(), 12);
206 assert_eq!(&encryption_key[0..4], &[220, 186, 155, 106]); assert_eq!(&nonce[0..4], &[46, 215, 220, 44]); }
210
211 #[test]
212 fn test_encrypt_decrypt() {
213 let data = b"This is some data to encrypt";
214 let encrypted = encrypt(data).expect("encryption should succeed");
215 let decrypted = decrypt(&encrypted, &encrypted.key).expect("decryption should succeed");
216 assert_eq!(&*decrypted, data);
217 }
218
219 #[test]
220 fn test_encrypt_decrypt_empty_data() {
221 let data = b"";
222 let encrypted = encrypt(data).expect("encryption should succeed");
223 let decrypted = decrypt(&encrypted, &encrypted.key).expect("decryption should succeed");
224 assert_eq!(&*decrypted, data);
225 }
226
227 #[test]
228 fn test_encrypt_decrypt_long_data() {
229 let data = "This is a very long string to test the encryption and decryption with a large amount of data. We want to make sure that the compression and decompression work correctly, and that the encryption and decryption can handle a significant amount of data without any issues. This should be longer than any reasonable message. Let's add some more to be absolutely sure. And even more, just to be safe.".as_bytes();
230 let encrypted = encrypt(data).expect("encryption should succeed");
231 let decrypted = decrypt(&encrypted, &encrypted.key).expect("decryption should succeed");
232 assert_eq!(&*decrypted, data);
233 }
234
235 #[test]
236 fn test_encrypt_decrypt_different_key() {
237 let data = b"This is some data";
238 let encrypted = encrypt(data).expect("encryption should succeed");
239 let different_key = create_test_key(); let result = decrypt(&encrypted, &different_key);
242 assert!(result.is_err());
243 match result.unwrap_err() {
244 DecryptionError::ChaCha => {} _ => panic!("Unexpected error type"),
246 }
247 }
248
249 #[test]
250 fn test_encrypt_decrypt_tampered_data() {
251 let data = b"This is some data";
252 let mut encrypted = encrypt(data).expect("encryption should succeed");
253 encrypted.bytes[0] ^= 0x01; let decrypted = decrypt(&encrypted, &encrypted.key)
257 .expect("decryption should succeed after ECC correction");
258
259 assert_eq!(decrypted.slice(..), data);
260 }
261
262 #[test]
263 fn test_validate_ecc_for_valid_and_truncated_data() {
264 let data = b"ECC validation data";
265 let encrypted = encrypt(data).expect("encryption should succeed");
266
267 assert!(validate_ecc(&encrypted), "fresh ciphertext should validate");
268
269 let truncated = &encrypted.bytes[..encrypted.bytes.len() - 1];
270 assert!(
271 !validate_ecc(truncated),
272 "truncated ciphertext should not validate"
273 );
274 }
275
276 #[test]
277 fn test_extract_encrypted_rejects_truncated_payload() {
278 let data = b"payload";
279 let encrypted = encrypt(data).expect("encryption should succeed");
280 let truncated = &encrypted.bytes[..encrypted.bytes.len() - 1];
281
282 let result = extract_encrypted(truncated);
283 assert!(result.is_err(), "truncated payload must fail ECC decode");
284 }
285
286 #[test]
287 fn test_decrypt_truncated_payload_returns_ecc_error() {
288 let data = b"payload";
289 let encrypted = encrypt(data).expect("encryption should succeed");
290 let truncated = &encrypted.bytes[..encrypted.bytes.len() - 1];
291
292 let result = decrypt(truncated, &encrypted.key);
293 assert!(
294 matches!(result, Err(DecryptionError::Ecc(_))),
295 "truncated payload should surface as ECC error"
296 );
297 }
298
299 #[test]
300 fn test_encrypted_hash_matches_ciphertext_bytes() {
301 let data = b"hash check";
302 let encrypted = encrypt(data).expect("encryption should succeed");
303 let recalculated = Hash::hash(&encrypted.bytes).expect("hashing bytes should succeed");
304
305 assert_eq!(encrypted.hash, recalculated);
306 }
307
308 #[test]
309 fn test_as_ref_encrypted() {
310 let data = b"Test data";
311 let encrypted = encrypt(data).expect("encryption should succeed");
312 let as_ref_data: &[u8] = encrypted.as_ref();
313 assert_eq!(as_ref_data, &*encrypted);
314 assert_eq!(as_ref_data, &encrypted.bytes[..]);
315 }
316
317 #[test]
318 fn test_deref_encrypted() {
319 let data = b"More test data";
320 let encrypted = encrypt(data).expect("encryption should succeed");
321 let deref_data: &[u8] = &encrypted; assert_eq!(deref_data, &encrypted.bytes[..]);
323 }
324
325 #[test]
326 fn test_key_from_hash() {
327 let data = b"Test data for key derivation";
328 let h = hash(data).expect("hashing should succeed");
329
330 let ParsedKey {
331 key,
332 length: _,
333 nonce: _,
334 } = (&h).into();
335
336 assert_eq!(key.len(), 32);
337 }
338
339 #[test]
340 fn test_encrypt_large_data() {
341 let data = vec![b'A'; 1024 * 1024];
343 let encrypted = encrypt(&data).expect("encryption should succeed");
344 let decrypted = decrypt(&encrypted, &encrypted.key).expect("decryption should succeed");
345 assert_eq!(&*decrypted, &data[..]);
346 }
347
348 #[test]
349 fn test_ps_cypher_error_display() {
350 let data = b"test";
351 let encrypted = encrypt(data).expect("encryption should succeed");
352 let bad_key = hash(b"invalid_key").expect("hashing should succeed");
353 let result = decrypt(&encrypted, &bad_key);
354
355 if let Err(e) = result {
356 let error_message = format!("{e}");
357 assert_eq!(
358 error_message,
359 "Encryption/Decryption failure (from chacha20poly1305)"
360 ); } else {
362 panic!("Expected an error, but got success");
363 }
364 }
365
366 #[test]
367 fn test_ps_cypher_error_source() {
368 let data = b"test";
369 let encrypted = encrypt(data).expect("encryption should succeed");
370 let bad_key = hash(b"invalid_key").expect("hashing should succeed");
371 let result = decrypt(&encrypted, &bad_key);
372
373 if let Err(e) = result {
374 let source = std::error::Error::source(&e);
375 if let Some(err) = source {
376 let _ = format!("{err}"); }
378 } else {
379 panic!("Expected an error, but got success");
380 }
381 }
382
383 #[test]
384 fn test_parsed_key_debug_redacts_key() {
385 let key = create_test_key();
386 let parsed: ParsedKey = (&key).into();
387 let debug_output = format!("{parsed:?}");
388
389 assert!(
390 debug_output.contains("<REDACTED>"),
391 "Debug output should redact the key"
392 );
393 assert!(
395 debug_output.contains("key: \"<REDACTED>\""),
396 "key field should be redacted"
397 );
398 assert!(debug_output.contains("nonce:"), "nonce should be present");
400 assert!(debug_output.contains("length:"), "length should be present");
401 }
402
403 #[test]
404 #[allow(clippy::clone_on_copy)]
405 fn test_parsed_key_clone_and_copy() {
406 let key = create_test_key();
407 let parsed: ParsedKey = (&key).into();
408 let cloned = parsed.clone(); let copied = parsed;
410
411 assert_eq!(parsed, cloned);
412 assert_eq!(parsed, copied);
413 }
414
415 #[test]
416 fn test_parsed_key_hash_trait() {
417 use std::collections::HashSet;
418
419 let key1 = create_test_key();
420 let key2 = hash(b"different data").expect("hashing should succeed");
421
422 let parsed1: ParsedKey = (&key1).into();
423 let parsed2: ParsedKey = (&key2).into();
424
425 let mut set = HashSet::new();
426 set.insert(parsed1);
427 set.insert(parsed2);
428
429 assert_eq!(set.len(), 2, "different keys should hash differently");
430 }
431
432 #[test]
433 fn test_parsed_key_ordering() {
434 let key1 = hash(b"aaa").expect("hashing should succeed");
435 let key2 = hash(b"bbb").expect("hashing should succeed");
436
437 let parsed1: ParsedKey = (&key1).into();
438 let parsed2: ParsedKey = (&key2).into();
439
440 let cmp1 = parsed1.cmp(&parsed2);
442 let cmp2 = parsed2.cmp(&parsed1);
443 assert_eq!(cmp1.reverse(), cmp2);
444 }
445
446 #[test]
447 fn test_encrypted_hash_trait() {
448 use std::collections::HashSet;
449
450 let encrypted1 = encrypt(b"data1").expect("encryption should succeed");
451 let encrypted2 = encrypt(b"data2").expect("encryption should succeed");
452
453 let mut set = HashSet::new();
454 set.insert(encrypted1);
455 set.insert(encrypted2);
456
457 assert_eq!(
458 set.len(),
459 2,
460 "different encryptions should hash differently"
461 );
462 }
463
464 #[test]
465 fn test_encrypted_ordering() {
466 let encrypted1 = encrypt(b"aaa").expect("encryption should succeed");
467 let encrypted2 = encrypt(b"bbb").expect("encryption should succeed");
468
469 let cmp1 = encrypted1.cmp(&encrypted2);
470 let cmp2 = encrypted2.cmp(&encrypted1);
471 assert_eq!(cmp1.reverse(), cmp2);
472 }
473
474 #[test]
475 fn test_encrypted_equality() {
476 let data = b"same data";
477 let encrypted1 = encrypt(data).expect("encryption should succeed");
478 let encrypted2 = encrypt(data).expect("encryption should succeed");
479
480 assert_eq!(
481 encrypted1, encrypted2,
482 "same input should produce equal encryptions"
483 );
484 }
485
486 #[test]
487 fn test_encrypt_decrypt_single_byte() {
488 let data = b"x";
489 let encrypted = encrypt(data).expect("encryption should succeed");
490 let decrypted = decrypt(&encrypted, &encrypted.key).expect("decryption should succeed");
491 assert_eq!(&*decrypted, data);
492 }
493
494 #[test]
495 fn test_encrypt_decrypt_binary_with_nulls() {
496 let data: &[u8] = &[0x00, 0x01, 0x00, 0xFF, 0x00, 0xFE, 0x00];
497 let encrypted = encrypt(data).expect("encryption should succeed");
498 let decrypted = decrypt(&encrypted, &encrypted.key).expect("decryption should succeed");
499 assert_eq!(&*decrypted, data);
500 }
501
502 #[test]
503 fn test_encrypt_decrypt_all_byte_values() {
504 let data: Vec<u8> = (0u8..=255).collect();
505 let encrypted = encrypt(&data).expect("encryption should succeed");
506 let decrypted = decrypt(&encrypted, &encrypted.key).expect("decryption should succeed");
507 assert_eq!(&*decrypted, &data[..]);
508 }
509
510 #[test]
511 fn test_encrypt_decrypt_unicode() {
512 let data = "Hello 世界! 🎉 Привет мир".as_bytes();
513 let encrypted = encrypt(data).expect("encryption should succeed");
514 let decrypted = decrypt(&encrypted, &encrypted.key).expect("decryption should succeed");
515 assert_eq!(&*decrypted, data);
516 }
517
518 #[test]
519 fn test_encrypt_decrypt_highly_compressible_data() {
520 let data = vec![b'A'; 10000];
522 let encrypted = encrypt(&data).expect("encryption should succeed");
523
524 assert!(
526 encrypted.bytes.len() < data.len(),
527 "highly compressible data should result in smaller ciphertext"
528 );
529
530 let decrypted = decrypt(&encrypted, &encrypted.key).expect("decryption should succeed");
531 assert_eq!(&*decrypted, &data[..]);
532 }
533
534 #[test]
535 fn test_encryption_determinism() {
536 let data = b"deterministic test data";
537
538 let encrypted1 = encrypt(data).expect("encryption should succeed");
539 let encrypted2 = encrypt(data).expect("encryption should succeed");
540
541 assert_eq!(
542 encrypted1.bytes, encrypted2.bytes,
543 "same input should produce identical ciphertext"
544 );
545 assert_eq!(
546 encrypted1.key, encrypted2.key,
547 "same input should produce identical key"
548 );
549 assert_eq!(
550 encrypted1.hash, encrypted2.hash,
551 "same input should produce identical hash"
552 );
553 }
554
555 #[test]
556 fn test_different_inputs_produce_different_outputs() {
557 let encrypted1 = encrypt(b"input 1").expect("encryption should succeed");
558 let encrypted2 = encrypt(b"input 2").expect("encryption should succeed");
559
560 assert_ne!(
561 encrypted1.bytes, encrypted2.bytes,
562 "different inputs should produce different ciphertexts"
563 );
564 assert_ne!(
565 encrypted1.key, encrypted2.key,
566 "different inputs should produce different keys"
567 );
568 }
569
570 #[test]
571 fn test_ecc_corrects_multiple_bit_errors() {
572 let data = b"ECC multi-bit correction test";
573 let mut encrypted = encrypt(data).expect("encryption should succeed");
574
575 encrypted.bytes[0] ^= 0x01;
577 encrypted.bytes[1] ^= 0x02;
578 encrypted.bytes[2] ^= 0x04;
579
580 let decrypted = decrypt(&encrypted, &encrypted.key)
581 .expect("decryption should succeed with ECC correction");
582 assert_eq!(&*decrypted, data);
583 }
584
585 #[test]
586 fn test_validate_ecc_detects_corruption_within_capability() {
587 let data = b"ECC validation test";
588 let mut encrypted = encrypt(data).expect("encryption should succeed");
589
590 encrypted.bytes[5] ^= 0x01;
592
593 let is_valid = validate_ecc(&encrypted);
596 let decrypted = decrypt(&encrypted, &encrypted.key);
599 assert!(
600 decrypted.is_ok() || !is_valid,
601 "corrupted data should either be correctable or detected as invalid"
602 );
603 }
604
605 #[test]
606 fn test_empty_slice_validation() {
607 assert!(
608 !validate_ecc(&[]),
609 "empty slice should not validate as valid ECC"
610 );
611 }
612
613 #[test]
614 fn test_extract_encrypted_empty_slice() {
615 let result = extract_encrypted(&[]);
616 assert!(result.is_err(), "empty slice should fail extraction");
617 }
618
619 #[test]
620 fn test_decrypt_empty_slice() {
621 let key = create_test_key();
622 let result = decrypt(&[], &key);
623 assert!(
624 matches!(result, Err(DecryptionError::Ecc(_))),
625 "empty slice should return ECC error"
626 );
627 }
628
629 #[test]
630 fn test_decryption_error_clone() {
631 let data = b"test";
632 let encrypted = encrypt(data).expect("encryption should succeed");
633 let bad_key = hash(b"wrong_key").expect("hashing should succeed");
634
635 let result = decrypt(&encrypted, &bad_key);
636 if let Err(e) = result {
637 let cloned = e.clone();
638 assert_eq!(format!("{e}"), format!("{cloned}"));
639 } else {
640 panic!("Expected decryption error");
641 }
642 }
643
644 #[test]
645 fn test_encryption_error_clone() {
646 let err = EncryptionError::ChaCha;
649 let cloned = err.clone();
650 assert_eq!(format!("{err}"), format!("{cloned}"));
651 }
652
653 #[test]
654 fn test_decryption_error_debug() {
655 let err = DecryptionError::ChaCha;
656 let debug_output = format!("{err:?}");
657 assert!(debug_output.contains("ChaCha"));
658 }
659
660 #[test]
661 fn test_encryption_error_debug() {
662 let err = EncryptionError::ChaCha;
663 let debug_output = format!("{err:?}");
664 assert!(debug_output.contains("ChaCha"));
665 }
666
667 #[test]
668 fn test_encrypted_debug() {
669 let encrypted = encrypt(b"debug test").expect("encryption should succeed");
670 let debug_output = format!("{encrypted:?}");
671 assert!(debug_output.contains("Encrypted"));
672 assert!(debug_output.contains("bytes"));
673 assert!(debug_output.contains("hash"));
674 assert!(debug_output.contains("key"));
675 }
676
677 #[test]
678 fn test_parsed_key_same_hash_produces_same_key() {
679 let h = hash(b"consistent").expect("hashing should succeed");
680 let parsed1: ParsedKey = (&h).into();
681 let parsed2: ParsedKey = (&h).into();
682
683 assert_eq!(parsed1, parsed2);
684 }
685
686 #[test]
687 fn test_encrypt_decrypt_powers_of_two_sizes() {
688 for power in 0..=10 {
689 let size = 1 << power;
690 let data = vec![0xAB_u8; size];
691 let encrypted = encrypt(&data).expect("encryption should succeed");
692 let decrypted = decrypt(&encrypted, &encrypted.key).expect("decryption should succeed");
693 assert_eq!(&*decrypted, &data[..], "failed for size {size}");
694 }
695 }
696
697 #[test]
698 fn test_encrypt_decrypt_boundary_sizes() {
699 for size in [
701 127, 128, 129, 255, 256, 257, 511, 512, 513, 1023, 1024, 1025,
702 ] {
703 let data = vec![0xCD_u8; size];
704 let encrypted = encrypt(&data).expect("encryption should succeed");
705 let decrypted = decrypt(&encrypted, &encrypted.key).expect("decryption should succeed");
706 assert_eq!(&*decrypted, &data[..], "failed for size {size}");
707 }
708 }
709}