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};
23
24/// A key for generating QUIC Header Protection masks.
25pub struct HeaderProtectionKey {
26    inner: KeyInner,
27    algorithm: &'static Algorithm,
28}
29
30#[allow(clippy::large_enum_variant, variant_size_differences)]
31enum KeyInner {
32    Aes(aes::Key),
33    ChaCha20(chacha::Key),
34}
35
36impl From<hkdf::Okm<'_, &'static Algorithm>> for HeaderProtectionKey {
37    fn from(okm: hkdf::Okm<&'static Algorithm>) -> Self {
38        let mut key_bytes = [0; super::MAX_KEY_LEN];
39        let algorithm = *okm.len();
40        let key_bytes = &mut key_bytes[..algorithm.key_len()];
41        okm.fill(key_bytes).unwrap();
42        Self::new(algorithm, key_bytes).unwrap()
43    }
44}
45
46impl HeaderProtectionKey {
47    /// Create a new header protection key.
48    ///
49    /// `key_bytes` must be exactly `algorithm.key_len` bytes long.
50    pub fn new(
51        algorithm: &'static Algorithm,
52        key_bytes: &[u8],
53    ) -> Result<Self, error::Unspecified> {
54        Ok(Self {
55            inner: (algorithm.init)(key_bytes, cpu::features())?,
56            algorithm,
57        })
58    }
59
60    /// Generate a new QUIC Header Protection mask.
61    ///
62    /// `sample` must be exactly `self.algorithm().sample_len()` bytes long.
63    pub fn new_mask(&self, sample: &[u8]) -> Result<[u8; 5], error::Unspecified> {
64        let sample = <&[u8; SAMPLE_LEN]>::try_from(sample)?;
65
66        let out = (self.algorithm.new_mask)(&self.inner, *sample);
67        Ok(out)
68    }
69
70    /// The key's algorithm.
71    #[inline(always)]
72    pub fn algorithm(&self) -> &'static Algorithm {
73        self.algorithm
74    }
75}
76
77const SAMPLE_LEN: usize = super::TAG_LEN;
78
79/// QUIC sample for new key masks
80pub type Sample = [u8; SAMPLE_LEN];
81
82/// A QUIC Header Protection Algorithm.
83pub struct Algorithm {
84    init: fn(key: &[u8], cpu_features: cpu::Features) -> Result<KeyInner, error::Unspecified>,
85
86    new_mask: fn(key: &KeyInner, sample: Sample) -> [u8; 5],
87
88    key_len: usize,
89    id: AlgorithmID,
90}
91
92impl hkdf::KeyType for &'static Algorithm {
93    #[inline]
94    fn len(&self) -> usize {
95        self.key_len()
96    }
97}
98
99impl Algorithm {
100    /// The length of the key.
101    #[inline(always)]
102    pub fn key_len(&self) -> usize {
103        self.key_len
104    }
105
106    /// The required sample length.
107    #[inline(always)]
108    pub fn sample_len(&self) -> usize {
109        SAMPLE_LEN
110    }
111}
112
113derive_debug_via_id!(Algorithm);
114
115#[derive(Debug, Eq, PartialEq)]
116enum AlgorithmID {
117    AES_128,
118    AES_256,
119    CHACHA20,
120}
121
122impl PartialEq for Algorithm {
123    fn eq(&self, other: &Self) -> bool {
124        self.id == other.id
125    }
126}
127
128impl Eq for Algorithm {}
129
130/// AES-128.
131pub static AES_128: Algorithm = Algorithm {
132    key_len: 16,
133    init: aes_init_128,
134    new_mask: aes_new_mask,
135    id: AlgorithmID::AES_128,
136};
137
138/// AES-256.
139pub static AES_256: Algorithm = Algorithm {
140    key_len: 32,
141    init: aes_init_256,
142    new_mask: aes_new_mask,
143    id: AlgorithmID::AES_256,
144};
145
146fn aes_init_128(key: &[u8], cpu_features: cpu::Features) -> Result<KeyInner, error::Unspecified> {
147    let aes_key = aes::Key::new(key, aes::Variant::AES_128, cpu_features)?;
148    Ok(KeyInner::Aes(aes_key))
149}
150
151fn aes_init_256(key: &[u8], cpu_features: cpu::Features) -> Result<KeyInner, error::Unspecified> {
152    let aes_key = aes::Key::new(key, aes::Variant::AES_256, cpu_features)?;
153    Ok(KeyInner::Aes(aes_key))
154}
155
156fn aes_new_mask(key: &KeyInner, sample: Sample) -> [u8; 5] {
157    let aes_key = match key {
158        KeyInner::Aes(key) => key,
159        _ => unreachable!(),
160    };
161
162    aes_key.new_mask(sample)
163}
164
165/// ChaCha20.
166pub static CHACHA20: Algorithm = Algorithm {
167    key_len: chacha::KEY_LEN,
168    init: chacha20_init,
169    new_mask: chacha20_new_mask,
170    id: AlgorithmID::CHACHA20,
171};
172
173fn chacha20_init(key: &[u8], _cpu_features: cpu::Features) -> Result<KeyInner, error::Unspecified> {
174    let chacha20_key: [u8; chacha::KEY_LEN] = key.try_into()?;
175    Ok(KeyInner::ChaCha20(chacha::Key::new(chacha20_key)))
176}
177
178fn chacha20_new_mask(key: &KeyInner, sample: Sample) -> [u8; 5] {
179    let chacha20_key = match key {
180        KeyInner::ChaCha20(key) => key,
181        _ => unreachable!(),
182    };
183
184    chacha20_key.new_mask(sample)
185}