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