1use crate::{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};
14use zx_status as zx;
15
16mod fscrypt_ino_lblk32;
17pub(crate) mod fxfs;
18
19pub const FSCRYPT_PADDING: usize = 16;
24const SECTOR_SIZE: u64 = 512;
27
28pub trait Cipher: std::fmt::Debug + Send + Sync {
30 fn encrypt(
37 &self,
38 ino: u64,
39 device_offset: u64,
40 file_offset: u64,
41 buffer: &mut [u8],
42 ) -> Result<(), Error>;
43
44 fn decrypt(
51 &self,
52 ino: u64,
53 device_offset: u64,
54 file_offset: u64,
55 buffer: &mut [u8],
56 ) -> Result<(), Error>;
57
58 fn encrypt_filename(&self, object_id: u64, buffer: &mut Vec<u8>) -> Result<(), Error>;
60
61 fn decrypt_filename(&self, object_id: u64, buffer: &mut Vec<u8>) -> Result<(), Error>;
63
64 fn hash_code(&self, _raw_filename: &[u8], filename: &str) -> u32;
67
68 fn hash_code_casefold(&self, _filename: &str) -> u32;
70
71 fn supports_inline_encryption(&self) -> bool;
73
74 fn crypt_ctx(&self, ino: u64, file_offset: u64) -> Option<(u32, u8)>;
77}
78
79#[inline]
83pub(crate) fn key_to_cipher(
84 key: &WrappedKey,
85 unwrapped_key: &UnwrappedKey,
86) -> Result<Option<Arc<dyn Cipher>>, zx::Status> {
87 match key {
88 WrappedKey::Fxfs(_) => Ok(Some(Arc::new(fxfs::FxfsCipher::new(&unwrapped_key)))),
89 WrappedKey::FscryptInoLblk32Dir { .. } => {
90 Ok(Some(Arc::new(fscrypt_ino_lblk32::FscryptInoLblk32DirCipher::new(&unwrapped_key))))
91 }
92 WrappedKey::FscryptInoLblk32File { .. } => {
93 Ok(Some(Arc::new(fscrypt_ino_lblk32::FscryptInoLblk32FileCipher::new(&unwrapped_key))))
94 }
95 _ => Err(zx::Status::NOT_SUPPORTED),
96 }
97}
98
99#[derive(Clone, Debug, Default)]
101pub struct CipherSet(BTreeMap<u64, Option<Arc<dyn Cipher>>>);
102impl CipherSet {
103 pub fn find_key(self: &Arc<Self>, id: u64) -> FindKeyResult {
104 if let Some(cipher) = self.0.get(&id) {
105 if let Some(cipher) = cipher {
106 FindKeyResult::Key(Arc::clone(cipher))
107 } else {
108 FindKeyResult::Unavailable
109 }
110 } else {
111 FindKeyResult::NotFound
112 }
113 }
114
115 pub fn add_key(&mut self, id: u64, cipher: Option<Arc<dyn Cipher>>) {
116 self.0.insert(id, cipher);
117 }
118}
119impl From<Vec<(u64, Option<Arc<dyn Cipher>>)>> for CipherSet {
120 fn from(keys: Vec<(u64, Option<Arc<dyn Cipher>>)>) -> Self {
121 Self(keys.into_iter().collect())
122 }
123}
124impl From<BTreeMap<u64, Option<Arc<dyn Cipher>>>> for CipherSet {
125 fn from(keys: BTreeMap<u64, Option<Arc<dyn Cipher>>>) -> Self {
126 Self(keys)
127 }
128}
129
130pub enum FindKeyResult {
131 NotFound,
133 Unavailable,
135 Key(Arc<dyn Cipher>),
136}
137
138assert_cfg!(target_endian = "little");
140#[derive(IntoBytes, KnownLayout, FromBytes, Immutable)]
141#[repr(C)]
142struct Tweak(u128);
143
144pub fn xor_in_place(a: &mut [u8], b: &[u8]) {
145 for (b1, b2) in a.iter_mut().zip(b.iter()) {
146 *b1 ^= *b2;
147 }
148}
149
150struct CbcEncryptProcessor<'a> {
152 tweak: Tweak,
153 data: &'a mut [u8],
154}
155
156impl<'a> CbcEncryptProcessor<'a> {
157 fn new(tweak: Tweak, data: &'a mut [u8]) -> Self {
158 Self { tweak, data }
159 }
160}
161
162impl BlockSizeUser for CbcEncryptProcessor<'_> {
163 type BlockSize = U16;
164}
165
166impl BlockClosure for CbcEncryptProcessor<'_> {
167 fn call<B: BlockBackend<BlockSize = Self::BlockSize>>(self, backend: &mut B) {
168 let Self { mut tweak, data } = self;
169 for block in data.chunks_exact_mut(16) {
170 xor_in_place(block, &tweak.0.to_le_bytes());
171 let chunk: &mut GenericArray<u8, _> = GenericArray::from_mut_slice(block);
172 backend.proc_block(InOut::from(chunk));
173 tweak.0 = u128::from_le_bytes(block.try_into().unwrap())
174 }
175 }
176}
177
178struct CbcDecryptProcessor<'a> {
180 tweak: Tweak,
181 data: &'a mut [u8],
182}
183
184impl<'a> CbcDecryptProcessor<'a> {
185 fn new(tweak: Tweak, data: &'a mut [u8]) -> Self {
186 Self { tweak, data }
187 }
188}
189
190impl BlockSizeUser for CbcDecryptProcessor<'_> {
191 type BlockSize = U16;
192}
193
194impl BlockClosure for CbcDecryptProcessor<'_> {
195 fn call<B: BlockBackend<BlockSize = Self::BlockSize>>(self, backend: &mut B) {
196 let Self { mut tweak, data } = self;
197 for block in data.chunks_exact_mut(16) {
198 let ciphertext = block.to_vec();
199 let chunk = GenericArray::from_mut_slice(block);
200 backend.proc_block(InOut::from(chunk));
201 xor_in_place(block, &tweak.0.to_le_bytes());
202 tweak.0 = u128::from_le_bytes(ciphertext.try_into().unwrap());
203 }
204 }
205}
206
207struct XtsProcessor<'a> {
209 tweak: Tweak,
210 data: &'a mut [u8],
211}
212
213impl<'a> XtsProcessor<'a> {
214 fn new(tweak: Tweak, data: &'a mut [u8]) -> Self {
216 assert_eq!(data.as_ptr() as usize & 15, 0, "data must be 16 byte aligned");
217 Self { tweak, data }
218 }
219}
220
221impl BlockSizeUser for XtsProcessor<'_> {
222 type BlockSize = U16;
223}
224
225impl BlockClosure for XtsProcessor<'_> {
226 fn call<B: BlockBackend<BlockSize = Self::BlockSize>>(self, backend: &mut B) {
227 let Self { mut tweak, data } = self;
228 for chunk in data.chunks_exact_mut(16) {
229 let ptr = chunk.as_mut_ptr() as *mut u128;
230 unsafe {
237 *ptr ^= tweak.0;
238 let chunk = ptr as *mut GenericArray<u8, U16>;
239 backend.proc_block(InOut::from_raw(chunk, chunk));
240 *ptr ^= tweak.0;
241 }
242 tweak.0 = (tweak.0 << 1) ^ ((tweak.0 as i128 >> 127) as u128 & 0x87);
243 }
244 }
245}