• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Functionality related to AES encryption
2 
3 use super::{nonce, Rng};
4 use crate::{get_tag_value, km_err, tag, try_to_vec, Error};
5 use alloc::vec::Vec;
6 use core::convert::TryInto;
7 use kmr_wire::keymint::{BlockMode, ErrorCode, KeyParam, PaddingMode};
8 use kmr_wire::KeySizeInBits;
9 use zeroize::ZeroizeOnDrop;
10 
11 /// Size of an AES block in bytes.
12 pub const BLOCK_SIZE: usize = 16;
13 
14 /// Size of AES-GCM nonce in bytes.
15 pub const GCM_NONCE_SIZE: usize = 12; // 96 bits
16 
17 /// AES variant.
18 #[derive(Clone)]
19 pub enum Variant {
20     Aes128,
21     Aes192,
22     Aes256,
23 }
24 
25 /// An AES-128, AES-192 or AES-256 key.
26 #[derive(Clone, PartialEq, Eq, ZeroizeOnDrop)]
27 pub enum Key {
28     Aes128([u8; 16]),
29     Aes192([u8; 24]),
30     Aes256([u8; 32]),
31 }
32 
33 impl Key {
34     /// Create a new [`Key`] from raw data, which must be 16, 24 or 32 bytes long.
new(data: Vec<u8>) -> Result<Self, Error>35     pub fn new(data: Vec<u8>) -> Result<Self, Error> {
36         match data.len() {
37             16 => Ok(Key::Aes128(data.try_into().unwrap())), // safe: len checked
38             24 => Ok(Key::Aes192(data.try_into().unwrap())), // safe: len checked
39             32 => Ok(Key::Aes256(data.try_into().unwrap())), // safe: len checked
40             l => Err(km_err!(UnsupportedKeySize, "AES keys must be 16, 24 or 32 bytes not {}", l)),
41         }
42     }
43     /// Create a new [`Key`] from raw data, which must be 16, 24 or 32 bytes long.
new_from(data: &[u8]) -> Result<Self, Error>44     pub fn new_from(data: &[u8]) -> Result<Self, Error> {
45         Key::new(try_to_vec(data)?)
46     }
47 
48     /// Indicate the size of the key in bits.
size(&self) -> KeySizeInBits49     pub fn size(&self) -> KeySizeInBits {
50         KeySizeInBits(match self {
51             Key::Aes128(_) => 128,
52             Key::Aes192(_) => 192,
53             Key::Aes256(_) => 256,
54         })
55     }
56 }
57 
58 /// Mode of AES plain cipher operation.  Associated value is the nonce.
59 #[derive(Clone, Copy, Debug)]
60 pub enum CipherMode {
61     EcbNoPadding,
62     EcbPkcs7Padding,
63     CbcNoPadding { nonce: [u8; BLOCK_SIZE] },
64     CbcPkcs7Padding { nonce: [u8; BLOCK_SIZE] },
65     Ctr { nonce: [u8; BLOCK_SIZE] },
66 }
67 
68 /// Mode of AES-GCM operation.  Associated value is the nonce.
69 #[derive(Clone, Copy, Debug)]
70 pub enum GcmMode {
71     GcmTag12 { nonce: [u8; GCM_NONCE_SIZE] },
72     GcmTag13 { nonce: [u8; GCM_NONCE_SIZE] },
73     GcmTag14 { nonce: [u8; GCM_NONCE_SIZE] },
74     GcmTag15 { nonce: [u8; GCM_NONCE_SIZE] },
75     GcmTag16 { nonce: [u8; GCM_NONCE_SIZE] },
76 }
77 
78 /// Mode of AES operation.
79 #[derive(Clone, Copy, Debug)]
80 pub enum Mode {
81     Cipher(CipherMode),
82     Aead(GcmMode),
83 }
84 
85 impl Mode {
86     /// Determine the [`Mode`], rejecting invalid parameters. Use `caller_nonce` if provided,
87     /// 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>88     pub fn new(
89         params: &[KeyParam],
90         caller_nonce: Option<&Vec<u8>>,
91         rng: &mut dyn Rng,
92     ) -> Result<Self, Error> {
93         let mode = tag::get_block_mode(params)?;
94         let padding = tag::get_padding_mode(params)?;
95         match mode {
96             BlockMode::Ecb => {
97                 if caller_nonce.is_some() {
98                     return Err(km_err!(InvalidNonce, "nonce unexpectedly provided for AES-ECB"));
99                 }
100                 match padding {
101                     PaddingMode::None => Ok(Mode::Cipher(CipherMode::EcbNoPadding)),
102                     PaddingMode::Pkcs7 => Ok(Mode::Cipher(CipherMode::EcbPkcs7Padding)),
103                     _ => Err(km_err!(
104                         IncompatiblePaddingMode,
105                         "expected NONE/PKCS7 padding for AES-ECB"
106                     )),
107                 }
108             }
109             BlockMode::Cbc => {
110                 let nonce: [u8; BLOCK_SIZE] =
111                     nonce(BLOCK_SIZE, caller_nonce, rng)?.try_into().map_err(|_e| {
112                         km_err!(InvalidNonce, "want {} byte nonce for AES-CBC", BLOCK_SIZE)
113                     })?;
114                 match padding {
115                     PaddingMode::None => Ok(Mode::Cipher(CipherMode::CbcNoPadding { nonce })),
116                     PaddingMode::Pkcs7 => Ok(Mode::Cipher(CipherMode::CbcPkcs7Padding { nonce })),
117                     _ => Err(km_err!(
118                         IncompatiblePaddingMode,
119                         "expected NONE/PKCS7 padding for AES-CBC"
120                     )),
121                 }
122             }
123             BlockMode::Ctr => {
124                 if padding != PaddingMode::None {
125                     return Err(km_err!(
126                         IncompatiblePaddingMode,
127                         "expected NONE padding for AES-CTR"
128                     ));
129                 }
130                 let nonce: [u8; BLOCK_SIZE] =
131                     nonce(BLOCK_SIZE, caller_nonce, rng)?.try_into().map_err(|_e| {
132                         km_err!(InvalidNonce, "want {} byte nonce for AES-CTR", BLOCK_SIZE)
133                     })?;
134                 Ok(Mode::Cipher(CipherMode::Ctr { nonce }))
135             }
136             BlockMode::Gcm => {
137                 if padding != PaddingMode::None {
138                     return Err(km_err!(
139                         IncompatiblePaddingMode,
140                         "expected NONE padding for AES-GCM"
141                     ));
142                 }
143                 let nonce: [u8; GCM_NONCE_SIZE] = nonce(GCM_NONCE_SIZE, caller_nonce, rng)?
144                     .try_into()
145                     .map_err(|_e| km_err!(InvalidNonce, "want 12 byte nonce for AES-GCM"))?;
146                 let tag_len = get_tag_value!(params, MacLength, ErrorCode::InvalidMacLength)?;
147                 if tag_len % 8 != 0 {
148                     return Err(km_err!(
149                         InvalidMacLength,
150                         "tag length {} not a multiple of 8",
151                         tag_len
152                     ));
153                 }
154                 match tag_len / 8 {
155                     12 => Ok(Mode::Aead(GcmMode::GcmTag12 { nonce })),
156                     13 => Ok(Mode::Aead(GcmMode::GcmTag13 { nonce })),
157                     14 => Ok(Mode::Aead(GcmMode::GcmTag14 { nonce })),
158                     15 => Ok(Mode::Aead(GcmMode::GcmTag15 { nonce })),
159                     16 => Ok(Mode::Aead(GcmMode::GcmTag16 { nonce })),
160                     v => Err(km_err!(
161                         InvalidMacLength,
162                         "want 12-16 byte tag for AES-GCM not {} bytes",
163                         v
164                     )),
165                 }
166             }
167         }
168     }
169 
170     /// Indicate whether the AES mode is an AEAD.
is_aead(&self) -> bool171     pub fn is_aead(&self) -> bool {
172         match self {
173             Mode::Aead(_) => true,
174             Mode::Cipher(_) => false,
175         }
176     }
177 }
178 
179 impl GcmMode {
180     /// Return the tag length (in bytes) for an AES-GCM mode.
tag_len(&self) -> usize181     pub fn tag_len(&self) -> usize {
182         match self {
183             GcmMode::GcmTag12 { nonce: _ } => 12,
184             GcmMode::GcmTag13 { nonce: _ } => 13,
185             GcmMode::GcmTag14 { nonce: _ } => 14,
186             GcmMode::GcmTag15 { nonce: _ } => 15,
187             GcmMode::GcmTag16 { nonce: _ } => 16,
188         }
189     }
190 }
191