• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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