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