fxfs_crypto/cipher/
fxfs.rs1use super::{Cipher, SECTOR_SIZE, Tweak, UnwrappedKey, XtsProcessor};
5use aes::Aes256;
6use aes::cipher::{BlockCipherDecrypt, BlockCipherEncrypt, KeyInit};
7use anyhow::Error;
8use log::warn;
9pub use storage_ptr_slice::MutPtrByteSlice;
10use zerocopy::IntoBytes;
11
12#[derive(Debug)]
13pub struct FxfsCipher {
14 key: Aes256,
15 legacy: bool,
16}
17impl FxfsCipher {
18 pub fn new(key: &UnwrappedKey) -> Self {
19 Self { key: Aes256::new(key.as_slice().try_into().unwrap()), legacy: false }
20 }
21
22 pub fn new_legacy(key: &UnwrappedKey) -> Self {
23 Self { key: Aes256::new(key.as_slice().try_into().unwrap()), legacy: true }
24 }
25}
26impl Cipher for FxfsCipher {
27 fn encrypt(
28 &self,
29 _ino: u64,
30 attribute_id: u64,
31 _device_offset: u64,
32 file_offset: u64,
33 mut buffer: MutPtrByteSlice<'_>,
34 ) -> Result<(), Error> {
35 fxfs_trace::duration!("encrypt", "len" => buffer.len());
36 assert_eq!(file_offset % SECTOR_SIZE, 0);
37 let mut sector_offset = file_offset / SECTOR_SIZE;
38 assert_eq!(buffer.len() % (SECTOR_SIZE as usize), 0);
39 let upper_tweak = if self.legacy { 0 } else { (attribute_id as u128) << 64 };
40 let mut offset = 0;
41 while offset < buffer.len() {
42 let sector = buffer.reborrow().subslice_mut(offset..offset + SECTOR_SIZE as usize);
43 let mut tweak = Tweak(upper_tweak | (sector_offset as u128));
44 self.key.encrypt_block(tweak.as_mut_bytes().try_into().unwrap());
46 self.key.encrypt_with_backend(XtsProcessor::new_in_place(tweak, sector));
47 sector_offset += 1;
48 offset += SECTOR_SIZE as usize;
49 }
50 Ok(())
51 }
52
53 fn decrypt(
54 &self,
55 _ino: u64,
56 attribute_id: u64,
57 _device_offset: u64,
58 file_offset: u64,
59 mut buffer: MutPtrByteSlice<'_>,
60 ) -> Result<(), Error> {
61 fxfs_trace::duration!("decrypt", "len" => buffer.len());
62 assert_eq!(file_offset % SECTOR_SIZE, 0);
63 let mut sector_offset = file_offset / SECTOR_SIZE;
64 assert_eq!(buffer.len() % (SECTOR_SIZE as usize), 0);
65 let upper_tweak = if self.legacy { 0 } else { (attribute_id as u128) << 64 };
66 let mut offset = 0;
67 while offset < buffer.len() {
68 let sector = buffer.reborrow().subslice_mut(offset..offset + SECTOR_SIZE as usize);
69 let mut tweak = Tweak(upper_tweak | (sector_offset as u128));
70 self.key.encrypt_block(tweak.as_mut_bytes().try_into().unwrap());
72 self.key.decrypt_with_backend(XtsProcessor::new_in_place(tweak, sector));
73 sector_offset += 1;
74 offset += SECTOR_SIZE as usize;
75 }
76 Ok(())
77 }
78
79 fn encrypt_filename(&self, _object_id: u64, _buffer: &mut Vec<u8>) -> Result<(), Error> {
80 debug_assert!(false, "encrypt_filename called on fxfs cipher");
81 Err(zx_status::Status::NOT_SUPPORTED.into())
82 }
83
84 fn decrypt_filename(&self, _object_id: u64, _buffer: &mut Vec<u8>) -> Result<(), Error> {
85 warn!("decrypt_filename called on fxfs cipher");
87 Err(zx_status::Status::NOT_SUPPORTED.into())
88 }
89
90 fn hash_code(&self, _raw_filename: &[u8], _filename: &str) -> Option<u32> {
91 debug_assert!(false, "hash_code called on fxfs cipher");
92 None
93 }
94
95 fn hash_code_casefold(&self, _filename: &str) -> u32 {
96 debug_assert!(false, "hash_code_casefold called on fxfs cipher");
97 0
98 }
99
100 fn supports_inline_encryption(&self) -> bool {
101 false
102 }
103
104 fn crypt_ctx(&self, _ino: u64, _attribute_id: u64, _file_offset: u64) -> Option<(u32, u8)> {
105 None
106 }
107}
108
109#[cfg(test)]
110mod tests {
111 use super::{Cipher, FxfsCipher, SECTOR_SIZE};
112 use crate::UnwrappedKey;
113 use storage_ptr_slice::MutPtrByteSlice;
114
115 #[test]
116 fn test_legacy_fxfs_cipher_ignores_attribute_id() {
117 let key = UnwrappedKey::new(vec![0x42; 32]);
118 let cipher = FxfsCipher::new_legacy(&key);
119 let mut buf0 = vec![0x12; SECTOR_SIZE as usize];
120 let mut buf1 = vec![0x12; SECTOR_SIZE as usize];
121
122 cipher.encrypt(1, 0, 0, 0, MutPtrByteSlice::from(&mut buf0[..])).expect("encrypt attr 0");
123 cipher.encrypt(1, 4, 0, 0, MutPtrByteSlice::from(&mut buf1[..])).expect("encrypt attr 4");
124 assert_eq!(
125 buf0, buf1,
126 "LegacyFxfsCipher should produce identical ciphertext for same file_offset regardless \
127 of attribute_id"
128 );
129 }
130
131 #[test]
132 fn test_fxfs_cipher_domain_separates_attribute_id() {
133 let key = UnwrappedKey::new(vec![0x42; 32]);
134 let cipher = FxfsCipher::new(&key);
135 let mut buf0 = vec![0x12; SECTOR_SIZE as usize];
136 let mut buf1 = vec![0x12; SECTOR_SIZE as usize];
137
138 cipher.encrypt(1, 0, 0, 0, MutPtrByteSlice::from(&mut buf0[..])).expect("encrypt attr 0");
139 cipher.encrypt(1, 4, 0, 0, MutPtrByteSlice::from(&mut buf1[..])).expect("encrypt attr 4");
140 assert_ne!(buf0, buf1, "FxfsCipher should domain-separate tweaks across attribute_id");
141
142 cipher.decrypt(1, 0, 0, 0, MutPtrByteSlice::from(&mut buf0[..])).expect("decrypt attr 0");
144 assert_eq!(buf0, vec![0x12; SECTOR_SIZE as usize]);
145
146 cipher.decrypt(1, 4, 0, 0, MutPtrByteSlice::from(&mut buf1[..])).expect("decrypt attr 4");
147 assert_eq!(buf1, vec![0x12; SECTOR_SIZE as usize]);
148 }
149}