1use serde::{Deserialize, Serialize};
37
38pub mod nonce;
39pub mod signature {
40 pub use crate::nonce::signature::*;
42}
43pub mod storage {
44 pub use crate::nonce::storage::*;
46}
47
48pub use nonce::{
50 BoxedCleanupStrategy,
51 CleanupStrategy,
52 ConfigPreset,
54 CredentialBuilder,
56 CredentialVerifier,
57 CustomCleanupStrategy,
58
59 HybridCleanupStrategy,
60 MacLike,
61 MemoryStorage,
63 NonceConfig,
64
65 NonceEntry,
66 NonceError,
67 NonceGeneratorFn,
68 NonceStorage,
69 SignatureAlgorithm,
71 StorageStats,
72 TimeProviderFn,
73};
74
75#[cfg(feature = "algo-hmac-sha256")]
77pub use nonce::{DefaultSignatureAlgorithm, create_default_algorithm};
78
79#[cfg(feature = "metrics")]
80pub use nonce::{
81 ErrorMetrics, InMemoryMetricsCollector, MetricEvent, MetricsCollector, MetricsTimer,
82 NoOpMetricsCollector, NonceMetrics, PerformanceMetrics,
83};
84
85#[derive(Debug, Clone, Serialize, Deserialize)]
109pub struct NonceCredential {
110 pub timestamp: u64,
111 pub nonce: String,
112 pub signature: String,
113}
114
115#[cfg(test)]
116mod tests {
117 use crate::{CredentialBuilder, CredentialVerifier, NonceError, storage::MemoryStorage};
118 use hmac::Mac;
119 use std::sync::Arc;
120
121 #[tokio::test]
122 async fn test_basic_credential_workflow() {
123 let secret = b"test_secret";
124 let payload = b"test_payload";
125 let storage: Arc<dyn crate::storage::NonceStorage> = Arc::new(MemoryStorage::new());
126
127 let credential = CredentialBuilder::new(secret).sign(payload).unwrap();
129
130 let result = CredentialVerifier::new(storage)
132 .with_secret(secret)
133 .verify(&credential, payload)
134 .await;
135
136 assert!(result.is_ok());
137 }
138
139 #[tokio::test]
140 async fn test_nonce_replay_protection() {
141 let secret = b"test_secret";
142 let payload = b"test_payload";
143 let storage: Arc<dyn crate::storage::NonceStorage> = Arc::new(MemoryStorage::new());
144
145 let credential = CredentialBuilder::new(secret).sign(payload).unwrap();
147
148 CredentialVerifier::new(Arc::clone(&storage))
150 .with_secret(secret)
151 .verify(&credential, payload)
152 .await
153 .unwrap();
154
155 let result = CredentialVerifier::new(storage)
157 .with_secret(secret)
158 .verify(&credential, payload)
159 .await;
160
161 assert!(matches!(result, Err(NonceError::DuplicateNonce)));
162 }
163
164 #[tokio::test]
165 async fn test_context_isolation() {
166 let secret = b"test_secret";
167 let payload = b"test_payload";
168 let storage: Arc<dyn crate::storage::NonceStorage> = Arc::new(MemoryStorage::new());
169
170 let credential = CredentialBuilder::new(secret).sign(payload).unwrap();
172
173 CredentialVerifier::new(Arc::clone(&storage))
175 .with_secret(secret)
176 .with_context(Some("context1"))
177 .verify(&credential, payload)
178 .await
179 .unwrap();
180
181 let result = CredentialVerifier::new(Arc::clone(&storage))
183 .with_secret(secret)
184 .with_context(Some("context2"))
185 .verify(&credential, payload)
186 .await;
187
188 assert!(result.is_ok());
189
190 let result = CredentialVerifier::new(storage)
192 .with_secret(secret)
193 .with_context(Some("context1"))
194 .verify(&credential, payload)
195 .await;
196
197 assert!(matches!(result, Err(NonceError::DuplicateNonce)));
198 }
199
200 #[tokio::test]
201 async fn test_invalid_signature() {
202 let payload = b"test_payload";
203 let storage: Arc<dyn crate::storage::NonceStorage> = Arc::new(MemoryStorage::new());
204
205 let credential = CredentialBuilder::new(b"secret1").sign(payload).unwrap();
207
208 let result = CredentialVerifier::new(storage)
210 .with_secret(b"secret2")
211 .verify(&credential, payload)
212 .await;
213
214 assert!(matches!(result, Err(NonceError::InvalidSignature)));
215 }
216
217 #[tokio::test]
218 async fn test_structured_signing() {
219 let secret = b"test_secret";
220 let storage: Arc<dyn crate::storage::NonceStorage> = Arc::new(MemoryStorage::new());
221 let components = [b"part1".as_slice(), b"part2", b"part3"];
222
223 let credential = CredentialBuilder::new(secret)
225 .sign_structured(&components)
226 .unwrap();
227
228 let result = CredentialVerifier::new(storage)
230 .with_secret(secret)
231 .verify_structured(&credential, &components)
232 .await;
233
234 assert!(result.is_ok());
235 }
236
237 #[tokio::test]
238 async fn test_custom_signing() {
239 let secret = b"test_secret";
240 let storage: Arc<dyn crate::storage::NonceStorage> = Arc::new(MemoryStorage::new());
241
242 let credential = CredentialBuilder::new(secret)
244 .sign_with(|mac, timestamp, nonce| {
245 mac.update(b"prefix:");
246 mac.update(timestamp.as_bytes());
247 mac.update(b":nonce:");
248 mac.update(nonce.as_bytes());
249 mac.update(b":custom");
250 })
251 .unwrap();
252
253 let result = CredentialVerifier::new(storage)
255 .with_secret(secret)
256 .verify_with(&credential, |mac| {
257 mac.update(b"prefix:");
258 mac.update(credential.timestamp.to_string().as_bytes());
259 mac.update(b":nonce:");
260 mac.update(credential.nonce.as_bytes());
261 mac.update(b":custom");
262 })
263 .await;
264
265 assert!(result.is_ok());
266 }
267
268 #[tokio::test]
269 async fn test_serialization() {
270 let secret = b"test_secret";
271 let payload = b"test_payload";
272
273 let credential = CredentialBuilder::new(secret).sign(payload).unwrap();
275
276 let json = serde_json::to_string(&credential).unwrap();
278 let deserialized: super::NonceCredential = serde_json::from_str(&json).unwrap();
279
280 assert_eq!(credential.timestamp, deserialized.timestamp);
281 assert_eq!(credential.nonce, deserialized.nonce);
282 assert_eq!(credential.signature, deserialized.signature);
283 }
284
285 #[tokio::test]
286 async fn test_secret_provider() {
287 let payload = b"test_payload";
288 let storage: Arc<dyn crate::storage::NonceStorage> = Arc::new(MemoryStorage::new());
289
290 let credential = CredentialBuilder::new(b"user123_secret")
292 .sign(payload)
293 .unwrap();
294
295 let result = CredentialVerifier::new(storage)
297 .with_context(Some("user123"))
298 .with_secret_provider(|context| {
299 let owned_context = context.map(|s| s.to_owned());
300 async move {
301 match owned_context.as_deref() {
302 Some("user123") => Ok(b"user123_secret".to_vec()),
303 Some("user456") => Ok(b"user456_secret".to_vec()),
304 _ => Err(NonceError::CryptoError("Unknown user".to_string())),
305 }
306 }
307 })
308 .verify(&credential, payload)
309 .await;
310
311 assert!(result.is_ok());
312 }
313}