fxfs_crypto/cipher/
fxfs.rs

1// Copyright 2025 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4use super::{Cipher, SECTOR_SIZE, Tweak, UnwrappedKey, XtsProcessor};
5use aes::Aes256;
6use aes::cipher::generic_array::GenericArray;
7use aes::cipher::{BlockDecrypt, BlockEncrypt, KeyInit};
8use anyhow::Error;
9use log::warn;
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(GenericArray::from_slice(key)) }
19    }
20}
21impl Cipher for FxfsCipher {
22    fn encrypt(
23        &self,
24        _ino: u64,
25        _device_offset: u64,
26        file_offset: u64,
27        buffer: &mut [u8],
28    ) -> Result<(), Error> {
29        fxfs_trace::duration!("encrypt", "len" => buffer.len());
30        assert_eq!(file_offset % SECTOR_SIZE, 0);
31        let mut sector_offset = file_offset / SECTOR_SIZE;
32        assert_eq!(buffer.len() % (SECTOR_SIZE as usize), 0);
33        for sector in buffer.chunks_exact_mut(SECTOR_SIZE as usize) {
34            let mut tweak = Tweak(sector_offset as u128);
35            // The same key is used for encrypting the data and computing the tweak.
36            self.key.encrypt_block(GenericArray::from_mut_slice(tweak.as_mut_bytes()));
37            self.key.encrypt_with_backend(XtsProcessor::new(tweak, sector));
38            sector_offset += 1;
39        }
40        Ok(())
41    }
42
43    fn decrypt(
44        &self,
45        _ino: u64,
46        _device_offset: u64,
47        file_offset: u64,
48        buffer: &mut [u8],
49    ) -> Result<(), Error> {
50        fxfs_trace::duration!("decrypt", "len" => buffer.len());
51        assert_eq!(file_offset % SECTOR_SIZE, 0);
52        let mut sector_offset = file_offset / SECTOR_SIZE;
53        assert_eq!(buffer.len() % (SECTOR_SIZE as usize), 0);
54        for sector in buffer.chunks_exact_mut(SECTOR_SIZE as usize) {
55            let mut tweak = Tweak(sector_offset as u128);
56            // The same key is used for encrypting the data and computing the tweak.
57            self.key.encrypt_block(GenericArray::from_mut_slice(tweak.as_mut_bytes()));
58            self.key.decrypt_with_backend(XtsProcessor::new(tweak, sector));
59            sector_offset += 1;
60        }
61        Ok(())
62    }
63
64    fn encrypt_filename(&self, _object_id: u64, _buffer: &mut Vec<u8>) -> Result<(), Error> {
65        debug_assert!(false, "encrypt_filename called on fxfs cipher");
66        Err(zx_status::Status::NOT_SUPPORTED.into())
67    }
68
69    fn decrypt_filename(&self, _object_id: u64, _buffer: &mut Vec<u8>) -> Result<(), Error> {
70        // NOTE: This isn't a debug assertion because it would trip on the golden image tests.
71        warn!("decrypt_filename called on fxfs cipher");
72        Err(zx_status::Status::NOT_SUPPORTED.into())
73    }
74
75    fn hash_code(&self, _raw_filename: &[u8], _filename: &str) -> Option<u32> {
76        debug_assert!(false, "hash_code called on fxfs cipher");
77        None
78    }
79
80    fn hash_code_casefold(&self, _filename: &str) -> u32 {
81        debug_assert!(false, "hash_code_casefold called on fxfs cipher");
82        0
83    }
84
85    fn supports_inline_encryption(&self) -> bool {
86        false
87    }
88
89    fn crypt_ctx(&self, _ino: u64, _file_offset: u64) -> Option<(u32, u8)> {
90        None
91    }
92}