1use super::{Cipher, SECTOR_SIZE, Tweak, UnwrappedKey, XtsInPlaceProcessor, XtsProcessor};
5use aes::Aes256;
6use aes::cipher::{BlockCipherDecrypt, BlockCipherEncrypt, KeyInit};
7use anyhow::Error;
8use log::warn;
9pub use storage_ptr_slice::{MutPtrByteSlice, PtrByteSlice};
10use zerocopy::IntoBytes;
11
12#[derive(Debug)]
13pub struct FxfsCipher {
14 key: Aes256,
15}
16impl FxfsCipher {
17 pub fn new(key: &UnwrappedKey) -> Self {
18 Self { key: Aes256::new(key.as_slice().try_into().unwrap()) }
19 }
20}
21impl Cipher for FxfsCipher {
22 fn encrypt(
23 &self,
24 _ino: u64,
25 attribute_id: u64,
26 _device_offset: u64,
27 file_offset: u64,
28 mut buffer: MutPtrByteSlice<'_>,
29 ) -> Result<(), Error> {
30 fxfs_trace::duration!("encrypt", "len" => buffer.len());
31 assert_eq!(file_offset % SECTOR_SIZE, 0);
32 let mut sector_offset = file_offset / SECTOR_SIZE;
33 assert_eq!(buffer.len() % (SECTOR_SIZE as usize), 0);
34 let upper_tweak = (attribute_id as u128) << 64;
35 let mut offset = 0;
36 while offset < buffer.len() {
37 let sector = buffer.reborrow().subslice_mut(offset..offset + SECTOR_SIZE as usize);
38 let mut tweak = Tweak(upper_tweak | (sector_offset as u128));
39 self.key.encrypt_block(tweak.as_mut_bytes().try_into().unwrap());
41 self.key.encrypt_with_backend(XtsInPlaceProcessor::new(tweak, sector));
42 sector_offset += 1;
43 offset += SECTOR_SIZE as usize;
44 }
45 Ok(())
46 }
47
48 fn decrypt(
49 &self,
50 _ino: u64,
51 attribute_id: u64,
52 _device_offset: u64,
53 file_offset: u64,
54 mut buffer: MutPtrByteSlice<'_>,
55 ) -> Result<(), Error> {
56 fxfs_trace::duration!("decrypt", "len" => buffer.len());
57 assert_eq!(file_offset % SECTOR_SIZE, 0);
58 let mut sector_offset = file_offset / SECTOR_SIZE;
59 assert_eq!(buffer.len() % (SECTOR_SIZE as usize), 0);
60 let upper_tweak = (attribute_id as u128) << 64;
61 let mut offset = 0;
62 while offset < buffer.len() {
63 let sector = buffer.reborrow().subslice_mut(offset..offset + SECTOR_SIZE as usize);
64 let mut tweak = Tweak(upper_tweak | (sector_offset as u128));
65 self.key.encrypt_block(tweak.as_mut_bytes().try_into().unwrap());
67 self.key.decrypt_with_backend(XtsInPlaceProcessor::new(tweak, sector));
68 sector_offset += 1;
69 offset += SECTOR_SIZE as usize;
70 }
71 Ok(())
72 }
73
74 fn decrypt_to(
75 &self,
76 _ino: u64,
77 attribute_id: u64,
78 _device_offset: u64,
79 file_offset: u64,
80 src: PtrByteSlice<'_>,
81 mut dst: MutPtrByteSlice<'_>,
82 ) -> Result<(), Error> {
83 fxfs_trace::duration!("decrypt_to", "len" => src.len());
84 assert_eq!(src.len(), dst.len());
85 assert_eq!(file_offset % SECTOR_SIZE, 0);
86 let mut sector_offset = file_offset / SECTOR_SIZE;
87 assert_eq!(src.len() % (SECTOR_SIZE as usize), 0);
88 let upper_tweak = (attribute_id as u128) << 64;
89 let mut offset = 0;
90 while offset < src.len() {
91 let src_sector = src.subslice(offset..offset + SECTOR_SIZE as usize);
92 let dst_sector = dst.reborrow().subslice_mut(offset..offset + SECTOR_SIZE as usize);
93 let mut tweak = Tweak(upper_tweak | (sector_offset as u128));
94 self.key.encrypt_block(tweak.as_mut_bytes().try_into().unwrap());
96 self.key.decrypt_with_backend(XtsProcessor::new(tweak, src_sector, dst_sector));
98 sector_offset += 1;
99 offset += SECTOR_SIZE as usize;
100 }
101 Ok(())
102 }
103
104 fn encrypt_filename(&self, _object_id: u64, _buffer: &mut Vec<u8>) -> Result<(), Error> {
105 debug_assert!(false, "encrypt_filename called on fxfs cipher");
106 Err(zx_status::Status::NOT_SUPPORTED.into())
107 }
108
109 fn decrypt_filename(&self, _object_id: u64, _buffer: &mut Vec<u8>) -> Result<(), Error> {
110 warn!("decrypt_filename called on fxfs cipher");
112 Err(zx_status::Status::NOT_SUPPORTED.into())
113 }
114
115 fn hash_code(&self, _raw_filename: &[u8], _filename: &str) -> Option<u32> {
116 debug_assert!(false, "hash_code called on fxfs cipher");
117 None
118 }
119
120 fn hash_code_casefold(&self, _filename: &str) -> u32 {
121 debug_assert!(false, "hash_code_casefold called on fxfs cipher");
122 0
123 }
124
125 fn supports_inline_encryption(&self) -> bool {
126 false
127 }
128
129 fn crypt_ctx(&self, _ino: u64, _attribute_id: u64, _file_offset: u64) -> Option<(u64, u8)> {
130 None
131 }
132}
133
134#[cfg(test)]
135mod tests {
136 use super::{Cipher, FxfsCipher, SECTOR_SIZE};
137 use crate::UnwrappedKey;
138 use storage_ptr_slice::{MutPtrByteSlice, PtrByteSlice};
139
140 #[test]
141 fn test_fxfs_cipher_domain_separates_attribute_id() {
142 let key = UnwrappedKey::new(vec![0x42; 32]);
143 let cipher = FxfsCipher::new(&key);
144 let mut buf0 = vec![0x12; SECTOR_SIZE as usize];
145 let mut buf1 = vec![0x12; SECTOR_SIZE as usize];
146
147 cipher.encrypt(1, 0, 0, 0, MutPtrByteSlice::from(&mut buf0[..])).expect("encrypt attr 0");
148 cipher.encrypt(1, 4, 0, 0, MutPtrByteSlice::from(&mut buf1[..])).expect("encrypt attr 4");
149 assert_ne!(buf0, buf1, "FxfsCipher should domain-separate tweaks across attribute_id");
150
151 cipher.decrypt(1, 0, 0, 0, MutPtrByteSlice::from(&mut buf0[..])).expect("decrypt attr 0");
153 assert_eq!(buf0, vec![0x12; SECTOR_SIZE as usize]);
154
155 cipher.decrypt(1, 4, 0, 0, MutPtrByteSlice::from(&mut buf1[..])).expect("decrypt attr 4");
156 assert_eq!(buf1, vec![0x12; SECTOR_SIZE as usize]);
157 }
158
159 #[test]
160 fn test_fxfs_cipher_decrypt_to() {
161 let key = UnwrappedKey::new(vec![0x42; 32]);
162 let cipher = FxfsCipher::new(&key);
163 #[derive(Clone, Copy)]
164 #[repr(align(64))]
165 struct Aligned64<T>(T);
166 let plaintext = Aligned64([0x12u8; 4 * SECTOR_SIZE as usize]);
167 let mut ciphertext = plaintext;
168 cipher.encrypt(1, 0, 0, 0, MutPtrByteSlice::from(&mut ciphertext.0[..])).expect("encrypt");
169
170 let mut decrypted = Aligned64([0u8; 4 * SECTOR_SIZE as usize]);
171 cipher
172 .decrypt_to(
173 1,
174 0,
175 0,
176 0,
177 PtrByteSlice::from(&ciphertext.0[..]),
178 MutPtrByteSlice::from(&mut decrypted.0[..]),
179 )
180 .expect("decrypt_to");
181 assert_eq!(decrypted.0, plaintext.0);
182 }
183}