1use crate::{EncryptionKey, UnwrappedKey, WrappedKey};
5use aes::cipher::inout::InOut;
6use aes::cipher::typenum::consts::U16;
7use aes::cipher::{
8 BlockCipherDecBackend, BlockCipherDecClosure, BlockCipherEncBackend, BlockCipherEncClosure,
9 BlockSizeUser,
10};
11use anyhow::Error;
12use static_assertions::assert_cfg;
13use std::collections::BTreeMap;
14use std::sync::Arc;
15use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, transmute_mut};
16use zx_status as zx;
17
18pub mod fscrypt_ino_lblk32;
19#[cfg(test)]
20mod fscrypt_test_data;
21pub(crate) mod fxfs;
22
23pub const FSCRYPT_PADDING: usize = 16;
28const SECTOR_SIZE: u64 = 512;
31
32pub trait Cipher: std::fmt::Debug + Send + Sync {
34 fn encrypt(
41 &self,
42 ino: u64,
43 attribute_id: u64,
44 device_offset: u64,
45 file_offset: u64,
46 buffer: &mut [u8],
47 ) -> Result<(), Error>;
48
49 fn decrypt(
56 &self,
57 ino: u64,
58 attribute_id: u64,
59 device_offset: u64,
60 file_offset: u64,
61 buffer: &mut [u8],
62 ) -> Result<(), Error>;
63
64 fn encrypt_filename(&self, object_id: u64, buffer: &mut Vec<u8>) -> Result<(), Error>;
66
67 fn decrypt_filename(&self, object_id: u64, buffer: &mut Vec<u8>) -> Result<(), Error>;
69
70 fn encrypt_symlink(&self, object_id: u64, buffer: &mut Vec<u8>) -> Result<(), Error> {
72 self.encrypt_filename(object_id, buffer)
73 }
74
75 fn decrypt_symlink(&self, object_id: u64, buffer: &mut Vec<u8>) -> Result<(), Error> {
77 self.decrypt_filename(object_id, buffer)
78 }
79
80 fn hash_code(&self, _raw_filename: &[u8], filename: &str) -> Option<u32>;
83
84 fn hash_code_casefold(&self, _filename: &str) -> u32;
86
87 fn supports_inline_encryption(&self) -> bool;
89
90 fn crypt_ctx(&self, ino: u64, attribute_id: u64, file_offset: u64) -> Option<(u32, u8)>;
93}
94
95#[derive(Clone, Copy, Debug, PartialEq, Eq)]
96pub enum KeyType {
97 LegacyFxfs,
98 Fxfs,
99 FscryptInoLblk32Dir,
100 FscryptInoLblk32File,
101}
102
103pub trait ToKeyType {
104 fn to_key_type(&self) -> Option<KeyType>;
105}
106
107impl ToKeyType for WrappedKey {
108 fn to_key_type(&self) -> Option<KeyType> {
109 match self {
110 WrappedKey::Fxfs(_) => Some(KeyType::Fxfs),
111 WrappedKey::FscryptInoLblk32Dir { .. } => Some(KeyType::FscryptInoLblk32Dir),
112 WrappedKey::FscryptInoLblk32File { .. } => Some(KeyType::FscryptInoLblk32File),
113 _ => None,
114 }
115 }
116}
117
118impl ToKeyType for EncryptionKey {
119 fn to_key_type(&self) -> Option<KeyType> {
120 match self {
121 EncryptionKey::LegacyFxfs(_) => Some(KeyType::LegacyFxfs),
122 EncryptionKey::Fxfs(_) => Some(KeyType::Fxfs),
123 EncryptionKey::FscryptInoLblk32Dir { .. } => Some(KeyType::FscryptInoLblk32Dir),
124 EncryptionKey::FscryptInoLblk32File { .. } => Some(KeyType::FscryptInoLblk32File),
125 }
126 }
127}
128
129impl ToKeyType for KeyType {
130 fn to_key_type(&self) -> Option<KeyType> {
131 Some(*self)
132 }
133}
134
135#[inline]
139pub fn key_to_cipher(
140 key_type: &impl ToKeyType,
141 unwrapped_key: &UnwrappedKey,
142) -> Result<Arc<dyn Cipher>, zx::Status> {
143 key_type
144 .to_key_type()
145 .map(|key_type| match key_type {
146 KeyType::LegacyFxfs => {
147 Arc::new(fxfs::FxfsCipher::new_legacy(&unwrapped_key)) as Arc<dyn Cipher>
148 }
149 KeyType::Fxfs => Arc::new(fxfs::FxfsCipher::new(&unwrapped_key)) as Arc<dyn Cipher>,
150 KeyType::FscryptInoLblk32Dir => {
151 Arc::new(fscrypt_ino_lblk32::FscryptInoLblk32DirCipher::new(&unwrapped_key))
152 }
153 KeyType::FscryptInoLblk32File => {
154 Arc::new(fscrypt_ino_lblk32::FscryptInoLblk32FileCipher::new(&unwrapped_key))
155 }
156 })
157 .ok_or(zx::Status::NOT_SUPPORTED)
158}
159
160#[derive(Clone, Debug)]
161pub enum CipherHolder {
162 Cipher(Arc<dyn Cipher>),
163 Unavailable,
164}
165
166impl CipherHolder {
167 pub fn into_cipher(self) -> Option<Arc<dyn Cipher>> {
168 match self {
169 CipherHolder::Cipher(c) => Some(c),
170 _ => None,
171 }
172 }
173}
174
175#[derive(Clone, Debug, Default)]
177pub struct CipherSet(BTreeMap<u64, CipherHolder>);
178impl CipherSet {
179 pub fn find_key(self: &Arc<Self>, id: u64) -> FindKeyResult {
180 match self.0.get(&id) {
181 Some(CipherHolder::Cipher(cipher)) => FindKeyResult::Key(Arc::clone(cipher)),
182 Some(CipherHolder::Unavailable) => FindKeyResult::Unavailable,
183 None => FindKeyResult::NotFound,
184 }
185 }
186
187 pub fn add_key(&mut self, id: u64, cipher: CipherHolder) {
188 self.0.insert(id, cipher);
189 }
190}
191impl From<Vec<(u64, CipherHolder)>> for CipherSet {
192 fn from(keys: Vec<(u64, CipherHolder)>) -> Self {
193 Self(keys.into_iter().collect())
194 }
195}
196impl From<BTreeMap<u64, CipherHolder>> for CipherSet {
197 fn from(keys: BTreeMap<u64, CipherHolder>) -> Self {
198 Self(keys)
199 }
200}
201
202pub enum FindKeyResult {
203 NotFound,
205 Unavailable,
207 Key(Arc<dyn Cipher>),
208}
209
210assert_cfg!(target_endian = "little");
212#[derive(IntoBytes, KnownLayout, FromBytes, Immutable)]
213#[repr(C)]
214struct Tweak(u128);
215
216struct XtsProcessor<'a> {
218 tweak: Tweak,
219 data: &'a mut [u8],
220}
221
222impl<'a> XtsProcessor<'a> {
223 fn new(tweak: Tweak, data: &'a mut [u8]) -> Self {
225 assert_eq!(data.as_ptr() as usize & 15, 0, "data must be 16 byte aligned");
226 Self { tweak, data }
227 }
228}
229
230impl BlockSizeUser for XtsProcessor<'_> {
231 type BlockSize = U16;
232}
233
234impl BlockCipherEncClosure for XtsProcessor<'_> {
235 fn call<B: BlockCipherEncBackend<BlockSize = Self::BlockSize>>(self, backend: &B) {
236 let Self { mut tweak, data } = self;
237 let (chunks, _remainder) = data.as_chunks_mut::<16>();
238 for chunk in chunks {
239 let val: &mut zerocopy::Unalign<u128> = transmute_mut!(chunk);
240 val.set(val.get() ^ tweak.0);
241
242 let chunk_ga: &mut aes::cipher::Array<u8, U16> = chunk.into();
243 backend.encrypt_block(InOut::from(chunk_ga));
244
245 let val: &mut zerocopy::Unalign<u128> = transmute_mut!(chunk);
246 val.set(val.get() ^ tweak.0);
247 tweak.0 = (tweak.0 << 1) ^ ((tweak.0 as i128 >> 127) as u128 & 0x87);
248 }
249 }
250}
251
252impl BlockCipherDecClosure for XtsProcessor<'_> {
253 fn call<B: BlockCipherDecBackend<BlockSize = Self::BlockSize>>(self, backend: &B) {
254 let Self { mut tweak, data } = self;
255 let (chunks, _remainder) = data.as_chunks_mut::<16>();
256 for chunk in chunks {
257 let val: &mut zerocopy::Unalign<u128> = transmute_mut!(chunk);
258 val.set(val.get() ^ tweak.0);
259
260 let chunk_ga: &mut aes::cipher::Array<u8, U16> = chunk.into();
261 backend.decrypt_block(InOut::from(chunk_ga));
262
263 let val: &mut zerocopy::Unalign<u128> = transmute_mut!(chunk);
264 val.set(val.get() ^ tweak.0);
265 tweak.0 = (tweak.0 << 1) ^ ((tweak.0 as i128 >> 127) as u128 & 0x87);
266 }
267 }
268}