use super::DcHeader;
use super::DevoCryptoError;
use super::Result;
use std::convert::TryFrom;
use aead::{
generic_array::{typenum, GenericArray},
Aead, NewAead, Payload,
};
use chacha20poly1305::XChaCha20Poly1305;
use rand::{rngs::OsRng, RngCore};
use sha2::{Digest, Sha256};
use zeroize::Zeroize;
#[derive(Zeroize)]
#[zeroize(drop)]
pub struct DcCiphertextV2 {
nonce: Vec<u8>,
ciphertext: Vec<u8>,
}
impl From<DcCiphertextV2> for Vec<u8> {
fn from(mut cipher: DcCiphertextV2) -> Vec<u8> {
let mut data = Vec::new();
data.append(&mut cipher.nonce);
data.append(&mut cipher.ciphertext);
data
}
}
impl TryFrom<&[u8]> for DcCiphertextV2 {
type Error = DevoCryptoError;
fn try_from(data: &[u8]) -> Result<DcCiphertextV2> {
if data.len() <= 24 {
return Err(DevoCryptoError::InvalidLength);
};
let mut nonce = vec![0u8; 24];
let mut ciphertext = vec![0u8; data.len() - 24];
nonce.copy_from_slice(&data[0..24]);
ciphertext.copy_from_slice(&data[24..]);
Ok(DcCiphertextV2 { nonce, ciphertext })
}
}
impl DcCiphertextV2 {
fn derive_key(secret: &[u8]) -> GenericArray<u8, typenum::U32> {
let mut hasher = Sha256::new();
hasher.input(secret);
hasher.result()
}
pub fn encrypt(data: &[u8], key: &[u8], header: &DcHeader) -> Result<DcCiphertextV2> {
let mut key = DcCiphertextV2::derive_key(&key);
let mut nonce = vec![0u8; 24];
OsRng.fill_bytes(&mut nonce);
let aad: Vec<u8> = (*header).clone().into();
let payload = Payload {
msg: data,
aad: &aad,
};
let cipher = XChaCha20Poly1305::new(key);
let ciphertext = cipher.encrypt(&GenericArray::from_slice(&nonce), payload)?;
key.zeroize();
Ok(DcCiphertextV2 { nonce, ciphertext })
}
pub fn decrypt(&self, key: &[u8], header: &DcHeader) -> Result<Vec<u8>> {
let mut key = DcCiphertextV2::derive_key(&key);
let aad: Vec<u8> = (*header).clone().into();
let payload = Payload {
msg: self.ciphertext.as_slice(),
aad: &aad,
};
let cipher = XChaCha20Poly1305::new(key);
let result = cipher.decrypt(&GenericArray::from_slice(&self.nonce), payload)?;
key.zeroize();
Ok(result)
}
}