use serde::{Deserialize, Serialize};
use crate::constants::*;
use crate::error::{CryptoError, FormatError};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum EncryptionMode {
Standard,
Cascade,
}
#[derive(Debug, Clone, Copy)]
pub struct HeaderFlags {
pub cascade_mode: bool,
}
impl HeaderFlags {
pub fn to_byte(self) -> u8 {
let mut flags = 0u8;
if self.cascade_mode {
flags |= 0x01;
}
flags
}
pub fn from_byte(byte: u8) -> Self {
Self {
cascade_mode: byte & 0x01 != 0,
}
}
}
#[derive(Debug, Clone)]
pub struct VaultHeader {
pub magic: [u8; 10],
pub version: u8,
pub flags: HeaderFlags,
pub salt: [u8; SALT_SIZE],
pub wrapped_master_key: [u8; WRAPPED_KEY_SIZE],
pub wrapped_mac_key: [u8; WRAPPED_KEY_SIZE],
pub chunk_size: u32,
pub header_mac: [u8; MAC_SIZE],
}
impl VaultHeader {
pub fn to_bytes(&self) -> [u8; HEADER_SIZE] {
let mut buf = [0u8; HEADER_SIZE];
buf[0..10].copy_from_slice(&self.magic);
buf[10] = self.version;
buf[11] = self.flags.to_byte();
buf[12..44].copy_from_slice(&self.salt);
buf[44..84].copy_from_slice(&self.wrapped_master_key);
buf[84..124].copy_from_slice(&self.wrapped_mac_key);
buf[124..128].copy_from_slice(&self.chunk_size.to_le_bytes());
buf[HEADER_SIZE - MAC_SIZE..].copy_from_slice(&self.header_mac);
buf
}
pub fn from_bytes(data: &[u8]) -> crate::Result<Self> {
if data.len() < HEADER_SIZE {
return Err(FormatError::TooSmall {
actual: data.len(),
expected: HEADER_SIZE,
}
.into());
}
let mut magic = [0u8; 10];
magic.copy_from_slice(&data[0..10]);
if &magic != MAGIC {
return Err(FormatError::InvalidMagic.into());
}
let version = data[10];
if version != VERSION {
return Err(FormatError::UnsupportedVersion(version).into());
}
let flags = HeaderFlags::from_byte(data[11]);
let mut salt = [0u8; SALT_SIZE];
salt.copy_from_slice(&data[12..44]);
let mut wrapped_master_key = [0u8; WRAPPED_KEY_SIZE];
wrapped_master_key.copy_from_slice(&data[44..84]);
let mut wrapped_mac_key = [0u8; WRAPPED_KEY_SIZE];
wrapped_mac_key.copy_from_slice(&data[84..124]);
let chunk_size = u32::from_le_bytes([data[124], data[125], data[126], data[127]]);
let mut header_mac = [0u8; MAC_SIZE];
header_mac.copy_from_slice(&data[HEADER_SIZE - MAC_SIZE..]);
Ok(Self {
magic,
version,
flags,
salt,
wrapped_master_key,
wrapped_mac_key,
chunk_size,
header_mac,
})
}
pub fn compute_mac(&self, mac_key: &[u8]) -> [u8; MAC_SIZE] {
use hmac::{Hmac, Mac};
use sha2::Sha512;
let mut header_bytes = self.to_bytes();
header_bytes[HEADER_SIZE - MAC_SIZE..].fill(0);
let mut hmac = Hmac::<Sha512>::new_from_slice(mac_key)
.expect("HMAC key length is always valid for SHA-512");
hmac.update(&header_bytes);
let result = hmac.finalize();
let mut mac = [0u8; MAC_SIZE];
mac.copy_from_slice(&result.into_bytes());
mac
}
pub fn verify_mac(&self, mac_key: &[u8]) -> crate::Result<()> {
use hmac::{Hmac, Mac};
use sha2::Sha512;
let mut header_bytes = self.to_bytes();
header_bytes[HEADER_SIZE - MAC_SIZE..].fill(0);
let mut hmac = Hmac::<Sha512>::new_from_slice(mac_key)
.expect("HMAC key length is always valid for SHA-512");
hmac.update(&header_bytes);
hmac.verify_slice(&self.header_mac)
.map_err(|_| CryptoError::HeaderMacMismatch)?;
Ok(())
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ManifestEntry {
pub encrypted_name: String,
#[serde(default, skip_serializing)]
pub name: String,
pub size: u64,
pub offset: u64,
pub chunk_count: u32,
#[serde(default)]
pub is_dir: bool,
pub modified: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VaultManifest {
pub created: String,
pub modified: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
pub entries: Vec<ManifestEntry>,
}