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