1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 //! Interface library for communicating with the hwbcc service.
18 //!
19 //! Where these interfaces require user-supplied buffers, it is
20 //! important for the buffers supplied to be large enough to
21 //! contain the entirety of the hwbcc service response. All services
22 //! provided are subject to tipc failures; the corresponding error
23 //! codes will be returned in these cases.
24 
25 #![feature(allocator_api)]
26 
27 #[cfg(test)]
28 mod test;
29 
30 mod err;
31 
32 #[allow(non_upper_case_globals)]
33 #[allow(non_camel_case_types)]
34 #[allow(non_snake_case)]
35 #[allow(unused)]
36 #[allow(deref_nullptr)] // https://github.com/rust-lang/rust-bindgen/issues/1651
37 mod sys {
38     include!(env!("BINDGEN_INC_FILE"));
39 }
40 
41 pub use err::HwBccError;
42 
43 use core::ffi::CStr;
44 use core::mem;
45 use sys::*;
46 use tipc::Serializer;
47 use tipc::{Deserialize, Handle, Serialize};
48 use trusty_std::alloc::{TryAllocFrom, Vec};
49 use trusty_sys::{c_long, Error};
50 
51 // Constant defined in trusty/user/base/interface/hwbcc/include/interface/hwbcc
52 pub const HWBCC_MAX_RESP_PAYLOAD_LENGTH: usize = HWBCC_MAX_RESP_PAYLOAD_SIZE as usize;
53 
54 #[derive(Copy, Clone)]
55 #[repr(u32)]
56 enum BccCmd {
57     RespBit = hwbcc_cmd_HWBCC_CMD_RESP_BIT,
58     SignData = hwbcc_cmd_HWBCC_CMD_SIGN_DATA,
59     GetBcc = hwbcc_cmd_HWBCC_CMD_GET_BCC,
60     GetDiceArtifacts = hwbcc_cmd_HWBCC_CMD_GET_DICE_ARTIFACTS,
61     NsDeprivilege = hwbcc_cmd_HWBCC_CMD_NS_DEPRIVILEGE,
62 }
63 
64 impl BccCmd {
validate_response(self, resp: u32) -> Result<(), HwBccError>65     fn validate_response(self, resp: u32) -> Result<(), HwBccError> {
66         if resp != self as u32 | BccCmd::RespBit as u32 {
67             log::error!("unknown response cmd: {:?}", resp);
68             return Err(HwBccError::InvalidCmdResponse);
69         }
70 
71         Ok(())
72     }
73 }
74 /// Generic header for all hwbcc requests.
75 struct BccMsgHeader {
76     cmd: BccCmd,
77     test_mode: HwBccMode,
78     context: u64,
79 }
80 
81 impl<'s> Serialize<'s> for BccMsgHeader {
serialize<'a: 's, S: Serializer<'s>>( &'a self, serializer: &mut S, ) -> Result<S::Ok, S::Error>82     fn serialize<'a: 's, S: Serializer<'s>>(
83         &'a self,
84         serializer: &mut S,
85     ) -> Result<S::Ok, S::Error> {
86         // SAFETY:
87         //  All serialized attributes are trivial types with
88         //  corresponding C representations
89         unsafe {
90             serializer.serialize_as_bytes(&self.cmd)?;
91             serializer.serialize_as_bytes(&self.test_mode)?;
92             serializer.serialize_as_bytes(&self.context)
93         }
94     }
95 }
96 
97 /// Request to sign data.
98 struct SignDataMsg<'a> {
99     header: BccMsgHeader,
100     /// Contains signing algorithm, data size, aad size
101     algorithm: SigningAlgorithm,
102     data: &'a [u8],
103     // size is needed for reference in serialization
104     data_size: u16,
105     aad: &'a [u8],
106     // size is needed for reference in serialization
107     aad_size: u32,
108 }
109 
110 impl<'a> SignDataMsg<'a> {
new( header: BccMsgHeader, algorithm: SigningAlgorithm, data: &'a [u8], aad: &'a [u8], ) -> Self111     fn new(
112         header: BccMsgHeader,
113         algorithm: SigningAlgorithm,
114         data: &'a [u8],
115         aad: &'a [u8],
116     ) -> Self {
117         Self {
118             header,
119             algorithm,
120             data,
121             data_size: data.len() as u16,
122             aad,
123             aad_size: aad.len() as u32,
124         }
125     }
126 }
127 
128 impl<'s> Serialize<'s> for SignDataMsg<'s> {
serialize<'a: 's, S: Serializer<'s>>( &'a self, serializer: &mut S, ) -> Result<S::Ok, S::Error>129     fn serialize<'a: 's, S: Serializer<'s>>(
130         &'a self,
131         serializer: &mut S,
132     ) -> Result<S::Ok, S::Error> {
133         self.header.serialize(serializer)?;
134         // SAFETY:
135         //  All serialized attributes are trivial types with
136         //  corresponding C representations
137         unsafe {
138             serializer.serialize_as_bytes(&self.algorithm)?;
139             serializer.serialize_as_bytes(&self.data_size)?;
140             serializer.serialize_as_bytes(&self.aad_size)?;
141         }
142         serializer.serialize_bytes(self.data)?;
143         serializer.serialize_bytes(self.aad)
144     }
145 }
146 
147 /// Response type for all hwbcc services.
148 struct HwBccResponse {
149     /// Status of command result.
150     status: i32,
151     /// Sent command, acknowledged by service if successful.
152     cmd: u32,
153     /// Response data.
154     payload: Vec<u8>,
155 }
156 
157 impl Deserialize for HwBccResponse {
158     type Error = HwBccError;
159     const MAX_SERIALIZED_SIZE: usize = HWBCC_MAX_RESP_PAYLOAD_LENGTH;
160 
deserialize(bytes: &[u8], handles: &mut [Option<Handle>]) -> Result<Self, Self::Error>161     fn deserialize(bytes: &[u8], handles: &mut [Option<Handle>]) -> Result<Self, Self::Error> {
162         if handles.len() != 0 {
163             for handle in handles {
164                 log::error!("unexpected handle: {:?}", handle);
165             }
166             return Err(HwBccError::InvalidCmdResponse);
167         }
168 
169         let header_size = mem::size_of::<hwbcc_resp_hdr>();
170         if bytes.len() < header_size {
171             log::error!("response too small");
172             return Err(HwBccError::BadLen);
173         }
174         // SAFETY: We have validated that the buffer contains enough data to
175         // represent a hwbcc_resp_hdr. The constructed lifetime here does not
176         // outlive the function and thus cannot outlive the lifetime of the
177         // buffer.
178         let (header, payload) = bytes.split_at(header_size);
179         let (prefix, header_body, _) = unsafe { header.align_to::<hwbcc_resp_hdr>() };
180         if !prefix.is_empty() {
181             log::error!("buffer too short or misaligned");
182             return Err(HwBccError::BadLen);
183         }
184         let msg: &hwbcc_resp_hdr = &header_body[0];
185         let response_payload = Vec::try_alloc_from(payload)?;
186 
187         if msg.payload_size as usize != response_payload.len() {
188             log::error!("response payload size is not as advertised");
189             return Err(HwBccError::BadLen);
190         }
191 
192         Ok(Self { status: msg.status, cmd: msg.cmd, payload: response_payload })
193     }
194 }
195 
196 /// Specifies test or release request types.
197 ///
198 /// `Test` mode derives key seed bytes with a secure RNG,
199 /// and should differ with each invocation, intra-test.
200 /// `Release` mode relies on the hwkey service to derive
201 /// its key seed.
202 #[derive(Copy, Clone, Debug)]
203 #[repr(u32)]
204 pub enum HwBccMode {
205     Release = 0,
206     Test = 1,
207 }
208 
209 /// Signing algorithm options.
210 ///
211 /// Project uses CBOR Object Signing and Encryption (COSE) encodings.
212 #[non_exhaustive]
213 #[derive(Copy, Clone, Debug)]
214 #[repr(i16)]
215 pub enum SigningAlgorithm {
216     ED25519 = hwbcc_algorithm_HWBCC_ALGORITHM_ED25519 as i16,
217 }
218 
recv_resp(session: &Handle, cmd: BccCmd, buf: &mut [u8]) -> Result<HwBccResponse, HwBccError>219 fn recv_resp(session: &Handle, cmd: BccCmd, buf: &mut [u8]) -> Result<HwBccResponse, HwBccError> {
220     let response: HwBccResponse = session.recv(buf)?;
221 
222     cmd.validate_response(response.cmd)?;
223 
224     if response.status != 0 {
225         log::error!("Status is not SUCCESS. Actual: {:?}", response.status);
226         return Err(HwBccError::System(Error::from(response.status as c_long)));
227     }
228 
229     Ok(response)
230 }
231 
read_payload(resp: HwBccResponse, buf: &mut [u8]) -> Result<&[u8], HwBccError>232 fn read_payload(resp: HwBccResponse, buf: &mut [u8]) -> Result<&[u8], HwBccError> {
233     let payload_size = resp.payload.len();
234     if payload_size > buf.len() {
235         log::error!("response payload is too large to fit into buffer");
236         return Err(HwBccError::BadLen);
237     }
238 
239     buf[..payload_size].copy_from_slice(&resp.payload);
240     Ok(&buf[..payload_size])
241 }
242 
243 /// DICE artifacts for a child node in the DICE chain/tree.
244 pub struct DiceArtifacts<'a> {
245     pub artifacts: &'a [u8],
246 }
247 
248 /// Retrieves DICE artifacts for a child node in the DICE chain/tree.
249 ///
250 /// The user supplies device-specific `context` as well as an
251 /// `artifacts` buffer, in which the service will write a portion
252 /// of its response payload.
253 ///
254 /// # Returns
255 ///
256 /// A [`DiceArtifacts`] result containing truncated prefixes of the populated
257 /// `artifacts` buffer. A [`HwBccError`] will be
258 /// returned if the user supplies an empty `artifacts` buffer.
259 ///
260 /// # Examples
261 ///
262 /// ```
263 /// let dice_artifacts_buf = &mut [0u8; HWBCC_MAX_RESP_PAYLOAD_LENGTH];
264 /// let DiceArtifacts { artifacts } =
265 ///     get_dice_artifacts(0, dice_artifacts_buf).expect("could not get protected data");
266 /// ```
267 ///
get_dice_artifacts<'a>( context: u64, artifacts: &'a mut [u8], ) -> Result<DiceArtifacts<'a>, HwBccError>268 pub fn get_dice_artifacts<'a>(
269     context: u64,
270     artifacts: &'a mut [u8],
271 ) -> Result<DiceArtifacts<'a>, HwBccError> {
272     if artifacts.is_empty() {
273         log::error!("DICE artifacts buffer must not be empty");
274         return Err(HwBccError::BadLen);
275     }
276 
277     let port = CStr::from_bytes_with_nul(HWBCC_PORT).expect("HWBCC_PORT was not null terminated");
278     let session = Handle::connect(port)?;
279 
280     let cmd = BccCmd::GetDiceArtifacts;
281     session.send(&BccMsgHeader { cmd, test_mode: HwBccMode::Release, context })?;
282 
283     let res_buf = &mut [0u8; HWBCC_MAX_RESP_PAYLOAD_LENGTH];
284     let response = recv_resp(&session, cmd, res_buf)?;
285     let artifacts = read_payload(response, artifacts)?;
286 
287     Ok(DiceArtifacts { artifacts })
288 }
289 
290 /// Deprivileges hwbcc from serving calls to non-secure clients.
291 ///
292 /// # Returns
293 ///
294 /// Err(HwBccError) on failure.
295 ///
296 /// # Examples
297 ///
298 /// ```
299 /// ns_deprivilege().expect("could not execute ns deprivilege");
300 ///
301 /// // assuming non-secure client
302 /// let dice_artifacts_buf = &mut [0u8; HWBCC_MAX_RESP_PAYLOAD_LENGTH];
303 /// let err =
304 ///     get_dice_artifacts(0, dice_artifacts_buf).expect_err("non-secure client has access to hwbcc services");
305 /// ```
306 ///
ns_deprivilege() -> Result<(), HwBccError>307 pub fn ns_deprivilege() -> Result<(), HwBccError> {
308     let port = CStr::from_bytes_with_nul(HWBCC_PORT).expect("HWBCC_PORT was not null terminated");
309     let session = Handle::connect(port)?;
310 
311     let cmd = BccCmd::NsDeprivilege;
312     session.send(&BccMsgHeader { cmd, test_mode: HwBccMode::Release, context: 0 })?;
313 
314     let res_buf = &mut [0u8; HWBCC_MAX_RESP_PAYLOAD_LENGTH];
315     recv_resp(&session, cmd, res_buf)?;
316 
317     Ok(())
318 }
319 
320 /// Retrieves Boot certificate chain (BCC).
321 /// Clients may request test values using `test_mode`.
322 ///
323 /// # Examples
324 ///
325 /// ```
326 /// let mut cose_sign1_buf = [0u8; HWBCC_MAX_RESP_PAYLOAD_LENGTH];
327 ///
328 /// let bcc = get_bcc (
329 ///     HwBccMode::Test,
330 ///     &mut bcc_buf,
331 /// )
332 /// .expect("could not get bcc");
333 /// ```
get_bcc<'a>(test_mode: HwBccMode, bcc: &'a mut [u8]) -> Result<&'a [u8], HwBccError>334 pub fn get_bcc<'a>(test_mode: HwBccMode, bcc: &'a mut [u8]) -> Result<&'a [u8], HwBccError> {
335     if bcc.is_empty() {
336         log::error!("bcc buffer must not be empty");
337         return Err(HwBccError::BadLen);
338     }
339 
340     let port = CStr::from_bytes_with_nul(HWBCC_PORT).expect("HWBCC_PORT was not null terminated");
341     let session = Handle::connect(port)?;
342 
343     let cmd = BccCmd::GetBcc;
344     session.send(&BccMsgHeader { cmd, test_mode, context: 0 })?;
345 
346     let res_buf = &mut [0u8; HWBCC_MAX_RESP_PAYLOAD_LENGTH];
347     let response = recv_resp(&session, cmd, res_buf)?;
348     let bcc = read_payload(response, bcc)?;
349 
350     Ok(bcc)
351 }
352 
353 /// Retrieves the signed data in a COSE-Sign1 message. Data signed using the CDI leaf private key.
354 /// Clients may request to sign using a test key via test_mode.
355 ///
356 /// # Examples
357 ///
358 /// ```
359 /// let mut cose_sign1_buf = [0u8; HWBCC_MAX_RESP_PAYLOAD_LENGTH];
360 ///
361 /// let cose_sign1 = sign_data(
362 ///     HwBccMode::Test,
363 ///     SigningAlgorithm::ED25519,
364 ///     TEST_MAC_KEY,
365 ///     TEST_AAD,
366 ///     &mut cose_sign1_buf,
367 /// )
368 /// .expect("could not get signed data");
369 /// ```
sign_data<'a>( test_mode: HwBccMode, cose_algorithm: SigningAlgorithm, data: &[u8], aad: &[u8], cose_sign1: &'a mut [u8], ) -> Result<&'a [u8], HwBccError>370 pub fn sign_data<'a>(
371     test_mode: HwBccMode,
372     cose_algorithm: SigningAlgorithm,
373     data: &[u8],
374     aad: &[u8],
375     cose_sign1: &'a mut [u8],
376 ) -> Result<&'a [u8], HwBccError> {
377     if cose_sign1.is_empty() {
378         log::error!("cose_sign1 buffer must not be empty");
379         return Err(HwBccError::BadLen);
380     }
381 
382     if aad.len() > HWBCC_MAX_AAD_SIZE as usize {
383         log::error!("AAD exceeds HWCC_MAX_AAD_SIZE limit");
384         return Err(HwBccError::BadLen);
385     }
386 
387     let port = CStr::from_bytes_with_nul(HWBCC_PORT).expect("HWBCC_PORT was not null terminated");
388     let session = Handle::connect(port)?;
389 
390     let cmd = BccCmd::SignData;
391     let req =
392         SignDataMsg::new(BccMsgHeader { cmd, test_mode, context: 0 }, cose_algorithm, data, aad);
393     session.send(&req)?;
394 
395     let res_buf = &mut [0u8; HWBCC_MAX_RESP_PAYLOAD_LENGTH];
396     let response = recv_resp(&session, cmd, res_buf)?;
397     let cose_sign1 = read_payload(response, cose_sign1)?;
398 
399     Ok(cose_sign1)
400 }
401