blob: ec3db6addc4af35093c6b82b4747cf98514cd7be [file] [log] [blame]
//! Functionality for KeyMint implementation that is common across HAL and TA.
#![no_std]
extern crate alloc;
use alloc::{
string::{String, ToString},
vec::Vec,
};
use core::convert::From;
use der::ErrorKind;
use kmr_wire::{cbor, keymint::ErrorCode, rpc, CborError};
pub use kmr_wire as wire;
pub mod crypto;
pub mod keyblob;
pub mod tag;
/// General error type.
#[derive(Debug)]
pub enum Error {
/// Error from CBOR conversion.
Cbor(CborError),
/// Error from ASN.1 DER conversion.
Der(ErrorKind),
/// Error as reported on the HAL interface.
///
/// The `IKeyMintDevice`, `ISharedSecret` and `ISecureClock` HALs all share the same numbering
/// space for error codes, encoded here as [`kmr_wire::keymint::ErrorCode`].
Hal(ErrorCode, String),
/// Error as reported on the `IRemotelyProvisionedComponent` HAL, which uses its own error
/// codes.
Rpc(rpc::ErrorCode, String),
/// Memory allocation error.
///
/// This holds a string literal rather than an allocated `String` to avoid allocating in an
/// allocation error path.
Alloc(&'static str),
}
// The following macros for error generation allow the message portion to be automatically
// compiled out in future, avoiding potential information leakage and allocation.
/// Macro to build an [`Error::Hal`] instance for a specific [`ErrorCode`] value known at compile
/// time: `km_err!(InvalidTag, "some {} format", arg)`.
#[macro_export]
macro_rules! km_err {
{ $error_code:ident, $($arg:tt)+ } => {
$crate::Error::Hal(kmr_wire::keymint::ErrorCode::$error_code,
alloc::format!("{}:{}: {}", file!(), line!(), format_args!($($arg)+))) };
}
/// Macro to build an [`Error::Hal`] instance:
/// `km_verr!(rc, "some {} format", arg)`.
#[macro_export]
macro_rules! km_verr {
{ $error_code:expr, $($arg:tt)+ } => {
$crate::Error::Hal($error_code,
alloc::format!("{}:{}: {}", file!(), line!(), format_args!($($arg)+))) };
}
/// Macro to build an [`Error::Alloc`] instance. Note that this builds a `&'static str` at compile
/// time, so there is no allocation needed for the message (which would be failure-prone when
/// dealing with an allocation failure).
#[macro_export]
macro_rules! alloc_err {
{ $len:expr } => {
$crate::Error::Alloc(
concat!(file!(), ":", line!(), ": failed allocation of size ", stringify!($len))
)
}
}
/// Macro to build an [`Error::Der`] instance from a [`der::Error`].
#[macro_export]
macro_rules! der_err {
{ $err:expr, $($arg:tt)+ } => {
{
log::warn!("{}: {:?} at {:?}", format_args!($($arg)+), $err, $err.position());
$crate::Error::Der($err.kind())
}
}
}
/// Macro to build an [`Error::Rpc`] instance for a specific [`rpc::ErrorCode`] value known at
/// compile time: `rpc_err!(Removed, "some {} format", arg)`.
#[macro_export]
macro_rules! rpc_err {
{ $error_code:ident, $($arg:tt)+ } => {
$crate::Error::Rpc(kmr_wire::rpc::ErrorCode::$error_code,
alloc::format!("{}:{}: {}", file!(), line!(), format_args!($($arg)+))) };
}
/// Macro to allocate a `Vec<T>` with the given length reserved, detecting allocation failure.
#[macro_export]
macro_rules! vec_try_with_capacity {
{ $len:expr} => {
{
let mut v = alloc::vec::Vec::new();
match v.try_reserve($len) {
Err(_e) => Err($crate::alloc_err!($len)),
Ok(_) => Ok(v),
}
}
}
}
/// Macro that mimics `vec!` but which detects allocation failure.
#[macro_export]
macro_rules! vec_try {
{ $elem:expr ; $len:expr } => {
kmr_wire::vec_try_fill_with_alloc_err($elem, $len, || $crate::alloc_err!($len))
};
{ $x1:expr, $x2:expr, $x3:expr, $x4:expr $(,)? } => {
kmr_wire::vec_try4_with_alloc_err($x1, $x2, $x3, $x4, || $crate::alloc_err!(4))
};
{ $x1:expr, $x2:expr, $x3:expr $(,)? } => {
kmr_wire::vec_try3_with_alloc_err($x1, $x2, $x3, || $crate::alloc_err!(3))
};
{ $x1:expr, $x2:expr $(,)? } => {
kmr_wire::vec_try2_with_alloc_err($x1, $x2, || $crate::alloc_err!(2))
};
{ $x1:expr $(,)? } => {
kmr_wire::vec_try1_with_alloc_err($x1, || $crate::alloc_err!(1))
};
}
/// Function that mimics `slice.to_vec()` but which detects allocation failures.
#[inline]
pub fn try_to_vec<T: Clone>(s: &[T]) -> Result<Vec<T>, Error> {
let mut v = vec_try_with_capacity!(s.len())?;
v.extend_from_slice(s);
Ok(v)
}
/// Extension trait to provide fallible-allocation variants of `Vec` methods.
pub trait FallibleAllocExt<T> {
/// Try to add the `value` to the collection, failing on memory exhaustion.
fn try_push(&mut self, value: T) -> Result<(), alloc::collections::TryReserveError>;
/// Try to extend the collection with the contents of `other`, failing on memory exhaustion.
fn try_extend_from_slice(
&mut self,
other: &[T],
) -> Result<(), alloc::collections::TryReserveError>
where
T: Clone;
}
impl<T> FallibleAllocExt<T> for Vec<T> {
fn try_push(&mut self, value: T) -> Result<(), alloc::collections::TryReserveError> {
self.try_reserve(1)?;
self.push(value);
Ok(())
}
fn try_extend_from_slice(
&mut self,
other: &[T],
) -> Result<(), alloc::collections::TryReserveError>
where
T: Clone,
{
self.try_reserve(other.len())?;
self.extend_from_slice(other);
Ok(())
}
}
impl From<alloc::collections::TryReserveError> for Error {
fn from(_e: alloc::collections::TryReserveError) -> Self {
Error::Hal(
kmr_wire::keymint::ErrorCode::MemoryAllocationFailed,
"allocation of Vec failed".to_string(),
)
}
}
impl From<CborError> for Error {
fn from(e: CborError) -> Self {
Error::Cbor(e)
}
}
impl From<cbor::value::Error> for Error {
fn from(e: cbor::value::Error) -> Self {
Self::Cbor(e.into())
}
}
/// Check for an expected error.
#[macro_export]
macro_rules! expect_err {
($result:expr, $err_msg:expr) => {
assert!(
$result.is_err(),
"Expected error containing '{}', got success {:?}",
$err_msg,
$result
);
let err = $result.err();
assert!(
alloc::format!("{:?}", err).contains($err_msg),
"Unexpected error {:?}, doesn't contain '{}'",
err,
$err_msg
);
};
}