use super::buffer::TailBuffer;
use crate::{Error, Key};
use aes_gcm_siv::{
aead::{Aead, AeadInPlace, KeyInit},
Aes256GcmSiv, Nonce,
};
use rand::RngCore;
const KEY_OFFSET: usize = 32;
const KEY_LEN: usize = 32;
const NONCE_SIZE: usize = 12;
const TAG_SIZE: usize = 16;
const MIN_PAYLOAD_LEN: usize = NONCE_SIZE + 1 + TAG_SIZE;
#[inline]
pub fn encrypt(plaintext: &[u8], key: &Key) -> Result<Vec<u8>, Error> {
if plaintext.is_empty() {
return Err(Error::EmptyPlaintext);
}
let key_arr: &[u8; KEY_LEN] = (&key.as_bytes()[KEY_OFFSET..KEY_OFFSET + KEY_LEN])
.try_into()
.unwrap();
let mut buffer = Vec::with_capacity(NONCE_SIZE + plaintext.len() + TAG_SIZE);
buffer.resize(NONCE_SIZE, 0);
rand::thread_rng().fill_bytes(&mut buffer[..NONCE_SIZE]);
let cipher = Aes256GcmSiv::new(key_arr.into());
let nonce = Nonce::from(*<&[u8; NONCE_SIZE]>::try_from(&buffer[..NONCE_SIZE]).unwrap());
let ct_with_tag = cipher
.encrypt(&nonce, plaintext)
.map_err(|_| Error::EncryptionFailed)?;
buffer.extend_from_slice(&ct_with_tag);
Ok(buffer)
}
#[inline]
pub fn encrypt_into(plaintext: &[u8], key: &Key, out: &mut Vec<u8>) -> Result<(), Error> {
if plaintext.is_empty() {
return Err(Error::EmptyPlaintext);
}
let key_arr: &[u8; KEY_LEN] = (&key.as_bytes()[KEY_OFFSET..KEY_OFFSET + KEY_LEN])
.try_into()
.unwrap();
let cipher = Aes256GcmSiv::new(key_arr.into());
let mut nonce_bytes = [0u8; NONCE_SIZE];
rand::thread_rng().fill_bytes(&mut nonce_bytes);
out.reserve(NONCE_SIZE + plaintext.len() + TAG_SIZE);
out.extend_from_slice(&nonce_bytes);
let pt_start = out.len();
out.extend_from_slice(plaintext);
let nonce = Nonce::from(nonce_bytes);
let mut tail = TailBuffer::new(out, pt_start);
cipher
.encrypt_in_place(&nonce, b"", &mut tail)
.map_err(|_| Error::EncryptionFailed)
}
#[inline]
pub fn decrypt(ciphertext: &[u8], key: &Key) -> Result<Vec<u8>, Error> {
if ciphertext.len() < MIN_PAYLOAD_LEN {
return Err(Error::PayloadTooShort);
}
let key_arr: &[u8; KEY_LEN] = (&key.as_bytes()[KEY_OFFSET..KEY_OFFSET + KEY_LEN])
.try_into()
.unwrap();
let nonce_bytes = &ciphertext[..NONCE_SIZE];
let ct_with_tag = &ciphertext[NONCE_SIZE..];
let cipher = Aes256GcmSiv::new(key_arr.into());
let nonce = Nonce::from(*<&[u8; NONCE_SIZE]>::try_from(nonce_bytes).unwrap());
cipher
.decrypt(&nonce, ct_with_tag)
.map_err(|_| Error::DecryptionFailed)
}
#[inline]
pub fn decrypt_into(ciphertext: &[u8], key: &Key, out: &mut Vec<u8>) -> Result<(), Error> {
if ciphertext.len() < MIN_PAYLOAD_LEN {
return Err(Error::PayloadTooShort);
}
let key_arr: &[u8; KEY_LEN] = (&key.as_bytes()[KEY_OFFSET..KEY_OFFSET + KEY_LEN])
.try_into()
.unwrap();
let cipher = Aes256GcmSiv::new(key_arr.into());
let mut nonce_bytes = [0u8; NONCE_SIZE];
nonce_bytes.copy_from_slice(&ciphertext[..NONCE_SIZE]);
let nonce = Nonce::from(nonce_bytes);
let ct_start = out.len();
out.extend_from_slice(&ciphertext[NONCE_SIZE..]);
let mut tail = TailBuffer::new(out, ct_start);
cipher
.decrypt_in_place(&nonce, b"", &mut tail)
.map_err(|_| Error::DecryptionFailed)
}