blob: 3d3db55933e4a089bb38d777df828e46b9d24b08 [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
Janis Danisevskisaaba4af2021-11-18 14:25:07 -0800149 /// Creates a ResidentArtifacts object from another one implementing the DiceArtifacts
150 /// trait. Like `new` this function can only create artifacts of appropriate size
151 /// because DiceArtifacts returns array references of appropriate size.
152 pub fn new_from<T: DiceArtifacts + ?Sized>(artifacts: &T) -> Result<Self> {
153 Ok(ResidentArtifacts {
154 cdi_attest: artifacts.cdi_attest()[..].try_into()?,
155 cdi_seal: artifacts.cdi_seal()[..].try_into()?,
156 bcc: artifacts.bcc(),
157 })
158 }
159
Janis Danisevskisc51dff82021-10-20 09:51:16 -0700160 /// Attempts to clone the artifacts. This operation is fallible due to the fallible
161 /// nature of ZVec.
162 pub fn try_clone(&self) -> Result<Self> {
163 Ok(ResidentArtifacts {
164 cdi_attest: self
165 .cdi_attest
166 .try_clone()
167 .context("In ResidentArtifacts::new: Trying to clone cdi_attest.")?,
168 cdi_seal: self
169 .cdi_seal
170 .try_clone()
171 .context("In ResidentArtifacts::new: Trying to clone cdi_seal.")?,
172 bcc: self.bcc.clone(),
173 })
174 }
175
176 /// Deconstruct the Artifacts into a tuple.
177 /// (CDI_ATTEST, CDI_SEAL, BCC)
178 pub fn into_tuple(self) -> (ZVec, ZVec, Vec<u8>) {
179 let ResidentArtifacts { cdi_attest, cdi_seal, bcc } = self;
180 (cdi_attest, cdi_seal, bcc)
181 }
182
183 fn execute_step(self, input_values: &dyn dice::InputValues) -> Result<Self> {
184 let ResidentArtifacts { cdi_attest, cdi_seal, bcc } = self;
185
186 let (cdi_attest, cdi_seal, bcc) = dice::OpenDiceCborContext::new()
187 .bcc_main_flow(
188 cdi_attest[..].try_into().with_context(|| {
189 format!("Trying to convert cdi_attest. (length: {})", cdi_attest.len())
190 })?,
191 cdi_seal[..].try_into().with_context(|| {
192 format!("Trying to convert cdi_seal. (length: {})", cdi_seal.len())
193 })?,
194 &bcc,
195 input_values,
196 )
197 .context("In ResidentArtifacts::execute_step:")?;
198 Ok(ResidentArtifacts { cdi_attest, cdi_seal, bcc })
199 }
200
201 /// Iterate through the iterator of dice input values performing one
202 /// BCC main flow step on each element.
203 pub fn execute_steps<'a, Iter>(self, input_values: Iter) -> Result<Self>
204 where
205 Iter: IntoIterator<Item = &'a dyn dice::InputValues>,
206 {
207 input_values
208 .into_iter()
209 .try_fold(self, |acc, input_values| acc.execute_step(input_values))
210 .context("In ResidentArtifacts::execute_step:")
211 }
212}
213
Janis Danisevskisaaba4af2021-11-18 14:25:07 -0800214/// An object that implements this trait provides the typical DICE artifacts.
215/// CDI_ATTEST, CDI_SEAL, and a certificate chain up to the public key that
216/// can be derived from CDI_ATTEST. Implementations should check the length of
217/// the stored CDI_* secrets on creation so that any valid instance returns the
218/// correct secrets in an infallible way.
219pub trait DiceArtifacts {
220 /// Returns CDI_ATTEST.
221 fn cdi_attest(&self) -> &[u8; dice::CDI_SIZE];
222 /// Returns CDI_SEAL.
223 fn cdi_seal(&self) -> &[u8; dice::CDI_SIZE];
224 /// Returns the attestation certificate chain in BCC format.
225 fn bcc(&self) -> Vec<u8>;
226}
227
228/// Implement this trait to provide read and write access to a secure artifact
229/// storage that can be used by the ResidentHal implementation.
230pub trait UpdatableDiceArtifacts {
231 /// With artifacts provides access to the stored artifacts for the duration
232 /// of the function call by means of calling the callback.
233 fn with_artifacts<F, T>(&self, f: F) -> Result<T>
234 where
235 F: FnOnce(&dyn DiceArtifacts) -> Result<T>;
236
237 /// Consumes the object and returns a an updated version of itself.
238 fn update(self, new_artifacts: &impl DiceArtifacts) -> Result<Self>
239 where
240 Self: Sized;
241}
242
243impl DiceArtifacts for ResidentArtifacts {
244 fn cdi_attest(&self) -> &[u8; dice::CDI_SIZE] {
245 self.cdi_attest[..].try_into().unwrap()
246 }
247 fn cdi_seal(&self) -> &[u8; dice::CDI_SIZE] {
248 self.cdi_seal[..].try_into().unwrap()
249 }
250 fn bcc(&self) -> Vec<u8> {
251 self.bcc.clone()
252 }
253}
254
Janis Danisevskisc51dff82021-10-20 09:51:16 -0700255/// This submodule implements a limited set of CBOR generation functionality. Essentially,
256/// a cbor header generator and some convenience functions for number and BSTR encoding.
257pub mod cbor {
258 use anyhow::{anyhow, Context, Result};
259 use std::convert::TryInto;
260 use std::io::Write;
261
262 /// CBOR encodes a positive number.
263 pub fn encode_number(n: u64, buffer: &mut dyn Write) -> Result<()> {
264 encode_header(0, n, buffer)
265 }
266
267 /// CBOR encodes a binary string.
268 pub fn encode_bstr(bstr: &[u8], buffer: &mut dyn Write) -> Result<()> {
269 encode_header(
270 2,
271 bstr.len().try_into().context("In encode_bstr: Failed to convert usize to u64.")?,
272 buffer,
273 )
274 .context("In encode_bstr: While writing header.")?;
275 let written = buffer.write(bstr).context("In encode_bstr: While writing payload.")?;
276 if written != bstr.len() {
277 return Err(anyhow!("In encode_bstr: Buffer too small. ({}, {})", written, bstr.len()));
278 }
279 Ok(())
280 }
281
282 /// Formats a CBOR header. `t` is the type, and n is the header argument.
283 pub fn encode_header(t: u8, n: u64, buffer: &mut dyn Write) -> Result<()> {
284 match n {
285 n if n < 24 => {
286 let written = buffer
287 .write(&u8::to_be_bytes(((t as u8) << 5) | (n as u8 & 0x1F)))
288 .with_context(|| {
289 format!("In encode_header: Failed to write header ({}, {})", t, n)
290 })?;
291 if written != 1 {
292 return Err(anyhow!("In encode_header: Buffer to small. ({}, {})", t, n));
293 }
294 }
295 n if n <= 0xFF => {
296 let written =
297 buffer.write(&u8::to_be_bytes(((t as u8) << 5) | (24u8 & 0x1F))).with_context(
298 || format!("In encode_header: Failed to write header ({}, {})", t, n),
299 )?;
300 if written != 1 {
301 return Err(anyhow!("In encode_header: Buffer to small. ({}, {})", t, n));
302 }
303 let written = buffer.write(&u8::to_be_bytes(n as u8)).with_context(|| {
304 format!("In encode_header: Failed to write size ({}, {})", t, n)
305 })?;
306 if written != 1 {
307 return Err(anyhow!(
308 "In encode_header while writing size: Buffer to small. ({}, {})",
309 t,
310 n
311 ));
312 }
313 }
314 n if n <= 0xFFFF => {
315 let written =
316 buffer.write(&u8::to_be_bytes(((t as u8) << 5) | (25u8 & 0x1F))).with_context(
317 || format!("In encode_header: Failed to write header ({}, {})", t, n),
318 )?;
319 if written != 1 {
320 return Err(anyhow!("In encode_header: Buffer to small. ({}, {})", t, n));
321 }
322 let written = buffer.write(&u16::to_be_bytes(n as u16)).with_context(|| {
323 format!("In encode_header: Failed to write size ({}, {})", t, n)
324 })?;
325 if written != 2 {
326 return Err(anyhow!(
327 "In encode_header while writing size: Buffer to small. ({}, {})",
328 t,
329 n
330 ));
331 }
332 }
333 n if n <= 0xFFFFFFFF => {
334 let written =
335 buffer.write(&u8::to_be_bytes(((t as u8) << 5) | (26u8 & 0x1F))).with_context(
336 || format!("In encode_header: Failed to write header ({}, {})", t, n),
337 )?;
338 if written != 1 {
339 return Err(anyhow!("In encode_header: Buffer to small. ({}, {})", t, n));
340 }
341 let written = buffer.write(&u32::to_be_bytes(n as u32)).with_context(|| {
342 format!("In encode_header: Failed to write size ({}, {})", t, n)
343 })?;
344 if written != 4 {
345 return Err(anyhow!(
346 "In encode_header while writing size: Buffer to small. ({}, {})",
347 t,
348 n
349 ));
350 }
351 }
352 n => {
353 let written =
354 buffer.write(&u8::to_be_bytes(((t as u8) << 5) | (27u8 & 0x1F))).with_context(
355 || format!("In encode_header: Failed to write header ({}, {})", t, n),
356 )?;
357 if written != 1 {
358 return Err(anyhow!("In encode_header: Buffer to small. ({}, {})", t, n));
359 }
360 let written = buffer.write(&u64::to_be_bytes(n as u64)).with_context(|| {
361 format!("In encode_header: Failed to write size ({}, {})", t, n)
362 })?;
363 if written != 8 {
364 return Err(anyhow!(
365 "In encode_header while writing size: Buffer to small. ({}, {})",
366 t,
367 n
368 ));
369 }
370 }
371 }
372 Ok(())
373 }
374
375 #[cfg(test)]
376 mod test {
377 use super::*;
378
379 fn encode_header_helper(t: u8, n: u64) -> Vec<u8> {
380 let mut b: Vec<u8> = vec![];
381 encode_header(t, n, &mut b).unwrap();
382 b
383 }
384
385 #[test]
386 fn encode_header_test() {
387 assert_eq!(&encode_header_helper(0, 0), &[0b000_00000]);
388 assert_eq!(&encode_header_helper(0, 23), &[0b000_10111]);
389 assert_eq!(&encode_header_helper(0, 24), &[0b000_11000, 24]);
390 assert_eq!(&encode_header_helper(0, 0xff), &[0b000_11000, 0xff]);
391 assert_eq!(&encode_header_helper(0, 0x100), &[0b000_11001, 0x01, 0x00]);
392 assert_eq!(&encode_header_helper(0, 0xffff), &[0b000_11001, 0xff, 0xff]);
393 assert_eq!(&encode_header_helper(0, 0x10000), &[0b000_11010, 0x00, 0x01, 0x00, 0x00]);
394 assert_eq!(
395 &encode_header_helper(0, 0xffffffff),
396 &[0b000_11010, 0xff, 0xff, 0xff, 0xff]
397 );
398 assert_eq!(
399 &encode_header_helper(0, 0x100000000),
400 &[0b000_11011, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00]
401 );
402 assert_eq!(
403 &encode_header_helper(0, 0xffffffffffffffff),
404 &[0b000_11011, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]
405 );
406 }
407 }
408}
409
410#[cfg(test)]
411mod test {
412 use super::*;
413 use android_hardware_security_dice::aidl::android::hardware::security::dice::{
414 Config::Config as BinderConfig, InputValues::InputValues as BinderInputValues,
415 };
416 use dice::InputValues as DiceInputValues;
417 use diced_open_dice_cbor as dice;
418
419 static CODE_HASH_TEST_VECTOR: [u8; dice::HASH_SIZE] = [1u8; dice::HASH_SIZE];
420 static CONFIG_DESCRIPTOR_TEST_VECTOR: &[u8] = &[3, 2, 1];
421 static AUTHORITY_HASH_TEST_VECTOR: [u8; dice::HASH_SIZE] = [3u8; dice::HASH_SIZE];
422 static AUTHORITY_DESCRIPTOR_TEST_VECTOR: &[u8] = &[1, 2, 3];
423 static HIDDEN_TEST_VECTOR: [u8; dice::HIDDEN_SIZE] = [4u8; dice::HIDDEN_SIZE];
424
425 #[test]
426 fn try_from_input_values_binder() {
427 let input_values_good = BinderInputValues {
428 codeHash: CODE_HASH_TEST_VECTOR.to_vec(),
429 config: BinderConfig { desc: CONFIG_DESCRIPTOR_TEST_VECTOR.to_vec() },
430 authorityHash: AUTHORITY_HASH_TEST_VECTOR.to_vec(),
431 authorityDescriptor: Some(AUTHORITY_DESCRIPTOR_TEST_VECTOR.to_vec()),
432 mode: BinderMode::NORMAL,
433 hidden: HIDDEN_TEST_VECTOR.to_vec(),
434 };
435
436 let converted_input_values: InputValues =
437 (&input_values_good).try_into().expect("This should succeed.");
438 assert_eq!(*converted_input_values.code_hash(), CODE_HASH_TEST_VECTOR);
439 assert_eq!(
440 converted_input_values.config(),
441 dice::Config::Descriptor(CONFIG_DESCRIPTOR_TEST_VECTOR)
442 );
443 assert_eq!(*converted_input_values.authority_hash(), AUTHORITY_HASH_TEST_VECTOR);
444 assert_eq!(
445 converted_input_values.authority_descriptor(),
446 Some(AUTHORITY_DESCRIPTOR_TEST_VECTOR)
447 );
448 assert_eq!(converted_input_values.mode(), dice::Mode::Normal);
449 assert_eq!(*converted_input_values.hidden(), HIDDEN_TEST_VECTOR);
450
451 // One more time without authority descriptor.
452 let input_values_still_good_without_authority_descriptor =
453 BinderInputValues { authorityDescriptor: None, ..input_values_good.clone() };
454
455 let converted_input_values: InputValues =
456 (&input_values_still_good_without_authority_descriptor)
457 .try_into()
458 .expect("This should succeed.");
459 assert_eq!(*converted_input_values.code_hash(), CODE_HASH_TEST_VECTOR);
460 assert_eq!(
461 converted_input_values.config(),
462 dice::Config::Descriptor(CONFIG_DESCRIPTOR_TEST_VECTOR)
463 );
464 assert_eq!(*converted_input_values.authority_hash(), AUTHORITY_HASH_TEST_VECTOR);
465 assert_eq!(converted_input_values.authority_descriptor(), None);
466 assert_eq!(converted_input_values.mode(), dice::Mode::Normal);
467 assert_eq!(*converted_input_values.hidden(), HIDDEN_TEST_VECTOR);
468
469 // Now check the failure cases.
470 // Wrong sized codeHash.
471 let input_values_bad_code_hash = BinderInputValues {
472 codeHash: vec![1u8; dice::HASH_SIZE + 1],
473 ..input_values_good.clone()
474 };
475
476 InputValues::try_from(&input_values_bad_code_hash)
477 .expect_err("Conversion of input values with wrong sized code hash succeeded.");
478
479 // Wrong sized authority hash.
480 let input_values_bad_authority_hash = BinderInputValues {
481 authorityHash: vec![1u8; dice::HASH_SIZE + 1],
482 ..input_values_good.clone()
483 };
484
485 InputValues::try_from(&input_values_bad_authority_hash)
486 .expect_err("Conversion of input values with wrong sized authority hash succeeded.");
487
488 // Wrong sized hidden.
489 let input_values_bad_hidden = BinderInputValues {
490 authorityHash: vec![1u8; dice::HASH_SIZE + 1],
491 ..input_values_good.clone()
492 };
493
494 InputValues::try_from(&input_values_bad_hidden)
495 .expect_err("Conversion of input values with wrong sized hidden succeeded.");
496 }
497}