mod chacha20_poly1305;
mod aes_gcm;
use {constant_time, error, init, polyfill};
pub use self::chacha20_poly1305::{CHACHA20_POLY1305, CHACHA20_POLY1305_OLD};
pub use self::aes_gcm::{AES_128_GCM, AES_256_GCM};
pub struct OpeningKey {
key: Key,
}
impl OpeningKey {
#[inline]
pub fn new(algorithm: &'static Algorithm, key_bytes: &[u8])
-> Result<OpeningKey, error::Unspecified> {
let mut key = OpeningKey {
key: Key {
algorithm: algorithm,
ctx_buf: [0; KEY_CTX_BUF_ELEMS]
}
};
try!(key.key.init(key_bytes));
Ok(key)
}
#[inline(always)]
pub fn algorithm(&self) -> &'static Algorithm { self.key.algorithm() }
}
pub fn open_in_place(key: &OpeningKey, nonce: &[u8], in_prefix_len: usize,
in_out: &mut [u8], ad: &[u8])
-> Result<usize, error::Unspecified> {
let nonce = try!(slice_as_array_ref!(nonce, NONCE_LEN));
let ciphertext_and_tag_len =
try!(in_out.len().checked_sub(in_prefix_len)
.ok_or(error::Unspecified));
let ciphertext_len =
try!(ciphertext_and_tag_len.checked_sub(TAG_LEN)
.ok_or(error::Unspecified));
try!(check_per_nonce_max_bytes(ciphertext_len));
let (in_out, received_tag) =
in_out.split_at_mut(in_prefix_len + ciphertext_len);
let mut calculated_tag = [0u8; TAG_LEN];
try!((key.key.algorithm.open)(&key.key.ctx_buf, nonce, in_out,
in_prefix_len, &mut calculated_tag, ad));
try!(constant_time::verify_slices_are_equal(&calculated_tag, received_tag));
Ok(ciphertext_len) }
pub struct SealingKey {
key: Key,
}
impl SealingKey {
#[inline]
pub fn new(algorithm: &'static Algorithm, key_bytes: &[u8])
-> Result<SealingKey, error::Unspecified> {
let mut key = SealingKey {
key: Key {
algorithm: algorithm,
ctx_buf: [0; KEY_CTX_BUF_ELEMS],
}
};
try!(key.key.init(key_bytes));
Ok(key)
}
#[inline(always)]
pub fn algorithm(&self) -> &'static Algorithm { self.key.algorithm() }
}
pub fn seal_in_place(key: &SealingKey, nonce: &[u8], in_out: &mut [u8],
out_suffix_capacity: usize, ad: &[u8])
-> Result<usize, error::Unspecified> {
if out_suffix_capacity < key.key.algorithm.max_overhead_len() {
return Err(error::Unspecified);
}
let nonce = try!(slice_as_array_ref!(nonce, NONCE_LEN));
let in_out_len =
try!(in_out.len().checked_sub(out_suffix_capacity)
.ok_or(error::Unspecified));
try!(check_per_nonce_max_bytes(in_out_len));
let (in_out, tag_out) = in_out.split_at_mut(in_out_len);
let tag_out = try!(slice_as_array_ref_mut!(tag_out, TAG_LEN));
try!((key.key.algorithm.seal)(&key.key.ctx_buf, nonce, in_out, tag_out, ad));
Ok(in_out_len + TAG_LEN)
}
struct Key {
ctx_buf: [u64; KEY_CTX_BUF_ELEMS],
algorithm: &'static Algorithm,
}
const KEY_CTX_BUF_ELEMS: usize = (KEY_CTX_BUF_LEN + 7) / 8;
const KEY_CTX_BUF_LEN: usize = self::aes_gcm::AES_KEY_CTX_BUF_LEN;
impl Key {
fn init(&mut self, key_bytes: &[u8]) -> Result<(), error::Unspecified> {
init::init_once();
if key_bytes.len() != self.algorithm.key_len() {
return Err(error::Unspecified);
}
let ctx_buf_bytes = polyfill::slice::u64_as_u8_mut(&mut self.ctx_buf);
(self.algorithm.init)(ctx_buf_bytes, key_bytes)
}
#[inline(always)]
fn algorithm(&self) -> &'static Algorithm { self.algorithm }
}
pub struct Algorithm {
init: fn(ctx_buf: &mut [u8], key: &[u8]) -> Result<(), error::Unspecified>,
seal: fn(ctx: &[u64; KEY_CTX_BUF_ELEMS], nonce: &[u8; NONCE_LEN],
in_out: &mut [u8], tag_out: &mut [u8; TAG_LEN], ad: &[u8])
-> Result<(), error::Unspecified>,
open: fn(ctx: &[u64; KEY_CTX_BUF_ELEMS], nonce: &[u8; NONCE_LEN],
in_out: &mut [u8], in_prefix_len: usize,
tag_out: &mut [u8; TAG_LEN], ad: &[u8])
-> Result<(), error::Unspecified>,
key_len: usize,
}
impl Algorithm {
#[inline(always)]
pub fn key_len(&self) -> usize { self.key_len }
#[inline(always)]
pub fn max_overhead_len(&self) -> usize { TAG_LEN }
#[inline(always)]
pub fn nonce_len(&self) -> usize { NONCE_LEN }
}
pub const MAX_OVERHEAD_LEN: usize = TAG_LEN;
const TAG_LEN: usize = 128 / 8;
const NONCE_LEN: usize = 96 / 8;
fn check_per_nonce_max_bytes(in_out_len: usize)
-> Result<(), error::Unspecified> {
if polyfill::u64_from_usize(in_out_len) >= (1u64 << 32) * 64 - 64 {
return Err(error::Unspecified)
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::super::{aead, error, test};
use std::vec::Vec;
pub fn test_aead(aead_alg: &'static aead::Algorithm, file_path: &str) {
test_aead_key_sizes(aead_alg);
test_aead_nonce_sizes(aead_alg).unwrap();
test::from_file(file_path, |section, test_case| {
assert_eq!(section, "");
let key_bytes = test_case.consume_bytes("KEY");
let nonce = test_case.consume_bytes("NONCE");
let plaintext = test_case.consume_bytes("IN");
let ad = test_case.consume_bytes("AD");
let mut ct = test_case.consume_bytes("CT");
let tag = test_case.consume_bytes("TAG");
let error = test_case.consume_optional_string("FAILS");
let max_overhead_len = aead_alg.max_overhead_len();
let mut s_in_out = plaintext.clone();
for _ in 0..max_overhead_len {
s_in_out.push(0);
}
let s_key = try!(aead::SealingKey::new(aead_alg, &key_bytes[..]));
let s_result = aead::seal_in_place(&s_key, &nonce[..],
&mut s_in_out[..],
max_overhead_len, &ad);
let o_key = try!(aead::OpeningKey::new(aead_alg, &key_bytes[..]));
ct.extend(tag);
static MINIMAL_IN_PREFIX_LENS: [usize; 36] = [
0,
1,
2,
5,
8,
5 + 8 ,
15, 16, 17,
31, 32, 33,
47, 48, 49,
63, 64, 65,
79, 80, 81,
95, 96, 97,
111, 112, 113,
127, 128, 129,
143, 144, 145,
255, 256, 257, ];
let mut more_comprehensive_in_prefix_lenghts = [0; 4096];
let in_prefix_lengths;
if cfg!(debug_assertions) {
in_prefix_lengths = &MINIMAL_IN_PREFIX_LENS[..];
} else {
for b in 0..more_comprehensive_in_prefix_lenghts.len() {
more_comprehensive_in_prefix_lenghts[b] = b;
}
in_prefix_lengths = &more_comprehensive_in_prefix_lenghts[..];
}
let mut o_in_out = vec![123u8; 4096];
for in_prefix_len in in_prefix_lengths.iter() {
o_in_out.truncate(0);
for _ in 0..*in_prefix_len {
o_in_out.push(123);
}
o_in_out.extend_from_slice(&ct[..]);
let o_result = aead::open_in_place(&o_key, &nonce[..],
*in_prefix_len,
&mut o_in_out[..], &ad);
match error {
None => {
assert_eq!(Ok(ct.len()), s_result);
assert_eq!(&ct[..], &s_in_out[..ct.len()]);
assert_eq!(Ok(plaintext.len()), o_result);
assert_eq!(&plaintext[..], &o_in_out[..plaintext.len()]);
},
Some(ref error) if error == "WRONG_NONCE_LENGTH" => {
assert_eq!(Err(error::Unspecified), s_result);
assert_eq!(Err(error::Unspecified), o_result);
},
Some(error) => {
unreachable!("Unexpected error test case: {}", error);
}
};
}
Ok(())
});
}
fn test_aead_key_sizes(aead_alg: &'static aead::Algorithm) {
let key_len = aead_alg.key_len();
let key_data = vec![0u8; key_len * 2];
assert!(aead::OpeningKey::new(aead_alg, &key_data[..key_len])
.is_ok());
assert!(aead::SealingKey::new(aead_alg, &key_data[..key_len])
.is_ok());
assert!(aead::OpeningKey::new(aead_alg, &key_data[..(key_len - 1)])
.is_err());
assert!(aead::SealingKey::new(aead_alg, &key_data[..(key_len - 1)])
.is_err());
assert!(aead::OpeningKey::new(aead_alg, &key_data[..(key_len + 1)])
.is_err());
assert!(aead::SealingKey::new(aead_alg, &key_data[..(key_len + 1)])
.is_err());
assert!(aead::OpeningKey::new(aead_alg, &key_data[..(key_len / 2)])
.is_err());
assert!(aead::SealingKey::new(aead_alg, &key_data[..(key_len / 2)])
.is_err());
assert!(aead::OpeningKey::new(aead_alg, &key_data[..(key_len * 2)])
.is_err());
assert!(aead::SealingKey::new(aead_alg, &key_data[..(key_len * 2)])
.is_err());
assert!(aead::OpeningKey::new(aead_alg, &[]).is_err());
assert!(aead::SealingKey::new(aead_alg, &[]).is_err());
assert!(aead::OpeningKey::new(aead_alg, &[0]).is_err());
assert!(aead::SealingKey::new(aead_alg, &[0]).is_err());
}
fn test_aead_nonce_sizes(aead_alg: &'static aead::Algorithm)
-> Result<(), error::Unspecified> {
let key_len = aead_alg.key_len;
let key_data = vec![0u8; key_len];
let o_key =
try!(aead::OpeningKey::new(aead_alg, &key_data[..key_len]));
let s_key =
try!(aead::SealingKey::new(aead_alg, &key_data[..key_len]));
let nonce_len = aead_alg.nonce_len();
let nonce = vec![0u8; nonce_len * 2];
let prefix_len = 0;
let suffix_space = aead_alg.max_overhead_len();
let ad: [u8; 0] = [];
let mut to_seal = b"hello, world".to_vec();
for _ in 0..suffix_space {
to_seal.push(0);
}
let to_seal = &to_seal[..];
let mut to_open = Vec::from(to_seal);
let ciphertext_len =
try!(aead::seal_in_place(&s_key, &nonce[..nonce_len], &mut to_open,
suffix_space, &ad));
let to_open = &to_open[..ciphertext_len];
{
let mut in_out = Vec::from(to_seal);
assert!(aead::seal_in_place(&s_key, &nonce[..nonce_len],
&mut in_out, suffix_space, &ad).is_ok());
}
{
let mut in_out = Vec::from(to_open);
assert!(aead::open_in_place(&o_key, &nonce[..nonce_len],
prefix_len, &mut in_out, &ad).is_ok());
}
{
let mut in_out = Vec::from(to_seal);
assert!(aead::seal_in_place(&s_key, &nonce[..(nonce_len - 1)],
&mut in_out, suffix_space, &ad).is_err());
}
{
let mut in_out = Vec::from(to_open);
assert!(aead::open_in_place(&o_key, &nonce[..(nonce_len - 1)],
prefix_len, &mut in_out, &ad).is_err());
}
{
let mut in_out = Vec::from(to_seal);
assert!(aead::seal_in_place(&s_key, &nonce[..(nonce_len + 1)],
&mut in_out, suffix_space, &ad).is_err());
}
{
let mut in_out = Vec::from(to_open);
assert!(aead::open_in_place(&o_key, &nonce[..(nonce_len + 1)],
prefix_len, &mut in_out, &ad).is_err());
}
{
let mut in_out = Vec::from(to_seal);
assert!(aead::seal_in_place(&s_key, &nonce[..(nonce_len / 2)],
&mut in_out, suffix_space, &ad).is_err());
}
{
let mut in_out = Vec::from(to_open);
assert!(aead::open_in_place(&o_key, &nonce[..(nonce_len / 2)],
prefix_len, &mut in_out, &ad).is_err());
}
{
let mut in_out = Vec::from(to_seal);
assert!(aead::seal_in_place(&s_key, &nonce[..(nonce_len * 2)],
&mut in_out, suffix_space, &ad).is_err());
}
{
let mut in_out = Vec::from(to_open);
assert!(aead::open_in_place(&o_key, &nonce[..(nonce_len * 2)],
prefix_len, &mut in_out, &ad).is_err());
}
{
let mut in_out = Vec::from(to_seal);
assert!(aead::seal_in_place(&s_key, &[], &mut in_out, suffix_space,
&ad).is_err());
}
{
let mut in_out = Vec::from(to_open);
assert!(aead::open_in_place(&o_key, &[], prefix_len, &mut in_out,
&ad).is_err());
}
{
let mut in_out = Vec::from(to_seal);
assert!(aead::seal_in_place(&s_key, &nonce[..1], &mut in_out,
suffix_space, &ad).is_err());
}
{
let mut in_out = Vec::from(to_open);
assert!(aead::open_in_place(&o_key, &nonce[..1], prefix_len,
&mut in_out, &ad).is_err());
}
{
let mut in_out = Vec::from(to_seal);
assert!(aead::seal_in_place(&s_key, &nonce[..16], &mut in_out,
suffix_space, &ad).is_err());
}
{
let mut in_out = Vec::from(to_open);
assert!(aead::open_in_place(&o_key, &nonce[..16], prefix_len,
&mut in_out, &ad).is_err());
}
Ok(())
}
}