Skip to main content

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#[derive(Clone, serde::Serialize, serde::Deserialize)]
101pub struct KeymintSealedData {
102    pub sealing_key_info: Vec<u8>,
103    pub sealing_key_blob: Vec<u8>,
104    pub sealed_keys: BTreeMap<String, Vec<u8>>,
105    #[serde(default)]
106    pub old_blob: Option<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 {
122            sealing_key_info,
123            sealing_key_blob,
124            sealed_keys: BTreeMap::default(),
125            old_blob: None,
126        })
127    }
128
129    /// Generates and seals a new key named `label`.  Updates this struct to contain the sealed key
130    /// (to be retrieved later by [`Self::unseal_key`]), and returns the unsealed key.
131    pub async fn create_key(&mut self, label: &str) -> Result<Vec<u8>, Error> {
132        let mut key = vec![0u8; 32];
133        zx::cprng_draw(&mut key[..]);
134        let sealed_data =
135            kms_stateless::seal(&self.sealing_key_info[..], &self.sealing_key_blob[..], &key[..])
136                .await
137                .context("Failed to seal keymint key")?;
138        self.sealed_keys.insert(label.to_string(), sealed_data);
139        Ok(key)
140    }
141
142    /// Unseals a key previously created via [`Self::create_key`].
143    /// Returns either the unsealed key, or a `UnsealResult::KeyRequiresUpgrade` if the
144    /// hardware determines the sealing protocol format is too old.
145    pub async fn unseal_key(&self, label: &str) -> Result<UnsealResult, Error> {
146        let sealed = self.sealed_keys.get(label).ok_or_else(|| anyhow!("Key not found"))?;
147        match kms_stateless::unseal(
148            &self.sealing_key_info[..],
149            &self.sealing_key_blob[..],
150            &sealed[..],
151        )
152        .await
153        {
154            Ok(key) => Ok(UnsealResult::Success(key)),
155            Err(e) => {
156                if let kms_stateless::SealingKeysError::Unseal(
157                    fidl_fuchsia_security_keymint::UnsealError::KeyRequiresUpgrade,
158                ) = e
159                {
160                    return Ok(UnsealResult::KeyRequiresUpgrade);
161                }
162                Err(e.into())
163            }
164        }
165    }
166
167    /// Attempts to upgrade the hardware sealing blob for the current configuration.
168    /// In the event of a successful upgrade, the previous active blob is moved to `old_blob`
169    /// for cleanup.
170    pub async fn upgrade_sealing_blob(&mut self) -> Result<(), Error> {
171        let new_blob = kms_stateless::upgrade_sealing_key(
172            &self.sealing_key_info[..],
173            &self.sealing_key_blob[..],
174        )
175        .await?;
176
177        let old_blob = std::mem::replace(&mut self.sealing_key_blob, new_blob);
178        if self.old_blob.is_some() {
179            tracing::warn!(
180                "Overwriting an existing old_blob during upgrade. A Keymint key has been leaked."
181            );
182        }
183        self.old_blob = Some(old_blob);
184
185        Ok(())
186    }
187}
188
189/// A strictly typed result indicating if the hardware unseal succeeded or required an upgrade.
190pub enum UnsealResult {
191    Success(Vec<u8>),
192    KeyRequiresUpgrade,
193}
194
195/// Deletes all keymint-managed keys.
196pub async fn delete_all_keymint_keys() -> Result<(), Error> {
197    Ok(kms_stateless::delete_all_keys().await?)
198}
199
200/// Deletes a specific keymint-managed key.
201pub async fn delete_sealing_key(key_blob: &[u8]) -> Result<(), Error> {
202    Ok(kms_stateless::delete_sealing_key(key_blob).await?)
203}
204
205#[derive(Debug)]
206pub enum KeySource {
207    /// An insecure static key is used.
208    Null(NullKeySource),
209    /// A hardware-derived key is used, which is accessed by interacting with the TEE via
210    /// kms_stateless.
211    TeeDerived(TeeDerivedKeySource),
212    /// A hardware-sealed key is used.  The key is stored in sealed format, and Keymint is used to
213    /// unseal the key at runtime.  The keymint sealing key is never available in plaintext to the
214    /// system.
215    KeymintSealed,
216}
217
218/// Returns all valid key sources when formatting a volume, based on `policy`.
219pub fn format_sources(policy: Policy) -> Vec<KeySource> {
220    match policy {
221        Policy::Null => vec![KeySource::Null(NullKeySource)],
222        Policy::TeeRequired => vec![KeySource::TeeDerived(TeeDerivedKeySource)],
223        Policy::TeeTransitional => vec![KeySource::TeeDerived(TeeDerivedKeySource)],
224        Policy::TeeOpportunistic => {
225            vec![KeySource::TeeDerived(TeeDerivedKeySource), KeySource::Null(NullKeySource)]
226        }
227        Policy::Keymint => vec![KeySource::KeymintSealed],
228    }
229}
230
231/// Returns all valid key sources when unsealing a volume, based on `policy`.
232pub fn unseal_sources(policy: Policy) -> Vec<KeySource> {
233    match policy {
234        Policy::Null => vec![KeySource::Null(NullKeySource)],
235        Policy::TeeRequired => vec![KeySource::TeeDerived(TeeDerivedKeySource)],
236        Policy::TeeTransitional => {
237            vec![KeySource::TeeDerived(TeeDerivedKeySource), KeySource::Null(NullKeySource)]
238        }
239        Policy::TeeOpportunistic => {
240            vec![KeySource::TeeDerived(TeeDerivedKeySource), KeySource::Null(NullKeySource)]
241        }
242        Policy::Keymint => vec![KeySource::KeymintSealed],
243    }
244}