1pub mod direntry;
5pub mod hkdf;
6pub mod proxy_filename;
7
8use anyhow::{Error, anyhow, ensure};
9use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned};
10
11pub const POLICY_FLAGS_PAD_16: u8 = 0x02;
12pub const POLICY_FLAGS_INO_LBLK_32: u8 = 0x10;
13const SUPPORTED_POLICY_FLAGS: u8 = POLICY_FLAGS_PAD_16 | POLICY_FLAGS_INO_LBLK_32;
14
15pub const ENCRYPTION_MODE_AES_256_XTS: u8 = 1;
16pub const ENCRYPTION_MODE_AES_256_CTS: u8 = 4;
17
18#[repr(C, packed)]
21#[derive(Copy, Clone, Debug, Immutable, KnownLayout, FromBytes, IntoBytes, Unaligned)]
22pub struct Context {
23 pub version: u8, pub contents_encryption_mode: u8, pub filenames_encryption_mode: u8, pub flags: u8, pub log2_data_unit_size: u8, _reserved: [u8; 3],
29 pub main_key_identifier: [u8; 16],
30 pub nonce: [u8; 16],
31}
32
33impl Context {
34 pub fn try_from_bytes(raw_context: &[u8]) -> Result<Option<Self>, Error> {
35 let this = Context::read_from_bytes(raw_context)
36 .map_err(|_| anyhow!("Bad sized crypto context"))?;
37 ensure!(this.version == 2, "Bad version number in crypto context");
38 ensure!(
39 this.contents_encryption_mode == ENCRYPTION_MODE_AES_256_XTS,
40 "Unsupported contents_encryption_mode",
41 );
42 ensure!(
43 this.filenames_encryption_mode == ENCRYPTION_MODE_AES_256_CTS,
44 "Unsupported filenames_encryption_mode"
45 );
46 ensure!(this.flags & !SUPPORTED_POLICY_FLAGS == 0, "Unsupported flags in crypto context");
49 ensure!(this.log2_data_unit_size == 0, "Unsupported custom DUN size");
51 Ok(Some(this))
52 }
53}
54
55pub fn main_key_to_identifier(main_key: &[u8; 64]) -> [u8; 16] {
57 hkdf::fscrypt_hkdf::<16>(main_key, &[], 1)
58}
59
60pub struct DirectoryKeys {
61 cts_key: [u8; 32],
62 ino_hash_key: [u8; 16],
63 dir_hash_key: [u8; 16],
64}
65
66impl DirectoryKeys {
67 pub fn to_unwrapped_key(&self) -> Vec<u8> {
69 let mut keys = Vec::with_capacity(64);
70 keys.extend_from_slice(&self.cts_key);
71 keys.extend_from_slice(&self.ino_hash_key);
72 keys.extend_from_slice(&self.dir_hash_key);
73 keys
74 }
75}
76
77pub fn to_directory_keys(main_key: &[u8], uuid: &[u8], nonce: &[u8]) -> DirectoryKeys {
80 let mut hdkf_info = [0; 17];
81 hdkf_info[0] = ENCRYPTION_MODE_AES_256_CTS;
82 hdkf_info[1..17].copy_from_slice(&uuid);
83 DirectoryKeys {
84 cts_key: hkdf::fscrypt_hkdf(main_key, &hdkf_info, hkdf::HKDF_CONTEXT_IV_INO_LBLK_32_KEY),
85 ino_hash_key: hkdf::fscrypt_hkdf(main_key, &[], hkdf::HKDF_CONTEXT_INODE_HASH_KEY),
86 dir_hash_key: hkdf::fscrypt_hkdf(main_key, &nonce, hkdf::HKDF_CONTEXT_DIRHASH_KEY),
87 }
88}
89
90pub fn to_xts_key(main_key: &[u8], uuid: [u8; 16]) -> [u8; 64] {
91 let mut hdkf_info = [0; 17];
92 hdkf_info[0] = ENCRYPTION_MODE_AES_256_XTS;
93 hdkf_info[1..17].copy_from_slice(&uuid);
94 hkdf::fscrypt_hkdf(&main_key, &hdkf_info, hkdf::HKDF_CONTEXT_IV_INO_LBLK_32_KEY)
95}
96
97#[cfg(test)]
98mod tests {
99 use super::*;
100
101 #[test]
102 fn test_main_key_to_identifier() {
103 let key_digest = "dc34d175ba21b27e2e92829b0dc12666ce8bfbcbae387014c6bb0d8b7678dafa6466bd7565b1a5999cd3f8a39a470528fa6816768e6985f0b10804af7d657810";
105 let key: [u8; 64] = hex::decode(&key_digest).unwrap().try_into().unwrap();
106 assert_eq!(hex::encode(main_key_to_identifier(&key)), "fc7f69a149f89a7529374cf9e96a6d13");
107 }
108}