use self::block::{Block, BLOCK_LEN};
use crate::{
constant_time, cpu, error,
polyfill::{self, convert::*},
};
pub use self::{
aes_gcm::{AES_128_GCM, AES_256_GCM},
chacha20_poly1305::CHACHA20_POLY1305,
nonce::{Nonce, NONCE_LEN},
};
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: Nonce, aad: Aad, in_prefix_len: usize,
ciphertext_and_tag_modified_in_place: &'a mut [u8],
) -> Result<&'a mut [u8], error::Unspecified> {
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 Tag(calculated_tag) =
(key.key.algorithm.open)(&key.key.inner, nonce, aad, in_prefix_len, in_out);
if constant_time::verify_slices_are_equal(calculated_tag.as_ref(), 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: Nonce, aad: Aad, 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 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: &mut [u8; TAG_LEN] = tag_out.try_into_()?;
let Tag(tag) = (key.key.algorithm.seal)(&key.key.inner, nonce, aad, in_out);
tag_out.copy_from_slice(tag.as_ref());
Ok(in_out_len + TAG_LEN)
}
#[repr(transparent)]
pub struct Aad<'a>(&'a [u8]);
impl<'a> Aad<'a> {
#[inline]
pub fn from(aad: &'a [u8]) -> Self { Aad(aad) }
}
impl Aad<'static> {
pub fn empty() -> Self { Self::from(&[]) }
}
struct Key {
inner: KeyInner,
algorithm: &'static Algorithm,
}
#[allow(variant_size_differences)]
enum KeyInner {
AesGcm(aes_gcm::Key),
ChaCha20Poly1305(chacha20_poly1305::Key),
}
impl Key {
fn new(algorithm: &'static Algorithm, key_bytes: &[u8]) -> Result<Self, error::Unspecified> {
cpu::cache_detected_features();
Ok(Key {
inner: (algorithm.init)(key_bytes)?,
algorithm,
})
}
#[inline(always)]
fn algorithm(&self) -> &'static Algorithm { self.algorithm }
}
pub struct Algorithm {
init: fn(key: &[u8]) -> Result<KeyInner, error::Unspecified>,
seal: fn(key: &KeyInner, nonce: Nonce, aad: Aad, in_out: &mut [u8]) -> Tag,
open:
fn(key: &KeyInner, nonce: Nonce, aad: Aad, in_prefix_len: usize, in_out: &mut [u8]) -> Tag,
key_len: usize,
id: AlgorithmID,
max_input_len: u64,
}
const fn max_input_len(block_len: usize, overhead_blocks_per_nonce: usize) -> u64 {
((1u64 << 32) - polyfill::u64_from_usize(overhead_blocks_per_nonce))
* polyfill::u64_from_usize(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_via_id!(Algorithm);
#[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 {}
#[must_use]
#[repr(C)]
struct Tag(Block);
const TAG_LEN: usize = BLOCK_LEN;
pub const MAX_TAG_LEN: usize = TAG_LEN;
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(())
}
#[derive(Clone, Copy)]
enum Direction {
Opening { in_prefix_len: usize },
Sealing,
}
mod aes;
mod aes_gcm;
mod block;
mod chacha;
mod chacha20_poly1305;
pub mod chacha20_poly1305_openssh;
mod gcm;
mod nonce;
mod poly1305;
pub mod quic;
mod shift;