1 // Copyright 2022, 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 //! Support for DICE derivation and BCC generation.
16 extern crate alloc;
17
18 use alloc::format;
19 use alloc::vec::Vec;
20 use ciborium::cbor;
21 use ciborium::Value;
22 use core::mem::size_of;
23 use diced_open_dice::{
24 bcc_handover_main_flow, hash, Config, DiceContext, DiceMode, Hash, InputValues, HIDDEN_SIZE,
25 };
26 use pvmfw_avb::{Capability, DebugLevel, Digest, VerifiedBootData};
27 use zerocopy::AsBytes;
28
29 // pVM firmware (like other VM components) is expected to populate some fields in DICE
30 // Configuration Descriptor. See dice_for_avf_guest.cddl
31 const COMPONENT_NAME_KEY: i64 = -70002;
32 const SECURITY_VERSION_KEY: i64 = -70005;
33 const RKP_VM_MARKER_KEY: i64 = -70006;
34 const INSTANCE_HASH_KEY: i64 = -71003;
35
36 #[derive(Debug)]
37 pub enum Error {
38 /// Error in CBOR operations
39 #[allow(dead_code)]
40 CborError(ciborium::value::Error),
41 /// Error in DICE operations
42 #[allow(dead_code)]
43 DiceError(diced_open_dice::DiceError),
44 }
45
46 impl From<ciborium::value::Error> for Error {
from(e: ciborium::value::Error) -> Self47 fn from(e: ciborium::value::Error) -> Self {
48 Self::CborError(e)
49 }
50 }
51
52 impl From<diced_open_dice::DiceError> for Error {
from(e: diced_open_dice::DiceError) -> Self53 fn from(e: diced_open_dice::DiceError) -> Self {
54 Self::DiceError(e)
55 }
56 }
57
58 // DICE in pvmfw result type.
59 type Result<T> = core::result::Result<T, Error>;
60
to_dice_mode(debug_level: DebugLevel) -> DiceMode61 fn to_dice_mode(debug_level: DebugLevel) -> DiceMode {
62 match debug_level {
63 DebugLevel::None => DiceMode::kDiceModeNormal,
64 DebugLevel::Full => DiceMode::kDiceModeDebug,
65 }
66 }
67
to_dice_hash(verified_boot_data: &VerifiedBootData) -> Result<Hash>68 fn to_dice_hash(verified_boot_data: &VerifiedBootData) -> Result<Hash> {
69 let mut digests = [0u8; size_of::<Digest>() * 2];
70 digests[..size_of::<Digest>()].copy_from_slice(&verified_boot_data.kernel_digest);
71 if let Some(initrd_digest) = verified_boot_data.initrd_digest {
72 digests[size_of::<Digest>()..].copy_from_slice(&initrd_digest);
73 }
74 Ok(hash(&digests)?)
75 }
76
77 #[derive(Clone)]
78 pub struct PartialInputs {
79 pub code_hash: Hash,
80 pub auth_hash: Hash,
81 pub mode: DiceMode,
82 pub security_version: u64,
83 pub rkp_vm_marker: bool,
84 }
85
86 impl PartialInputs {
new(data: &VerifiedBootData) -> Result<Self>87 pub fn new(data: &VerifiedBootData) -> Result<Self> {
88 let code_hash = to_dice_hash(data)?;
89 let auth_hash = hash(data.public_key)?;
90 let mode = to_dice_mode(data.debug_level);
91 // We use rollback_index from vbmeta as the security_version field in dice certificate.
92 let security_version = data.rollback_index;
93 let rkp_vm_marker = data.has_capability(Capability::RemoteAttest);
94
95 Ok(Self { code_hash, auth_hash, mode, security_version, rkp_vm_marker })
96 }
97
write_next_bcc( self, current_bcc_handover: &[u8], salt: &[u8; HIDDEN_SIZE], instance_hash: Option<Hash>, deferred_rollback_protection: bool, next_bcc: &mut [u8], context: DiceContext, ) -> Result<()>98 pub fn write_next_bcc(
99 self,
100 current_bcc_handover: &[u8],
101 salt: &[u8; HIDDEN_SIZE],
102 instance_hash: Option<Hash>,
103 deferred_rollback_protection: bool,
104 next_bcc: &mut [u8],
105 context: DiceContext,
106 ) -> Result<()> {
107 let config = self
108 .generate_config_descriptor(instance_hash)
109 .map_err(|_| diced_open_dice::DiceError::InvalidInput)?;
110
111 let dice_inputs = InputValues::new(
112 self.code_hash,
113 Config::Descriptor(&config),
114 self.auth_hash,
115 self.mode,
116 self.make_hidden(salt, deferred_rollback_protection)?,
117 );
118 let _ = bcc_handover_main_flow(current_bcc_handover, &dice_inputs, next_bcc, context)?;
119 Ok(())
120 }
121
make_hidden( &self, salt: &[u8; HIDDEN_SIZE], deferred_rollback_protection: bool, ) -> diced_open_dice::Result<[u8; HIDDEN_SIZE]>122 fn make_hidden(
123 &self,
124 salt: &[u8; HIDDEN_SIZE],
125 deferred_rollback_protection: bool,
126 ) -> diced_open_dice::Result<[u8; HIDDEN_SIZE]> {
127 // We want to make sure we get a different sealing CDI for:
128 // - VMs with different salt values
129 // - An RKP VM and any other VM (regardless of salt)
130 // - depending on whether rollback protection has been deferred to payload. This ensures the
131 // adversary cannot leak the secrets by using old images & setting
132 // `deferred_rollback_protection` to true.
133 // The hidden input for DICE affects the sealing CDI (but the values in the config
134 // descriptor do not).
135 // Since the hidden input has to be a fixed size, create it as a hash of the values we
136 // want included.
137 #[derive(AsBytes)]
138 #[repr(C, packed)]
139 struct HiddenInput {
140 rkp_vm_marker: bool,
141 salt: [u8; HIDDEN_SIZE],
142 deferred_rollback_protection: bool,
143 }
144 hash(
145 HiddenInput {
146 rkp_vm_marker: self.rkp_vm_marker,
147 salt: *salt,
148 deferred_rollback_protection,
149 }
150 .as_bytes(),
151 )
152 }
153
generate_config_descriptor(&self, instance_hash: Option<Hash>) -> Result<Vec<u8>>154 fn generate_config_descriptor(&self, instance_hash: Option<Hash>) -> Result<Vec<u8>> {
155 let mut config = Vec::with_capacity(4);
156 config.push((cbor!(COMPONENT_NAME_KEY)?, cbor!("vm_entry")?));
157 if cfg!(dice_changes) {
158 config.push((cbor!(SECURITY_VERSION_KEY)?, cbor!(self.security_version)?));
159 }
160 if self.rkp_vm_marker {
161 config.push((cbor!(RKP_VM_MARKER_KEY)?, Value::Null))
162 }
163 if let Some(instance_hash) = instance_hash {
164 config.push((cbor!(INSTANCE_HASH_KEY)?, Value::from(instance_hash.as_slice())));
165 }
166 let config = Value::Map(config);
167 Ok(cbor_util::serialize(&config).map_err(|e| {
168 ciborium::value::Error::Custom(format!("Error in serialization: {e:?}"))
169 })?)
170 }
171 }
172
173 #[cfg(test)]
174 mod tests {
175 use crate::{
176 Hash, PartialInputs, COMPONENT_NAME_KEY, INSTANCE_HASH_KEY, RKP_VM_MARKER_KEY,
177 SECURITY_VERSION_KEY,
178 };
179 use ciborium::Value;
180 use diced_open_dice::DiceArtifacts;
181 use diced_open_dice::DiceContext;
182 use diced_open_dice::DiceMode;
183 use diced_open_dice::KeyAlgorithm;
184 use diced_open_dice::HIDDEN_SIZE;
185 use diced_sample_inputs::make_sample_bcc_and_cdis;
186 use pvmfw_avb::Capability;
187 use pvmfw_avb::DebugLevel;
188 use pvmfw_avb::Digest;
189 use pvmfw_avb::VerifiedBootData;
190 use std::collections::HashMap;
191 use std::mem::size_of;
192 use std::vec;
193
194 const COMPONENT_VERSION_KEY: i64 = -70003;
195 const RESETTABLE_KEY: i64 = -70004;
196 const BASE_VB_DATA: VerifiedBootData = VerifiedBootData {
197 debug_level: DebugLevel::None,
198 kernel_digest: [1u8; size_of::<Digest>()],
199 initrd_digest: Some([2u8; size_of::<Digest>()]),
200 public_key: b"public key",
201 capabilities: vec![],
202 rollback_index: 42,
203 page_size: None,
204 };
205 const HASH: Hash = *b"sixtyfourbyteslongsentencearerarebutletsgiveitatrycantbethathard";
206
207 #[test]
base_data_conversion()208 fn base_data_conversion() {
209 let vb_data = BASE_VB_DATA;
210 let inputs = PartialInputs::new(&vb_data).unwrap();
211
212 assert_eq!(inputs.mode, DiceMode::kDiceModeNormal);
213 assert_eq!(inputs.security_version, 42);
214 assert!(!inputs.rkp_vm_marker);
215
216 // TODO(b/313608219): Consider checks for code_hash and possibly auth_hash.
217 }
218
219 #[test]
debuggable_conversion()220 fn debuggable_conversion() {
221 let vb_data = VerifiedBootData { debug_level: DebugLevel::Full, ..BASE_VB_DATA };
222 let inputs = PartialInputs::new(&vb_data).unwrap();
223
224 assert_eq!(inputs.mode, DiceMode::kDiceModeDebug);
225 }
226
227 #[test]
rkp_vm_conversion()228 fn rkp_vm_conversion() {
229 let vb_data =
230 VerifiedBootData { capabilities: vec![Capability::RemoteAttest], ..BASE_VB_DATA };
231 let inputs = PartialInputs::new(&vb_data).unwrap();
232
233 assert!(inputs.rkp_vm_marker);
234 }
235
236 #[test]
base_config_descriptor()237 fn base_config_descriptor() {
238 let vb_data = BASE_VB_DATA;
239 let inputs = PartialInputs::new(&vb_data).unwrap();
240 let config_map = decode_config_descriptor(&inputs, None);
241
242 assert_eq!(config_map.get(&COMPONENT_NAME_KEY).unwrap().as_text().unwrap(), "vm_entry");
243 assert_eq!(config_map.get(&COMPONENT_VERSION_KEY), None);
244 assert_eq!(config_map.get(&RESETTABLE_KEY), None);
245 if cfg!(dice_changes) {
246 assert_eq!(
247 config_map.get(&SECURITY_VERSION_KEY).unwrap().as_integer().unwrap(),
248 42.into()
249 );
250 } else {
251 assert_eq!(config_map.get(&SECURITY_VERSION_KEY), None);
252 }
253 assert_eq!(config_map.get(&RKP_VM_MARKER_KEY), None);
254 }
255
256 #[test]
config_descriptor_with_rkp_vm()257 fn config_descriptor_with_rkp_vm() {
258 let vb_data =
259 VerifiedBootData { capabilities: vec![Capability::RemoteAttest], ..BASE_VB_DATA };
260 let inputs = PartialInputs::new(&vb_data).unwrap();
261 let config_map = decode_config_descriptor(&inputs, Some(HASH));
262
263 assert!(config_map.get(&RKP_VM_MARKER_KEY).unwrap().is_null());
264 }
265
266 #[test]
config_descriptor_with_instance_hash()267 fn config_descriptor_with_instance_hash() {
268 let vb_data =
269 VerifiedBootData { capabilities: vec![Capability::RemoteAttest], ..BASE_VB_DATA };
270 let inputs = PartialInputs::new(&vb_data).unwrap();
271 let config_map = decode_config_descriptor(&inputs, Some(HASH));
272 assert_eq!(*config_map.get(&INSTANCE_HASH_KEY).unwrap(), Value::from(HASH.as_slice()));
273 }
274
275 #[test]
config_descriptor_without_instance_hash()276 fn config_descriptor_without_instance_hash() {
277 let vb_data =
278 VerifiedBootData { capabilities: vec![Capability::RemoteAttest], ..BASE_VB_DATA };
279 let inputs = PartialInputs::new(&vb_data).unwrap();
280 let config_map = decode_config_descriptor(&inputs, None);
281 assert!(!config_map.contains_key(&INSTANCE_HASH_KEY));
282 }
283
decode_config_descriptor( inputs: &PartialInputs, instance_hash: Option<Hash>, ) -> HashMap<i64, Value>284 fn decode_config_descriptor(
285 inputs: &PartialInputs,
286 instance_hash: Option<Hash>,
287 ) -> HashMap<i64, Value> {
288 let config_descriptor = inputs.generate_config_descriptor(instance_hash).unwrap();
289
290 let cbor_map =
291 cbor_util::deserialize::<Value>(&config_descriptor).unwrap().into_map().unwrap();
292
293 cbor_map
294 .into_iter()
295 .map(|(k, v)| ((k.into_integer().unwrap().try_into().unwrap()), v))
296 .collect()
297 }
298
299 #[test]
changing_deferred_rpb_changes_secrets()300 fn changing_deferred_rpb_changes_secrets() {
301 let vb_data = VerifiedBootData { debug_level: DebugLevel::Full, ..BASE_VB_DATA };
302 let inputs = PartialInputs::new(&vb_data).unwrap();
303 let mut buffer_without_defer = [0; 4096];
304 let mut buffer_with_defer = [0; 4096];
305 let mut buffer_without_defer_retry = [0; 4096];
306 let context = DiceContext {
307 authority_algorithm: KeyAlgorithm::Ed25519,
308 subject_algorithm: KeyAlgorithm::Ed25519,
309 };
310
311 let sample_dice_input: &[u8] = &[
312 0xa3, // CDI attest
313 0x01, 0x58, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
314 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
315 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // CDI seal
316 0x02, 0x58, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
317 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
318 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // DICE chain
319 0x03, 0x82, 0xa6, 0x01, 0x02, 0x03, 0x27, 0x04, 0x02, 0x20, 0x01, 0x21, 0x40, 0x22,
320 0x40, 0x84, 0x40, 0xa0, 0x40, 0x40,
321 // 8-bytes of trailing data that aren't part of the DICE chain.
322 0x84, 0x41, 0x55, 0xa0, 0x42, 0x11, 0x22, 0x40,
323 ];
324
325 inputs
326 .clone()
327 .write_next_bcc(
328 sample_dice_input,
329 &[0u8; HIDDEN_SIZE],
330 Some([0u8; 64]),
331 false,
332 &mut buffer_without_defer,
333 context.clone(),
334 )
335 .unwrap();
336 let bcc_handover1 = diced_open_dice::bcc_handover_parse(&buffer_without_defer).unwrap();
337
338 inputs
339 .clone()
340 .write_next_bcc(
341 sample_dice_input,
342 &[0u8; HIDDEN_SIZE],
343 Some([0u8; 64]),
344 true,
345 &mut buffer_with_defer,
346 context.clone(),
347 )
348 .unwrap();
349 let bcc_handover2 = diced_open_dice::bcc_handover_parse(&buffer_with_defer).unwrap();
350
351 inputs
352 .clone()
353 .write_next_bcc(
354 sample_dice_input,
355 &[0u8; HIDDEN_SIZE],
356 Some([0u8; 64]),
357 false,
358 &mut buffer_without_defer_retry,
359 context.clone(),
360 )
361 .unwrap();
362 let bcc_handover3 =
363 diced_open_dice::bcc_handover_parse(&buffer_without_defer_retry).unwrap();
364
365 assert_ne!(bcc_handover1.cdi_seal(), bcc_handover2.cdi_seal());
366 assert_eq!(bcc_handover1.cdi_seal(), bcc_handover3.cdi_seal());
367 }
368
369 #[test]
dice_derivation_with_different_algorithms_is_valid()370 fn dice_derivation_with_different_algorithms_is_valid() {
371 let dice_artifacts = make_sample_bcc_and_cdis().unwrap();
372 let bcc_handover0_bytes = to_bcc_handover(&dice_artifacts);
373 let vb_data = VerifiedBootData { debug_level: DebugLevel::Full, ..BASE_VB_DATA };
374 let inputs = PartialInputs::new(&vb_data).unwrap();
375 let mut buffer = [0; 4096];
376
377 inputs
378 .clone()
379 .write_next_bcc(
380 &bcc_handover0_bytes,
381 &[0u8; HIDDEN_SIZE],
382 Some([0u8; 64]),
383 true,
384 &mut buffer,
385 DiceContext {
386 authority_algorithm: KeyAlgorithm::Ed25519,
387 subject_algorithm: KeyAlgorithm::EcdsaP256,
388 },
389 )
390 .expect("Failed to derive Ed25519 -> EcdsaP256 BCC");
391 let bcc_handover1 = diced_open_dice::bcc_handover_parse(&buffer).unwrap();
392 let bcc_handover1_bytes = to_bcc_handover(&bcc_handover1);
393 buffer.fill(0);
394
395 inputs
396 .clone()
397 .write_next_bcc(
398 &bcc_handover1_bytes,
399 &[0u8; HIDDEN_SIZE],
400 Some([0u8; 64]),
401 true,
402 &mut buffer,
403 DiceContext {
404 authority_algorithm: KeyAlgorithm::EcdsaP256,
405 subject_algorithm: KeyAlgorithm::EcdsaP384,
406 },
407 )
408 .expect("Failed to derive EcdsaP256 -> EcdsaP384 BCC");
409 let bcc_handover2 = diced_open_dice::bcc_handover_parse(&buffer).unwrap();
410 let bcc_handover2_bytes = to_bcc_handover(&bcc_handover2);
411 buffer.fill(0);
412
413 inputs
414 .clone()
415 .write_next_bcc(
416 &bcc_handover2_bytes,
417 &[0u8; HIDDEN_SIZE],
418 Some([0u8; 64]),
419 true,
420 &mut buffer,
421 DiceContext {
422 authority_algorithm: KeyAlgorithm::EcdsaP384,
423 subject_algorithm: KeyAlgorithm::Ed25519,
424 },
425 )
426 .expect("Failed to derive EcdsaP384 -> Ed25519 BCC");
427 let _bcc_handover3 = diced_open_dice::bcc_handover_parse(&buffer).unwrap();
428
429 // TODO(b/378813154): Check the DICE chain with `hwtrust` once the profile version
430 // is updated.
431 // The check cannot be done now because parsing the chain causes the following error:
432 // Invalid payload at index 3. Caused by:
433 // 0: opendice.example.p256
434 // 1: unknown profile version
435 }
436
to_bcc_handover(dice_artifacts: &dyn DiceArtifacts) -> Vec<u8>437 fn to_bcc_handover(dice_artifacts: &dyn DiceArtifacts) -> Vec<u8> {
438 let dice_chain = cbor_util::deserialize::<Value>(dice_artifacts.bcc().unwrap()).unwrap();
439 let bcc_handover = Value::Map(vec![
440 (Value::Integer(1.into()), Value::Bytes(dice_artifacts.cdi_attest().to_vec())),
441 (Value::Integer(2.into()), Value::Bytes(dice_artifacts.cdi_seal().to_vec())),
442 (Value::Integer(3.into()), dice_chain),
443 ]);
444 cbor_util::serialize(&bcc_handover).unwrap()
445 }
446 }
447