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