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, DiceMode, Hash, InputValues, HIDDEN_SIZE,
25 };
26 use pvmfw_avb::{Capability, DebugLevel, Digest, VerifiedBootData};
27 use zerocopy::AsBytes;
28 
29 const COMPONENT_NAME_KEY: i64 = -70002;
30 const SECURITY_VERSION_KEY: i64 = -70005;
31 const RKP_VM_MARKER_KEY: i64 = -70006;
32 // TODO(b/291245237): Document this key along with others used in ConfigDescriptor in AVF based VM.
33 const INSTANCE_HASH_KEY: i64 = -71003;
34 
35 #[derive(Debug)]
36 pub enum Error {
37     /// Error in CBOR operations
38     CborError(ciborium::value::Error),
39     /// Error in DICE operations
40     DiceError(diced_open_dice::DiceError),
41 }
42 
43 impl From<ciborium::value::Error> for Error {
from(e: ciborium::value::Error) -> Self44     fn from(e: ciborium::value::Error) -> Self {
45         Self::CborError(e)
46     }
47 }
48 
49 impl From<diced_open_dice::DiceError> for Error {
from(e: diced_open_dice::DiceError) -> Self50     fn from(e: diced_open_dice::DiceError) -> Self {
51         Self::DiceError(e)
52     }
53 }
54 
55 // DICE in pvmfw result type.
56 type Result<T> = core::result::Result<T, Error>;
57 
to_dice_mode(debug_level: DebugLevel) -> DiceMode58 fn to_dice_mode(debug_level: DebugLevel) -> DiceMode {
59     match debug_level {
60         DebugLevel::None => DiceMode::kDiceModeNormal,
61         DebugLevel::Full => DiceMode::kDiceModeDebug,
62     }
63 }
64 
to_dice_hash(verified_boot_data: &VerifiedBootData) -> Result<Hash>65 fn to_dice_hash(verified_boot_data: &VerifiedBootData) -> Result<Hash> {
66     let mut digests = [0u8; size_of::<Digest>() * 2];
67     digests[..size_of::<Digest>()].copy_from_slice(&verified_boot_data.kernel_digest);
68     if let Some(initrd_digest) = verified_boot_data.initrd_digest {
69         digests[size_of::<Digest>()..].copy_from_slice(&initrd_digest);
70     }
71     Ok(hash(&digests)?)
72 }
73 
74 #[derive(Clone)]
75 pub struct PartialInputs {
76     pub code_hash: Hash,
77     pub auth_hash: Hash,
78     pub mode: DiceMode,
79     pub security_version: u64,
80     pub rkp_vm_marker: bool,
81 }
82 
83 impl PartialInputs {
new(data: &VerifiedBootData) -> Result<Self>84     pub fn new(data: &VerifiedBootData) -> Result<Self> {
85         let code_hash = to_dice_hash(data)?;
86         let auth_hash = hash(data.public_key)?;
87         let mode = to_dice_mode(data.debug_level);
88         // We use rollback_index from vbmeta as the security_version field in dice certificate.
89         let security_version = data.rollback_index;
90         let rkp_vm_marker = data.has_capability(Capability::RemoteAttest);
91 
92         Ok(Self { code_hash, auth_hash, mode, security_version, rkp_vm_marker })
93     }
94 
write_next_bcc( self, current_bcc_handover: &[u8], salt: &[u8; HIDDEN_SIZE], instance_hash: Option<Hash>, deferred_rollback_protection: bool, next_bcc: &mut [u8], ) -> Result<()>95     pub fn write_next_bcc(
96         self,
97         current_bcc_handover: &[u8],
98         salt: &[u8; HIDDEN_SIZE],
99         instance_hash: Option<Hash>,
100         deferred_rollback_protection: bool,
101         next_bcc: &mut [u8],
102     ) -> Result<()> {
103         let config = self
104             .generate_config_descriptor(instance_hash)
105             .map_err(|_| diced_open_dice::DiceError::InvalidInput)?;
106 
107         let dice_inputs = InputValues::new(
108             self.code_hash,
109             Config::Descriptor(&config),
110             self.auth_hash,
111             self.mode,
112             self.make_hidden(salt, deferred_rollback_protection)?,
113         );
114         let _ = bcc_handover_main_flow(current_bcc_handover, &dice_inputs, next_bcc)?;
115         Ok(())
116     }
117 
make_hidden( &self, salt: &[u8; HIDDEN_SIZE], deferred_rollback_protection: bool, ) -> diced_open_dice::Result<[u8; HIDDEN_SIZE]>118     fn make_hidden(
119         &self,
120         salt: &[u8; HIDDEN_SIZE],
121         deferred_rollback_protection: bool,
122     ) -> diced_open_dice::Result<[u8; HIDDEN_SIZE]> {
123         // We want to make sure we get a different sealing CDI for:
124         // - VMs with different salt values
125         // - An RKP VM and any other VM (regardless of salt)
126         // - depending on whether rollback protection has been deferred to payload. This ensures the
127         //   adversary cannot leak the secrets by using old images & setting
128         //   `deferred_rollback_protection` to true.
129         // The hidden input for DICE affects the sealing CDI (but the values in the config
130         // descriptor do not).
131         // Since the hidden input has to be a fixed size, create it as a hash of the values we
132         // want included.
133         #[derive(AsBytes)]
134         #[repr(C, packed)]
135         struct HiddenInput {
136             rkp_vm_marker: bool,
137             salt: [u8; HIDDEN_SIZE],
138             deferred_rollback_protection: bool,
139         }
140         hash(
141             HiddenInput {
142                 rkp_vm_marker: self.rkp_vm_marker,
143                 salt: *salt,
144                 deferred_rollback_protection,
145             }
146             .as_bytes(),
147         )
148     }
149 
generate_config_descriptor(&self, instance_hash: Option<Hash>) -> Result<Vec<u8>>150     fn generate_config_descriptor(&self, instance_hash: Option<Hash>) -> Result<Vec<u8>> {
151         let mut config = Vec::with_capacity(4);
152         config.push((cbor!(COMPONENT_NAME_KEY)?, cbor!("vm_entry")?));
153         if cfg!(dice_changes) {
154             config.push((cbor!(SECURITY_VERSION_KEY)?, cbor!(self.security_version)?));
155         }
156         if self.rkp_vm_marker {
157             config.push((cbor!(RKP_VM_MARKER_KEY)?, Value::Null))
158         }
159         if let Some(instance_hash) = instance_hash {
160             config.push((cbor!(INSTANCE_HASH_KEY)?, Value::from(instance_hash.as_slice())));
161         }
162         let config = Value::Map(config);
163         Ok(cbor_util::serialize(&config).map_err(|e| {
164             ciborium::value::Error::Custom(format!("Error in serialization: {e:?}"))
165         })?)
166     }
167 }
168 
169 /// Flushes data caches over the provided address range.
170 ///
171 /// # Safety
172 ///
173 /// The provided address and size must be to an address range that is valid for read and write
174 /// (typically on the stack, .bss, .data, or provided BCC) from a single allocation
175 /// (e.g. stack array).
176 #[no_mangle]
177 #[cfg(not(test))]
DiceClearMemory( _ctx: *mut core::ffi::c_void, size: usize, addr: *mut core::ffi::c_void, )178 unsafe extern "C" fn DiceClearMemory(
179     _ctx: *mut core::ffi::c_void,
180     size: usize,
181     addr: *mut core::ffi::c_void,
182 ) {
183     use core::slice;
184     use vmbase::memory::flushed_zeroize;
185 
186     // SAFETY: We require our caller to provide a valid range within a single object. The open-dice
187     // always calls this on individual stack-allocated arrays which ensures that.
188     let region = unsafe { slice::from_raw_parts_mut(addr as *mut u8, size) };
189     flushed_zeroize(region)
190 }
191 
192 #[cfg(test)]
193 mod tests {
194     use crate::{
195         Hash, PartialInputs, COMPONENT_NAME_KEY, INSTANCE_HASH_KEY, RKP_VM_MARKER_KEY,
196         SECURITY_VERSION_KEY,
197     };
198     use ciborium::Value;
199     use diced_open_dice::DiceArtifacts;
200     use diced_open_dice::DiceMode;
201     use diced_open_dice::HIDDEN_SIZE;
202     use pvmfw_avb::Capability;
203     use pvmfw_avb::DebugLevel;
204     use pvmfw_avb::Digest;
205     use pvmfw_avb::VerifiedBootData;
206     use std::collections::HashMap;
207     use std::mem::size_of;
208     use std::vec;
209 
210     const COMPONENT_VERSION_KEY: i64 = -70003;
211     const RESETTABLE_KEY: i64 = -70004;
212     const BASE_VB_DATA: VerifiedBootData = VerifiedBootData {
213         debug_level: DebugLevel::None,
214         kernel_digest: [1u8; size_of::<Digest>()],
215         initrd_digest: Some([2u8; size_of::<Digest>()]),
216         public_key: b"public key",
217         capabilities: vec![],
218         rollback_index: 42,
219     };
220     const HASH: Hash = *b"sixtyfourbyteslongsentencearerarebutletsgiveitatrycantbethathard";
221 
222     #[test]
base_data_conversion()223     fn base_data_conversion() {
224         let vb_data = BASE_VB_DATA;
225         let inputs = PartialInputs::new(&vb_data).unwrap();
226 
227         assert_eq!(inputs.mode, DiceMode::kDiceModeNormal);
228         assert_eq!(inputs.security_version, 42);
229         assert!(!inputs.rkp_vm_marker);
230 
231         // TODO(b/313608219): Consider checks for code_hash and possibly auth_hash.
232     }
233 
234     #[test]
debuggable_conversion()235     fn debuggable_conversion() {
236         let vb_data = VerifiedBootData { debug_level: DebugLevel::Full, ..BASE_VB_DATA };
237         let inputs = PartialInputs::new(&vb_data).unwrap();
238 
239         assert_eq!(inputs.mode, DiceMode::kDiceModeDebug);
240     }
241 
242     #[test]
rkp_vm_conversion()243     fn rkp_vm_conversion() {
244         let vb_data =
245             VerifiedBootData { capabilities: vec![Capability::RemoteAttest], ..BASE_VB_DATA };
246         let inputs = PartialInputs::new(&vb_data).unwrap();
247 
248         assert!(inputs.rkp_vm_marker);
249     }
250 
251     #[test]
base_config_descriptor()252     fn base_config_descriptor() {
253         let vb_data = BASE_VB_DATA;
254         let inputs = PartialInputs::new(&vb_data).unwrap();
255         let config_map = decode_config_descriptor(&inputs, None);
256 
257         assert_eq!(config_map.get(&COMPONENT_NAME_KEY).unwrap().as_text().unwrap(), "vm_entry");
258         assert_eq!(config_map.get(&COMPONENT_VERSION_KEY), None);
259         assert_eq!(config_map.get(&RESETTABLE_KEY), None);
260         if cfg!(dice_changes) {
261             assert_eq!(
262                 config_map.get(&SECURITY_VERSION_KEY).unwrap().as_integer().unwrap(),
263                 42.into()
264             );
265         } else {
266             assert_eq!(config_map.get(&SECURITY_VERSION_KEY), None);
267         }
268         assert_eq!(config_map.get(&RKP_VM_MARKER_KEY), None);
269     }
270 
271     #[test]
config_descriptor_with_rkp_vm()272     fn config_descriptor_with_rkp_vm() {
273         let vb_data =
274             VerifiedBootData { capabilities: vec![Capability::RemoteAttest], ..BASE_VB_DATA };
275         let inputs = PartialInputs::new(&vb_data).unwrap();
276         let config_map = decode_config_descriptor(&inputs, Some(HASH));
277 
278         assert!(config_map.get(&RKP_VM_MARKER_KEY).unwrap().is_null());
279     }
280 
281     #[test]
config_descriptor_with_instance_hash()282     fn config_descriptor_with_instance_hash() {
283         let vb_data =
284             VerifiedBootData { capabilities: vec![Capability::RemoteAttest], ..BASE_VB_DATA };
285         let inputs = PartialInputs::new(&vb_data).unwrap();
286         let config_map = decode_config_descriptor(&inputs, Some(HASH));
287         assert_eq!(*config_map.get(&INSTANCE_HASH_KEY).unwrap(), Value::from(HASH.as_slice()));
288     }
289 
290     #[test]
config_descriptor_without_instance_hash()291     fn config_descriptor_without_instance_hash() {
292         let vb_data =
293             VerifiedBootData { capabilities: vec![Capability::RemoteAttest], ..BASE_VB_DATA };
294         let inputs = PartialInputs::new(&vb_data).unwrap();
295         let config_map = decode_config_descriptor(&inputs, None);
296         assert!(!config_map.contains_key(&INSTANCE_HASH_KEY));
297     }
298 
decode_config_descriptor( inputs: &PartialInputs, instance_hash: Option<Hash>, ) -> HashMap<i64, Value>299     fn decode_config_descriptor(
300         inputs: &PartialInputs,
301         instance_hash: Option<Hash>,
302     ) -> HashMap<i64, Value> {
303         let config_descriptor = inputs.generate_config_descriptor(instance_hash).unwrap();
304 
305         let cbor_map =
306             cbor_util::deserialize::<Value>(&config_descriptor).unwrap().into_map().unwrap();
307 
308         cbor_map
309             .into_iter()
310             .map(|(k, v)| ((k.into_integer().unwrap().try_into().unwrap()), v))
311             .collect()
312     }
313 
314     #[test]
changing_deferred_rpb_changes_secrets()315     fn changing_deferred_rpb_changes_secrets() {
316         let vb_data = VerifiedBootData { debug_level: DebugLevel::Full, ..BASE_VB_DATA };
317         let inputs = PartialInputs::new(&vb_data).unwrap();
318         let mut buffer_without_defer = [0; 4096];
319         let mut buffer_with_defer = [0; 4096];
320         let mut buffer_without_defer_retry = [0; 4096];
321 
322         let sample_dice_input: &[u8] = &[
323             0xa3, // CDI attest
324             0x01, 0x58, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
325             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
326             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // CDI seal
327             0x02, 0x58, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
328             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
329             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // DICE chain
330             0x03, 0x82, 0xa6, 0x01, 0x02, 0x03, 0x27, 0x04, 0x02, 0x20, 0x01, 0x21, 0x40, 0x22,
331             0x40, 0x84, 0x40, 0xa0, 0x40, 0x40,
332             // 8-bytes of trailing data that aren't part of the DICE chain.
333             0x84, 0x41, 0x55, 0xa0, 0x42, 0x11, 0x22, 0x40,
334         ];
335 
336         inputs
337             .clone()
338             .write_next_bcc(
339                 sample_dice_input,
340                 &[0u8; HIDDEN_SIZE],
341                 Some([0u8; 64]),
342                 false,
343                 &mut buffer_without_defer,
344             )
345             .unwrap();
346         let bcc_handover1 = diced_open_dice::bcc_handover_parse(&buffer_without_defer).unwrap();
347 
348         inputs
349             .clone()
350             .write_next_bcc(
351                 sample_dice_input,
352                 &[0u8; HIDDEN_SIZE],
353                 Some([0u8; 64]),
354                 true,
355                 &mut buffer_with_defer,
356             )
357             .unwrap();
358         let bcc_handover2 = diced_open_dice::bcc_handover_parse(&buffer_with_defer).unwrap();
359 
360         inputs
361             .clone()
362             .write_next_bcc(
363                 sample_dice_input,
364                 &[0u8; HIDDEN_SIZE],
365                 Some([0u8; 64]),
366                 false,
367                 &mut buffer_without_defer_retry,
368             )
369             .unwrap();
370         let bcc_handover3 =
371             diced_open_dice::bcc_handover_parse(&buffer_without_defer_retry).unwrap();
372 
373         assert_ne!(bcc_handover1.cdi_seal(), bcc_handover2.cdi_seal());
374         assert_eq!(bcc_handover1.cdi_seal(), bcc_handover3.cdi_seal());
375     }
376 }
377