Skip to content

Commit afea211

Browse files
authored
Update crypto.rs
1 parent 8ed6b6e commit afea211

1 file changed

Lines changed: 53 additions & 0 deletions

File tree

source-code/security/crypto.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ const REKEK_CTX: &[u8] = b"ghostfs-rekey-wrapping-v1";
1717
const DIRENC_CTX: &[u8] = b"ghostfs-dirname-encryption-v1";
1818
const DIRNAME_BLIND_CTX: &[u8] = b"ghostfs-dirname-blindindex-v1";
1919
const SIGNING_KEY_CTX: &[u8] = b"ghostfs-forensics-signing-key-v1";
20+
const INODE_ENC_CTX: &[u8] = b"ghostfs-inode-metadata-encryption-v1";
21+
const XATTR_ENC_CTX: &[u8] = b"ghostfs-xattr-encryption-v1";
22+
const XATTR_BLIND_CTX: &[u8] = b"ghostfs-xattr-blindindex-v1";
2023

2124
/// AAD = "GFS" || ino (8B LE) || block_idx (8B LE) || volume_uuid (16B)
2225
/// Wiąże szyfrogram z konkretnym blokiem konkretnego pliku konkretnego wolumenu.
@@ -136,6 +139,56 @@ impl Crypto {
136139
*h.finalize().as_bytes()
137140
}
138141

142+
/// Klucz szyfrujący METADANE inode (rozmiar, uprawnienia, czasy,
143+
/// uid/gid) — wcześniej `fuser::FileAttr` leżał jako jawny `bincode`
144+
/// wprost w wartości klucza sled `inode:{ino}`, mimo że dane bloków
145+
/// (`data:*`) i nazwy plików (`didx:*`, patrz dirindex.rs) były już
146+
/// szyfrowane. Ktoś z dostępem tylko do plików bazy sled (skopiowany
147+
/// obraz dysku, backup bez klucza) mógł odczytać rozmiar, właściciela,
148+
/// uprawnienia i znaczniki czasu KAŻDEGO pliku bez znajomości master
149+
/// key — dla systemu plików reklamującego się jako "cybersecurity"
150+
/// to była realna luka poufności metadanych, nie tylko teoretyczna.
151+
///
152+
/// Jeden klucz dla całego wolumenu (nie per-inode jak FEK) — metadane
153+
/// nie mają tej samej potrzeby izolacji co dane plików, a unikalność
154+
/// nonce jest i tak gwarantowana przez schemat sesyjny w `next_nonce()`.
155+
pub fn derive_inode_enc_key(&self) -> Key {
156+
let mut h = Hasher::new_keyed(&self.master_key);
157+
h.update(INODE_ENC_CTX);
158+
h.update(&self.volume_uuid);
159+
*h.finalize().as_bytes()
160+
}
161+
162+
/// Klucz szyfrujący WARTOŚĆ rozszerzonego atrybutu (xattr) danego
163+
/// inode — patrz `fs/xattr.rs`. Wcześniej zarówno NAZWA (wprost w
164+
/// kluczu sled) jak i WARTOŚĆ xattr leżały jawnym tekstem — a xattr
165+
/// bywają wrażliwe (etykiety SELinux/ACL, tokeny, metadane aplikacji
166+
/// typu "pobrano z <url>"), więc to była realna, nie tylko teoretyczna
167+
/// luka poufności, tej samej klasy co nazwy plików i metadane inode.
168+
pub fn derive_xattr_enc_key(&self, ino: u64) -> Key {
169+
let mut h = Hasher::new_keyed(&self.master_key);
170+
h.update(XATTR_ENC_CTX);
171+
h.update(&ino.to_le_bytes());
172+
h.update(&self.volume_uuid);
173+
*h.finalize().as_bytes()
174+
}
175+
176+
/// Blind index dla NAZWY xattr — ten sam wzorzec co
177+
/// `dirname_blind_index`: deterministyczny, kluczowany master_key,
178+
/// pozwala na lookup/set/remove O(1) bez odszyfrowywania, ale odporny
179+
/// na atak słownikowy na typowe nazwy xattr ("security.selinux",
180+
/// "user.comment", ...) bez znajomości klucza.
181+
pub fn xattr_blind_index(&self, ino: u64, name: &[u8]) -> [u8; 32] {
182+
let mut ctx_hasher = Hasher::new_keyed(&self.master_key);
183+
ctx_hasher.update(XATTR_BLIND_CTX);
184+
ctx_hasher.update(&ino.to_le_bytes());
185+
ctx_hasher.update(&self.volume_uuid);
186+
let subkey = *ctx_hasher.finalize().as_bytes();
187+
let mut h = Hasher::new_keyed(&subkey);
188+
h.update(name);
189+
*h.finalize().as_bytes()
190+
}
191+
139192
/// Zaszyfruj blok danych z pełnym AAD (ino, block_idx, volume_uuid).
140193
/// Użyj tej metody dla bloków danych — nie `encrypt_with_key` bez kontekstu.
141194
pub fn encrypt_block(

0 commit comments

Comments
 (0)