1// Copyright 2022 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
45use fuchsia_async::TimeoutExt as _;
6use futures::{TryFutureExt as _, TryStreamExt as _};
7use std::ffi::{CStr, CString};
89/// Hardware derived key is expected to be a 128-bit AES key.
10const DERIVED_KEY_SIZE: usize = 16;
11const KEY_INFO_SIZE: usize = 32;
1213#[derive(Copy, Clone, Debug)]
14pub enum TaKeysafeCommand {
15 GetUserDataStorageKey = 8,
16 RotateHardwareDerivedKey = 9,
17}
1819/// Error values generated by this library
20#[derive(Debug, thiserror::Error)]
21pub enum Error {
22#[error("tee command {0:?} failed: {1}")]
23TeeCommand(TaKeysafeCommand, u32),
24#[error("tee command {0:?} failed: not supported")]
25TeeCommandNotSupported(TaKeysafeCommand),
26#[error(
27"tee command {:?} failed: buffer with hardcoded size {} bytes was too small",
28 .0,
29 DERIVED_KEY_SIZE,
30 )]
31TeeCommandBufferTooSmall(TaKeysafeCommand),
32#[error("failed to open dev directory")]
33DevDirectoryOpen(#[from] fuchsia_fs::node::OpenError),
34#[error("timeout waiting for tee device")]
35TeeDeviceWaitTimeout,
36#[error("failure waiting for tee device")]
37TeeDeviceWaitFailure(#[from] anyhow::Error),
38}
3940/// The info used to identify a key.
41pub struct KeyInfo {
42 info: [u8; KEY_INFO_SIZE],
43}
4445impl KeyInfo {
46/// Creates a new key info buffer using the provided string as the identifier.
47pub fn new(info: impl ToString) -> Self {
48Self::new_bytes(info.to_string().as_bytes())
49 }
5051/// Creates a new key info buffer using "zxcrypt" as the identifier.
52pub fn new_zxcrypt() -> Self {
53Self::new_bytes("zxcrypt".as_bytes())
54 }
5556fn new_bytes(info_bytes: &[u8]) -> Self {
57let mut info = [0; KEY_INFO_SIZE];
58// This will panic if the provided info is longer than 32 bytes, which is fine because
59 // that's a programming error.
60info[..info_bytes.len()].copy_from_slice(info_bytes);
61Self { info }
62 }
63}
6465fn call_command(
66 device: Option<&CStr>,
67 op: &mut tee::TeecOperation,
68 id: TaKeysafeCommand,
69) -> Result<(), Error> {
70match device {
71Some(dev) => tee::call_command_on_device(dev, op, id as u32),
72None => tee::call_command(op, id as u32),
73 }
74 .map_err(|e| match e {
75 tee::TEEC_ERROR_NOT_SUPPORTED => Error::TeeCommandNotSupported(id),
76 tee::TEEC_ERROR_SHORT_BUFFER => Error::TeeCommandBufferTooSmall(id),
77 e => Error::TeeCommand(id, e),
78 })
79}
8081fn get_key_from_tee_device(device: Option<&CStr>, info: KeyInfo) -> Result<Vec<u8>, Error> {
82let mut key_buf = [0u8; DERIVED_KEY_SIZE];
8384let mut op = tee::create_operation(
85 tee::teec_param_types(
86 tee::TEEC_MEMREF_TEMP_INPUT,
87 tee::TEEC_NONE,
88 tee::TEEC_NONE,
89 tee::TEEC_MEMREF_TEMP_OUTPUT,
90 ),
91 [
92 tee::get_memref_input_parameter(&info.info),
93 tee::get_zero_parameter(),
94 tee::get_zero_parameter(),
95 tee::get_memref_output_parameter(&mut key_buf),
96 ],
97 );
9899 call_command(device, &mut op, TaKeysafeCommand::GetUserDataStorageKey)?;
100101Ok(key_buf.to_vec())
102}
103104fn rotate_key_from_tee_device(device: Option<&CStr>, info: KeyInfo) -> Result<(), Error> {
105let mut op = tee::create_operation(
106 tee::teec_param_types(
107 tee::TEEC_MEMREF_TEMP_INPUT,
108 tee::TEEC_NONE,
109 tee::TEEC_NONE,
110 tee::TEEC_NONE,
111 ),
112 [
113 tee::get_memref_input_parameter(&info.info),
114 tee::get_zero_parameter(),
115 tee::get_zero_parameter(),
116 tee::get_zero_parameter(),
117 ],
118 );
119120 call_command(device, &mut op, TaKeysafeCommand::RotateHardwareDerivedKey)
121}
122123/// Gets a hardware derived key using the first device found in /dev/class/tee.
124/// This is useful in early boot when other services may not be up.
125pub async fn get_hardware_derived_key(info: KeyInfo) -> Result<Vec<u8>, Error> {
126const DEV_CLASS_TEE: &str = "/dev/class/tee";
127128let dir = fuchsia_fs::directory::open_in_namespace(DEV_CLASS_TEE, fuchsia_fs::Flags::empty())?;
129let mut stream = device_watcher::watch_for_files(&dir).await?;
130let first = stream
131 .try_next()
132 .map_err(Error::from)
133 .on_timeout(std::time::Duration::from_secs(5), || Err(Error::TeeDeviceWaitTimeout))
134 .await?;
135let first = first.ok_or_else(|| {
136 Error::TeeDeviceWaitFailure(anyhow::anyhow!(
137"'{DEV_CLASS_TEE}' watcher closed unexpectedly"
138))
139 })?;
140let first = first.to_str().expect("paths are utf-8");
141142let dev = format!("{DEV_CLASS_TEE}/{first}");
143let dev = CString::new(dev).expect("paths do not contain nul bytes");
144 get_key_from_tee_device(Some(&dev), info)
145}
146147/// Gets a hardware derived key using the service fuchsia.tee.Application. This should be used from
148/// components.
149pub async fn get_hardware_derived_key_from_service(info: KeyInfo) -> Result<Vec<u8>, Error> {
150 get_key_from_tee_device(None, info)
151}
152153/// Rotates the hardware derived key from a tee device at the /dev/class/tee.
154/// This is useful in early boot when other services may not be up.
155pub async fn rotate_hardware_derived_key(info: KeyInfo) -> Result<(), Error> {
156const DEV_CLASS_TEE: &str = "/dev/class/tee";
157158let dir = fuchsia_fs::directory::open_in_namespace(DEV_CLASS_TEE, fuchsia_fs::Flags::empty())?;
159let mut stream = device_watcher::watch_for_files(&dir).await?;
160let first = stream
161 .try_next()
162 .map_err(Error::from)
163 .on_timeout(std::time::Duration::from_secs(5), || Err(Error::TeeDeviceWaitTimeout))
164 .await?;
165let first = first.ok_or_else(|| {
166 Error::TeeDeviceWaitFailure(anyhow::anyhow!(
167"'{DEV_CLASS_TEE}' watcher closed unexpectedly"
168))
169 })?;
170let first = first.to_str().expect("paths are utf-8");
171172let dev = format!("{DEV_CLASS_TEE}/{first}");
173let dev = CString::new(dev).expect("paths do not contain nul bytes");
174 rotate_key_from_tee_device(Some(&dev), info)
175}
176177/// Rotates an existing hardware derived key identified by [`info`] using the service
178/// fuchsia.tee.Application. This should be used from components.
179pub async fn rotate_hardware_derived_key_from_service(info: KeyInfo) -> Result<(), Error> {
180 rotate_key_from_tee_device(None, info)
181}