• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #[cfg(test)]
30 mod test_serializer;
31 
32 mod err;
33 pub mod srv;
34 
35 #[allow(non_upper_case_globals)]
36 #[allow(non_camel_case_types)]
37 #[allow(non_snake_case)]
38 #[allow(unused)]
39 #[allow(deref_nullptr)] // https://github.com/rust-lang/rust-bindgen/issues/1651
40 mod sys {
41     include!(env!("BINDGEN_INC_FILE"));
42 }
43 
44 pub use err::HwBccError;
45 
46 use core::ffi::CStr;
47 use core::mem;
48 use coset::iana;
49 use num_derive::FromPrimitive;
50 use sys::*;
51 use tipc::Serializer;
52 use tipc::{Deserialize, Handle, Serialize};
53 use trusty_std::alloc::{TryAllocFrom, Vec};
54 use trusty_sys::{c_long, Error};
55 use zerocopy::IntoBytes;
56 
57 // Constant defined in trusty/user/base/interface/hwbcc/include/interface/hwbcc
58 pub const HWBCC_MAX_RESP_PAYLOAD_LENGTH: usize = HWBCC_MAX_RESP_PAYLOAD_SIZE as usize;
59 
60 #[derive(Copy, Clone, Debug, PartialEq, FromPrimitive)]
61 #[repr(u32)]
62 enum BccCmd {
63     RespBit = hwbcc_cmd_HWBCC_CMD_RESP_BIT,
64     SignData = hwbcc_cmd_HWBCC_CMD_SIGN_DATA,
65     GetBcc = hwbcc_cmd_HWBCC_CMD_GET_BCC,
66     GetDiceArtifacts = hwbcc_cmd_HWBCC_CMD_GET_DICE_ARTIFACTS,
67     NsDeprivilege = hwbcc_cmd_HWBCC_CMD_NS_DEPRIVILEGE,
68 }
69 
70 impl BccCmd {
validate_response(self, resp: u32) -> Result<(), HwBccError>71     fn validate_response(self, resp: u32) -> Result<(), HwBccError> {
72         if resp != self as u32 | BccCmd::RespBit as u32 {
73             log::error!("unknown response cmd: {:?}", resp);
74             return Err(HwBccError::InvalidCmdResponse);
75         }
76 
77         Ok(())
78     }
79 }
80 /// Generic header for all hwbcc requests.
81 #[derive(Debug, PartialEq)]
82 struct BccMsgHeader {
83     cmd: BccCmd,
84     test_mode: HwBccMode,
85     context: u64,
86 }
87 
88 /// The request message for the hwbcc service.
89 /// Note that the Serialize trait implementation ensures compatibility with
90 /// C clients and servers. We do not depend on the representation of this
91 /// struct for that compatibility.
92 #[derive(Debug, PartialEq)]
93 struct BccMsg<'a> {
94     header: BccMsgHeader,
95     sign_req: Option<SignDataMsg<'a>>,
96 }
97 
98 impl<'a> BccMsg<'a> {
new(cmd: BccCmd, test_mode: HwBccMode, context: u64) -> Self99     fn new(cmd: BccCmd, test_mode: HwBccMode, context: u64) -> Self {
100         Self { header: BccMsgHeader { cmd, test_mode, context }, sign_req: None }
101     }
102 
add_signing_req(&mut self, algorithm: SigningAlgorithm, data: &'a [u8], aad: &'a [u8])103     fn add_signing_req(&mut self, algorithm: SigningAlgorithm, data: &'a [u8], aad: &'a [u8]) {
104         self.sign_req = Some(SignDataMsg::new(algorithm, data, aad));
105     }
106 }
107 
108 impl<'s> Serialize<'s> for BccMsg<'s> {
serialize<'a: 's, S: Serializer<'s>>( &'a self, serializer: &mut S, ) -> Result<S::Ok, S::Error>109     fn serialize<'a: 's, S: Serializer<'s>>(
110         &'a self,
111         serializer: &mut S,
112     ) -> Result<S::Ok, S::Error> {
113         let ok = self.header.serialize(serializer)?;
114         if let Some(sign_req) = &self.sign_req {
115             return sign_req.serialize(serializer);
116         }
117         Ok(ok)
118     }
119 }
120 
121 impl<'s> Serialize<'s> for BccMsgHeader {
serialize<'a: 's, S: Serializer<'s>>( &'a self, serializer: &mut S, ) -> Result<S::Ok, S::Error>122     fn serialize<'a: 's, S: Serializer<'s>>(
123         &'a self,
124         serializer: &mut S,
125     ) -> Result<S::Ok, S::Error> {
126         // SAFETY:
127         //  All serialized attributes are trivial types with
128         //  corresponding C representations
129         unsafe {
130             serializer.serialize_as_bytes(&self.cmd)?;
131             serializer.serialize_as_bytes(&self.test_mode)?;
132             serializer.serialize_as_bytes(&self.context)
133         }
134     }
135 }
136 
137 /// Request to sign data.
138 #[derive(Debug, PartialEq)]
139 struct SignDataMsg<'a> {
140     /// Contains signing algorithm, data size, aad size
141     algorithm: SigningAlgorithm,
142     data: &'a [u8],
143     // size is needed for reference in serialization
144     data_size: u16,
145     aad: &'a [u8],
146     // size is needed for reference in serialization
147     aad_size: u32,
148 }
149 
150 impl<'a> SignDataMsg<'a> {
new(algorithm: SigningAlgorithm, data: &'a [u8], aad: &'a [u8]) -> Self151     fn new(algorithm: SigningAlgorithm, data: &'a [u8], aad: &'a [u8]) -> Self {
152         Self { algorithm, data, data_size: data.len() as u16, aad, aad_size: aad.len() as u32 }
153     }
154 }
155 
156 impl<'s> Serialize<'s> for SignDataMsg<'s> {
serialize<'a: 's, S: Serializer<'s>>( &'a self, serializer: &mut S, ) -> Result<S::Ok, S::Error>157     fn serialize<'a: 's, S: Serializer<'s>>(
158         &'a self,
159         serializer: &mut S,
160     ) -> Result<S::Ok, S::Error> {
161         // SAFETY:
162         //  All serialized attributes are trivial types with
163         //  corresponding C representations
164         unsafe {
165             serializer.serialize_as_bytes(&self.algorithm)?;
166             serializer.serialize_as_bytes(&self.data_size)?;
167             serializer.serialize_as_bytes(&self.aad_size)?;
168         }
169         serializer.serialize_bytes(self.data)?;
170         serializer.serialize_bytes(self.aad)
171     }
172 }
173 
174 /// Response type for all hwbcc services.
175 #[derive(Debug, PartialEq)]
176 struct HwBccResponse {
177     /// Status of command result.
178     status: i32,
179     /// Sent command, acknowledged by service if successful.
180     cmd: u32,
181     /// The payload size in bytes. We store this separately because
182     /// to serialize a response we need a size guaranteed to live as
183     /// long as the input serializer and calling payload.len() won't
184     /// satisfy that constraint. There are no current use cases where
185     /// payload is mutable. If they arise, this field needs to be kept
186     /// in sync with the length of the payload.
187     payload_size: u32,
188     /// Response data.
189     payload: Vec<u8>,
190 }
191 
192 impl HwBccResponse {
try_new_with_payload( status: Error, cmd: BccCmd, payload: Vec<u8>, ) -> Result<Self, HwBccError>193     fn try_new_with_payload(
194         status: Error,
195         cmd: BccCmd,
196         payload: Vec<u8>,
197     ) -> Result<Self, HwBccError> {
198         if payload.len() > HWBCC_MAX_RESP_PAYLOAD_LENGTH {
199             return Err(HwBccError::BadLen);
200         }
201 
202         Ok(HwBccResponse {
203             status: status as i32,
204             cmd: hwbcc_cmd_HWBCC_CMD_RESP_BIT | cmd as u32,
205             payload_size: payload.len().try_into()?,
206             payload: payload,
207         })
208     }
209 
new_without_payload(status: Error, cmd: BccCmd) -> HwBccResponse210     fn new_without_payload(status: Error, cmd: BccCmd) -> HwBccResponse {
211         HwBccResponse {
212             status: status as i32,
213             cmd: hwbcc_cmd_HWBCC_CMD_RESP_BIT | cmd as u32,
214             payload_size: 0,
215             payload: Vec::new(),
216         }
217     }
218 }
219 
220 impl<'s> Serialize<'s> for HwBccResponse {
221     /// We serialize for compatibility
222     /// with hwbcc_resp_hdr as defined in hwbcc.h
serialize<'a: 's, S: Serializer<'s>>( &'a self, serializer: &mut S, ) -> Result<S::Ok, S::Error>223     fn serialize<'a: 's, S: Serializer<'s>>(
224         &'a self,
225         serializer: &mut S,
226     ) -> Result<S::Ok, S::Error> {
227         serializer.serialize_bytes(self.cmd.as_bytes())?;
228         serializer.serialize_bytes(self.status.as_bytes())?;
229         serializer.serialize_bytes(self.payload_size.as_bytes())?;
230 
231         serializer.serialize_bytes(&self.payload)
232     }
233 }
234 
235 impl Deserialize for HwBccResponse {
236     type Error = HwBccError;
237     const MAX_SERIALIZED_SIZE: usize = HWBCC_MAX_RESP_PAYLOAD_LENGTH;
238 
deserialize(bytes: &[u8], handles: &mut [Option<Handle>]) -> Result<Self, Self::Error>239     fn deserialize(bytes: &[u8], handles: &mut [Option<Handle>]) -> Result<Self, Self::Error> {
240         if handles.len() != 0 {
241             for handle in handles {
242                 log::error!("unexpected handle: {:?}", handle);
243             }
244             return Err(HwBccError::InvalidCmdResponse);
245         }
246 
247         let header_size = mem::size_of::<hwbcc_resp_hdr>();
248         if bytes.len() < header_size {
249             log::error!("response too small");
250             return Err(HwBccError::BadLen);
251         }
252         // SAFETY: We have validated that the buffer contains enough data to
253         // represent a hwbcc_resp_hdr. The constructed lifetime here does not
254         // outlive the function and thus cannot outlive the lifetime of the
255         // buffer.
256         let (header, payload) = bytes.split_at(header_size);
257         let (prefix, header_body, _) = unsafe { header.align_to::<hwbcc_resp_hdr>() };
258         if !prefix.is_empty() {
259             log::error!("buffer too short or misaligned");
260             return Err(HwBccError::BadLen);
261         }
262         let msg: &hwbcc_resp_hdr = &header_body[0];
263         let response_payload = Vec::try_alloc_from(payload)?;
264 
265         if msg.payload_size as usize != response_payload.len() {
266             log::error!("response payload size is not as advertised");
267             return Err(HwBccError::BadLen);
268         }
269 
270         Ok(Self {
271             status: msg.status,
272             cmd: msg.cmd,
273             payload_size: msg.payload_size,
274             payload: response_payload,
275         })
276     }
277 }
278 
279 /// Specifies test or release request types.
280 ///
281 /// `Test` mode derives key seed bytes with a secure RNG,
282 /// and should differ with each invocation, intra-test.
283 /// `Release` mode relies on the hwkey service to derive
284 /// its key seed.
285 #[derive(Copy, Clone, Debug, PartialEq, FromPrimitive)]
286 #[repr(u32)]
287 pub enum HwBccMode {
288     Release = 0,
289     Test = 1,
290 }
291 
292 /// Signing algorithm options.
293 ///
294 /// Project uses CBOR Object Signing and Encryption (COSE) encodings.
295 #[non_exhaustive]
296 #[derive(Copy, Clone, Debug, PartialEq, FromPrimitive)]
297 #[repr(i16)]
298 pub enum SigningAlgorithm {
299     ED25519 = hwbcc_algorithm_HWBCC_ALGORITHM_ED25519 as i16,
300 }
301 
302 impl From<SigningAlgorithm> for iana::Algorithm {
from(alg: SigningAlgorithm) -> Self303     fn from(alg: SigningAlgorithm) -> Self {
304         match alg {
305             SigningAlgorithm::ED25519 => iana::Algorithm::EdDSA,
306         }
307     }
308 }
309 
recv_resp(session: &Handle, cmd: BccCmd, buf: &mut [u8]) -> Result<HwBccResponse, HwBccError>310 fn recv_resp(session: &Handle, cmd: BccCmd, buf: &mut [u8]) -> Result<HwBccResponse, HwBccError> {
311     let response: HwBccResponse = session.recv(buf)?;
312 
313     cmd.validate_response(response.cmd)?;
314 
315     if response.status != 0 {
316         log::error!("Status is not SUCCESS. Actual: {:?}", response.status);
317         return Err(HwBccError::System(Error::from(response.status as c_long)));
318     }
319 
320     Ok(response)
321 }
322 
read_payload(resp: HwBccResponse, buf: &mut [u8]) -> Result<&[u8], HwBccError>323 fn read_payload(resp: HwBccResponse, buf: &mut [u8]) -> Result<&[u8], HwBccError> {
324     let payload_size = resp.payload.len();
325     if payload_size > buf.len() {
326         log::error!("response payload is too large to fit into buffer");
327         return Err(HwBccError::BadLen);
328     }
329 
330     buf[..payload_size].copy_from_slice(&resp.payload);
331     Ok(&buf[..payload_size])
332 }
333 
334 /// DICE artifacts for a child node in the DICE chain/tree.
335 pub struct DiceArtifacts<'a> {
336     pub artifacts: &'a [u8],
337 }
338 
339 /// Retrieves DICE artifacts for a child node in the DICE chain/tree.
340 ///
341 /// The user supplies device-specific `context` as well as an
342 /// `artifacts` buffer, in which the service will write a portion
343 /// of its response payload.
344 ///
345 /// # Returns
346 ///
347 /// A [`DiceArtifacts`] result containing truncated prefixes of the populated
348 /// `artifacts` buffer. A [`HwBccError`] will be
349 /// returned if the user supplies an empty `artifacts` buffer.
350 ///
351 /// # Examples
352 ///
353 /// ```
354 /// let dice_artifacts_buf = &mut [0u8; HWBCC_MAX_RESP_PAYLOAD_LENGTH];
355 /// let DiceArtifacts { artifacts } =
356 ///     get_dice_artifacts(0, dice_artifacts_buf).expect("could not get protected data");
357 /// ```
358 ///
get_dice_artifacts<'a>( context: u64, artifacts: &'a mut [u8], ) -> Result<DiceArtifacts<'a>, HwBccError>359 pub fn get_dice_artifacts<'a>(
360     context: u64,
361     artifacts: &'a mut [u8],
362 ) -> Result<DiceArtifacts<'a>, HwBccError> {
363     if artifacts.is_empty() {
364         log::error!("DICE artifacts buffer must not be empty");
365         return Err(HwBccError::BadLen);
366     }
367 
368     let port = CStr::from_bytes_with_nul(HWBCC_PORT).expect("HWBCC_PORT was not null terminated");
369     let session = Handle::connect(port)?;
370 
371     let cmd = BccCmd::GetDiceArtifacts;
372     session.send(&BccMsg::new(cmd, HwBccMode::Release, context))?;
373 
374     let res_buf = &mut [0u8; HWBCC_MAX_RESP_PAYLOAD_LENGTH];
375     let response = recv_resp(&session, cmd, res_buf)?;
376     let artifacts = read_payload(response, artifacts)?;
377 
378     Ok(DiceArtifacts { artifacts })
379 }
380 
381 /// Deprivileges hwbcc from serving calls to non-secure clients.
382 ///
383 /// # Returns
384 ///
385 /// Err(HwBccError) on failure.
386 ///
387 /// # Examples
388 ///
389 /// ```
390 /// ns_deprivilege().expect("could not execute ns deprivilege");
391 ///
392 /// // assuming non-secure client
393 /// let dice_artifacts_buf = &mut [0u8; HWBCC_MAX_RESP_PAYLOAD_LENGTH];
394 /// let err =
395 ///     get_dice_artifacts(0, dice_artifacts_buf).expect_err("non-secure client has access to hwbcc services");
396 /// ```
397 ///
ns_deprivilege() -> Result<(), HwBccError>398 pub fn ns_deprivilege() -> Result<(), HwBccError> {
399     let port = CStr::from_bytes_with_nul(HWBCC_PORT).expect("HWBCC_PORT was not null terminated");
400     let session = Handle::connect(port)?;
401 
402     let cmd = BccCmd::NsDeprivilege;
403     session.send(&BccMsg::new(cmd, HwBccMode::Release, 0))?;
404 
405     let res_buf = &mut [0u8; HWBCC_MAX_RESP_PAYLOAD_LENGTH];
406     recv_resp(&session, cmd, res_buf)?;
407 
408     Ok(())
409 }
410 
411 /// Retrieves Boot certificate chain (BCC).
412 /// Clients may request test values using `test_mode`.
413 ///
414 /// # Examples
415 ///
416 /// ```
417 /// let mut cose_sign1_buf = [0u8; HWBCC_MAX_RESP_PAYLOAD_LENGTH];
418 ///
419 /// let bcc = get_bcc (
420 ///     HwBccMode::Test,
421 ///     &mut bcc_buf,
422 /// )
423 /// .expect("could not get bcc");
424 /// ```
get_bcc<'a>(test_mode: HwBccMode, bcc: &'a mut [u8]) -> Result<&'a [u8], HwBccError>425 pub fn get_bcc<'a>(test_mode: HwBccMode, bcc: &'a mut [u8]) -> Result<&'a [u8], HwBccError> {
426     if bcc.is_empty() {
427         log::error!("bcc buffer must not be empty");
428         return Err(HwBccError::BadLen);
429     }
430 
431     let port = CStr::from_bytes_with_nul(HWBCC_PORT).expect("HWBCC_PORT was not null terminated");
432     let session = Handle::connect(port)?;
433 
434     let cmd = BccCmd::GetBcc;
435     session.send(&BccMsg::new(cmd, test_mode, 0))?;
436 
437     let res_buf = &mut [0u8; HWBCC_MAX_RESP_PAYLOAD_LENGTH];
438     let response = recv_resp(&session, cmd, res_buf)?;
439     let bcc = read_payload(response, bcc)?;
440 
441     Ok(bcc)
442 }
443 
444 /// Retrieves the signed data in a COSE-Sign1 message. Data signed using the CDI leaf private key.
445 /// Clients may request to sign using a test key via test_mode.
446 ///
447 /// # Examples
448 ///
449 /// ```
450 /// let mut cose_sign1_buf = [0u8; HWBCC_MAX_RESP_PAYLOAD_LENGTH];
451 ///
452 /// let cose_sign1 = sign_data(
453 ///     HwBccMode::Test,
454 ///     SigningAlgorithm::ED25519,
455 ///     TEST_MAC_KEY,
456 ///     TEST_AAD,
457 ///     &mut cose_sign1_buf,
458 /// )
459 /// .expect("could not get signed data");
460 /// ```
sign_data<'a>( test_mode: HwBccMode, cose_algorithm: SigningAlgorithm, data: &[u8], aad: &[u8], cose_sign1: &'a mut [u8], ) -> Result<&'a [u8], HwBccError>461 pub fn sign_data<'a>(
462     test_mode: HwBccMode,
463     cose_algorithm: SigningAlgorithm,
464     data: &[u8],
465     aad: &[u8],
466     cose_sign1: &'a mut [u8],
467 ) -> Result<&'a [u8], HwBccError> {
468     if cose_sign1.is_empty() {
469         log::error!("cose_sign1 buffer must not be empty");
470         return Err(HwBccError::BadLen);
471     }
472 
473     if aad.len() > HWBCC_MAX_AAD_SIZE as usize {
474         log::error!("AAD exceeds HWCC_MAX_AAD_SIZE limit");
475         return Err(HwBccError::BadLen);
476     }
477 
478     let port = CStr::from_bytes_with_nul(HWBCC_PORT).expect("HWBCC_PORT was not null terminated");
479     let session = Handle::connect(port)?;
480 
481     let cmd = BccCmd::SignData;
482     let mut req = BccMsg::new(cmd, test_mode, 0);
483     req.add_signing_req(cose_algorithm, data, aad);
484 
485     session.send(&req)?;
486 
487     let res_buf = &mut [0u8; HWBCC_MAX_RESP_PAYLOAD_LENGTH];
488     let response = recv_resp(&session, cmd, res_buf)?;
489     let cose_sign1 = read_payload(response, cose_sign1)?;
490 
491     Ok(cose_sign1)
492 }
493 
494 #[cfg(test)]
495 mod tests {
496     use crate::test_serializer::TestSerializer;
497     use crate::{BccCmd, HwBccResponse};
498     use test::expect_eq;
499     use tipc::{Deserialize, Serialize};
500 
501     #[test]
test_hwbcc_resp_serde()502     fn test_hwbcc_resp_serde() {
503         let test_payload = "i am an expected response payload".as_bytes();
504 
505         let mut serializer = TestSerializer::default();
506         let hwbcc_resp =
507             HwBccResponse::try_new_with_payload(0.into(), BccCmd::GetBcc, test_payload.to_vec())
508                 .expect("Failed to create HwBccResponse for test");
509         let _ =
510             hwbcc_resp.serialize(&mut serializer).expect("serialization of HwBccResponse failed");
511 
512         let deserialized =
513             HwBccResponse::deserialize(&mut serializer.buffers, &mut serializer.handles)
514                 .expect("deserialization failed");
515 
516         expect_eq!(deserialized, hwbcc_resp);
517     }
518 }
519