use std::fmt;
#[derive(Debug)]
pub enum Error {
Format(FormatError),
Crypto(CryptoError),
Io(std::io::Error),
Manifest(String),
EntryNotFound(String),
PasswordPolicy(String),
InvalidPath(String),
}
#[derive(Debug)]
pub enum FormatError {
TooSmall { actual: usize, expected: usize },
InvalidMagic,
UnsupportedVersion(u8),
ManifestTooLarge(usize),
ManifestTruncated,
InvalidChunkSize(u32),
}
#[derive(Debug)]
pub enum CryptoError {
KeyDerivation(String),
KeyUnwrap,
HeaderMacMismatch,
ChunkEncrypt { chunk_index: u32 },
ChunkDecrypt { chunk_index: u32 },
SivOperation(String),
CascadeEncrypt { chunk_index: u32 },
CascadeDecrypt { chunk_index: u32 },
ManifestEncoding,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::Format(e) => write!(f, "format error: {e}"),
Error::Crypto(e) => write!(f, "crypto error: {e}"),
Error::Io(e) => write!(f, "I/O error: {e}"),
Error::Manifest(msg) => write!(f, "manifest error: {msg}"),
Error::EntryNotFound(name) => write!(f, "entry not found: {name}"),
Error::PasswordPolicy(msg) => write!(f, "password policy: {msg}"),
Error::InvalidPath(msg) => write!(f, "invalid path: {msg}"),
}
}
}
impl fmt::Display for FormatError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FormatError::TooSmall { actual, expected } => {
write!(f, "file too small ({actual} bytes, need {expected})")
}
FormatError::InvalidMagic => write!(f, "invalid magic bytes (not an AeroVault v2 file)"),
FormatError::UnsupportedVersion(v) => write!(f, "unsupported version {v}"),
FormatError::ManifestTooLarge(size) => {
write!(f, "manifest too large ({size} bytes, max 64 MiB)")
}
FormatError::ManifestTruncated => write!(f, "manifest data truncated"),
FormatError::InvalidChunkSize(size) => {
write!(f, "invalid chunk size {size} (must be 4 KiB - 16 MiB)")
}
}
}
}
impl fmt::Display for CryptoError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CryptoError::KeyDerivation(msg) => write!(f, "key derivation failed: {msg}"),
CryptoError::KeyUnwrap => write!(f, "key unwrap failed (wrong password?)"),
CryptoError::HeaderMacMismatch => write!(f, "header MAC mismatch (tampered or wrong password)"),
CryptoError::ChunkEncrypt { chunk_index } => {
write!(f, "chunk {chunk_index} encryption failed")
}
CryptoError::ChunkDecrypt { chunk_index } => {
write!(f, "chunk {chunk_index} decryption failed")
}
CryptoError::SivOperation(msg) => write!(f, "AES-SIV operation failed: {msg}"),
CryptoError::CascadeEncrypt { chunk_index } => {
write!(f, "cascade encryption failed at chunk {chunk_index}")
}
CryptoError::CascadeDecrypt { chunk_index } => {
write!(f, "cascade decryption failed at chunk {chunk_index}")
}
CryptoError::ManifestEncoding => write!(f, "manifest is not valid UTF-8"),
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Error::Io(e) => Some(e),
Error::Format(e) => Some(e),
Error::Crypto(e) => Some(e),
_ => None,
}
}
}
impl std::error::Error for FormatError {}
impl std::error::Error for CryptoError {}
impl From<std::io::Error> for Error {
fn from(e: std::io::Error) -> Self {
Error::Io(e)
}
}
impl From<FormatError> for Error {
fn from(e: FormatError) -> Self {
Error::Format(e)
}
}
impl From<CryptoError> for Error {
fn from(e: CryptoError) -> Self {
Error::Crypto(e)
}
}