1 // 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 
17 use anyhow::{anyhow, bail, Context, Error, Result};
18 use byteorder::{NativeEndian, ReadBytesExt};
19 use diced_open_dice::{
20     bcc_handover_parse, retry_bcc_main_flow, BccHandover, Config, DiceArtifacts, DiceMode, Hash,
21     Hidden, InputValues, OwnedDiceArtifacts,
22 };
23 use keystore2_crypto::ZVec;
24 use libc::{c_void, mmap, munmap, MAP_FAILED, MAP_PRIVATE, PROT_READ};
25 use openssl::hkdf::hkdf;
26 use openssl::md::Md;
27 use std::fs;
28 use std::os::unix::io::AsRawFd;
29 use std::path::{Path, PathBuf};
30 use std::ptr::null_mut;
31 use std::slice;
32 
33 /// Artifacts that are mapped into the process address space from the driver.
34 pub enum DiceDriver<'a> {
35     /// Implementation that reads bcc handover from the dice driver.
36     Real {
37         /// Path to the driver character device (e.g. /dev/open-dice0).
38         driver_path: PathBuf,
39         /// Address of the memory to mmap driver to.
40         mmap_addr: *mut c_void,
41         /// Size of the mmap.
42         mmap_size: usize,
43         /// BCC handover.
44         bcc_handover: BccHandover<'a>,
45     },
46     /// Fake implementation used in tests and non-protected VMs.
47     Fake(OwnedDiceArtifacts),
48     /// 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     },
55 }
56 
57 impl DiceDriver<'_> {
dice_artifacts(&self) -> &dyn DiceArtifacts58     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,
62             Self::FromFile { dice_artifacts, .. } => dice_artifacts,
63         }
64     }
65 
66     /// Creates a new dice driver from the given driver_path.
new(driver_path: &Path, is_strict_boot: bool) -> Result<Self>67     pub fn new(driver_path: &Path, is_strict_boot: bool) -> Result<Self> {
68         log::info!("Creating DiceDriver backed by {driver_path:?} driver");
69         if driver_path.exists() {
70             log::info!("Using DICE values from driver");
71         } else if is_strict_boot {
72             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 
109     /// Create a new dice driver that reads dice_artifacts from the given file.
from_file(file_path: &Path) -> Result<Self>110     pub fn from_file(file_path: &Path) -> Result<Self> {
111         log::info!("Creating DiceDriver backed by {file_path:?} file");
112         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 
119     /// Derives a sealing key of `key_length` bytes from the DICE sealing CDI.
get_sealing_key(&self, identifier: &[u8], key_length: usize) -> Result<ZVec>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 
130     /// Derives a new dice chain.
derive( self, code_hash: Hash, config_desc: &[u8], authority_hash: Hash, debug: bool, hidden: Hidden, ) -> Result<OwnedDiceArtifacts>131     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")?;
154         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 { .. } => (),
166         }
167         Ok(next_dice_artifacts)
168     }
169 }
170 
171 impl Drop for DiceDriver<'_> {
drop(&mut self)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 }
184 
185 #[cfg(test)]
186 mod tests {
187     use super::*;
188     use core::ffi::CStr;
189     use diced_open_dice::{
190         hash, retry_bcc_format_config_descriptor, DiceConfigValues, HIDDEN_SIZE,
191     };
192     use std::fs::File;
193 
assert_eq_bytes(expected: &[u8], actual: &[u8])194     fn assert_eq_bytes(expected: &[u8], actual: &[u8]) {
195         assert_eq!(
196             expected,
197             actual,
198             "Expected {}, got {}",
199             hex::encode(expected),
200             hex::encode(actual)
201         )
202     }
203 
204     #[test]
test_write_bcc_to_file_read_from_file() -> Result<()>205     fn test_write_bcc_to_file_read_from_file() -> Result<()> {
206         let dice_artifacts = diced_sample_inputs::make_sample_bcc_and_cdis()?;
207 
208         let test_file = tempfile::NamedTempFile::new()?;
209         serde_cbor::to_writer(test_file.as_file(), &dice_artifacts)?;
210         test_file.as_file().sync_all()?;
211 
212         let dice = DiceDriver::from_file(test_file.as_ref())?;
213 
214         let dice_artifacts2 = dice.dice_artifacts();
215         assert_eq_bytes(dice_artifacts.cdi_attest(), dice_artifacts2.cdi_attest());
216         assert_eq_bytes(dice_artifacts.cdi_seal(), dice_artifacts2.cdi_seal());
217         assert_eq_bytes(dice_artifacts.bcc().expect("bcc"), dice_artifacts2.bcc().expect("bcc"));
218 
219         Ok(())
220     }
221 
222     #[test]
test_dice_driver_from_file_deletes_file_after_derive() -> Result<()>223     fn test_dice_driver_from_file_deletes_file_after_derive() -> Result<()> {
224         let tmp_dir = tempfile::tempdir()?;
225 
226         let file_path = tmp_dir.path().join("test-dice-chain.raw");
227 
228         {
229             let dice_artifacts = diced_sample_inputs::make_sample_bcc_and_cdis()?;
230             let file = File::create(&file_path)?;
231             serde_cbor::to_writer(file, &dice_artifacts)?;
232         }
233 
234         let dice = DiceDriver::from_file(&file_path)?;
235 
236         let values = DiceConfigValues {
237             component_name: Some(CStr::from_bytes_with_nul(b"test\0")?),
238             ..Default::default()
239         };
240         let desc = retry_bcc_format_config_descriptor(&values)?;
241         let code_hash = hash(&String::from("test code hash").into_bytes())?;
242         let authority_hash = hash(&String::from("test authority hash").into_bytes())?;
243         let hidden = [0; HIDDEN_SIZE];
244 
245         let _ = dice.derive(code_hash, &desc, authority_hash, false, hidden)?;
246 
247         assert!(!file_path.exists());
248 
249         Ok(())
250     }
251 }
252