Skip to main content

origin_crypto_sdk/
error.rs

1// SPDX-License-Identifier: Apache-2.0
2
3//! Unified error type for cryptographic operations.
4//!
5//! All fallible operations in this crate return [`Result<T>`](Result),
6//! which is `std::result::Result<T, CryptoError>`.
7//!
8//! # Error categories
9//!
10//! | Variant | When it occurs |
11//! |---------|---------------|
12//! | [`CryptoError::InvalidKeyLength`] | Key bytes are wrong size for the algorithm |
13//! | [`CryptoError::InvalidKey`] | Key is malformed or fails validation |
14//! | [`CryptoError::InvalidNonceLength`] | Nonce bytes are wrong size |
15//! | [`CryptoError::AuthenticationFailed`] | Signature or AEAD tag verification failed |
16//! | [`CryptoError::Encryption`] | Encryption failed (rare — usually a bug) |
17//! | [`CryptoError::Decryption`] | Decryption failed (ciphertext tampered, wrong key, etc.) |
18//! | [`CryptoError::Kdf`] | Key derivation failed (bad params, Argon2 failure, etc.) |
19//! | [`CryptoError::Pqc`] | Post-quantum primitive failed |
20//! | [`CryptoError::ReedSolomon`] | Error correction encode/decode failed |
21//! | [`CryptoError::InvalidParameter`] | Invalid input parameter (empty seed, expired handle, etc.) |
22//! | [`CryptoError::Io`] | Underlying I/O error |
23//! | [`CryptoError::Serialization`] | Serialization or deserialization failed |
24//! | [`CryptoError::Compression`] | Compression or decompression failed |
25
26use std::fmt;
27
28/// Unified error type for cryptographic operations.
29///
30/// See [module-level documentation](self) for a description of each variant.
31#[derive(Debug)]
32pub enum CryptoError {
33    // ── Key / nonce errors (structured) ──
34
35    /// Key bytes are the wrong length for the algorithm.
36    ///
37    /// Example: passed 16 bytes to a function expecting a 32-byte key.
38    InvalidKeyLength {
39        /// Name of the algorithm (e.g. "Ed25519", "Falcon-1024").
40        algorithm: &'static str,
41        /// Required length in bytes.
42        expected: usize,
43        /// Actual length received.
44        got: usize,
45    },
46
47    /// Key is structurally valid in length but fails algorithmic validation.
48    ///
49    /// Example: Ed25519 public key does not encode a valid curve point,
50    /// or Falcon secret key fails deserialization.
51    InvalidKey(String),
52
53    /// Nonce bytes are the wrong length.
54    InvalidNonceLength {
55        /// Name of the algorithm (e.g. "XChaCha20-Poly1305").
56        algorithm: &'static str,
57        /// Required nonce length in bytes.
58        expected: usize,
59        /// Actual length received.
60        got: usize,
61    },
62
63    // ── Verification failures ──
64
65    /// Authentication tag or signature verification failed.
66    ///
67    /// This is the normal "wrong key / tampered data" error. No further
68    /// detail is given to avoid oracle attacks.
69    AuthenticationFailed,
70
71    // ── Operation failures (string detail) ──
72
73    /// Encryption failed.
74    Encryption(String),
75
76    /// Decryption failed (wrong key, tampered ciphertext, etc.).
77    Decryption(String),
78
79    /// Key derivation failed.
80    Kdf(String),
81
82    /// Post-quantum primitive operation failed.
83    Pqc(String),
84
85    /// Reed-Solomon encode/decode error.
86    ReedSolomon(String),
87
88    /// Invalid input parameter.
89    InvalidParameter(String),
90
91    /// Serialization or deserialization error.
92    Serialization(String),
93
94    /// Compression or decompression error.
95    Compression(String),
96
97    /// Underlying I/O error.
98    Io(std::io::Error),
99}
100
101impl fmt::Display for CryptoError {
102    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103        match self {
104            CryptoError::InvalidKeyLength {
105                algorithm,
106                expected,
107                got,
108            } => write!(
109                f,
110                "Invalid key length for {algorithm}: expected {expected} bytes, got {got}"
111            ),
112            CryptoError::InvalidKey(msg) => write!(f, "Invalid key: {msg}"),
113            CryptoError::InvalidNonceLength {
114                algorithm,
115                expected,
116                got,
117            } => write!(
118                f,
119                "Invalid nonce length for {algorithm}: expected {expected} bytes, got {got}"
120            ),
121            CryptoError::AuthenticationFailed => write!(
122                f,
123                "Authentication failed (wrong key, tampered data, or invalid signature)"
124            ),
125            CryptoError::Encryption(msg) => write!(f, "Encryption failed: {msg}"),
126            CryptoError::Decryption(msg) => write!(f, "Decryption failed: {msg}"),
127            CryptoError::Kdf(msg) => write!(f, "KDF error: {msg}"),
128            CryptoError::Pqc(msg) => write!(f, "PQC error: {msg}"),
129            CryptoError::ReedSolomon(msg) => write!(f, "Reed-Solomon error: {msg}"),
130            CryptoError::InvalidParameter(msg) => write!(f, "Invalid parameter: {msg}"),
131            CryptoError::Serialization(msg) => write!(f, "Serialization error: {msg}"),
132            CryptoError::Compression(msg) => write!(f, "Compression error: {msg}"),
133            CryptoError::Io(err) => write!(f, "I/O error: {err}"),
134        }
135    }
136}
137
138impl std::error::Error for CryptoError {
139    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
140        match self {
141            CryptoError::Io(err) => Some(err),
142            _ => None,
143        }
144    }
145}
146
147impl From<std::io::Error> for CryptoError {
148    fn from(err: std::io::Error) -> Self {
149        CryptoError::Io(err)
150    }
151}
152
153impl Clone for CryptoError {
154    fn clone(&self) -> Self {
155        match self {
156            CryptoError::InvalidKeyLength {
157                algorithm,
158                expected,
159                got,
160            } => CryptoError::InvalidKeyLength {
161                algorithm,
162                expected: *expected,
163                got: *got,
164            },
165            CryptoError::InvalidKey(msg) => CryptoError::InvalidKey(msg.clone()),
166            CryptoError::InvalidNonceLength {
167                algorithm,
168                expected,
169                got,
170            } => CryptoError::InvalidNonceLength {
171                algorithm,
172                expected: *expected,
173                got: *got,
174            },
175            CryptoError::AuthenticationFailed => CryptoError::AuthenticationFailed,
176            CryptoError::Encryption(msg) => CryptoError::Encryption(msg.clone()),
177            CryptoError::Decryption(msg) => CryptoError::Decryption(msg.clone()),
178            CryptoError::Kdf(msg) => CryptoError::Kdf(msg.clone()),
179            CryptoError::Pqc(msg) => CryptoError::Pqc(msg.clone()),
180            CryptoError::ReedSolomon(msg) => CryptoError::ReedSolomon(msg.clone()),
181            CryptoError::InvalidParameter(msg) => CryptoError::InvalidParameter(msg.clone()),
182            CryptoError::Serialization(msg) => CryptoError::Serialization(msg.clone()),
183            CryptoError::Compression(msg) => CryptoError::Compression(msg.clone()),
184            CryptoError::Io(err) => CryptoError::Io(std::io::Error::new(
185                err.kind(),
186                err.to_string(),
187            )),
188        }
189    }
190}
191
192/// Result alias for all fallible operations in this crate.
193pub type Result<T> = std::result::Result<T, CryptoError>;
194
195#[cfg(test)]
196mod tests {
197    use super::*;
198
199    #[test]
200    fn test_invalid_key_length_display() {
201        let err = CryptoError::InvalidKeyLength {
202            algorithm: "Falcon-1024",
203            expected: 32,
204            got: 16,
205        };
206        let msg = err.to_string();
207        assert!(msg.contains("Falcon-1024"));
208        assert!(msg.contains("expected 32"));
209        assert!(msg.contains("got 16"));
210    }
211
212    #[test]
213    fn test_auth_failed_display() {
214        let err = CryptoError::AuthenticationFailed;
215        assert!(err.to_string().contains("Authentication failed"));
216    }
217
218    #[test]
219    fn test_clone_roundtrip() {
220        let err = CryptoError::InvalidKeyLength {
221            algorithm: "Ed25519",
222            expected: 32,
223            got: 31,
224        };
225        let cloned = err.clone();
226        assert_eq!(err.to_string(), cloned.to_string());
227    }
228
229    #[test]
230    fn test_io_from() {
231        let io_err = std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "truncated");
232        let crypto_err: CryptoError = io_err.into();
233        assert!(matches!(crypto_err, CryptoError::Io(_)));
234        assert!(crypto_err.to_string().contains("truncated"));
235    }
236}