ring/aead/
quic.rs

1// Copyright 2018 Brian Smith.
2//
3// Permission to use, copy, modify, and/or distribute this software for any
4// purpose with or without fee is hereby granted, provided that the above
5// copyright notice and this permission notice appear in all copies.
6//
7// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
8// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
10// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14
15//! QUIC Header Protection.
16//!
17//! See draft-ietf-quic-tls.
18
19use crate::{
20    aead::{aes, chacha},
21    cpu, error, hkdf,
22};
23use core::convert::{TryFrom, TryInto};
24
25/// A key for generating QUIC Header Protection masks.
26pub struct HeaderProtectionKey {
27    inner: KeyInner,
28    algorithm: &'static Algorithm,
29}
30
31#[allow(clippy::large_enum_variant, variant_size_differences)]
32enum KeyInner {
33    Aes(aes::Key),
34    ChaCha20(chacha::Key),
35}
36
37impl From<hkdf::Okm<'_, &'static Algorithm>> for HeaderProtectionKey {
38    fn from(okm: hkdf::Okm<&'static Algorithm>) -> Self {
39        let mut key_bytes = [0; super::MAX_KEY_LEN];
40        let algorithm = *okm.len();
41        let key_bytes = &mut key_bytes[..algorithm.key_len()];
42        okm.fill(key_bytes).unwrap();
43        Self::new(algorithm, key_bytes).unwrap()
44    }
45}
46
47impl HeaderProtectionKey {
48    /// Create a new header protection key.
49    ///
50    /// `key_bytes` must be exactly `algorithm.key_len` bytes long.
51    pub fn new(
52        algorithm: &'static Algorithm,
53        key_bytes: &[u8],
54    ) -> Result<Self, error::Unspecified> {
55        Ok(Self {
56            inner: (algorithm.init)(key_bytes, cpu::features())?,
57            algorithm,
58        })
59    }
60
61    /// Generate a new QUIC Header Protection mask.
62    ///
63    /// `sample` must be exactly `self.algorithm().sample_len()` bytes long.
64    pub fn new_mask(&self, sample: &[u8]) -> Result<[u8; 5], error::Unspecified> {
65        let sample = <&[u8; SAMPLE_LEN]>::try_from(sample)?;
66
67        let out = (self.algorithm.new_mask)(&self.inner, *sample);
68        Ok(out)
69    }
70
71    /// The key's algorithm.
72    #[inline(always)]
73    pub fn algorithm(&self) -> &'static Algorithm {
74        self.algorithm
75    }
76}
77
78const SAMPLE_LEN: usize = super::TAG_LEN;
79
80/// QUIC sample for new key masks
81pub type Sample = [u8; SAMPLE_LEN];
82
83/// A QUIC Header Protection Algorithm.
84pub struct Algorithm {
85    init: fn(key: &[u8], cpu_features: cpu::Features) -> Result<KeyInner, error::Unspecified>,
86
87    new_mask: fn(key: &KeyInner, sample: Sample) -> [u8; 5],
88
89    key_len: usize,
90    id: AlgorithmID,
91}
92
93impl hkdf::KeyType for &'static Algorithm {
94    #[inline]
95    fn len(&self) -> usize {
96        self.key_len()
97    }
98}
99
100impl Algorithm {
101    /// The length of the key.
102    #[inline(always)]
103    pub fn key_len(&self) -> usize {
104        self.key_len
105    }
106
107    /// The required sample length.
108    #[inline(always)]
109    pub fn sample_len(&self) -> usize {
110        SAMPLE_LEN
111    }
112}
113
114derive_debug_via_id!(Algorithm);
115
116#[derive(Debug, Eq, PartialEq)]
117enum AlgorithmID {
118    AES_128,
119    AES_256,
120    CHACHA20,
121}
122
123impl PartialEq for Algorithm {
124    fn eq(&self, other: &Self) -> bool {
125        self.id == other.id
126    }
127}
128
129impl Eq for Algorithm {}
130
131/// AES-128.
132pub static AES_128: Algorithm = Algorithm {
133    key_len: 16,
134    init: aes_init_128,
135    new_mask: aes_new_mask,
136    id: AlgorithmID::AES_128,
137};
138
139/// AES-256.
140pub static AES_256: Algorithm = Algorithm {
141    key_len: 32,
142    init: aes_init_256,
143    new_mask: aes_new_mask,
144    id: AlgorithmID::AES_256,
145};
146
147fn aes_init_128(key: &[u8], cpu_features: cpu::Features) -> Result<KeyInner, error::Unspecified> {
148    let aes_key = aes::Key::new(key, aes::Variant::AES_128, cpu_features)?;
149    Ok(KeyInner::Aes(aes_key))
150}
151
152fn aes_init_256(key: &[u8], cpu_features: cpu::Features) -> Result<KeyInner, error::Unspecified> {
153    let aes_key = aes::Key::new(key, aes::Variant::AES_256, cpu_features)?;
154    Ok(KeyInner::Aes(aes_key))
155}
156
157fn aes_new_mask(key: &KeyInner, sample: Sample) -> [u8; 5] {
158    let aes_key = match key {
159        KeyInner::Aes(key) => key,
160        _ => unreachable!(),
161    };
162
163    aes_key.new_mask(sample)
164}
165
166/// ChaCha20.
167pub static CHACHA20: Algorithm = Algorithm {
168    key_len: chacha::KEY_LEN,
169    init: chacha20_init,
170    new_mask: chacha20_new_mask,
171    id: AlgorithmID::CHACHA20,
172};
173
174fn chacha20_init(key: &[u8], _todo: cpu::Features) -> Result<KeyInner, error::Unspecified> {
175    let chacha20_key: [u8; chacha::KEY_LEN] = key.try_into()?;
176    Ok(KeyInner::ChaCha20(chacha::Key::from(chacha20_key)))
177}
178
179fn chacha20_new_mask(key: &KeyInner, sample: Sample) -> [u8; 5] {
180    let chacha20_key = match key {
181        KeyInner::ChaCha20(key) => key,
182        _ => unreachable!(),
183    };
184
185    chacha20_key.new_mask(sample)
186}