blob: 8be73a452838fdadad49d4a6bd6bbdb056d2454b [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
39 CborError(ciborium::value::Error),
40 /// Error in DICE operations
41 DiceError(diced_open_dice::DiceError),
42}
43
44impl From<ciborium::value::Error> for Error {
45 fn from(e: ciborium::value::Error) -> Self {
46 Self::CborError(e)
47 }
48}
49
50impl From<diced_open_dice::DiceError> for Error {
51 fn from(e: diced_open_dice::DiceError) -> Self {
52 Self::DiceError(e)
53 }
54}
55
56// DICE in pvmfw result type.
57type Result<T> = core::result::Result<T, Error>;
58
Alice Wang31226132023-01-31 12:44:39 +000059fn to_dice_mode(debug_level: DebugLevel) -> DiceMode {
Alice Wang1f0add02023-01-23 16:22:53 +000060 match debug_level {
Alice Wang31226132023-01-31 12:44:39 +000061 DebugLevel::None => DiceMode::kDiceModeNormal,
62 DebugLevel::Full => DiceMode::kDiceModeDebug,
Alice Wang1f0add02023-01-23 16:22:53 +000063 }
64}
65
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +000066fn to_dice_hash(verified_boot_data: &VerifiedBootData) -> Result<Hash> {
Alice Wang1f0add02023-01-23 16:22:53 +000067 let mut digests = [0u8; size_of::<Digest>() * 2];
68 digests[..size_of::<Digest>()].copy_from_slice(&verified_boot_data.kernel_digest);
69 if let Some(initrd_digest) = verified_boot_data.initrd_digest {
70 digests[size_of::<Digest>()..].copy_from_slice(&initrd_digest);
71 }
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +000072 Ok(hash(&digests)?)
Alice Wang1f0add02023-01-23 16:22:53 +000073}
Pierre-Clément Tosi4f4f5eb2022-12-08 14:31:42 +000074
Shikha Panwarf3acfd12024-05-28 15:48:13 +000075#[derive(Clone)]
Pierre-Clément Tosif58f3a32023-02-02 16:24:23 +000076pub struct PartialInputs {
Pierre-Clément Tosi1cc5eb72023-02-02 11:09:18 +000077 pub code_hash: Hash,
78 pub auth_hash: Hash,
79 pub mode: DiceMode,
Shikha Panwara26f16a2023-09-27 09:39:00 +000080 pub security_version: u64,
Alan Stokesa639b582023-11-22 13:14:27 +000081 pub rkp_vm_marker: bool,
Pierre-Clément Tosif58f3a32023-02-02 16:24:23 +000082}
Pierre-Clément Tosi4f4f5eb2022-12-08 14:31:42 +000083
Pierre-Clément Tosif58f3a32023-02-02 16:24:23 +000084impl PartialInputs {
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +000085 pub fn new(data: &VerifiedBootData) -> Result<Self> {
Pierre-Clément Tosif58f3a32023-02-02 16:24:23 +000086 let code_hash = to_dice_hash(data)?;
87 let auth_hash = hash(data.public_key)?;
88 let mode = to_dice_mode(data.debug_level);
Shikha Panwara26f16a2023-09-27 09:39:00 +000089 // We use rollback_index from vbmeta as the security_version field in dice certificate.
90 let security_version = data.rollback_index;
Alan Stokesa639b582023-11-22 13:14:27 +000091 let rkp_vm_marker = data.has_capability(Capability::RemoteAttest);
Pierre-Clément Tosi4f4f5eb2022-12-08 14:31:42 +000092
Alan Stokesa639b582023-11-22 13:14:27 +000093 Ok(Self { code_hash, auth_hash, mode, security_version, rkp_vm_marker })
Pierre-Clément Tosif58f3a32023-02-02 16:24:23 +000094 }
95
Alan Stokesc4354b82023-05-04 16:06:52 +010096 pub fn write_next_bcc(
Alice Wang843d8312023-02-15 09:47:06 +000097 self,
Alan Stokesc4354b82023-05-04 16:06:52 +010098 current_bcc_handover: &[u8],
Alice Wang843d8312023-02-15 09:47:06 +000099 salt: &[u8; HIDDEN_SIZE],
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +0000100 instance_hash: Option<Hash>,
Shikha Panwarffadd4d2024-05-28 13:47:56 +0000101 deferred_rollback_protection: bool,
Alan Stokesc4354b82023-05-04 16:06:52 +0100102 next_bcc: &mut [u8],
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +0000103 ) -> Result<()> {
104 let config = self
105 .generate_config_descriptor(instance_hash)
106 .map_err(|_| diced_open_dice::DiceError::InvalidInput)?;
Pierre-Clément Tosif58f3a32023-02-02 16:24:23 +0000107
Alan Stokesc4354b82023-05-04 16:06:52 +0100108 let dice_inputs = InputValues::new(
Pierre-Clément Tosif58f3a32023-02-02 16:24:23 +0000109 self.code_hash,
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +0000110 Config::Descriptor(&config),
Pierre-Clément Tosif58f3a32023-02-02 16:24:23 +0000111 self.auth_hash,
112 self.mode,
Shikha Panwarffadd4d2024-05-28 13:47:56 +0000113 self.make_hidden(salt, deferred_rollback_protection)?,
Alan Stokesc4354b82023-05-04 16:06:52 +0100114 );
115 let _ = bcc_handover_main_flow(current_bcc_handover, &dice_inputs, next_bcc)?;
116 Ok(())
Pierre-Clément Tosif58f3a32023-02-02 16:24:23 +0000117 }
Alan Stokesddb988c2023-11-27 11:13:06 +0000118
Shikha Panwarffadd4d2024-05-28 13:47:56 +0000119 fn make_hidden(
120 &self,
121 salt: &[u8; HIDDEN_SIZE],
122 deferred_rollback_protection: bool,
123 ) -> diced_open_dice::Result<[u8; HIDDEN_SIZE]> {
Alan Stokesa17cfba2024-02-14 17:34:51 +0000124 // We want to make sure we get a different sealing CDI for:
125 // - VMs with different salt values
126 // - An RKP VM and any other VM (regardless of salt)
Shikha Panwarffadd4d2024-05-28 13:47:56 +0000127 // - depending on whether rollback protection has been deferred to payload. This ensures the
128 // adversary cannot leak the secrets by using old images & setting
129 // `deferred_rollback_protection` to true.
Alan Stokesa17cfba2024-02-14 17:34:51 +0000130 // The hidden input for DICE affects the sealing CDI (but the values in the config
131 // descriptor do not).
132 // Since the hidden input has to be a fixed size, create it as a hash of the values we
133 // want included.
134 #[derive(AsBytes)]
135 #[repr(C, packed)]
136 struct HiddenInput {
137 rkp_vm_marker: bool,
138 salt: [u8; HIDDEN_SIZE],
Shikha Panwarffadd4d2024-05-28 13:47:56 +0000139 deferred_rollback_protection: bool,
Alan Stokesa17cfba2024-02-14 17:34:51 +0000140 }
Shikha Panwarffadd4d2024-05-28 13:47:56 +0000141 hash(
142 HiddenInput {
143 rkp_vm_marker: self.rkp_vm_marker,
144 salt: *salt,
145 deferred_rollback_protection,
146 }
147 .as_bytes(),
148 )
Alan Stokesa17cfba2024-02-14 17:34:51 +0000149 }
150
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +0000151 fn generate_config_descriptor(&self, instance_hash: Option<Hash>) -> Result<Vec<u8>> {
152 let mut config = Vec::with_capacity(4);
153 config.push((cbor!(COMPONENT_NAME_KEY)?, cbor!("vm_entry")?));
154 if cfg!(dice_changes) {
155 config.push((cbor!(SECURITY_VERSION_KEY)?, cbor!(self.security_version)?));
156 }
157 if self.rkp_vm_marker {
158 config.push((cbor!(RKP_VM_MARKER_KEY)?, Value::Null))
159 }
160 if let Some(instance_hash) = instance_hash {
161 config.push((cbor!(INSTANCE_HASH_KEY)?, Value::from(instance_hash.as_slice())));
162 }
163 let config = Value::Map(config);
164 Ok(cbor_util::serialize(&config).map_err(|e| {
165 ciborium::value::Error::Custom(format!("Error in serialization: {e:?}"))
166 })?)
Alan Stokesddb988c2023-11-27 11:13:06 +0000167 }
Pierre-Clément Tosi4f4f5eb2022-12-08 14:31:42 +0000168}
Pierre-Clément Tosibec84662023-01-04 14:25:33 +0000169
170/// Flushes data caches over the provided address range.
171///
172/// # Safety
173///
Alan Stokesddb988c2023-11-27 11:13:06 +0000174/// The provided address and size must be to an address range that is valid for read and write
175/// (typically on the stack, .bss, .data, or provided BCC) from a single allocation
176/// (e.g. stack array).
Pierre-Clément Tosibec84662023-01-04 14:25:33 +0000177#[no_mangle]
Alan Stokesddb988c2023-11-27 11:13:06 +0000178#[cfg(not(test))]
179unsafe extern "C" fn DiceClearMemory(
180 _ctx: *mut core::ffi::c_void,
181 size: usize,
182 addr: *mut core::ffi::c_void,
183) {
184 use core::slice;
185 use vmbase::memory::flushed_zeroize;
186
187 // SAFETY: We require our caller to provide a valid range within a single object. The open-dice
188 // always calls this on individual stack-allocated arrays which ensures that.
Pierre-Clément Tosibec84662023-01-04 14:25:33 +0000189 let region = unsafe { slice::from_raw_parts_mut(addr as *mut u8, size) };
190 flushed_zeroize(region)
191}
Alan Stokesddb988c2023-11-27 11:13:06 +0000192
193#[cfg(test)]
194mod tests {
Shikha Panwarf3acfd12024-05-28 15:48:13 +0000195 use crate::{
196 Hash, PartialInputs, COMPONENT_NAME_KEY, INSTANCE_HASH_KEY, RKP_VM_MARKER_KEY,
197 SECURITY_VERSION_KEY,
198 };
Alan Stokesddb988c2023-11-27 11:13:06 +0000199 use ciborium::Value;
Shikha Panwarf3acfd12024-05-28 15:48:13 +0000200 use diced_open_dice::DiceArtifacts;
201 use diced_open_dice::DiceMode;
202 use diced_open_dice::HIDDEN_SIZE;
203 use pvmfw_avb::Capability;
204 use pvmfw_avb::DebugLevel;
205 use pvmfw_avb::Digest;
206 use pvmfw_avb::VerifiedBootData;
Alan Stokesddb988c2023-11-27 11:13:06 +0000207 use std::collections::HashMap;
Shikha Panwarf3acfd12024-05-28 15:48:13 +0000208 use std::mem::size_of;
Alan Stokesddb988c2023-11-27 11:13:06 +0000209 use std::vec;
210
Alan Stokesddb988c2023-11-27 11:13:06 +0000211 const COMPONENT_VERSION_KEY: i64 = -70003;
212 const RESETTABLE_KEY: i64 = -70004;
Alan Stokesddb988c2023-11-27 11:13:06 +0000213 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 capabilities: vec![],
219 rollback_index: 42,
220 };
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +0000221 const HASH: Hash = *b"sixtyfourbyteslongsentencearerarebutletsgiveitatrycantbethathard";
Alan Stokesddb988c2023-11-27 11:13:06 +0000222
223 #[test]
224 fn base_data_conversion() {
225 let vb_data = BASE_VB_DATA;
226 let inputs = PartialInputs::new(&vb_data).unwrap();
227
228 assert_eq!(inputs.mode, DiceMode::kDiceModeNormal);
229 assert_eq!(inputs.security_version, 42);
230 assert!(!inputs.rkp_vm_marker);
231
232 // TODO(b/313608219): Consider checks for code_hash and possibly auth_hash.
233 }
234
235 #[test]
236 fn debuggable_conversion() {
237 let vb_data = VerifiedBootData { debug_level: DebugLevel::Full, ..BASE_VB_DATA };
238 let inputs = PartialInputs::new(&vb_data).unwrap();
239
240 assert_eq!(inputs.mode, DiceMode::kDiceModeDebug);
241 }
242
243 #[test]
244 fn rkp_vm_conversion() {
245 let vb_data =
246 VerifiedBootData { capabilities: vec![Capability::RemoteAttest], ..BASE_VB_DATA };
247 let inputs = PartialInputs::new(&vb_data).unwrap();
248
249 assert!(inputs.rkp_vm_marker);
250 }
251
252 #[test]
253 fn base_config_descriptor() {
254 let vb_data = BASE_VB_DATA;
255 let inputs = PartialInputs::new(&vb_data).unwrap();
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +0000256 let config_map = decode_config_descriptor(&inputs, None);
Alan Stokesddb988c2023-11-27 11:13:06 +0000257
258 assert_eq!(config_map.get(&COMPONENT_NAME_KEY).unwrap().as_text().unwrap(), "vm_entry");
259 assert_eq!(config_map.get(&COMPONENT_VERSION_KEY), None);
260 assert_eq!(config_map.get(&RESETTABLE_KEY), None);
261 if cfg!(dice_changes) {
262 assert_eq!(
263 config_map.get(&SECURITY_VERSION_KEY).unwrap().as_integer().unwrap(),
264 42.into()
265 );
266 } else {
267 assert_eq!(config_map.get(&SECURITY_VERSION_KEY), None);
268 }
269 assert_eq!(config_map.get(&RKP_VM_MARKER_KEY), None);
270 }
271
272 #[test]
273 fn config_descriptor_with_rkp_vm() {
274 let vb_data =
275 VerifiedBootData { capabilities: vec![Capability::RemoteAttest], ..BASE_VB_DATA };
276 let inputs = PartialInputs::new(&vb_data).unwrap();
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +0000277 let config_map = decode_config_descriptor(&inputs, Some(HASH));
Alan Stokesddb988c2023-11-27 11:13:06 +0000278
279 assert!(config_map.get(&RKP_VM_MARKER_KEY).unwrap().is_null());
280 }
281
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +0000282 #[test]
283 fn config_descriptor_with_instance_hash() {
284 let vb_data =
285 VerifiedBootData { capabilities: vec![Capability::RemoteAttest], ..BASE_VB_DATA };
286 let inputs = PartialInputs::new(&vb_data).unwrap();
287 let config_map = decode_config_descriptor(&inputs, Some(HASH));
288 assert_eq!(*config_map.get(&INSTANCE_HASH_KEY).unwrap(), Value::from(HASH.as_slice()));
289 }
290
291 #[test]
292 fn config_descriptor_without_instance_hash() {
293 let vb_data =
294 VerifiedBootData { capabilities: vec![Capability::RemoteAttest], ..BASE_VB_DATA };
295 let inputs = PartialInputs::new(&vb_data).unwrap();
296 let config_map = decode_config_descriptor(&inputs, None);
Chris Wailese9579642024-05-29 18:25:19 -0700297 assert!(!config_map.contains_key(&INSTANCE_HASH_KEY));
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +0000298 }
299
300 fn decode_config_descriptor(
301 inputs: &PartialInputs,
302 instance_hash: Option<Hash>,
303 ) -> HashMap<i64, Value> {
304 let config_descriptor = inputs.generate_config_descriptor(instance_hash).unwrap();
Alan Stokesddb988c2023-11-27 11:13:06 +0000305
306 let cbor_map =
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +0000307 cbor_util::deserialize::<Value>(&config_descriptor).unwrap().into_map().unwrap();
Alan Stokesddb988c2023-11-27 11:13:06 +0000308
309 cbor_map
310 .into_iter()
311 .map(|(k, v)| ((k.into_integer().unwrap().try_into().unwrap()), v))
312 .collect()
313 }
Shikha Panwarf3acfd12024-05-28 15:48:13 +0000314
315 #[test]
316 fn changing_deferred_rpb_changes_secrets() {
317 let vb_data = VerifiedBootData { debug_level: DebugLevel::Full, ..BASE_VB_DATA };
318 let inputs = PartialInputs::new(&vb_data).unwrap();
319 let mut buffer_without_defer = [0; 4096];
320 let mut buffer_with_defer = [0; 4096];
321 let mut buffer_without_defer_retry = [0; 4096];
322
323 let sample_dice_input: &[u8] = &[
324 0xa3, // CDI attest
325 0x01, 0x58, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
326 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
327 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // CDI seal
328 0x02, 0x58, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
329 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
330 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // DICE chain
331 0x03, 0x82, 0xa6, 0x01, 0x02, 0x03, 0x27, 0x04, 0x02, 0x20, 0x01, 0x21, 0x40, 0x22,
332 0x40, 0x84, 0x40, 0xa0, 0x40, 0x40,
333 // 8-bytes of trailing data that aren't part of the DICE chain.
334 0x84, 0x41, 0x55, 0xa0, 0x42, 0x11, 0x22, 0x40,
335 ];
336
337 inputs
338 .clone()
339 .write_next_bcc(
340 sample_dice_input,
341 &[0u8; HIDDEN_SIZE],
342 Some([0u8; 64]),
343 false,
344 &mut buffer_without_defer,
345 )
346 .unwrap();
347 let bcc_handover1 = diced_open_dice::bcc_handover_parse(&buffer_without_defer).unwrap();
348
349 inputs
350 .clone()
351 .write_next_bcc(
352 sample_dice_input,
353 &[0u8; HIDDEN_SIZE],
354 Some([0u8; 64]),
355 true,
356 &mut buffer_with_defer,
357 )
358 .unwrap();
359 let bcc_handover2 = diced_open_dice::bcc_handover_parse(&buffer_with_defer).unwrap();
360
361 inputs
362 .clone()
363 .write_next_bcc(
364 sample_dice_input,
365 &[0u8; HIDDEN_SIZE],
366 Some([0u8; 64]),
367 false,
368 &mut buffer_without_defer_retry,
369 )
370 .unwrap();
371 let bcc_handover3 =
372 diced_open_dice::bcc_handover_parse(&buffer_without_defer_retry).unwrap();
373
374 assert_ne!(bcc_handover1.cdi_seal(), bcc_handover2.cdi_seal());
375 assert_eq!(bcc_handover1.cdi_seal(), bcc_handover3.cdi_seal());
376 }
Alan Stokesddb988c2023-11-27 11:13:06 +0000377}