blob: 79edb51a57ae757530a3798ffd9863d80ee0036e [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> {
Alan Stokes1125e012023-10-13 12:31:10 +010068 if driver_path.exists() {
69 log::info!("Using DICE values from driver");
Nikita Ioffee18cc132024-02-28 16:13:36 +000070 } else if is_strict_boot {
Alan Stokes1125e012023-10-13 12:31:10 +010071 bail!("Strict boot requires DICE value from driver but none were found");
72 } else {
73 log::warn!("Using sample DICE values");
74 let dice_artifacts = diced_sample_inputs::make_sample_bcc_and_cdis()
75 .expect("Failed to create sample dice artifacts.");
76 return Ok(Self::Fake(dice_artifacts));
77 };
78
79 let mut file = fs::File::open(driver_path)
80 .map_err(|error| Error::new(error).context("Opening driver"))?;
81 let mmap_size =
82 file.read_u64::<NativeEndian>()
83 .map_err(|error| Error::new(error).context("Reading driver"))? as usize;
84 // SAFETY: It's safe to map the driver as the service will only create a single
85 // mapping per process.
86 let mmap_addr = unsafe {
87 let fd = file.as_raw_fd();
88 mmap(null_mut(), mmap_size, PROT_READ, MAP_PRIVATE, fd, 0)
89 };
90 if mmap_addr == MAP_FAILED {
91 bail!("Failed to mmap {:?}", driver_path);
92 }
93 let mmap_buf =
94 // SAFETY: The slice is created for the region of memory that was just
95 // successfully mapped into the process address space so it will be
96 // accessible and not referenced from anywhere else.
97 unsafe { slice::from_raw_parts((mmap_addr as *const u8).as_ref().unwrap(), mmap_size) };
98 let bcc_handover =
99 bcc_handover_parse(mmap_buf).map_err(|_| anyhow!("Failed to parse Bcc Handover"))?;
100 Ok(Self::Real {
101 driver_path: driver_path.to_path_buf(),
102 mmap_addr,
103 mmap_size,
104 bcc_handover,
105 })
106 }
107
Nikita Ioffeab035a52024-03-14 00:33:50 +0000108 /// Create a new dice driver that reads dice_artifacts from the given file.
109 pub fn from_file(file_path: &Path) -> Result<Self> {
110 let file =
111 fs::File::open(file_path).map_err(|error| Error::new(error).context("open file"))?;
112 let dice_artifacts = serde_cbor::from_reader(file)
113 .map_err(|error| Error::new(error).context("read file"))?;
114 Ok(Self::FromFile { file_path: file_path.to_path_buf(), dice_artifacts })
115 }
116
Alan Stokes1125e012023-10-13 12:31:10 +0100117 /// Derives a sealing key of `key_length` bytes from the DICE sealing CDI.
118 pub fn get_sealing_key(&self, identifier: &[u8], key_length: usize) -> Result<ZVec> {
119 // Deterministically derive a key to use for sealing data, rather than using the CDI
120 // directly, so we have the chance to rotate the key if needed. A salt isn't needed as the
121 // input key material is already cryptographically strong.
122 let mut key = ZVec::new(key_length)?;
123 let salt = &[];
124 hkdf(&mut key, Md::sha256(), self.dice_artifacts().cdi_seal(), salt, identifier)?;
125 Ok(key)
126 }
127
Nikita Ioffee18cc132024-02-28 16:13:36 +0000128 /// Derives a new dice chain.
Alan Stokes1125e012023-10-13 12:31:10 +0100129 pub fn derive(
130 self,
131 code_hash: Hash,
132 config_desc: &[u8],
133 authority_hash: Hash,
134 debug: bool,
135 hidden: Hidden,
136 ) -> Result<OwnedDiceArtifacts> {
137 let input_values = InputValues::new(
138 code_hash,
139 Config::Descriptor(config_desc),
140 authority_hash,
141 if debug { DiceMode::kDiceModeDebug } else { DiceMode::kDiceModeNormal },
142 hidden,
143 );
144 let current_dice_artifacts = self.dice_artifacts();
145 let next_dice_artifacts = retry_bcc_main_flow(
146 current_dice_artifacts.cdi_attest(),
147 current_dice_artifacts.cdi_seal(),
148 current_dice_artifacts.bcc().ok_or_else(|| anyhow!("bcc is none"))?,
149 &input_values,
150 )
151 .context("DICE derive from driver")?;
152 if let Self::Real { driver_path, .. } = &self {
153 // Writing to the device wipes the artifacts. The string is ignored by the driver but
154 // included for documentation.
155 fs::write(driver_path, "wipe")
156 .map_err(|err| Error::new(err).context("Wiping driver"))?;
157 }
158 Ok(next_dice_artifacts)
159 }
160}
161
162impl Drop for DiceDriver<'_> {
163 fn drop(&mut self) {
164 if let &mut Self::Real { mmap_addr, mmap_size, .. } = self {
165 // SAFETY: All references to the mapped region have the same lifetime as self. Since
166 // self is being dropped, so are all the references to the mapped region meaning it's
167 // safe to unmap.
168 let ret = unsafe { munmap(mmap_addr, mmap_size) };
169 if ret != 0 {
170 log::warn!("Failed to munmap ({})", ret);
171 }
172 }
173 }
174}
Nikita Ioffeab035a52024-03-14 00:33:50 +0000175
176#[cfg(test)]
177mod tests {
178 use super::*;
179
180 fn assert_eq_bytes(expected: &[u8], actual: &[u8]) {
181 assert_eq!(
182 expected,
183 actual,
184 "Expected {}, got {}",
185 hex::encode(expected),
186 hex::encode(actual)
187 )
188 }
189
190 #[test]
191 fn test_write_bcc_to_file_read_from_file() -> Result<()> {
192 let dice_artifacts = diced_sample_inputs::make_sample_bcc_and_cdis()?;
193
194 let test_file = tempfile::NamedTempFile::new()?;
195 serde_cbor::to_writer(test_file.as_file(), &dice_artifacts)?;
196 test_file.as_file().sync_all()?;
197
198 let dice = DiceDriver::from_file(test_file.as_ref())?;
199
200 let dice_artifacts2 = dice.dice_artifacts();
201 assert_eq_bytes(dice_artifacts.cdi_attest(), dice_artifacts2.cdi_attest());
202 assert_eq_bytes(dice_artifacts.cdi_seal(), dice_artifacts2.cdi_seal());
203 assert_eq_bytes(dice_artifacts.bcc().expect("bcc"), dice_artifacts2.bcc().expect("bcc"));
204
205 Ok(())
206 }
207}