// Copyright 2022, The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //! BoringSSL-based implementation of AES. use crate::{openssl_err, openssl_err_or, ossl}; use alloc::boxed::Box; use alloc::vec::Vec; use core::cmp::min; use kmr_common::{ crypto, crypto::OpaqueOr, explicit, km_err, vec_try, vec_try_with_capacity, Error, FallibleAllocExt, }; use openssl::symm::{Cipher, Crypter}; /// [`crypto::Aes`] implementation based on BoringSSL. pub struct BoringAes; impl crypto::Aes for BoringAes { fn begin( &self, key: OpaqueOr, mode: crypto::aes::CipherMode, dir: crypto::SymmetricOperation, ) -> Result, Error> { let key = explicit!(key)?; let dir_mode = match dir { crypto::SymmetricOperation::Encrypt => openssl::symm::Mode::Encrypt, crypto::SymmetricOperation::Decrypt => openssl::symm::Mode::Decrypt, }; let crypter = match mode { crypto::aes::CipherMode::EcbNoPadding | crypto::aes::CipherMode::EcbPkcs7Padding => { let (cipher, key) = match &key { crypto::aes::Key::Aes128(k) => (Cipher::aes_128_ecb(), &k[..]), crypto::aes::Key::Aes192(k) => (Cipher::aes_192_ecb(), &k[..]), crypto::aes::Key::Aes256(k) => (Cipher::aes_256_ecb(), &k[..]), }; let mut crypter = Crypter::new(cipher, dir_mode, key, None) .map_err(openssl_err!("failed to create ECB Crypter"))?; if let crypto::aes::CipherMode::EcbPkcs7Padding = mode { crypter.pad(true); } else { crypter.pad(false); } crypter } crypto::aes::CipherMode::CbcNoPadding { nonce: n } | crypto::aes::CipherMode::CbcPkcs7Padding { nonce: n } => { let (cipher, key) = match &key { crypto::aes::Key::Aes128(k) => (Cipher::aes_128_cbc(), &k[..]), crypto::aes::Key::Aes192(k) => (Cipher::aes_192_cbc(), &k[..]), crypto::aes::Key::Aes256(k) => (Cipher::aes_256_cbc(), &k[..]), }; let mut crypter = Crypter::new(cipher, dir_mode, key, Some(&n[..])) .map_err(openssl_err!("failed to create CBC Crypter"))?; if let crypto::aes::CipherMode::CbcPkcs7Padding { nonce: _ } = mode { crypter.pad(true); } else { crypter.pad(false); } crypter } crypto::aes::CipherMode::Ctr { nonce: n } => { let (cipher, key) = match &key { crypto::aes::Key::Aes128(k) => (Cipher::aes_128_ctr(), &k[..]), crypto::aes::Key::Aes192(k) => (Cipher::aes_192_ctr(), &k[..]), crypto::aes::Key::Aes256(k) => (Cipher::aes_256_ctr(), &k[..]), }; Crypter::new(cipher, dir_mode, key, Some(&n[..])) .map_err(openssl_err!("failed to create CTR Crypter"))? } }; Ok(Box::new(BoringAesOperation { crypter })) } fn begin_aead( &self, key: OpaqueOr, mode: crypto::aes::GcmMode, dir: crypto::SymmetricOperation, ) -> Result, Error> { let key = explicit!(key)?; let dir_mode = match dir { crypto::SymmetricOperation::Encrypt => openssl::symm::Mode::Encrypt, crypto::SymmetricOperation::Decrypt => openssl::symm::Mode::Decrypt, }; let crypter = match mode { crypto::aes::GcmMode::GcmTag12 { nonce: n } | crypto::aes::GcmMode::GcmTag13 { nonce: n } | crypto::aes::GcmMode::GcmTag14 { nonce: n } | crypto::aes::GcmMode::GcmTag15 { nonce: n } | crypto::aes::GcmMode::GcmTag16 { nonce: n } => { let (cipher, key) = match &key { crypto::aes::Key::Aes128(k) => (Cipher::aes_128_gcm(), &k[..]), crypto::aes::Key::Aes192(k) => (Cipher::aes_192_gcm(), &k[..]), crypto::aes::Key::Aes256(k) => (Cipher::aes_256_gcm(), &k[..]), }; Crypter::new(cipher, dir_mode, key, Some(&n[..])).map_err(openssl_err!( "failed to create GCM Crypter for {:?} {:?}", mode, dir ))? } }; Ok(match dir { crypto::SymmetricOperation::Encrypt => Box::new({ BoringAesGcmEncryptOperation { mode, inner: BoringAesOperation { crypter } } }), crypto::SymmetricOperation::Decrypt => Box::new(BoringAesGcmDecryptOperation { crypter, decrypt_tag_len: mode.tag_len(), pending_input_tail: vec_try_with_capacity!(mode.tag_len())?, }), }) } } /// AES operation based on BoringSSL. pub struct BoringAesOperation { crypter: openssl::symm::Crypter, } impl crypto::EmittingOperation for BoringAesOperation { fn update(&mut self, data: &[u8]) -> Result, Error> { let mut output = vec_try![0; data.len() + crypto::aes::BLOCK_SIZE]?; let out_len = self .crypter .update(data, &mut output) .map_err(openssl_err!("update {} bytes from input failed", data.len()))?; output.truncate(out_len); Ok(output) } fn finish(mut self: Box) -> Result, Error> { let mut output = vec_try![0; crypto::aes::BLOCK_SIZE]?; let out_len = ossl!(self.crypter.finalize(&mut output))?; output.truncate(out_len); Ok(output) } } /// AES-GCM encrypt operation based on BoringSSL. pub struct BoringAesGcmEncryptOperation { mode: crypto::aes::GcmMode, inner: BoringAesOperation, } impl crypto::AadOperation for BoringAesGcmEncryptOperation { fn update_aad(&mut self, aad: &[u8]) -> Result<(), Error> { ossl!(self.inner.crypter.aad_update(aad)) } } impl crypto::EmittingOperation for BoringAesGcmEncryptOperation { fn update(&mut self, data: &[u8]) -> Result, Error> { self.inner.update(data) } fn finish(mut self: Box) -> Result, Error> { let mut output = vec_try![0; crypto::aes::BLOCK_SIZE + self.mode.tag_len()]?; let offset = self .inner .crypter .finalize(&mut output) .map_err(openssl_err_or!(VerificationFailed, "failed to finalize"))?; self.inner .crypter .get_tag(&mut output[offset..offset + self.mode.tag_len()]) .map_err(openssl_err!("failed to get tag of len {}", self.mode.tag_len()))?; output.truncate(offset + self.mode.tag_len()); Ok(output) } } /// AES-GCM decrypt operation based on BoringSSL. pub struct BoringAesGcmDecryptOperation { crypter: openssl::symm::Crypter, // Size of a final tag when decrypting. decrypt_tag_len: usize, // For decryption, the last `decrypt_tag_len` bytes of input must be fed in separately. // However, the overall size of the input data is not known in advance, so we need to hold up to // `decrypt_tag_len` bytes on input in reserve until `finish()`. pending_input_tail: Vec, // Capacity = decrypt_tag_len } impl crypto::AadOperation for BoringAesGcmDecryptOperation { fn update_aad(&mut self, aad: &[u8]) -> Result<(), Error> { ossl!(self.crypter.aad_update(aad)) } } impl crypto::EmittingOperation for BoringAesGcmDecryptOperation { fn update(&mut self, data: &[u8]) -> Result, Error> { // The current input is the (self.pending_input_tail || data) combination. let combined_len = self.pending_input_tail.len() + data.len(); if combined_len <= self.decrypt_tag_len { // Adding on this data is still not enough for more than just a tag, // so save off the input data for next time and return. self.pending_input_tail.try_extend_from_slice(data)?; return Ok(Vec::new()); } // At this point the combination (self.pending_input_tail || data) includes enough data to both: // - feed some into the cipher // - still keep a full self.decrypt_tag_len worth of data still pending. let cipherable_len = combined_len - self.decrypt_tag_len; let cipherable_from_pending = min(cipherable_len, self.pending_input_tail.len()); let cipherable_from_data = cipherable_len - cipherable_from_pending; let mut output = vec_try![0; data.len()]?; let mut offset = 0; if cipherable_from_pending > 0 { offset = self .crypter .update(&self.pending_input_tail[..cipherable_from_pending], &mut output) .map_err(openssl_err!( "update {} bytes from pending failed", cipherable_from_pending ))?; } if cipherable_from_data > 0 { let out_len = self .crypter .update(&data[..cipherable_from_data], &mut output[offset..]) .map_err(openssl_err!("update {} bytes from input failed", cipherable_from_data))?; offset += out_len; } output.truncate(offset); // Reset `self.pending_input_tail` to the unused data. let leftover_pending = self.pending_input_tail.len() - cipherable_from_pending; self.pending_input_tail.resize(self.decrypt_tag_len, 0); self.pending_input_tail.copy_within(cipherable_from_pending.., 0); self.pending_input_tail[leftover_pending..].copy_from_slice(&data[cipherable_from_data..]); Ok(output) } fn finish(mut self: Box) -> Result, Error> { // Need to feed in the entire tag before completion. if self.pending_input_tail.len() != self.decrypt_tag_len { return Err(km_err!( InvalidTag, "only {} bytes of pending data, need {}", self.pending_input_tail.len(), self.decrypt_tag_len )); } self.crypter.set_tag(&self.pending_input_tail).map_err(openssl_err!( "failed to set {} bytes of tag", self.pending_input_tail.len() ))?; // Feeding in just the tag should not result in any output data. let mut output = Vec::new(); let out_len = self .crypter .finalize(&mut output) .map_err(openssl_err_or!(VerificationFailed, "failed to finalize"))?; if out_len != 0 { return Err(km_err!( BoringSslError, "finalizing AES-GCM tag produced {} bytes of data!", out_len )); } Ok(output) } }