blob: d7916d1c7c49dff67d22bcc7110b5a9172c1db44 [file] [log] [blame]
// Copyright 2025 The Fuchsia Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::cell::{RefCell, RefMut};
use std::cmp::min;
use std::collections::HashMap;
use std::iter;
use std::marker::PhantomData;
use std::rc::Rc;
use aes::{Aes128, Aes192, Aes256};
use cbc::{Decryptor as CbcDecryptor, Encryptor as CbcEncryptor};
use cmac::Cmac;
use crypto_common::{KeyInit, KeyIvInit};
use digest::DynDigest as Digest;
use ecb::{Decryptor as EcbDecryptor, Encryptor as EcbEncryptor};
use hmac::Hmac;
use rand_core::{CryptoRng, RngCore};
use rsa::traits::{PaddingScheme as RsaPaddingScheme, PublicKeyParts as _};
use rsa::{Oaep, RsaPrivateKey};
use sha1::Sha1;
use sha2::{Sha224, Sha256, Sha384, Sha512};
use tee_internal::{
Algorithm, Attribute, EccCurve, Error, Mode, OperationHandle, Result as TeeResult, Usage,
};
use crate::storage::{
AesKey, HmacSha1Key, HmacSha224Key, HmacSha256Key, HmacSha384Key, HmacSha512Key, Key,
KeyType as _, NoKey, Object, RsaKeypair,
};
use crate::ErrorWithSize;
type AesCmac128 = Cmac<Aes128>;
type AesCmac192 = Cmac<Aes192>;
type AesCmac256 = Cmac<Aes256>;
type HmacSha1 = Hmac<Sha1>;
type HmacSha224 = Hmac<Sha224>;
type HmacSha256 = Hmac<Sha256>;
type HmacSha384 = Hmac<Sha384>;
type HmacSha512 = Hmac<Sha512>;
pub fn is_algorithm_supported(alg: Algorithm, element: EccCurve) -> bool {
if element != EccCurve::None {
return false;
}
match alg {
Algorithm::Sha1
| Algorithm::Sha224
| Algorithm::Sha256
| Algorithm::Sha384
| Algorithm::Sha512
| Algorithm::AesCbcNopad
| Algorithm::AesEcbNopad
| Algorithm::AesCmac
| Algorithm::HmacSha1
| Algorithm::HmacSha224
| Algorithm::HmacSha256
| Algorithm::HmacSha384
| Algorithm::HmacSha512
| Algorithm::RsaesPkcs1OaepMgf1Sha1 => true,
_ => false,
}
}
// An RNG abstraction in the shape expected by RustCrypto APIs.
pub(crate) struct Rng {}
impl RngCore for Rng {
fn next_u32(&mut self) -> u32 {
let val = 0u32;
self.fill_bytes(&mut val.to_le_bytes());
val
}
fn next_u64(&mut self) -> u64 {
let val = 0u64;
self.fill_bytes(&mut val.to_le_bytes());
val
}
fn fill_bytes(&mut self, dest: &mut [u8]) {
zx::cprng_draw(dest)
}
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> {
self.fill_bytes(dest);
Ok(())
}
}
impl CryptoRng for Rng {}
// A MAC abstraction conveniently shaped for our API glue needs.
trait Mac {
fn output_size(&self) -> usize;
fn update(&mut self, data: &[u8]);
fn reset(&mut self);
fn finalize_into_reset(&mut self, out: &mut [u8]);
// Returns Error::MacInvalid in the case of failure.
fn verify_reset(&mut self, expected: &[u8]) -> TeeResult;
}
// Implementations for the hmac digest types.
impl<M> Mac for M
where
M: digest::FixedOutputReset + digest::MacMarker + digest::Update,
{
fn output_size(&self) -> usize {
// OutputSizeUser is a subtrait of FixedOutputReset.
<Self as digest::OutputSizeUser>::output_size()
}
fn update(&mut self, data: &[u8]) {
<Self as digest::Update>::update(self, data)
}
fn reset(&mut self) {
// Reset is a subtrait of FixedOutputReset.
<Self as digest::Reset>::reset(self)
}
fn finalize_into_reset(&mut self, out: &mut [u8]) {
<Self as digest::FixedOutputReset>::finalize_into_reset(self, out.into())
}
fn verify_reset(&mut self, expected: &[u8]) -> TeeResult {
let finalized = <Self as digest::FixedOutputReset>::finalize_fixed_reset(self);
if finalized.as_slice() == expected {
Ok(())
} else {
Err(Error::MacInvalid)
}
}
}
// Supported MAC algorithm types.
enum MacType {
AesCmac,
HmacSha1,
HmacSha224,
HmacSha256,
HmacSha384,
HmacSha512,
}
// A cipher abstraction conveniently shaped for our API glue needs.
trait Cipher {
fn block_size(&self) -> usize;
fn set_iv(&mut self, iv: &[u8]);
fn reset(&mut self);
fn encrypt(&self, input: &[u8], output: &mut [u8]);
fn encrypt_in_place(&self, inout: &mut [u8]);
fn decrypt(&self, input: &[u8], output: &mut [u8]);
fn decrypt_in_place(&self, inout: &mut [u8]);
}
impl<C: PreCipher> Cipher for C {
fn set_iv(&mut self, iv: &[u8]) {
self.set_iv(iv)
}
fn reset(&mut self) {
self.reset()
}
fn block_size(&self) -> usize {
debug_assert_eq!(C::Encryptor::block_size(), C::Decryptor::block_size());
C::Encryptor::block_size()
}
fn encrypt(&self, input: &[u8], output: &mut [u8]) {
self.new_encryptor().encrypt(input, output);
}
fn encrypt_in_place(&self, inout: &mut [u8]) {
self.new_encryptor().encrypt_in_place(inout)
}
fn decrypt(&self, input: &[u8], output: &mut [u8]) {
self.new_decryptor().decrypt(input, output)
}
fn decrypt_in_place(&self, inout: &mut [u8]) {
self.new_decryptor().decrypt_in_place(inout)
}
}
// Ideally, we'd just use a trait like this in place of Cipher, but the
// presence of associated types makes it non-dyn-compatible.
trait PreCipher {
type Encryptor: Encryptor;
type Decryptor: Decryptor;
fn set_iv(&mut self, iv: &[u8]);
fn reset(&mut self);
// The minting of new encryptors or decryptors in general should happen
// only after set_iv() has been called.
fn new_encryptor(&self) -> Self::Encryptor;
fn new_decryptor(&self) -> Self::Decryptor;
}
trait Encryptor {
fn block_size() -> usize;
fn encrypt(&mut self, input: &[u8], output: &mut [u8]);
fn encrypt_in_place(&mut self, inout: &mut [u8]);
}
trait Decryptor {
fn block_size() -> usize;
fn decrypt(&mut self, input: &[u8], output: &mut [u8]);
fn decrypt_in_place(&mut self, inout: &mut [u8]);
}
// A general cipher type that requires an initialization vector.
struct CipherWithIv<E, D>
where
E: Encryptor + KeyIvInit,
D: Decryptor + KeyIvInit,
{
key: Vec<u8>,
iv: Vec<u8>,
phantom: PhantomData<(E, D)>,
}
impl<E, D> CipherWithIv<E, D>
where
E: Encryptor + KeyIvInit,
D: Decryptor + KeyIvInit,
{
fn new(key: &[u8]) -> Self {
Self { key: key.to_vec(), iv: Vec::new(), phantom: PhantomData::default() }
}
}
impl<E, D> PreCipher for CipherWithIv<E, D>
where
E: Encryptor + KeyIvInit,
D: Decryptor + KeyIvInit,
{
type Encryptor = E;
type Decryptor = D;
fn set_iv(&mut self, iv: &[u8]) {
self.iv = iv.to_vec()
}
fn reset(&mut self) {
self.iv.clear()
}
fn new_encryptor(&self) -> E {
E::new_from_slices(&self.key, &self.iv).unwrap()
}
fn new_decryptor(&self) -> D {
D::new_from_slices(&self.key, &self.iv).unwrap()
}
}
// A general cipher type that does not require an initialization vector.
struct CipherWithoutIv<E, D>
where
E: Encryptor + KeyInit,
D: Decryptor + KeyInit,
{
key: Vec<u8>,
phantom: PhantomData<(E, D)>,
}
impl<E, D> CipherWithoutIv<E, D>
where
E: Encryptor + KeyInit,
D: Decryptor + KeyInit,
{
fn new(key: &[u8]) -> Self {
Self { key: key.to_vec(), phantom: PhantomData::default() }
}
}
impl<E, D> PreCipher for CipherWithoutIv<E, D>
where
E: Encryptor + KeyInit,
D: Decryptor + KeyInit,
{
type Encryptor = E;
type Decryptor = D;
// Why not panic? Two reasons:
// * the spec does not prescribe any behaviour for calling CipherInit(iv)
// for an algorithm that does require an IV, though it does prescribe
// ignoring any IVs passed to MAC algorithms with MacInit(), so there's
// an argument for consistency;
// * it simplifies the one intended callsite of CipherInit() to make
// set_iv() and unconditional call.
fn set_iv(&mut self, _iv: &[u8]) {}
fn reset(&mut self) {}
fn new_encryptor(&self) -> E {
E::new_from_slice(&self.key).unwrap()
}
fn new_decryptor(&self) -> D {
D::new_from_slice(&self.key).unwrap()
}
}
// Provides Encryptor and Decryptor implementations for some of the
// RustCrypto-shaped encryptors and decryptors (which sadly don't implement
// some official trait themselves encoding their API).
//
// We use token trees in the macro matcher to permit the use of `$encryptor<C>`
// and `$decryptor<C>`, which wouldn't parse if specified more naturally as
// type paths.
macro_rules! rustcrypto_encryptor_and_decryptor {
($encryptor:tt, $decryptor:tt) => {
impl<C> Encryptor for $encryptor<C>
where
C: cipher::BlockCipher + cipher::BlockEncryptMut,
{
fn block_size() -> usize {
C::block_size()
}
fn encrypt(&mut self, input: &[u8], output: &mut [u8]) {
use cipher::BlockEncryptMut;
assert!(output.len() >= input.len());
let block_size = C::block_size();
let chunks =
iter::zip(input.chunks_exact(block_size), output.chunks_exact_mut(block_size));
for (in_block, out_block) in chunks {
self.encrypt_block_b2b_mut(in_block.into(), out_block.into());
}
}
fn encrypt_in_place(&mut self, inout: &mut [u8]) {
use cipher::BlockEncryptMut;
for block in inout.chunks_exact_mut(C::block_size()) {
self.encrypt_block_mut(block.into())
}
}
}
impl<C> Decryptor for $decryptor<C>
where
C: cipher::BlockCipher + cipher::BlockDecryptMut,
{
fn block_size() -> usize {
C::block_size()
}
fn decrypt(&mut self, input: &[u8], output: &mut [u8]) {
use cipher::BlockDecryptMut;
assert!(output.len() >= input.len());
let block_size = C::block_size();
let chunks =
iter::zip(input.chunks_exact(block_size), output.chunks_exact_mut(block_size));
for (in_block, out_block) in chunks {
self.decrypt_block_b2b_mut(in_block.into(), out_block.into());
}
}
fn decrypt_in_place(&mut self, inout: &mut [u8]) {
use cipher::BlockDecryptMut;
for block in inout.chunks_exact_mut(C::block_size()) {
self.decrypt_block_mut(block.into())
}
}
}
};
}
rustcrypto_encryptor_and_decryptor!(CbcEncryptor, CbcDecryptor);
rustcrypto_encryptor_and_decryptor!(EcbEncryptor, EcbDecryptor);
type AesCbcNopad<C> = CipherWithIv<cbc::Encryptor<C>, cbc::Decryptor<C>>;
type Aes128CbcNopad = AesCbcNopad<Aes128>;
type Aes192CbcNopad = AesCbcNopad<Aes192>;
type Aes256CbcNopad = AesCbcNopad<Aes256>;
type AesEcbNopad<C> = CipherWithoutIv<ecb::Encryptor<C>, ecb::Decryptor<C>>;
type Aes128EcbNopad = AesEcbNopad<Aes128>;
type Aes192EcbNopad = AesEcbNopad<Aes192>;
type Aes256EcbNopad = AesEcbNopad<Aes256>;
enum CipherType {
AesCbcNopad,
AesEcbNopad,
}
trait AsymmetricEncryptionKey {
fn decrypt(
&self,
params: &[Attribute],
input: &[u8],
output: &mut [u8],
) -> Result<usize, ErrorWithSize>;
}
trait RsaPadding<D>: RsaPaddingScheme
where
D: 'static + Digest + digest::Digest + Send + Sync,
{
fn new() -> Self;
}
impl<D> RsaPadding<D> for Oaep
where
D: 'static + Digest + digest::Digest + Send + Sync,
{
fn new() -> Self {
Oaep::new::<D>()
}
}
struct RsaEncryptionKey<D, Padding>
where
D: 'static + Digest + digest::Digest + Send + Sync,
Padding: RsaPadding<D>,
{
private: Rc<RsaPrivateKey>,
phantom: PhantomData<(D, Padding)>,
}
impl<D, Padding> RsaEncryptionKey<D, Padding>
where
D: 'static + Digest + digest::Digest + Send + Sync,
Padding: RsaPadding<D>,
{
fn new(private: Rc<RsaPrivateKey>) -> Self {
Self { private, phantom: PhantomData::default() }
}
}
impl<D, Padding> AsymmetricEncryptionKey for RsaEncryptionKey<D, Padding>
where
D: 'static + Digest + digest::Digest + Send + Sync,
Padding: RsaPadding<D>,
{
fn decrypt(
&self,
params: &[Attribute],
input: &[u8],
output: &mut [u8],
) -> Result<usize, ErrorWithSize> {
if !params.is_empty() {
unimplemented!();
}
let output_size = self.private.size() as usize;
if input.len() != output_size {
return Err(ErrorWithSize::new(Error::BadParameters));
}
if output.len() < output_size {
return Err(ErrorWithSize::short_buffer(output_size));
}
let decrypted = self.private.decrypt(Padding::new(), input).expect("Failed to decrypt");
let written = &mut output[..decrypted.len()];
written.copy_from_slice(&decrypted);
Ok(written.len())
}
}
enum AsymmetricEncryptionKeyType {
RsaOaepSha1,
}
// Encapsulated an abstracted helper classes particular to supported
// algorithms.
enum Helper {
Digest(Box<dyn Digest>),
Cipher(Option<Box<dyn Cipher>>, CipherType),
Mac(Option<Box<dyn Mac>>, MacType),
AsymmetricEncryptionKey(Option<Box<dyn AsymmetricEncryptionKey>>, AsymmetricEncryptionKeyType),
}
impl Helper {
fn new(algorithm: Algorithm) -> TeeResult<Self> {
match algorithm {
Algorithm::Sha1 => Ok(Helper::Digest(Box::new(Sha1::default()))),
Algorithm::Sha224 => Ok(Helper::Digest(Box::new(Sha224::default()))),
Algorithm::Sha256 => Ok(Helper::Digest(Box::new(Sha256::default()))),
Algorithm::Sha384 => Ok(Helper::Digest(Box::new(Sha384::default()))),
Algorithm::Sha512 => Ok(Helper::Digest(Box::new(Sha512::default()))),
Algorithm::AesCbcNopad => Ok(Helper::Cipher(None, CipherType::AesCbcNopad)),
Algorithm::AesEcbNopad => Ok(Helper::Cipher(None, CipherType::AesEcbNopad)),
Algorithm::AesCmac => Ok(Helper::Mac(None, MacType::AesCmac)),
Algorithm::HmacSha1 => Ok(Helper::Mac(None, MacType::HmacSha1)),
Algorithm::HmacSha224 => Ok(Helper::Mac(None, MacType::HmacSha224)),
Algorithm::HmacSha256 => Ok(Helper::Mac(None, MacType::HmacSha256)),
Algorithm::HmacSha384 => Ok(Helper::Mac(None, MacType::HmacSha384)),
Algorithm::HmacSha512 => Ok(Helper::Mac(None, MacType::HmacSha512)),
Algorithm::RsaesPkcs1OaepMgf1Sha1 => {
Ok(Helper::AsymmetricEncryptionKey(None, AsymmetricEncryptionKeyType::RsaOaepSha1))
}
_ => Err(Error::NotSupported),
}
}
fn initialize(&mut self, key: &Key) {
match self {
Helper::Digest(digest) => {
// Digests do not need initialization.
assert!(matches!(key, Key::Data(NoKey {})));
digest.reset()
}
Helper::Cipher(cipher, cipher_type) => {
let Key::Aes(AesKey { secret }) = key else {
panic!("Wrong key type ({:?}) - expected AES", key.get_type());
};
match cipher_type {
CipherType::AesCbcNopad => {
let cbc: Box<dyn Cipher> = match secret.len() {
16 => Box::new(Aes128CbcNopad::new(&secret)),
24 => Box::new(Aes192CbcNopad::new(&secret)),
32 => Box::new(Aes256CbcNopad::new(&secret)),
len => panic!("Invalid AES key length: {len}"),
};
*cipher = Some(cbc);
}
CipherType::AesEcbNopad => {
let ecb: Box<dyn Cipher> = match secret.len() {
16 => Box::new(Aes128EcbNopad::new(&secret)),
24 => Box::new(Aes192EcbNopad::new(&secret)),
32 => Box::new(Aes256EcbNopad::new(&secret)),
len => panic!("Invalid AES key length: {len}"),
};
*cipher = Some(ecb);
}
}
}
Helper::Mac(mac, mac_type) => match mac_type {
MacType::AesCmac => {
let Key::Aes(AesKey { secret }) = key else {
panic!("Wrong key type ({:?}) - expected AES", key.get_type());
};
let cmac: Box<dyn Mac> = match secret.len() {
16 => Box::new(AesCmac128::new_from_slice(&secret).unwrap()),
24 => Box::new(AesCmac192::new_from_slice(&secret).unwrap()),
32 => Box::new(AesCmac256::new_from_slice(&secret).unwrap()),
len => panic!("Invalid AES key length: {len}"),
};
*mac = Some(cmac);
}
MacType::HmacSha1 => {
let Key::HmacSha1(HmacSha1Key { secret }) = key else {
panic!("Wrong key type ({:?}) - expected HMAC SHA1", key.get_type());
};
*mac = Some(Box::new(HmacSha1::new_from_slice(&secret).unwrap()))
}
MacType::HmacSha224 => {
let Key::HmacSha224(HmacSha224Key { secret }) = key else {
panic!("Wrong key type ({:?}) - expected HMAC SHA224", key.get_type());
};
*mac = Some(Box::new(HmacSha224::new_from_slice(&secret).unwrap()))
}
MacType::HmacSha256 => {
let Key::HmacSha256(HmacSha256Key { secret }) = key else {
panic!("Wrong key type ({:?}) - expected HMAC SHA256", key.get_type());
};
*mac = Some(Box::new(HmacSha256::new_from_slice(&secret).unwrap()))
}
MacType::HmacSha384 => {
let Key::HmacSha384(HmacSha384Key { secret }) = key else {
panic!("Wrong key type ({:?}) - expected HMAC SHA384", key.get_type());
};
*mac = Some(Box::new(HmacSha384::new_from_slice(&secret).unwrap()))
}
MacType::HmacSha512 => {
let Key::HmacSha512(HmacSha512Key { secret }) = key else {
panic!("Wrong key type ({:?}) - expected HMAC SHA512", key.get_type());
};
*mac = Some(Box::new(HmacSha512::new_from_slice(&secret).unwrap()))
}
},
Helper::AsymmetricEncryptionKey(aenc, aenc_type) => match aenc_type {
AsymmetricEncryptionKeyType::RsaOaepSha1 => {
let Key::RsaKeypair(rsa) = key else {
panic!("Wrong key type ({:?}) - expected RSA keypair", key.get_type());
};
*aenc = Some(Box::new(RsaEncryptionKey::<Sha1, Oaep>::new(rsa.private_key())));
}
},
}
}
fn reset(&mut self) {
match self {
Helper::Digest(digest) => digest.reset(),
Helper::Cipher(cipher, _) => {
if let Some(cipher) = cipher {
cipher.reset()
}
}
Helper::Mac(mac, _) => {
if let Some(mac) = mac {
mac.reset()
}
}
Helper::AsymmetricEncryptionKey(_, _) => {}
}
}
}
#[derive(Debug, Eq, PartialEq)]
enum OpState {
Initial,
Active,
// Holds the finalized data yet to be fully extracted, along with an index
// pointing to the next byte to extract.
Extracting((Vec<u8>, usize)),
}
pub struct Operation {
algorithm: Algorithm,
mode: Mode,
key: Key,
max_key_size: u32, // The initial, allocated max key size.
state: OpState,
helper: Helper,
}
impl Operation {
fn new(algorithm: Algorithm, mode: Mode, max_key_size: u32) -> TeeResult<Self> {
Ok(Self {
algorithm,
mode,
key: Key::Data(NoKey {}),
max_key_size,
state: OpState::Initial,
helper: Helper::new(algorithm)?,
})
}
fn as_digest(&mut self) -> &mut Box<dyn Digest> {
if let Helper::Digest(ref mut digest) = &mut self.helper {
digest
} else {
panic!("{:?} is not a digest algorithm", self.algorithm)
}
}
fn as_cipher(&mut self) -> &mut Box<dyn Cipher> {
if let Helper::Cipher(ref mut cipher, _) = &mut self.helper {
cipher.as_mut().expect("TEE_OperationSetKey() has not yet been called")
} else {
panic!("{:?} is not a cipher algorithm", self.algorithm)
}
}
fn as_mac(&mut self) -> &mut Box<dyn Mac> {
if let Helper::Mac(ref mut mac, _) = &mut self.helper {
mac.as_mut().expect("TEE_OperationSetKey() has not yet been called")
} else {
panic!("{:?} is not a MAC algorithm", self.algorithm)
}
}
fn as_asymmetric_encryption_key(&mut self) -> &mut Box<dyn AsymmetricEncryptionKey> {
if let Helper::AsymmetricEncryptionKey(ref mut aenc, _) = &mut self.helper {
aenc.as_mut().expect("TEE_OperationSetKey() has not yet been called")
} else {
panic!("{:?} is not a asymmetric encryption key algorithm", self.algorithm)
}
}
// Returns whether the operation is in the extracting state and, if so, the
// number of remaining bytes left to extract.
fn is_extracting(&self) -> (bool, usize) {
if let OpState::Extracting((ref data, ref pos)) = self.state {
(true, data.len() - pos)
} else {
(false, 0)
}
}
fn reset(&mut self) {
self.helper.reset();
self.state = OpState::Initial;
}
fn set_key(&mut self, obj: Rc<RefCell<dyn Object>>) -> TeeResult {
let obj = obj.borrow();
let key = obj.key();
assert!(
key.max_size() <= self.max_key_size,
"Provided key size ({}) exceeds configured max ({})",
key.max_size(),
self.max_key_size
);
assert_eq!(
self.state,
OpState::Initial,
"Operation must be in the initial state (not {:?})",
self.state
);
match self.algorithm {
Algorithm::AesCbcNopad | Algorithm::AesEcbNopad => match self.mode {
Mode::Encrypt | Mode::Decrypt => {
let usage = obj.usage();
if self.mode == Mode::Encrypt {
assert!(usage.contains(Usage::ENCRYPT | Usage::VERIFY));
} else {
assert!(usage.contains(Usage::DECRYPT | Usage::SIGN));
}
}
_ => return Err(Error::NotImplemented),
},
Algorithm::Md5
| Algorithm::Sha1
| Algorithm::Sha224
| Algorithm::Sha256
| Algorithm::Sha384
| Algorithm::Sha512
| Algorithm::Sha3_224
| Algorithm::Sha3_256
| Algorithm::Sha3_384
| Algorithm::Sha3_512
| Algorithm::Shake128
| Algorithm::Shake256 => {
panic!("Algorithm {:?} has no associated object type", self.algorithm);
}
Algorithm::AesCmac
| Algorithm::HmacSha1
| Algorithm::HmacSha224
| Algorithm::HmacSha256
| Algorithm::HmacSha384
| Algorithm::HmacSha512 => {}
Algorithm::RsaesPkcs1OaepMgf1Sha1 => {}
_ => return Err(Error::NotImplemented),
};
self.key = key.clone();
self.helper.initialize(&self.key);
Ok(())
}
fn clear_key(&mut self) -> TeeResult {
self.key = Key::Data(NoKey {});
self.state = OpState::Initial;
Ok(())
}
// Provided the operation is in its extracting state, this reads as many
// bytes of that data as possible into the provided buffer, returning the
// size of the read.
fn extract_finalized(&mut self, buf: &mut [u8]) -> usize {
let OpState::Extracting((ref data, ref mut pos)) = self.state else {
panic!("Operation is not in the extracting state: {:?}", self.state);
};
if buf.is_empty() || *pos >= data.len() {
return 0;
}
let read_size = min(data.len() - *pos, buf.len());
let in_chunk = &data.as_slice()[*pos..(*pos + read_size)];
let out_chunk = &mut buf[..read_size];
out_chunk.copy_from_slice(in_chunk);
*pos += read_size;
read_size
}
// See TEE_DigestUpdate().
fn update_digest(&mut self, chunk: &[u8]) {
assert_eq!(self.mode, Mode::Digest);
assert!(self.state == OpState::Initial || self.state == OpState::Active);
self.as_digest().update(chunk);
self.state = OpState::Active;
}
// See TEE_DigestDoFinal().
//
// This should be two separate operations each with clean semantics:
// update + finalize. However, the spec wants the two zipped together here
// where the first can't happen if the preconditions of the second aren't
// met, adding complication.
fn update_and_finalize_digest_into(
&mut self,
last_chunk: &[u8],
buf: &mut [u8],
) -> Result<(), ErrorWithSize> {
assert_eq!(self.mode, Mode::Digest);
if let (true, left_to_extract) = self.is_extracting() {
assert!(last_chunk.is_empty());
if left_to_extract > buf.len() {
return Err(ErrorWithSize::short_buffer(left_to_extract));
}
let written = self.extract_digest(buf);
debug_assert_eq!(written, left_to_extract);
self.state = OpState::Initial;
return Ok(());
}
let buf = {
let digest = self.as_digest();
let output_size = digest.output_size();
if output_size > buf.len() {
return Err(ErrorWithSize::short_buffer(output_size));
}
if !last_chunk.is_empty() {
digest.update(last_chunk);
}
&mut buf[..output_size]
};
self.as_digest().finalize_into_reset(buf).unwrap();
self.state = OpState::Initial;
Ok(())
}
// Finalizes the digest and puts the operation in the extracting state. If
// already in the extracting state, this is a no-op.
fn finalize_digest(&mut self) {
assert_eq!(self.mode, Mode::Digest);
let (extracting, _) = self.is_extracting();
if extracting {
return;
}
let bytes = self.as_digest().finalize_reset();
self.state = OpState::Extracting((Vec::from(bytes), 0));
}
// See TEE_DigestExtract().
fn extract_digest(&mut self, buf: &mut [u8]) -> usize {
self.finalize_digest();
self.extract_finalized(buf)
}
// See TEE_CipherInit()
fn init_cipher(&mut self, iv: &[u8]) {
if self.state == OpState::Active {
self.as_cipher().reset();
} else {
assert_eq!(self.state, OpState::Initial);
}
self.as_cipher().set_iv(iv);
// Currently supported MAC algorithms don't deal in initialization vectors.
self.state = OpState::Active;
}
// The error value indicates the minimum required size of the output buffer
// (i.e., the total number of full blocks to encrypt/decrypt).
fn update_cipher(&mut self, src: &[u8], dest: &mut [u8]) -> Result<(), ErrorWithSize> {
assert_eq!(self.state, OpState::Active);
let block_size = self.as_cipher().block_size();
let num_blocks_in = src.len() / block_size;
let num_blocks_out = dest.len() / block_size;
// The output buffer size should be at least the total size of the
// number of full blocks in `src` to encrypt/decrypt.
if num_blocks_in > num_blocks_out {
return Err(ErrorWithSize::short_buffer(num_blocks_in * block_size));
}
if self.mode == Mode::Encrypt {
self.as_cipher().encrypt(src, dest);
} else {
assert_eq!(self.mode, Mode::Decrypt);
self.as_cipher().decrypt(src, dest);
}
Ok(())
}
fn update_cipher_in_place(&mut self, inout: &mut [u8]) {
assert_eq!(self.state, OpState::Active);
if self.mode == Mode::Encrypt {
self.as_cipher().encrypt_in_place(inout);
} else {
assert_eq!(self.mode, Mode::Decrypt);
self.as_cipher().decrypt_in_place(inout);
}
}
// The error value indicates the minimum required size of the output buffer
// (i.e., the total number of full blocks to encrypt/decrypt, which should
// be the same size as `src` itself).
fn finalize_cipher(&mut self, src: &[u8], dest: &mut [u8]) -> Result<(), ErrorWithSize> {
let block_size = self.as_cipher().block_size();
assert_eq!(src.len() % block_size, 0);
assert!(dest.len() >= src.len());
self.update_cipher(src, dest)?;
self.state = OpState::Initial;
Ok(())
}
fn finalize_cipher_in_place(&mut self, inout: &mut [u8]) {
let block_size = self.as_cipher().block_size();
assert_eq!(inout.len() % block_size, 0);
self.update_cipher_in_place(inout);
self.state = OpState::Initial;
}
// See TEE_MACInit().
fn init_mac(&mut self, _iv: &[u8]) {
assert_eq!(self.mode, Mode::Mac);
assert!(self.state == OpState::Initial || self.state == OpState::Active);
if self.state == OpState::Active {
self.as_mac().reset();
}
// Currently supported MAC algorithms don't deal in initialization
// vectors; the spec say to ignore the provided one in that case.
self.state = OpState::Active;
}
// See TEE_MACUpdate().
fn update_mac(&mut self, chunk: &[u8]) {
assert_eq!(self.mode, Mode::Mac);
assert_eq!(self.state, OpState::Active);
let mac = self.as_mac();
if !chunk.is_empty() {
mac.update(chunk);
}
}
// See TEE_MACComputeFinal().
fn compute_final_mac(
&mut self,
message: &[u8],
output: &mut [u8],
) -> Result<(), ErrorWithSize> {
assert_eq!(self.mode, Mode::Mac);
assert_eq!(self.state, OpState::Active);
let output_size = self.as_mac().output_size();
if output.len() < output_size {
return Err(ErrorWithSize::short_buffer(output_size));
}
// Make sure we validate the output buffer size before updating the
// digest.
let mac = self.as_mac();
if !message.is_empty() {
mac.update(message);
}
mac.finalize_into_reset(&mut output[..output_size]);
self.state = OpState::Initial;
Ok(())
}
// See TEE_MACCompareFinal().
fn compare_final_mac(&mut self, message: &[u8], expected: &[u8]) -> TeeResult {
self.update_mac(message);
let result = self.as_mac().verify_reset(expected);
self.state = OpState::Initial;
result
}
// See TEE_AsymmetricDecrypt().
fn asymmetric_decrypt(
&mut self,
params: &[Attribute],
src: &[u8],
dest: &mut [u8],
) -> Result<usize, ErrorWithSize> {
assert_eq!(self.mode, Mode::Decrypt);
self.as_asymmetric_encryption_key().decrypt(params, src, dest)
}
}
pub struct Operations {
operations: HashMap<OperationHandle, RefCell<Operation>>,
next_operation_handle_value: OperationHandle,
}
impl Operations {
pub fn new() -> Self {
Self {
operations: HashMap::new(),
next_operation_handle_value: OperationHandle::from_value(1),
}
}
pub fn allocate(
&mut self,
algorithm: Algorithm,
mode: Mode,
max_key_size: u32,
) -> TeeResult<OperationHandle> {
// We could directly check `FooKey::is_valid_size(max_key_size)` in
// each match arm, but by forwarding the appropriate key size check
// function pointer and doing it indirectly after the match statement,
// we ensure that the check is always made and reduce a bit of
// boilerplate while we're at it.
let is_valid_key_size = match algorithm {
Algorithm::AesCbcNopad | Algorithm::AesEcbNopad => {
match mode {
Mode::Encrypt | Mode::Decrypt => {}
_ => {
return Err(Error::NotSupported);
}
};
AesKey::is_valid_size
}
Algorithm::Md5
| Algorithm::Sha1
| Algorithm::Sha224
| Algorithm::Sha256
| Algorithm::Sha384
| Algorithm::Sha512
| Algorithm::Sha3_224
| Algorithm::Sha3_256
| Algorithm::Sha3_384
| Algorithm::Sha3_512
| Algorithm::Shake128
| Algorithm::Shake256 => {
if mode != Mode::Digest {
return Err(Error::NotSupported);
}
NoKey::is_valid_size
}
Algorithm::AesCmac => {
if mode != Mode::Mac {
return Err(Error::NotSupported);
}
AesKey::is_valid_size
}
Algorithm::HmacSha1 => {
if mode != Mode::Mac {
return Err(Error::NotSupported);
}
HmacSha1Key::is_valid_size
}
Algorithm::HmacSha224 => {
if mode != Mode::Mac {
return Err(Error::NotSupported);
}
HmacSha224Key::is_valid_size
}
Algorithm::HmacSha256 => {
if mode != Mode::Mac {
return Err(Error::NotSupported);
}
HmacSha256Key::is_valid_size
}
Algorithm::HmacSha384 => {
if mode != Mode::Mac {
return Err(Error::NotSupported);
}
HmacSha384Key::is_valid_size
}
Algorithm::HmacSha512 => {
if mode != Mode::Mac {
return Err(Error::NotSupported);
}
HmacSha512Key::is_valid_size
}
Algorithm::RsaesPkcs1OaepMgf1Sha1 => {
if mode != Mode::Encrypt && mode != Mode::Decrypt {
return Err(Error::NotSupported);
}
RsaKeypair::is_valid_size
}
_ => {
inspect_stubs::track_stub!(
TODO("https://fxbug.dev/360942581"),
"unsupported algorithm",
);
return Err(Error::NotImplemented);
}
};
if !is_valid_key_size(max_key_size) {
return Err(Error::NotSupported);
}
let operation = Operation::new(algorithm, mode, max_key_size)?;
let handle = self.allocate_operation_handle();
let prev = self.operations.insert(handle, RefCell::new(operation));
debug_assert!(prev.is_none());
Ok(handle)
}
fn allocate_operation_handle(&mut self) -> OperationHandle {
let handle = self.next_operation_handle_value;
self.next_operation_handle_value = OperationHandle::from_value(*handle + 1);
handle
}
fn get_mut(&self, operation: OperationHandle) -> RefMut<'_, Operation> {
self.operations.get(&operation).unwrap().borrow_mut()
}
pub fn free(&mut self, operation: OperationHandle) {
if operation.is_null() {
return;
}
let _ = self.operations.remove(&operation).unwrap();
}
pub fn reset(&mut self, operation: OperationHandle) {
self.get_mut(operation).reset()
}
pub fn set_key(
&mut self,
operation: OperationHandle,
key: Rc<RefCell<dyn Object>>,
) -> TeeResult {
self.get_mut(operation).set_key(key)
}
pub fn clear_key(&mut self, operation: OperationHandle) -> TeeResult {
self.get_mut(operation).clear_key()
}
pub fn update_digest(&mut self, operation: OperationHandle, chunk: &[u8]) {
self.get_mut(operation).update_digest(chunk);
}
pub fn update_and_finalize_digest_into(
&mut self,
operation: OperationHandle,
last_chunk: &[u8],
buf: &mut [u8],
) -> Result<(), ErrorWithSize> {
self.get_mut(operation).update_and_finalize_digest_into(last_chunk, buf)
}
pub fn extract_digest<'a>(&mut self, operation: OperationHandle, buf: &'a mut [u8]) -> usize {
self.get_mut(operation).extract_digest(buf)
}
pub fn init_cipher(&mut self, operation: OperationHandle, iv: &[u8]) {
self.get_mut(operation).init_cipher(iv)
}
pub fn update_cipher(
&mut self,
operation: OperationHandle,
input: &[u8],
output: &mut [u8],
) -> Result<(), ErrorWithSize> {
self.get_mut(operation).update_cipher(input, output)
}
pub fn update_cipher_in_place(&mut self, operation: OperationHandle, inout: &mut [u8]) {
self.get_mut(operation).update_cipher_in_place(inout)
}
pub fn finalize_cipher(
&mut self,
operation: OperationHandle,
input: &[u8],
output: &mut [u8],
) -> Result<(), ErrorWithSize> {
self.get_mut(operation).finalize_cipher(input, output)
}
pub fn finalize_cipher_in_place(&mut self, operation: OperationHandle, inout: &mut [u8]) {
self.get_mut(operation).finalize_cipher_in_place(inout)
}
pub fn init_mac(&mut self, operation: OperationHandle, iv: &[u8]) {
self.get_mut(operation).init_mac(iv)
}
pub fn update_mac(&mut self, operation: OperationHandle, chunk: &[u8]) {
self.get_mut(operation).update_mac(chunk)
}
pub fn compute_final_mac(
&mut self,
operation: OperationHandle,
message: &[u8],
mac: &mut [u8],
) -> Result<(), ErrorWithSize> {
self.get_mut(operation).compute_final_mac(message, mac)
}
pub fn compare_final_mac(
&mut self,
operation: OperationHandle,
message: &[u8],
mac: &[u8],
) -> TeeResult {
self.get_mut(operation).compare_final_mac(message, mac)
}
pub fn asymmetric_decrypt(
&mut self,
operation: OperationHandle,
params: &[Attribute],
src: &[u8],
dest: &mut [u8],
) -> Result<usize, ErrorWithSize> {
self.get_mut(operation).asymmetric_decrypt(params, src, dest)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[fuchsia::test]
fn operation_lifecycle() -> Result<(), Error> {
let mut operations = Operations::new();
let operation = operations.allocate(Algorithm::Sha256, Mode::Digest, 0).unwrap();
operations.free(operation);
Ok(())
}
}