1 | // Copyright 2018-2024 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 | use super::{nonce::Nonce, overlapping, quic::Sample, NONCE_LEN}; |
16 | use crate::{ |
17 | constant_time, |
18 | cpu::{self, GetFeature as _}, |
19 | error, |
20 | polyfill::unwrap_const, |
21 | }; |
22 | use cfg_if::cfg_if; |
23 | use core::num::NonZeroU32; |
24 | |
25 | pub(super) use ffi::Counter; |
26 | |
27 | #[macro_use ] |
28 | mod ffi; |
29 | |
30 | mod bs; |
31 | pub(super) mod fallback; |
32 | pub(super) mod hw; |
33 | pub(super) mod vp; |
34 | |
35 | pub type Overlapping<'o> = overlapping::Overlapping<'o, u8>; |
36 | pub type OverlappingPartialBlock<'o> = overlapping::PartialBlock<'o, u8, BLOCK_LEN>; |
37 | |
38 | cfg_if! { |
39 | if #[cfg(any(all(target_arch = "aarch64" , target_endian = "little" ), target_arch = "x86_64" ))] { |
40 | pub(super) use ffi::AES_KEY; |
41 | } else { |
42 | use ffi::AES_KEY; |
43 | } |
44 | } |
45 | |
46 | #[derive (Clone)] |
47 | pub(super) enum Key { |
48 | #[cfg (any( |
49 | all(target_arch = "aarch64" , target_endian = "little" ), |
50 | target_arch = "x86_64" , |
51 | target_arch = "x86" |
52 | ))] |
53 | Hw(hw::Key), |
54 | |
55 | #[cfg (any( |
56 | all(target_arch = "aarch64" , target_endian = "little" ), |
57 | all(target_arch = "arm" , target_endian = "little" ), |
58 | target_arch = "x86" , |
59 | target_arch = "x86_64" |
60 | ))] |
61 | Vp(vp::Key), |
62 | |
63 | Fallback(fallback::Key), |
64 | } |
65 | |
66 | impl Key { |
67 | #[inline ] |
68 | pub fn new( |
69 | bytes: KeyBytes<'_>, |
70 | cpu_features: cpu::Features, |
71 | ) -> Result<Self, error::Unspecified> { |
72 | #[cfg (any( |
73 | all(target_arch = "aarch64" , target_endian = "little" ), |
74 | target_arch = "x86" , |
75 | target_arch = "x86_64" |
76 | ))] |
77 | if let Some(hw_features) = cpu_features.get_feature() { |
78 | return Ok(Self::Hw(hw::Key::new( |
79 | bytes, |
80 | hw_features, |
81 | cpu_features.get_feature(), |
82 | )?)); |
83 | } |
84 | |
85 | #[cfg (any( |
86 | all(target_arch = "aarch64" , target_endian = "little" ), |
87 | all(target_arch = "arm" , target_endian = "little" ), |
88 | target_arch = "x86_64" , |
89 | target_arch = "x86" |
90 | ))] |
91 | if let Some(vp_features) = cpu_features.get_feature() { |
92 | return Ok(Self::Vp(vp::Key::new(bytes, vp_features)?)); |
93 | } |
94 | |
95 | let _ = cpu_features; |
96 | |
97 | Ok(Self::Fallback(fallback::Key::new(bytes)?)) |
98 | } |
99 | |
100 | #[inline ] |
101 | fn encrypt_block(&self, a: Block) -> Block { |
102 | match self { |
103 | #[cfg (any( |
104 | all(target_arch = "aarch64" , target_endian = "little" ), |
105 | target_arch = "x86_64" , |
106 | target_arch = "x86" |
107 | ))] |
108 | Key::Hw(inner) => inner.encrypt_block(a), |
109 | |
110 | #[cfg (any( |
111 | all(target_arch = "aarch64" , target_endian = "little" ), |
112 | all(target_arch = "arm" , target_endian = "little" ), |
113 | target_arch = "x86" , |
114 | target_arch = "x86_64" |
115 | ))] |
116 | Key::Vp(inner) => inner.encrypt_block(a), |
117 | |
118 | Key::Fallback(inner) => inner.encrypt_block(a), |
119 | } |
120 | } |
121 | |
122 | pub fn new_mask(&self, sample: Sample) -> [u8; 5] { |
123 | let [b0, b1, b2, b3, b4, ..] = self.encrypt_block(sample); |
124 | [b0, b1, b2, b3, b4] |
125 | } |
126 | } |
127 | |
128 | pub const AES_128_KEY_LEN: usize = 128 / 8; |
129 | pub const AES_256_KEY_LEN: usize = 256 / 8; |
130 | |
131 | pub enum KeyBytes<'a> { |
132 | AES_128(&'a [u8; AES_128_KEY_LEN]), |
133 | AES_256(&'a [u8; AES_256_KEY_LEN]), |
134 | } |
135 | |
136 | // `Counter` is `ffi::Counter` as its representation is dictated by its use in |
137 | // the FFI. |
138 | impl Counter { |
139 | pub fn one(nonce: Nonce) -> Self { |
140 | let mut value: [u8; 16] = [0u8; BLOCK_LEN]; |
141 | value[..NONCE_LEN].copy_from_slice(src:nonce.as_ref()); |
142 | value[BLOCK_LEN - 1] = 1; |
143 | Self(value) |
144 | } |
145 | |
146 | pub fn increment(&mut self) -> Iv { |
147 | const ONE: NonZeroU32 = unwrap_const(NonZeroU32::new(1)); |
148 | |
149 | let iv: Iv = Iv(self.0); |
150 | self.increment_by_less_safe(ONE); |
151 | iv |
152 | } |
153 | |
154 | fn increment_by_less_safe(&mut self, increment_by: NonZeroU32) { |
155 | let [.., c0: &mut u8, c1: &mut u8, c2: &mut u8, c3: &mut u8] = &mut self.0; |
156 | let old_value: u32 = u32::from_be_bytes([*c0, *c1, *c2, *c3]); |
157 | let new_value: u32 = old_value + increment_by.get(); |
158 | [*c0, *c1, *c2, *c3] = u32::to_be_bytes(self:new_value); |
159 | } |
160 | } |
161 | |
162 | /// The IV for a single block encryption. |
163 | /// |
164 | /// Intentionally not `Clone` to ensure each is used only once. |
165 | pub struct Iv(Block); |
166 | |
167 | impl From<Counter> for Iv { |
168 | fn from(counter: Counter) -> Self { |
169 | Self(counter.0) |
170 | } |
171 | } |
172 | |
173 | pub(super) type Block = [u8; BLOCK_LEN]; |
174 | pub(super) const BLOCK_LEN: usize = 16; |
175 | pub(super) const ZERO_BLOCK: Block = [0u8; BLOCK_LEN]; |
176 | |
177 | pub(super) trait EncryptBlock { |
178 | fn encrypt_block(&self, block: Block) -> Block; |
179 | fn encrypt_iv_xor_block(&self, iv: Iv, block: Block) -> Block; |
180 | } |
181 | |
182 | pub(super) trait EncryptCtr32 { |
183 | fn ctr32_encrypt_within(&self, in_out: Overlapping<'_>, ctr: &mut Counter); |
184 | } |
185 | |
186 | #[allow (dead_code)] |
187 | fn encrypt_block_using_encrypt_iv_xor_block(key: &impl EncryptBlock, block: Block) -> Block { |
188 | key.encrypt_iv_xor_block(Iv(block), ZERO_BLOCK) |
189 | } |
190 | |
191 | fn encrypt_iv_xor_block_using_encrypt_block( |
192 | key: &impl EncryptBlock, |
193 | iv: Iv, |
194 | block: Block, |
195 | ) -> Block { |
196 | let encrypted_iv: [u8; 16] = key.encrypt_block(iv.0); |
197 | constant_time::xor_16(a:encrypted_iv, b:block) |
198 | } |
199 | |
200 | #[allow (dead_code)] |
201 | fn encrypt_iv_xor_block_using_ctr32(key: &impl EncryptCtr32, iv: Iv, mut block: Block) -> Block { |
202 | let mut ctr: Counter = Counter(iv.0); // This is OK because we're only encrypting one block. |
203 | key.ctr32_encrypt_within(in_out:block.as_mut().into(), &mut ctr); |
204 | block |
205 | } |
206 | |
207 | #[cfg (test)] |
208 | mod tests { |
209 | use super::*; |
210 | use crate::test; |
211 | |
212 | #[test ] |
213 | pub fn test_aes() { |
214 | test::run(test_file!("aes_tests.txt" ), |section, test_case| { |
215 | assert_eq!(section, "" ); |
216 | let key = consume_key(test_case , "Key" ); |
217 | let input = test_case .consume_bytes("Input" ); |
218 | let block: Block = input.as_slice().try_into()?; |
219 | let expected_output = test_case .consume_bytes("Output" ); |
220 | |
221 | let output = key.encrypt_block(block); |
222 | assert_eq!(output.as_ref(), &expected_output[..]); |
223 | |
224 | Ok(()) |
225 | }) |
226 | } |
227 | |
228 | fn consume_key(test_case: &mut test::TestCase, name: &str) -> Key { |
229 | let key = test_case .consume_bytes(name); |
230 | let key = &key[..]; |
231 | let key = match key.len() { |
232 | 16 => KeyBytes::AES_128(key.try_into().unwrap()), |
233 | 32 => KeyBytes::AES_256(key.try_into().unwrap()), |
234 | _ => unreachable!(), |
235 | }; |
236 | Key::new(key, cpu::features()).unwrap() |
237 | } |
238 | } |
239 | |