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
15use super::{Block, KeyBytes, Overlapping, BLOCK_LEN};
16use crate::{bits::BitLength, c, error};
17use core::num::{NonZeroU32, NonZeroUsize};
18
19/// nonce || big-endian counter.
20#[repr(transparent)]
21pub(in super::super) struct Counter(pub(super) [u8; BLOCK_LEN]);
22
23// Keep this in sync with AES_KEY in aes.h.
24#[repr(C)]
25#[derive(Clone)]
26pub(in super::super) struct AES_KEY {
27 pub rd_key: [u32; 4 * (MAX_ROUNDS + 1)],
28 pub rounds: c::uint,
29}
30
31// Keep this in sync with `AES_MAXNR` in aes.h.
32const MAX_ROUNDS: usize = 14;
33
34impl AES_KEY {
35 #[inline]
36 pub(super) unsafe fn new(
37 f: unsafe extern "C" fn(*const u8, BitLength<c::int>, *mut AES_KEY) -> c::int,
38 bytes: KeyBytes<'_>,
39 ) -> Result<Self, error::Unspecified> {
40 let mut key = Self {
41 rd_key: [0; 4 * (MAX_ROUNDS + 1)],
42 rounds: 0,
43 };
44
45 let (bytes, key_bits) = match bytes {
46 KeyBytes::AES_128(bytes) => (&bytes[..], BitLength::from_bits(128)),
47 KeyBytes::AES_256(bytes) => (&bytes[..], BitLength::from_bits(256)),
48 };
49
50 // Unusually, in this case zero means success and non-zero means failure.
51 if 0 == unsafe { f(bytes.as_ptr(), key_bits, &mut key) } {
52 debug_assert_ne!(key.rounds, 0); // Sanity check initialization.
53 Ok(key)
54 } else {
55 Err(error::Unspecified)
56 }
57 }
58}
59
60#[cfg(all(target_arch = "arm", target_endian = "little"))]
61impl AES_KEY {
62 pub(super) unsafe fn derive(
63 f: for<'a> unsafe extern "C" fn(*mut AES_KEY, &'a AES_KEY),
64 src: &Self,
65 ) -> Self {
66 let mut r = AES_KEY {
67 rd_key: [0u32; 4 * (MAX_ROUNDS + 1)],
68 rounds: 0,
69 };
70 unsafe { f(&mut r, src) };
71 r
72 }
73
74 pub(super) fn rounds(&self) -> u32 {
75 self.rounds
76 }
77}
78
79// SAFETY:
80// * The function `$name` must read `bits` bits from `user_key`; `bits` will
81// always be a valid AES key length, i.e. a whole number of bytes.
82// * `$name` must set `key.rounds` to the value expected by the corresponding
83// encryption/decryption functions and return 0, or otherwise must return
84// non-zero to indicate failure.
85// * `$name` may inspect CPU features.
86//
87// In BoringSSL, the C prototypes for these are in
88// crypto/fipsmodule/aes/internal.h.
89macro_rules! set_encrypt_key {
90 ( $name:ident, $key_bytes:expr $(,)? ) => {{
91 use crate::{bits::BitLength, c};
92 prefixed_extern! {
93 fn $name(user_key: *const u8, bits: BitLength<c::int>, key: *mut AES_KEY) -> c::int;
94 }
95 $crate::aead::aes::ffi::AES_KEY::new($name, $key_bytes)
96 }};
97}
98
99macro_rules! encrypt_block {
100 ($name:ident, $block:expr, $key:expr) => {{
101 use crate::aead::aes::{ffi::AES_KEY, Block};
102 prefixed_extern! {
103 fn $name(a: &Block, r: *mut Block, key: &AES_KEY);
104 }
105 $key.encrypt_block($name, $block)
106 }};
107}
108
109impl AES_KEY {
110 #[inline]
111 pub(super) unsafe fn encrypt_block(
112 &self,
113 f: unsafe extern "C" fn(&Block, *mut Block, &AES_KEY),
114 a: Block,
115 ) -> Block {
116 let mut result: MaybeUninit<[u8; 16]> = core::mem::MaybeUninit::uninit();
117 unsafe {
118 f(&a, result.as_mut_ptr(), self);
119 result.assume_init()
120 }
121 }
122}
123
124/// SAFETY:
125/// * The caller must ensure that `$key` was initialized with the
126/// `set_encrypt_key!` invocation that `$name` requires.
127/// * The caller must ensure that fhe function `$name` satisfies the conditions
128/// for the `f` parameter to `ctr32_encrypt_blocks`.
129macro_rules! ctr32_encrypt_blocks {
130 ($name:ident, $in_out:expr, $key:expr, $ctr:expr $(,)? ) => {{
131 use crate::{
132 aead::aes::{ffi::AES_KEY, Counter, BLOCK_LEN},
133 c,
134 };
135 prefixed_extern! {
136 fn $name(
137 input: *const [u8; BLOCK_LEN],
138 output: *mut [u8; BLOCK_LEN],
139 blocks: c::NonZero_size_t,
140 key: &AES_KEY,
141 ivec: &Counter,
142 );
143 }
144 $key.ctr32_encrypt_blocks($name, $in_out, $ctr)
145 }};
146}
147
148impl AES_KEY {
149 /// SAFETY:
150 /// * `f` must not read more than `blocks` blocks from `input`.
151 /// * `f` must write exactly `block` blocks to `output`.
152 /// * In particular, `f` must handle blocks == 0 without reading from `input`
153 /// or writing to `output`.
154 /// * `f` must support the input overlapping with the output exactly or
155 /// with any nonnegative offset `n` (i.e. `input == output.add(n)`);
156 /// `f` does NOT need to support the cases where input < output.
157 /// * `key` must have been initialized with the `set_encrypt_key!` invocation
158 /// that corresponds to `f`.
159 /// * `f` may inspect CPU features.
160 #[inline]
161 pub(super) unsafe fn ctr32_encrypt_blocks(
162 &self,
163 f: unsafe extern "C" fn(
164 input: *const [u8; BLOCK_LEN],
165 output: *mut [u8; BLOCK_LEN],
166 blocks: c::NonZero_size_t,
167 key: &AES_KEY,
168 ivec: &Counter,
169 ),
170 in_out: Overlapping<'_>,
171 ctr: &mut Counter,
172 ) {
173 in_out.with_input_output_len(|input, output, len| {
174 debug_assert_eq!(len % BLOCK_LEN, 0);
175
176 let blocks = match NonZeroUsize::new(len / BLOCK_LEN) {
177 Some(blocks) => blocks,
178 None => {
179 return;
180 }
181 };
182
183 let input: *const [u8; BLOCK_LEN] = input.cast();
184 let output: *mut [u8; BLOCK_LEN] = output.cast();
185 let blocks_u32: NonZeroU32 = blocks.try_into().unwrap();
186
187 // SAFETY:
188 // * `input` points to `blocks` blocks.
189 // * `output` points to space for `blocks` blocks to be written.
190 // * input == output.add(n), where n == src.start, and the caller is
191 // responsible for ensuing this sufficient for `f` to work correctly.
192 // * `blocks` is non-zero so `f` doesn't have to work for empty slices.
193 // * The caller is responsible for ensuring `key` was initialized by the
194 // `set_encrypt_key!` invocation required by `f`.
195 unsafe {
196 f(input, output, blocks, self, ctr);
197 }
198
199 ctr.increment_by_less_safe(blocks_u32);
200 });
201 }
202}
203