xref: /aosp_15_r20/system/keymint/common/src/crypto/aes.rs (revision 9860b7637a5f185913c70aa0caabe3ecb78441e4)
1*9860b763SAndroid Build Coastguard Worker // Copyright 2022, The Android Open Source Project
2*9860b763SAndroid Build Coastguard Worker //
3*9860b763SAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License");
4*9860b763SAndroid Build Coastguard Worker // you may not use this file except in compliance with the License.
5*9860b763SAndroid Build Coastguard Worker // You may obtain a copy of the License at
6*9860b763SAndroid Build Coastguard Worker //
7*9860b763SAndroid Build Coastguard Worker //     http://www.apache.org/licenses/LICENSE-2.0
8*9860b763SAndroid Build Coastguard Worker //
9*9860b763SAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*9860b763SAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS,
11*9860b763SAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*9860b763SAndroid Build Coastguard Worker // See the License for the specific language governing permissions and
13*9860b763SAndroid Build Coastguard Worker // limitations under the License.
14*9860b763SAndroid Build Coastguard Worker 
15*9860b763SAndroid Build Coastguard Worker //! Functionality related to AES encryption
16*9860b763SAndroid Build Coastguard Worker 
17*9860b763SAndroid Build Coastguard Worker use super::{nonce, Rng};
18*9860b763SAndroid Build Coastguard Worker use crate::{get_tag_value, km_err, tag, try_to_vec, Error};
19*9860b763SAndroid Build Coastguard Worker use alloc::vec::Vec;
20*9860b763SAndroid Build Coastguard Worker use core::convert::TryInto;
21*9860b763SAndroid Build Coastguard Worker use kmr_wire::keymint::{BlockMode, ErrorCode, KeyParam, PaddingMode};
22*9860b763SAndroid Build Coastguard Worker use kmr_wire::KeySizeInBits;
23*9860b763SAndroid Build Coastguard Worker use zeroize::ZeroizeOnDrop;
24*9860b763SAndroid Build Coastguard Worker 
25*9860b763SAndroid Build Coastguard Worker /// Size of an AES block in bytes.
26*9860b763SAndroid Build Coastguard Worker pub const BLOCK_SIZE: usize = 16;
27*9860b763SAndroid Build Coastguard Worker 
28*9860b763SAndroid Build Coastguard Worker /// Size of AES-GCM nonce in bytes.
29*9860b763SAndroid Build Coastguard Worker pub const GCM_NONCE_SIZE: usize = 12; // 96 bits
30*9860b763SAndroid Build Coastguard Worker 
31*9860b763SAndroid Build Coastguard Worker /// AES variant.
32*9860b763SAndroid Build Coastguard Worker #[derive(Clone)]
33*9860b763SAndroid Build Coastguard Worker pub enum Variant {
34*9860b763SAndroid Build Coastguard Worker     /// AES-128
35*9860b763SAndroid Build Coastguard Worker     Aes128,
36*9860b763SAndroid Build Coastguard Worker     /// AES-192
37*9860b763SAndroid Build Coastguard Worker     Aes192,
38*9860b763SAndroid Build Coastguard Worker     /// AES-256
39*9860b763SAndroid Build Coastguard Worker     Aes256,
40*9860b763SAndroid Build Coastguard Worker }
41*9860b763SAndroid Build Coastguard Worker 
42*9860b763SAndroid Build Coastguard Worker /// An AES-128, AES-192 or AES-256 key.
43*9860b763SAndroid Build Coastguard Worker #[derive(Clone, PartialEq, Eq, ZeroizeOnDrop)]
44*9860b763SAndroid Build Coastguard Worker pub enum Key {
45*9860b763SAndroid Build Coastguard Worker     /// AES-128
46*9860b763SAndroid Build Coastguard Worker     Aes128([u8; 16]),
47*9860b763SAndroid Build Coastguard Worker     /// AES-192
48*9860b763SAndroid Build Coastguard Worker     Aes192([u8; 24]),
49*9860b763SAndroid Build Coastguard Worker     /// AES-256
50*9860b763SAndroid Build Coastguard Worker     Aes256([u8; 32]),
51*9860b763SAndroid Build Coastguard Worker }
52*9860b763SAndroid Build Coastguard Worker 
53*9860b763SAndroid Build Coastguard Worker impl Key {
54*9860b763SAndroid Build Coastguard Worker     /// Create a new [`Key`] from raw data, which must be 16, 24 or 32 bytes long.
new(data: Vec<u8>) -> Result<Self, Error>55*9860b763SAndroid Build Coastguard Worker     pub fn new(data: Vec<u8>) -> Result<Self, Error> {
56*9860b763SAndroid Build Coastguard Worker         match data.len() {
57*9860b763SAndroid Build Coastguard Worker             16 => Ok(Key::Aes128(data.try_into().unwrap())), // safe: len checked
58*9860b763SAndroid Build Coastguard Worker             24 => Ok(Key::Aes192(data.try_into().unwrap())), // safe: len checked
59*9860b763SAndroid Build Coastguard Worker             32 => Ok(Key::Aes256(data.try_into().unwrap())), // safe: len checked
60*9860b763SAndroid Build Coastguard Worker             l => Err(km_err!(UnsupportedKeySize, "AES keys must be 16, 24 or 32 bytes not {}", l)),
61*9860b763SAndroid Build Coastguard Worker         }
62*9860b763SAndroid Build Coastguard Worker     }
63*9860b763SAndroid Build Coastguard Worker     /// Create a new [`Key`] from raw data, which must be 16, 24 or 32 bytes long.
new_from(data: &[u8]) -> Result<Self, Error>64*9860b763SAndroid Build Coastguard Worker     pub fn new_from(data: &[u8]) -> Result<Self, Error> {
65*9860b763SAndroid Build Coastguard Worker         Key::new(try_to_vec(data)?)
66*9860b763SAndroid Build Coastguard Worker     }
67*9860b763SAndroid Build Coastguard Worker 
68*9860b763SAndroid Build Coastguard Worker     /// Indicate the size of the key in bits.
size(&self) -> KeySizeInBits69*9860b763SAndroid Build Coastguard Worker     pub fn size(&self) -> KeySizeInBits {
70*9860b763SAndroid Build Coastguard Worker         KeySizeInBits(match self {
71*9860b763SAndroid Build Coastguard Worker             Key::Aes128(_) => 128,
72*9860b763SAndroid Build Coastguard Worker             Key::Aes192(_) => 192,
73*9860b763SAndroid Build Coastguard Worker             Key::Aes256(_) => 256,
74*9860b763SAndroid Build Coastguard Worker         })
75*9860b763SAndroid Build Coastguard Worker     }
76*9860b763SAndroid Build Coastguard Worker }
77*9860b763SAndroid Build Coastguard Worker 
78*9860b763SAndroid Build Coastguard Worker /// Mode of AES plain cipher operation.  Associated value is the nonce.
79*9860b763SAndroid Build Coastguard Worker #[derive(Clone, Copy, Debug)]
80*9860b763SAndroid Build Coastguard Worker pub enum CipherMode {
81*9860b763SAndroid Build Coastguard Worker     /// ECB mode with no padding.
82*9860b763SAndroid Build Coastguard Worker     EcbNoPadding,
83*9860b763SAndroid Build Coastguard Worker     /// ECB mode with PKCS#7 padding.
84*9860b763SAndroid Build Coastguard Worker     EcbPkcs7Padding,
85*9860b763SAndroid Build Coastguard Worker     /// CBC mode with no padding.
86*9860b763SAndroid Build Coastguard Worker     CbcNoPadding {
87*9860b763SAndroid Build Coastguard Worker         /// Nonce to use.
88*9860b763SAndroid Build Coastguard Worker         nonce: [u8; BLOCK_SIZE],
89*9860b763SAndroid Build Coastguard Worker     },
90*9860b763SAndroid Build Coastguard Worker     /// CBC mode with PKCS#7 padding.
91*9860b763SAndroid Build Coastguard Worker     CbcPkcs7Padding {
92*9860b763SAndroid Build Coastguard Worker         /// Nonce to use.
93*9860b763SAndroid Build Coastguard Worker         nonce: [u8; BLOCK_SIZE],
94*9860b763SAndroid Build Coastguard Worker     },
95*9860b763SAndroid Build Coastguard Worker     /// CTR mode with the given nonce.
96*9860b763SAndroid Build Coastguard Worker     Ctr {
97*9860b763SAndroid Build Coastguard Worker         /// Nonce to use.
98*9860b763SAndroid Build Coastguard Worker         nonce: [u8; BLOCK_SIZE],
99*9860b763SAndroid Build Coastguard Worker     },
100*9860b763SAndroid Build Coastguard Worker }
101*9860b763SAndroid Build Coastguard Worker 
102*9860b763SAndroid Build Coastguard Worker /// Mode of AES-GCM operation.  Associated value is the nonce, size of
103*9860b763SAndroid Build Coastguard Worker /// tag is indicated by the variant name.
104*9860b763SAndroid Build Coastguard Worker #[allow(missing_docs)]
105*9860b763SAndroid Build Coastguard Worker #[derive(Clone, Copy, Debug)]
106*9860b763SAndroid Build Coastguard Worker pub enum GcmMode {
107*9860b763SAndroid Build Coastguard Worker     GcmTag12 { nonce: [u8; GCM_NONCE_SIZE] },
108*9860b763SAndroid Build Coastguard Worker     GcmTag13 { nonce: [u8; GCM_NONCE_SIZE] },
109*9860b763SAndroid Build Coastguard Worker     GcmTag14 { nonce: [u8; GCM_NONCE_SIZE] },
110*9860b763SAndroid Build Coastguard Worker     GcmTag15 { nonce: [u8; GCM_NONCE_SIZE] },
111*9860b763SAndroid Build Coastguard Worker     GcmTag16 { nonce: [u8; GCM_NONCE_SIZE] },
112*9860b763SAndroid Build Coastguard Worker }
113*9860b763SAndroid Build Coastguard Worker 
114*9860b763SAndroid Build Coastguard Worker /// Mode of AES operation.
115*9860b763SAndroid Build Coastguard Worker #[derive(Clone, Copy, Debug)]
116*9860b763SAndroid Build Coastguard Worker pub enum Mode {
117*9860b763SAndroid Build Coastguard Worker     /// Perform unauthenticated cipher operation.
118*9860b763SAndroid Build Coastguard Worker     Cipher(CipherMode),
119*9860b763SAndroid Build Coastguard Worker     /// Perform authenticated cipher with additional data operation.
120*9860b763SAndroid Build Coastguard Worker     Aead(GcmMode),
121*9860b763SAndroid Build Coastguard Worker }
122*9860b763SAndroid Build Coastguard Worker 
123*9860b763SAndroid Build Coastguard Worker impl Mode {
124*9860b763SAndroid Build Coastguard Worker     /// Determine the [`Mode`], rejecting invalid parameters. Use `caller_nonce` if provided,
125*9860b763SAndroid Build Coastguard Worker     /// otherwise generate a new nonce using the provided [`Rng`] instance.
new( params: &[KeyParam], caller_nonce: Option<&Vec<u8>>, rng: &mut dyn Rng, ) -> Result<Self, Error>126*9860b763SAndroid Build Coastguard Worker     pub fn new(
127*9860b763SAndroid Build Coastguard Worker         params: &[KeyParam],
128*9860b763SAndroid Build Coastguard Worker         caller_nonce: Option<&Vec<u8>>,
129*9860b763SAndroid Build Coastguard Worker         rng: &mut dyn Rng,
130*9860b763SAndroid Build Coastguard Worker     ) -> Result<Self, Error> {
131*9860b763SAndroid Build Coastguard Worker         let mode = tag::get_block_mode(params)?;
132*9860b763SAndroid Build Coastguard Worker         let padding = tag::get_padding_mode(params)?;
133*9860b763SAndroid Build Coastguard Worker         match mode {
134*9860b763SAndroid Build Coastguard Worker             BlockMode::Ecb => {
135*9860b763SAndroid Build Coastguard Worker                 if caller_nonce.is_some() {
136*9860b763SAndroid Build Coastguard Worker                     return Err(km_err!(InvalidNonce, "nonce unexpectedly provided for AES-ECB"));
137*9860b763SAndroid Build Coastguard Worker                 }
138*9860b763SAndroid Build Coastguard Worker                 match padding {
139*9860b763SAndroid Build Coastguard Worker                     PaddingMode::None => Ok(Mode::Cipher(CipherMode::EcbNoPadding)),
140*9860b763SAndroid Build Coastguard Worker                     PaddingMode::Pkcs7 => Ok(Mode::Cipher(CipherMode::EcbPkcs7Padding)),
141*9860b763SAndroid Build Coastguard Worker                     _ => Err(km_err!(
142*9860b763SAndroid Build Coastguard Worker                         IncompatiblePaddingMode,
143*9860b763SAndroid Build Coastguard Worker                         "expected NONE/PKCS7 padding for AES-ECB"
144*9860b763SAndroid Build Coastguard Worker                     )),
145*9860b763SAndroid Build Coastguard Worker                 }
146*9860b763SAndroid Build Coastguard Worker             }
147*9860b763SAndroid Build Coastguard Worker             BlockMode::Cbc => {
148*9860b763SAndroid Build Coastguard Worker                 let nonce: [u8; BLOCK_SIZE] =
149*9860b763SAndroid Build Coastguard Worker                     nonce(BLOCK_SIZE, caller_nonce, rng)?.try_into().map_err(|_e| {
150*9860b763SAndroid Build Coastguard Worker                         km_err!(InvalidNonce, "want {} byte nonce for AES-CBC", BLOCK_SIZE)
151*9860b763SAndroid Build Coastguard Worker                     })?;
152*9860b763SAndroid Build Coastguard Worker                 match padding {
153*9860b763SAndroid Build Coastguard Worker                     PaddingMode::None => Ok(Mode::Cipher(CipherMode::CbcNoPadding { nonce })),
154*9860b763SAndroid Build Coastguard Worker                     PaddingMode::Pkcs7 => Ok(Mode::Cipher(CipherMode::CbcPkcs7Padding { nonce })),
155*9860b763SAndroid Build Coastguard Worker                     _ => Err(km_err!(
156*9860b763SAndroid Build Coastguard Worker                         IncompatiblePaddingMode,
157*9860b763SAndroid Build Coastguard Worker                         "expected NONE/PKCS7 padding for AES-CBC"
158*9860b763SAndroid Build Coastguard Worker                     )),
159*9860b763SAndroid Build Coastguard Worker                 }
160*9860b763SAndroid Build Coastguard Worker             }
161*9860b763SAndroid Build Coastguard Worker             BlockMode::Ctr => {
162*9860b763SAndroid Build Coastguard Worker                 if padding != PaddingMode::None {
163*9860b763SAndroid Build Coastguard Worker                     return Err(km_err!(
164*9860b763SAndroid Build Coastguard Worker                         IncompatiblePaddingMode,
165*9860b763SAndroid Build Coastguard Worker                         "expected NONE padding for AES-CTR"
166*9860b763SAndroid Build Coastguard Worker                     ));
167*9860b763SAndroid Build Coastguard Worker                 }
168*9860b763SAndroid Build Coastguard Worker                 let nonce: [u8; BLOCK_SIZE] =
169*9860b763SAndroid Build Coastguard Worker                     nonce(BLOCK_SIZE, caller_nonce, rng)?.try_into().map_err(|_e| {
170*9860b763SAndroid Build Coastguard Worker                         km_err!(InvalidNonce, "want {} byte nonce for AES-CTR", BLOCK_SIZE)
171*9860b763SAndroid Build Coastguard Worker                     })?;
172*9860b763SAndroid Build Coastguard Worker                 Ok(Mode::Cipher(CipherMode::Ctr { nonce }))
173*9860b763SAndroid Build Coastguard Worker             }
174*9860b763SAndroid Build Coastguard Worker             BlockMode::Gcm => {
175*9860b763SAndroid Build Coastguard Worker                 if padding != PaddingMode::None {
176*9860b763SAndroid Build Coastguard Worker                     return Err(km_err!(
177*9860b763SAndroid Build Coastguard Worker                         IncompatiblePaddingMode,
178*9860b763SAndroid Build Coastguard Worker                         "expected NONE padding for AES-GCM"
179*9860b763SAndroid Build Coastguard Worker                     ));
180*9860b763SAndroid Build Coastguard Worker                 }
181*9860b763SAndroid Build Coastguard Worker                 let nonce: [u8; GCM_NONCE_SIZE] = nonce(GCM_NONCE_SIZE, caller_nonce, rng)?
182*9860b763SAndroid Build Coastguard Worker                     .try_into()
183*9860b763SAndroid Build Coastguard Worker                     .map_err(|_e| km_err!(InvalidNonce, "want 12 byte nonce for AES-GCM"))?;
184*9860b763SAndroid Build Coastguard Worker                 let tag_len = get_tag_value!(params, MacLength, ErrorCode::InvalidMacLength)?;
185*9860b763SAndroid Build Coastguard Worker                 if tag_len % 8 != 0 {
186*9860b763SAndroid Build Coastguard Worker                     return Err(km_err!(
187*9860b763SAndroid Build Coastguard Worker                         InvalidMacLength,
188*9860b763SAndroid Build Coastguard Worker                         "tag length {} not a multiple of 8",
189*9860b763SAndroid Build Coastguard Worker                         tag_len
190*9860b763SAndroid Build Coastguard Worker                     ));
191*9860b763SAndroid Build Coastguard Worker                 }
192*9860b763SAndroid Build Coastguard Worker                 match tag_len / 8 {
193*9860b763SAndroid Build Coastguard Worker                     12 => Ok(Mode::Aead(GcmMode::GcmTag12 { nonce })),
194*9860b763SAndroid Build Coastguard Worker                     13 => Ok(Mode::Aead(GcmMode::GcmTag13 { nonce })),
195*9860b763SAndroid Build Coastguard Worker                     14 => Ok(Mode::Aead(GcmMode::GcmTag14 { nonce })),
196*9860b763SAndroid Build Coastguard Worker                     15 => Ok(Mode::Aead(GcmMode::GcmTag15 { nonce })),
197*9860b763SAndroid Build Coastguard Worker                     16 => Ok(Mode::Aead(GcmMode::GcmTag16 { nonce })),
198*9860b763SAndroid Build Coastguard Worker                     v => Err(km_err!(
199*9860b763SAndroid Build Coastguard Worker                         InvalidMacLength,
200*9860b763SAndroid Build Coastguard Worker                         "want 12-16 byte tag for AES-GCM not {} bytes",
201*9860b763SAndroid Build Coastguard Worker                         v
202*9860b763SAndroid Build Coastguard Worker                     )),
203*9860b763SAndroid Build Coastguard Worker                 }
204*9860b763SAndroid Build Coastguard Worker             }
205*9860b763SAndroid Build Coastguard Worker         }
206*9860b763SAndroid Build Coastguard Worker     }
207*9860b763SAndroid Build Coastguard Worker 
208*9860b763SAndroid Build Coastguard Worker     /// Indicate whether the AES mode is an AEAD.
is_aead(&self) -> bool209*9860b763SAndroid Build Coastguard Worker     pub fn is_aead(&self) -> bool {
210*9860b763SAndroid Build Coastguard Worker         match self {
211*9860b763SAndroid Build Coastguard Worker             Mode::Aead(_) => true,
212*9860b763SAndroid Build Coastguard Worker             Mode::Cipher(_) => false,
213*9860b763SAndroid Build Coastguard Worker         }
214*9860b763SAndroid Build Coastguard Worker     }
215*9860b763SAndroid Build Coastguard Worker }
216*9860b763SAndroid Build Coastguard Worker 
217*9860b763SAndroid Build Coastguard Worker impl GcmMode {
218*9860b763SAndroid Build Coastguard Worker     /// Return the tag length (in bytes) for an AES-GCM mode.
tag_len(&self) -> usize219*9860b763SAndroid Build Coastguard Worker     pub fn tag_len(&self) -> usize {
220*9860b763SAndroid Build Coastguard Worker         match self {
221*9860b763SAndroid Build Coastguard Worker             GcmMode::GcmTag12 { nonce: _ } => 12,
222*9860b763SAndroid Build Coastguard Worker             GcmMode::GcmTag13 { nonce: _ } => 13,
223*9860b763SAndroid Build Coastguard Worker             GcmMode::GcmTag14 { nonce: _ } => 14,
224*9860b763SAndroid Build Coastguard Worker             GcmMode::GcmTag15 { nonce: _ } => 15,
225*9860b763SAndroid Build Coastguard Worker             GcmMode::GcmTag16 { nonce: _ } => 16,
226*9860b763SAndroid Build Coastguard Worker         }
227*9860b763SAndroid Build Coastguard Worker     }
228*9860b763SAndroid Build Coastguard Worker }
229