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