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