aws_lc_rs/cipher/
aes.rs

1// Copyright 2018 Brian Smith.
2// SPDX-License-Identifier: ISC
3// Modifications copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
4// SPDX-License-Identifier: Apache-2.0 OR ISC
5
6use crate::aws_lc::{
7    AES_cbc_encrypt, AES_cfb128_encrypt, AES_ctr128_encrypt, AES_ecb_encrypt, AES_DECRYPT,
8    AES_ENCRYPT, AES_KEY,
9};
10use crate::cipher::block::Block;
11use crate::error::Unspecified;
12use crate::fips::indicator_check;
13use zeroize::Zeroize;
14
15use super::{DecryptionContext, EncryptionContext, OperatingMode, SymmetricCipherKey};
16
17/// Length of an AES-128 key in bytes.
18pub const AES_128_KEY_LEN: usize = 16;
19
20/// Length of an AES-192 key in bytes.
21pub const AES_192_KEY_LEN: usize = 24;
22
23/// Length of an AES-256 key in bytes.
24pub const AES_256_KEY_LEN: usize = 32;
25
26/// The number of bytes for an AES-CBC initialization vector (IV)
27pub const AES_CBC_IV_LEN: usize = 16;
28
29/// The number of bytes for an AES-CTR initialization vector (IV)
30pub const AES_CTR_IV_LEN: usize = 16;
31
32/// The number of bytes for an AES-CFB initialization vector (IV)
33pub const AES_CFB_IV_LEN: usize = 16;
34
35pub const AES_BLOCK_LEN: usize = 16;
36
37#[inline]
38pub(crate) fn encrypt_block(aes_key: &AES_KEY, mut block: Block) -> Block {
39    {
40        let block_ref = block.as_mut();
41        debug_assert_eq!(block_ref.len(), AES_BLOCK_LEN);
42        aes_ecb_encrypt(aes_key, block_ref);
43    }
44    block
45}
46
47pub(super) fn encrypt_ctr_mode(
48    key: &SymmetricCipherKey,
49    context: EncryptionContext,
50    in_out: &mut [u8],
51) -> Result<DecryptionContext, Unspecified> {
52    #[allow(clippy::match_wildcard_for_single_variants)]
53    let key = match &key {
54        SymmetricCipherKey::Aes128 { enc_key, .. }
55        | SymmetricCipherKey::Aes192 { enc_key, .. }
56        | SymmetricCipherKey::Aes256 { enc_key, .. } => enc_key,
57        _ => unreachable!(),
58    };
59
60    let mut iv = {
61        let mut iv = [0u8; AES_CTR_IV_LEN];
62        iv.copy_from_slice((&context).try_into()?);
63        iv
64    };
65
66    let mut buffer = [0u8; AES_BLOCK_LEN];
67
68    aes_ctr128_encrypt(key, &mut iv, &mut buffer, in_out);
69    iv.zeroize();
70
71    Ok(context.into())
72}
73
74pub(super) fn decrypt_ctr_mode<'in_out>(
75    key: &SymmetricCipherKey,
76    context: DecryptionContext,
77    in_out: &'in_out mut [u8],
78) -> Result<&'in_out mut [u8], Unspecified> {
79    // it's the same in CTR, just providing a nice named wrapper to match
80    encrypt_ctr_mode(key, context.into(), in_out).map(|_| in_out)
81}
82
83pub(super) fn encrypt_cbc_mode(
84    key: &SymmetricCipherKey,
85    context: EncryptionContext,
86    in_out: &mut [u8],
87) -> Result<DecryptionContext, Unspecified> {
88    #[allow(clippy::match_wildcard_for_single_variants)]
89    let key = match &key {
90        SymmetricCipherKey::Aes128 { enc_key, .. }
91        | SymmetricCipherKey::Aes192 { enc_key, .. }
92        | SymmetricCipherKey::Aes256 { enc_key, .. } => enc_key,
93        _ => unreachable!(),
94    };
95
96    let mut iv = {
97        let mut iv = [0u8; AES_CBC_IV_LEN];
98        iv.copy_from_slice((&context).try_into()?);
99        iv
100    };
101
102    aes_cbc_encrypt(key, &mut iv, in_out);
103    iv.zeroize();
104
105    Ok(context.into())
106}
107
108#[allow(clippy::needless_pass_by_value)]
109pub(super) fn decrypt_cbc_mode<'in_out>(
110    key: &SymmetricCipherKey,
111    context: DecryptionContext,
112    in_out: &'in_out mut [u8],
113) -> Result<&'in_out mut [u8], Unspecified> {
114    #[allow(clippy::match_wildcard_for_single_variants)]
115    let key = match &key {
116        SymmetricCipherKey::Aes128 { dec_key, .. }
117        | SymmetricCipherKey::Aes192 { dec_key, .. }
118        | SymmetricCipherKey::Aes256 { dec_key, .. } => dec_key,
119        _ => unreachable!(),
120    };
121
122    let mut iv = {
123        let mut iv = [0u8; AES_CBC_IV_LEN];
124        iv.copy_from_slice((&context).try_into()?);
125        iv
126    };
127
128    aes_cbc_decrypt(key, &mut iv, in_out);
129    iv.zeroize();
130
131    Ok(in_out)
132}
133
134#[allow(clippy::needless_pass_by_value)]
135pub(super) fn encrypt_cfb_mode(
136    key: &SymmetricCipherKey,
137    mode: OperatingMode,
138    context: EncryptionContext,
139    in_out: &mut [u8],
140) -> Result<DecryptionContext, Unspecified> {
141    #[allow(clippy::match_wildcard_for_single_variants)]
142    let key = match &key {
143        SymmetricCipherKey::Aes128 { enc_key, .. }
144        | SymmetricCipherKey::Aes192 { enc_key, .. }
145        | SymmetricCipherKey::Aes256 { enc_key, .. } => enc_key,
146        _ => unreachable!(),
147    };
148
149    let mut iv = {
150        let mut iv = [0u8; AES_CFB_IV_LEN];
151        iv.copy_from_slice((&context).try_into()?);
152        iv
153    };
154
155    let cfb_encrypt: fn(&AES_KEY, &mut [u8], &mut [u8]) = match mode {
156        // TODO: Hopefully support CFB1, and CFB8
157        OperatingMode::CFB128 => aes_cfb128_encrypt,
158        _ => unreachable!(),
159    };
160
161    cfb_encrypt(key, &mut iv, in_out);
162    iv.zeroize();
163
164    Ok(context.into())
165}
166
167#[allow(clippy::needless_pass_by_value)]
168pub(super) fn decrypt_cfb_mode<'in_out>(
169    key: &SymmetricCipherKey,
170    mode: OperatingMode,
171    context: DecryptionContext,
172    in_out: &'in_out mut [u8],
173) -> Result<&'in_out mut [u8], Unspecified> {
174    #[allow(clippy::match_wildcard_for_single_variants)]
175    let key = match &key {
176        SymmetricCipherKey::Aes128 { enc_key, .. }
177        | SymmetricCipherKey::Aes192 { enc_key, .. }
178        | SymmetricCipherKey::Aes256 { enc_key, .. } => enc_key,
179        _ => unreachable!(),
180    };
181
182    let mut iv = {
183        let mut iv = [0u8; AES_CFB_IV_LEN];
184        iv.copy_from_slice((&context).try_into()?);
185        iv
186    };
187
188    let cfb_decrypt: fn(&AES_KEY, &mut [u8], &mut [u8]) = match mode {
189        // TODO: Hopefully support CFB1, and CFB8
190        OperatingMode::CFB128 => aes_cfb128_decrypt,
191        _ => unreachable!(),
192    };
193
194    cfb_decrypt(key, &mut iv, in_out);
195
196    iv.zeroize();
197
198    Ok(in_out)
199}
200
201#[allow(clippy::needless_pass_by_value, clippy::unnecessary_wraps)]
202pub(super) fn encrypt_ecb_mode(
203    key: &SymmetricCipherKey,
204    context: EncryptionContext,
205    in_out: &mut [u8],
206) -> Result<DecryptionContext, Unspecified> {
207    if !matches!(context, EncryptionContext::None) {
208        unreachable!();
209    }
210
211    #[allow(clippy::match_wildcard_for_single_variants)]
212    let key = match &key {
213        SymmetricCipherKey::Aes128 { enc_key, .. }
214        | SymmetricCipherKey::Aes192 { enc_key, .. }
215        | SymmetricCipherKey::Aes256 { enc_key, .. } => enc_key,
216        _ => unreachable!(),
217    };
218
219    let mut in_out_iter = in_out.chunks_exact_mut(AES_BLOCK_LEN);
220
221    for block in in_out_iter.by_ref() {
222        aes_ecb_encrypt(key, block);
223    }
224
225    // This is a sanity check that should not happen. We validate in `encrypt` that in_out.len() % block_len == 0
226    // for this mode.
227    debug_assert!(in_out_iter.into_remainder().is_empty());
228
229    Ok(context.into())
230}
231
232#[allow(clippy::needless_pass_by_value, clippy::unnecessary_wraps)]
233pub(super) fn decrypt_ecb_mode<'in_out>(
234    key: &SymmetricCipherKey,
235    context: DecryptionContext,
236    in_out: &'in_out mut [u8],
237) -> Result<&'in_out mut [u8], Unspecified> {
238    if !matches!(context, DecryptionContext::None) {
239        unreachable!();
240    }
241
242    #[allow(clippy::match_wildcard_for_single_variants)]
243    let key = match &key {
244        SymmetricCipherKey::Aes128 { dec_key, .. }
245        | SymmetricCipherKey::Aes192 { dec_key, .. }
246        | SymmetricCipherKey::Aes256 { dec_key, .. } => dec_key,
247        _ => unreachable!(),
248    };
249
250    {
251        let mut in_out_iter = in_out.chunks_exact_mut(AES_BLOCK_LEN);
252
253        for block in in_out_iter.by_ref() {
254            aes_ecb_decrypt(key, block);
255        }
256
257        // This is a sanity check hat should not fail. We validate in `decrypt` that in_out.len() % block_len == 0 for
258        // this mode.
259        debug_assert!(in_out_iter.into_remainder().is_empty());
260    }
261
262    Ok(in_out)
263}
264
265fn aes_ecb_encrypt(key: &AES_KEY, in_out: &mut [u8]) {
266    indicator_check!(unsafe {
267        AES_ecb_encrypt(in_out.as_ptr(), in_out.as_mut_ptr(), key, AES_ENCRYPT);
268    });
269}
270
271fn aes_ecb_decrypt(key: &AES_KEY, in_out: &mut [u8]) {
272    indicator_check!(unsafe {
273        AES_ecb_encrypt(in_out.as_ptr(), in_out.as_mut_ptr(), key, AES_DECRYPT);
274    });
275}
276
277fn aes_ctr128_encrypt(key: &AES_KEY, iv: &mut [u8], block_buffer: &mut [u8], in_out: &mut [u8]) {
278    let mut num: u32 = 0;
279
280    indicator_check!(unsafe {
281        AES_ctr128_encrypt(
282            in_out.as_ptr(),
283            in_out.as_mut_ptr(),
284            in_out.len(),
285            key,
286            iv.as_mut_ptr(),
287            block_buffer.as_mut_ptr(),
288            &mut num,
289        );
290    });
291
292    Zeroize::zeroize(block_buffer);
293}
294
295fn aes_cbc_encrypt(key: &AES_KEY, iv: &mut [u8], in_out: &mut [u8]) {
296    indicator_check!(unsafe {
297        AES_cbc_encrypt(
298            in_out.as_ptr(),
299            in_out.as_mut_ptr(),
300            in_out.len(),
301            key,
302            iv.as_mut_ptr(),
303            AES_ENCRYPT,
304        );
305    });
306}
307
308fn aes_cbc_decrypt(key: &AES_KEY, iv: &mut [u8], in_out: &mut [u8]) {
309    indicator_check!(unsafe {
310        AES_cbc_encrypt(
311            in_out.as_ptr(),
312            in_out.as_mut_ptr(),
313            in_out.len(),
314            key,
315            iv.as_mut_ptr(),
316            AES_DECRYPT,
317        );
318    });
319}
320
321fn aes_cfb128_encrypt(key: &AES_KEY, iv: &mut [u8], in_out: &mut [u8]) {
322    let mut num: i32 = 0;
323    indicator_check!(unsafe {
324        AES_cfb128_encrypt(
325            in_out.as_ptr(),
326            in_out.as_mut_ptr(),
327            in_out.len(),
328            key,
329            iv.as_mut_ptr(),
330            &mut num,
331            AES_ENCRYPT,
332        );
333    });
334}
335
336fn aes_cfb128_decrypt(key: &AES_KEY, iv: &mut [u8], in_out: &mut [u8]) {
337    let mut num: i32 = 0;
338    indicator_check!(unsafe {
339        AES_cfb128_encrypt(
340            in_out.as_ptr(),
341            in_out.as_mut_ptr(),
342            in_out.len(),
343            key,
344            iv.as_mut_ptr(),
345            &mut num,
346            AES_DECRYPT,
347        );
348    });
349}