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