use bytes::Bytes;
use ps_cypher::DecryptionError;
use ps_hash::Hash;
use crate::{DataChunkError, OwnedDataChunk, Result};
pub fn decrypt(encrypted: impl AsRef<[u8]>, key: &Hash) -> Result<OwnedDataChunk> {
let data = match ps_cypher::decrypt(encrypted.as_ref(), key) {
Ok(buffer) => Bytes::from_owner(buffer),
Err(DecryptionError::KeyMismatch) => Err(DataChunkError::HashMismatch)?,
Err(err) => Err(err)?,
};
let chunk = OwnedDataChunk::from_parts_unchecked(data, *key);
Ok(chunk)
}
#[cfg(test)]
#[allow(clippy::expect_used)]
mod tests {
use chacha20poly1305::aead::{Aead, KeyInit};
use chacha20poly1305::ChaCha20Poly1305;
use crate::DataChunkError;
const PARITY: u8 = 12;
#[test]
fn decrypt_rejects_forged_ciphertext() {
let legitimate = b"legitimate chunk data";
let forged_plaintext = b"forged malicious data";
let key = ps_hash::hash(legitimate).expect("hashing should succeed");
let compressed =
ps_compress::compress(forged_plaintext).expect("compression should succeed");
let [_, _, nonce @ ..] = *key.parity();
let encryption_key = *key.digest();
let chacha = ChaCha20Poly1305::new(&encryption_key.into());
let sealed = chacha
.encrypt(&nonce.into(), compressed.as_ref())
.expect("sealing should succeed");
let mut forged = ps_ecc::encode(&sealed, PARITY).expect("ECC encoding should succeed");
let tag = ps_cypher::tag(&forged);
forged
.extend_from_slice(tag)
.expect("appending the tag should succeed");
let result = super::decrypt(&forged, &key);
assert!(matches!(result, Err(DataChunkError::HashMismatch)));
}
}