use crate::errors;
use ring::aead;
pub const NONCE_SIZE: usize = 12;
fn _check_key(
algo: &'static aead::Algorithm,
key: &[u8],
) -> Result<(), errors::Error> {
if key.len() != algo.key_len() {
return Err(errors::Error::KeySizeMismatch);
}
Ok(())
}
fn _check_in_out(
algo: &'static aead::Algorithm,
in_out: &[u8],
) -> Result<(), errors::Error> {
if in_out.len() < algo.tag_len() {
return Err(errors::Error::BufferTooSmall);
}
Ok(())
}
pub fn seal_in_place(
algo: &'static aead::Algorithm,
nonce: [u8; NONCE_SIZE],
aad: &[u8],
key: &[u8],
in_out: &mut [u8],
) -> Result<usize, errors::Error> {
_check_key(algo, key)?;
_check_in_out(algo, in_out)?;
let tag_size = algo.tag_len();
let plaintext_size: usize = in_out.len() - tag_size;
let plaintext = &mut in_out[..plaintext_size];
let unbound_key = aead::UnboundKey::new(algo, key).unwrap();
let key = aead::LessSafeKey::new(unbound_key);
let nonce = aead::Nonce::assume_unique_for_key(nonce);
let aad = aead::Aad::from(aad);
let res = key.seal_in_place_separate_tag(nonce, aad, plaintext);
match res {
Ok(t) => {
let tag = &mut in_out[plaintext_size..];
tag.copy_from_slice(t.as_ref());
Ok(plaintext_size)
}
Err(error) => panic!("Error during sealing: {:?}", error),
}
}
pub fn open_in_place(
algo: &'static aead::Algorithm,
nonce: [u8; NONCE_SIZE],
aad: &[u8],
key: &[u8],
in_out: &mut [u8],
) -> Result<usize, errors::Error> {
_check_key(algo, key)?;
_check_in_out(algo, in_out)?;
let unbound_key = aead::UnboundKey::new(algo, key).unwrap();
let key = aead::LessSafeKey::new(unbound_key);
let nonce = aead::Nonce::assume_unique_for_key(nonce);
let aad = aead::Aad::from(aad);
let res = key.open_in_place(nonce, aad, in_out);
match res {
Ok(plaintext) => Ok(plaintext.len()),
Err(_) => Err(errors::Error::DecryptionError),
}
}
#[cfg(test)]
mod tests {
use super::*;
const BUF_SIZE: usize = 36;
#[test]
fn test_key() {
for algo in &[&aead::AES_256_GCM, &aead::CHACHA20_POLY1305] {
for key in &[
vec![],
vec![0; 1],
vec![0; algo.key_len() - 1],
vec![0; algo.key_len() + 1],
] {
assert_eq!(
_check_key(algo, key),
Err(errors::Error::KeySizeMismatch)
);
}
}
}
#[test]
fn test_in_out() {
for algo in &[&aead::AES_256_GCM, &aead::CHACHA20_POLY1305] {
for in_out in &[vec![], vec![0; 1], vec![0; algo.tag_len() - 1]] {
assert_eq!(
_check_in_out(algo, in_out),
Err(errors::Error::BufferTooSmall)
);
}
}
}
fn _test_seal_open(algo: &'static aead::Algorithm) {
let nonce = [1; 12];
let aad = [2; 9];
let key = vec![3; algo.key_len()];
let mut in_out: [u8; BUF_SIZE];
let mut res: Result<usize, errors::Error>;
let plaintext_size = BUF_SIZE - algo.tag_len();
let exp_res = Ok(plaintext_size);
let dec_err = Err(errors::Error::DecryptionError);
let buf_err = Err(errors::Error::BufferTooSmall);
let key_err = Err(errors::Error::KeySizeMismatch);
let seal = || {
let r;
let mut _in_out = [4; BUF_SIZE];
r = seal_in_place(algo, nonce.clone(), &aad, &key, &mut _in_out);
assert_eq!(r, exp_res);
_in_out
};
in_out = seal();
let mut bad_nonce = nonce.clone();
bad_nonce[0] = 9;
res = open_in_place(algo, bad_nonce.clone(), &aad, &key, &mut in_out);
assert_eq!(res, dec_err);
in_out = seal();
let mut bad_aad = aad.clone();
bad_aad[0] = 9;
res = open_in_place(algo, nonce.clone(), &bad_aad, &key, &mut in_out);
assert_eq!(res, dec_err);
in_out = seal();
let mut bad_key = key.clone();
bad_key[0] = 9;
res = open_in_place(algo, nonce.clone(), &aad, &bad_key, &mut in_out);
assert_eq!(res, dec_err);
in_out = seal();
let mut bad_in_out = in_out.clone();
bad_in_out[0] = 9;
res = open_in_place(algo, nonce.clone(), &aad, &key, &mut bad_in_out);
assert_eq!(res, dec_err);
in_out = seal();
let mut bad_in_out = in_out.clone();
bad_in_out[in_out.len() - 1] = 9;
res = open_in_place(algo, nonce.clone(), &aad, &key, &mut bad_in_out);
assert_eq!(res, dec_err);
res = seal_in_place(algo, nonce.clone(), &aad, &key, &mut []);
assert_eq!(res, buf_err);
res = open_in_place(algo, nonce.clone(), &aad, &key, &mut []);
assert_eq!(res, buf_err);
res = seal_in_place(algo, nonce.clone(), &aad, &[], &mut in_out);
assert_eq!(res, key_err);
res = open_in_place(algo, nonce.clone(), &aad, &[], &mut in_out);
assert_eq!(res, key_err);
let algo2: &'static aead::Algorithm;
if algo == &aead::AES_256_GCM {
algo2 = &aead::CHACHA20_POLY1305;
} else {
algo2 = &aead::AES_256_GCM;
}
in_out = seal();
res = open_in_place(algo2, nonce.clone(), &aad, &key, &mut in_out);
assert_eq!(res, dec_err);
in_out = seal();
res = open_in_place(algo, nonce.clone(), &aad, &key, &mut in_out);
assert_eq!(res, exp_res);
assert_eq!(in_out[..res.unwrap()], vec![4u8; res.unwrap()][..]);
}
#[test]
fn test_seal_open_aes() {
_test_seal_open(&aead::AES_256_GCM);
}
#[test]
fn test_seal_open_chacha20() {
_test_seal_open(&aead::CHACHA20_POLY1305);
}
}