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 /// An AES-128, AES-192 or AES-256 key. 43 #[derive(Clone, PartialEq, Eq, ZeroizeOnDrop)] 44 pub enum Key { 45 /// AES-128 46 Aes128([u8; 16]), 47 /// AES-192 48 Aes192([u8; 24]), 49 /// AES-256 50 Aes256([u8; 32]), 51 } 52 53 impl Key { 54 /// Create a new [`Key`] from raw data, which must be 16, 24 or 32 bytes long. new(data: Vec<u8>) -> Result<Self, Error>55 pub fn new(data: Vec<u8>) -> Result<Self, Error> { 56 match data.len() { 57 16 => Ok(Key::Aes128(data.try_into().unwrap())), // safe: len checked 58 24 => Ok(Key::Aes192(data.try_into().unwrap())), // safe: len checked 59 32 => Ok(Key::Aes256(data.try_into().unwrap())), // safe: len checked 60 l => Err(km_err!(UnsupportedKeySize, "AES keys must be 16, 24 or 32 bytes not {}", l)), 61 } 62 } 63 /// Create a new [`Key`] from raw data, which must be 16, 24 or 32 bytes long. new_from(data: &[u8]) -> Result<Self, Error>64 pub fn new_from(data: &[u8]) -> Result<Self, Error> { 65 Key::new(try_to_vec(data)?) 66 } 67 68 /// Indicate the size of the key in bits. size(&self) -> KeySizeInBits69 pub fn size(&self) -> KeySizeInBits { 70 KeySizeInBits(match self { 71 Key::Aes128(_) => 128, 72 Key::Aes192(_) => 192, 73 Key::Aes256(_) => 256, 74 }) 75 } 76 } 77 78 /// Mode of AES plain cipher operation. Associated value is the nonce. 79 #[derive(Clone, Copy, Debug)] 80 pub enum CipherMode { 81 /// ECB mode with no padding. 82 EcbNoPadding, 83 /// ECB mode with PKCS#7 padding. 84 EcbPkcs7Padding, 85 /// CBC mode with no padding. 86 CbcNoPadding { 87 /// Nonce to use. 88 nonce: [u8; BLOCK_SIZE], 89 }, 90 /// CBC mode with PKCS#7 padding. 91 CbcPkcs7Padding { 92 /// Nonce to use. 93 nonce: [u8; BLOCK_SIZE], 94 }, 95 /// CTR mode with the given nonce. 96 Ctr { 97 /// Nonce to use. 98 nonce: [u8; BLOCK_SIZE], 99 }, 100 } 101 102 /// Mode of AES-GCM operation. Associated value is the nonce, size of 103 /// tag is indicated by the variant name. 104 #[allow(missing_docs)] 105 #[derive(Clone, Copy, Debug)] 106 pub enum GcmMode { 107 GcmTag12 { nonce: [u8; GCM_NONCE_SIZE] }, 108 GcmTag13 { nonce: [u8; GCM_NONCE_SIZE] }, 109 GcmTag14 { nonce: [u8; GCM_NONCE_SIZE] }, 110 GcmTag15 { nonce: [u8; GCM_NONCE_SIZE] }, 111 GcmTag16 { nonce: [u8; GCM_NONCE_SIZE] }, 112 } 113 114 /// Mode of AES operation. 115 #[derive(Clone, Copy, Debug)] 116 pub enum Mode { 117 /// Perform unauthenticated cipher operation. 118 Cipher(CipherMode), 119 /// Perform authenticated cipher with additional data operation. 120 Aead(GcmMode), 121 } 122 123 impl Mode { 124 /// Determine the [`Mode`], rejecting invalid parameters. Use `caller_nonce` if provided, 125 /// 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 pub fn new( 127 params: &[KeyParam], 128 caller_nonce: Option<&Vec<u8>>, 129 rng: &mut dyn Rng, 130 ) -> Result<Self, Error> { 131 let mode = tag::get_block_mode(params)?; 132 let padding = tag::get_padding_mode(params)?; 133 match mode { 134 BlockMode::Ecb => { 135 if caller_nonce.is_some() { 136 return Err(km_err!(InvalidNonce, "nonce unexpectedly provided for AES-ECB")); 137 } 138 match padding { 139 PaddingMode::None => Ok(Mode::Cipher(CipherMode::EcbNoPadding)), 140 PaddingMode::Pkcs7 => Ok(Mode::Cipher(CipherMode::EcbPkcs7Padding)), 141 _ => Err(km_err!( 142 IncompatiblePaddingMode, 143 "expected NONE/PKCS7 padding for AES-ECB" 144 )), 145 } 146 } 147 BlockMode::Cbc => { 148 let nonce: [u8; BLOCK_SIZE] = 149 nonce(BLOCK_SIZE, caller_nonce, rng)?.try_into().map_err(|_e| { 150 km_err!(InvalidNonce, "want {} byte nonce for AES-CBC", BLOCK_SIZE) 151 })?; 152 match padding { 153 PaddingMode::None => Ok(Mode::Cipher(CipherMode::CbcNoPadding { nonce })), 154 PaddingMode::Pkcs7 => Ok(Mode::Cipher(CipherMode::CbcPkcs7Padding { nonce })), 155 _ => Err(km_err!( 156 IncompatiblePaddingMode, 157 "expected NONE/PKCS7 padding for AES-CBC" 158 )), 159 } 160 } 161 BlockMode::Ctr => { 162 if padding != PaddingMode::None { 163 return Err(km_err!( 164 IncompatiblePaddingMode, 165 "expected NONE padding for AES-CTR" 166 )); 167 } 168 let nonce: [u8; BLOCK_SIZE] = 169 nonce(BLOCK_SIZE, caller_nonce, rng)?.try_into().map_err(|_e| { 170 km_err!(InvalidNonce, "want {} byte nonce for AES-CTR", BLOCK_SIZE) 171 })?; 172 Ok(Mode::Cipher(CipherMode::Ctr { nonce })) 173 } 174 BlockMode::Gcm => { 175 if padding != PaddingMode::None { 176 return Err(km_err!( 177 IncompatiblePaddingMode, 178 "expected NONE padding for AES-GCM" 179 )); 180 } 181 let nonce: [u8; GCM_NONCE_SIZE] = nonce(GCM_NONCE_SIZE, caller_nonce, rng)? 182 .try_into() 183 .map_err(|_e| km_err!(InvalidNonce, "want 12 byte nonce for AES-GCM"))?; 184 let tag_len = get_tag_value!(params, MacLength, ErrorCode::InvalidMacLength)?; 185 if tag_len % 8 != 0 { 186 return Err(km_err!( 187 InvalidMacLength, 188 "tag length {} not a multiple of 8", 189 tag_len 190 )); 191 } 192 match tag_len / 8 { 193 12 => Ok(Mode::Aead(GcmMode::GcmTag12 { nonce })), 194 13 => Ok(Mode::Aead(GcmMode::GcmTag13 { nonce })), 195 14 => Ok(Mode::Aead(GcmMode::GcmTag14 { nonce })), 196 15 => Ok(Mode::Aead(GcmMode::GcmTag15 { nonce })), 197 16 => Ok(Mode::Aead(GcmMode::GcmTag16 { nonce })), 198 v => Err(km_err!( 199 InvalidMacLength, 200 "want 12-16 byte tag for AES-GCM not {} bytes", 201 v 202 )), 203 } 204 } 205 } 206 } 207 208 /// Indicate whether the AES mode is an AEAD. is_aead(&self) -> bool209 pub fn is_aead(&self) -> bool { 210 match self { 211 Mode::Aead(_) => true, 212 Mode::Cipher(_) => false, 213 } 214 } 215 } 216 217 impl GcmMode { 218 /// Return the tag length (in bytes) for an AES-GCM mode. tag_len(&self) -> usize219 pub fn tag_len(&self) -> usize { 220 match self { 221 GcmMode::GcmTag12 { nonce: _ } => 12, 222 GcmMode::GcmTag13 { nonce: _ } => 13, 223 GcmMode::GcmTag14 { nonce: _ } => 14, 224 GcmMode::GcmTag15 { nonce: _ } => 15, 225 GcmMode::GcmTag16 { nonce: _ } => 16, 226 } 227 } 228 } 229