1use crate::{EncryptionKey, UnwrappedKey, WrappedKey};
5use aes::cipher::generic_array::GenericArray;
6use aes::cipher::inout::InOut;
7use aes::cipher::typenum::consts::U16;
8use aes::cipher::{BlockBackend, BlockClosure, BlockSizeUser};
9use anyhow::Error;
10use static_assertions::assert_cfg;
11use std::collections::BTreeMap;
12use std::sync::Arc;
13use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, transmute_mut};
14use zx_status as zx;
15
16pub mod fscrypt_ino_lblk32;
17#[cfg(test)]
18mod fscrypt_test_data;
19pub(crate) mod fxfs;
20
21pub const FSCRYPT_PADDING: usize = 16;
26const SECTOR_SIZE: u64 = 512;
29
30pub trait Cipher: std::fmt::Debug + Send + Sync {
32 fn encrypt(
39 &self,
40 ino: u64,
41 device_offset: u64,
42 file_offset: u64,
43 buffer: &mut [u8],
44 ) -> Result<(), Error>;
45
46 fn decrypt(
53 &self,
54 ino: u64,
55 device_offset: u64,
56 file_offset: u64,
57 buffer: &mut [u8],
58 ) -> Result<(), Error>;
59
60 fn encrypt_filename(&self, object_id: u64, buffer: &mut Vec<u8>) -> Result<(), Error>;
62
63 fn decrypt_filename(&self, object_id: u64, buffer: &mut Vec<u8>) -> Result<(), Error>;
65
66 fn encrypt_symlink(&self, object_id: u64, buffer: &mut Vec<u8>) -> Result<(), Error> {
68 self.encrypt_filename(object_id, buffer)
69 }
70
71 fn decrypt_symlink(&self, object_id: u64, buffer: &mut Vec<u8>) -> Result<(), Error> {
73 self.decrypt_filename(object_id, buffer)
74 }
75
76 fn hash_code(&self, _raw_filename: &[u8], filename: &str) -> Option<u32>;
79
80 fn hash_code_casefold(&self, _filename: &str) -> u32;
82
83 fn supports_inline_encryption(&self) -> bool;
85
86 fn crypt_ctx(&self, ino: u64, file_offset: u64) -> Option<(u32, u8)>;
89}
90
91#[derive(Clone, Copy, Debug, PartialEq)]
92pub enum KeyType {
93 Fxfs,
94 FscryptInoLblk32Dir,
95 FscryptInoLblk32File,
96}
97
98pub trait ToKeyType {
99 fn to_key_type(&self) -> Option<KeyType>;
100}
101
102impl ToKeyType for WrappedKey {
103 fn to_key_type(&self) -> Option<KeyType> {
104 match self {
105 WrappedKey::Fxfs(_) => Some(KeyType::Fxfs),
106 WrappedKey::FscryptInoLblk32Dir { .. } => Some(KeyType::FscryptInoLblk32Dir),
107 WrappedKey::FscryptInoLblk32File { .. } => Some(KeyType::FscryptInoLblk32File),
108 _ => None,
109 }
110 }
111}
112
113impl ToKeyType for EncryptionKey {
114 fn to_key_type(&self) -> Option<KeyType> {
115 match self {
116 EncryptionKey::Fxfs(_) => Some(KeyType::Fxfs),
117 EncryptionKey::FscryptInoLblk32Dir { .. } => Some(KeyType::FscryptInoLblk32Dir),
118 EncryptionKey::FscryptInoLblk32File { .. } => Some(KeyType::FscryptInoLblk32File),
119 }
120 }
121}
122
123impl ToKeyType for KeyType {
124 fn to_key_type(&self) -> Option<KeyType> {
125 Some(*self)
126 }
127}
128
129#[inline]
133pub fn key_to_cipher(
134 key_type: &impl ToKeyType,
135 unwrapped_key: &UnwrappedKey,
136) -> Result<Arc<dyn Cipher>, zx::Status> {
137 key_type
138 .to_key_type()
139 .map(|key_type| match key_type {
140 KeyType::Fxfs => Arc::new(fxfs::FxfsCipher::new(&unwrapped_key)) as Arc<dyn Cipher>,
141 KeyType::FscryptInoLblk32Dir => {
142 Arc::new(fscrypt_ino_lblk32::FscryptInoLblk32DirCipher::new(&unwrapped_key))
143 }
144 KeyType::FscryptInoLblk32File => {
145 Arc::new(fscrypt_ino_lblk32::FscryptInoLblk32FileCipher::new(&unwrapped_key))
146 }
147 })
148 .ok_or(zx::Status::NOT_SUPPORTED)
149}
150
151#[derive(Clone, Debug)]
152pub enum CipherHolder {
153 Cipher(Arc<dyn Cipher>),
154 Unavailable(KeyType),
155}
156
157impl CipherHolder {
158 pub fn into_cipher(self) -> Option<Arc<dyn Cipher>> {
159 match self {
160 CipherHolder::Cipher(c) => Some(c),
161 _ => None,
162 }
163 }
164}
165
166#[derive(Clone, Debug, Default)]
168pub struct CipherSet(BTreeMap<u64, CipherHolder>);
169impl CipherSet {
170 pub fn find_key(self: &Arc<Self>, id: u64) -> FindKeyResult {
171 match self.0.get(&id) {
172 Some(CipherHolder::Cipher(cipher)) => FindKeyResult::Key(Arc::clone(cipher)),
173 Some(CipherHolder::Unavailable(key_type)) => FindKeyResult::Unavailable(*key_type),
174 None => FindKeyResult::NotFound,
175 }
176 }
177
178 pub fn add_key(&mut self, id: u64, cipher: CipherHolder) {
179 self.0.insert(id, cipher);
180 }
181}
182impl From<Vec<(u64, CipherHolder)>> for CipherSet {
183 fn from(keys: Vec<(u64, CipherHolder)>) -> Self {
184 Self(keys.into_iter().collect())
185 }
186}
187impl From<BTreeMap<u64, CipherHolder>> for CipherSet {
188 fn from(keys: BTreeMap<u64, CipherHolder>) -> Self {
189 Self(keys)
190 }
191}
192
193pub enum FindKeyResult {
194 NotFound,
196 Unavailable(KeyType),
198 Key(Arc<dyn Cipher>),
199}
200
201assert_cfg!(target_endian = "little");
203#[derive(IntoBytes, KnownLayout, FromBytes, Immutable)]
204#[repr(C)]
205struct Tweak(u128);
206
207pub fn xor_in_place(a: &mut [u8], b: &[u8]) {
208 for (b1, b2) in a.iter_mut().zip(b.iter()) {
209 *b1 ^= *b2;
210 }
211}
212
213struct CbcEncryptProcessor<'a> {
215 tweak: Tweak,
216 data: &'a mut [u8],
217}
218
219impl<'a> CbcEncryptProcessor<'a> {
220 fn new(tweak: Tweak, data: &'a mut [u8]) -> Self {
221 Self { tweak, data }
222 }
223}
224
225impl BlockSizeUser for CbcEncryptProcessor<'_> {
226 type BlockSize = U16;
227}
228
229impl BlockClosure for CbcEncryptProcessor<'_> {
230 fn call<B: BlockBackend<BlockSize = Self::BlockSize>>(self, backend: &mut B) {
231 let Self { mut tweak, data } = self;
232 for block in data.chunks_exact_mut(16) {
233 xor_in_place(block, &tweak.0.to_le_bytes());
234 let chunk: &mut GenericArray<u8, _> = GenericArray::from_mut_slice(block);
235 backend.proc_block(InOut::from(chunk));
236 tweak.0 = u128::from_le_bytes(block.try_into().unwrap())
237 }
238 }
239}
240
241struct CbcDecryptProcessor<'a> {
243 tweak: Tweak,
244 data: &'a mut [u8],
245}
246
247impl<'a> CbcDecryptProcessor<'a> {
248 fn new(tweak: Tweak, data: &'a mut [u8]) -> Self {
249 Self { tweak, data }
250 }
251}
252
253impl BlockSizeUser for CbcDecryptProcessor<'_> {
254 type BlockSize = U16;
255}
256
257impl BlockClosure for CbcDecryptProcessor<'_> {
258 fn call<B: BlockBackend<BlockSize = Self::BlockSize>>(self, backend: &mut B) {
259 let Self { mut tweak, data } = self;
260 for block in data.chunks_exact_mut(16) {
261 let ciphertext = block.to_vec();
262 let chunk = GenericArray::from_mut_slice(block);
263 backend.proc_block(InOut::from(chunk));
264 xor_in_place(block, &tweak.0.to_le_bytes());
265 tweak.0 = u128::from_le_bytes(ciphertext.try_into().unwrap());
266 }
267 }
268}
269
270struct XtsProcessor<'a> {
272 tweak: Tweak,
273 data: &'a mut [u8],
274}
275
276impl<'a> XtsProcessor<'a> {
277 fn new(tweak: Tweak, data: &'a mut [u8]) -> Self {
279 assert_eq!(data.as_ptr() as usize & 15, 0, "data must be 16 byte aligned");
280 Self { tweak, data }
281 }
282}
283
284impl BlockSizeUser for XtsProcessor<'_> {
285 type BlockSize = U16;
286}
287
288impl BlockClosure for XtsProcessor<'_> {
289 fn call<B: BlockBackend<BlockSize = Self::BlockSize>>(self, backend: &mut B) {
290 let Self { mut tweak, data } = self;
291 let (chunks, _remainder) = data.as_chunks_mut::<16>();
292 for chunk in chunks {
293 let val: &mut zerocopy::Unalign<u128> = transmute_mut!(chunk);
294 val.set(val.get() ^ tweak.0);
295
296 let chunk_ga: &mut GenericArray<u8, U16> = chunk.into();
297 backend.proc_block(InOut::from(chunk_ga));
298
299 let val: &mut zerocopy::Unalign<u128> = transmute_mut!(chunk);
300 val.set(val.get() ^ tweak.0);
301 tweak.0 = (tweak.0 << 1) ^ ((tweak.0 as i128 >> 127) as u128 & 0x87);
302 }
303 }
304}