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