nonce_auth/nonce/error.rs
1use thiserror::Error;
2
3/// Error types that can occur during nonce authentication operations.
4///
5/// This enum represents all possible errors that can occur when using
6/// the nonce authentication library. Each variant corresponds to a
7/// specific failure mode in the authentication process.
8///
9/// # Error Categories
10///
11/// - **Authentication Errors**: `DuplicateNonce`, `ExpiredNonce`, `InvalidSignature`, `TimestampOutOfWindow`
12/// - **System Errors**: `DatabaseError`, `CryptoError`
13///
14/// # Example
15///
16/// ```rust
17/// use nonce_auth::{NonceServer, NonceError, NonceClient, storage::MemoryStorage};
18/// use hmac::Mac;
19/// use std::sync::Arc;
20///
21/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
22/// let storage = Arc::new(MemoryStorage::new());
23/// let server = NonceServer::new(b"secret", storage, None, None);
24/// server.init().await?;
25/// let client = NonceClient::new(b"secret");
26/// let protection_data = client.create_protection_data(|mac, timestamp, nonce| {
27/// mac.update(timestamp.as_bytes());
28/// mac.update(nonce.as_bytes());
29/// })?;
30///
31/// // Handle different error types
32/// match server.verify_protection_data(&protection_data, None, |mac| {
33/// mac.update(protection_data.timestamp.to_string().as_bytes());
34/// mac.update(protection_data.nonce.as_bytes());
35/// }).await {
36/// Ok(()) => println!("Request verified"),
37/// Err(NonceError::DuplicateNonce) => println!("Nonce already used"),
38/// Err(NonceError::InvalidSignature) => println!("Invalid signature"),
39/// Err(NonceError::TimestampOutOfWindow) => println!("Request too old"),
40/// Err(e) => println!("Other error: {e}"),
41/// }
42/// # Ok(())
43/// # }
44/// ```
45#[derive(Error, Debug)]
46pub enum NonceError {
47 /// The nonce has already been used and cannot be reused.
48 ///
49 /// This error occurs when a client attempts to use a nonce that has
50 /// already been consumed by the server. This is the primary mechanism
51 /// for preventing replay attacks.
52 ///
53 /// # When This Occurs
54 ///
55 /// - A client sends the same signed request twice
56 /// - A malicious actor attempts to replay a captured request
57 /// - Network issues cause duplicate request delivery
58 ///
59 /// # Resolution
60 ///
61 /// The client should generate a new signed request with a fresh nonce.
62 #[error("Nonce already exists")]
63 DuplicateNonce,
64
65 /// The nonce has expired and is no longer valid.
66 ///
67 /// This error occurs when a nonce exists in the database but has
68 /// exceeded its time-to-live (TTL) duration. Expired nonces are
69 /// considered invalid and should be cleaned up.
70 ///
71 /// # When This Occurs
72 ///
73 /// - A client uses a very old signed request
74 /// - The server's TTL is set too short for the use case
75 /// - There are significant delays in request processing
76 ///
77 /// # Resolution
78 ///
79 /// The client should generate a new signed request with a fresh nonce.
80 #[error("Nonce expired")]
81 ExpiredNonce,
82
83 /// The HMAC signature verification failed.
84 ///
85 /// This error occurs when the provided signature doesn't match the
86 /// expected signature calculated by the server. This indicates either
87 /// a tampered request or mismatched secrets.
88 ///
89 /// # When This Occurs
90 ///
91 /// - Client and server are using different secret keys
92 /// - The request has been tampered with in transit
93 /// - There's a bug in the signature generation/verification logic
94 /// - The timestamp or nonce values have been modified
95 ///
96 /// # Resolution
97 ///
98 /// - Verify that client and server use the same secret key
99 /// - Check for request tampering or transmission errors
100 /// - Ensure proper signature generation on the client side
101 #[error("Invalid signature")]
102 InvalidSignature,
103
104 /// The request timestamp is outside the allowed time window.
105 ///
106 /// This error occurs when the timestamp in the signed request is
107 /// either too old or too far in the future compared to the server's
108 /// current time, exceeding the configured time window.
109 ///
110 /// # When This Occurs
111 ///
112 /// - Client and server clocks are significantly out of sync
113 /// - Network delays cause old requests to arrive late
114 /// - The time window is configured too strictly
115 /// - A malicious actor attempts to use very old captured requests
116 ///
117 /// # Resolution
118 ///
119 /// - Synchronize client and server clocks (e.g., using NTP)
120 /// - Increase the time window if appropriate for your use case
121 /// - Generate fresh requests closer to when they'll be sent
122 #[error("Timestamp out of window")]
123 TimestampOutOfWindow,
124
125 /// A database operation failed.
126 ///
127 /// This error occurs when there's a problem with the underlying
128 /// SQLite database operations, such as connection issues, disk
129 /// space problems, or corruption.
130 ///
131 /// # When This Occurs
132 ///
133 /// - Database file is corrupted or inaccessible
134 /// - Insufficient disk space for database operations
135 /// - Database is locked by another process
136 /// - File permission issues
137 ///
138 /// # Resolution
139 ///
140 /// - Check database file permissions and disk space
141 /// - Verify database file integrity
142 /// - Ensure proper database initialization
143 /// - Check for competing database access
144 #[error("Database error: {0}")]
145 DatabaseError(String),
146
147 /// A cryptographic operation failed.
148 ///
149 /// This error occurs when there's a problem with the HMAC signature
150 /// generation or verification process, typically due to invalid
151 /// key material or system-level crypto issues.
152 ///
153 /// # When This Occurs
154 ///
155 /// - Invalid or corrupted secret key
156 /// - System-level cryptographic library issues
157 /// - Memory allocation failures during crypto operations
158 ///
159 /// # Resolution
160 ///
161 /// - Verify the secret key is valid and properly formatted
162 /// - Check system cryptographic library installation
163 /// - Ensure sufficient system resources
164 #[error("Crypto error: {0}")]
165 CryptoError(String),
166}
167
168// SQLite error conversion is now provided in examples/sqlite_storage.rs
169// since rusqlite is no longer a core dependency
170
171#[cfg(test)]
172mod tests {
173 use super::*;
174
175 #[test]
176 fn test_error_display() {
177 assert_eq!(
178 NonceError::DuplicateNonce.to_string(),
179 "Nonce already exists"
180 );
181 assert_eq!(NonceError::ExpiredNonce.to_string(), "Nonce expired");
182 assert_eq!(
183 NonceError::InvalidSignature.to_string(),
184 "Invalid signature"
185 );
186 assert_eq!(
187 NonceError::TimestampOutOfWindow.to_string(),
188 "Timestamp out of window"
189 );
190
191 let db_error = NonceError::DatabaseError("test error".to_string());
192 assert_eq!(db_error.to_string(), "Database error: test error");
193
194 let crypto_error = NonceError::CryptoError("crypto test error".to_string());
195 assert_eq!(crypto_error.to_string(), "Crypto error: crypto test error");
196 }
197
198 #[test]
199 fn test_error_debug() {
200 let error = NonceError::DuplicateNonce;
201 let debug_str = format!("{error:?}");
202 assert_eq!(debug_str, "DuplicateNonce");
203 }
204
205 #[test]
206 fn test_error_is_send_sync() {
207 fn assert_send_sync<T: Send + Sync>() {}
208 assert_send_sync::<NonceError>();
209 }
210
211 #[test]
212 fn test_error_types() {
213 // Test that all error variants can be created and displayed
214 let errors = vec![
215 NonceError::DuplicateNonce,
216 NonceError::ExpiredNonce,
217 NonceError::InvalidSignature,
218 NonceError::TimestampOutOfWindow,
219 NonceError::DatabaseError("test".to_string()),
220 NonceError::CryptoError("test".to_string()),
221 ];
222
223 for error in errors {
224 // Each error should have a non-empty string representation
225 assert!(!error.to_string().is_empty());
226 // Each error should be debug-printable
227 assert!(!format!("{error:?}").is_empty());
228 }
229 }
230}