pub const SALT_LEN: usize = 64;
pub const HEADER_LEN: usize = 448;
pub const VOLUME_HEADER_LEN: usize = SALT_LEN + HEADER_LEN;
pub const NORMAL_HEADER_OFFSET: u64 = 0;
pub const HIDDEN_HEADER_OFFSET: u64 = 65_536;
pub const MAGIC_VERA: &[u8; 4] = b"VERA";
pub const MAGIC_TRUE: &[u8; 4] = b"TRUE";
fn be_u32(d: &[u8], o: usize) -> u32 {
let mut b = [0u8; 4];
if let Some(s) = d.get(o..o + 4) {
b.copy_from_slice(s);
}
u32::from_be_bytes(b)
}
fn be_u64(d: &[u8], o: usize) -> u64 {
let mut b = [0u8; 8];
if let Some(s) = d.get(o..o + 8) {
b.copy_from_slice(s);
}
u64::from_be_bytes(b)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Flavor {
VeraCrypt,
TrueCrypt,
}
#[derive(Debug, Clone)]
pub struct VeraHeader {
pub flavor: Flavor,
pub version: u16,
pub encrypted_area_start: u64,
pub encrypted_area_size: u64,
pub volume_size: u64,
pub hidden_size: u64,
pub sector_size: u32,
pub master_keys: [u8; 256],
}
impl VeraHeader {
#[must_use]
pub fn validate(dec: &[u8]) -> Option<VeraHeader> {
if dec.len() < HEADER_LEN {
return None;
}
let flavor = match dec.get(0..4)? {
m if m == MAGIC_VERA => Flavor::VeraCrypt,
m if m == MAGIC_TRUE => Flavor::TrueCrypt,
_ => return None,
};
if be_u32(dec, 8) != crc32fast::hash(dec.get(192..448)?) {
return None;
}
if be_u32(dec, 188) != crc32fast::hash(dec.get(0..188)?) {
return None;
}
let mut master_keys = [0u8; 256];
master_keys.copy_from_slice(dec.get(192..448)?);
Some(VeraHeader {
flavor,
version: (be_u32(dec, 4) >> 16) as u16,
encrypted_area_start: be_u64(dec, 44),
encrypted_area_size: be_u64(dec, 52),
volume_size: be_u64(dec, 36),
hidden_size: be_u64(dec, 28),
sector_size: be_u32(dec, 64),
master_keys,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
fn valid_header(magic: &[u8; 4]) -> [u8; HEADER_LEN] {
let mut dec = [0u8; HEADER_LEN];
dec[0..4].copy_from_slice(magic);
dec[4..6].copy_from_slice(&5u16.to_be_bytes()); dec[28..36].copy_from_slice(&4096u64.to_be_bytes()); dec[36..44].copy_from_slice(&1_048_576u64.to_be_bytes()); dec[44..52].copy_from_slice(&131_072u64.to_be_bytes()); dec[52..60].copy_from_slice(&36_864u64.to_be_bytes()); dec[64..68].copy_from_slice(&512u32.to_be_bytes()); for (i, b) in dec[192..448].iter_mut().enumerate() {
*b = (i as u8) ^ 0x5a; }
let crc_mk = crc32fast::hash(&dec[192..448]);
dec[8..12].copy_from_slice(&crc_mk.to_be_bytes());
let crc_hdr = crc32fast::hash(&dec[0..188]);
dec[188..192].copy_from_slice(&crc_hdr.to_be_bytes());
dec
}
#[test]
fn validates_vera_and_true_magic() {
let v = VeraHeader::validate(&valid_header(MAGIC_VERA)).expect("VERA header");
assert_eq!(v.flavor, Flavor::VeraCrypt);
assert_eq!(v.version, 5);
assert_eq!(v.encrypted_area_start, 131_072);
assert_eq!(v.encrypted_area_size, 36_864);
assert_eq!(v.volume_size, 1_048_576);
assert_eq!(v.hidden_size, 4096);
assert_eq!(v.sector_size, 512);
let t = VeraHeader::validate(&valid_header(MAGIC_TRUE)).expect("TRUE header");
assert_eq!(t.flavor, Flavor::TrueCrypt);
}
#[test]
fn rejects_short_input() {
assert!(VeraHeader::validate(&[0u8; HEADER_LEN - 1]).is_none());
}
#[test]
fn rejects_bad_magic() {
let mut dec = valid_header(MAGIC_VERA);
dec[0] = b'X'; assert!(VeraHeader::validate(&dec).is_none());
}
#[test]
fn rejects_master_key_crc_mismatch() {
let mut dec = valid_header(MAGIC_VERA);
dec[400] ^= 0xff; assert!(VeraHeader::validate(&dec).is_none());
}
#[test]
fn rejects_header_field_crc_mismatch() {
let mut dec = valid_header(MAGIC_VERA);
dec[64] ^= 0xff; assert!(VeraHeader::validate(&dec).is_none());
}
}