aescrypt_rs/error.rs
1//! Error types for AES Crypt operations.
2//!
3//! Every fallible function in this crate returns
4//! [`Result<T, AescryptError>`](AescryptError). [`AescryptError`] discriminates between
5//! I/O failures, cryptographic failures, header / extension parsing failures, and
6//! unsupported file format versions.
7//!
8//! # Variant → API table
9//!
10//! | Variant | Typical producer |
11//! | -------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
12//! | [`AescryptError::Io`] | [`encrypt`], [`decrypt`], [`read_version`], every reader/writer helper in [`crate::encryption`] / [`crate::decryption`] |
13//! | [`AescryptError::Crypto`] | [`derive_ackdf_key`], [`derive_pbkdf2_key`], [`Pbkdf2Builder::derive_secure`], [`utf8_to_utf16le`] |
14//! | [`AescryptError::Header`] | [`encrypt`], [`decrypt`], [`read_version`], [`derive_setup_key`], [`write_header`] / [`write_extensions`] / [`write_iterations`], [`read_file_version`], [`read_kdf_iterations`], [`consume_all_extensions`], [`extract_session_data`], [`decrypt_ciphertext_stream`] |
15//! | [`AescryptError::UnsupportedVersion`] | [`write_header`], [`write_extensions`], [`write_iterations`], [`read_file_version`] |
16//!
17//! [`encrypt`]: crate::encrypt()
18//! [`decrypt`]: crate::decrypt()
19//! [`read_version`]: crate::read_version
20//! [`derive_ackdf_key`]: crate::derive_ackdf_key
21//! [`derive_pbkdf2_key`]: crate::derive_pbkdf2_key
22//! [`Pbkdf2Builder::derive_secure`]: crate::Pbkdf2Builder::derive_secure
23//! [`utf8_to_utf16le`]: crate::utilities::utf8_to_utf16le
24//! [`derive_setup_key`]: crate::encryption::derive_setup_key
25//! [`write_header`]: crate::encryption::write_header
26//! [`write_extensions`]: crate::encryption::write_extensions
27//! [`write_iterations`]: crate::encryption::write_iterations
28//! [`read_file_version`]: crate::decryption::read_file_version
29//! [`read_kdf_iterations`]: crate::decryption::read_kdf_iterations
30//! [`consume_all_extensions`]: crate::decryption::consume_all_extensions
31//! [`extract_session_data`]: crate::decryption::extract_session_data
32//! [`decrypt_ciphertext_stream`]: crate::decryption::decrypt_ciphertext_stream
33
34use thiserror::Error;
35
36/// The error type returned by every fallible AES Crypt operation in this crate.
37///
38/// `AescryptError` is non-exhaustive in spirit: it discriminates four classes of
39/// failure (I/O, cryptographic, header/format, unsupported version) but the
40/// human-readable message inside [`Crypto`](Self::Crypto) and
41/// [`Header`](Self::Header) is part of the error display, not the structured API,
42/// and may be refined in patch releases.
43///
44/// # Errors
45///
46/// All four variants are constructed by code inside this crate; downstream callers
47/// generally pattern-match on the variant and surface a friendly message based on
48/// the [`Display`](std::fmt::Display) impl provided by [`thiserror`].
49///
50/// See the [variant → API table](self) at the module level for which public APIs
51/// produce each variant.
52///
53/// # Security
54///
55/// Error messages are written for human diagnostics. They never embed the
56/// password, derived keys, IVs, salts, or plaintext. Untrusted callers may safely
57/// log the [`Display`](std::fmt::Display) form. Wrap-and-`?` is the recommended
58/// pattern; do not attempt to recover from [`Header`](Self::Header) by retrying
59/// with different inputs.
60#[derive(Error, Debug)]
61pub enum AescryptError {
62 /// An I/O operation on the underlying reader or writer failed.
63 ///
64 /// This variant wraps [`std::io::Error`] verbatim and is produced by every
65 /// public function that performs streaming reads or writes — including
66 /// [`crate::encrypt()`], [`crate::decrypt()`], [`crate::read_version`], and
67 /// the lower-level helpers in [`crate::encryption`] / [`crate::decryption`].
68 /// Common causes: file not found, permission denied, broken pipe, premature
69 /// EOF inside the header / session block / payload.
70 #[error("I/O error: {0}")]
71 Io(#[from] std::io::Error),
72
73 /// A cryptographic primitive returned an error.
74 ///
75 /// Produced by:
76 ///
77 /// - [`crate::derive_pbkdf2_key`] / [`crate::Pbkdf2Builder::derive_secure`]
78 /// when the underlying `pbkdf2` crate rejects its parameters.
79 /// - [`crate::derive_ackdf_key`] when the password is not valid UTF-8
80 /// (forwarded from [`crate::utilities::utf8_to_utf16le`]).
81 /// - [`crate::utilities::utf8_to_utf16le`] for non-UTF-8 password bytes.
82 ///
83 /// The wrapped `String` is a short human-readable description and is part of
84 /// the [`Display`](std::fmt::Display) output only — it is not a stable
85 /// machine-readable code.
86 #[error("Crypto error: {0}")]
87 Crypto(String),
88
89 /// A header, extension, or trailer in the AES Crypt file failed validation.
90 ///
91 /// Triggered by, for example:
92 ///
93 /// - Invalid magic bytes (header is not `b"AES"`).
94 /// - Reserved byte after the version is not `0x00` for v1–v3.
95 /// - More than 256 extension blocks in a v2/v3 header (DoS guard).
96 /// - PBKDF2 iteration count outside
97 /// [`PBKDF2_MIN_ITER`](crate::constants::PBKDF2_MIN_ITER)
98 /// `..=` [`PBKDF2_MAX_ITER`](crate::constants::PBKDF2_MAX_ITER).
99 /// - Empty password supplied to [`crate::encrypt()`].
100 /// - Session-block HMAC mismatch ("session data corrupted or tampered").
101 /// - Payload HMAC mismatch ("HMAC verification failed").
102 /// - v3 PKCS#7 padding malformed ("v3: invalid PKCS#7 padding").
103 /// - v0/v1/v2/v3 trailer length wrong ("expected … trailer").
104 ///
105 /// **Security note**: an HMAC failure is reported as `Header(...)` for
106 /// historical reasons; treat it as authenticated-decryption failure and
107 /// discard any plaintext already written to the output.
108 #[error("Header error: {0}")]
109 Header(String),
110
111 /// The file declares an AES Crypt format version this crate cannot handle.
112 ///
113 /// Returned by [`crate::decryption::read_file_version`] when the version
114 /// byte is `> 3`, and by the encryption-side [`crate::encryption::write_header`]
115 /// / [`write_extensions`](crate::encryption::write_extensions) /
116 /// [`write_iterations`](crate::encryption::write_iterations) when callers
117 /// request a version `< 3` (this crate writes v3 only). The contained `u8`
118 /// is the rejected version number.
119 #[error("Unsupported version: {0}")]
120 UnsupportedVersion(u8),
121}
122
123impl From<&'static str> for AescryptError {
124 fn from(msg: &'static str) -> Self {
125 AescryptError::Crypto(msg.to_string())
126 }
127}