Skip to main content

shadow_crypt_shell/
errors.rs

1use shadow_crypt_core::archive::ArchiveError;
2use shadow_crypt_core::errors::{CryptError, FileError, HeaderError, KeyDerivationError};
3use std::io;
4use thiserror::Error;
5
6/// Convenience type for workflow results.
7pub type WorkflowResult<T> = Result<T, WorkflowError>;
8
9#[derive(Debug, Error)]
10pub enum WorkflowError {
11    #[error("User input error: {0}")]
12    UserInput(String),
13
14    #[error("Password error: {0}")]
15    Password(String),
16
17    #[error("File error: {0}")]
18    File(String),
19
20    #[error("I/O error: {0}")]
21    Io(#[from] io::Error),
22
23    #[error("Key derivation error: {0}")]
24    KeyDerivation(#[from] KeyDerivationError),
25
26    #[error("Salt generation error: {0}")]
27    SaltGeneration(String),
28
29    #[error("Nonce generation error: {0}")]
30    NonceGeneration(String),
31
32    #[error("Cryptography error: {0}")]
33    CryptographyError(#[from] CryptError),
34
35    #[error("Header error: {0}")]
36    HeaderError(#[from] HeaderError),
37
38    #[error("{0}")]
39    Format(#[from] FileError),
40
41    #[error("{0}")]
42    Archive(#[from] ArchiveError),
43
44    #[error("Encryption error: {0}")]
45    Encryption(String),
46
47    #[error("Decryption error: {0}")]
48    Decryption(String),
49
50    /// Like [`WorkflowError::Decryption`], but every failed file failed
51    /// authentication (wrong password, or corrupted files).
52    #[error("Decryption error: {0}")]
53    Authentication(String),
54
55    #[error("Listing error: {0}")]
56    Listing(String),
57
58    /// A failure while processing one file of a batch, keeping the
59    /// underlying error's kind intact.
60    #[error("'{filename}': {source}")]
61    PerFile {
62        filename: String,
63        source: Box<WorkflowError>,
64    },
65}
66
67impl WorkflowError {
68    pub fn per_file(filename: &str, source: WorkflowError) -> Self {
69        WorkflowError::PerFile {
70            filename: filename.to_string(),
71            source: Box::new(source),
72        }
73    }
74
75    /// True when the error means the content did not authenticate: a wrong
76    /// password, or a corrupted/tampered file. Indistinguishable by design.
77    pub fn is_authentication_failure(&self) -> bool {
78        match self {
79            WorkflowError::Format(FileError::Crypt(CryptError::DecryptionError(_))) => true,
80            WorkflowError::Authentication(_) => true,
81            WorkflowError::PerFile { source, .. } => source.is_authentication_failure(),
82            _ => false,
83        }
84    }
85
86    /// Process exit code for this error, so scripts can react:
87    /// 1 = operation failed, 2 = invalid usage or input,
88    /// 3 = authentication failure (wrong password or corrupted file).
89    pub fn exit_code(&self) -> i32 {
90        if self.is_authentication_failure() {
91            return 3;
92        }
93        match self {
94            WorkflowError::UserInput(_) | WorkflowError::Password(_) => 2,
95            WorkflowError::PerFile { source, .. } => source.exit_code(),
96            _ => 1,
97        }
98    }
99}