use crate::aead;
use crate::errors;
use crate::metadata;
use crate::pbkdf2;
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct RingCryptor<'a> {
aad: &'a [u8],
}
impl<'a> RingCryptor<'a> {
pub fn new() -> Self {
Self { aad: &[] }
}
pub fn with_aad(self, aad: &'a [u8]) -> Self {
Self { aad }
}
pub fn get_key_size(enc_algo: &metadata::EncryptionAlgorithm) -> usize {
match enc_algo {
metadata::EncryptionAlgorithm::AES256GCM(_) => {
ring::aead::AES_256_GCM.key_len()
}
metadata::EncryptionAlgorithm::ChaCha20Poly1305(_) => {
ring::aead::CHACHA20_POLY1305.key_len()
}
}
}
pub fn verify_key_size(
enc_algo: &metadata::EncryptionAlgorithm,
key: &[u8],
) -> Result<(), errors::Error> {
if key.len() != Self::get_key_size(enc_algo) {
return Err(errors::Error::KeySizeMismatch);
}
Ok(())
}
fn _get_enc_info(
&self,
enc_algo: &metadata::EncryptionAlgorithm,
) -> (&'static ring::aead::Algorithm, [u8; aead::NONCE_SIZE]) {
match enc_algo {
metadata::EncryptionAlgorithm::AES256GCM(meta) => {
(&ring::aead::AES_256_GCM, meta.nonce)
}
metadata::EncryptionAlgorithm::ChaCha20Poly1305(meta) => {
(&ring::aead::CHACHA20_POLY1305, meta.nonce)
}
}
}
pub fn derive_key_no_alloc(
meta: &metadata::Metadata,
secret: &[u8],
key: &mut [u8],
) -> Result<(), errors::Error> {
Self::verify_key_size(&meta.enc_algo, key)?;
match meta.key_deriv_algo {
metadata::KeyDerivationAlgorithm::None => {
Self::verify_key_size(&meta.enc_algo, secret)?;
key.copy_from_slice(secret);
}
metadata::KeyDerivationAlgorithm::PBKDF2(meta) => {
let algo = match meta.hash_fn {
metadata::HashFunction::SHA256 => {
ring::pbkdf2::PBKDF2_HMAC_SHA256
}
metadata::HashFunction::SHA384 => {
ring::pbkdf2::PBKDF2_HMAC_SHA384
}
metadata::HashFunction::SHA512 => {
ring::pbkdf2::PBKDF2_HMAC_SHA512
}
};
pbkdf2::derive_key(
algo,
meta.iterations,
&meta.salt,
secret,
key,
)?;
}
};
Ok(())
}
pub fn derive_key(
meta: &metadata::Metadata,
secret: &[u8],
) -> Result<zeroize::Zeroizing<Vec<u8>>, errors::Error> {
let key_size = Self::get_key_size(&meta.enc_algo);
let mut key = zeroize::Zeroizing::new(vec![0u8; key_size]);
Self::derive_key_no_alloc(meta, secret, &mut key)?;
Ok(key)
}
fn _seal_in_place(
&self,
enc_algo: &metadata::EncryptionAlgorithm,
key: &[u8],
buf: &mut [u8],
) -> Result<usize, errors::Error> {
Self::verify_key_size(enc_algo, key)?;
let (algo, nonce) = self._get_enc_info(enc_algo);
aead::seal_in_place(algo, nonce, self.aad, key, buf)
}
fn _open_in_place(
&self,
enc_algo: &metadata::EncryptionAlgorithm,
key: &[u8],
buf: &mut [u8],
) -> Result<usize, errors::Error> {
Self::verify_key_size(enc_algo, key)?;
let (algo, nonce) = self._get_enc_info(enc_algo);
aead::open_in_place(algo, nonce, self.aad, key, buf)
}
pub fn seal_in_place(
&self,
meta: &metadata::Metadata,
key: &[u8],
buf: &mut [u8],
) -> Result<usize, errors::Error> {
self._seal_in_place(&meta.enc_algo, &key, buf)
}
pub fn seal_with_meta(
&self,
meta: &metadata::Metadata,
key: &[u8],
plaintext: &[u8],
) -> Result<Vec<u8>, errors::Error> {
let (mut buf, meta_size) = meta.to_buf();
let mut ciphertext = &mut buf[meta_size..];
ciphertext[..plaintext.len()].copy_from_slice(plaintext);
let _ = self.seal_in_place(meta, key, &mut ciphertext)?;
Ok(buf)
}
pub fn seal_with_key(
&self,
key: &[u8],
plaintext: &[u8],
) -> Result<Vec<u8>, errors::Error> {
let meta = metadata::Metadata::generate_for_key(plaintext.len());
self.seal_with_meta(&meta, key, plaintext)
}
pub fn seal_with_passphrase(
&self,
pass: &[u8],
plaintext: &[u8],
) -> Result<Vec<u8>, errors::Error> {
let meta =
metadata::Metadata::generate_for_passphrase(plaintext.len());
let key = Self::derive_key(&meta, pass)?;
self.seal_with_meta(&meta, &key, plaintext)
}
pub fn open_in_place(
&self,
meta: &metadata::Metadata,
key: &[u8],
buf: &mut [u8],
) -> Result<usize, errors::Error> {
self._open_in_place(&meta.enc_algo, &key, buf)
}
pub fn open_with_meta(
&self,
meta: &metadata::Metadata,
key: &[u8],
ciphertext: &[u8],
) -> Result<Vec<u8>, errors::Error> {
let mut buf = ciphertext.to_vec();
let size = self.open_in_place(meta, key, &mut buf)?;
let _ = buf.drain(size..);
Ok(buf)
}
pub fn open(
&self,
secret: &[u8],
buf: &[u8],
) -> Result<Vec<u8>, errors::Error> {
let (meta, meta_size) = metadata::Metadata::from_buf(buf)?;
let ciphertext = &buf[meta_size..];
let key = Self::derive_key(&meta, secret)?;
self.open_with_meta(&meta, &key, ciphertext)
}
}
#[cfg(test)]
mod tests {
use super::*;
enum KeyOpts {
None,
PBKDF2,
}
enum EncOpts {
AES,
ChaCha,
}
fn generate_meta(
size: usize,
key_opts: KeyOpts,
enc_opts: EncOpts,
) -> metadata::Metadata {
let key_algo = match key_opts {
KeyOpts::None => metadata::KeyDerivationAlgorithm::None,
KeyOpts::PBKDF2 => {
let mut key_deriv_meta =
metadata::KeyDerivationMetadata::generate();
key_deriv_meta.iterations = 1;
metadata::KeyDerivationAlgorithm::PBKDF2(key_deriv_meta)
}
};
let enc_meta = metadata::EncryptionMetadata::generate();
let enc_algo = match enc_opts {
EncOpts::AES => metadata::EncryptionAlgorithm::AES256GCM(enc_meta),
EncOpts::ChaCha => {
metadata::EncryptionAlgorithm::ChaCha20Poly1305(enc_meta)
}
};
metadata::Metadata::new(key_algo, enc_algo, size)
}
#[test]
fn test_derive_key() {
let key_err = Err(errors::Error::KeySizeMismatch);
let pass_err = Err(errors::Error::PassphraseTooSmall);
let meta = metadata::Metadata::generate_for_key(0);
let key_size = RingCryptor::get_key_size(&meta.enc_algo);
let secret = vec![9u8; key_size];
let mut key = vec![0u8; key_size];
let res = RingCryptor::derive_key_no_alloc(&meta, &[], &mut key);
assert_eq!(res, key_err);
let res = RingCryptor::derive_key_no_alloc(&meta, &secret, &mut []);
assert_eq!(res, key_err);
let res = RingCryptor::derive_key_no_alloc(&meta, &secret, &mut key);
assert_eq!(res, Ok(()));
assert_eq!(secret, key);
let meta = metadata::Metadata::generate_for_passphrase(0);
let key_size = RingCryptor::get_key_size(&meta.enc_algo);
let secret = vec![9u8; key_size];
let mut key = vec![0u8; key_size];
let res = RingCryptor::derive_key_no_alloc(&meta, &secret, &mut []);
assert_eq!(res, key_err);
let res = RingCryptor::derive_key_no_alloc(&meta, &[], &mut key);
assert_eq!(res, pass_err);
let res = RingCryptor::derive_key_no_alloc(&meta, &secret, &mut key);
assert_eq!(res, Ok(()));
assert!(key != secret);
assert!(key != vec![0u8; key_size]);
}
#[test]
fn test_seal_open_in_place() {
let plaintext = "The cake is a lie".as_bytes();
let aad = "My context".as_bytes();
let buf_err = Err(errors::Error::BufferTooSmall);
let dec_err = Err(errors::Error::DecryptionError);
let key_err = Err(errors::Error::KeySizeMismatch);
let cryptor = RingCryptor::new();
let cryptor_with_aad = RingCryptor::new().with_aad(aad);
let meta1 =
generate_meta(plaintext.len(), KeyOpts::PBKDF2, EncOpts::AES);
let secret1 = "My passphrase 1".as_bytes();
let key_size = RingCryptor::get_key_size(&meta1.enc_algo);
let mut key1 = vec![0u8; key_size];
RingCryptor::derive_key_no_alloc(&meta1, &secret1, &mut key1).unwrap();
let meta2 =
generate_meta(plaintext.len(), KeyOpts::PBKDF2, EncOpts::ChaCha);
let key2 = key1.clone();
let meta3 =
generate_meta(plaintext.len(), KeyOpts::None, EncOpts::AES);
let key3 = vec![9u8; ring::aead::AES_256_GCM.key_len()];
let meta4 =
generate_meta(plaintext.len(), KeyOpts::None, EncOpts::ChaCha);
let key4 = key3.clone();
for (meta, key) in &[
(meta1, &key1),
(meta2, &key2),
(meta3, &key3),
(meta4, &key4),
] {
let err = cryptor.seal_in_place(&meta, key, &mut []);
assert_eq!(buf_err, err);
}
for meta in &[meta1, meta2, meta3, meta4] {
let err = cryptor.seal_in_place(&meta, &[], &mut []);
assert_eq!(key_err, err);
let err = cryptor.seal_in_place(&meta, &secret1, &mut []);
assert_eq!(key_err, err);
}
let mut ciphertexts = Vec::new();
for (meta, key) in &[
(meta1, &key1),
(meta2, &key2),
(meta3, &key3),
(meta4, &key4),
] {
let (mut buf, meta_size) = meta.to_buf();
let mut ciphertext = &mut buf[meta_size..];
ciphertext[..plaintext.len()].copy_from_slice(plaintext);
let res = cryptor.seal_in_place(&meta, key, &mut ciphertext);
assert_eq!(res, Ok(plaintext.len()));
let _ = buf.drain(..meta_size);
ciphertexts.push(buf);
}
for (meta, key) in &[
(meta1, &key1),
(meta2, &key2),
(meta3, &key3),
(meta4, &key4),
] {
let err = cryptor.open_in_place(&meta, key, &mut []);
assert_eq!(buf_err, err);
}
for meta in &[meta1, meta2, meta3, meta4] {
let err = cryptor.open_in_place(&meta, &[], &mut []);
assert_eq!(key_err, err);
}
let wrong_key1 = vec![1u8; ring::aead::AES_256_GCM.key_len()];
let wrong_key2 = vec![2u8; ring::aead::CHACHA20_POLY1305.key_len()];
for (meta, wrong_key, buf) in &[
(meta1, &wrong_key1, &ciphertexts[0]),
(meta2, &wrong_key2, &ciphertexts[1]),
(meta3, &wrong_key1, &ciphertexts[2]),
(meta4, &wrong_key2, &ciphertexts[3]),
] {
let mut buf = buf.to_vec();
let err = cryptor.open_in_place(&meta, wrong_key, &mut buf);
assert_eq!(dec_err, err);
}
for (meta, key, buf) in &mut [
(meta1, &key1, &ciphertexts[0]),
(meta2, &key2, &ciphertexts[1]),
(meta3, &key3, &ciphertexts[2]),
(meta4, &key4, &ciphertexts[3]),
] {
let mut buf = buf.to_vec();
let err = cryptor_with_aad.open_in_place(&meta, key, &mut buf);
assert_eq!(dec_err, err);
}
for (meta, key, buf) in &[
(meta1, &key1, &ciphertexts[1]),
(meta2, &key2, &ciphertexts[0]),
(meta3, &key3, &ciphertexts[3]),
(meta4, &key4, &ciphertexts[2]),
] {
let mut buf = buf.to_vec();
let err = cryptor.open_in_place(&meta, key, &mut buf);
assert_eq!(dec_err, err);
}
let wrong_meta1 =
generate_meta(plaintext.len(), KeyOpts::PBKDF2, EncOpts::AES);
let wrong_meta2 =
generate_meta(plaintext.len(), KeyOpts::PBKDF2, EncOpts::ChaCha);
let wrong_meta3 =
generate_meta(plaintext.len(), KeyOpts::None, EncOpts::AES);
let wrong_meta4 =
generate_meta(plaintext.len(), KeyOpts::None, EncOpts::ChaCha);
for (wrong_meta, key, buf) in &mut [
(wrong_meta1, &key1, &ciphertexts[0]),
(wrong_meta2, &key2, &ciphertexts[1]),
(wrong_meta3, &key3, &ciphertexts[2]),
(wrong_meta4, &key4, &ciphertexts[3]),
] {
let mut buf = buf.to_vec();
let err = cryptor.open_in_place(&wrong_meta, key, &mut buf);
assert_eq!(dec_err, err);
}
for (meta, key, buf) in &[
(meta1, &key1, &ciphertexts[0]),
(meta2, &key2, &ciphertexts[1]),
(meta3, &key3, &ciphertexts[2]),
(meta4, &key4, &ciphertexts[3]),
] {
let mut buf = buf.to_vec();
let res = cryptor.open_in_place(&meta, key, &mut buf);
assert_eq!(res, Ok(plaintext.len()));
assert_eq!(&buf[..plaintext.len()], plaintext);
}
}
#[test]
fn test_seal_open() {
let data = vec![9u8; 9];
let cryptor = RingCryptor::new().with_aad("death".as_bytes());
let res = cryptor.seal_with_passphrase("pass".as_bytes(), &data);
assert!(res.is_ok());
let res = cryptor.open("pass".as_bytes(), &res.unwrap());
assert!(res.is_ok());
assert_eq!(data, res.unwrap());
}
}