blob: 540fd03bde4e4b2ff7b7b419b9adb151873600f6 [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 }
96
97 hash(HiddenInput { rkp_vm_marker: self.rkp_vm_marker, salt: *salt }.as_bytes())
98 }
99
Alan Stokesddb988c2023-11-27 11:13:06 +0000100 fn generate_config_descriptor<'a>(
101 &self,
102 config_descriptor_buffer: &'a mut [u8],
103 ) -> diced_open_dice::Result<&'a [u8]> {
104 let config_values = DiceConfigValues {
105 component_name: Some(cstr!("vm_entry")),
106 security_version: if cfg!(dice_changes) { Some(self.security_version) } else { None },
107 rkp_vm_marker: self.rkp_vm_marker,
108 ..Default::default()
109 };
110 let config_descriptor_size =
111 bcc_format_config_descriptor(&config_values, config_descriptor_buffer)?;
112 let config = &config_descriptor_buffer[..config_descriptor_size];
113 Ok(config)
114 }
Pierre-Clément Tosi4f4f5eb2022-12-08 14:31:42 +0000115}
Pierre-Clément Tosibec84662023-01-04 14:25:33 +0000116
117/// Flushes data caches over the provided address range.
118///
119/// # Safety
120///
Alan Stokesddb988c2023-11-27 11:13:06 +0000121/// The provided address and size must be to an address range that is valid for read and write
122/// (typically on the stack, .bss, .data, or provided BCC) from a single allocation
123/// (e.g. stack array).
Pierre-Clément Tosibec84662023-01-04 14:25:33 +0000124#[no_mangle]
Alan Stokesddb988c2023-11-27 11:13:06 +0000125#[cfg(not(test))]
126unsafe extern "C" fn DiceClearMemory(
127 _ctx: *mut core::ffi::c_void,
128 size: usize,
129 addr: *mut core::ffi::c_void,
130) {
131 use core::slice;
132 use vmbase::memory::flushed_zeroize;
133
134 // SAFETY: We require our caller to provide a valid range within a single object. The open-dice
135 // always calls this on individual stack-allocated arrays which ensures that.
Pierre-Clément Tosibec84662023-01-04 14:25:33 +0000136 let region = unsafe { slice::from_raw_parts_mut(addr as *mut u8, size) };
137 flushed_zeroize(region)
138}
Alan Stokesddb988c2023-11-27 11:13:06 +0000139
140#[cfg(test)]
141mod tests {
142 use super::*;
143 use ciborium::Value;
144 use std::collections::HashMap;
145 use std::vec;
146
147 const COMPONENT_NAME_KEY: i64 = -70002;
148 const COMPONENT_VERSION_KEY: i64 = -70003;
149 const RESETTABLE_KEY: i64 = -70004;
150 const SECURITY_VERSION_KEY: i64 = -70005;
151 const RKP_VM_MARKER_KEY: i64 = -70006;
152
153 const BASE_VB_DATA: VerifiedBootData = VerifiedBootData {
154 debug_level: DebugLevel::None,
155 kernel_digest: [1u8; size_of::<Digest>()],
156 initrd_digest: Some([2u8; size_of::<Digest>()]),
157 public_key: b"public key",
158 capabilities: vec![],
159 rollback_index: 42,
160 };
161
162 #[test]
163 fn base_data_conversion() {
164 let vb_data = BASE_VB_DATA;
165 let inputs = PartialInputs::new(&vb_data).unwrap();
166
167 assert_eq!(inputs.mode, DiceMode::kDiceModeNormal);
168 assert_eq!(inputs.security_version, 42);
169 assert!(!inputs.rkp_vm_marker);
170
171 // TODO(b/313608219): Consider checks for code_hash and possibly auth_hash.
172 }
173
174 #[test]
175 fn debuggable_conversion() {
176 let vb_data = VerifiedBootData { debug_level: DebugLevel::Full, ..BASE_VB_DATA };
177 let inputs = PartialInputs::new(&vb_data).unwrap();
178
179 assert_eq!(inputs.mode, DiceMode::kDiceModeDebug);
180 }
181
182 #[test]
183 fn rkp_vm_conversion() {
184 let vb_data =
185 VerifiedBootData { capabilities: vec![Capability::RemoteAttest], ..BASE_VB_DATA };
186 let inputs = PartialInputs::new(&vb_data).unwrap();
187
188 assert!(inputs.rkp_vm_marker);
189 }
190
191 #[test]
192 fn base_config_descriptor() {
193 let vb_data = BASE_VB_DATA;
194 let inputs = PartialInputs::new(&vb_data).unwrap();
195 let config_map = decode_config_descriptor(&inputs);
196
197 assert_eq!(config_map.get(&COMPONENT_NAME_KEY).unwrap().as_text().unwrap(), "vm_entry");
198 assert_eq!(config_map.get(&COMPONENT_VERSION_KEY), None);
199 assert_eq!(config_map.get(&RESETTABLE_KEY), None);
200 if cfg!(dice_changes) {
201 assert_eq!(
202 config_map.get(&SECURITY_VERSION_KEY).unwrap().as_integer().unwrap(),
203 42.into()
204 );
205 } else {
206 assert_eq!(config_map.get(&SECURITY_VERSION_KEY), None);
207 }
208 assert_eq!(config_map.get(&RKP_VM_MARKER_KEY), None);
209 }
210
211 #[test]
212 fn config_descriptor_with_rkp_vm() {
213 let vb_data =
214 VerifiedBootData { capabilities: vec![Capability::RemoteAttest], ..BASE_VB_DATA };
215 let inputs = PartialInputs::new(&vb_data).unwrap();
216 let config_map = decode_config_descriptor(&inputs);
217
218 assert!(config_map.get(&RKP_VM_MARKER_KEY).unwrap().is_null());
219 }
220
221 fn decode_config_descriptor(inputs: &PartialInputs) -> HashMap<i64, Value> {
222 let mut buffer = [0; 128];
223 let config_descriptor = inputs.generate_config_descriptor(&mut buffer).unwrap();
224
225 let cbor_map =
226 cbor_util::deserialize::<Value>(config_descriptor).unwrap().into_map().unwrap();
227
228 cbor_map
229 .into_iter()
230 .map(|(k, v)| ((k.into_integer().unwrap().try_into().unwrap()), v))
231 .collect()
232 }
233}