mod error;
pub use error::{DecryptionError, EncryptionError, ParseKeyError, PsCypherError};
pub use ps_buffer::Buffer;
use chacha20poly1305::aead::{Aead, KeyInit};
use chacha20poly1305::ChaCha20Poly1305;
use ps_deflate::{compress, decompress};
use ps_ecc::{decode, encode, Codeword, DecodeError};
use ps_hash::Hash;
use ps_pint16::PackedInt;
use std::ops::Deref;
use std::sync::Arc;
pub struct Encrypted {
pub bytes: Buffer,
pub hash: Arc<Hash>,
pub key: Arc<Hash>,
}
const KSIZE: usize = 32;
const NSIZE: usize = 12;
const PARITY: u8 = 12;
pub struct ParsedKey {
key: [u8; KSIZE],
nonce: [u8; NSIZE],
length: usize,
}
pub fn parse_key<K: AsRef<[u8]>>(key: K) -> Result<ParsedKey, ParseKeyError> {
let key = key.as_ref();
let key = if key.len() > ps_hash::HASH_SIZE_TOTAL_BIN {
&ps_base64::decode(key)
} else {
key
};
let len = key.len().min(48);
if len < 34 {
return Err(ParseKeyError::InsufficientKeyLength(len.try_into()?));
}
let parsed = ParsedKey {
key: key[0..32].try_into()?,
length: PackedInt::from_16_bits(key[32..34].try_into()?).to_usize(),
nonce: key[len - NSIZE..len].try_into()?,
};
Ok(parsed)
}
pub fn encrypt<D: AsRef<[u8]>>(data: D) -> Result<Encrypted, EncryptionError> {
let compressed_data = compress(data.as_ref())?;
let hash_of_raw_data = ps_hash::hash(data)?;
let ParsedKey {
key: encryption_key,
length: _,
nonce,
} = parse_key(hash_of_raw_data.as_bytes())?;
let chacha = ChaCha20Poly1305::new(&encryption_key.into());
let encrypted_data = chacha.encrypt(&nonce.into(), compressed_data.as_ref())?;
let bytes = encode(&encrypted_data, PARITY)?;
let hash = Hash::hash(&bytes)?.into();
let encrypted = Encrypted {
bytes,
hash,
key: hash_of_raw_data.into(),
};
Ok(encrypted)
}
pub fn decrypt<D: AsRef<[u8]>, K: AsRef<[u8]>>(data: D, key: K) -> Result<Buffer, DecryptionError> {
let ParsedKey {
key: encryption_key,
length: out_size,
nonce,
} = parse_key(key)?;
let ecc_decoded = extract_encrypted(data.as_ref())?;
let chacha = ChaCha20Poly1305::new(&encryption_key.into());
let compressed_data = chacha.decrypt(&nonce.into(), &ecc_decoded[..])?;
Ok(decompress(&compressed_data, out_size)?)
}
#[inline]
pub fn extract_encrypted(data: &[u8]) -> Result<Codeword, DecodeError> {
decode(data, PARITY)
}
impl AsRef<[u8]> for Encrypted {
fn as_ref(&self) -> &[u8] {
self
}
}
impl Deref for Encrypted {
type Target = [u8];
fn deref(&self) -> &Self::Target {
&self.bytes
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use ps_buffer::ToBuffer;
use ps_hash::hash;
use super::*;
#[test]
fn test_encrypt_and_decrypt() -> Result<(), PsCypherError> {
let original_data = b"Hello, World!";
let encrypted_data = encrypt(original_data)?;
let decrypted_data = decrypt(&encrypted_data.bytes, encrypted_data.key.as_bytes())?;
assert_ne!(
original_data.to_buffer().unwrap(),
encrypted_data.bytes,
"Encryption should modify the data"
);
assert_eq!(
encrypted_data.bytes.len(),
31 + 2 * usize::from(PARITY),
"Encrypted data should be 31 bytes long"
);
assert_eq!(
original_data,
&decrypted_data[..],
"Decryption should reverse encryption"
);
Ok(())
}
fn create_test_key() -> Hash {
hash("Hello, world!").unwrap()
}
#[test]
fn test_parse_key() {
let key_bytes = create_test_key();
let ParsedKey {
key: encryption_key,
length: _,
nonce,
} = parse_key(key_bytes).unwrap();
assert_eq!(encryption_key.len(), 32);
assert_eq!(nonce.len(), 12);
assert_eq!(&encryption_key[0..4], &[220, 186, 155, 106]); assert_eq!(&nonce[0..4], &[46, 215, 220, 44]); }
#[test]
fn test_encrypt_decrypt() {
let data = b"This is some data to encrypt";
let encrypted = encrypt(data).unwrap();
let decrypted = decrypt(&encrypted, encrypted.key.as_bytes()).unwrap();
assert_eq!(&*decrypted, data);
}
#[test]
fn test_encrypt_decrypt_empty_data() {
let data = b"";
let encrypted = encrypt(data).unwrap();
let decrypted = decrypt(&encrypted, encrypted.key.as_bytes()).unwrap();
assert_eq!(&*decrypted, data);
}
#[test]
fn test_encrypt_decrypt_long_data() {
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();
let encrypted = encrypt(data).unwrap();
let decrypted = decrypt(&encrypted, encrypted.key.as_bytes()).unwrap();
assert_eq!(&*decrypted, data);
}
#[test]
fn test_encrypt_decrypt_different_key() {
let data = b"This is some data";
let encrypted = encrypt(data).unwrap();
let different_key = create_test_key();
let result = decrypt(&encrypted, different_key);
assert!(result.is_err());
match result.unwrap_err() {
DecryptionError::ChaChaError => {} _ => panic!("Unexpected error type"),
}
}
#[test]
fn test_encrypt_decrypt_tampered_data() -> Result<(), PsCypherError> {
let data = b"This is some data";
let mut encrypted = encrypt(data).unwrap();
encrypted.bytes[0] ^= 0x01;
let decrypted = decrypt(&encrypted, encrypted.key.as_bytes())?;
assert_eq!(decrypted.slice(..), data);
Ok(())
}
#[test]
fn test_as_ref_encrypted() {
let data = b"Test data";
let encrypted = encrypt(data).unwrap();
let as_ref_data: &[u8] = encrypted.as_ref();
assert_eq!(as_ref_data, &*encrypted);
assert_eq!(as_ref_data, &encrypted.bytes[..]);
}
#[test]
fn test_deref_encrypted() {
let data = b"More test data";
let encrypted = encrypt(data).unwrap();
let deref_data: &[u8] = &encrypted; assert_eq!(deref_data, &encrypted.bytes[..]);
}
#[test]
fn test_key_from_hash() {
let data = b"Test data for key derivation";
let h = hash(data).unwrap();
let ParsedKey {
key,
length: _,
nonce: _,
} = parse_key(h.as_bytes()).unwrap();
assert_eq!(key.len(), 32);
}
#[test]
fn test_encrypt_large_data() {
let data = vec![b'A'; 1024 * 1024];
let encrypted = encrypt(&data).unwrap();
let decrypted = decrypt(&encrypted, encrypted.key.as_bytes()).unwrap();
assert_eq!(&*decrypted, &data[..]);
}
#[test]
fn test_ps_cypher_error_display() {
let data = b"test";
let encrypted = encrypt(data).unwrap();
let bad_key = b"invalid_key";
let result = decrypt(&encrypted, bad_key);
if let Err(e) = result {
let error_message = format!("{e}");
assert_eq!(error_message, "Key length of 11 is insufficient."); } else {
panic!("Expected an error, but got success");
}
}
#[test]
fn test_ps_cypher_error_source() {
let data = b"test";
let encrypted = encrypt(data).unwrap();
let bad_key = b"invalid_key";
let result = decrypt(&encrypted, bad_key);
if let Err(e) = result {
let source = std::error::Error::source(&e);
if let Some(err) = source {
let _ = format!("{err}"); }
} else {
panic!("Expected an error, but got success");
}
}
}