blob: abb8a7bc2b58e8958b062478fb0e7d2c2438ae65 [file] [log] [blame]
Janis Danisevskisc51dff82021-10-20 09:51:16 -07001// Copyright 2021, The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Implements utility functions and types for diced and the dice HAL.
16
17use android_hardware_security_dice::aidl::android::hardware::security::dice::{
18 Bcc::Bcc, BccHandover::BccHandover, InputValues::InputValues as BinderInputValues,
19 Mode::Mode as BinderMode,
20};
21use anyhow::{anyhow, Context, Result};
22use dice::ContextImpl;
23use diced_open_dice_cbor as dice;
24use keystore2_crypto::ZVec;
25use std::convert::{TryFrom, TryInto};
26
27/// This new type wraps a reference to BinderInputValues and implements the open dice
28/// InputValues trait.
29#[derive(Debug)]
30pub struct InputValues<'a>(&'a BinderInputValues);
31
32impl<'a> TryFrom<&'a BinderInputValues> for InputValues<'a> {
33 type Error = anyhow::Error;
34
35 fn try_from(input_values: &'a BinderInputValues) -> Result<InputValues<'a>> {
36 if input_values.codeHash.len() != dice::HASH_SIZE {
37 return Err(anyhow!(format!(
38 "In try_from: Code hash has invalid size: {}",
39 input_values.codeHash.len()
40 )));
41 }
42 if input_values.authorityHash.len() != dice::HASH_SIZE {
43 return Err(anyhow!(format!(
44 "In try_from: Authority hash has invalid size: {}",
45 input_values.authorityHash.len()
46 )));
47 }
48 if input_values.hidden.len() != dice::HIDDEN_SIZE {
49 return Err(anyhow!(format!(
50 "In try_from: Hidden has invalid size: {}",
51 input_values.hidden.len()
52 )));
53 }
54
55 Ok(Self(input_values))
56 }
57}
58
59impl From<&InputValues<'_>> for BinderInputValues {
60 fn from(input_values: &InputValues) -> BinderInputValues {
61 input_values.0.clone()
62 }
63}
64impl From<InputValues<'_>> for BinderInputValues {
65 fn from(input_values: InputValues) -> BinderInputValues {
66 input_values.0.clone()
67 }
68}
69
70impl dice::InputValues for InputValues<'_> {
71 fn code_hash(&self) -> &[u8; dice::HASH_SIZE] {
72 // If `self` was created using try_from the length was checked and this cannot panic.
73 self.0.codeHash.as_slice().try_into().unwrap()
74 }
75
76 fn config(&self) -> dice::Config {
77 dice::Config::Descriptor(self.0.config.desc.as_slice())
78 }
79
80 fn authority_hash(&self) -> &[u8; dice::HASH_SIZE] {
81 // If `self` was created using try_from the length was checked and this cannot panic.
82 self.0.authorityHash.as_slice().try_into().unwrap()
83 }
84
85 fn authority_descriptor(&self) -> Option<&[u8]> {
86 self.0.authorityDescriptor.as_deref()
87 }
88
89 fn mode(&self) -> dice::Mode {
90 match self.0.mode {
91 BinderMode::NOT_INITIALIZED => dice::Mode::NotConfigured,
92 BinderMode::NORMAL => dice::Mode::Normal,
93 BinderMode::DEBUG => dice::Mode::Debug,
94 BinderMode::RECOVERY => dice::Mode::Recovery,
95 _ => dice::Mode::NotConfigured,
96 }
97 }
98
99 fn hidden(&self) -> &[u8; dice::HIDDEN_SIZE] {
100 // If `self` was created using try_from the length was checked and this cannot panic.
101 self.0.hidden.as_slice().try_into().unwrap()
102 }
103}
104
105/// Initializes an aidl defined BccHandover object with the arguments `cdi_attest`, `cdi_seal`,
106/// and `bcc`.
107pub fn make_bcc_handover(
108 cdi_attest: &[u8; dice::CDI_SIZE],
109 cdi_seal: &[u8; dice::CDI_SIZE],
110 bcc: &[u8],
111) -> Result<BccHandover> {
112 Ok(BccHandover {
113 cdiAttest: cdi_attest.to_vec(),
114 cdiSeal: cdi_seal.to_vec(),
115 bcc: Bcc { data: bcc.to_vec() },
116 })
117}
118
119/// ResidentArtifacts stores a set of dice artifacts comprising CDI_ATTEST, CDI_SEAL,
120/// and the BCC formatted attestation certificate chain. The sensitive secrets are
121/// stored in zeroing vectors, and it implements functionality to perform DICE
122/// derivation steps using libopen-dice-cbor.
123pub struct ResidentArtifacts {
124 cdi_attest: ZVec,
125 cdi_seal: ZVec,
126 bcc: Vec<u8>,
127}
128
129impl ResidentArtifacts {
130 /// Create a ResidentArtifacts object. The parameters ensure that the stored secrets
131 /// can only have the appropriate size, so that subsequent casts to array references
132 /// cannot fail.
133 pub fn new(
134 cdi_attest: &[u8; dice::CDI_SIZE],
135 cdi_seal: &[u8; dice::CDI_SIZE],
136 bcc: &[u8],
137 ) -> Result<Self> {
138 Ok(ResidentArtifacts {
139 cdi_attest: cdi_attest[..]
140 .try_into()
141 .context("In ResidentArtifacts::new: Trying to convert cdi_attest to ZVec.")?,
142 cdi_seal: cdi_seal[..]
143 .try_into()
144 .context("In ResidentArtifacts::new: Trying to convert cdi_seal to ZVec.")?,
145 bcc: bcc.to_vec(),
146 })
147 }
148
149 /// Attempts to clone the artifacts. This operation is fallible due to the fallible
150 /// nature of ZVec.
151 pub fn try_clone(&self) -> Result<Self> {
152 Ok(ResidentArtifacts {
153 cdi_attest: self
154 .cdi_attest
155 .try_clone()
156 .context("In ResidentArtifacts::new: Trying to clone cdi_attest.")?,
157 cdi_seal: self
158 .cdi_seal
159 .try_clone()
160 .context("In ResidentArtifacts::new: Trying to clone cdi_seal.")?,
161 bcc: self.bcc.clone(),
162 })
163 }
164
165 /// Deconstruct the Artifacts into a tuple.
166 /// (CDI_ATTEST, CDI_SEAL, BCC)
167 pub fn into_tuple(self) -> (ZVec, ZVec, Vec<u8>) {
168 let ResidentArtifacts { cdi_attest, cdi_seal, bcc } = self;
169 (cdi_attest, cdi_seal, bcc)
170 }
171
172 fn execute_step(self, input_values: &dyn dice::InputValues) -> Result<Self> {
173 let ResidentArtifacts { cdi_attest, cdi_seal, bcc } = self;
174
175 let (cdi_attest, cdi_seal, bcc) = dice::OpenDiceCborContext::new()
176 .bcc_main_flow(
177 cdi_attest[..].try_into().with_context(|| {
178 format!("Trying to convert cdi_attest. (length: {})", cdi_attest.len())
179 })?,
180 cdi_seal[..].try_into().with_context(|| {
181 format!("Trying to convert cdi_seal. (length: {})", cdi_seal.len())
182 })?,
183 &bcc,
184 input_values,
185 )
186 .context("In ResidentArtifacts::execute_step:")?;
187 Ok(ResidentArtifacts { cdi_attest, cdi_seal, bcc })
188 }
189
190 /// Iterate through the iterator of dice input values performing one
191 /// BCC main flow step on each element.
192 pub fn execute_steps<'a, Iter>(self, input_values: Iter) -> Result<Self>
193 where
194 Iter: IntoIterator<Item = &'a dyn dice::InputValues>,
195 {
196 input_values
197 .into_iter()
198 .try_fold(self, |acc, input_values| acc.execute_step(input_values))
199 .context("In ResidentArtifacts::execute_step:")
200 }
201}
202
203/// This submodule implements a limited set of CBOR generation functionality. Essentially,
204/// a cbor header generator and some convenience functions for number and BSTR encoding.
205pub mod cbor {
206 use anyhow::{anyhow, Context, Result};
207 use std::convert::TryInto;
208 use std::io::Write;
209
210 /// CBOR encodes a positive number.
211 pub fn encode_number(n: u64, buffer: &mut dyn Write) -> Result<()> {
212 encode_header(0, n, buffer)
213 }
214
215 /// CBOR encodes a binary string.
216 pub fn encode_bstr(bstr: &[u8], buffer: &mut dyn Write) -> Result<()> {
217 encode_header(
218 2,
219 bstr.len().try_into().context("In encode_bstr: Failed to convert usize to u64.")?,
220 buffer,
221 )
222 .context("In encode_bstr: While writing header.")?;
223 let written = buffer.write(bstr).context("In encode_bstr: While writing payload.")?;
224 if written != bstr.len() {
225 return Err(anyhow!("In encode_bstr: Buffer too small. ({}, {})", written, bstr.len()));
226 }
227 Ok(())
228 }
229
230 /// Formats a CBOR header. `t` is the type, and n is the header argument.
231 pub fn encode_header(t: u8, n: u64, buffer: &mut dyn Write) -> Result<()> {
232 match n {
233 n if n < 24 => {
234 let written = buffer
235 .write(&u8::to_be_bytes(((t as u8) << 5) | (n as u8 & 0x1F)))
236 .with_context(|| {
237 format!("In encode_header: Failed to write header ({}, {})", t, n)
238 })?;
239 if written != 1 {
240 return Err(anyhow!("In encode_header: Buffer to small. ({}, {})", t, n));
241 }
242 }
243 n if n <= 0xFF => {
244 let written =
245 buffer.write(&u8::to_be_bytes(((t as u8) << 5) | (24u8 & 0x1F))).with_context(
246 || format!("In encode_header: Failed to write header ({}, {})", t, n),
247 )?;
248 if written != 1 {
249 return Err(anyhow!("In encode_header: Buffer to small. ({}, {})", t, n));
250 }
251 let written = buffer.write(&u8::to_be_bytes(n as u8)).with_context(|| {
252 format!("In encode_header: Failed to write size ({}, {})", t, n)
253 })?;
254 if written != 1 {
255 return Err(anyhow!(
256 "In encode_header while writing size: Buffer to small. ({}, {})",
257 t,
258 n
259 ));
260 }
261 }
262 n if n <= 0xFFFF => {
263 let written =
264 buffer.write(&u8::to_be_bytes(((t as u8) << 5) | (25u8 & 0x1F))).with_context(
265 || format!("In encode_header: Failed to write header ({}, {})", t, n),
266 )?;
267 if written != 1 {
268 return Err(anyhow!("In encode_header: Buffer to small. ({}, {})", t, n));
269 }
270 let written = buffer.write(&u16::to_be_bytes(n as u16)).with_context(|| {
271 format!("In encode_header: Failed to write size ({}, {})", t, n)
272 })?;
273 if written != 2 {
274 return Err(anyhow!(
275 "In encode_header while writing size: Buffer to small. ({}, {})",
276 t,
277 n
278 ));
279 }
280 }
281 n if n <= 0xFFFFFFFF => {
282 let written =
283 buffer.write(&u8::to_be_bytes(((t as u8) << 5) | (26u8 & 0x1F))).with_context(
284 || format!("In encode_header: Failed to write header ({}, {})", t, n),
285 )?;
286 if written != 1 {
287 return Err(anyhow!("In encode_header: Buffer to small. ({}, {})", t, n));
288 }
289 let written = buffer.write(&u32::to_be_bytes(n as u32)).with_context(|| {
290 format!("In encode_header: Failed to write size ({}, {})", t, n)
291 })?;
292 if written != 4 {
293 return Err(anyhow!(
294 "In encode_header while writing size: Buffer to small. ({}, {})",
295 t,
296 n
297 ));
298 }
299 }
300 n => {
301 let written =
302 buffer.write(&u8::to_be_bytes(((t as u8) << 5) | (27u8 & 0x1F))).with_context(
303 || format!("In encode_header: Failed to write header ({}, {})", t, n),
304 )?;
305 if written != 1 {
306 return Err(anyhow!("In encode_header: Buffer to small. ({}, {})", t, n));
307 }
308 let written = buffer.write(&u64::to_be_bytes(n as u64)).with_context(|| {
309 format!("In encode_header: Failed to write size ({}, {})", t, n)
310 })?;
311 if written != 8 {
312 return Err(anyhow!(
313 "In encode_header while writing size: Buffer to small. ({}, {})",
314 t,
315 n
316 ));
317 }
318 }
319 }
320 Ok(())
321 }
322
323 #[cfg(test)]
324 mod test {
325 use super::*;
326
327 fn encode_header_helper(t: u8, n: u64) -> Vec<u8> {
328 let mut b: Vec<u8> = vec![];
329 encode_header(t, n, &mut b).unwrap();
330 b
331 }
332
333 #[test]
334 fn encode_header_test() {
335 assert_eq!(&encode_header_helper(0, 0), &[0b000_00000]);
336 assert_eq!(&encode_header_helper(0, 23), &[0b000_10111]);
337 assert_eq!(&encode_header_helper(0, 24), &[0b000_11000, 24]);
338 assert_eq!(&encode_header_helper(0, 0xff), &[0b000_11000, 0xff]);
339 assert_eq!(&encode_header_helper(0, 0x100), &[0b000_11001, 0x01, 0x00]);
340 assert_eq!(&encode_header_helper(0, 0xffff), &[0b000_11001, 0xff, 0xff]);
341 assert_eq!(&encode_header_helper(0, 0x10000), &[0b000_11010, 0x00, 0x01, 0x00, 0x00]);
342 assert_eq!(
343 &encode_header_helper(0, 0xffffffff),
344 &[0b000_11010, 0xff, 0xff, 0xff, 0xff]
345 );
346 assert_eq!(
347 &encode_header_helper(0, 0x100000000),
348 &[0b000_11011, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00]
349 );
350 assert_eq!(
351 &encode_header_helper(0, 0xffffffffffffffff),
352 &[0b000_11011, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]
353 );
354 }
355 }
356}
357
358#[cfg(test)]
359mod test {
360 use super::*;
361 use android_hardware_security_dice::aidl::android::hardware::security::dice::{
362 Config::Config as BinderConfig, InputValues::InputValues as BinderInputValues,
363 };
364 use dice::InputValues as DiceInputValues;
365 use diced_open_dice_cbor as dice;
366
367 static CODE_HASH_TEST_VECTOR: [u8; dice::HASH_SIZE] = [1u8; dice::HASH_SIZE];
368 static CONFIG_DESCRIPTOR_TEST_VECTOR: &[u8] = &[3, 2, 1];
369 static AUTHORITY_HASH_TEST_VECTOR: [u8; dice::HASH_SIZE] = [3u8; dice::HASH_SIZE];
370 static AUTHORITY_DESCRIPTOR_TEST_VECTOR: &[u8] = &[1, 2, 3];
371 static HIDDEN_TEST_VECTOR: [u8; dice::HIDDEN_SIZE] = [4u8; dice::HIDDEN_SIZE];
372
373 #[test]
374 fn try_from_input_values_binder() {
375 let input_values_good = BinderInputValues {
376 codeHash: CODE_HASH_TEST_VECTOR.to_vec(),
377 config: BinderConfig { desc: CONFIG_DESCRIPTOR_TEST_VECTOR.to_vec() },
378 authorityHash: AUTHORITY_HASH_TEST_VECTOR.to_vec(),
379 authorityDescriptor: Some(AUTHORITY_DESCRIPTOR_TEST_VECTOR.to_vec()),
380 mode: BinderMode::NORMAL,
381 hidden: HIDDEN_TEST_VECTOR.to_vec(),
382 };
383
384 let converted_input_values: InputValues =
385 (&input_values_good).try_into().expect("This should succeed.");
386 assert_eq!(*converted_input_values.code_hash(), CODE_HASH_TEST_VECTOR);
387 assert_eq!(
388 converted_input_values.config(),
389 dice::Config::Descriptor(CONFIG_DESCRIPTOR_TEST_VECTOR)
390 );
391 assert_eq!(*converted_input_values.authority_hash(), AUTHORITY_HASH_TEST_VECTOR);
392 assert_eq!(
393 converted_input_values.authority_descriptor(),
394 Some(AUTHORITY_DESCRIPTOR_TEST_VECTOR)
395 );
396 assert_eq!(converted_input_values.mode(), dice::Mode::Normal);
397 assert_eq!(*converted_input_values.hidden(), HIDDEN_TEST_VECTOR);
398
399 // One more time without authority descriptor.
400 let input_values_still_good_without_authority_descriptor =
401 BinderInputValues { authorityDescriptor: None, ..input_values_good.clone() };
402
403 let converted_input_values: InputValues =
404 (&input_values_still_good_without_authority_descriptor)
405 .try_into()
406 .expect("This should succeed.");
407 assert_eq!(*converted_input_values.code_hash(), CODE_HASH_TEST_VECTOR);
408 assert_eq!(
409 converted_input_values.config(),
410 dice::Config::Descriptor(CONFIG_DESCRIPTOR_TEST_VECTOR)
411 );
412 assert_eq!(*converted_input_values.authority_hash(), AUTHORITY_HASH_TEST_VECTOR);
413 assert_eq!(converted_input_values.authority_descriptor(), None);
414 assert_eq!(converted_input_values.mode(), dice::Mode::Normal);
415 assert_eq!(*converted_input_values.hidden(), HIDDEN_TEST_VECTOR);
416
417 // Now check the failure cases.
418 // Wrong sized codeHash.
419 let input_values_bad_code_hash = BinderInputValues {
420 codeHash: vec![1u8; dice::HASH_SIZE + 1],
421 ..input_values_good.clone()
422 };
423
424 InputValues::try_from(&input_values_bad_code_hash)
425 .expect_err("Conversion of input values with wrong sized code hash succeeded.");
426
427 // Wrong sized authority hash.
428 let input_values_bad_authority_hash = BinderInputValues {
429 authorityHash: vec![1u8; dice::HASH_SIZE + 1],
430 ..input_values_good.clone()
431 };
432
433 InputValues::try_from(&input_values_bad_authority_hash)
434 .expect_err("Conversion of input values with wrong sized authority hash succeeded.");
435
436 // Wrong sized hidden.
437 let input_values_bad_hidden = BinderInputValues {
438 authorityHash: vec![1u8; dice::HASH_SIZE + 1],
439 ..input_values_good.clone()
440 };
441
442 InputValues::try_from(&input_values_bad_hidden)
443 .expect_err("Conversion of input values with wrong sized hidden succeeded.");
444 }
445}