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