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