forensic_vfs/encryption.rs
1//! [`EncryptionLayer`] — full-disk-encryption translation, a distinct layer between
2//! volume and filesystem.
3//!
4//! BitLocker/LUKS/FileVault sit between a volume and its filesystem; the resolver
5//! (in the engine) probes for them by on-disk header magic. Credentials are
6//! supplied at resolve time through an injected [`CredentialSource`], never stored
7//! in a [`crate::pathspec::PathSpec`], so a serialized address never leaks keys.
8
9use crate::error::VfsResult;
10use crate::source::DynSource;
11
12/// The FDE scheme.
13#[non_exhaustive]
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
15pub enum EncryptionScheme {
16 Bitlocker,
17 Luks1,
18 Luks2,
19 FileVault,
20 ApfsEncrypted,
21 /// VeraCrypt / TrueCrypt full-volume encryption (XTS, optional cipher cascade).
22 VeraCrypt,
23}
24
25/// One credential offered to a encryption layer.
26#[non_exhaustive]
27#[derive(Debug, Clone)]
28pub enum Credential {
29 Password(String),
30 /// BitLocker numeric recovery key or LUKS passphrase file contents.
31 RecoveryKey(String),
32 /// Raw key material (FVEK / volume master key).
33 KeyBytes(Vec<u8>),
34 /// A keyfile path the provider will read.
35 KeyFile(std::path::PathBuf),
36}
37
38/// Supplies credentials at resolve time. Injected into the resolve call, kept out
39/// of the serialized address, so a `PathSpec` is safe to persist and re-open
40/// (the caller re-supplies credentials on re-open).
41pub trait CredentialSource: Send + Sync {
42 /// Offer credentials for a target (a volume GUID / label / scheme name). An
43 /// empty slice means "none available" — the layer then errors
44 /// [`crate::error::VfsError::NeedCredentials`] rather than guessing.
45 fn credentials_for(&self, scheme: EncryptionScheme, target: &str) -> Vec<Credential>;
46}
47
48/// An encryption translation over one [`crate::ImageSource`]: consumes credentials +
49/// ciphertext sectors, presents a decrypted [`DynSource`].
50pub trait EncryptionLayer: Send + Sync {
51 fn scheme(&self) -> EncryptionScheme;
52 /// Present the decrypted volume. Errs `NeedCredentials` if keys are absent,
53 /// `Decode` (loud, with the header bytes) on a bad key / unsupported cipher.
54 fn open(&self, creds: &dyn CredentialSource) -> VfsResult<DynSource>;
55
56 /// Findings raised while parsing the FDE header. Behind the `findings`
57 /// feature so a bare reader does not inherit forensicnomicon.
58 #[cfg(feature = "findings")]
59 fn findings(&self) -> VfsResult<Vec<forensicnomicon::report::Finding>> {
60 Ok(Vec::new())
61 }
62}