1use crate::{EncryptionKey, UnwrappedKey, WrappedKey};
5use aes::cipher::inout::InOutBuf;
6use aes::cipher::{
7 Block, BlockCipherDecrypt, BlockModeDecrypt, BlockModeEncrypt, KeyInit, KeyIvInit,
8};
9use anyhow::{Error, ensure};
10use std::collections::BTreeMap;
11use std::sync::Arc;
12pub use storage_ptr_slice::{MutPtrByteSlice, PtrByteSlice};
13pub use storage_xts::{Tweak, XtsInPlaceProcessor, XtsProcessor};
14use zerocopy::IntoBytes;
15use zx_status as zx;
16
17pub mod fscrypt_ino_lblk32;
18pub mod fscrypt_ino_lblk64;
19#[cfg(test)]
20mod fscrypt_test_data;
21pub(crate) mod fxfs;
22
23pub const FSCRYPT_PADDING: usize = 16;
28const SECTOR_SIZE: u64 = 512;
31const BLOCK_SIZE: usize = 4096;
32const MAX_FILENAME_LEN: usize = 255;
33const MAX_SYMLINK_LEN: usize = 4093;
34
35fn encrypt_filename_cts(
36 cts_key: &[u8; 32],
37 iv: &[u32; 4],
38 buffer: &mut Vec<u8>,
39 max_len: usize,
40) -> Result<(), Error> {
41 ensure!(buffer.len() <= max_len, "Filename too long");
42 buffer.resize(buffer.len().next_multiple_of(FSCRYPT_PADDING), 0);
43
44 let mut cbc = cbc::Encryptor::<aes::Aes256>::new(
45 cts_key.try_into().unwrap(),
46 iv.as_bytes().try_into().unwrap(),
47 );
48 let inout = InOutBuf::<'_, '_, u8>::from(&mut buffer[..]);
49 let (mut blocks, _): (InOutBuf<'_, '_, Block<aes::Aes256>>, _) = inout.into_chunks();
50 let mut chunks = blocks.get_out();
51 cbc.encrypt_blocks(&mut chunks);
52 if chunks.len() >= 2 {
53 chunks.swap(chunks.len() - 1, chunks.len() - 2);
58 buffer.truncate(max_len);
59 }
60 Ok(())
61}
62
63fn decrypt_filename_cts(
64 cts_key: &[u8; 32],
65 iv: &[u32; 4],
66 buffer: &mut Vec<u8>,
67 max_len: usize,
68) -> Result<(), Error> {
69 let alignment = buffer.len() % FSCRYPT_PADDING;
70 if alignment != 0 {
71 ensure!(buffer.len() == max_len, "Unexpected filename length");
75
76 let cipher = aes::Aes256::new(cts_key.try_into().unwrap());
78 let mut out: Block<aes::Aes256> =
79 buffer[max_len - alignment - FSCRYPT_PADDING..max_len - alignment].try_into().unwrap();
80 cipher.decrypt_block(&mut out);
81
82 buffer.extend_from_slice(&out[alignment..]);
84 }
85
86 let mut cbc = cbc::Decryptor::<aes::Aes256>::new(
87 cts_key.try_into().unwrap(),
88 iv.as_bytes().try_into().unwrap(),
89 );
90 let inout = InOutBuf::<'_, '_, u8>::from(&mut buffer[..]);
91 let (mut blocks, _): (InOutBuf<'_, '_, Block<aes::Aes256>>, _) = inout.into_chunks();
92 let mut chunks = blocks.get_out();
93 if chunks.len() >= 2 {
94 chunks.swap(chunks.len() - 1, chunks.len() - 2);
95 }
96 cbc.decrypt_blocks(&mut chunks);
97
98 while let Some(0) = buffer.last() {
100 buffer.pop();
101 }
102 Ok(())
103}
104
105pub trait Cipher: std::fmt::Debug + Send + Sync {
107 fn encrypt(
114 &self,
115 ino: u64,
116 attribute_id: u64,
117 device_offset: u64,
118 file_offset: u64,
119 buffer: MutPtrByteSlice<'_>,
120 ) -> Result<(), Error>;
121
122 fn decrypt(
129 &self,
130 ino: u64,
131 attribute_id: u64,
132 device_offset: u64,
133 file_offset: u64,
134 buffer: MutPtrByteSlice<'_>,
135 ) -> Result<(), Error>;
136
137 fn decrypt_to(
143 &self,
144 ino: u64,
145 attribute_id: u64,
146 device_offset: u64,
147 file_offset: u64,
148 src: PtrByteSlice<'_>,
149 mut dst: MutPtrByteSlice<'_>,
150 ) -> Result<(), Error> {
151 dst.copy_from_ptr_slice(src);
152 self.decrypt(ino, attribute_id, device_offset, file_offset, dst)
153 }
154
155 fn encrypt_filename(&self, object_id: u64, buffer: &mut Vec<u8>) -> Result<(), Error>;
157
158 fn decrypt_filename(&self, object_id: u64, buffer: &mut Vec<u8>) -> Result<(), Error>;
160
161 fn encrypt_symlink(&self, object_id: u64, buffer: &mut Vec<u8>) -> Result<(), Error> {
163 self.encrypt_filename(object_id, buffer)
164 }
165
166 fn decrypt_symlink(&self, object_id: u64, buffer: &mut Vec<u8>) -> Result<(), Error> {
168 self.decrypt_filename(object_id, buffer)
169 }
170
171 fn hash_code(&self, _raw_filename: &[u8], filename: &str) -> Option<u32>;
174
175 fn hash_code_casefold(&self, _filename: &str) -> u32;
177
178 fn supports_inline_encryption(&self) -> bool;
180
181 fn crypt_ctx(&self, ino: u64, attribute_id: u64, file_offset: u64) -> Option<(u64, u8)>;
184}
185
186#[derive(Clone, Copy, Debug, PartialEq, Eq)]
187pub enum KeyType {
188 Fxfs,
189 FscryptInoLblk32Dir,
190 FscryptInoLblk32File,
191 FscryptInoLblk64Dir,
192 FscryptInoLblk64File,
193}
194
195pub trait ToKeyType {
196 fn to_key_type(&self) -> Option<KeyType>;
197}
198
199impl ToKeyType for WrappedKey {
200 fn to_key_type(&self) -> Option<KeyType> {
201 match self {
202 WrappedKey::Fxfs(_) => Some(KeyType::Fxfs),
203 WrappedKey::FscryptInoLblk32Dir { .. } => Some(KeyType::FscryptInoLblk32Dir),
204 WrappedKey::FscryptInoLblk32File { .. } => Some(KeyType::FscryptInoLblk32File),
205 WrappedKey::FscryptInoLblk64Dir { .. } => Some(KeyType::FscryptInoLblk64Dir),
206 WrappedKey::FscryptInoLblk64File { .. } => Some(KeyType::FscryptInoLblk64File),
207 _ => None,
208 }
209 }
210}
211
212impl ToKeyType for EncryptionKey {
213 fn to_key_type(&self) -> Option<KeyType> {
214 match self {
215 EncryptionKey::LegacyFxfs(_) => unreachable!(),
216 EncryptionKey::Fxfs(_) => Some(KeyType::Fxfs),
217 EncryptionKey::FscryptInoLblk32Dir { .. } => Some(KeyType::FscryptInoLblk32Dir),
218 EncryptionKey::FscryptInoLblk32File { .. } => Some(KeyType::FscryptInoLblk32File),
219 EncryptionKey::FscryptInoLblk64Dir { .. } => Some(KeyType::FscryptInoLblk64Dir),
220 EncryptionKey::FscryptInoLblk64File { .. } => Some(KeyType::FscryptInoLblk64File),
221 }
222 }
223}
224
225impl ToKeyType for KeyType {
226 fn to_key_type(&self) -> Option<KeyType> {
227 Some(*self)
228 }
229}
230
231#[inline]
235pub fn key_to_cipher(
236 key_type: &impl ToKeyType,
237 unwrapped_key: &UnwrappedKey,
238) -> Result<Arc<dyn Cipher>, zx::Status> {
239 key_type
240 .to_key_type()
241 .map(|key_type| match key_type {
242 KeyType::Fxfs => Arc::new(fxfs::FxfsCipher::new(unwrapped_key)) as Arc<dyn Cipher>,
243 KeyType::FscryptInoLblk32Dir => {
244 Arc::new(fscrypt_ino_lblk32::FscryptInoLblk32DirCipher::new(unwrapped_key))
245 }
246 KeyType::FscryptInoLblk32File => {
247 Arc::new(fscrypt_ino_lblk32::FscryptInoLblk32FileCipher::new(unwrapped_key))
248 }
249 KeyType::FscryptInoLblk64Dir => {
250 Arc::new(fscrypt_ino_lblk64::FscryptInoLblk64DirCipher::new(unwrapped_key))
251 }
252 KeyType::FscryptInoLblk64File => {
253 Arc::new(fscrypt_ino_lblk64::FscryptInoLblk64FileCipher::new(unwrapped_key))
254 }
255 })
256 .ok_or(zx::Status::NOT_SUPPORTED)
257}
258
259#[derive(Clone, Debug)]
260pub enum CipherHolder {
261 Cipher(Arc<dyn Cipher>),
262 Unavailable,
263}
264
265impl CipherHolder {
266 pub fn into_cipher(self) -> Option<Arc<dyn Cipher>> {
267 match self {
268 CipherHolder::Cipher(c) => Some(c),
269 _ => None,
270 }
271 }
272}
273
274#[derive(Clone, Debug, Default)]
276pub struct CipherSet(BTreeMap<u64, CipherHolder>);
277impl CipherSet {
278 pub fn find_key(self: &Arc<Self>, id: u64) -> FindKeyResult {
279 match self.0.get(&id) {
280 Some(CipherHolder::Cipher(cipher)) => FindKeyResult::Key(Arc::clone(cipher)),
281 Some(CipherHolder::Unavailable) => FindKeyResult::Unavailable,
282 None => FindKeyResult::NotFound,
283 }
284 }
285
286 pub fn add_key(&mut self, id: u64, cipher: CipherHolder) {
287 self.0.insert(id, cipher);
288 }
289}
290impl From<Vec<(u64, CipherHolder)>> for CipherSet {
291 fn from(keys: Vec<(u64, CipherHolder)>) -> Self {
292 Self(keys.into_iter().collect())
293 }
294}
295impl From<BTreeMap<u64, CipherHolder>> for CipherSet {
296 fn from(keys: BTreeMap<u64, CipherHolder>) -> Self {
297 Self(keys)
298 }
299}
300
301pub enum FindKeyResult {
302 NotFound,
304 Unavailable,
306 Key(Arc<dyn Cipher>),
307}