blob: 5aaf7d6235850cb52a27ce153a8545d8ea7d480c [file]
// 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<crypto::aes::Key>,
mode: crypto::aes::CipherMode,
dir: crypto::SymmetricOperation,
) -> Result<Box<dyn crypto::EmittingOperation>, 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<crypto::aes::Key>,
mode: crypto::aes::GcmMode,
dir: crypto::SymmetricOperation,
) -> Result<Box<dyn crypto::AadOperation>, 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<Vec<u8>, 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<Self>) -> Result<Vec<u8>, 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<Vec<u8>, Error> {
self.inner.update(data)
}
fn finish(mut self: Box<Self>) -> Result<Vec<u8>, 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<u8>, // 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<Vec<u8>, 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<Self>) -> Result<Vec<u8>, 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)
}
}