|
|
@ -1,57 +1,135 @@ |
|
|
|
use lzma::LzmaError; |
|
|
|
use std::error::Error; |
|
|
|
use std::fmt; |
|
|
|
use std::path::PathBuf; |
|
|
|
use thiserror::Error; |
|
|
|
use crate::bottle_cap::BottleType; |
|
|
|
|
|
|
|
/// std::io::Error is too annoying
|
|
|
|
#[derive(Debug)] |
|
|
|
#[derive(Error, Debug)] |
|
|
|
#[non_exhaustive] |
|
|
|
pub enum BottleError { |
|
|
|
IoError(std::io::Error), |
|
|
|
#[error("I/O error: {:?}{}",
|
|
|
|
.0.kind(), |
|
|
|
.0.get_ref().map(|inner| format!(": {}", inner)).unwrap_or_else(|| "".to_string()) |
|
|
|
)] |
|
|
|
IoError(#[from] std::io::Error), |
|
|
|
|
|
|
|
#[error("{0}: {1:?}")] |
|
|
|
FileError(String, std::io::ErrorKind), |
|
|
|
|
|
|
|
#[error("Bad magic number")] |
|
|
|
BadMagic, |
|
|
|
|
|
|
|
#[error("Unknown bottle version")] |
|
|
|
UnknownVersion, |
|
|
|
|
|
|
|
#[error("Unknown bottle type")] |
|
|
|
UnknownBottleType, |
|
|
|
|
|
|
|
#[error("Bottle header too large")] |
|
|
|
HeaderTooLarge, |
|
|
|
|
|
|
|
#[error("Bad CRC32C: got {got:x}, expected {expected:x}")] |
|
|
|
BadCrc { expected: u32, got: u32 }, |
|
|
|
|
|
|
|
#[error("Corrupt stream (bad signal)")] |
|
|
|
CorruptStream, |
|
|
|
|
|
|
|
#[error("Internal error: previous stream was not closed")] |
|
|
|
InvalidBottleState, |
|
|
|
|
|
|
|
#[error("Wrong bottle type: got {got:?}, expected {expected:?}")] |
|
|
|
WrongBottleType { expected: BottleType, got: BottleType }, |
|
|
|
|
|
|
|
#[error("Corrupt bottle: incorrect stream type(s) for bottle")] |
|
|
|
WrongStreamType, |
|
|
|
|
|
|
|
#[error("Extra stream in bottle")] |
|
|
|
UnexpectedStream, |
|
|
|
|
|
|
|
#[error("Bottle is missing a mandatory header")] |
|
|
|
MissingHeader, |
|
|
|
NoStream, // internal error
|
|
|
|
|
|
|
|
// signatures:
|
|
|
|
#[error("Internal error: No stream has been opened in this bottle")] |
|
|
|
NoStream, |
|
|
|
|
|
|
|
|
|
|
|
// ----- signatures:
|
|
|
|
|
|
|
|
#[error("Not an Ed25519 key")] |
|
|
|
NotAnEd25519Key, |
|
|
|
|
|
|
|
#[error("Error decoding ssh key format")] |
|
|
|
SshEncodingError, |
|
|
|
|
|
|
|
#[error("Not an ssh key file: {0}")] |
|
|
|
InvalidSshFile(String), |
|
|
|
UnsupportedSshFileEncryption(String, String), |
|
|
|
|
|
|
|
#[error("Unsupported encryption ({kind}) in ssh file: {filename}")] |
|
|
|
UnsupportedSshFileEncryption { filename: String, kind: String }, |
|
|
|
|
|
|
|
#[error("Password required for this SSH key file")] |
|
|
|
SshPasswordRequired, |
|
|
|
|
|
|
|
#[error("Error encrypting message with Ed25519 key material")] |
|
|
|
DryocBoxError, |
|
|
|
|
|
|
|
// compressed bottles:
|
|
|
|
|
|
|
|
// ----- compressed bottles:
|
|
|
|
|
|
|
|
#[error("Unknown compression")] |
|
|
|
UnknownCompression, |
|
|
|
|
|
|
|
#[error("Error compressing data")] |
|
|
|
CompressionError, // probably an internal error in the compressor
|
|
|
|
|
|
|
|
#[error("LZMA error: {0}")] |
|
|
|
Lzma2Error(LzmaError), |
|
|
|
|
|
|
|
// encrypted bottles:
|
|
|
|
|
|
|
|
// ----- encrypted bottles:
|
|
|
|
|
|
|
|
#[error("No CPRNG random bytes available")] |
|
|
|
NoRandom, |
|
|
|
|
|
|
|
#[error("Unknown encryption")] |
|
|
|
UnknownEncryption, |
|
|
|
|
|
|
|
#[error("Bad key size for encryption algorithm")] |
|
|
|
BadKeySize, |
|
|
|
|
|
|
|
#[error("No key or password provided for encrypted bottle")] |
|
|
|
RequiresKey, |
|
|
|
|
|
|
|
#[error("Password provided but encrypted bottle is not password protected")] |
|
|
|
NotPasswordEncrypted, |
|
|
|
|
|
|
|
#[error("Internal error: Argon2 key generation error: {0}")] |
|
|
|
Argon2Error(argonautica::Error), // probably an internal error
|
|
|
|
|
|
|
|
#[error("Public key in encrypted bottle is corrupted")] |
|
|
|
CorruptPublicKey, |
|
|
|
|
|
|
|
#[error("No public key that can decode this bottle (encrypted for: {})", .0.join(", "))] |
|
|
|
NoMatchingPublicKey(Vec<String>), |
|
|
|
|
|
|
|
#[error("Encryption engine failure (wrong password or key?)")] |
|
|
|
CipherError, |
|
|
|
|
|
|
|
// file bottles:
|
|
|
|
|
|
|
|
// ----- file bottles:
|
|
|
|
|
|
|
|
#[error("File archive is missing some blocks (corrupted)")] |
|
|
|
IncompleteFileArchive, |
|
|
|
|
|
|
|
#[error("Unknown hash type for file blocking")] |
|
|
|
UnknownHashType, |
|
|
|
|
|
|
|
#[error("Invalid path for adding to archive (contains . or ..): {0:?}")] |
|
|
|
InvalidAddPath(PathBuf), |
|
|
|
|
|
|
|
#[error("Invalid path (failed to parse: OS error?)")] |
|
|
|
BadPath, |
|
|
|
|
|
|
|
#[error("Paths would resolve to the same relative path in the archive: {0:?}, {1:?}")] |
|
|
|
DuplicatePaths(PathBuf, PathBuf), |
|
|
|
} |
|
|
|
|
|
|
@ -61,81 +139,4 @@ impl BottleError { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
impl Error for BottleError { |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
impl From<std::io::Error> for BottleError { |
|
|
|
fn from(error: std::io::Error) -> BottleError { |
|
|
|
BottleError::IoError(error) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
impl fmt::Display for BottleError { |
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
|
|
|
match self { |
|
|
|
Self::IoError(e) => { |
|
|
|
if let Some(inner) = e.get_ref() { |
|
|
|
if e.kind() == std::io::ErrorKind::Other { |
|
|
|
write!(f, "{}", inner)? |
|
|
|
} else { |
|
|
|
write!(f, "I/O error: {:?}: {}", e.kind(), inner)? |
|
|
|
} |
|
|
|
} else { |
|
|
|
write!(f, "I/O error: {:?}", e.kind())? |
|
|
|
} |
|
|
|
}, |
|
|
|
Self::FileError(filename, e) => write!(f, "{}: {:?}", filename, e)?, |
|
|
|
Self::BadMagic => write!(f, "Bad magic number")?, |
|
|
|
Self::UnknownVersion => write!(f, "Unknown bottle version")?, |
|
|
|
Self::UnknownBottleType => write!(f, "Unknown bottle type")?, |
|
|
|
Self::HeaderTooLarge => write!(f, "Bottle header too large")?, |
|
|
|
Self::BadCrc { expected, got } => write!(f, "Bad CRC32C: got {:x}, expected {:x}", got, expected)?, |
|
|
|
Self::CorruptStream => write!(f, "Corrupt stream (bad signal)")?, |
|
|
|
Self::InvalidBottleState => write!(f, "Internal error: previous stream was not closed")?, |
|
|
|
Self::WrongBottleType { expected, got } => { |
|
|
|
write!(f, "Wrong bottle type: got {:?}, expected {:?}", got, expected)? |
|
|
|
}, |
|
|
|
Self::WrongStreamType => write!(f, "Corrupt bottle: incorrect stream type(s) for bottle")?, |
|
|
|
Self::UnexpectedStream => write!(f, "Extra stream in bottle")?, |
|
|
|
Self::MissingHeader => write!(f, "Bottle is missing a mandatory header")?, |
|
|
|
Self::NoStream => write!(f, "No stream has been opened in this bottle")?, |
|
|
|
Self::NotAnEd25519Key => write!(f, "Not an Ed25519 key")?, |
|
|
|
Self::SshEncodingError => write!(f, "Error decoding ssh key format")?, |
|
|
|
Self::InvalidSshFile(filename) => write!(f, "Not an ssh key file: {}", filename)?, |
|
|
|
Self::UnsupportedSshFileEncryption(filename, kind) => { |
|
|
|
write!(f, "Unsupported encryption ({}) in ssh file: {}", kind, filename)? |
|
|
|
}, |
|
|
|
Self::SshPasswordRequired => write!(f, "Password required for this SSH key file")?, |
|
|
|
Self::DryocBoxError => write!(f, "Error encrypting message with Ed25519 key material")?, |
|
|
|
Self::UnknownCompression => write!(f, "Unknown compression")?, |
|
|
|
Self::CompressionError => write!(f, "Error compressing data")?, |
|
|
|
Self::Lzma2Error(e) => write!(f, "LZMA error: {}", e)?, |
|
|
|
Self::NoRandom => write!(f, "No CPRNG random bytes available")?, |
|
|
|
Self::UnknownEncryption => write!(f, "Unknown encryption")?, |
|
|
|
Self::BadKeySize => write!(f, "Bad key size for encryption algorithm")?, |
|
|
|
Self::RequiresKey => write!(f, "No key or password provided for encrypted bottle")?, |
|
|
|
Self::NotPasswordEncrypted => { |
|
|
|
write!(f, "Password provided but encrypted bottle is not password protected")? |
|
|
|
}, |
|
|
|
Self::Argon2Error(e) => write!(f, "Argon2 key generation error: {}", e)?, |
|
|
|
Self::CorruptPublicKey => write!(f, "Public key in encrypted bottle is corrupted")?, |
|
|
|
Self::NoMatchingPublicKey(names) => { |
|
|
|
write!(f, "No public key that can decode this bottle (encrypted for: {})", names.join(", "))? |
|
|
|
}, |
|
|
|
Self::CipherError => write!(f, "Encryption engine failure (wrong password or key?)")?, |
|
|
|
Self::IncompleteFileArchive => write!(f, "File archive is missing some blocks (corrupted)")?, |
|
|
|
Self::UnknownHashType => write!(f, "Unknown hash type for file blocking")?, |
|
|
|
Self::InvalidAddPath(path) => { |
|
|
|
write!(f, "Invalid path for adding to archive (contains . or ..): {:?}", path)? |
|
|
|
}, |
|
|
|
Self::BadPath => write!(f, "Invalid path (failed to parse: OS error?)")?, |
|
|
|
Self::DuplicatePaths(path1, path2) => { |
|
|
|
write!(f, "Paths would resolve to the same relative path in the archive: {:?}, {:?}", path1, path2)? |
|
|
|
}, |
|
|
|
}; |
|
|
|
Ok(()) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
pub type BottleResult<T> = Result<T, BottleError>; |
|
|
|