1 use crate::{openssl_err, openssl_err_or, ossl}; 2 use alloc::boxed::Box; 3 use alloc::vec::Vec; 4 use core::cmp::min; 5 use kmr_common::{ 6 crypto, crypto::OpaqueOr, explicit, km_err, vec_try, vec_try_with_capacity, Error, 7 FallibleAllocExt, 8 }; 9 use openssl::symm::{Cipher, Crypter}; 10 11 /// [`crypto::Aes`] implementation based on BoringSSL. 12 pub struct BoringAes; 13 14 impl crypto::Aes for BoringAes { begin( &self, key: OpaqueOr<crypto::aes::Key>, mode: crypto::aes::CipherMode, dir: crypto::SymmetricOperation, ) -> Result<Box<dyn crypto::EmittingOperation>, Error>15 fn begin( 16 &self, 17 key: OpaqueOr<crypto::aes::Key>, 18 mode: crypto::aes::CipherMode, 19 dir: crypto::SymmetricOperation, 20 ) -> Result<Box<dyn crypto::EmittingOperation>, Error> { 21 let key = explicit!(key)?; 22 let dir_mode = match dir { 23 crypto::SymmetricOperation::Encrypt => openssl::symm::Mode::Encrypt, 24 crypto::SymmetricOperation::Decrypt => openssl::symm::Mode::Decrypt, 25 }; 26 let crypter = match mode { 27 crypto::aes::CipherMode::EcbNoPadding | crypto::aes::CipherMode::EcbPkcs7Padding => { 28 let (cipher, key) = match &key { 29 crypto::aes::Key::Aes128(k) => (Cipher::aes_128_ecb(), &k[..]), 30 crypto::aes::Key::Aes192(k) => (Cipher::aes_192_ecb(), &k[..]), 31 crypto::aes::Key::Aes256(k) => (Cipher::aes_256_ecb(), &k[..]), 32 }; 33 let mut crypter = Crypter::new(cipher, dir_mode, key, None) 34 .map_err(openssl_err!("failed to create ECB Crypter"))?; 35 if let crypto::aes::CipherMode::EcbPkcs7Padding = mode { 36 crypter.pad(true); 37 } else { 38 crypter.pad(false); 39 } 40 crypter 41 } 42 43 crypto::aes::CipherMode::CbcNoPadding { nonce: n } 44 | crypto::aes::CipherMode::CbcPkcs7Padding { nonce: n } => { 45 let (cipher, key) = match &key { 46 crypto::aes::Key::Aes128(k) => (Cipher::aes_128_cbc(), &k[..]), 47 crypto::aes::Key::Aes192(k) => (Cipher::aes_192_cbc(), &k[..]), 48 crypto::aes::Key::Aes256(k) => (Cipher::aes_256_cbc(), &k[..]), 49 }; 50 let mut crypter = Crypter::new(cipher, dir_mode, key, Some(&n[..])) 51 .map_err(openssl_err!("failed to create CBC Crypter"))?; 52 if let crypto::aes::CipherMode::CbcPkcs7Padding { nonce: _ } = mode { 53 crypter.pad(true); 54 } else { 55 crypter.pad(false); 56 } 57 crypter 58 } 59 60 crypto::aes::CipherMode::Ctr { nonce: n } => { 61 let (cipher, key) = match &key { 62 crypto::aes::Key::Aes128(k) => (Cipher::aes_128_ctr(), &k[..]), 63 crypto::aes::Key::Aes192(k) => (Cipher::aes_192_ctr(), &k[..]), 64 crypto::aes::Key::Aes256(k) => (Cipher::aes_256_ctr(), &k[..]), 65 }; 66 Crypter::new(cipher, dir_mode, key, Some(&n[..])) 67 .map_err(openssl_err!("failed to create CTR Crypter"))? 68 } 69 }; 70 71 Ok(Box::new(BoringAesOperation { crypter })) 72 } 73 begin_aead( &self, key: OpaqueOr<crypto::aes::Key>, mode: crypto::aes::GcmMode, dir: crypto::SymmetricOperation, ) -> Result<Box<dyn crypto::AadOperation>, Error>74 fn begin_aead( 75 &self, 76 key: OpaqueOr<crypto::aes::Key>, 77 mode: crypto::aes::GcmMode, 78 dir: crypto::SymmetricOperation, 79 ) -> Result<Box<dyn crypto::AadOperation>, Error> { 80 let key = explicit!(key)?; 81 let dir_mode = match dir { 82 crypto::SymmetricOperation::Encrypt => openssl::symm::Mode::Encrypt, 83 crypto::SymmetricOperation::Decrypt => openssl::symm::Mode::Decrypt, 84 }; 85 let crypter = match mode { 86 crypto::aes::GcmMode::GcmTag12 { nonce: n } 87 | crypto::aes::GcmMode::GcmTag13 { nonce: n } 88 | crypto::aes::GcmMode::GcmTag14 { nonce: n } 89 | crypto::aes::GcmMode::GcmTag15 { nonce: n } 90 | crypto::aes::GcmMode::GcmTag16 { nonce: n } => { 91 let (cipher, key) = match &key { 92 crypto::aes::Key::Aes128(k) => (Cipher::aes_128_gcm(), &k[..]), 93 crypto::aes::Key::Aes192(k) => (Cipher::aes_192_gcm(), &k[..]), 94 crypto::aes::Key::Aes256(k) => (Cipher::aes_256_gcm(), &k[..]), 95 }; 96 Crypter::new(cipher, dir_mode, key, Some(&n[..])).map_err(openssl_err!( 97 "failed to create GCM Crypter for {:?} {:?}", 98 mode, 99 dir 100 ))? 101 } 102 }; 103 104 Ok(match dir { 105 crypto::SymmetricOperation::Encrypt => Box::new({ 106 BoringAesGcmEncryptOperation { mode, inner: BoringAesOperation { crypter } } 107 }), 108 crypto::SymmetricOperation::Decrypt => Box::new(BoringAesGcmDecryptOperation { 109 crypter, 110 decrypt_tag_len: mode.tag_len(), 111 pending_input_tail: vec_try_with_capacity!(mode.tag_len())?, 112 }), 113 }) 114 } 115 } 116 117 /// [`crypto::AesOperation`] implementation based on BoringSSL. 118 pub struct BoringAesOperation { 119 crypter: openssl::symm::Crypter, 120 } 121 122 impl crypto::EmittingOperation for BoringAesOperation { update(&mut self, data: &[u8]) -> Result<Vec<u8>, Error>123 fn update(&mut self, data: &[u8]) -> Result<Vec<u8>, Error> { 124 let mut output = vec_try![0; data.len() + crypto::aes::BLOCK_SIZE]?; 125 let out_len = self 126 .crypter 127 .update(data, &mut output) 128 .map_err(openssl_err!("update {} bytes from input failed", data.len()))?; 129 output.truncate(out_len); 130 Ok(output) 131 } 132 finish(mut self: Box<Self>) -> Result<Vec<u8>, Error>133 fn finish(mut self: Box<Self>) -> Result<Vec<u8>, Error> { 134 let mut output = vec_try![0; crypto::aes::BLOCK_SIZE]?; 135 let out_len = ossl!(self.crypter.finalize(&mut output))?; 136 output.truncate(out_len); 137 Ok(output) 138 } 139 } 140 141 /// [`crypto::AesGcmEncryptOperation`] implementation based on BoringSSL. 142 pub struct BoringAesGcmEncryptOperation { 143 mode: crypto::aes::GcmMode, 144 inner: BoringAesOperation, 145 } 146 147 impl crypto::AadOperation for BoringAesGcmEncryptOperation { update_aad(&mut self, aad: &[u8]) -> Result<(), Error>148 fn update_aad(&mut self, aad: &[u8]) -> Result<(), Error> { 149 ossl!(self.inner.crypter.aad_update(aad)) 150 } 151 } 152 153 impl crypto::EmittingOperation for BoringAesGcmEncryptOperation { update(&mut self, data: &[u8]) -> Result<Vec<u8>, Error>154 fn update(&mut self, data: &[u8]) -> Result<Vec<u8>, Error> { 155 self.inner.update(data) 156 } 157 finish(mut self: Box<Self>) -> Result<Vec<u8>, Error>158 fn finish(mut self: Box<Self>) -> Result<Vec<u8>, Error> { 159 let mut output = vec_try![0; crypto::aes::BLOCK_SIZE + self.mode.tag_len()]?; 160 let offset = self 161 .inner 162 .crypter 163 .finalize(&mut output) 164 .map_err(openssl_err_or!(VerificationFailed, "failed to finalize"))?; 165 166 self.inner 167 .crypter 168 .get_tag(&mut output[offset..offset + self.mode.tag_len()]) 169 .map_err(openssl_err!("failed to get tag of len {}", self.mode.tag_len()))?; 170 output.truncate(offset + self.mode.tag_len()); 171 Ok(output) 172 } 173 } 174 175 /// [`crypto::AesGcmDecryptOperation`] implementation based on BoringSSL. 176 pub struct BoringAesGcmDecryptOperation { 177 crypter: openssl::symm::Crypter, 178 179 // Size of a final tag when decrypting. 180 decrypt_tag_len: usize, 181 182 // For decryption, the last `decrypt_tag_len` bytes of input must be fed in separately. 183 // However, the overall size of the input data is not known in advance, so we need to hold up to 184 // `decrypt_tag_len` bytes on input in reserve until `finish()`. 185 pending_input_tail: Vec<u8>, // Capacity = decrypt_tag_len 186 } 187 188 impl crypto::AadOperation for BoringAesGcmDecryptOperation { update_aad(&mut self, aad: &[u8]) -> Result<(), Error>189 fn update_aad(&mut self, aad: &[u8]) -> Result<(), Error> { 190 ossl!(self.crypter.aad_update(aad)) 191 } 192 } 193 194 impl crypto::EmittingOperation for BoringAesGcmDecryptOperation { update(&mut self, data: &[u8]) -> Result<Vec<u8>, Error>195 fn update(&mut self, data: &[u8]) -> Result<Vec<u8>, Error> { 196 // The current input is the (self.pending_input_tail || data) combination. 197 let combined_len = self.pending_input_tail.len() + data.len(); 198 if combined_len <= self.decrypt_tag_len { 199 // Adding on this data is still not enough for more than just a tag, 200 // so save off the input data for next time and return. 201 self.pending_input_tail.try_extend_from_slice(data)?; 202 return Ok(Vec::new()); 203 } 204 205 // At this point the combination (self.pending_input_tail || data) includes enough data to both: 206 // - feed some into the cipher 207 // - still keep a full self.decrypt_tag_len worth of data still pending. 208 let cipherable_len = combined_len - self.decrypt_tag_len; 209 let cipherable_from_pending = min(cipherable_len, self.pending_input_tail.len()); 210 let cipherable_from_data = cipherable_len - cipherable_from_pending; 211 212 let mut output = vec_try![0; data.len()]?; 213 let mut offset = 0; 214 if cipherable_from_pending > 0 { 215 offset = self 216 .crypter 217 .update(&self.pending_input_tail[..cipherable_from_pending], &mut output) 218 .map_err(openssl_err!( 219 "update {} bytes from pending failed", 220 cipherable_from_pending 221 ))?; 222 } 223 if cipherable_from_data > 0 { 224 let out_len = self 225 .crypter 226 .update(&data[..cipherable_from_data], &mut output[offset..]) 227 .map_err(openssl_err!("update {} bytes from input failed", cipherable_from_data))?; 228 offset += out_len; 229 } 230 output.truncate(offset); 231 232 // Reset `self.pending_input_tail` to the unused data. 233 let leftover_pending = self.pending_input_tail.len() - cipherable_from_pending; 234 self.pending_input_tail.resize(self.decrypt_tag_len, 0); 235 self.pending_input_tail.copy_within(cipherable_from_pending.., 0); 236 self.pending_input_tail[leftover_pending..].copy_from_slice(&data[cipherable_from_data..]); 237 238 Ok(output) 239 } 240 finish(mut self: Box<Self>) -> Result<Vec<u8>, Error>241 fn finish(mut self: Box<Self>) -> Result<Vec<u8>, Error> { 242 // Need to feed in the entire tag before completion. 243 if self.pending_input_tail.len() != self.decrypt_tag_len { 244 return Err(km_err!( 245 InvalidTag, 246 "only {} bytes of pending data, need {}", 247 self.pending_input_tail.len(), 248 self.decrypt_tag_len 249 )); 250 } 251 self.crypter.set_tag(&self.pending_input_tail).map_err(openssl_err!( 252 "failed to set {} bytes of tag", 253 self.pending_input_tail.len() 254 ))?; 255 256 // Feeding in just the tag should not result in any output data. 257 let mut output = Vec::new(); 258 let out_len = self 259 .crypter 260 .finalize(&mut output) 261 .map_err(openssl_err_or!(VerificationFailed, "failed to finalize"))?; 262 if out_len != 0 { 263 return Err(km_err!( 264 UnknownError, 265 "finalizing AES-GCM tag produced {} bytes of data!", 266 out_len 267 )); 268 } 269 Ok(output) 270 } 271 } 272