ps_datachunk/utils/
decrypt.rs1use bytes::Bytes;
2use ps_cypher::DecryptionError;
3use ps_hash::Hash;
4
5use crate::{DataChunkError, OwnedDataChunk, Result};
6
7pub fn decrypt(encrypted: impl AsRef<[u8]>, key: &Hash) -> Result<OwnedDataChunk> {
8 let data = match ps_cypher::decrypt(encrypted.as_ref(), key) {
9 Ok(buffer) => Bytes::from_owner(buffer),
10 Err(DecryptionError::KeyMismatch) => Err(DataChunkError::HashMismatch)?,
12 Err(err) => Err(err)?,
14 };
15
16 let chunk = OwnedDataChunk::from_parts_unchecked(data, *key);
18
19 Ok(chunk)
20}
21
22#[cfg(test)]
23#[allow(clippy::expect_used)]
24mod tests {
25 use chacha20poly1305::aead::{Aead, KeyInit};
26 use chacha20poly1305::ChaCha20Poly1305;
27
28 use crate::DataChunkError;
29
30 const PARITY: u8 = 12;
32
33 #[test]
37 fn decrypt_rejects_forged_ciphertext() {
38 let legitimate = b"legitimate chunk data";
39 let forged_plaintext = b"forged malicious data";
40
41 let key = ps_hash::hash(legitimate).expect("hashing should succeed");
42
43 let compressed =
46 ps_compress::compress(forged_plaintext).expect("compression should succeed");
47
48 let [_, _, nonce @ ..] = *key.parity();
49 let encryption_key = *key.digest();
50
51 let chacha = ChaCha20Poly1305::new(&encryption_key.into());
52 let sealed = chacha
53 .encrypt(&nonce.into(), compressed.as_ref())
54 .expect("sealing should succeed");
55 let forged = ps_ecc::encode(&sealed, PARITY).expect("ECC encoding should succeed");
56
57 let result = super::decrypt(&forged, &key);
58
59 assert!(matches!(result, Err(DataChunkError::HashMismatch)));
60 }
61}