• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Implementation of a HAL service for KeyMint.
2 //!
3 //! This implementation relies on a `SerializedChannel` abstraction for a communication channel to
4 //! the trusted application (TA).  Incoming method invocations for the HAL service are converted
5 //! into corresponding request structures, which are then serialized (using CBOR) and send down the
6 //! channel.  A serialized response is then read from the channel, which is deserialized into a
7 //! response structure.  The contents of this response structure are then used to populate the
8 //! return values of the HAL service method.
9 
10 #![allow(non_snake_case)]
11 
12 use core::{convert::TryInto, fmt::Debug};
13 use kmr_wire::{
14     cbor, cbor_type_error, keymint::ErrorCode, keymint::NEXT_MESSAGE_SIGNAL_TRUE, AsCborValue,
15     CborError, Code, KeyMintOperation,
16 };
17 use log::{error, info, warn};
18 use std::{
19     ffi::CString,
20     io::{Read, Write},
21     ops::DerefMut,
22     sync::MutexGuard,
23 };
24 
25 pub use binder;
26 
27 pub mod env;
28 pub mod hal;
29 pub mod keymint;
30 pub mod rpc;
31 pub mod secureclock;
32 pub mod sharedsecret;
33 #[cfg(test)]
34 mod tests;
35 
36 /// Emit a failure for a failed CBOR conversion.
37 #[inline]
failed_cbor(err: CborError) -> binder::Status38 pub fn failed_cbor(err: CborError) -> binder::Status {
39     binder::Status::new_service_specific_error(
40         ErrorCode::UnknownError as i32,
41         Some(&CString::new(format!("CBOR conversion failed: {:?}", err)).unwrap()),
42     )
43 }
44 
45 /// Abstraction of a channel to a secure world TA implementation.
46 pub trait SerializedChannel: Debug + Send {
47     /// Maximum supported size for the channel in bytes.
48     const MAX_SIZE: usize;
49 
50     /// Accepts serialized request messages and returns serialized return values
51     /// (or an error if communication via the channel is lost).
execute(&mut self, serialized_req: &[u8]) -> binder::Result<Vec<u8>>52     fn execute(&mut self, serialized_req: &[u8]) -> binder::Result<Vec<u8>>;
53 }
54 
55 /// A helper method to be used in the [`execute`] method above, in order to handle
56 /// responses received from the TA, especially those which are larger than the capacity of the
57 /// channel between the HAL and the TA.
58 /// This inspects the message, checks the first byte to see if the response arrives in multiple
59 /// messages. A boolean indicating whether or not to wait for the next message and the
60 /// response content (with the first byte stripped off) are returned to
61 /// the HAL service . Implementation of this method must be in sync with its counterpart
62 /// in the `kmr-ta` crate.
extract_rsp(rsp: &[u8]) -> binder::Result<(bool, &[u8])>63 pub fn extract_rsp(rsp: &[u8]) -> binder::Result<(bool, &[u8])> {
64     if rsp.len() < 2 {
65         return Err(binder::Status::new_exception(
66             binder::ExceptionCode::ILLEGAL_ARGUMENT,
67             Some(&CString::new("message is too small to extract the response data").unwrap()),
68         ));
69     }
70     Ok((rsp[0] == NEXT_MESSAGE_SIGNAL_TRUE, &rsp[1..]))
71 }
72 
73 /// Write a message to a stream-oriented [`Write`] item, with length framing.
write_msg<W: Write>(w: &mut W, data: &[u8]) -> binder::Result<()>74 pub fn write_msg<W: Write>(w: &mut W, data: &[u8]) -> binder::Result<()> {
75     // The underlying `Write` item does not guarantee delivery of complete messages.
76     // Make this possible by adding framing in the form of a big-endian `u32` holding
77     // the message length.
78     let data_len: u32 = data.len().try_into().map_err(|_e| {
79         binder::Status::new_exception(
80             binder::ExceptionCode::BAD_PARCELABLE,
81             Some(&CString::new("encoded request message too large").unwrap()),
82         )
83     })?;
84     let data_len_data = data_len.to_be_bytes();
85     w.write_all(&data_len_data[..]).map_err(|e| {
86         error!("Failed to write length to stream: {}", e);
87         binder::Status::new_exception(
88             binder::ExceptionCode::BAD_PARCELABLE,
89             Some(&CString::new("failed to write framing length").unwrap()),
90         )
91     })?;
92     w.write_all(data).map_err(|e| {
93         error!("Failed to write data to stream: {}", e);
94         binder::Status::new_exception(
95             binder::ExceptionCode::BAD_PARCELABLE,
96             Some(&CString::new("failed to write data").unwrap()),
97         )
98     })?;
99     Ok(())
100 }
101 
102 /// Read a message from a stream-oriented [`Read`] item, with length framing.
read_msg<R: Read>(r: &mut R) -> binder::Result<Vec<u8>>103 pub fn read_msg<R: Read>(r: &mut R) -> binder::Result<Vec<u8>> {
104     // The data read from the `Read` item has a 4-byte big-endian length prefix.
105     let mut len_data = [0u8; 4];
106     r.read_exact(&mut len_data).map_err(|e| {
107         error!("Failed to read length from stream: {}", e);
108         binder::Status::new_exception(binder::ExceptionCode::TRANSACTION_FAILED, None)
109     })?;
110     let len = u32::from_be_bytes(len_data);
111     let mut data = vec![0; len as usize];
112     r.read_exact(&mut data).map_err(|e| {
113         error!("Failed to read data from stream: {}", e);
114         binder::Status::new_exception(binder::ExceptionCode::TRANSACTION_FAILED, None)
115     })?;
116     Ok(data)
117 }
118 
119 /// Message-oriented wrapper around a pair of stream-oriented channels.  This allows a pair of
120 /// uni-directional channels that don't necessarily preserve message boundaries to appear as a
121 /// single bi-directional channel that does preserve message boundaries.
122 #[derive(Debug)]
123 pub struct MessageChannel<R: Read, W: Write> {
124     r: R,
125     w: W,
126 }
127 
128 impl<R: Read + Debug + Send, W: Write + Debug + Send> SerializedChannel for MessageChannel<R, W> {
129     const MAX_SIZE: usize = 4096;
130 
execute(&mut self, serialized_req: &[u8]) -> binder::Result<Vec<u8>>131     fn execute(&mut self, serialized_req: &[u8]) -> binder::Result<Vec<u8>> {
132         write_msg(&mut self.w, serialized_req)?;
133         read_msg(&mut self.r)
134     }
135 }
136 
137 /// Execute an operation by serializing and sending a request structure down a channel, and
138 /// deserializing and returning the response.
139 ///
140 /// This implementation relies on the internal serialization format for `PerformOpReq` and
141 /// `PerformOpRsp` to allow direct use of the specific request/response types.
channel_execute<T, R, S>(channel: &mut T, req: R) -> binder::Result<S> where T: SerializedChannel, R: AsCborValue + Code<KeyMintOperation>, S: AsCborValue + Code<KeyMintOperation>,142 fn channel_execute<T, R, S>(channel: &mut T, req: R) -> binder::Result<S>
143 where
144     T: SerializedChannel,
145     R: AsCborValue + Code<KeyMintOperation>,
146     S: AsCborValue + Code<KeyMintOperation>,
147 {
148     // Manually build an array that includes the opcode and the encoded request and encode it.
149     // This is equivalent to `PerformOpReq::to_vec()`.
150     let req_arr = cbor::value::Value::Array(vec![
151         <R>::CODE.to_cbor_value().map_err(failed_cbor)?,
152         req.to_cbor_value().map_err(failed_cbor)?,
153     ]);
154     let mut req_data = Vec::new();
155     cbor::ser::into_writer(&req_arr, &mut req_data).map_err(|e| {
156         binder::Status::new_service_specific_error(
157             ErrorCode::UnknownError as i32,
158             Some(
159                 &CString::new(format!("failed to write CBOR request to buffer: {:?}", e)).unwrap(),
160             ),
161         )
162     })?;
163 
164     if req_data.len() > T::MAX_SIZE {
165         error!(
166             "HAL operation {:?} encodes bigger {} than max size {}",
167             <R>::CODE,
168             req_data.len(),
169             T::MAX_SIZE
170         );
171         return Err(binder::Status::new_service_specific_error(
172             ErrorCode::InvalidInputLength as i32,
173             Some(&CString::new("encoded request message too large").unwrap()),
174         ));
175     }
176 
177     // Send in request bytes, get back response bytes.
178     let rsp_data = channel.execute(&req_data)?;
179 
180     // Convert the raw response data to an array of [error code, opt_response].
181     let rsp_value = kmr_wire::read_to_value(&rsp_data).map_err(failed_cbor)?;
182     let mut rsp_array = match rsp_value {
183         cbor::value::Value::Array(a) if a.len() == 2 => a,
184         _ => {
185             error!("HAL: failed to parse response data 2-array!");
186             return cbor_type_error(&rsp_value, "arr of len 2").map_err(failed_cbor);
187         }
188     };
189     let opt_response = rsp_array.remove(1);
190     let error_code = <i32>::from_cbor_value(rsp_array.remove(0)).map_err(failed_cbor)?;
191     // The error code is in a numbering space that depends on the specific HAL being
192     // invoked (IRemotelyProvisionedComponent vs. the rest). However, the OK value is
193     // the same in all spaces.
194     if error_code != ErrorCode::Ok as i32 {
195         warn!("HAL: command {:?} failed: {:?}", <R>::CODE, error_code);
196         return Err(binder::Status::new_service_specific_error(error_code, None));
197     }
198 
199     // The optional response should be an array of exactly 1 element (because the 0-element case
200     // corresponds to a non-OK error code, which has just been dealt with).
201     let rsp = match opt_response {
202         cbor::value::Value::Array(mut a) if a.len() == 1 => a.remove(0),
203         _ => {
204             error!("HAL: failed to parse response data structure!");
205             return cbor_type_error(&opt_response, "arr of len 1").map_err(failed_cbor);
206         }
207     };
208 
209     // The response is expected to be an array of 2 elements: a op_type code and an encoded response
210     // structure.  The op_type code indicates the type of response structure, which should be what
211     // we expect.
212     let mut inner_rsp_array = match rsp {
213         cbor::value::Value::Array(a) if a.len() == 2 => a,
214         _ => {
215             error!("HAL: failed to parse inner response data structure!");
216             return cbor_type_error(&rsp, "arr of len 2").map_err(failed_cbor);
217         }
218     };
219     let inner_rsp = inner_rsp_array.remove(1);
220     let op_type =
221         <KeyMintOperation>::from_cbor_value(inner_rsp_array.remove(0)).map_err(failed_cbor)?;
222     if op_type != <S>::CODE {
223         error!("HAL: inner response data for unexpected opcode {:?}!", op_type);
224         return Err(failed_cbor(CborError::UnexpectedItem("wrong ret code", "rsp ret code")));
225     }
226 
227     <S>::from_cbor_value(inner_rsp).map_err(failed_cbor)
228 }
229 
230 /// Abstraction of a HAL service that uses an underlying [`SerializedChannel`] to communicate with
231 /// an associated TA.
232 trait ChannelHalService<T: SerializedChannel> {
233     /// Return the underlying channel.
channel(&self) -> MutexGuard<T>234     fn channel(&self) -> MutexGuard<T>;
235 
236     /// Execute the given request, by serializing it and sending it down the internal channel.  Then
237     /// read and deserialize the response.
execute<R, S>(&self, req: R) -> binder::Result<S> where R: AsCborValue + Code<KeyMintOperation>, S: AsCborValue + Code<KeyMintOperation>,238     fn execute<R, S>(&self, req: R) -> binder::Result<S>
239     where
240         R: AsCborValue + Code<KeyMintOperation>,
241         S: AsCborValue + Code<KeyMintOperation>,
242     {
243         channel_execute(self.channel().deref_mut(), req)
244     }
245 }
246 
247 /// Let the TA know information about the userspace environment.
send_hal_info<T: SerializedChannel>(channel: &mut T) -> binder::Result<()>248 pub fn send_hal_info<T: SerializedChannel>(channel: &mut T) -> binder::Result<()> {
249     let req = env::populate_hal_info().map_err(|e| {
250         binder::Status::new_exception(
251             binder::ExceptionCode::BAD_PARCELABLE,
252             Some(&CString::new(format!("failed to determine HAL environment: {}", e)).unwrap()),
253         )
254     })?;
255     info!("HAL->TA: environment info is {:?}", req);
256     let _rsp: kmr_wire::SetHalInfoResponse = channel_execute(channel, req)?;
257     Ok(())
258 }
259 
260 /// Let the TA know information about the boot environment.
send_boot_info<T: SerializedChannel>( channel: &mut T, req: kmr_wire::SetBootInfoRequest, ) -> binder::Result<()>261 pub fn send_boot_info<T: SerializedChannel>(
262     channel: &mut T,
263     req: kmr_wire::SetBootInfoRequest,
264 ) -> binder::Result<()> {
265     info!("boot->TA: boot info is {:?}", req);
266     let _rsp: kmr_wire::SetBootInfoResponse = channel_execute(channel, req)?;
267     Ok(())
268 }
269 
270 /// Provision the TA with attestation ID information.
send_attest_ids<T: SerializedChannel>( channel: &mut T, ids: kmr_wire::AttestationIdInfo, ) -> binder::Result<()>271 pub fn send_attest_ids<T: SerializedChannel>(
272     channel: &mut T,
273     ids: kmr_wire::AttestationIdInfo,
274 ) -> binder::Result<()> {
275     let req = kmr_wire::SetAttestationIdsRequest { ids };
276     info!("provision->attestation IDs are {:?}", req);
277     let _rsp: kmr_wire::SetAttestationIdsResponse = channel_execute(channel, req)?;
278     Ok(())
279 }
280 
281 /// Let the TA know that early boot has ended
early_boot_ended<T: SerializedChannel>(channel: &mut T) -> binder::Result<()>282 pub fn early_boot_ended<T: SerializedChannel>(channel: &mut T) -> binder::Result<()> {
283     info!("boot->TA: early boot ended");
284     let req = kmr_wire::EarlyBootEndedRequest {};
285     let _rsp: kmr_wire::EarlyBootEndedResponse = channel_execute(channel, req)?;
286     Ok(())
287 }
288