use thiserror::Error;
pub type CryptoResult<T> = Result<T, CryptoError>;
#[derive(Error, Debug, Clone)]
pub enum CryptoError {
#[error("Key derivation failed: {reason}")]
KeyDerivation { reason: String },
#[error("Encryption failed: {reason}")]
Encryption { reason: String },
#[error("Decryption failed: {reason}")]
Decryption { reason: String },
#[error("Invalid key: {reason}")]
InvalidKey { reason: String },
#[error("Invalid nonce: {reason}")]
InvalidNonce { reason: String },
#[error("Invalid salt: {reason}")]
InvalidSalt { reason: String },
#[error("Random number generation failed: {reason}")]
RandomGeneration { reason: String },
#[error("Invalid input: {reason}")]
InvalidInput { reason: String },
#[error("Serialization error: {reason}")]
Serialization { reason: String },
#[error("Authentication failed - data may have been tampered with")]
AuthenticationFailed,
#[error("Cryptographic operation failed: {message}")]
Generic { message: String },
}
impl CryptoError {
pub fn key_derivation<S: Into<String>>(reason: S) -> Self {
Self::KeyDerivation {
reason: reason.into(),
}
}
pub fn encryption<S: Into<String>>(reason: S) -> Self {
Self::Encryption {
reason: reason.into(),
}
}
pub fn decryption<S: Into<String>>(reason: S) -> Self {
Self::Decryption {
reason: reason.into(),
}
}
pub fn invalid_key<S: Into<String>>(reason: S) -> Self {
Self::InvalidKey {
reason: reason.into(),
}
}
pub fn invalid_nonce<S: Into<String>>(reason: S) -> Self {
Self::InvalidNonce {
reason: reason.into(),
}
}
pub fn invalid_salt<S: Into<String>>(reason: S) -> Self {
Self::InvalidSalt {
reason: reason.into(),
}
}
pub fn random_generation<S: Into<String>>(reason: S) -> Self {
Self::RandomGeneration {
reason: reason.into(),
}
}
pub fn invalid_input<S: Into<String>>(reason: S) -> Self {
Self::InvalidInput {
reason: reason.into(),
}
}
pub fn serialization<S: Into<String>>(reason: S) -> Self {
Self::Serialization {
reason: reason.into(),
}
}
pub fn generic<S: Into<String>>(message: S) -> Self {
Self::Generic {
message: message.into(),
}
}
pub fn authentication_failed() -> Self {
Self::AuthenticationFailed
}
}
impl From<argon2::Error> for CryptoError {
fn from(err: argon2::Error) -> Self {
CryptoError::key_derivation(err.to_string())
}
}
impl From<chacha20poly1305::Error> for CryptoError {
fn from(err: chacha20poly1305::Error) -> Self {
CryptoError::encryption(err.to_string())
}
}
impl From<hex::FromHexError> for CryptoError {
fn from(err: hex::FromHexError) -> Self {
CryptoError::serialization(err.to_string())
}
}
impl From<serde_json::Error> for CryptoError {
fn from(err: serde_json::Error) -> Self {
CryptoError::serialization(err.to_string())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_creation() {
let err = CryptoError::encryption("test reason");
assert!(err.to_string().contains("test reason"));
}
#[test]
fn test_error_conversion() {
let argon_err = argon2::Error::B64Encoding(base64ct::Error::InvalidEncoding);
let crypto_err: CryptoError = argon_err.into();
assert!(matches!(crypto_err, CryptoError::KeyDerivation { .. }));
}
}