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 
19 use crate::{
20     aead::{aes, chacha},
21     cpu, error, hkdf,
22 };
23 
24 /// A key for generating QUIC Header Protection masks.
25 pub struct HeaderProtectionKey {
26     inner: KeyInner,
27     algorithm: &'static Algorithm,
28 }
29 
30 #[allow(clippy::large_enum_variant, variant_size_differences)]
31 enum KeyInner {
32     Aes(aes::Key),
33     ChaCha20(chacha::Key),
34 }
35 
36 impl From<hkdf::Okm<'_, &'static Algorithm>> for HeaderProtectionKey {
from(okm: hkdf::Okm<&'static Algorithm>) -> Self37     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 
46 impl HeaderProtectionKey {
47     /// Create a new header protection key.
48     ///
49     /// `key_bytes` must be exactly `algorithm.key_len` bytes long.
new( algorithm: &'static Algorithm, key_bytes: &[u8], ) -> Result<Self, error::Unspecified>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.
new_mask(&self, sample: &[u8]) -> Result<[u8; 5], error::Unspecified>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)]
algorithm(&self) -> &'static Algorithm72     pub fn algorithm(&self) -> &'static Algorithm {
73         self.algorithm
74     }
75 }
76 
77 const SAMPLE_LEN: usize = super::TAG_LEN;
78 
79 /// QUIC sample for new key masks
80 pub type Sample = [u8; SAMPLE_LEN];
81 
82 /// A QUIC Header Protection Algorithm.
83 pub 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 
92 impl hkdf::KeyType for &'static Algorithm {
93     #[inline]
len(&self) -> usize94     fn len(&self) -> usize {
95         self.key_len()
96     }
97 }
98 
99 impl Algorithm {
100     /// The length of the key.
101     #[inline(always)]
key_len(&self) -> usize102     pub fn key_len(&self) -> usize {
103         self.key_len
104     }
105 
106     /// The required sample length.
107     #[inline(always)]
sample_len(&self) -> usize108     pub fn sample_len(&self) -> usize {
109         SAMPLE_LEN
110     }
111 }
112 
113 derive_debug_via_id!(Algorithm);
114 
115 #[derive(Debug, Eq, PartialEq)]
116 enum AlgorithmID {
117     AES_128,
118     AES_256,
119     CHACHA20,
120 }
121 
122 impl PartialEq for Algorithm {
eq(&self, other: &Self) -> bool123     fn eq(&self, other: &Self) -> bool {
124         self.id == other.id
125     }
126 }
127 
128 impl Eq for Algorithm {}
129 
130 /// AES-128.
131 pub 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.
139 pub 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 
aes_init_128(key: &[u8], cpu_features: cpu::Features) -> Result<KeyInner, error::Unspecified>146 fn 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 
aes_init_256(key: &[u8], cpu_features: cpu::Features) -> Result<KeyInner, error::Unspecified>151 fn 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 
aes_new_mask(key: &KeyInner, sample: Sample) -> [u8; 5]156 fn 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.
166 pub 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 
chacha20_init(key: &[u8], cpu_features: cpu::Features) -> Result<KeyInner, error::Unspecified>173 fn 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(
176         chacha20_key,
177         cpu_features,
178     )))
179 }
180 
chacha20_new_mask(key: &KeyInner, sample: Sample) -> [u8; 5]181 fn chacha20_new_mask(key: &KeyInner, sample: Sample) -> [u8; 5] {
182     let chacha20_key = match key {
183         KeyInner::ChaCha20(key) => key,
184         _ => unreachable!(),
185     };
186 
187     chacha20_key.new_mask(sample)
188 }
189