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