1 //! Functionality for KeyMint implementation that is common across HAL and TA.
2
3 #![no_std]
4 extern crate alloc;
5
6 use alloc::{
7 string::{String, ToString},
8 vec::Vec,
9 };
10 use core::convert::From;
11 use der::ErrorKind;
12 use kmr_wire::{cbor, keymint::ErrorCode, rpc, CborError};
13
14 pub use kmr_wire as wire;
15
16 pub mod crypto;
17 pub mod keyblob;
18 pub mod tag;
19
20 /// General error type.
21 #[derive(Debug)]
22 pub enum Error {
23 Cbor(CborError),
24 Der(ErrorKind),
25 // The IKeyMintDevice, ISharedSecret and ISecureClock HALs all share the same numbering
26 // space for error codes, encoded here as [`kmr_wire::keymint::ErrorCode`].
27 Hal(ErrorCode, String),
28 // The IRemotelyProvisionedComponent HAL uses its own error codes.
29 Rpc(rpc::ErrorCode, String),
30 // For an allocation error, hold a string literal rather than an allocated String to
31 // avoid allocating in error path.
32 Alloc(&'static str),
33 }
34
35 // The following macros for error generation allow the message portion to be automatically
36 // compiled out in future, avoiding potential information leakage and allocation.
37
38 /// Macro to build an [`Error::Hal`] instance for a specific [`ErrorCode`] value known at compile
39 /// time: `km_err!(InvalidTag, "some {} format", arg)`.
40 #[macro_export]
41 macro_rules! km_err {
42 { $error_code:ident, $($arg:tt)+ } => {
43 $crate::Error::Hal(kmr_wire::keymint::ErrorCode::$error_code,
44 alloc::format!("{}:{}: {}", file!(), line!(), format_args!($($arg)+))) };
45 }
46
47 /// Macro to build an [`Error::Hal`] instance:
48 /// `km_verr!(rc, "some {} format", arg)`.
49 #[macro_export]
50 macro_rules! km_verr {
51 { $error_code:expr, $($arg:tt)+ } => {
52 $crate::Error::Hal($error_code,
53 alloc::format!("{}:{}: {}", file!(), line!(), format_args!($($arg)+))) };
54 }
55
56 /// Macro to build an [`Error::Alloc`] instance. Note that this builds a `&'static str` at compile
57 /// time, so there is no allocation needed for the message (which would be failure-prone when
58 /// dealing with an allocation failure).
59 #[macro_export]
60 macro_rules! alloc_err {
61 { $len:expr } => {
62 $crate::Error::Alloc(
63 concat!(file!(), ":", line!(), ": failed allocation of size ", stringify!($len))
64 )
65 }
66 }
67
68 /// Macro to build an [`Error::Rpc`] instance for a specific [`rpc::ErrorCode`] value known at
69 /// compile time: `rpc_err!(Removed, "some {} format", arg)`.
70 #[macro_export]
71 macro_rules! rpc_err {
72 { $error_code:ident, $($arg:tt)+ } => {
73 $crate::Error::Rpc(kmr_wire::rpc::ErrorCode::$error_code,
74 alloc::format!("{}:{}: {}", file!(), line!(), format_args!($($arg)+))) };
75 }
76
77 /// Macro to allocate a `Vec<T>` with the given length reserved, detecting allocation failure.
78 #[macro_export]
79 macro_rules! vec_try_with_capacity {
80 { $len:expr} => {
81 {
82 let mut v = alloc::vec::Vec::new();
83 match v.try_reserve($len) {
84 Err(_e) => Err($crate::alloc_err!($len)),
85 Ok(_) => Ok(v),
86 }
87 }
88 }
89 }
90
91 /// Macro that mimics `vec!` but which detects allocation failure.
92 #[macro_export]
93 macro_rules! vec_try {
94 { $elem:expr ; $len:expr } => {
95 kmr_wire::vec_try_fill_with_alloc_err($elem, $len, || $crate::alloc_err!($len))
96 };
97 { $x1:expr, $x2:expr, $x3:expr, $x4:expr $(,)? } => {
98 kmr_wire::vec_try4_with_alloc_err($x1, $x2, $x3, $x4, || $crate::alloc_err!(4))
99 };
100 { $x1:expr, $x2:expr, $x3:expr $(,)? } => {
101 kmr_wire::vec_try3_with_alloc_err($x1, $x2, $x3, || $crate::alloc_err!(3))
102 };
103 { $x1:expr, $x2:expr $(,)? } => {
104 kmr_wire::vec_try2_with_alloc_err($x1, $x2, || $crate::alloc_err!(2))
105 };
106 { $x1:expr $(,)? } => {
107 kmr_wire::vec_try1_with_alloc_err($x1, || $crate::alloc_err!(1))
108 };
109 }
110
111 /// Function that mimics `slice.to_vec()` but which detects allocation failures.
112 #[inline]
try_to_vec<T: Clone>(s: &[T]) -> Result<Vec<T>, Error>113 pub fn try_to_vec<T: Clone>(s: &[T]) -> Result<Vec<T>, Error> {
114 let mut v = vec_try_with_capacity!(s.len())?;
115 v.extend_from_slice(s);
116 Ok(v)
117 }
118
119 /// Extension trait to provide fallible-allocation variants of `Vec` methods.
120 pub trait FallibleAllocExt<T> {
try_push(&mut self, value: T) -> Result<(), alloc::collections::TryReserveError>121 fn try_push(&mut self, value: T) -> Result<(), alloc::collections::TryReserveError>;
try_extend_from_slice( &mut self, other: &[T], ) -> Result<(), alloc::collections::TryReserveError> where T: Clone122 fn try_extend_from_slice(
123 &mut self,
124 other: &[T],
125 ) -> Result<(), alloc::collections::TryReserveError>
126 where
127 T: Clone;
128 }
129
130 impl<T> FallibleAllocExt<T> for Vec<T> {
try_push(&mut self, value: T) -> Result<(), alloc::collections::TryReserveError>131 fn try_push(&mut self, value: T) -> Result<(), alloc::collections::TryReserveError> {
132 self.try_reserve(1)?;
133 self.push(value);
134 Ok(())
135 }
try_extend_from_slice( &mut self, other: &[T], ) -> Result<(), alloc::collections::TryReserveError> where T: Clone,136 fn try_extend_from_slice(
137 &mut self,
138 other: &[T],
139 ) -> Result<(), alloc::collections::TryReserveError>
140 where
141 T: Clone,
142 {
143 self.try_reserve(other.len())?;
144 self.extend_from_slice(other);
145 Ok(())
146 }
147 }
148
149 impl From<alloc::collections::TryReserveError> for Error {
from(_e: alloc::collections::TryReserveError) -> Self150 fn from(_e: alloc::collections::TryReserveError) -> Self {
151 Error::Hal(
152 kmr_wire::keymint::ErrorCode::MemoryAllocationFailed,
153 "allocation of Vec failed".to_string(),
154 )
155 }
156 }
157
158 impl From<CborError> for Error {
from(e: CborError) -> Self159 fn from(e: CborError) -> Self {
160 Error::Cbor(e)
161 }
162 }
163
164 impl From<cbor::value::Error> for Error {
from(e: cbor::value::Error) -> Self165 fn from(e: cbor::value::Error) -> Self {
166 Self::Cbor(e.into())
167 }
168 }
169
170 impl From<der::Error> for Error {
from(e: der::Error) -> Self171 fn from(e: der::Error) -> Self {
172 Error::Der(e.kind())
173 }
174 }
175
176 /// Check for an expected error.
177 #[macro_export]
178 macro_rules! expect_err {
179 ($result:expr, $err_msg:expr) => {
180 assert!(
181 $result.is_err(),
182 "Expected error containing '{}', got success {:?}",
183 $err_msg,
184 $result
185 );
186 let err = $result.err();
187 assert!(
188 alloc::format!("{:?}", err).contains($err_msg),
189 "Unexpected error {:?}, doesn't contain '{}'",
190 err,
191 $err_msg
192 );
193 };
194 }
195