crypt_policy/
lib.rs

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.
4
5//! 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.
7
8use std::collections::BTreeMap;
9
10use anyhow::{Context, Error, anyhow, bail};
11
12#[derive(Clone, Copy, Debug, PartialEq)]
13pub enum Policy {
14    Null,
15    TeeRequired,
16    TeeTransitional,
17    TeeOpportunistic,
18    Keymint,
19}
20
21impl TryFrom<String> for Policy {
22    type Error = Error;
23
24    fn try_from(value: String) -> Result<Self, Self::Error> {
25        match value.as_ref() {
26            "null" => Ok(Policy::Null),
27            "tee" => Ok(Policy::TeeRequired),
28            "tee-transitional" => Ok(Policy::TeeTransitional),
29            "tee-opportunistic" => Ok(Policy::TeeOpportunistic),
30            "keymint" => Ok(Policy::Keymint),
31            p => bail!("unrecognized key source policy: '{p}'"),
32        }
33    }
34}
35
36impl std::fmt::Display for Policy {
37    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
38        match self {
39            Self::Null => f.write_str("null"),
40            Self::TeeRequired => f.write_str("tee"),
41            Self::TeeTransitional => f.write_str("tee-transitional"),
42            Self::TeeOpportunistic => f.write_str("tee-opportunistic"),
43            Self::Keymint => f.write_str("keymint"),
44        }
45    }
46}
47
48/// Reads the policy from well-known locations in `/boot`.
49pub async fn get_policy() -> Result<Policy, Error> {
50    fuchsia_fs::file::read_in_namespace_to_string("/boot/config/zxcrypt").await?.try_into()
51}
52
53/// Fxfs and zxcrypt have different null keys, so operations have to indicate which is ultimately
54/// going to consume the key we produce.
55#[derive(Clone, Copy, Debug)]
56pub enum KeyConsumer {
57    /// The null key for fxfs is a 128-bit key with the bytes "zxcrypt" at the beginning and then
58    /// padded with zeros. This is for legacy reasons - earlier versions of this code picked this
59    /// key, so we need to continue to use it to avoid wiping everyone's null-key-encrypted fxfs
60    /// data partitions.
61    Fxfs,
62    /// The null key for zxcrypt is a 256-bit key containing all zeros.
63    Zxcrypt,
64}
65
66#[derive(Debug)]
67pub struct NullKeySource;
68
69impl NullKeySource {
70    pub fn get_key(&self, consumer: KeyConsumer) -> Vec<u8> {
71        match consumer {
72            KeyConsumer::Fxfs => {
73                let mut key = b"zxcrypt".to_vec();
74                key.resize(16, 0);
75                key
76            }
77            KeyConsumer::Zxcrypt => vec![0u8; 32],
78        }
79    }
80}
81
82#[derive(Debug)]
83pub struct TeeDerivedKeySource;
84
85impl TeeDerivedKeySource {
86    pub async fn get_key(&self) -> Result<Vec<u8>, Error> {
87        // Regardless of the consumer of this key, the key we retrieve with kms is always
88        // named "zxcrypt". This is so that old recovery images that might not be aware of
89        // fxfs can still wipe the data keys during a factory reset.
90        kms_stateless::get_hardware_derived_key(kms_stateless::KeyInfo::new_zxcrypt())
91            .await
92            .context("failed to get hardware key")
93    }
94}
95
96/// Bundles together a handle to a Keymint sealing key together with a list of keys sealed by the
97/// sealing key.  The contents of this struct can be persistently stored, as it contains no
98/// plaintext secrets.
99///
100/// Note that it is intentional that this struct does not implement serde::{Serialize, Deserialize};
101/// clients of KeymintSealedKeySource are better equipped to choose an appropriate format and manage
102/// versioning.
103pub struct KeymintSealedData {
104    pub sealing_key_info: Vec<u8>,
105    pub sealing_key_blob: Vec<u8>,
106    pub sealed_keys: BTreeMap<String, Vec<u8>>,
107}
108
109impl KeymintSealedData {
110    /// Generates a new hardware-backed sealing key based off of `sealing_key_info` and creates a
111    /// new instance of [`KeymintSealedData`] which uses this sealing key.
112    ///
113    /// Note that repeated calls to this will yield different sealing keys.  The sealing key should
114    /// be persisted if it needs to be reused.
115    pub async fn new() -> Result<Self, Error> {
116        let mut sealing_key_info = vec![0u8; 32];
117        zx::cprng_draw(&mut sealing_key_info[..]);
118        let sealing_key_blob = kms_stateless::create_sealing_key(&sealing_key_info[..])
119            .await
120            .context("Failed to create sealing key")?;
121        Ok(Self { sealing_key_info, sealing_key_blob, sealed_keys: BTreeMap::default() })
122    }
123
124    /// Generates and seals a new key named `label`.  Updates this struct to contain the sealed key
125    /// (to be retrieved later by [`Self::unseal_key`]), and returns the unsealed key.
126    pub async fn create_key(&mut self, label: &str) -> Result<Vec<u8>, Error> {
127        let mut key = vec![0u8; 32];
128        zx::cprng_draw(&mut key[..]);
129        let sealed_data =
130            kms_stateless::seal(&self.sealing_key_info[..], &self.sealing_key_blob[..], &key[..])
131                .await
132                .context("Failed to seal keymint key")?;
133        self.sealed_keys.insert(label.to_string(), sealed_data);
134        Ok(key)
135    }
136
137    /// Unseals a key previously created via [`Self::create_key`].  Returns the unsealed key.
138    pub async fn unseal_key(&self, label: &str) -> Result<Vec<u8>, Error> {
139        let sealed = self.sealed_keys.get(label).ok_or_else(|| anyhow!("Key not found"))?;
140        Ok(kms_stateless::unseal(
141            &self.sealing_key_info[..],
142            &self.sealing_key_blob[..],
143            &sealed[..],
144        )
145        .await?)
146    }
147}
148
149#[derive(Debug)]
150pub enum KeySource {
151    /// An insecure static key is used.
152    Null(NullKeySource),
153    /// A hardware-derived key is used, which is accessed by interacting with the TEE via
154    /// kms_stateless.
155    TeeDerived(TeeDerivedKeySource),
156    /// A hardware-sealed key is used.  The key is stored in sealed format, and Keymint is used to
157    /// unseal the key at runtime.  The keymint sealing key is never available in plaintext to the
158    /// system.
159    KeymintSealed,
160}
161
162/// Returns all valid key sources when formatting a volume, based on `policy`.
163pub fn format_sources(policy: Policy) -> Vec<KeySource> {
164    match policy {
165        Policy::Null => vec![KeySource::Null(NullKeySource)],
166        Policy::TeeRequired => vec![KeySource::TeeDerived(TeeDerivedKeySource)],
167        Policy::TeeTransitional => vec![KeySource::TeeDerived(TeeDerivedKeySource)],
168        Policy::TeeOpportunistic => {
169            vec![KeySource::TeeDerived(TeeDerivedKeySource), KeySource::Null(NullKeySource)]
170        }
171        Policy::Keymint => vec![KeySource::KeymintSealed],
172    }
173}
174
175/// Returns all valid key sources when unsealing a volume, based on `policy`.
176pub fn unseal_sources(policy: Policy) -> Vec<KeySource> {
177    match policy {
178        Policy::Null => vec![KeySource::Null(NullKeySource)],
179        Policy::TeeRequired => vec![KeySource::TeeDerived(TeeDerivedKeySource)],
180        Policy::TeeTransitional => {
181            vec![KeySource::TeeDerived(TeeDerivedKeySource), KeySource::Null(NullKeySource)]
182        }
183        Policy::TeeOpportunistic => {
184            vec![KeySource::TeeDerived(TeeDerivedKeySource), KeySource::Null(NullKeySource)]
185        }
186        Policy::Keymint => vec![KeySource::KeymintSealed],
187    }
188}