blob: 67865e576359aaaf1e556abc08627be24ddf5c75 [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.
16
Alice Wang1f0add02023-01-23 16:22:53 +000017use core::mem::size_of;
Pierre-Clément Tosi1bf532b2023-11-13 11:06:20 +000018use cstr::cstr;
Alice Wang843d8312023-02-15 09:47:06 +000019use diced_open_dice::{
Alan Stokesc6e92462023-08-22 14:43:17 +010020 bcc_format_config_descriptor, bcc_handover_main_flow, hash, Config, DiceConfigValues, DiceMode,
21 Hash, InputValues, HIDDEN_SIZE,
Alice Wang843d8312023-02-15 09:47:06 +000022};
Alan Stokesa639b582023-11-22 13:14:27 +000023use pvmfw_avb::{Capability, DebugLevel, Digest, VerifiedBootData};
Alan Stokesa17cfba2024-02-14 17:34:51 +000024use zerocopy::AsBytes;
Alice Wang1f0add02023-01-23 16:22:53 +000025
Alice Wang31226132023-01-31 12:44:39 +000026fn to_dice_mode(debug_level: DebugLevel) -> DiceMode {
Alice Wang1f0add02023-01-23 16:22:53 +000027 match debug_level {
Alice Wang31226132023-01-31 12:44:39 +000028 DebugLevel::None => DiceMode::kDiceModeNormal,
29 DebugLevel::Full => DiceMode::kDiceModeDebug,
Alice Wang1f0add02023-01-23 16:22:53 +000030 }
31}
32
Alice Wang843d8312023-02-15 09:47:06 +000033fn to_dice_hash(verified_boot_data: &VerifiedBootData) -> diced_open_dice::Result<Hash> {
Alice Wang1f0add02023-01-23 16:22:53 +000034 let mut digests = [0u8; size_of::<Digest>() * 2];
35 digests[..size_of::<Digest>()].copy_from_slice(&verified_boot_data.kernel_digest);
36 if let Some(initrd_digest) = verified_boot_data.initrd_digest {
37 digests[size_of::<Digest>()..].copy_from_slice(&initrd_digest);
38 }
39 hash(&digests)
40}
Pierre-Clément Tosi4f4f5eb2022-12-08 14:31:42 +000041
Pierre-Clément Tosif58f3a32023-02-02 16:24:23 +000042pub struct PartialInputs {
Pierre-Clément Tosi1cc5eb72023-02-02 11:09:18 +000043 pub code_hash: Hash,
44 pub auth_hash: Hash,
45 pub mode: DiceMode,
Shikha Panwara26f16a2023-09-27 09:39:00 +000046 pub security_version: u64,
Alan Stokesa639b582023-11-22 13:14:27 +000047 pub rkp_vm_marker: bool,
Pierre-Clément Tosif58f3a32023-02-02 16:24:23 +000048}
Pierre-Clément Tosi4f4f5eb2022-12-08 14:31:42 +000049
Pierre-Clément Tosif58f3a32023-02-02 16:24:23 +000050impl PartialInputs {
Alice Wang843d8312023-02-15 09:47:06 +000051 pub fn new(data: &VerifiedBootData) -> diced_open_dice::Result<Self> {
Pierre-Clément Tosif58f3a32023-02-02 16:24:23 +000052 let code_hash = to_dice_hash(data)?;
53 let auth_hash = hash(data.public_key)?;
54 let mode = to_dice_mode(data.debug_level);
Shikha Panwara26f16a2023-09-27 09:39:00 +000055 // We use rollback_index from vbmeta as the security_version field in dice certificate.
56 let security_version = data.rollback_index;
Alan Stokesa639b582023-11-22 13:14:27 +000057 let rkp_vm_marker = data.has_capability(Capability::RemoteAttest);
Pierre-Clément Tosi4f4f5eb2022-12-08 14:31:42 +000058
Alan Stokesa639b582023-11-22 13:14:27 +000059 Ok(Self { code_hash, auth_hash, mode, security_version, rkp_vm_marker })
Pierre-Clément Tosif58f3a32023-02-02 16:24:23 +000060 }
61
Alan Stokesc4354b82023-05-04 16:06:52 +010062 pub fn write_next_bcc(
Alice Wang843d8312023-02-15 09:47:06 +000063 self,
Alan Stokesc4354b82023-05-04 16:06:52 +010064 current_bcc_handover: &[u8],
Alice Wang843d8312023-02-15 09:47:06 +000065 salt: &[u8; HIDDEN_SIZE],
Alan Stokesc4354b82023-05-04 16:06:52 +010066 next_bcc: &mut [u8],
67 ) -> diced_open_dice::Result<()> {
68 let mut config_descriptor_buffer = [0; 128];
Alan Stokesddb988c2023-11-27 11:13:06 +000069 let config = self.generate_config_descriptor(&mut config_descriptor_buffer)?;
Pierre-Clément Tosif58f3a32023-02-02 16:24:23 +000070
Alan Stokesc4354b82023-05-04 16:06:52 +010071 let dice_inputs = InputValues::new(
Pierre-Clément Tosif58f3a32023-02-02 16:24:23 +000072 self.code_hash,
73 Config::Descriptor(config),
74 self.auth_hash,
75 self.mode,
Alan Stokesa17cfba2024-02-14 17:34:51 +000076 self.make_hidden(salt)?,
Alan Stokesc4354b82023-05-04 16:06:52 +010077 );
78 let _ = bcc_handover_main_flow(current_bcc_handover, &dice_inputs, next_bcc)?;
79 Ok(())
Pierre-Clément Tosif58f3a32023-02-02 16:24:23 +000080 }
Alan Stokesddb988c2023-11-27 11:13:06 +000081
Alan Stokesa17cfba2024-02-14 17:34:51 +000082 fn make_hidden(&self, salt: &[u8; HIDDEN_SIZE]) -> diced_open_dice::Result<[u8; HIDDEN_SIZE]> {
83 // We want to make sure we get a different sealing CDI for:
84 // - VMs with different salt values
85 // - An RKP VM and any other VM (regardless of salt)
86 // The hidden input for DICE affects the sealing CDI (but the values in the config
87 // descriptor do not).
88 // Since the hidden input has to be a fixed size, create it as a hash of the values we
89 // want included.
90 #[derive(AsBytes)]
91 #[repr(C, packed)]
92 struct HiddenInput {
93 rkp_vm_marker: bool,
94 salt: [u8; HIDDEN_SIZE],
95 }
Shikha Panwar73ba0d42024-03-20 14:43:21 +000096 // TODO(b/291213394): Include `defer_rollback_protection` flag in the Hidden Input to
97 // differentiate the secrets in both cases.
Alan Stokesa17cfba2024-02-14 17:34:51 +000098 hash(HiddenInput { rkp_vm_marker: self.rkp_vm_marker, salt: *salt }.as_bytes())
99 }
100
Alan Stokesddb988c2023-11-27 11:13:06 +0000101 fn generate_config_descriptor<'a>(
102 &self,
103 config_descriptor_buffer: &'a mut [u8],
104 ) -> diced_open_dice::Result<&'a [u8]> {
105 let config_values = DiceConfigValues {
106 component_name: Some(cstr!("vm_entry")),
107 security_version: if cfg!(dice_changes) { Some(self.security_version) } else { None },
108 rkp_vm_marker: self.rkp_vm_marker,
109 ..Default::default()
110 };
111 let config_descriptor_size =
112 bcc_format_config_descriptor(&config_values, config_descriptor_buffer)?;
113 let config = &config_descriptor_buffer[..config_descriptor_size];
114 Ok(config)
115 }
Pierre-Clément Tosi4f4f5eb2022-12-08 14:31:42 +0000116}
Pierre-Clément Tosibec84662023-01-04 14:25:33 +0000117
118/// Flushes data caches over the provided address range.
119///
120/// # Safety
121///
Alan Stokesddb988c2023-11-27 11:13:06 +0000122/// The provided address and size must be to an address range that is valid for read and write
123/// (typically on the stack, .bss, .data, or provided BCC) from a single allocation
124/// (e.g. stack array).
Pierre-Clément Tosibec84662023-01-04 14:25:33 +0000125#[no_mangle]
Alan Stokesddb988c2023-11-27 11:13:06 +0000126#[cfg(not(test))]
127unsafe extern "C" fn DiceClearMemory(
128 _ctx: *mut core::ffi::c_void,
129 size: usize,
130 addr: *mut core::ffi::c_void,
131) {
132 use core::slice;
133 use vmbase::memory::flushed_zeroize;
134
135 // SAFETY: We require our caller to provide a valid range within a single object. The open-dice
136 // always calls this on individual stack-allocated arrays which ensures that.
Pierre-Clément Tosibec84662023-01-04 14:25:33 +0000137 let region = unsafe { slice::from_raw_parts_mut(addr as *mut u8, size) };
138 flushed_zeroize(region)
139}
Alan Stokesddb988c2023-11-27 11:13:06 +0000140
141#[cfg(test)]
142mod tests {
143 use super::*;
144 use ciborium::Value;
145 use std::collections::HashMap;
146 use std::vec;
147
148 const COMPONENT_NAME_KEY: i64 = -70002;
149 const COMPONENT_VERSION_KEY: i64 = -70003;
150 const RESETTABLE_KEY: i64 = -70004;
151 const SECURITY_VERSION_KEY: i64 = -70005;
152 const RKP_VM_MARKER_KEY: i64 = -70006;
153
154 const BASE_VB_DATA: VerifiedBootData = VerifiedBootData {
155 debug_level: DebugLevel::None,
156 kernel_digest: [1u8; size_of::<Digest>()],
157 initrd_digest: Some([2u8; size_of::<Digest>()]),
158 public_key: b"public key",
159 capabilities: vec![],
160 rollback_index: 42,
161 };
162
163 #[test]
164 fn base_data_conversion() {
165 let vb_data = BASE_VB_DATA;
166 let inputs = PartialInputs::new(&vb_data).unwrap();
167
168 assert_eq!(inputs.mode, DiceMode::kDiceModeNormal);
169 assert_eq!(inputs.security_version, 42);
170 assert!(!inputs.rkp_vm_marker);
171
172 // TODO(b/313608219): Consider checks for code_hash and possibly auth_hash.
173 }
174
175 #[test]
176 fn debuggable_conversion() {
177 let vb_data = VerifiedBootData { debug_level: DebugLevel::Full, ..BASE_VB_DATA };
178 let inputs = PartialInputs::new(&vb_data).unwrap();
179
180 assert_eq!(inputs.mode, DiceMode::kDiceModeDebug);
181 }
182
183 #[test]
184 fn rkp_vm_conversion() {
185 let vb_data =
186 VerifiedBootData { capabilities: vec![Capability::RemoteAttest], ..BASE_VB_DATA };
187 let inputs = PartialInputs::new(&vb_data).unwrap();
188
189 assert!(inputs.rkp_vm_marker);
190 }
191
192 #[test]
193 fn base_config_descriptor() {
194 let vb_data = BASE_VB_DATA;
195 let inputs = PartialInputs::new(&vb_data).unwrap();
196 let config_map = decode_config_descriptor(&inputs);
197
198 assert_eq!(config_map.get(&COMPONENT_NAME_KEY).unwrap().as_text().unwrap(), "vm_entry");
199 assert_eq!(config_map.get(&COMPONENT_VERSION_KEY), None);
200 assert_eq!(config_map.get(&RESETTABLE_KEY), None);
201 if cfg!(dice_changes) {
202 assert_eq!(
203 config_map.get(&SECURITY_VERSION_KEY).unwrap().as_integer().unwrap(),
204 42.into()
205 );
206 } else {
207 assert_eq!(config_map.get(&SECURITY_VERSION_KEY), None);
208 }
209 assert_eq!(config_map.get(&RKP_VM_MARKER_KEY), None);
210 }
211
212 #[test]
213 fn config_descriptor_with_rkp_vm() {
214 let vb_data =
215 VerifiedBootData { capabilities: vec![Capability::RemoteAttest], ..BASE_VB_DATA };
216 let inputs = PartialInputs::new(&vb_data).unwrap();
217 let config_map = decode_config_descriptor(&inputs);
218
219 assert!(config_map.get(&RKP_VM_MARKER_KEY).unwrap().is_null());
220 }
221
222 fn decode_config_descriptor(inputs: &PartialInputs) -> HashMap<i64, Value> {
223 let mut buffer = [0; 128];
224 let config_descriptor = inputs.generate_config_descriptor(&mut buffer).unwrap();
225
226 let cbor_map =
227 cbor_util::deserialize::<Value>(config_descriptor).unwrap().into_map().unwrap();
228
229 cbor_map
230 .into_iter()
231 .map(|(k, v)| ((k.into_integer().unwrap().try_into().unwrap()), v))
232 .collect()
233 }
234}