• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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