1use hmac::{Hmac, KeyInit, Mac};
4use sha2::{Digest, Sha256};
5use std::collections::VecDeque;
6use std::sync::Mutex;
7use std::sync::atomic::{AtomicU64, Ordering};
8
9pub struct BloomFilter {
12 bits: Vec<u64>,
13 num_bits: usize,
14 num_hashes: usize,
15 count: AtomicU64,
16}
17
18impl BloomFilter {
19 pub fn new(expected_items: usize, false_positive_rate: f64) -> Self {
20 let num_bits = (-(expected_items as f64 * false_positive_rate.ln())
21 / (std::f64::consts::LN_2.powi(2)))
22 .ceil() as usize;
23 let num_bits = num_bits.max(64);
24 let num_hashes =
25 ((num_bits as f64 / expected_items as f64) * std::f64::consts::LN_2).ceil() as usize;
26 let num_hashes = num_hashes.max(1);
27 let words = num_bits.div_ceil(64);
28 Self {
29 bits: vec![0u64; words],
30 num_bits,
31 num_hashes,
32 count: AtomicU64::new(0),
33 }
34 }
35
36 pub fn insert(&mut self, data: &[u8]) {
37 let (h1, h2) = double_hash(data);
38 for i in 0..self.num_hashes {
39 let combined = h1.wrapping_add((i as u64).wrapping_mul(h2));
40 let idx = (combined as usize) % self.num_bits;
41 let word = idx / 64;
42 let bit = idx % 64;
43 self.bits[word] |= 1u64 << bit;
44 }
45 self.count.fetch_add(1, Ordering::SeqCst);
46 }
47
48 pub fn contains(&self, data: &[u8]) -> bool {
49 let (h1, h2) = double_hash(data);
50 for i in 0..self.num_hashes {
51 let combined = h1.wrapping_add((i as u64).wrapping_mul(h2));
52 let idx = (combined as usize) % self.num_bits;
53 let word = idx / 64;
54 let bit = idx % 64;
55 if self.bits[word] & (1u64 << bit) == 0 {
56 return false;
57 }
58 }
59 true
60 }
61
62 pub fn estimated_count(&self) -> u64 {
63 self.count.load(Ordering::SeqCst)
64 }
65 pub fn num_bits(&self) -> usize {
66 self.num_bits
67 }
68 pub fn num_hashes(&self) -> usize {
69 self.num_hashes
70 }
71}
72
73fn double_hash(data: &[u8]) -> (u64, u64) {
74 let mut h1 = Sha256::new();
75 h1.update(b"h1");
76 h1.update(data);
77 let r1 = h1.finalize();
78 let mut h2 = Sha256::new();
79 h2.update(b"h2");
80 h2.update(data);
81 let r2 = h2.finalize();
82 (
83 u64::from_be_bytes(r1[..8].try_into().unwrap()),
84 u64::from_be_bytes(r2[..8].try_into().unwrap()),
85 )
86}
87
88pub struct CountMinSketch {
91 table: Vec<Vec<AtomicU64>>,
92 width: usize,
93 depth: usize,
94}
95
96impl CountMinSketch {
97 pub fn new(width: usize, depth: usize) -> Self {
98 Self {
99 table: (0..depth)
100 .map(|_| (0..width).map(|_| AtomicU64::new(0)).collect())
101 .collect(),
102 width,
103 depth,
104 }
105 }
106
107 pub fn add(&self, data: &[u8], count: u64) {
108 for (d, row) in self.table.iter().enumerate() {
109 let idx = hash_at_depth(data, d, self.width);
110 row[idx].fetch_add(count, Ordering::SeqCst);
111 }
112 }
113
114 pub fn estimate(&self, data: &[u8]) -> u64 {
115 self.table
116 .iter()
117 .enumerate()
118 .map(|(d, row)| {
119 let idx = hash_at_depth(data, d, self.width);
120 row[idx].load(Ordering::SeqCst)
121 })
122 .min()
123 .unwrap_or(0)
124 }
125}
126
127fn hash_at_depth(data: &[u8], depth: usize, width: usize) -> usize {
128 let mut h = Sha256::new();
129 h.update(depth.to_be_bytes());
130 h.update(data);
131 let result = h.finalize();
132 u64::from_be_bytes(result[..8].try_into().unwrap()) as usize % width
133}
134
135pub struct RingBuffer<T: Clone> {
138 data: VecDeque<T>,
139 capacity: usize,
140}
141
142impl<T: Clone> RingBuffer<T> {
143 pub fn new(capacity: usize) -> Self {
144 Self {
145 data: VecDeque::with_capacity(capacity),
146 capacity,
147 }
148 }
149
150 pub fn push(&mut self, item: T) {
151 if self.data.len() >= self.capacity {
152 self.data.pop_front();
153 }
154 self.data.push_back(item);
155 }
156
157 pub fn latest(&self) -> Option<&T> {
158 self.data.back()
159 }
160 pub fn oldest(&self) -> Option<&T> {
161 self.data.front()
162 }
163 pub fn len(&self) -> usize {
164 self.data.len()
165 }
166 pub fn is_empty(&self) -> bool {
167 self.data.is_empty()
168 }
169 pub fn is_full(&self) -> bool {
170 self.data.len() == self.capacity
171 }
172 pub fn iter(&self) -> impl Iterator<Item = &T> {
173 self.data.iter()
174 }
175 pub fn clear(&mut self) {
176 self.data.clear();
177 }
178 pub fn as_vec(&self) -> Vec<T> {
179 self.data.iter().cloned().collect()
180 }
181}
182
183pub struct TestFixtures;
186
187impl TestFixtures {
188 pub fn random_bytes(n: usize) -> Vec<u8> {
189 use rand_core::{OsRng, RngCore};
190 let mut buf = vec![0u8; n];
191 OsRng.fill_bytes(&mut buf);
192 buf
193 }
194
195 pub fn random_hex(n: usize) -> String {
196 hex::encode(Self::random_bytes(n))
197 }
198
199 pub fn random_message() -> Vec<u8> {
200 Self::random_bytes(32)
201 }
202
203 pub fn random_session_id() -> String {
204 format!("session-{}", hex::encode(Self::random_bytes(4)))
205 }
206
207 pub fn random_signer_id() -> String {
208 format!("signer-{}", hex::encode(Self::random_bytes(4)))
209 }
210
211 pub fn random_quorum_id() -> String {
212 format!("quorum-{}", hex::encode(Self::random_bytes(4)))
213 }
214
215 pub fn fake_signature() -> Vec<u8> {
216 Self::random_bytes(64)
217 }
218 pub fn fake_share() -> Vec<u8> {
219 Self::random_bytes(32)
220 }
221 pub fn fake_public_key() -> Vec<u8> {
222 Self::random_bytes(33)
223 }
224
225 pub fn batch_messages(n: usize) -> Vec<Vec<u8>> {
228 (0..n)
229 .map(|i| {
230 let mut msg = vec![0u8; 32];
231 msg[0] = i as u8;
232 msg
233 })
234 .collect()
235 }
236}
237
238pub fn hkdf_extract(salt: &[u8], ikm: &[u8]) -> [u8; 32] {
241 let salt = if salt.is_empty() {
242 &[0u8; 32][..]
243 } else {
244 salt
245 };
246 let mut mac = Hmac::<Sha256>::new_from_slice(salt).expect("HMAC");
247 mac.update(ikm);
248 let result = mac.finalize().into_bytes();
249 let mut prk = [0u8; 32];
250 prk.copy_from_slice(&result);
251 prk
252}
253
254pub const HKDF_SHA256_MAX_OUTPUT: usize = 255 * 32;
257
258#[derive(Debug, thiserror::Error)]
260pub enum HkdfError {
261 #[error("HKDF output length {requested} exceeds RFC 5869 max of {max}")]
265 OutputTooLong { requested: usize, max: usize },
266}
267
268pub fn hkdf_expand(prk: &[u8; 32], info: &[u8], length: usize) -> Result<Vec<u8>, HkdfError> {
269 if length > HKDF_SHA256_MAX_OUTPUT {
270 return Err(HkdfError::OutputTooLong {
271 requested: length,
272 max: HKDF_SHA256_MAX_OUTPUT,
273 });
274 }
275 let mut output = Vec::with_capacity(length);
276 let mut previous: Vec<u8> = Vec::new();
277 let mut counter: u16 = 1;
280 while output.len() < length {
281 let mut mac = Hmac::<Sha256>::new_from_slice(prk).expect("HMAC");
282 mac.update(&previous);
283 mac.update(info);
284 mac.update(&[counter as u8]);
285 previous = mac.finalize().into_bytes().to_vec();
286 let take = previous.len().min(length - output.len());
287 output.extend_from_slice(&previous[..take]);
288 counter += 1;
289 }
290 Ok(output)
291}
292
293pub fn hkdf(salt: &[u8], ikm: &[u8], info: &[u8], length: usize) -> Result<Vec<u8>, HkdfError> {
294 let prk = hkdf_extract(salt, ikm);
295 hkdf_expand(&prk, info, length)
296}
297
298pub fn key_wrap(kek: &[u8; 32], plaintext: &[u8]) -> Vec<u8> {
301 let mut mac = Hmac::<Sha256>::new_from_slice(kek).expect("HMAC");
303 mac.update(plaintext);
304 let tag = mac.finalize().into_bytes();
305 let mut wrapped = Vec::with_capacity(plaintext.len() + 32);
306 for (i, &b) in plaintext.iter().enumerate() {
307 wrapped.push(b ^ kek[i % 32]);
308 }
309 wrapped.extend_from_slice(&tag[..16]);
310 wrapped
311}
312
313pub fn key_unwrap(kek: &[u8; 32], wrapped: &[u8]) -> Option<Vec<u8>> {
314 if wrapped.len() < 16 {
315 return None;
316 }
317 let body = &wrapped[..wrapped.len() - 16];
318 let tag = &wrapped[wrapped.len() - 16..];
319 let unwrapped: Vec<u8> = body
320 .iter()
321 .enumerate()
322 .map(|(i, &b)| b ^ kek[i % 32])
323 .collect();
324 let mut mac = Hmac::<Sha256>::new_from_slice(kek).expect("HMAC");
325 mac.update(&unwrapped);
326 let expected = &mac.finalize().into_bytes()[..16];
327 if tag == expected {
328 Some(unwrapped)
329 } else {
330 None
331 }
332}
333
334pub fn secure_overwrite(data: &mut [u8]) {
337 for b in data.iter_mut() {
339 *b = 0x00;
340 }
341 for b in data.iter_mut() {
342 *b = 0xFF;
343 }
344 use rand_core::{OsRng, RngCore};
345 for chunk in data.chunks_mut(1) {
346 let mut buf = [0u8; 1];
347 OsRng.fill_bytes(&mut buf);
348 chunk[0] = buf[0];
349 }
350}
351
352pub struct SecureBuffer {
353 data: Vec<u8>,
354 zeroized: bool,
355}
356
357impl SecureBuffer {
358 pub fn new(data: Vec<u8>) -> Self {
359 Self {
360 data,
361 zeroized: false,
362 }
363 }
364 pub fn as_slice(&self) -> &[u8] {
365 &self.data
366 }
367 pub fn len(&self) -> usize {
368 self.data.len()
369 }
370 pub fn is_empty(&self) -> bool {
371 self.data.is_empty()
372 }
373
374 pub fn zeroize(&mut self) {
375 if !self.zeroized {
376 secure_overwrite(&mut self.data);
377 self.zeroized = true;
378 }
379 }
380
381 pub fn is_zeroized(&self) -> bool {
382 self.zeroized
383 }
384}
385
386impl Drop for SecureBuffer {
387 fn drop(&mut self) {
388 if !self.zeroized {
389 secure_overwrite(&mut self.data);
390 }
391 }
392}
393
394pub struct EntropyMonitor {
397 samples: Mutex<VecDeque<u32>>,
398 max_samples: usize,
399 min_threshold: u32,
400 alerts: AtomicU64,
401}
402
403impl EntropyMonitor {
404 pub fn new(max_samples: usize, min_threshold: u32) -> Self {
405 Self {
406 samples: Mutex::new(VecDeque::with_capacity(max_samples)),
407 max_samples,
408 min_threshold,
409 alerts: AtomicU64::new(0),
410 }
411 }
412
413 pub fn record(&self, entropy_bits: u32) {
414 let mut samples = self.samples.lock().unwrap();
415 if samples.len() >= self.max_samples {
416 samples.pop_front();
417 }
418 samples.push_back(entropy_bits);
419 if entropy_bits < self.min_threshold {
420 self.alerts.fetch_add(1, Ordering::SeqCst);
421 }
422 }
423
424 pub fn latest(&self) -> Option<u32> {
425 self.samples.lock().unwrap().back().copied()
426 }
427 pub fn average(&self) -> f64 {
428 let s = self.samples.lock().unwrap();
429 if s.is_empty() {
430 return 0.0;
431 }
432 s.iter().sum::<u32>() as f64 / s.len() as f64
433 }
434 pub fn min(&self) -> Option<u32> {
435 self.samples.lock().unwrap().iter().copied().min()
436 }
437 pub fn alert_count(&self) -> u64 {
438 self.alerts.load(Ordering::SeqCst)
439 }
440 pub fn is_low(&self) -> bool {
441 self.latest()
442 .map(|e| e < self.min_threshold)
443 .unwrap_or(false)
444 }
445 pub fn sample_count(&self) -> usize {
446 self.samples.lock().unwrap().len()
447 }
448}
449
450pub struct BurstTokenBucket {
453 tokens: Mutex<f64>,
454 capacity: f64,
455 refill_rate: f64, burst_capacity: f64,
457 last_refill: Mutex<std::time::Instant>,
458}
459
460impl BurstTokenBucket {
461 pub fn new(sustained_rate: f64, burst_multiplier: f64) -> Self {
462 let capacity = sustained_rate * burst_multiplier;
463 Self {
464 tokens: Mutex::new(capacity),
465 capacity,
466 refill_rate: sustained_rate,
467 burst_capacity: capacity,
468 last_refill: Mutex::new(std::time::Instant::now()),
469 }
470 }
471
472 fn refill(&self) {
473 let mut tokens = self.tokens.lock().unwrap();
474 let mut last = self.last_refill.lock().unwrap();
475 let elapsed = last.elapsed().as_secs_f64();
476 *tokens = (*tokens + elapsed * self.refill_rate).min(self.capacity);
477 *last = std::time::Instant::now();
478 }
479
480 pub fn try_consume(&self, cost: f64) -> bool {
481 self.refill();
482 let mut tokens = self.tokens.lock().unwrap();
483 if *tokens >= cost {
484 *tokens -= cost;
485 true
486 } else {
487 false
488 }
489 }
490
491 pub fn available_tokens(&self) -> f64 {
492 self.refill();
493 *self.tokens.lock().unwrap()
494 }
495
496 pub fn burst_capacity(&self) -> f64 {
497 self.burst_capacity
498 }
499 pub fn sustained_rate(&self) -> f64 {
500 self.refill_rate
501 }
502}
503
504pub struct ConcurrentTestRunner {
507 results: Mutex<Vec<ConcTestResult>>,
508}
509
510#[derive(Debug, Clone)]
511pub struct ConcTestResult {
512 pub name: String,
513 pub passed: bool,
514 pub duration_micros: u64,
515}
516
517impl ConcurrentTestRunner {
518 pub fn new() -> Self {
519 Self {
520 results: Mutex::new(Vec::new()),
521 }
522 }
523
524 pub fn run<F>(&self, name: &str, test_fn: F) -> bool
525 where
526 F: FnOnce() -> bool + Send + 'static,
527 {
528 let start = std::time::Instant::now();
529 let passed = test_fn();
530 let duration = start.elapsed().as_micros() as u64;
531 self.results.lock().unwrap().push(ConcTestResult {
532 name: name.into(),
533 passed,
534 duration_micros: duration,
535 });
536 passed
537 }
538
539 pub fn run_concurrent<F>(&self, tests: Vec<(&str, F)>) -> usize
540 where
541 F: Fn() -> bool + Send + Sync + 'static,
542 {
543 let passed_count = std::sync::Arc::new(AtomicU64::new(0));
544 let mut handles = Vec::new();
545 for (name, test_fn) in tests {
546 let pc = std::sync::Arc::clone(&passed_count);
547 let results = std::sync::Arc::new(Mutex::new(Vec::new()));
548 let r2 = std::sync::Arc::clone(&results);
549 let name = name.to_string();
550 handles.push(std::thread::spawn(move || {
551 let start = std::time::Instant::now();
552 let passed = test_fn();
553 let duration = start.elapsed().as_micros() as u64;
554 r2.lock().unwrap().push(ConcTestResult {
555 name,
556 passed,
557 duration_micros: duration,
558 });
559 if passed {
560 pc.fetch_add(1, Ordering::SeqCst);
561 }
562 }));
563 }
564 let _total = handles.len();
565 for h in handles {
566 let _ = h.join();
567 }
568 passed_count.load(Ordering::SeqCst) as usize
569 }
570
571 pub fn results(&self) -> Vec<ConcTestResult> {
572 self.results.lock().unwrap().clone()
573 }
574 pub fn total(&self) -> usize {
575 self.results.lock().unwrap().len()
576 }
577 pub fn passed(&self) -> usize {
578 self.results
579 .lock()
580 .unwrap()
581 .iter()
582 .filter(|r| r.passed)
583 .count()
584 }
585 pub fn failed(&self) -> usize {
586 self.total() - self.passed()
587 }
588 pub fn avg_duration_micros(&self) -> f64 {
589 let r = self.results.lock().unwrap();
590 if r.is_empty() {
591 return 0.0;
592 }
593 r.iter().map(|r| r.duration_micros as f64).sum::<f64>() / r.len() as f64
594 }
595}
596
597impl Default for ConcurrentTestRunner {
598 fn default() -> Self {
599 Self::new()
600 }
601}
602
603pub struct SpscSender<T> {
606 shared: std::sync::Arc<SpscShared<T>>,
607}
608pub struct SpscReceiver<T> {
609 shared: std::sync::Arc<SpscShared<T>>,
610}
611
612struct SpscShared<T> {
613 buffer: Mutex<VecDeque<T>>,
614 capacity: usize,
615 total_sent: AtomicU64,
616 total_received: AtomicU64,
617}
618
619pub fn spsc_channel<T>(capacity: usize) -> (SpscSender<T>, SpscReceiver<T>) {
620 let shared = std::sync::Arc::new(SpscShared {
621 buffer: Mutex::new(VecDeque::with_capacity(capacity)),
622 capacity,
623 total_sent: AtomicU64::new(0),
624 total_received: AtomicU64::new(0),
625 });
626 (
627 SpscSender {
628 shared: std::sync::Arc::clone(&shared),
629 },
630 SpscReceiver { shared },
631 )
632}
633
634impl<T> SpscSender<T> {
635 pub fn send(&self, item: T) -> bool {
636 let mut buf = self.shared.buffer.lock().unwrap();
637 if buf.len() >= self.shared.capacity {
638 return false;
639 }
640 buf.push_back(item);
641 self.shared.total_sent.fetch_add(1, Ordering::SeqCst);
642 true
643 }
644 pub fn total_sent(&self) -> u64 {
645 self.shared.total_sent.load(Ordering::SeqCst)
646 }
647}
648
649impl<T> SpscReceiver<T> {
650 pub fn recv(&self) -> Option<T> {
651 let item = self.shared.buffer.lock().unwrap().pop_front();
652 if item.is_some() {
653 self.shared.total_received.fetch_add(1, Ordering::SeqCst);
654 }
655 item
656 }
657 pub fn len(&self) -> usize {
658 self.shared.buffer.lock().unwrap().len()
659 }
660 pub fn is_empty(&self) -> bool {
661 self.len() == 0
662 }
663 pub fn total_received(&self) -> u64 {
664 self.shared.total_received.load(Ordering::SeqCst)
665 }
666}
667
668#[cfg(test)]
669mod tests {
670 use super::*;
671
672 #[test]
674 fn bloom_insert_and_contains() {
675 let mut bf = BloomFilter::new(1000, 0.01);
676 bf.insert(b"apple");
677 bf.insert(b"banana");
678 assert!(bf.contains(b"apple"));
679 assert!(bf.contains(b"banana"));
680 }
681
682 #[test]
683 fn bloom_absent_not_present() {
684 let mut bf = BloomFilter::new(1000, 0.01);
685 bf.insert(b"present");
686 let false_positives = (0..100)
688 .filter(|i| bf.contains(format!("absent-{i}").as_bytes()))
689 .count();
690 assert!(false_positives < 10); }
692
693 #[test]
694 fn bloom_count() {
695 let mut bf = BloomFilter::new(100, 0.05);
696 for i in 0..50 {
697 bf.insert(format!("item-{i}").as_bytes());
698 }
699 assert_eq!(bf.estimated_count(), 50);
700 }
701
702 #[test]
704 fn cms_estimate_frequency() {
705 let cms = CountMinSketch::new(1000, 5);
706 for _ in 0..10 {
707 cms.add(b"popular", 1);
708 }
709 for _ in 0..3 {
710 cms.add(b"rare", 1);
711 }
712 assert!(cms.estimate(b"popular") >= 10);
713 assert!(cms.estimate(b"rare") >= 3);
714 }
715
716 #[test]
717 fn cms_unknown_is_low() {
718 let cms = CountMinSketch::new(1000, 5);
719 cms.add(b"x", 1);
720 assert!(cms.estimate(b"unknown") <= 5);
721 }
722
723 #[test]
725 fn ring_buffer_evicts_oldest() {
726 let mut rb = RingBuffer::new(3);
727 rb.push(1);
728 rb.push(2);
729 rb.push(3);
730 rb.push(4);
731 assert_eq!(rb.len(), 3);
732 assert_eq!(*rb.oldest().unwrap(), 2);
733 assert_eq!(*rb.latest().unwrap(), 4);
734 }
735
736 #[test]
737 fn ring_buffer_iter() {
738 let mut rb = RingBuffer::new(5);
739 rb.push(1);
740 rb.push(2);
741 rb.push(3);
742 let v: Vec<i32> = rb.iter().copied().collect();
743 assert_eq!(v, vec![1, 2, 3]);
744 }
745
746 #[test]
748 fn fixtures_random_bytes() {
749 let b1 = TestFixtures::random_bytes(32);
750 let b2 = TestFixtures::random_bytes(32);
751 assert_eq!(b1.len(), 32);
752 assert_ne!(b1, b2);
753 }
754
755 #[test]
756 fn fixtures_session_id() {
757 let id = TestFixtures::random_session_id();
758 assert!(id.starts_with("session-"));
759 }
760
761 #[test]
762 fn fixtures_batch_messages() {
763 let msgs = TestFixtures::batch_messages(5);
764 assert_eq!(msgs.len(), 5);
765 assert_eq!(msgs[0][0], 0);
766 assert_eq!(msgs[4][0], 4);
767 }
768
769 #[test]
771 fn hkdf_extract_produces_prk() {
772 let prk = hkdf_extract(b"salt", b"ikm");
773 assert_eq!(prk.len(), 32);
774 }
775
776 #[test]
777 fn hkdf_expand_produces_output() {
778 let prk = [0x42u8; 32];
779 let okm = hkdf_expand(&prk, b"info", 64).unwrap();
780 assert_eq!(okm.len(), 64);
781 }
782
783 #[test]
784 fn hkdf_expand_rejects_over_rfc_limit() {
785 let prk = [0x42u8; 32];
786 assert!(hkdf_expand(&prk, b"info", HKDF_SHA256_MAX_OUTPUT).is_ok());
788 let err = hkdf_expand(&prk, b"info", HKDF_SHA256_MAX_OUTPUT + 1).unwrap_err();
790 assert!(matches!(err, HkdfError::OutputTooLong { .. }));
791 }
792
793 #[test]
794 fn hkdf_expand_max_output_is_uniform() {
795 let prk = [0x07u8; 32];
798 let okm = hkdf_expand(&prk, b"info", HKDF_SHA256_MAX_OUTPUT).unwrap();
799 assert_eq!(okm.len(), HKDF_SHA256_MAX_OUTPUT);
800 let head = &okm[..32];
801 let tail = &okm[HKDF_SHA256_MAX_OUTPUT - 32..];
802 assert_ne!(head, tail, "tail block must differ from first block");
803 }
804
805 #[test]
806 fn hkdf_full_round_trips() {
807 let okm1 = hkdf(b"salt", b"ikm", b"info", 32).unwrap();
808 let okm2 = hkdf(b"salt", b"ikm", b"info", 32).unwrap();
809 assert_eq!(okm1, okm2); }
811
812 #[test]
813 fn hkdf_different_info_different_output() {
814 let okm1 = hkdf(b"s", b"ikm", b"info1", 32).unwrap();
815 let okm2 = hkdf(b"s", b"ikm", b"info2", 32).unwrap();
816 assert_ne!(okm1, okm2);
817 }
818
819 #[test]
820 fn hkdf_matches_rfc5869_test_vector_1() {
821 let ikm = [0x0bu8; 22];
828 let salt = [
829 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
830 ];
831 let info = [0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9];
832 let expected = [
833 0x3c, 0xb2, 0x5f, 0x25, 0xfa, 0xac, 0xd5, 0x7a, 0x90, 0x43, 0x4f, 0x64, 0xd0, 0x36,
834 0x2f, 0x2a, 0x2d, 0x2d, 0x0a, 0x90, 0xcf, 0x1a, 0x5a, 0x4c, 0x5d, 0xb0, 0x2d, 0x56,
835 0xec, 0xc4, 0xc5, 0xbf, 0x34, 0x00, 0x72, 0x08, 0xd5, 0xb8, 0x87, 0x18, 0x58, 0x65,
836 ];
837 let okm = hkdf(&salt, &ikm, &info, 42).unwrap();
838 assert_eq!(okm, expected);
839 }
840
841 #[test]
843 fn key_wrap_unwrap_round_trips() {
844 let kek = [0x42u8; 32];
845 let plaintext = TestFixtures::random_bytes(32);
846 let wrapped = key_wrap(&kek, &plaintext);
847 let unwrapped = key_unwrap(&kek, &wrapped).unwrap();
848 assert_eq!(unwrapped, plaintext);
849 }
850
851 #[test]
852 fn key_wrap_tampered_rejected() {
853 let kek = [0x42u8; 32];
854 let mut wrapped = key_wrap(&kek, b"secret");
855 wrapped[0] ^= 0xFF;
856 assert!(key_unwrap(&kek, &wrapped).is_none());
857 }
858
859 #[test]
861 fn secure_overwrite_changes_data() {
862 let mut data = vec![0xAA; 32];
863 secure_overwrite(&mut data);
864 assert!(data.iter().any(|&b| b != 0xAA));
865 }
866
867 #[test]
868 fn secure_buffer_zeroize() {
869 let mut buf = SecureBuffer::new(vec![0x42; 32]);
870 assert!(!buf.is_zeroized());
871 buf.zeroize();
872 assert!(buf.is_zeroized());
873 let unchanged = buf.as_slice().iter().filter(|&&b| b == 0x42).count();
877 assert!(
878 unchanged < 32,
879 "buffer was not overwritten (still all 0x42)"
880 );
881 }
882
883 #[test]
885 fn entropy_monitor_records() {
886 let mon = EntropyMonitor::new(10, 100);
887 mon.record(256);
888 mon.record(128);
889 mon.record(64);
890 assert_eq!(mon.sample_count(), 3);
891 assert_eq!(mon.latest(), Some(64));
892 assert!(mon.average() > 0.0);
893 }
894
895 #[test]
896 fn entropy_monitor_alerts() {
897 let mon = EntropyMonitor::new(10, 100);
898 mon.record(256);
899 mon.record(32);
900 assert_eq!(mon.alert_count(), 1);
901 assert!(mon.is_low());
902 }
903
904 #[test]
906 fn burst_bucket_allows_initial_burst() {
907 let bucket = BurstTokenBucket::new(10.0, 5.0); assert!(bucket.try_consume(50.0)); assert!(!bucket.try_consume(1.0)); }
911
912 #[test]
913 fn burst_bucket_refills_over_time() {
914 let bucket = BurstTokenBucket::new(1000.0, 1.0); bucket.try_consume(1000.0);
916 std::thread::sleep(std::time::Duration::from_millis(10));
917 assert!(bucket.try_consume(1.0)); }
919
920 #[test]
922 fn concurrent_runner_runs_tests() {
923 let runner = ConcurrentTestRunner::new();
924 runner.run("test1", || true);
925 runner.run("test2", || false);
926 assert_eq!(runner.total(), 2);
927 assert_eq!(runner.passed(), 1);
928 assert_eq!(runner.failed(), 1);
929 }
930
931 #[test]
932 fn concurrent_runner_parallel() {
933 type TestFn = (&'static str, fn() -> bool);
934 let runner = ConcurrentTestRunner::new();
935 let tests: Vec<TestFn> = vec![
936 ("t1", || true),
937 ("t2", || true),
938 ("t3", || true),
939 ("t4", || false),
940 ];
941 let passed = runner.run_concurrent(tests);
942 assert_eq!(passed, 3);
943 }
944
945 #[test]
947 fn spsc_send_recv() {
948 let (tx, rx) = spsc_channel::<i32>(10);
949 assert!(tx.send(42));
950 assert_eq!(rx.recv(), Some(42));
951 assert!(rx.is_empty());
952 }
953
954 #[test]
955 fn spsc_respects_capacity() {
956 let (tx, _rx) = spsc_channel::<i32>(2);
957 assert!(tx.send(1));
958 assert!(tx.send(2));
959 assert!(!tx.send(3)); }
961
962 #[test]
963 fn spsc_ordering() {
964 let (tx, rx) = spsc_channel::<i32>(10);
965 tx.send(1);
966 tx.send(2);
967 tx.send(3);
968 assert_eq!(rx.recv(), Some(1));
969 assert_eq!(rx.recv(), Some(2));
970 assert_eq!(rx.recv(), Some(3));
971 }
972
973 #[test]
974 fn spsc_counters() {
975 let (tx, rx) = spsc_channel::<i32>(10);
976 tx.send(1);
977 tx.send(2);
978 rx.recv();
979 assert_eq!(tx.total_sent(), 2);
980 assert_eq!(rx.total_received(), 1);
981 }
982}