blob: aaf26913203c8c2b0373468291c4a082014bf398 [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
Pierre-Clément Tosif58f3a32023-02-02 16:24:23 +000074pub struct PartialInputs {
Pierre-Clément Tosi1cc5eb72023-02-02 11:09:18 +000075 pub code_hash: Hash,
76 pub auth_hash: Hash,
77 pub mode: DiceMode,
Shikha Panwara26f16a2023-09-27 09:39:00 +000078 pub security_version: u64,
Alan Stokesa639b582023-11-22 13:14:27 +000079 pub rkp_vm_marker: bool,
Pierre-Clément Tosif58f3a32023-02-02 16:24:23 +000080}
Pierre-Clément Tosi4f4f5eb2022-12-08 14:31:42 +000081
Pierre-Clément Tosif58f3a32023-02-02 16:24:23 +000082impl PartialInputs {
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +000083 pub fn new(data: &VerifiedBootData) -> Result<Self> {
Pierre-Clément Tosif58f3a32023-02-02 16:24:23 +000084 let code_hash = to_dice_hash(data)?;
85 let auth_hash = hash(data.public_key)?;
86 let mode = to_dice_mode(data.debug_level);
Shikha Panwara26f16a2023-09-27 09:39:00 +000087 // We use rollback_index from vbmeta as the security_version field in dice certificate.
88 let security_version = data.rollback_index;
Alan Stokesa639b582023-11-22 13:14:27 +000089 let rkp_vm_marker = data.has_capability(Capability::RemoteAttest);
Pierre-Clément Tosi4f4f5eb2022-12-08 14:31:42 +000090
Alan Stokesa639b582023-11-22 13:14:27 +000091 Ok(Self { code_hash, auth_hash, mode, security_version, rkp_vm_marker })
Pierre-Clément Tosif58f3a32023-02-02 16:24:23 +000092 }
93
Alan Stokesc4354b82023-05-04 16:06:52 +010094 pub fn write_next_bcc(
Alice Wang843d8312023-02-15 09:47:06 +000095 self,
Alan Stokesc4354b82023-05-04 16:06:52 +010096 current_bcc_handover: &[u8],
Alice Wang843d8312023-02-15 09:47:06 +000097 salt: &[u8; HIDDEN_SIZE],
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +000098 instance_hash: Option<Hash>,
Alan Stokesc4354b82023-05-04 16:06:52 +010099 next_bcc: &mut [u8],
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +0000100 ) -> Result<()> {
101 let config = self
102 .generate_config_descriptor(instance_hash)
103 .map_err(|_| diced_open_dice::DiceError::InvalidInput)?;
Pierre-Clément Tosif58f3a32023-02-02 16:24:23 +0000104
Alan Stokesc4354b82023-05-04 16:06:52 +0100105 let dice_inputs = InputValues::new(
Pierre-Clément Tosif58f3a32023-02-02 16:24:23 +0000106 self.code_hash,
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +0000107 Config::Descriptor(&config),
Pierre-Clément Tosif58f3a32023-02-02 16:24:23 +0000108 self.auth_hash,
109 self.mode,
Alan Stokesa17cfba2024-02-14 17:34:51 +0000110 self.make_hidden(salt)?,
Alan Stokesc4354b82023-05-04 16:06:52 +0100111 );
112 let _ = bcc_handover_main_flow(current_bcc_handover, &dice_inputs, next_bcc)?;
113 Ok(())
Pierre-Clément Tosif58f3a32023-02-02 16:24:23 +0000114 }
Alan Stokesddb988c2023-11-27 11:13:06 +0000115
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +0000116 fn make_hidden(&self, salt: &[u8; HIDDEN_SIZE]) -> Result<[u8; HIDDEN_SIZE]> {
Alan Stokesa17cfba2024-02-14 17:34:51 +0000117 // We want to make sure we get a different sealing CDI for:
118 // - VMs with different salt values
119 // - An RKP VM and any other VM (regardless of salt)
120 // The hidden input for DICE affects the sealing CDI (but the values in the config
121 // descriptor do not).
122 // Since the hidden input has to be a fixed size, create it as a hash of the values we
123 // want included.
124 #[derive(AsBytes)]
125 #[repr(C, packed)]
126 struct HiddenInput {
127 rkp_vm_marker: bool,
128 salt: [u8; HIDDEN_SIZE],
129 }
Shikha Panwar73ba0d42024-03-20 14:43:21 +0000130 // TODO(b/291213394): Include `defer_rollback_protection` flag in the Hidden Input to
131 // differentiate the secrets in both cases.
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +0000132 Ok(hash(HiddenInput { rkp_vm_marker: self.rkp_vm_marker, salt: *salt }.as_bytes())?)
Alan Stokesa17cfba2024-02-14 17:34:51 +0000133 }
134
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +0000135 fn generate_config_descriptor(&self, instance_hash: Option<Hash>) -> Result<Vec<u8>> {
136 let mut config = Vec::with_capacity(4);
137 config.push((cbor!(COMPONENT_NAME_KEY)?, cbor!("vm_entry")?));
138 if cfg!(dice_changes) {
139 config.push((cbor!(SECURITY_VERSION_KEY)?, cbor!(self.security_version)?));
140 }
141 if self.rkp_vm_marker {
142 config.push((cbor!(RKP_VM_MARKER_KEY)?, Value::Null))
143 }
144 if let Some(instance_hash) = instance_hash {
145 config.push((cbor!(INSTANCE_HASH_KEY)?, Value::from(instance_hash.as_slice())));
146 }
147 let config = Value::Map(config);
148 Ok(cbor_util::serialize(&config).map_err(|e| {
149 ciborium::value::Error::Custom(format!("Error in serialization: {e:?}"))
150 })?)
Alan Stokesddb988c2023-11-27 11:13:06 +0000151 }
Pierre-Clément Tosi4f4f5eb2022-12-08 14:31:42 +0000152}
Pierre-Clément Tosibec84662023-01-04 14:25:33 +0000153
154/// Flushes data caches over the provided address range.
155///
156/// # Safety
157///
Alan Stokesddb988c2023-11-27 11:13:06 +0000158/// The provided address and size must be to an address range that is valid for read and write
159/// (typically on the stack, .bss, .data, or provided BCC) from a single allocation
160/// (e.g. stack array).
Pierre-Clément Tosibec84662023-01-04 14:25:33 +0000161#[no_mangle]
Alan Stokesddb988c2023-11-27 11:13:06 +0000162#[cfg(not(test))]
163unsafe extern "C" fn DiceClearMemory(
164 _ctx: *mut core::ffi::c_void,
165 size: usize,
166 addr: *mut core::ffi::c_void,
167) {
168 use core::slice;
169 use vmbase::memory::flushed_zeroize;
170
171 // SAFETY: We require our caller to provide a valid range within a single object. The open-dice
172 // always calls this on individual stack-allocated arrays which ensures that.
Pierre-Clément Tosibec84662023-01-04 14:25:33 +0000173 let region = unsafe { slice::from_raw_parts_mut(addr as *mut u8, size) };
174 flushed_zeroize(region)
175}
Alan Stokesddb988c2023-11-27 11:13:06 +0000176
177#[cfg(test)]
178mod tests {
179 use super::*;
180 use ciborium::Value;
181 use std::collections::HashMap;
182 use std::vec;
183
Alan Stokesddb988c2023-11-27 11:13:06 +0000184 const COMPONENT_VERSION_KEY: i64 = -70003;
185 const RESETTABLE_KEY: i64 = -70004;
Alan Stokesddb988c2023-11-27 11:13:06 +0000186 const BASE_VB_DATA: VerifiedBootData = VerifiedBootData {
187 debug_level: DebugLevel::None,
188 kernel_digest: [1u8; size_of::<Digest>()],
189 initrd_digest: Some([2u8; size_of::<Digest>()]),
190 public_key: b"public key",
191 capabilities: vec![],
192 rollback_index: 42,
193 };
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +0000194 const HASH: Hash = *b"sixtyfourbyteslongsentencearerarebutletsgiveitatrycantbethathard";
Alan Stokesddb988c2023-11-27 11:13:06 +0000195
196 #[test]
197 fn base_data_conversion() {
198 let vb_data = BASE_VB_DATA;
199 let inputs = PartialInputs::new(&vb_data).unwrap();
200
201 assert_eq!(inputs.mode, DiceMode::kDiceModeNormal);
202 assert_eq!(inputs.security_version, 42);
203 assert!(!inputs.rkp_vm_marker);
204
205 // TODO(b/313608219): Consider checks for code_hash and possibly auth_hash.
206 }
207
208 #[test]
209 fn debuggable_conversion() {
210 let vb_data = VerifiedBootData { debug_level: DebugLevel::Full, ..BASE_VB_DATA };
211 let inputs = PartialInputs::new(&vb_data).unwrap();
212
213 assert_eq!(inputs.mode, DiceMode::kDiceModeDebug);
214 }
215
216 #[test]
217 fn rkp_vm_conversion() {
218 let vb_data =
219 VerifiedBootData { capabilities: vec![Capability::RemoteAttest], ..BASE_VB_DATA };
220 let inputs = PartialInputs::new(&vb_data).unwrap();
221
222 assert!(inputs.rkp_vm_marker);
223 }
224
225 #[test]
226 fn base_config_descriptor() {
227 let vb_data = BASE_VB_DATA;
228 let inputs = PartialInputs::new(&vb_data).unwrap();
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +0000229 let config_map = decode_config_descriptor(&inputs, None);
Alan Stokesddb988c2023-11-27 11:13:06 +0000230
231 assert_eq!(config_map.get(&COMPONENT_NAME_KEY).unwrap().as_text().unwrap(), "vm_entry");
232 assert_eq!(config_map.get(&COMPONENT_VERSION_KEY), None);
233 assert_eq!(config_map.get(&RESETTABLE_KEY), None);
234 if cfg!(dice_changes) {
235 assert_eq!(
236 config_map.get(&SECURITY_VERSION_KEY).unwrap().as_integer().unwrap(),
237 42.into()
238 );
239 } else {
240 assert_eq!(config_map.get(&SECURITY_VERSION_KEY), None);
241 }
242 assert_eq!(config_map.get(&RKP_VM_MARKER_KEY), None);
243 }
244
245 #[test]
246 fn config_descriptor_with_rkp_vm() {
247 let vb_data =
248 VerifiedBootData { capabilities: vec![Capability::RemoteAttest], ..BASE_VB_DATA };
249 let inputs = PartialInputs::new(&vb_data).unwrap();
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +0000250 let config_map = decode_config_descriptor(&inputs, Some(HASH));
Alan Stokesddb988c2023-11-27 11:13:06 +0000251
252 assert!(config_map.get(&RKP_VM_MARKER_KEY).unwrap().is_null());
253 }
254
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +0000255 #[test]
256 fn config_descriptor_with_instance_hash() {
257 let vb_data =
258 VerifiedBootData { capabilities: vec![Capability::RemoteAttest], ..BASE_VB_DATA };
259 let inputs = PartialInputs::new(&vb_data).unwrap();
260 let config_map = decode_config_descriptor(&inputs, Some(HASH));
261 assert_eq!(*config_map.get(&INSTANCE_HASH_KEY).unwrap(), Value::from(HASH.as_slice()));
262 }
263
264 #[test]
265 fn config_descriptor_without_instance_hash() {
266 let vb_data =
267 VerifiedBootData { capabilities: vec![Capability::RemoteAttest], ..BASE_VB_DATA };
268 let inputs = PartialInputs::new(&vb_data).unwrap();
269 let config_map = decode_config_descriptor(&inputs, None);
Chris Wailese9579642024-05-29 18:25:19 -0700270 assert!(!config_map.contains_key(&INSTANCE_HASH_KEY));
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +0000271 }
272
273 fn decode_config_descriptor(
274 inputs: &PartialInputs,
275 instance_hash: Option<Hash>,
276 ) -> HashMap<i64, Value> {
277 let config_descriptor = inputs.generate_config_descriptor(instance_hash).unwrap();
Alan Stokesddb988c2023-11-27 11:13:06 +0000278
279 let cbor_map =
Shikha Panwar8f7fc1a2024-04-10 10:41:34 +0000280 cbor_util::deserialize::<Value>(&config_descriptor).unwrap().into_map().unwrap();
Alan Stokesddb988c2023-11-27 11:13:06 +0000281
282 cbor_map
283 .into_iter()
284 .map(|(k, v)| ((k.into_integer().unwrap().try_into().unwrap()), v))
285 .collect()
286 }
287}