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}