Skip to main content

fxfs_crypto/
cipher.rs

1// Copyright 2025 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.
4use crate::{EncryptionKey, UnwrappedKey, WrappedKey};
5use anyhow::Error;
6use std::collections::BTreeMap;
7use std::sync::Arc;
8pub use storage_ptr_slice::MutPtrByteSlice;
9use zx_status as zx;
10
11pub mod fscrypt_ino_lblk32;
12#[cfg(test)]
13mod fscrypt_test_data;
14pub(crate) mod fxfs;
15
16// TODO(https://fxbug.dev/375700939): Support different padding sizes based on SET_ENCRYPTION_POLICY
17// flags.
18// Note: This constant is used in platform code. It would be nice to move all fscrypt
19// internals into fxfs_lib and keep platform as simple as possible.
20pub const FSCRYPT_PADDING: usize = 16;
21// Fxfs will always use a block size >= 512 bytes, so we just assume a sector size of 512 bytes,
22// which will work fine even if a different block size is used by Fxfs or the underlying device.
23const SECTOR_SIZE: u64 = 512;
24
25/// Trait defining common methods shared across all ciphers.
26pub trait Cipher: std::fmt::Debug + Send + Sync {
27    /// Encrypts data in the `buffer`.
28    ///
29    /// * `offset` is the byte offset within the file.
30    /// * `buffer` is mutated in place.
31    ///
32    /// `buffer` *must* be 16 byte aligned.
33    fn encrypt(
34        &self,
35        ino: u64,
36        attribute_id: u64,
37        device_offset: u64,
38        file_offset: u64,
39        buffer: MutPtrByteSlice<'_>,
40    ) -> Result<(), Error>;
41
42    /// Decrypt the data in `buffer`.
43    ///
44    /// * `offset` is the byte offset within the file.
45    /// * `buffer` is mutated in place.
46    ///
47    /// `buffer` *must* be 16 byte aligned.
48    fn decrypt(
49        &self,
50        ino: u64,
51        attribute_id: u64,
52        device_offset: u64,
53        file_offset: u64,
54        buffer: MutPtrByteSlice<'_>,
55    ) -> Result<(), Error>;
56
57    /// Encrypts the filename contained in `buffer`.
58    fn encrypt_filename(&self, object_id: u64, buffer: &mut Vec<u8>) -> Result<(), Error>;
59
60    /// Decrypts the filename contained in `buffer`.
61    fn decrypt_filename(&self, object_id: u64, buffer: &mut Vec<u8>) -> Result<(), Error>;
62
63    /// Encrypts the symlink target contained in `buffer`.
64    fn encrypt_symlink(&self, object_id: u64, buffer: &mut Vec<u8>) -> Result<(), Error> {
65        self.encrypt_filename(object_id, buffer)
66    }
67
68    /// Decrypts the symlink target contained in `buffer`.
69    fn decrypt_symlink(&self, object_id: u64, buffer: &mut Vec<u8>) -> Result<(), Error> {
70        self.decrypt_filename(object_id, buffer)
71    }
72
73    /// Returns a hash_code to use.
74    /// Note in the case of encrypted filenames, takes the raw encrypted bytes.
75    fn hash_code(&self, _raw_filename: &[u8], filename: &str) -> Option<u32>;
76
77    /// Returns a case-folded hash_code to use for 'filename'.
78    fn hash_code_casefold(&self, _filename: &str) -> u32;
79
80    /// True if supports inline encryption
81    fn supports_inline_encryption(&self) -> bool;
82
83    /// If this cipher type supports inline encryption, returns the (dun, slot) value.
84    /// Else returns None.
85    fn crypt_ctx(&self, ino: u64, attribute_id: u64, file_offset: u64) -> Option<(u32, u8)>;
86}
87
88#[derive(Clone, Copy, Debug, PartialEq, Eq)]
89pub enum KeyType {
90    LegacyFxfs,
91    Fxfs,
92    FscryptInoLblk32Dir,
93    FscryptInoLblk32File,
94}
95
96pub trait ToKeyType {
97    fn to_key_type(&self) -> Option<KeyType>;
98}
99
100impl ToKeyType for WrappedKey {
101    fn to_key_type(&self) -> Option<KeyType> {
102        match self {
103            WrappedKey::Fxfs(_) => Some(KeyType::Fxfs),
104            WrappedKey::FscryptInoLblk32Dir { .. } => Some(KeyType::FscryptInoLblk32Dir),
105            WrappedKey::FscryptInoLblk32File { .. } => Some(KeyType::FscryptInoLblk32File),
106            _ => None,
107        }
108    }
109}
110
111impl ToKeyType for EncryptionKey {
112    fn to_key_type(&self) -> Option<KeyType> {
113        match self {
114            EncryptionKey::LegacyFxfs(_) => Some(KeyType::LegacyFxfs),
115            EncryptionKey::Fxfs(_) => Some(KeyType::Fxfs),
116            EncryptionKey::FscryptInoLblk32Dir { .. } => Some(KeyType::FscryptInoLblk32Dir),
117            EncryptionKey::FscryptInoLblk32File { .. } => Some(KeyType::FscryptInoLblk32File),
118        }
119    }
120}
121
122impl ToKeyType for KeyType {
123    fn to_key_type(&self) -> Option<KeyType> {
124        Some(*self)
125    }
126}
127
128/// Helper function to obtain a Cipher for a key.
129/// Uses key to interpret the meaning of the UnwrappedKey blob and then creates a
130/// cipher instance from the blob, returning it.
131#[inline]
132pub fn key_to_cipher(
133    key_type: &impl ToKeyType,
134    unwrapped_key: &UnwrappedKey,
135) -> Result<Arc<dyn Cipher>, zx::Status> {
136    key_type
137        .to_key_type()
138        .map(|key_type| match key_type {
139            KeyType::LegacyFxfs => {
140                Arc::new(fxfs::FxfsCipher::new_legacy(&unwrapped_key)) as Arc<dyn Cipher>
141            }
142            KeyType::Fxfs => Arc::new(fxfs::FxfsCipher::new(&unwrapped_key)) as Arc<dyn Cipher>,
143            KeyType::FscryptInoLblk32Dir => {
144                Arc::new(fscrypt_ino_lblk32::FscryptInoLblk32DirCipher::new(&unwrapped_key))
145            }
146            KeyType::FscryptInoLblk32File => {
147                Arc::new(fscrypt_ino_lblk32::FscryptInoLblk32FileCipher::new(&unwrapped_key))
148            }
149        })
150        .ok_or(zx::Status::NOT_SUPPORTED)
151}
152
153#[derive(Clone, Debug)]
154pub enum CipherHolder {
155    Cipher(Arc<dyn Cipher>),
156    Unavailable,
157}
158
159impl CipherHolder {
160    pub fn into_cipher(self) -> Option<Arc<dyn Cipher>> {
161        match self {
162            CipherHolder::Cipher(c) => Some(c),
163            _ => None,
164        }
165    }
166}
167
168/// A container that holds ciphers related to a specific object.
169#[derive(Clone, Debug, Default)]
170pub struct CipherSet(BTreeMap<u64, CipherHolder>);
171impl CipherSet {
172    pub fn find_key(self: &Arc<Self>, id: u64) -> FindKeyResult {
173        match self.0.get(&id) {
174            Some(CipherHolder::Cipher(cipher)) => FindKeyResult::Key(Arc::clone(cipher)),
175            Some(CipherHolder::Unavailable) => FindKeyResult::Unavailable,
176            None => FindKeyResult::NotFound,
177        }
178    }
179
180    pub fn add_key(&mut self, id: u64, cipher: CipherHolder) {
181        self.0.insert(id, cipher);
182    }
183}
184impl From<Vec<(u64, CipherHolder)>> for CipherSet {
185    fn from(keys: Vec<(u64, CipherHolder)>) -> Self {
186        Self(keys.into_iter().collect())
187    }
188}
189impl From<BTreeMap<u64, CipherHolder>> for CipherSet {
190    fn from(keys: BTreeMap<u64, CipherHolder>) -> Self {
191        Self(keys)
192    }
193}
194
195pub enum FindKeyResult {
196    /// No key registered with that key_id.
197    NotFound,
198    /// The key is known, but not available for use (cannot be unwrapped).
199    Unavailable,
200    Key(Arc<dyn Cipher>),
201}
202
203pub use storage_xts::{Tweak, XtsProcessor};