Coverage Report

Created: 2025-07-18 06:08

/rust/registry/src/index.crates.io-6f17d22bba15001f/ring-0.17.14/src/aead/aes.rs
Line
Count
Source (jump to first uncovered line)
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
0
95
0
        let _ = cpu_features;
96
0
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
}