use {constant_time, error, init, poly1305, polyfill};
pub use self::chacha20_poly1305::CHACHA20_POLY1305;
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> {
Ok(OpeningKey {
key: Key::new(algorithm, key_bytes)?,
})
}
#[inline(always)]
pub fn algorithm(&self) -> &'static Algorithm { self.key.algorithm() }
}
pub fn open_in_place<'a>(key: &OpeningKey, nonce: &[u8], ad: &[u8],
in_prefix_len: usize,
ciphertext_and_tag_modified_in_place: &'a mut [u8])
-> Result<&'a mut [u8], error::Unspecified> {
let nonce = slice_as_array_ref!(nonce, NONCE_LEN)?;
let ciphertext_and_tag_len =
ciphertext_and_tag_modified_in_place.len()
.checked_sub(in_prefix_len).ok_or(error::Unspecified)?;
let ciphertext_len =
ciphertext_and_tag_len.checked_sub(TAG_LEN).ok_or(error::Unspecified)?;
check_per_nonce_max_bytes(key.key.algorithm, ciphertext_len)?;
let (in_out, received_tag) =
ciphertext_and_tag_modified_in_place
.split_at_mut(in_prefix_len + ciphertext_len);
let mut calculated_tag = [0u8; TAG_LEN];
(key.key.algorithm.open)(&key.key.ctx_buf, nonce, &ad, in_prefix_len,
in_out, &mut calculated_tag)?;
if constant_time::verify_slices_are_equal(&calculated_tag, received_tag)
.is_err() {
for b in &mut in_out[..ciphertext_len] {
*b = 0;
}
return Err(error::Unspecified);
}
Ok(&mut in_out[..ciphertext_len])
}
pub struct SealingKey {
key: Key,
}
impl SealingKey {
#[inline]
pub fn new(algorithm: &'static Algorithm, key_bytes: &[u8])
-> Result<SealingKey, error::Unspecified> {
Ok(SealingKey {
key: Key::new(algorithm, key_bytes)?,
})
}
#[inline(always)]
pub fn algorithm(&self) -> &'static Algorithm { self.key.algorithm() }
}
pub fn seal_in_place(key: &SealingKey, nonce: &[u8], ad: &[u8],
in_out: &mut [u8], out_suffix_capacity: usize)
-> Result<usize, error::Unspecified> {
if out_suffix_capacity < key.key.algorithm.tag_len() {
return Err(error::Unspecified);
}
let nonce = slice_as_array_ref!(nonce, NONCE_LEN)?;
let in_out_len =
in_out.len().checked_sub(out_suffix_capacity).ok_or(error::Unspecified)?;
check_per_nonce_max_bytes(key.key.algorithm, in_out_len)?;
let (in_out, tag_out) = in_out.split_at_mut(in_out_len);
let tag_out = slice_as_array_ref_mut!(tag_out, TAG_LEN)?;
(key.key.algorithm.seal)(&key.key.ctx_buf, nonce, ad, in_out, tag_out)?;
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 new(algorithm: &'static Algorithm, key_bytes: &[u8]) -> Result<Self, error::Unspecified> {
if key_bytes.len() != algorithm.key_len() {
return Err(error::Unspecified);
}
let mut r = Key {
algorithm,
ctx_buf: [0; KEY_CTX_BUF_ELEMS],
};
init::init_once();
{
let ctx_buf_bytes = polyfill::slice::u64_as_u8_mut(&mut r.ctx_buf);
(r.algorithm.init)(ctx_buf_bytes, key_bytes)?;
}
Ok(r)
}
#[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], ad: &[u8],
in_out: &mut [u8], tag_out: &mut [u8; TAG_LEN])
-> Result<(), error::Unspecified>,
open: fn(ctx: &[u64; KEY_CTX_BUF_ELEMS], nonce: &[u8; NONCE_LEN],
ad: &[u8], in_prefix_len: usize, in_out: &mut [u8],
tag_out: &mut [u8; TAG_LEN]) -> Result<(), error::Unspecified>,
key_len: usize,
id: AlgorithmID,
max_input_len: u64,
}
macro_rules! max_input_len {
($block_len:expr, $overhead_blocks_per_nonce:expr) => {
(((1u64 << 32) - $overhead_blocks_per_nonce) * $block_len)
}
}
impl Algorithm {
#[inline(always)]
pub fn key_len(&self) -> usize { self.key_len }
#[inline(always)]
pub fn tag_len(&self) -> usize { TAG_LEN }
#[inline(always)]
pub fn nonce_len(&self) -> usize { NONCE_LEN }
}
derive_debug_from_field!(Algorithm, id);
#[allow(non_camel_case_types)]
#[derive(Debug, Eq, PartialEq)]
enum AlgorithmID {
AES_128_GCM,
AES_256_GCM,
CHACHA20_POLY1305,
}
impl PartialEq for Algorithm {
fn eq(&self, other: &Self) -> bool { self.id == other.id }
}
impl Eq for Algorithm {}
pub const MAX_TAG_LEN: usize = TAG_LEN;
const TAG_LEN: usize = poly1305::TAG_LEN;
const NONCE_LEN: usize = 96 / 8;
fn check_per_nonce_max_bytes(alg: &Algorithm, in_out_len: usize)
-> Result<(), error::Unspecified> {
if polyfill::u64_from_usize(in_out_len) > alg.max_input_len {
return Err(error::Unspecified);
}
Ok(())
}
pub mod chacha20_poly1305_openssh;
mod chacha20_poly1305;
mod aes_gcm;