blob: 470711f2a66d4b0238daedae0127efe303d8e3a4 [file] [log] [blame]
Pierre-Clément Tosi4f4f5eb2022-12-08 14:31:42 +00001// 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.
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +000016extern crate alloc;
Pierre-Clément Tosi4f4f5eb2022-12-08 14:31:42 +000017
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +000018use alloc::format;
19use alloc::vec::Vec;
20use ciborium::cbor;
21use ciborium::Value;
Alice Wang1f0add02023-01-23 16:22:53 +000022use core::mem::size_of;
Alice Wang843d8312023-02-15 09:47:06 +000023use diced_open_dice::{
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +000024 bcc_handover_main_flow, hash, Config, DiceMode, Hash, InputValues, HIDDEN_SIZE,
Alice Wang843d8312023-02-15 09:47:06 +000025};
Alan Stokesa639b582023-11-22 13:14:27 +000026use pvmfw_avb::{Capability, DebugLevel, Digest, VerifiedBootData};
Alan Stokesa17cfba2024-02-14 17:34:51 +000027use zerocopy::AsBytes;
Alice Wang1f0add02023-01-23 16:22:53 +000028
Shikha Panwar47e5b612024-05-20 19:50:02 +000029// pVM firmware (like other VM components) is expected to populate some fields in DICE
30// Configuration Descriptor. See dice_for_avf_guest.cddl
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +000031const COMPONENT_NAME_KEY: i64 = -70002;
32const SECURITY_VERSION_KEY: i64 = -70005;
33const RKP_VM_MARKER_KEY: i64 = -70006;
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +000034const INSTANCE_HASH_KEY: i64 = -71003;
35
36#[derive(Debug)]
37pub enum Error {
38 /// Error in CBOR operations
Stephen Hinese1b42892024-08-07 11:03:44 -070039 #[allow(dead_code)]
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +000040 CborError(ciborium::value::Error),
41 /// Error in DICE operations
Stephen Hinese1b42892024-08-07 11:03:44 -070042 #[allow(dead_code)]
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +000043 DiceError(diced_open_dice::DiceError),
44}
45
46impl From<ciborium::value::Error> for Error {
47 fn from(e: ciborium::value::Error) -> Self {
48 Self::CborError(e)
49 }
50}
51
52impl From<diced_open_dice::DiceError> for Error {
53 fn from(e: diced_open_dice::DiceError) -> Self {
54 Self::DiceError(e)
55 }
56}
57
58// DICE in pvmfw result type.
59type Result<T> = core::result::Result<T, Error>;
60
Alice Wang31226132023-01-31 12:44:39 +000061fn to_dice_mode(debug_level: DebugLevel) -> DiceMode {
Alice Wang1f0add02023-01-23 16:22:53 +000062 match debug_level {
Alice Wang31226132023-01-31 12:44:39 +000063 DebugLevel::None => DiceMode::kDiceModeNormal,
64 DebugLevel::Full => DiceMode::kDiceModeDebug,
Alice Wang1f0add02023-01-23 16:22:53 +000065 }
66}
67
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +000068fn to_dice_hash(verified_boot_data: &VerifiedBootData) -> Result<Hash> {
Alice Wang1f0add02023-01-23 16:22:53 +000069 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 }
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +000074 Ok(hash(&digests)?)
Alice Wang1f0add02023-01-23 16:22:53 +000075}
Pierre-Clément Tosi4f4f5eb2022-12-08 14:31:42 +000076
Shikha Panwarf3acfd12024-05-28 15:48:13 +000077#[derive(Clone)]
Pierre-Clément Tosif58f3a32023-02-02 16:24:23 +000078pub struct PartialInputs {
Pierre-Clément Tosi1cc5eb72023-02-02 11:09:18 +000079 pub code_hash: Hash,
80 pub auth_hash: Hash,
81 pub mode: DiceMode,
Shikha Panwara26f16a2023-09-27 09:39:00 +000082 pub security_version: u64,
Alan Stokesa639b582023-11-22 13:14:27 +000083 pub rkp_vm_marker: bool,
Pierre-Clément Tosif58f3a32023-02-02 16:24:23 +000084}
Pierre-Clément Tosi4f4f5eb2022-12-08 14:31:42 +000085
Pierre-Clément Tosif58f3a32023-02-02 16:24:23 +000086impl PartialInputs {
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +000087 pub fn new(data: &VerifiedBootData) -> Result<Self> {
Pierre-Clément Tosif58f3a32023-02-02 16:24:23 +000088 let code_hash = to_dice_hash(data)?;
89 let auth_hash = hash(data.public_key)?;
90 let mode = to_dice_mode(data.debug_level);
Shikha Panwara26f16a2023-09-27 09:39:00 +000091 // We use rollback_index from vbmeta as the security_version field in dice certificate.
92 let security_version = data.rollback_index;
Alan Stokesa639b582023-11-22 13:14:27 +000093 let rkp_vm_marker = data.has_capability(Capability::RemoteAttest);
Pierre-Clément Tosi4f4f5eb2022-12-08 14:31:42 +000094
Alan Stokesa639b582023-11-22 13:14:27 +000095 Ok(Self { code_hash, auth_hash, mode, security_version, rkp_vm_marker })
Pierre-Clément Tosif58f3a32023-02-02 16:24:23 +000096 }
97
Alan Stokesc4354b82023-05-04 16:06:52 +010098 pub fn write_next_bcc(
Alice Wang843d8312023-02-15 09:47:06 +000099 self,
Alan Stokesc4354b82023-05-04 16:06:52 +0100100 current_bcc_handover: &[u8],
Alice Wang843d8312023-02-15 09:47:06 +0000101 salt: &[u8; HIDDEN_SIZE],
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +0000102 instance_hash: Option<Hash>,
Shikha Panwarffadd4d2024-05-28 13:47:56 +0000103 deferred_rollback_protection: bool,
Alan Stokesc4354b82023-05-04 16:06:52 +0100104 next_bcc: &mut [u8],
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +0000105 ) -> Result<()> {
106 let config = self
107 .generate_config_descriptor(instance_hash)
108 .map_err(|_| diced_open_dice::DiceError::InvalidInput)?;
Pierre-Clément Tosif58f3a32023-02-02 16:24:23 +0000109
Alan Stokesc4354b82023-05-04 16:06:52 +0100110 let dice_inputs = InputValues::new(
Pierre-Clément Tosif58f3a32023-02-02 16:24:23 +0000111 self.code_hash,
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +0000112 Config::Descriptor(&config),
Pierre-Clément Tosif58f3a32023-02-02 16:24:23 +0000113 self.auth_hash,
114 self.mode,
Shikha Panwarffadd4d2024-05-28 13:47:56 +0000115 self.make_hidden(salt, deferred_rollback_protection)?,
Alan Stokesc4354b82023-05-04 16:06:52 +0100116 );
117 let _ = bcc_handover_main_flow(current_bcc_handover, &dice_inputs, next_bcc)?;
118 Ok(())
Pierre-Clément Tosif58f3a32023-02-02 16:24:23 +0000119 }
Alan Stokesddb988c2023-11-27 11:13:06 +0000120
Shikha Panwarffadd4d2024-05-28 13:47:56 +0000121 fn make_hidden(
122 &self,
123 salt: &[u8; HIDDEN_SIZE],
124 deferred_rollback_protection: bool,
125 ) -> diced_open_dice::Result<[u8; HIDDEN_SIZE]> {
Alan Stokesa17cfba2024-02-14 17:34:51 +0000126 // We want to make sure we get a different sealing CDI for:
127 // - VMs with different salt values
128 // - An RKP VM and any other VM (regardless of salt)
Shikha Panwarffadd4d2024-05-28 13:47:56 +0000129 // - depending on whether rollback protection has been deferred to payload. This ensures the
130 // adversary cannot leak the secrets by using old images & setting
131 // `deferred_rollback_protection` to true.
Alan Stokesa17cfba2024-02-14 17:34:51 +0000132 // The hidden input for DICE affects the sealing CDI (but the values in the config
133 // descriptor do not).
134 // Since the hidden input has to be a fixed size, create it as a hash of the values we
135 // want included.
136 #[derive(AsBytes)]
137 #[repr(C, packed)]
138 struct HiddenInput {
139 rkp_vm_marker: bool,
140 salt: [u8; HIDDEN_SIZE],
Shikha Panwarffadd4d2024-05-28 13:47:56 +0000141 deferred_rollback_protection: bool,
Alan Stokesa17cfba2024-02-14 17:34:51 +0000142 }
Shikha Panwarffadd4d2024-05-28 13:47:56 +0000143 hash(
144 HiddenInput {
145 rkp_vm_marker: self.rkp_vm_marker,
146 salt: *salt,
147 deferred_rollback_protection,
148 }
149 .as_bytes(),
150 )
Alan Stokesa17cfba2024-02-14 17:34:51 +0000151 }
152
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +0000153 fn generate_config_descriptor(&self, instance_hash: Option<Hash>) -> Result<Vec<u8>> {
154 let mut config = Vec::with_capacity(4);
155 config.push((cbor!(COMPONENT_NAME_KEY)?, cbor!("vm_entry")?));
156 if cfg!(dice_changes) {
157 config.push((cbor!(SECURITY_VERSION_KEY)?, cbor!(self.security_version)?));
158 }
159 if self.rkp_vm_marker {
160 config.push((cbor!(RKP_VM_MARKER_KEY)?, Value::Null))
161 }
162 if let Some(instance_hash) = instance_hash {
163 config.push((cbor!(INSTANCE_HASH_KEY)?, Value::from(instance_hash.as_slice())));
164 }
165 let config = Value::Map(config);
166 Ok(cbor_util::serialize(&config).map_err(|e| {
167 ciborium::value::Error::Custom(format!("Error in serialization: {e:?}"))
168 })?)
Alan Stokesddb988c2023-11-27 11:13:06 +0000169 }
Pierre-Clément Tosi4f4f5eb2022-12-08 14:31:42 +0000170}
Pierre-Clément Tosibec84662023-01-04 14:25:33 +0000171
172/// Flushes data caches over the provided address range.
173///
174/// # Safety
175///
Alan Stokesddb988c2023-11-27 11:13:06 +0000176/// The provided address and size must be to an address range that is valid for read and write
177/// (typically on the stack, .bss, .data, or provided BCC) from a single allocation
178/// (e.g. stack array).
Pierre-Clément Tosibec84662023-01-04 14:25:33 +0000179#[no_mangle]
Alan Stokesddb988c2023-11-27 11:13:06 +0000180#[cfg(not(test))]
181unsafe extern "C" fn DiceClearMemory(
182 _ctx: *mut core::ffi::c_void,
183 size: usize,
184 addr: *mut core::ffi::c_void,
185) {
186 use core::slice;
187 use vmbase::memory::flushed_zeroize;
188
189 // SAFETY: We require our caller to provide a valid range within a single object. The open-dice
190 // always calls this on individual stack-allocated arrays which ensures that.
Pierre-Clément Tosibec84662023-01-04 14:25:33 +0000191 let region = unsafe { slice::from_raw_parts_mut(addr as *mut u8, size) };
192 flushed_zeroize(region)
193}
Alan Stokesddb988c2023-11-27 11:13:06 +0000194
195#[cfg(test)]
196mod tests {
Shikha Panwarf3acfd12024-05-28 15:48:13 +0000197 use crate::{
198 Hash, PartialInputs, COMPONENT_NAME_KEY, INSTANCE_HASH_KEY, RKP_VM_MARKER_KEY,
199 SECURITY_VERSION_KEY,
200 };
Alan Stokesddb988c2023-11-27 11:13:06 +0000201 use ciborium::Value;
Shikha Panwarf3acfd12024-05-28 15:48:13 +0000202 use diced_open_dice::DiceArtifacts;
203 use diced_open_dice::DiceMode;
204 use diced_open_dice::HIDDEN_SIZE;
205 use pvmfw_avb::Capability;
206 use pvmfw_avb::DebugLevel;
207 use pvmfw_avb::Digest;
208 use pvmfw_avb::VerifiedBootData;
Alan Stokesddb988c2023-11-27 11:13:06 +0000209 use std::collections::HashMap;
Shikha Panwarf3acfd12024-05-28 15:48:13 +0000210 use std::mem::size_of;
Alan Stokesddb988c2023-11-27 11:13:06 +0000211 use std::vec;
212
Alan Stokesddb988c2023-11-27 11:13:06 +0000213 const COMPONENT_VERSION_KEY: i64 = -70003;
214 const RESETTABLE_KEY: i64 = -70004;
Alan Stokesddb988c2023-11-27 11:13:06 +0000215 const BASE_VB_DATA: VerifiedBootData = VerifiedBootData {
216 debug_level: DebugLevel::None,
217 kernel_digest: [1u8; size_of::<Digest>()],
218 initrd_digest: Some([2u8; size_of::<Digest>()]),
219 public_key: b"public key",
220 capabilities: vec![],
221 rollback_index: 42,
222 };
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +0000223 const HASH: Hash = *b"sixtyfourbyteslongsentencearerarebutletsgiveitatrycantbethathard";
Alan Stokesddb988c2023-11-27 11:13:06 +0000224
225 #[test]
226 fn base_data_conversion() {
227 let vb_data = BASE_VB_DATA;
228 let inputs = PartialInputs::new(&vb_data).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]
238 fn debuggable_conversion() {
239 let vb_data = VerifiedBootData { debug_level: DebugLevel::Full, ..BASE_VB_DATA };
240 let inputs = PartialInputs::new(&vb_data).unwrap();
241
242 assert_eq!(inputs.mode, DiceMode::kDiceModeDebug);
243 }
244
245 #[test]
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).unwrap();
250
251 assert!(inputs.rkp_vm_marker);
252 }
253
254 #[test]
255 fn base_config_descriptor() {
256 let vb_data = BASE_VB_DATA;
257 let inputs = PartialInputs::new(&vb_data).unwrap();
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +0000258 let config_map = decode_config_descriptor(&inputs, None);
Alan Stokesddb988c2023-11-27 11:13:06 +0000259
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 if cfg!(dice_changes) {
264 assert_eq!(
265 config_map.get(&SECURITY_VERSION_KEY).unwrap().as_integer().unwrap(),
266 42.into()
267 );
268 } else {
269 assert_eq!(config_map.get(&SECURITY_VERSION_KEY), None);
270 }
271 assert_eq!(config_map.get(&RKP_VM_MARKER_KEY), None);
272 }
273
274 #[test]
275 fn config_descriptor_with_rkp_vm() {
276 let vb_data =
277 VerifiedBootData { capabilities: vec![Capability::RemoteAttest], ..BASE_VB_DATA };
278 let inputs = PartialInputs::new(&vb_data).unwrap();
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +0000279 let config_map = decode_config_descriptor(&inputs, Some(HASH));
Alan Stokesddb988c2023-11-27 11:13:06 +0000280
281 assert!(config_map.get(&RKP_VM_MARKER_KEY).unwrap().is_null());
282 }
283
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +0000284 #[test]
285 fn config_descriptor_with_instance_hash() {
286 let vb_data =
287 VerifiedBootData { capabilities: vec![Capability::RemoteAttest], ..BASE_VB_DATA };
288 let inputs = PartialInputs::new(&vb_data).unwrap();
289 let config_map = decode_config_descriptor(&inputs, Some(HASH));
290 assert_eq!(*config_map.get(&INSTANCE_HASH_KEY).unwrap(), Value::from(HASH.as_slice()));
291 }
292
293 #[test]
294 fn config_descriptor_without_instance_hash() {
295 let vb_data =
296 VerifiedBootData { capabilities: vec![Capability::RemoteAttest], ..BASE_VB_DATA };
297 let inputs = PartialInputs::new(&vb_data).unwrap();
298 let config_map = decode_config_descriptor(&inputs, None);
Chris Wailese9579642024-05-29 18:25:19 -0700299 assert!(!config_map.contains_key(&INSTANCE_HASH_KEY));
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +0000300 }
301
302 fn decode_config_descriptor(
303 inputs: &PartialInputs,
304 instance_hash: Option<Hash>,
305 ) -> HashMap<i64, Value> {
306 let config_descriptor = inputs.generate_config_descriptor(instance_hash).unwrap();
Alan Stokesddb988c2023-11-27 11:13:06 +0000307
308 let cbor_map =
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +0000309 cbor_util::deserialize::<Value>(&config_descriptor).unwrap().into_map().unwrap();
Alan Stokesddb988c2023-11-27 11:13:06 +0000310
311 cbor_map
312 .into_iter()
313 .map(|(k, v)| ((k.into_integer().unwrap().try_into().unwrap()), v))
314 .collect()
315 }
Shikha Panwarf3acfd12024-05-28 15:48:13 +0000316
317 #[test]
318 fn changing_deferred_rpb_changes_secrets() {
319 let vb_data = VerifiedBootData { debug_level: DebugLevel::Full, ..BASE_VB_DATA };
320 let inputs = PartialInputs::new(&vb_data).unwrap();
321 let mut buffer_without_defer = [0; 4096];
322 let mut buffer_with_defer = [0; 4096];
323 let mut buffer_without_defer_retry = [0; 4096];
324
325 let sample_dice_input: &[u8] = &[
326 0xa3, // CDI attest
327 0x01, 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, // CDI seal
330 0x02, 0x58, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
331 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
332 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // DICE chain
333 0x03, 0x82, 0xa6, 0x01, 0x02, 0x03, 0x27, 0x04, 0x02, 0x20, 0x01, 0x21, 0x40, 0x22,
334 0x40, 0x84, 0x40, 0xa0, 0x40, 0x40,
335 // 8-bytes of trailing data that aren't part of the DICE chain.
336 0x84, 0x41, 0x55, 0xa0, 0x42, 0x11, 0x22, 0x40,
337 ];
338
339 inputs
340 .clone()
341 .write_next_bcc(
342 sample_dice_input,
343 &[0u8; HIDDEN_SIZE],
344 Some([0u8; 64]),
345 false,
346 &mut buffer_without_defer,
347 )
348 .unwrap();
349 let bcc_handover1 = diced_open_dice::bcc_handover_parse(&buffer_without_defer).unwrap();
350
351 inputs
352 .clone()
353 .write_next_bcc(
354 sample_dice_input,
355 &[0u8; HIDDEN_SIZE],
356 Some([0u8; 64]),
357 true,
358 &mut buffer_with_defer,
359 )
360 .unwrap();
361 let bcc_handover2 = diced_open_dice::bcc_handover_parse(&buffer_with_defer).unwrap();
362
363 inputs
364 .clone()
365 .write_next_bcc(
366 sample_dice_input,
367 &[0u8; HIDDEN_SIZE],
368 Some([0u8; 64]),
369 false,
370 &mut buffer_without_defer_retry,
371 )
372 .unwrap();
373 let bcc_handover3 =
374 diced_open_dice::bcc_handover_parse(&buffer_without_defer_retry).unwrap();
375
376 assert_ne!(bcc_handover1.cdi_seal(), bcc_handover2.cdi_seal());
377 assert_eq!(bcc_handover1.cdi_seal(), bcc_handover3.cdi_seal());
378 }
Alan Stokesddb988c2023-11-27 11:13:06 +0000379}