blob: 245bf118875307bdd10443e1c9cfa5c9631965fd [file] [log] [blame]
Alan Stokes1125e012023-10-13 12:31:10 +01001// 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//! Logic for handling the DICE values and boot operations.
16
17use anyhow::{anyhow, bail, Context, Error, Result};
18use byteorder::{NativeEndian, ReadBytesExt};
19use diced_open_dice::{
20 bcc_handover_parse, retry_bcc_main_flow, BccHandover, Config, DiceArtifacts, DiceMode, Hash,
21 Hidden, InputValues, OwnedDiceArtifacts,
22};
23use keystore2_crypto::ZVec;
24use libc::{c_void, mmap, munmap, MAP_FAILED, MAP_PRIVATE, PROT_READ};
25use openssl::hkdf::hkdf;
26use openssl::md::Md;
27use std::fs;
28use std::os::unix::io::AsRawFd;
29use std::path::{Path, PathBuf};
30use std::ptr::null_mut;
31use std::slice;
32
33/// Artifacts that are mapped into the process address space from the driver.
34pub enum DiceDriver<'a> {
Nikita Ioffeab035a52024-03-14 00:33:50 +000035 /// Implementation that reads bcc handover from the dice driver.
Alan Stokes1125e012023-10-13 12:31:10 +010036 Real {
Nikita Ioffee18cc132024-02-28 16:13:36 +000037 /// Path to the driver character device (e.g. /dev/open-dice0).
Alan Stokes1125e012023-10-13 12:31:10 +010038 driver_path: PathBuf,
Nikita Ioffee18cc132024-02-28 16:13:36 +000039 /// Address of the memory to mmap driver to.
Alan Stokes1125e012023-10-13 12:31:10 +010040 mmap_addr: *mut c_void,
Nikita Ioffee18cc132024-02-28 16:13:36 +000041 /// Size of the mmap.
Alan Stokes1125e012023-10-13 12:31:10 +010042 mmap_size: usize,
Nikita Ioffee18cc132024-02-28 16:13:36 +000043 /// BCC handover.
Alan Stokes1125e012023-10-13 12:31:10 +010044 bcc_handover: BccHandover<'a>,
45 },
Nikita Ioffee18cc132024-02-28 16:13:36 +000046 /// Fake implementation used in tests and non-protected VMs.
Alan Stokes1125e012023-10-13 12:31:10 +010047 Fake(OwnedDiceArtifacts),
Nikita Ioffeab035a52024-03-14 00:33:50 +000048 /// Implementation that reads bcc handover from the file.
49 FromFile {
50 /// Path to the file to read dice chain from,
51 file_path: PathBuf,
52 /// Dice artifacts read from file_path,
53 dice_artifacts: OwnedDiceArtifacts,
54 },
Alan Stokes1125e012023-10-13 12:31:10 +010055}
56
57impl DiceDriver<'_> {
58 fn dice_artifacts(&self) -> &dyn DiceArtifacts {
59 match self {
60 Self::Real { bcc_handover, .. } => bcc_handover,
61 Self::Fake(owned_dice_artifacts) => owned_dice_artifacts,
Nikita Ioffeab035a52024-03-14 00:33:50 +000062 Self::FromFile { dice_artifacts, .. } => dice_artifacts,
Alan Stokes1125e012023-10-13 12:31:10 +010063 }
64 }
65
Nikita Ioffee18cc132024-02-28 16:13:36 +000066 /// Creates a new dice driver from the given driver_path.
67 pub fn new(driver_path: &Path, is_strict_boot: bool) -> Result<Self> {
Nikita Ioffeabb6d8a2024-03-12 23:01:47 +000068 log::info!("Creating DiceDriver backed by {driver_path:?} driver");
Alan Stokes1125e012023-10-13 12:31:10 +010069 if driver_path.exists() {
70 log::info!("Using DICE values from driver");
Nikita Ioffee18cc132024-02-28 16:13:36 +000071 } else if is_strict_boot {
Alan Stokes1125e012023-10-13 12:31:10 +010072 bail!("Strict boot requires DICE value from driver but none were found");
73 } else {
74 log::warn!("Using sample DICE values");
75 let dice_artifacts = diced_sample_inputs::make_sample_bcc_and_cdis()
76 .expect("Failed to create sample dice artifacts.");
77 return Ok(Self::Fake(dice_artifacts));
78 };
79
80 let mut file = fs::File::open(driver_path)
81 .map_err(|error| Error::new(error).context("Opening driver"))?;
82 let mmap_size =
83 file.read_u64::<NativeEndian>()
84 .map_err(|error| Error::new(error).context("Reading driver"))? as usize;
85 // SAFETY: It's safe to map the driver as the service will only create a single
86 // mapping per process.
87 let mmap_addr = unsafe {
88 let fd = file.as_raw_fd();
89 mmap(null_mut(), mmap_size, PROT_READ, MAP_PRIVATE, fd, 0)
90 };
91 if mmap_addr == MAP_FAILED {
92 bail!("Failed to mmap {:?}", driver_path);
93 }
94 let mmap_buf =
95 // SAFETY: The slice is created for the region of memory that was just
96 // successfully mapped into the process address space so it will be
97 // accessible and not referenced from anywhere else.
98 unsafe { slice::from_raw_parts((mmap_addr as *const u8).as_ref().unwrap(), mmap_size) };
99 let bcc_handover =
100 bcc_handover_parse(mmap_buf).map_err(|_| anyhow!("Failed to parse Bcc Handover"))?;
101 Ok(Self::Real {
102 driver_path: driver_path.to_path_buf(),
103 mmap_addr,
104 mmap_size,
105 bcc_handover,
106 })
107 }
108
Nikita Ioffeab035a52024-03-14 00:33:50 +0000109 /// Create a new dice driver that reads dice_artifacts from the given file.
110 pub fn from_file(file_path: &Path) -> Result<Self> {
Nikita Ioffeabb6d8a2024-03-12 23:01:47 +0000111 log::info!("Creating DiceDriver backed by {file_path:?} file");
Nikita Ioffeab035a52024-03-14 00:33:50 +0000112 let file =
113 fs::File::open(file_path).map_err(|error| Error::new(error).context("open file"))?;
114 let dice_artifacts = serde_cbor::from_reader(file)
115 .map_err(|error| Error::new(error).context("read file"))?;
116 Ok(Self::FromFile { file_path: file_path.to_path_buf(), dice_artifacts })
117 }
118
Alan Stokes1125e012023-10-13 12:31:10 +0100119 /// Derives a sealing key of `key_length` bytes from the DICE sealing CDI.
120 pub fn get_sealing_key(&self, identifier: &[u8], key_length: usize) -> Result<ZVec> {
121 // Deterministically derive a key to use for sealing data, rather than using the CDI
122 // directly, so we have the chance to rotate the key if needed. A salt isn't needed as the
123 // input key material is already cryptographically strong.
124 let mut key = ZVec::new(key_length)?;
125 let salt = &[];
126 hkdf(&mut key, Md::sha256(), self.dice_artifacts().cdi_seal(), salt, identifier)?;
127 Ok(key)
128 }
129
Nikita Ioffee18cc132024-02-28 16:13:36 +0000130 /// Derives a new dice chain.
Alan Stokes1125e012023-10-13 12:31:10 +0100131 pub fn derive(
132 self,
133 code_hash: Hash,
134 config_desc: &[u8],
135 authority_hash: Hash,
136 debug: bool,
137 hidden: Hidden,
138 ) -> Result<OwnedDiceArtifacts> {
139 let input_values = InputValues::new(
140 code_hash,
141 Config::Descriptor(config_desc),
142 authority_hash,
143 if debug { DiceMode::kDiceModeDebug } else { DiceMode::kDiceModeNormal },
144 hidden,
145 );
146 let current_dice_artifacts = self.dice_artifacts();
147 let next_dice_artifacts = retry_bcc_main_flow(
148 current_dice_artifacts.cdi_attest(),
149 current_dice_artifacts.cdi_seal(),
150 current_dice_artifacts.bcc().ok_or_else(|| anyhow!("bcc is none"))?,
151 &input_values,
152 )
153 .context("DICE derive from driver")?;
Nikita Ioffeabb6d8a2024-03-12 23:01:47 +0000154 match &self {
155 Self::Real { driver_path, .. } => {
156 // Writing to the device wipes the artifacts. The string is ignored by the driver
157 // but included for documentation.
158 fs::write(driver_path, "wipe")
159 .map_err(|err| Error::new(err).context("Wiping driver"))?;
160 }
161 Self::FromFile { file_path, .. } => {
162 fs::remove_file(file_path)
163 .map_err(|err| Error::new(err).context("Deleting file"))?;
164 }
165 Self::Fake { .. } => (),
Alan Stokes1125e012023-10-13 12:31:10 +0100166 }
167 Ok(next_dice_artifacts)
168 }
169}
170
171impl Drop for DiceDriver<'_> {
172 fn drop(&mut self) {
173 if let &mut Self::Real { mmap_addr, mmap_size, .. } = self {
174 // SAFETY: All references to the mapped region have the same lifetime as self. Since
175 // self is being dropped, so are all the references to the mapped region meaning it's
176 // safe to unmap.
177 let ret = unsafe { munmap(mmap_addr, mmap_size) };
178 if ret != 0 {
179 log::warn!("Failed to munmap ({})", ret);
180 }
181 }
182 }
183}
Nikita Ioffeab035a52024-03-14 00:33:50 +0000184
185#[cfg(test)]
186mod tests {
187 use super::*;
Nikita Ioffeabb6d8a2024-03-12 23:01:47 +0000188 use diced_open_dice::{
189 hash, retry_bcc_format_config_descriptor, DiceConfigValues, HIDDEN_SIZE,
190 };
191 use std::fs::File;
Nikita Ioffeab035a52024-03-14 00:33:50 +0000192
193 fn assert_eq_bytes(expected: &[u8], actual: &[u8]) {
194 assert_eq!(
195 expected,
196 actual,
197 "Expected {}, got {}",
198 hex::encode(expected),
199 hex::encode(actual)
200 )
201 }
202
203 #[test]
204 fn test_write_bcc_to_file_read_from_file() -> Result<()> {
205 let dice_artifacts = diced_sample_inputs::make_sample_bcc_and_cdis()?;
206
207 let test_file = tempfile::NamedTempFile::new()?;
208 serde_cbor::to_writer(test_file.as_file(), &dice_artifacts)?;
209 test_file.as_file().sync_all()?;
210
211 let dice = DiceDriver::from_file(test_file.as_ref())?;
212
213 let dice_artifacts2 = dice.dice_artifacts();
214 assert_eq_bytes(dice_artifacts.cdi_attest(), dice_artifacts2.cdi_attest());
215 assert_eq_bytes(dice_artifacts.cdi_seal(), dice_artifacts2.cdi_seal());
216 assert_eq_bytes(dice_artifacts.bcc().expect("bcc"), dice_artifacts2.bcc().expect("bcc"));
217
218 Ok(())
219 }
Nikita Ioffeabb6d8a2024-03-12 23:01:47 +0000220
221 #[test]
222 fn test_dice_driver_from_file_deletes_file_after_derive() -> Result<()> {
223 let tmp_dir = tempfile::tempdir()?;
224
225 let file_path = tmp_dir.path().join("test-dice-chain.raw");
226
227 {
228 let dice_artifacts = diced_sample_inputs::make_sample_bcc_and_cdis()?;
229 let file = File::create(&file_path)?;
230 serde_cbor::to_writer(file, &dice_artifacts)?;
231 }
232
233 let dice = DiceDriver::from_file(&file_path)?;
234
Chris Wailes73a1eb72025-01-15 11:56:43 -0800235 let values = DiceConfigValues { component_name: Some(c"test"), ..Default::default() };
Nikita Ioffeabb6d8a2024-03-12 23:01:47 +0000236 let desc = retry_bcc_format_config_descriptor(&values)?;
237 let code_hash = hash(&String::from("test code hash").into_bytes())?;
238 let authority_hash = hash(&String::from("test authority hash").into_bytes())?;
239 let hidden = [0; HIDDEN_SIZE];
240
241 let _ = dice.derive(code_hash, &desc, authority_hash, false, hidden)?;
242
243 assert!(!file_path.exists());
244
245 Ok(())
246 }
Nikita Ioffeab035a52024-03-14 00:33:50 +0000247}