1use alloc::vec::Vec;
7use core::sync::atomic::{
8 AtomicU64,
9 Ordering,
10};
11#[cfg(all(feature = "alloc", feature = "std"))]
12#[allow(clippy::disallowed_types)]
13use std::collections::HashSet;
14
15use lib_q_core::{
16 Error,
17 Nonce,
18 Result,
19};
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub struct NonceConfig {
24 pub check_uniqueness: bool,
26 pub max_tracked_nonces: usize,
28 pub secure_generation: bool,
30 pub nonce_size: usize,
32}
33
34impl Default for NonceConfig {
35 fn default() -> Self {
36 Self {
37 check_uniqueness: true,
38 max_tracked_nonces: 1000,
39 secure_generation: true,
40 nonce_size: 16, }
42 }
43}
44
45impl NonceConfig {
46 pub fn strict() -> Self {
48 Self {
49 check_uniqueness: true,
50 max_tracked_nonces: 10000,
51 secure_generation: true,
52 nonce_size: 16,
53 }
54 }
55
56 pub fn permissive() -> Self {
58 Self {
59 check_uniqueness: false,
60 max_tracked_nonces: 0,
61 secure_generation: false,
62 nonce_size: 16,
63 }
64 }
65}
66
67pub struct NonceManager {
70 config: NonceConfig,
71 counter: AtomicU64,
72 #[cfg(all(feature = "alloc", feature = "std"))]
74 #[allow(clippy::disallowed_types)]
75 used_nonces: std::sync::RwLock<HashSet<Vec<u8>>>,
76 #[cfg(not(all(feature = "alloc", feature = "std")))]
78 used_nonces: AtomicU64,
79}
80
81impl NonceManager {
82 pub fn new() -> Self {
84 Self::with_config(NonceConfig::default())
85 }
86
87 pub fn with_config(config: NonceConfig) -> Self {
89 Self {
90 config,
91 counter: AtomicU64::new(0),
92 #[cfg(all(feature = "alloc", feature = "std"))]
93 #[allow(clippy::disallowed_types)]
94 used_nonces: std::sync::RwLock::new(HashSet::new()),
95 #[cfg(not(all(feature = "alloc", feature = "std")))]
96 used_nonces: AtomicU64::new(0),
97 }
98 }
99
100 pub fn generate_nonce(&self) -> Result<Nonce> {
102 if self.config.secure_generation {
103 self.generate_secure_nonce()
104 } else {
105 self.generate_counter_nonce()
106 }
107 }
108
109 fn generate_secure_nonce(&self) -> Result<Nonce> {
111 let mut nonce_data = Vec::with_capacity(self.config.nonce_size);
113
114 #[cfg(all(feature = "std", not(target_arch = "wasm32")))]
116 {
117 use std::collections::hash_map::DefaultHasher;
118 use std::hash::{
119 Hash,
120 Hasher,
121 };
122 use std::time::{
123 SystemTime,
124 UNIX_EPOCH,
125 };
126
127 let now = SystemTime::now()
129 .duration_since(UNIX_EPOCH)
130 .unwrap_or_default()
131 .as_nanos() as u64;
132 let counter = self.counter.fetch_add(1, Ordering::SeqCst);
133
134 let mut hasher = DefaultHasher::new();
136 now.hash(&mut hasher);
137 counter.hash(&mut hasher);
138 let seed = hasher.finish();
139
140 for i in 0..self.config.nonce_size {
142 let mut byte_hasher = DefaultHasher::new();
143 (seed + i as u64).hash(&mut byte_hasher);
144 nonce_data.push((byte_hasher.finish() & 0xFF) as u8);
145 }
146 }
147
148 #[cfg(any(not(feature = "std"), target_arch = "wasm32"))]
151 {
152 let counter = self.counter.fetch_add(1, Ordering::SeqCst);
153
154 let mut state = counter;
156 for _ in 0..self.config.nonce_size {
157 state = state.wrapping_mul(0x41C64E6D).wrapping_add(12345);
158 nonce_data.push((state >> 24) as u8);
159 }
160 }
161
162 if self.is_nonce_used(&nonce_data)? {
164 return self.generate_secure_nonce();
166 }
167 nonce_data.resize(self.config.nonce_size, 0);
168
169 if nonce_data.iter().all(|&b| b == 0) {
171 nonce_data[0] = 1; }
173 if nonce_data.iter().all(|&b| b == 0xFF) {
174 nonce_data[0] = 0xFE; }
176
177 Ok(Nonce::new(nonce_data))
178 }
179
180 fn generate_counter_nonce(&self) -> Result<Nonce> {
182 let counter = self.counter.fetch_add(1, Ordering::SeqCst);
183
184 let mut nonce_data = Vec::with_capacity(self.config.nonce_size);
185
186 for i in 0..self.config.nonce_size {
188 let byte = ((counter.wrapping_mul(0x9E3779B9u64.wrapping_add(i as u64))) >> 24) as u8;
189 nonce_data.push(byte);
190 }
191
192 if nonce_data.iter().all(|&b| b == 0) {
194 nonce_data[0] = 1; }
196 if nonce_data.iter().all(|&b| b == 0xFF) {
197 nonce_data[0] = 0xFE; }
199
200 Ok(Nonce::new(nonce_data))
201 }
202
203 fn is_nonce_used(&self, nonce_data: &[u8]) -> Result<bool> {
205 #[cfg(all(feature = "alloc", feature = "std"))]
206 {
207 if let Ok(used_nonces) = self.used_nonces.read() {
208 Ok(used_nonces.contains(nonce_data))
209 } else {
210 Err(Error::InvalidNonceSize {
211 expected: 0,
212 actual: 0,
213 })
214 }
215 }
216
217 #[cfg(not(all(feature = "alloc", feature = "std")))]
218 {
219 let hash = self.hash_nonce(nonce_data);
221 let used_nonces = self.used_nonces.load(Ordering::SeqCst);
222 Ok((used_nonces & (1 << (hash % 64))) != 0)
223 }
224 }
225
226 fn mark_nonce_used_internal(&self, nonce_data: &[u8]) -> Result<()> {
228 #[cfg(all(feature = "alloc", feature = "std"))]
229 {
230 if let Ok(mut used_nonces) = self.used_nonces.write() {
231 used_nonces.insert(nonce_data.to_vec());
232
233 if used_nonces.len() > 10000 {
235 let to_remove: Vec<_> = used_nonces.iter().take(1000).cloned().collect();
237 for entry in to_remove {
238 used_nonces.remove(&entry);
239 }
240 }
241 Ok(())
242 } else {
243 Err(Error::InvalidNonceSize {
244 expected: 0,
245 actual: 0,
246 })
247 }
248 }
249
250 #[cfg(not(all(feature = "alloc", feature = "std")))]
251 {
252 let hash = self.hash_nonce(nonce_data);
254 let mut used_nonces = self.used_nonces.load(Ordering::SeqCst);
255 used_nonces |= 1 << (hash % 64);
256 self.used_nonces.store(used_nonces, Ordering::SeqCst);
257 Ok(())
258 }
259 }
260
261 #[cfg(not(all(feature = "alloc", feature = "std")))]
263 fn hash_nonce(&self, nonce_data: &[u8]) -> u64 {
264 let mut hash = 0u64;
265 for &byte in nonce_data {
266 hash = hash.wrapping_mul(31).wrapping_add(byte as u64);
267 }
268 hash
269 }
270
271 pub fn validate_nonce(&self, nonce: &Nonce) -> Result<()> {
273 if !self.config.check_uniqueness {
274 return Ok(());
275 }
276
277 self.validate_nonce_format(nonce)?;
279
280 let nonce_data = nonce.as_bytes();
282 if self.is_nonce_used(nonce_data)? {
283 return Err(Error::InvalidNonceSize {
284 expected: 0,
285 actual: 0,
286 });
287 }
288
289 self.mark_nonce_used_internal(nonce_data)
291 }
292
293 fn validate_nonce_format(&self, nonce: &Nonce) -> Result<()> {
295 let nonce_bytes = nonce.as_bytes();
296
297 if nonce_bytes.len() != self.config.nonce_size {
298 return Err(Error::InvalidNonceSize {
299 expected: self.config.nonce_size,
300 actual: nonce_bytes.len(),
301 });
302 }
303
304 if nonce_bytes.iter().all(|&b| b == 0) {
306 return Err(Error::InvalidNonceSize {
307 expected: 1,
308 actual: 0,
309 });
310 }
311
312 if nonce_bytes.iter().all(|&b| b == 0xFF) {
314 return Err(Error::InvalidNonceSize {
315 expected: 1,
316 actual: 0,
317 });
318 }
319
320 Ok(())
321 }
322
323 pub fn is_nonce_unique(&self, nonce: &Nonce) -> bool {
325 if !self.config.check_uniqueness {
326 return true;
327 }
328
329 match self.is_nonce_used(nonce.as_bytes()) {
331 Ok(used) => !used,
332 Err(_) => false, }
334 }
335
336 pub fn mark_nonce_used(&self, nonce: &Nonce) -> Result<()> {
338 if !self.config.check_uniqueness {
339 return Ok(());
340 }
341
342 self.validate_nonce_format(nonce)?;
344 self.mark_nonce_used_internal(nonce.as_bytes())
345 }
346
347 pub fn get_counter(&self) -> u64 {
349 self.counter.load(Ordering::SeqCst)
350 }
351
352 pub fn reset_counter(&self) {
354 self.counter.store(0, Ordering::SeqCst);
355 }
356}
357
358impl Default for NonceManager {
359 fn default() -> Self {
360 Self::new()
361 }
362}
363
364#[cfg(all(feature = "alloc", feature = "std"))]
366static NONCE_MANAGER: std::sync::LazyLock<NonceManager> =
367 std::sync::LazyLock::new(|| NonceManager {
368 config: NonceConfig {
369 check_uniqueness: true,
370 max_tracked_nonces: 1000,
371 secure_generation: true,
372 nonce_size: 16,
373 },
374 counter: AtomicU64::new(0),
375 #[allow(clippy::disallowed_types)]
376 used_nonces: std::sync::RwLock::new(HashSet::new()),
377 });
378
379#[cfg(not(all(feature = "alloc", feature = "std")))]
381static NONCE_MANAGER: NonceManager = NonceManager {
382 config: NonceConfig {
383 check_uniqueness: true,
384 max_tracked_nonces: 1000,
385 secure_generation: true,
386 nonce_size: 16,
387 },
388 counter: AtomicU64::new(0),
389 used_nonces: AtomicU64::new(0),
390};
391
392#[cfg(all(feature = "alloc", feature = "std"))]
394pub fn get_nonce_manager() -> &'static NonceManager {
395 &NONCE_MANAGER
396}
397
398#[cfg(not(all(feature = "alloc", feature = "std")))]
399pub fn get_nonce_manager() -> &'static NonceManager {
400 &NONCE_MANAGER
401}
402
403pub fn generate_nonce() -> Result<Nonce> {
405 get_nonce_manager().generate_nonce()
406}
407
408pub fn validate_nonce(nonce: &Nonce) -> Result<()> {
410 get_nonce_manager().validate_nonce(nonce)
411}
412
413pub fn is_nonce_unique(nonce: &Nonce) -> bool {
415 get_nonce_manager().is_nonce_unique(nonce)
416}
417
418pub fn mark_nonce_used(nonce: &Nonce) -> Result<()> {
420 get_nonce_manager().mark_nonce_used(nonce)
421}
422
423pub mod utils {
425 use super::*;
426
427 pub fn nonce_from_counter(counter: u64, nonce_size: usize) -> Nonce {
429 let mut nonce_data = Vec::with_capacity(nonce_size);
430 nonce_data.extend_from_slice(&counter.to_le_bytes());
431 nonce_data.resize(nonce_size, 0);
432 Nonce::new(nonce_data)
433 }
434
435 pub fn nonce_from_random(random_data: &[u8], nonce_size: usize) -> Result<Nonce> {
437 if random_data.len() < nonce_size {
438 return Err(Error::InvalidNonceSize {
439 expected: nonce_size,
440 actual: random_data.len(),
441 });
442 }
443
444 let nonce_data = random_data[..nonce_size].to_vec();
445 Ok(Nonce::new(nonce_data))
446 }
447
448 pub fn nonce_from_key_and_counter(key: &[u8], counter: u64, nonce_size: usize) -> Nonce {
450 let mut nonce_data = Vec::with_capacity(nonce_size);
451
452 nonce_data.extend_from_slice(&counter.to_le_bytes());
454
455 let remaining = nonce_size.saturating_sub(8);
457 let key_bytes = key.len().min(remaining);
458 nonce_data.extend_from_slice(&key[..key_bytes]);
459
460 nonce_data.resize(nonce_size, 0);
462
463 Nonce::new(nonce_data)
464 }
465}
466
467#[cfg(test)]
468mod tests {
469 #[cfg(not(feature = "std"))]
470 use alloc::vec;
471
472 use super::*;
473
474 #[test]
475 fn test_nonce_config_defaults() {
476 let config = NonceConfig::default();
477 assert!(config.check_uniqueness);
478 assert_eq!(config.max_tracked_nonces, 1000);
479 assert!(config.secure_generation);
480 assert_eq!(config.nonce_size, 16);
481 }
482
483 #[test]
484 fn test_nonce_config_strict() {
485 let config = NonceConfig::strict();
486 assert!(config.check_uniqueness);
487 assert_eq!(config.max_tracked_nonces, 10000);
488 assert!(config.secure_generation);
489 assert_eq!(config.nonce_size, 16);
490 }
491
492 #[test]
493 fn test_nonce_config_permissive() {
494 let config = NonceConfig::permissive();
495 assert!(!config.check_uniqueness);
496 assert_eq!(config.max_tracked_nonces, 0);
497 assert!(!config.secure_generation);
498 assert_eq!(config.nonce_size, 16);
499 }
500
501 #[test]
502 fn test_nonce_manager_creation() {
503 let manager = NonceManager::new();
504 assert_eq!(manager.get_counter(), 0);
505 }
506
507 #[test]
508 fn test_nonce_manager_with_config() {
509 let config = NonceConfig::strict();
510 let manager = NonceManager::with_config(config);
511 assert_eq!(manager.get_counter(), 0);
512 }
513
514 #[test]
515 fn test_generate_secure_nonce() {
516 let manager = NonceManager::new();
517 let nonce1 = manager.generate_nonce().unwrap();
518 let nonce2 = manager.generate_nonce().unwrap();
519
520 assert_eq!(nonce1.as_bytes().len(), 16);
521 assert_eq!(nonce2.as_bytes().len(), 16);
522 assert_ne!(nonce1.as_bytes(), nonce2.as_bytes());
523 }
524
525 #[test]
526 fn test_generate_counter_nonce() {
527 let config = NonceConfig {
528 secure_generation: false,
529 ..Default::default()
530 };
531 let manager = NonceManager::with_config(config);
532
533 let nonce1 = manager.generate_nonce().unwrap();
534 let nonce2 = manager.generate_nonce().unwrap();
535
536 assert_eq!(nonce1.as_bytes().len(), 16);
537 assert_eq!(nonce2.as_bytes().len(), 16);
538 assert_ne!(nonce1.as_bytes(), nonce2.as_bytes());
539
540 assert_eq!(manager.get_counter(), 2);
542 }
543
544 #[test]
545 fn test_validate_nonce_format() {
546 let manager = NonceManager::new();
547
548 let nonce = Nonce::new(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
550 assert!(manager.validate_nonce(&nonce).is_ok());
551
552 let zero_nonce = Nonce::new(vec![0u8; 16]);
554 assert!(manager.validate_nonce(&zero_nonce).is_err());
555
556 let ones_nonce = Nonce::new(vec![0xFFu8; 16]);
558 assert!(manager.validate_nonce(&ones_nonce).is_err());
559
560 let wrong_size_nonce = Nonce::new(vec![1, 2, 3, 4]);
562 assert!(manager.validate_nonce(&wrong_size_nonce).is_err());
563 }
564
565 #[test]
566 fn test_nonce_uniqueness() {
567 let manager = NonceManager::new();
568 let nonce = Nonce::new(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
569
570 assert!(manager.is_nonce_unique(&nonce));
571 assert!(manager.mark_nonce_used(&nonce).is_ok());
572 }
573
574 #[test]
575 fn test_counter_operations() {
576 let manager = NonceManager::new();
577
578 assert_eq!(manager.get_counter(), 0);
579
580 let _nonce1 = manager.generate_nonce().unwrap();
581 assert_eq!(manager.get_counter(), 1);
582
583 let _nonce2 = manager.generate_nonce().unwrap();
584 assert_eq!(manager.get_counter(), 2);
585
586 manager.reset_counter();
587 assert_eq!(manager.get_counter(), 0);
588 }
589
590 #[test]
591 fn test_global_nonce_functions() {
592 let nonce1 = generate_nonce().unwrap();
593 let nonce2 = generate_nonce().unwrap();
594
595 assert_eq!(nonce1.as_bytes().len(), 16);
596 assert_eq!(nonce2.as_bytes().len(), 16);
597 assert_ne!(nonce1.as_bytes(), nonce2.as_bytes());
598
599 assert!(validate_nonce(&nonce1).is_ok());
601 assert!(validate_nonce(&nonce2).is_ok());
602
603 assert!(mark_nonce_used(&nonce1).is_ok());
605 assert!(mark_nonce_used(&nonce2).is_ok());
606 }
607
608 #[test]
609 fn test_nonce_utils() {
610 let nonce1 = utils::nonce_from_counter(42, 16);
612 assert_eq!(nonce1.as_bytes().len(), 16);
613
614 let random_data = vec![
616 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
617 ];
618 let nonce2 = utils::nonce_from_random(&random_data, 16).unwrap();
619 assert_eq!(nonce2.as_bytes().len(), 16);
620
621 let key = vec![1, 2, 3, 4, 5, 6, 7, 8];
623 let nonce3 = utils::nonce_from_key_and_counter(&key, 123, 16);
624 assert_eq!(nonce3.as_bytes().len(), 16);
625 }
626}