veracrypt_forensic/
lib.rs1#![forbid(unsafe_code)]
8#![cfg_attr(test, allow(clippy::unwrap_used, clippy::expect_used))]
9
10use veracrypt::{Flavor, VolumeInfo};
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
14pub enum Severity {
15 Info,
17 Low,
19 Medium,
21}
22
23#[derive(Debug, Clone, PartialEq, Eq)]
25pub struct Anomaly {
26 pub severity: Severity,
28 pub code: &'static str,
30 pub note: String,
32}
33
34#[must_use]
36pub fn audit(info: &VolumeInfo, hidden_size: u64) -> Vec<Anomaly> {
37 let mut out = Vec::new();
38
39 if info.flavor == Flavor::TrueCrypt {
40 out.push(Anomaly {
41 severity: Severity::Low,
42 code: "VC-LEGACY-TRUECRYPT",
43 note: "volume is a legacy TrueCrypt (not VeraCrypt) container".to_string(),
44 });
45 }
46
47 if hidden_size != 0 {
48 out.push(Anomaly {
49 severity: Severity::Medium,
50 code: "VC-HIDDEN-VOLUME-DECLARED",
51 note: format!(
52 "outer header declares a hidden volume of {hidden_size} bytes (deniable-encryption indicator)"
53 ),
54 });
55 }
56
57 out.push(Anomaly {
58 severity: Severity::Info,
59 code: "VC-CIPHER-INVENTORY",
60 note: format!(
61 "PRF {}, cipher {}, data area at byte {}",
62 info.prf.name(),
63 info.cipher.name(),
64 info.encrypted_area_start
65 ),
66 });
67
68 out
69}
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74 use veracrypt::{Cipher, Prf};
75
76 fn info(flavor: Flavor) -> VolumeInfo {
77 VolumeInfo {
78 flavor,
79 prf: Prf::Sha512,
80 cipher: Cipher::Aes,
81 version: 5,
82 encrypted_area_start: 131_072,
83 encrypted_area_size: 36_864,
84 }
85 }
86
87 #[test]
88 fn veracrypt_clean_only_inventory() {
89 let a = audit(&info(Flavor::VeraCrypt), 0);
90 assert_eq!(a.len(), 1);
91 assert_eq!(a[0].code, "VC-CIPHER-INVENTORY");
92 }
93
94 #[test]
95 fn flags_truecrypt_and_hidden() {
96 let a = audit(&info(Flavor::TrueCrypt), 4096);
97 let codes: Vec<_> = a.iter().map(|x| x.code).collect();
98 assert!(codes.contains(&"VC-LEGACY-TRUECRYPT"));
99 assert!(codes.contains(&"VC-HIDDEN-VOLUME-DECLARED"));
100 }
101}