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 use core::convert::{TryFrom, TryInto};
24
25 /// A key for generating QUIC Header Protection masks.
26 pub struct HeaderProtectionKey {
27 inner: KeyInner,
28 algorithm: &'static Algorithm,
29 }
30
31 #[allow(clippy::large_enum_variant, variant_size_differences)]
32 enum KeyInner {
33 Aes(aes::Key),
34 ChaCha20(chacha::Key),
35 }
36
37 impl From<hkdf::Okm<'_, &'static Algorithm>> for HeaderProtectionKey {
from(okm: hkdf::Okm<&'static Algorithm>) -> Self38 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
47 impl HeaderProtectionKey {
48 /// Create a new header protection key.
49 ///
50 /// `key_bytes` must be exactly `algorithm.key_len` bytes long.
new( algorithm: &'static Algorithm, key_bytes: &[u8], ) -> Result<Self, error::Unspecified>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.
new_mask(&self, sample: &[u8]) -> Result<[u8; 5], error::Unspecified>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)]
algorithm(&self) -> &'static Algorithm73 pub fn algorithm(&self) -> &'static Algorithm {
74 self.algorithm
75 }
76 }
77
78 const SAMPLE_LEN: usize = super::TAG_LEN;
79
80 /// QUIC sample for new key masks
81 pub type Sample = [u8; SAMPLE_LEN];
82
83 /// A QUIC Header Protection Algorithm.
84 pub 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
93 impl hkdf::KeyType for &'static Algorithm {
94 #[inline]
len(&self) -> usize95 fn len(&self) -> usize {
96 self.key_len()
97 }
98 }
99
100 impl Algorithm {
101 /// The length of the key.
102 #[inline(always)]
key_len(&self) -> usize103 pub fn key_len(&self) -> usize {
104 self.key_len
105 }
106
107 /// The required sample length.
108 #[inline(always)]
sample_len(&self) -> usize109 pub fn sample_len(&self) -> usize {
110 SAMPLE_LEN
111 }
112 }
113
114 derive_debug_via_id!(Algorithm);
115
116 #[derive(Debug, Eq, PartialEq)]
117 enum AlgorithmID {
118 AES_128,
119 AES_256,
120 CHACHA20,
121 }
122
123 impl PartialEq for Algorithm {
eq(&self, other: &Self) -> bool124 fn eq(&self, other: &Self) -> bool {
125 self.id == other.id
126 }
127 }
128
129 impl Eq for Algorithm {}
130
131 /// AES-128.
132 pub 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.
140 pub 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
aes_init_128(key: &[u8], cpu_features: cpu::Features) -> Result<KeyInner, error::Unspecified>147 fn 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
aes_init_256(key: &[u8], cpu_features: cpu::Features) -> Result<KeyInner, error::Unspecified>152 fn 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
aes_new_mask(key: &KeyInner, sample: Sample) -> [u8; 5]157 fn 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.
167 pub 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
chacha20_init(key: &[u8], _todo: cpu::Features) -> Result<KeyInner, error::Unspecified>174 fn 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
chacha20_new_mask(key: &KeyInner, sample: Sample) -> [u8; 5]179 fn 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 }
187