/src/spdm-rs/external/ring/src/aead/aes.rs
Line  | Count  | Source  | 
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 AUTHOR DISCLAIMS ALL WARRANTIES  | 
8  |  | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF  | 
9  |  | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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  |  |     bb,  | 
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  | 0  |     pub fn new(  | 
69  | 0  |         bytes: KeyBytes<'_>,  | 
70  | 0  |         cpu_features: cpu::Features,  | 
71  | 0  |     ) -> 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  | 0  |         if let Some(hw_features) = cpu_features.get_feature() { | 
78  | 0  |             return Ok(Self::Hw(hw::Key::new(  | 
79  | 0  |                 bytes,  | 
80  | 0  |                 hw_features,  | 
81  | 0  |                 cpu_features.get_feature(),  | 
82  | 0  |             )?));  | 
83  | 0  |         }  | 
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  | 0  |         if let Some(vp_features) = cpu_features.get_feature() { | 
92  | 0  |             return Ok(Self::Vp(vp::Key::new(bytes, vp_features)?));  | 
93  | 0  |         }  | 
94  |  |  | 
95  | 0  |         let _ = cpu_features;  | 
96  |  |  | 
97  | 0  |         Ok(Self::Fallback(fallback::Key::new(bytes)?))  | 
98  | 0  |     }  | 
99  |  |  | 
100  |  |     #[inline]  | 
101  | 0  |     fn encrypt_block(&self, a: Block) -> Block { | 
102  | 0  |         match self { | 
103  |  |             #[cfg(any(  | 
104  |  |                 all(target_arch = "aarch64", target_endian = "little"),  | 
105  |  |                 target_arch = "x86_64",  | 
106  |  |                 target_arch = "x86"  | 
107  |  |             ))]  | 
108  | 0  |             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  | 0  |             Key::Vp(inner) => inner.encrypt_block(a),  | 
117  |  |  | 
118  | 0  |             Key::Fallback(inner) => inner.encrypt_block(a),  | 
119  |  |         }  | 
120  | 0  |     }  | 
121  |  |  | 
122  | 0  |     pub fn new_mask(&self, sample: Sample) -> [u8; 5] { | 
123  | 0  |         let [b0, b1, b2, b3, b4, ..] = self.encrypt_block(sample);  | 
124  | 0  |         [b0, b1, b2, b3, b4]  | 
125  | 0  |     }  | 
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  | 0  |     pub fn one(nonce: Nonce) -> Self { | 
140  | 0  |         let mut value = [0u8; BLOCK_LEN];  | 
141  | 0  |         value[..NONCE_LEN].copy_from_slice(nonce.as_ref());  | 
142  | 0  |         value[BLOCK_LEN - 1] = 1;  | 
143  | 0  |         Self(value)  | 
144  | 0  |     }  | 
145  |  |  | 
146  | 0  |     pub fn increment(&mut self) -> Iv { | 
147  |  |         const ONE: NonZeroU32 = unwrap_const(NonZeroU32::new(1));  | 
148  |  |  | 
149  | 0  |         let iv = Iv(self.0);  | 
150  | 0  |         self.increment_by_less_safe(ONE);  | 
151  | 0  |         iv  | 
152  | 0  |     }  | 
153  |  |  | 
154  | 0  |     pub(super) fn increment_by_less_safe(&mut self, increment_by: NonZeroU32) { | 
155  | 0  |         let [.., c0, c1, c2, c3] = &mut self.0;  | 
156  | 0  |         let old_value: u32 = u32::from_be_bytes([*c0, *c1, *c2, *c3]);  | 
157  | 0  |         let new_value = old_value.wrapping_add(increment_by.get());  | 
158  | 0  |         [*c0, *c1, *c2, *c3] = u32::to_be_bytes(new_value);  | 
159  | 0  |     }  | 
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  | 0  |     fn from(counter: Counter) -> Self { | 
169  | 0  |         Self(counter.0)  | 
170  | 0  |     }  | 
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  | 0  | fn encrypt_block_using_encrypt_iv_xor_block(key: &impl EncryptBlock, block: Block) -> Block { | 
188  | 0  |     key.encrypt_iv_xor_block(Iv(block), ZERO_BLOCK)  | 
189  | 0  | } Unexecuted instantiation: ring::aead::aes::encrypt_block_using_encrypt_iv_xor_block::<ring::aead::aes::hw::Key> Unexecuted instantiation: ring::aead::aes::encrypt_block_using_encrypt_iv_xor_block::<ring::aead::aes::vp::Key>  | 
190  |  |  | 
191  | 0  | fn encrypt_iv_xor_block_using_encrypt_block(  | 
192  | 0  |     key: &impl EncryptBlock,  | 
193  | 0  |     iv: Iv,  | 
194  | 0  |     block: Block,  | 
195  | 0  | ) -> Block { | 
196  | 0  |     let encrypted_iv = key.encrypt_block(iv.0);  | 
197  | 0  |     bb::xor_16(encrypted_iv, block)  | 
198  | 0  | }  | 
199  |  |  | 
200  |  | #[allow(dead_code)]  | 
201  | 0  | fn encrypt_iv_xor_block_using_ctr32(key: &impl EncryptCtr32, iv: Iv, mut block: Block) -> Block { | 
202  | 0  |     let mut ctr = Counter(iv.0); // This is OK because we're only encrypting one block.  | 
203  | 0  |     key.ctr32_encrypt_within(block.as_mut().into(), &mut ctr);  | 
204  | 0  |     block  | 
205  | 0  | } Unexecuted instantiation: ring::aead::aes::encrypt_iv_xor_block_using_ctr32::<ring::aead::aes::hw::Key> Unexecuted instantiation: ring::aead::aes::encrypt_iv_xor_block_using_ctr32::<ring::aead::aes::vp::Key>  | 
206  |  |  | 
207  |  | #[cfg(test)]  | 
208  |  | mod tests { | 
209  |  |     use super::*;  | 
210  |  |     use crate::testutil as test;  | 
211  |  |  | 
212  |  |     #[test]  | 
213  |  |     pub fn test_aes() { | 
214  |  |         test::run(test_vector_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  |  |  | 
240  |  | // These AES-GCM-specific tests are here instead of in `aes_gcm` because  | 
241  |  | // `Counter`'s API isn't visible (enough) to aes_gcm.  | 
242  |  | #[cfg(test)]  | 
243  |  | mod aes_gcm_tests { | 
244  |  |     use super::{super::aes_gcm::MAX_IN_OUT_LEN, *}; | 
245  |  |     use core::num::NonZeroU32;  | 
246  |  |  | 
247  |  |     #[test]  | 
248  |  |     fn test_aes_gcm_counter_blocks_max() { | 
249  |  |         test_aes_gcm_counter_blocks(MAX_IN_OUT_LEN, &[0, 0, 0, 0]);  | 
250  |  |     }  | 
251  |  |  | 
252  |  |     #[test]  | 
253  |  |     fn test_aes_gcm_counter_blocks_max_minus_one() { | 
254  |  |         test_aes_gcm_counter_blocks(MAX_IN_OUT_LEN - BLOCK_LEN, &[0xff, 0xff, 0xff, 0xff]);  | 
255  |  |     }  | 
256  |  |  | 
257  |  |     fn test_aes_gcm_counter_blocks(in_out_len: usize, expected_final_counter: &[u8; 4]) { | 
258  |  |         fn ctr32(ctr: &Counter) -> &[u8; 4] { | 
259  |  |             (&ctr.0[12..]).try_into().unwrap()  | 
260  |  |         }  | 
261  |  |  | 
262  |  |         let rounded_down = in_out_len / BLOCK_LEN;  | 
263  |  |         let blocks = rounded_down + (if in_out_len % BLOCK_LEN == 0 { 0 } else { 1 }); | 
264  |  |         let blocks = u32::try_from(blocks)  | 
265  |  |             .ok()  | 
266  |  |             .and_then(NonZeroU32::new)  | 
267  |  |             .unwrap();  | 
268  |  |  | 
269  |  |         let nonce = Nonce::assume_unique_for_key([1; 12]);  | 
270  |  |         let mut ctr = Counter::one(nonce);  | 
271  |  |         assert_eq!(ctr32(&ctr), &[0, 0, 0, 1]);  | 
272  |  |         let _tag_iv = ctr.increment();  | 
273  |  |         assert_eq!(ctr32(&ctr), &[0, 0, 0, 2]);  | 
274  |  |         ctr.increment_by_less_safe(blocks);  | 
275  |  |  | 
276  |  |         // `MAX_IN_OUT_LEN` is less on 32-bit targets, so we don't even get  | 
277  |  |         // close to wrapping, but run the tests on them anyway.  | 
278  |  |         #[cfg(target_pointer_width = "64")]  | 
279  |  |         assert_eq!(ctr32(&ctr), expected_final_counter);  | 
280  |  |  | 
281  |  |         #[cfg(target_pointer_width = "32")]  | 
282  |  |         let _ = expected_final_counter;  | 
283  |  |     }  | 
284  |  | }  |