1// Copyright 2024 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.
45//! crypt_policy contains all the key policy logic for the different operations that can be done
6//! with hardware keys. Keeping the policy logic in one place makes it easier to audit.
78use anyhow::{bail, Context, Error};
910#[derive(Clone, Copy, PartialEq)]
11pub enum Policy {
12 Null,
13 TeeRequired,
14 TeeTransitional,
15 TeeOpportunistic,
16}
1718impl TryFrom<String> for Policy {
19type Error = Error;
2021fn try_from(value: String) -> Result<Self, Self::Error> {
22match value.as_ref() {
23"null" => Ok(Policy::Null),
24"tee" => Ok(Policy::TeeRequired),
25"tee-transitional" => Ok(Policy::TeeTransitional),
26"tee-opportunistic" => Ok(Policy::TeeOpportunistic),
27 p => bail!("unrecognized key source policy: '{p}'"),
28 }
29 }
30}
3132/// Reads the policy from well-known locations in `/boot`.
33pub async fn get_policy() -> Result<Policy, Error> {
34let policy = fuchsia_fs::file::read_in_namespace_to_string("/boot/config/zxcrypt").await;
35let policy = match policy {
36Ok(policy) => policy,
37// In tests, the configuration is found in the same package as fshost.
38Err(_) => fuchsia_fs::file::read_in_namespace_to_string("/pkg/config/zxcrypt").await?,
39 };
40 policy.try_into()
41}
4243#[derive(Debug)]
44pub enum KeySource {
45 Null,
46 Tee,
47}
4849/// Fxfs and zxcrypt have different null keys, so operations have to indicate which is ultimately
50/// going to consume the key we produce.
51pub enum KeyConsumer {
52/// The null key for fxfs is a 128-bit key with the bytes "zxcrypt" at the beginning and then
53 /// padded with zeros. This is for legacy reasons - earlier versions of this code picked this
54 /// key, so we need to continue to use it to avoid wiping everyone's null-key-encrypted fxfs
55 /// data partitions.
56Fxfs,
57/// The null key for zxcrypt is a 256-bit key containing all zeros.
58Zxcrypt,
59}
6061impl KeySource {
62pub async fn get_key(&self, consumer: KeyConsumer) -> Result<Vec<u8>, Error> {
63match self {
64 KeySource::Null => match consumer {
65 KeyConsumer::Fxfs => {
66let mut key = b"zxcrypt".to_vec();
67 key.resize(16, 0);
68Ok(key)
69 }
70 KeyConsumer::Zxcrypt => Ok(vec![0u8; 32]),
71 },
72 KeySource::Tee => {
73// Regardless of the consumer of this key, the key we retrieve with kms is always
74 // named "zxcrypt". This is so that old recovery images that might not be aware of
75 // fxfs can still wipe the data keys during a factory reset.
76kms_stateless::get_hardware_derived_key(kms_stateless::KeyInfo::new_zxcrypt())
77 .await
78.context("failed to get hardware key")
79 }
80 }
81 }
82}
8384/// Returns all valid key sources when formatting a volume, based on `policy`.
85pub fn format_sources(policy: Policy) -> Vec<KeySource> {
86match policy {
87 Policy::Null => vec![KeySource::Null],
88 Policy::TeeRequired => vec![KeySource::Tee],
89 Policy::TeeTransitional => vec![KeySource::Tee],
90 Policy::TeeOpportunistic => vec![KeySource::Tee, KeySource::Null],
91 }
92}
9394/// Returns all valid key sources when unsealing a volume, based on `policy`.
95pub fn unseal_sources(policy: Policy) -> Vec<KeySource> {
96match policy {
97 Policy::Null => vec![KeySource::Null],
98 Policy::TeeRequired => vec![KeySource::Tee],
99 Policy::TeeTransitional => vec![KeySource::Tee, KeySource::Null],
100 Policy::TeeOpportunistic => vec![KeySource::Tee, KeySource::Null],
101 }
102}