blob: da19931208b7ddfe04316f2ee9faabbed280a923 [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 Panwar8f7fc1a2024-04-10 10:41:34 +000029const COMPONENT_NAME_KEY: i64 = -70002;
30const SECURITY_VERSION_KEY: i64 = -70005;
31const RKP_VM_MARKER_KEY: i64 = -70006;
32// TODO(b/291245237): Document this key along with others used in ConfigDescriptor in AVF based VM.
33const INSTANCE_HASH_KEY: i64 = -71003;
34
35#[derive(Debug)]
36pub 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
43impl From<ciborium::value::Error> for Error {
44 fn from(e: ciborium::value::Error) -> Self {
45 Self::CborError(e)
46 }
47}
48
49impl From<diced_open_dice::DiceError> for Error {
50 fn from(e: diced_open_dice::DiceError) -> Self {
51 Self::DiceError(e)
52 }
53}
54
55// DICE in pvmfw result type.
56type Result<T> = core::result::Result<T, Error>;
57
Alice Wang31226132023-01-31 12:44:39 +000058fn to_dice_mode(debug_level: DebugLevel) -> DiceMode {
Alice Wang1f0add02023-01-23 16:22:53 +000059 match debug_level {
Alice Wang31226132023-01-31 12:44:39 +000060 DebugLevel::None => DiceMode::kDiceModeNormal,
61 DebugLevel::Full => DiceMode::kDiceModeDebug,
Alice Wang1f0add02023-01-23 16:22:53 +000062 }
63}
64
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +000065fn to_dice_hash(verified_boot_data: &VerifiedBootData) -> Result<Hash> {
Alice Wang1f0add02023-01-23 16:22:53 +000066 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 }
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +000071 Ok(hash(&digests)?)
Alice Wang1f0add02023-01-23 16:22:53 +000072}
Pierre-Clément Tosi4f4f5eb2022-12-08 14:31:42 +000073
Shikha Panwarf3acfd12024-05-28 15:48:13 +000074#[derive(Clone)]
Pierre-Clément Tosif58f3a32023-02-02 16:24:23 +000075pub struct PartialInputs {
Pierre-Clément Tosi1cc5eb72023-02-02 11:09:18 +000076 pub code_hash: Hash,
77 pub auth_hash: Hash,
78 pub mode: DiceMode,
Shikha Panwara26f16a2023-09-27 09:39:00 +000079 pub security_version: u64,
Alan Stokesa639b582023-11-22 13:14:27 +000080 pub rkp_vm_marker: bool,
Pierre-Clément Tosif58f3a32023-02-02 16:24:23 +000081}
Pierre-Clément Tosi4f4f5eb2022-12-08 14:31:42 +000082
Pierre-Clément Tosif58f3a32023-02-02 16:24:23 +000083impl PartialInputs {
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +000084 pub fn new(data: &VerifiedBootData) -> Result<Self> {
Pierre-Clément Tosif58f3a32023-02-02 16:24:23 +000085 let code_hash = to_dice_hash(data)?;
86 let auth_hash = hash(data.public_key)?;
87 let mode = to_dice_mode(data.debug_level);
Shikha Panwara26f16a2023-09-27 09:39:00 +000088 // We use rollback_index from vbmeta as the security_version field in dice certificate.
89 let security_version = data.rollback_index;
Alan Stokesa639b582023-11-22 13:14:27 +000090 let rkp_vm_marker = data.has_capability(Capability::RemoteAttest);
Pierre-Clément Tosi4f4f5eb2022-12-08 14:31:42 +000091
Alan Stokesa639b582023-11-22 13:14:27 +000092 Ok(Self { code_hash, auth_hash, mode, security_version, rkp_vm_marker })
Pierre-Clément Tosif58f3a32023-02-02 16:24:23 +000093 }
94
Alan Stokesc4354b82023-05-04 16:06:52 +010095 pub fn write_next_bcc(
Alice Wang843d8312023-02-15 09:47:06 +000096 self,
Alan Stokesc4354b82023-05-04 16:06:52 +010097 current_bcc_handover: &[u8],
Alice Wang843d8312023-02-15 09:47:06 +000098 salt: &[u8; HIDDEN_SIZE],
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +000099 instance_hash: Option<Hash>,
Shikha Panwarffadd4d2024-05-28 13:47:56 +0000100 deferred_rollback_protection: bool,
Alan Stokesc4354b82023-05-04 16:06:52 +0100101 next_bcc: &mut [u8],
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +0000102 ) -> Result<()> {
103 let config = self
104 .generate_config_descriptor(instance_hash)
105 .map_err(|_| diced_open_dice::DiceError::InvalidInput)?;
Pierre-Clément Tosif58f3a32023-02-02 16:24:23 +0000106
Alan Stokesc4354b82023-05-04 16:06:52 +0100107 let dice_inputs = InputValues::new(
Pierre-Clément Tosif58f3a32023-02-02 16:24:23 +0000108 self.code_hash,
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +0000109 Config::Descriptor(&config),
Pierre-Clément Tosif58f3a32023-02-02 16:24:23 +0000110 self.auth_hash,
111 self.mode,
Shikha Panwarffadd4d2024-05-28 13:47:56 +0000112 self.make_hidden(salt, deferred_rollback_protection)?,
Alan Stokesc4354b82023-05-04 16:06:52 +0100113 );
114 let _ = bcc_handover_main_flow(current_bcc_handover, &dice_inputs, next_bcc)?;
115 Ok(())
Pierre-Clément Tosif58f3a32023-02-02 16:24:23 +0000116 }
Alan Stokesddb988c2023-11-27 11:13:06 +0000117
Shikha Panwarffadd4d2024-05-28 13:47:56 +0000118 fn make_hidden(
119 &self,
120 salt: &[u8; HIDDEN_SIZE],
121 deferred_rollback_protection: bool,
122 ) -> diced_open_dice::Result<[u8; HIDDEN_SIZE]> {
Alan Stokesa17cfba2024-02-14 17:34:51 +0000123 // 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)
Shikha Panwarffadd4d2024-05-28 13:47:56 +0000126 // - 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.
Alan Stokesa17cfba2024-02-14 17:34:51 +0000129 // 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],
Shikha Panwarffadd4d2024-05-28 13:47:56 +0000138 deferred_rollback_protection: bool,
Alan Stokesa17cfba2024-02-14 17:34:51 +0000139 }
Shikha Panwarffadd4d2024-05-28 13:47:56 +0000140 hash(
141 HiddenInput {
142 rkp_vm_marker: self.rkp_vm_marker,
143 salt: *salt,
144 deferred_rollback_protection,
145 }
146 .as_bytes(),
147 )
Alan Stokesa17cfba2024-02-14 17:34:51 +0000148 }
149
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +0000150 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 })?)
Alan Stokesddb988c2023-11-27 11:13:06 +0000166 }
Pierre-Clément Tosi4f4f5eb2022-12-08 14:31:42 +0000167}
Pierre-Clément Tosibec84662023-01-04 14:25:33 +0000168
169/// Flushes data caches over the provided address range.
170///
171/// # Safety
172///
Alan Stokesddb988c2023-11-27 11:13:06 +0000173/// 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).
Pierre-Clément Tosibec84662023-01-04 14:25:33 +0000176#[no_mangle]
Alan Stokesddb988c2023-11-27 11:13:06 +0000177#[cfg(not(test))]
178unsafe 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.
Pierre-Clément Tosibec84662023-01-04 14:25:33 +0000188 let region = unsafe { slice::from_raw_parts_mut(addr as *mut u8, size) };
189 flushed_zeroize(region)
190}
Alan Stokesddb988c2023-11-27 11:13:06 +0000191
192#[cfg(test)]
193mod tests {
Shikha Panwarf3acfd12024-05-28 15:48:13 +0000194 use crate::{
195 Hash, PartialInputs, COMPONENT_NAME_KEY, INSTANCE_HASH_KEY, RKP_VM_MARKER_KEY,
196 SECURITY_VERSION_KEY,
197 };
Alan Stokesddb988c2023-11-27 11:13:06 +0000198 use ciborium::Value;
Shikha Panwarf3acfd12024-05-28 15:48:13 +0000199 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;
Alan Stokesddb988c2023-11-27 11:13:06 +0000206 use std::collections::HashMap;
Shikha Panwarf3acfd12024-05-28 15:48:13 +0000207 use std::mem::size_of;
Alan Stokesddb988c2023-11-27 11:13:06 +0000208 use std::vec;
209
Alan Stokesddb988c2023-11-27 11:13:06 +0000210 const COMPONENT_VERSION_KEY: i64 = -70003;
211 const RESETTABLE_KEY: i64 = -70004;
Alan Stokesddb988c2023-11-27 11:13:06 +0000212 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 };
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +0000220 const HASH: Hash = *b"sixtyfourbyteslongsentencearerarebutletsgiveitatrycantbethathard";
Alan Stokesddb988c2023-11-27 11:13:06 +0000221
222 #[test]
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]
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]
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]
252 fn base_config_descriptor() {
253 let vb_data = BASE_VB_DATA;
254 let inputs = PartialInputs::new(&vb_data).unwrap();
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +0000255 let config_map = decode_config_descriptor(&inputs, None);
Alan Stokesddb988c2023-11-27 11:13:06 +0000256
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]
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();
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +0000276 let config_map = decode_config_descriptor(&inputs, Some(HASH));
Alan Stokesddb988c2023-11-27 11:13:06 +0000277
278 assert!(config_map.get(&RKP_VM_MARKER_KEY).unwrap().is_null());
279 }
280
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +0000281 #[test]
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]
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);
Chris Wailese9579642024-05-29 18:25:19 -0700296 assert!(!config_map.contains_key(&INSTANCE_HASH_KEY));
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +0000297 }
298
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();
Alan Stokesddb988c2023-11-27 11:13:06 +0000304
305 let cbor_map =
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +0000306 cbor_util::deserialize::<Value>(&config_descriptor).unwrap().into_map().unwrap();
Alan Stokesddb988c2023-11-27 11:13:06 +0000307
308 cbor_map
309 .into_iter()
310 .map(|(k, v)| ((k.into_integer().unwrap().try_into().unwrap()), v))
311 .collect()
312 }
Shikha Panwarf3acfd12024-05-28 15:48:13 +0000313
314 #[test]
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 }
Alan Stokesddb988c2023-11-27 11:13:06 +0000376}