blob: 1996121f76d23601f57f35b216f3495375b8f3d1 [file] [log] [blame]
// Copyright 2018 Brian Smith.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//! QUIC Header Protection.
//!
//! See draft-ietf-quic-tls.
use crate::{
aead::{aes, chacha},
cpu, error, hkdf,
};
use core::convert::{TryFrom, TryInto};
/// A key for generating QUIC Header Protection masks.
pub struct HeaderProtectionKey {
inner: KeyInner,
algorithm: &'static Algorithm,
}
#[allow(clippy::large_enum_variant, variant_size_differences)]
enum KeyInner {
Aes(aes::Key),
ChaCha20(chacha::Key),
}
impl From<hkdf::Okm<'_, &'static Algorithm>> for HeaderProtectionKey {
fn from(okm: hkdf::Okm<&'static Algorithm>) -> Self {
let mut key_bytes = [0; super::MAX_KEY_LEN];
let algorithm = *okm.len();
let key_bytes = &mut key_bytes[..algorithm.key_len()];
okm.fill(key_bytes).unwrap();
Self::new(algorithm, key_bytes).unwrap()
}
}
impl HeaderProtectionKey {
/// Create a new header protection key.
///
/// `key_bytes` must be exactly `algorithm.key_len` bytes long.
pub fn new(
algorithm: &'static Algorithm,
key_bytes: &[u8],
) -> Result<Self, error::Unspecified> {
Ok(Self {
inner: (algorithm.init)(key_bytes, cpu::features())?,
algorithm,
})
}
/// Generate a new QUIC Header Protection mask.
///
/// `sample` must be exactly `self.algorithm().sample_len()` bytes long.
pub fn new_mask(&self, sample: &[u8]) -> Result<[u8; 5], error::Unspecified> {
let sample = <&[u8; SAMPLE_LEN]>::try_from(sample)?;
let out = (self.algorithm.new_mask)(&self.inner, *sample);
Ok(out)
}
/// The key's algorithm.
#[inline(always)]
pub fn algorithm(&self) -> &'static Algorithm {
self.algorithm
}
}
const SAMPLE_LEN: usize = super::TAG_LEN;
/// QUIC sample for new key masks
pub type Sample = [u8; SAMPLE_LEN];
/// A QUIC Header Protection Algorithm.
pub struct Algorithm {
init: fn(key: &[u8], cpu_features: cpu::Features) -> Result<KeyInner, error::Unspecified>,
new_mask: fn(key: &KeyInner, sample: Sample) -> [u8; 5],
key_len: usize,
id: AlgorithmID,
}
impl hkdf::KeyType for &'static Algorithm {
#[inline]
fn len(&self) -> usize {
self.key_len()
}
}
impl Algorithm {
/// The length of the key.
#[inline(always)]
pub fn key_len(&self) -> usize {
self.key_len
}
/// The required sample length.
#[inline(always)]
pub fn sample_len(&self) -> usize {
SAMPLE_LEN
}
}
derive_debug_via_id!(Algorithm);
#[derive(Debug, Eq, PartialEq)]
enum AlgorithmID {
AES_128,
AES_256,
CHACHA20,
}
impl PartialEq for Algorithm {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl Eq for Algorithm {}
/// AES-128.
pub static AES_128: Algorithm = Algorithm {
key_len: 16,
init: aes_init_128,
new_mask: aes_new_mask,
id: AlgorithmID::AES_128,
};
/// AES-256.
pub static AES_256: Algorithm = Algorithm {
key_len: 32,
init: aes_init_256,
new_mask: aes_new_mask,
id: AlgorithmID::AES_256,
};
fn aes_init_128(key: &[u8], cpu_features: cpu::Features) -> Result<KeyInner, error::Unspecified> {
let aes_key = aes::Key::new(key, aes::Variant::AES_128, cpu_features)?;
Ok(KeyInner::Aes(aes_key))
}
fn aes_init_256(key: &[u8], cpu_features: cpu::Features) -> Result<KeyInner, error::Unspecified> {
let aes_key = aes::Key::new(key, aes::Variant::AES_256, cpu_features)?;
Ok(KeyInner::Aes(aes_key))
}
fn aes_new_mask(key: &KeyInner, sample: Sample) -> [u8; 5] {
let aes_key = match key {
KeyInner::Aes(key) => key,
_ => unreachable!(),
};
aes_key.new_mask(sample)
}
/// ChaCha20.
pub static CHACHA20: Algorithm = Algorithm {
key_len: chacha::KEY_LEN,
init: chacha20_init,
new_mask: chacha20_new_mask,
id: AlgorithmID::CHACHA20,
};
fn chacha20_init(key: &[u8], cpu_features: cpu::Features) -> Result<KeyInner, error::Unspecified> {
let chacha20_key: [u8; chacha::KEY_LEN] = key.try_into()?;
Ok(KeyInner::ChaCha20(chacha::Key::new(
chacha20_key,
cpu_features,
)))
}
fn chacha20_new_mask(key: &KeyInner, sample: Sample) -> [u8; 5] {
let chacha20_key = match key {
KeyInner::ChaCha20(key) => key,
_ => unreachable!(),
};
chacha20_key.new_mask(sample)
}