1use crate::security::{
7 SecurityError, SecurityBuffer, SecurityNetworkParser,
8 SecurityHandshakeStateMachine, SecurityKey, KeyType,
9 SecurityHkdf, SecurityAesGcm, SecurityCompare, MessageType, HandshakeState
10};
11
12#[derive(Debug, Clone)]
14pub struct FuzzingConfig {
15 pub max_input_size: usize,
17 pub mutation_strategies: Vec<MutationStrategy>,
19 pub coverage_targets: Vec<CoverageTarget>,
21 pub timeout_ms: u64,
23 pub seed_count: usize,
25}
26
27#[derive(Debug, Clone, PartialEq, Eq)]
29pub enum MutationStrategy {
30 BitFlip,
32 ByteFlip,
34 Arithmetic,
36 InterestingValues,
38 BlockDeletion,
40 BlockDuplication,
42 BlockInsertion,
44 Dictionary,
46 Havoc,
48}
49
50#[derive(Debug, Clone, PartialEq, Eq)]
52pub enum CoverageTarget {
53 BufferBounds,
55 ProtocolParsing,
57 CryptographicOperations,
59 StateMachineTransitions,
61 MemoryHygiene,
63 ResourceExhaustion,
65 ConstantTimeOperations,
67}
68
69impl Default for FuzzingConfig {
70 fn default() -> Self {
71 FuzzingConfig {
72 max_input_size: 65536, mutation_strategies: vec![
74 MutationStrategy::BitFlip,
75 MutationStrategy::ByteFlip,
76 MutationStrategy::Arithmetic,
77 MutationStrategy::InterestingValues,
78 MutationStrategy::BlockDeletion,
79 MutationStrategy::BlockDuplication,
80 MutationStrategy::Havoc,
81 ],
82 coverage_targets: vec![
83 CoverageTarget::BufferBounds,
84 CoverageTarget::ProtocolParsing,
85 CoverageTarget::CryptographicOperations,
86 CoverageTarget::StateMachineTransitions,
87 CoverageTarget::MemoryHygiene,
88 CoverageTarget::ResourceExhaustion,
89 CoverageTarget::ConstantTimeOperations,
90 ],
91 timeout_ms: 1000, seed_count: 10000,
93 }
94 }
95}
96
97pub struct BufferFuzzingHarness {
99 config: FuzzingConfig,
100 _crash_inputs: Vec<Vec<u8>>,
101 slow_inputs: Vec<Vec<u8>>,
102 coverage_hits: std::collections::HashMap<String, usize>,
103}
104
105impl BufferFuzzingHarness {
106 pub fn new(config: FuzzingConfig) -> Self {
108 BufferFuzzingHarness {
109 config,
110 _crash_inputs: Vec::new(),
111 slow_inputs: Vec::new(),
112 coverage_hits: std::collections::HashMap::new(),
113 }
114 }
115
116 pub fn fuzz_buffer_operations(&mut self, input: &[u8]) -> FuzzingResult {
118 let start = std::time::Instant::now();
119
120 for size in [0, 1, 64, 1024, 65536, 1048576] {
122 match SecurityBuffer::new(size) {
123 Ok(mut buffer) => {
124 self.record_coverage("buffer_creation_success");
125
126 if !input.is_empty() && input.len() <= buffer.capacity() {
128 match buffer.write_slice(input) {
129 Ok(_) => self.record_coverage("buffer_write_success"),
130 Err(_) => self.record_coverage("buffer_write_failure"),
131 }
132 }
133
134 if buffer.len() > 0 {
136 let read_size = (input.len() % buffer.len()).max(1);
137 match buffer.read_exact(read_size) {
138 Ok(_) => self.record_coverage("buffer_read_success"),
139 Err(_) => self.record_coverage("buffer_read_failure"),
140 }
141 }
142
143 let new_pos = input.len() % (buffer.len() + 1);
145 match buffer.set_position(new_pos) {
146 Ok(_) => self.record_coverage("buffer_set_position_success"),
147 Err(_) => self.record_coverage("buffer_set_position_failure"),
148 }
149 },
150 Err(_) => self.record_coverage("buffer_creation_failure"),
151 }
152 }
153
154 self.test_buffer_edge_cases(input);
156
157 let elapsed = start.elapsed();
158 if elapsed.as_millis() > self.config.timeout_ms as u128 {
159 self.slow_inputs.push(input.to_vec());
160 return FuzzingResult::Slow;
161 }
162
163 FuzzingResult::Success
164 }
165
166 fn test_buffer_edge_cases(&mut self, _input: &[u8]) {
167 match SecurityBuffer::new(1048576) { Ok(mut buffer) => {
170 let large_data = vec![0x42u8; 1048576];
172 match buffer.write_slice(&large_data) {
173 Ok(_) => self.record_coverage("max_buffer_write"),
174 Err(_) => self.record_coverage("max_buffer_write_failure"),
175 }
176 },
177 Err(_) => self.record_coverage("max_buffer_creation_failure"),
178 }
179
180 match SecurityBuffer::new(0) {
182 Ok(mut buffer) => {
183 match buffer.write_slice(b"test") {
184 Ok(_) => self.record_coverage("zero_buffer_write_unexpected"),
185 Err(_) => self.record_coverage("zero_buffer_write_expected"),
186 }
187 },
188 Err(_) => self.record_coverage("zero_buffer_creation_expected"),
189 }
190 }
191
192 fn record_coverage(&mut self, hit: &str) {
193 *self.coverage_hits.entry(hit.to_string()).or_insert(0) += 1;
194 }
195}
196
197pub struct NetworkFuzzingHarness {
199 config: FuzzingConfig,
200 parser: SecurityNetworkParser,
201 protocol_violations: Vec<ProtocolViolation>,
202}
203
204#[derive(Debug, Clone)]
206pub struct ProtocolViolation {
207 pub violation_type: String,
209 pub input: Vec<u8>,
211 pub error: SecurityError,
213}
214
215impl NetworkFuzzingHarness {
216 pub fn new(config: FuzzingConfig) -> Self {
218 NetworkFuzzingHarness {
219 config,
220 parser: SecurityNetworkParser::new(),
221 protocol_violations: Vec::new(),
222 }
223 }
224
225 pub fn fuzz_network_parsing(&mut self, input: &[u8]) -> FuzzingResult {
227 let start = std::time::Instant::now();
228
229 self.test_message_parsing(input);
231 self.test_header_parsing(input);
232 self.test_handshake_parsing(input);
233 self.test_data_parsing(input);
234
235 let elapsed = start.elapsed();
236 if elapsed.as_millis() > self.config.timeout_ms as u128 {
237 return FuzzingResult::Slow;
238 }
239
240 FuzzingResult::Success
241 }
242
243 fn test_message_parsing(&mut self, input: &[u8]) {
244 match self.parser.parse_message(input) {
245 Ok(message) => {
246 if message.header.payload_length as usize != message.payload.len() {
248 self.record_violation("payload_length_mismatch", input,
249 SecurityError::InvalidLength {
250 expected: message.header.payload_length as usize,
251 actual: message.payload.len(),
252 });
253 }
254 },
255 Err(error) => {
256 match error {
258 SecurityError::BufferTooSmall { .. } => {},
259 SecurityError::InvalidProtocolVersion { .. } => {},
260 SecurityError::InvalidMessageType(_) => {},
261 _ => self.record_violation("unexpected_parse_error", input, error),
262 }
263 }
264 }
265 }
266
267 fn test_header_parsing(&mut self, input: &[u8]) {
268 if input.len() >= 24 { match self.parser.parse_header(&input[..24]) {
270 Ok(header) => {
271 if header.payload_length > 1048576 { self.record_violation("excessive_payload_length", input,
274 SecurityError::ResourceExhaustionProtection {
275 resource: "payload_length".to_string(),
276 limit: 1048576,
277 requested: header.payload_length as usize,
278 });
279 }
280 },
281 Err(error) => {
282 match error {
284 SecurityError::InvalidProtocolVersion { .. } => {},
285 SecurityError::InvalidMessageType(_) => {},
286 SecurityError::InvalidCipherSuite(_) => {},
287 _ => self.record_violation("unexpected_header_error", input, error),
288 }
289 }
290 }
291 }
292 }
293
294 fn test_handshake_parsing(&mut self, input: &[u8]) {
295 let handshake_types = [MessageType::HandshakeInit, MessageType::HandshakeResponse, MessageType::HandshakeComplete];
297
298 for handshake_type in &handshake_types {
299 match self.parser.parse_handshake_message(input, *handshake_type) {
300 Ok(_) => {
301 },
303 Err(error) => {
304 match error {
306 SecurityError::InvalidMessageType(_) => {},
307 SecurityError::InvalidCipherSuite(_) => {},
308 _ => self.record_violation("unexpected_handshake_error", input, error),
309 }
310 }
311 }
312 }
313 }
314
315 fn test_data_parsing(&mut self, input: &[u8]) {
316 match self.parser.parse_data_message(input) {
317 Ok(_) => {
318 },
320 Err(error) => {
321 match error {
323 SecurityError::InvalidMessageType(_) => {},
324 SecurityError::InvalidHkdfInput { .. } => {},
325 _ => self.record_violation("unexpected_data_error", input, error),
326 }
327 }
328 }
329 }
330
331 fn record_violation(&mut self, violation_type: &str, input: &[u8], error: SecurityError) {
332 self.protocol_violations.push(ProtocolViolation {
333 violation_type: violation_type.to_string(),
334 input: input.to_vec(),
335 error,
336 });
337 }
338}
339
340pub struct CryptoFuzzingHarness {
342 config: FuzzingConfig,
343 timing_leaks: Vec<TimingLeak>,
344}
345
346#[derive(Debug, Clone)]
348pub struct TimingLeak {
349 pub operation: String,
351 pub input_size: usize,
353 pub timing_variance: f64,
355}
356
357impl CryptoFuzzingHarness {
358 pub fn new(config: FuzzingConfig) -> Self {
360 CryptoFuzzingHarness {
361 config,
362 timing_leaks: Vec::new(),
363 }
364 }
365
366 pub fn fuzz_crypto_operations(&mut self, input: &[u8]) -> FuzzingResult {
368 let start = std::time::Instant::now();
369
370 self.test_key_operations(input);
372
373 self.test_hkdf_operations(input);
375
376 self.test_aes_gcm_operations(input);
378
379 self.test_constant_time_operations(input);
381
382 let elapsed = start.elapsed();
383 if elapsed.as_millis() > self.config.timeout_ms as u128 {
384 return FuzzingResult::Slow;
385 }
386
387 FuzzingResult::Success
388 }
389
390 fn test_key_operations(&mut self, input: &[u8]) {
391 for key_type in [KeyType::Encryption, KeyType::Authentication, KeyType::Metadata] {
393 match SecurityKey::from_slice(input, key_type) {
394 Ok(key) => {
395 let _ = key.as_slice();
397 let _ = key.key_type();
398 let _ = key.len();
399 },
400 Err(error) => {
401 match error {
403 SecurityError::InvalidKey { .. } => {},
404 _ => println!("Unexpected key error: {:?}", error),
405 }
406 }
407 }
408 }
409 }
410
411 fn test_hkdf_operations(&mut self, input: &[u8]) {
412 if input.len() >= 32 { let salt = if input.len() > 32 { Some(&input[32..]) } else { None };
414 let info = b"test info";
415
416 match SecurityHkdf::derive_keys(input, salt, info, 32) {
417 Ok(_) => {
418 },
420 Err(error) => {
421 match error {
423 SecurityError::InvalidHkdfInput { .. } => {},
424 _ => println!("Unexpected HKDF error: {:?}", error),
425 }
426 }
427 }
428 }
429 }
430
431 fn test_aes_gcm_operations(&mut self, input: &[u8]) {
432 if input.len() >= 44 { let key_data = &input[..32];
434 let nonce = &input[32..44];
435 let plaintext = if input.len() > 44 { &input[44..] } else { b"test" };
436
437 match SecurityKey::from_slice(key_data, KeyType::Encryption) {
438 Ok(key) => {
439 match SecurityAesGcm::encrypt(&key, nonce, plaintext, None) {
441 Ok(ciphertext) => {
442 match SecurityAesGcm::decrypt(&key, nonce, &ciphertext, None) {
444 Ok(decrypted) => {
445 if decrypted != plaintext {
447 println!("Decryption mismatch detected");
448 }
449 },
450 Err(error) => {
451 match error {
452 SecurityError::InvalidHkdfInput { .. } => {},
453 _ => println!("Unexpected decryption error: {:?}", error),
454 }
455 }
456 }
457 },
458 Err(error) => {
459 match error {
460 SecurityError::InvalidHkdfInput { .. } => {},
461 SecurityError::InvalidNonce { .. } => {},
462 _ => println!("Unexpected encryption error: {:?}", error),
463 }
464 }
465 }
466 },
467 Err(error) => {
468 match error {
469 SecurityError::InvalidKey { .. } => {},
470 _ => println!("Unexpected key error: {:?}", error),
471 }
472 }
473 }
474 }
475 }
476
477 fn test_constant_time_operations(&mut self, input: &[u8]) {
478 if input.len() >= 64 {
480 let a = &input[..32];
481 let b = &input[32..64];
482
483 let mut timings = Vec::new();
485 for _ in 0..100 {
486 let start = std::time::Instant::now();
487 let _ = SecurityCompare::constant_time_eq(a, b);
488 let elapsed = start.elapsed();
489 timings.push(elapsed.as_nanos() as f64);
490 }
491
492 let mean = timings.iter().sum::<f64>() / timings.len() as f64;
494 let variance = timings.iter().map(|t| (t - mean).powi(2)).sum::<f64>() / timings.len() as f64;
495
496 if variance > mean * 0.1 {
498 self.timing_leaks.push(TimingLeak {
499 operation: "constant_time_eq".to_string(),
500 input_size: a.len(),
501 timing_variance: variance,
502 });
503 }
504 }
505 }
506}
507
508pub struct StateMachineFuzzingHarness {
510 config: FuzzingConfig,
511 invalid_transitions: Vec<InvalidTransition>,
512}
513
514#[derive(Debug, Clone)]
516pub struct InvalidTransition {
517 pub from_state: HandshakeState,
519 pub to_state: HandshakeState,
521 pub input: Vec<u8>,
523}
524
525impl StateMachineFuzzingHarness {
526 pub fn new(config: FuzzingConfig) -> Self {
528 StateMachineFuzzingHarness {
529 config,
530 invalid_transitions: Vec::new(),
531 }
532 }
533
534 pub fn fuzz_state_machine(&mut self, input: &[u8]) -> FuzzingResult {
536 let start = std::time::Instant::now();
537
538 self.test_all_transitions(input);
540
541 self.test_state_machine_inputs(input);
543
544 let elapsed = start.elapsed();
545 if elapsed.as_millis() > self.config.timeout_ms as u128 {
546 return FuzzingResult::Slow;
547 }
548
549 FuzzingResult::Success
550 }
551
552 fn test_all_transitions(&mut self, input: &[u8]) {
553 let all_states = [
554 HandshakeState::Init,
555 HandshakeState::WaitingResponse,
556 HandshakeState::WaitingComplete,
557 HandshakeState::Completed,
558 HandshakeState::Failed,
559 ];
560
561 for from_state in &all_states {
562 for to_state in &all_states {
563 let mut sm = match SecurityHandshakeStateMachine::new(16384) {
564 Ok(sm) => sm,
565 Err(_) => continue,
566 };
567
568 match sm.transition_state(*to_state) {
572 Ok(_) => {
573 },
575 Err(_) => {
576 self.invalid_transitions.push(InvalidTransition {
578 from_state: *from_state,
579 to_state: *to_state,
580 input: input.to_vec(),
581 });
582 }
583 }
584 }
585 }
586 }
587
588 fn test_state_machine_inputs(&mut self, input: &[u8]) {
589 let mut sm = match SecurityHandshakeStateMachine::new(16384) {
590 Ok(sm) => sm,
591 Err(_) => return,
592 };
593
594 for size in [0, 1, 64, 1024, 16384] {
596 if input.len() >= size {
597 let test_input = &input[..size];
598
599 match sm.process_init(test_input) {
601 Ok(_) => {
602 },
604 Err(error) => {
605 match error {
606 SecurityError::ResourceExhaustionProtection { .. } => {},
607 SecurityError::InvalidHkdfInput { .. } => {},
608 _ => println!("Unexpected init error: {:?}", error),
609 }
610 }
611 }
612
613 let _ = sm.reset();
615 }
616 }
617 }
618}
619
620#[derive(Debug, Clone, PartialEq, Eq)]
622pub enum FuzzingResult {
623 Success,
625 Crash(String),
627 Slow,
629 Timeout,
631 MemoryExhaustion,
633}
634
635pub struct SecurityFuzzingOrchestrator {
637 config: FuzzingConfig,
638 buffer_harness: BufferFuzzingHarness,
639 network_harness: NetworkFuzzingHarness,
640 crypto_harness: CryptoFuzzingHarness,
641 state_machine_harness: StateMachineFuzzingHarness,
642 results: FuzzingResults,
643}
644
645#[derive(Debug, Default, Clone)]
647pub struct FuzzingResults {
648 pub total_runs: usize,
650 pub successful_runs: usize,
652 pub crashes: Vec<(String, Vec<u8>)>,
654 pub slow_inputs: Vec<Vec<u8>>,
656 pub protocol_violations: Vec<ProtocolViolation>,
658 pub timing_leaks: Vec<TimingLeak>,
660 pub invalid_transitions: Vec<InvalidTransition>,
662 pub coverage_stats: std::collections::HashMap<String, usize>,
664}
665
666impl SecurityFuzzingOrchestrator {
667 pub fn new(config: FuzzingConfig) -> Self {
669 SecurityFuzzingOrchestrator {
670 config: config.clone(),
671 buffer_harness: BufferFuzzingHarness::new(config.clone()),
672 network_harness: NetworkFuzzingHarness::new(config.clone()),
673 crypto_harness: CryptoFuzzingHarness::new(config.clone()),
674 state_machine_harness: StateMachineFuzzingHarness::new(config.clone()),
675 results: FuzzingResults::default(),
676 }
677 }
678
679 pub fn run_fuzzing_campaign(&mut self, duration_seconds: u64) -> FuzzingResults {
681 let start = std::time::Instant::now();
682 let duration = std::time::Duration::from_secs(duration_seconds);
683
684 while start.elapsed() < duration {
685 let input = self.generate_random_input();
687
688 self.run_all_harnesses(&input);
690
691 self.results.total_runs += 1;
692 }
693
694 self.collect_results();
696 self.results.clone()
697 }
698
699 fn generate_random_input(&self) -> Vec<u8> {
700 use rand::Rng;
701 let mut rng = rand::thread_rng();
702 let size = rng.gen_range(0..self.config.max_input_size);
703 (0..size).map(|_| rng.gen()).collect()
704 }
705
706 fn run_all_harnesses(&mut self, input: &[u8]) {
707 match self.buffer_harness.fuzz_buffer_operations(input) {
709 FuzzingResult::Success => self.results.successful_runs += 1,
710 FuzzingResult::Crash(msg) => self.results.crashes.push((msg, input.to_vec())),
711 FuzzingResult::Slow => self.results.slow_inputs.push(input.to_vec()),
712 _ => {},
713 }
714
715 match self.network_harness.fuzz_network_parsing(input) {
717 FuzzingResult::Success => {},
718 FuzzingResult::Crash(msg) => self.results.crashes.push((msg, input.to_vec())),
719 _ => {},
720 }
721
722 match self.crypto_harness.fuzz_crypto_operations(input) {
724 FuzzingResult::Success => {},
725 FuzzingResult::Crash(msg) => self.results.crashes.push((msg, input.to_vec())),
726 _ => {},
727 }
728
729 match self.state_machine_harness.fuzz_state_machine(input) {
731 FuzzingResult::Success => {},
732 FuzzingResult::Crash(msg) => self.results.crashes.push((msg, input.to_vec())),
733 _ => {},
734 }
735 }
736
737 fn collect_results(&mut self) {
738 self.results.protocol_violations.extend(
740 self.network_harness.protocol_violations.clone()
741 );
742
743 self.results.timing_leaks.extend(
745 self.crypto_harness.timing_leaks.clone()
746 );
747
748 self.results.invalid_transitions.extend(
750 self.state_machine_harness.invalid_transitions.clone()
751 );
752
753 self.results.coverage_stats.extend(
755 self.buffer_harness.coverage_hits.clone()
756 );
757 }
758
759 pub fn generate_report(&self) -> String {
761 let mut report = String::new();
762 report.push_str("B4AE Security Fuzzing Report\n");
763 report.push_str("===========================\n\n");
764
765 report.push_str(&format!("Total runs: {}\n", self.results.total_runs));
766 report.push_str(&format!("Successful runs: {}\n", self.results.successful_runs));
767 report.push_str(&format!("Crashes: {}\n", self.results.crashes.len()));
768 report.push_str(&format!("Slow inputs: {}\n", self.results.slow_inputs.len()));
769 report.push_str(&format!("Protocol violations: {}\n", self.results.protocol_violations.len()));
770 report.push_str(&format!("Timing leaks: {}\n", self.results.timing_leaks.len()));
771 report.push_str(&format!("Invalid transitions: {}\n", self.results.invalid_transitions.len()));
772
773 if !self.results.crashes.is_empty() {
774 report.push_str("\nCritical Issues Found:\n");
775 for (i, (msg, _)) in self.results.crashes.iter().enumerate() {
776 report.push_str(&format!(" {}. {}\n", i + 1, msg));
777 }
778 }
779
780 if !self.results.protocol_violations.is_empty() {
781 report.push_str("\nProtocol Violations:\n");
782 for (i, violation) in self.results.protocol_violations.iter().enumerate() {
783 report.push_str(&format!(" {}. {}: {:?}\n", i + 1, violation.violation_type, violation.error));
784 }
785 }
786
787 report
788 }
789}
790
791#[cfg(test)]
792mod tests {
793 use super::*;
794
795 #[test]
796 fn test_fuzzing_harness_creation() {
797 let config = FuzzingConfig::default();
798
799 let _buffer_harness = BufferFuzzingHarness::new(config.clone());
800 let _network_harness = NetworkFuzzingHarness::new(config.clone());
801 let _crypto_harness = CryptoFuzzingHarness::new(config.clone());
802 let _state_machine_harness = StateMachineFuzzingHarness::new(config.clone());
803
804 let orchestrator = SecurityFuzzingOrchestrator::new(config);
805
806 assert_eq!(orchestrator.results.total_runs, 0);
808 assert_eq!(orchestrator.results.successful_runs, 0);
809 }
810
811 #[test]
812 fn test_fuzzing_with_sample_inputs() {
813 let config = FuzzingConfig {
814 max_input_size: 1024,
815 seed_count: 100,
816 ..Default::default()
817 };
818
819 let mut orchestrator = SecurityFuzzingOrchestrator::new(config);
820
821 let sample_inputs = vec![
823 vec![], vec![0x00], vec![0xFF; 100], vec![0x00, 0x01, 0x02, 0x03], b"B4AE_PROTOCOL_HEADER".to_vec(), ];
829
830 for input in sample_inputs {
831 orchestrator.run_all_harnesses(&input);
832 }
833
834 assert!(orchestrator.results.crashes.is_empty());
836 assert!(orchestrator.results.total_runs >= 5);
837 }
838
839 #[test]
840 fn test_report_generation() {
841 let config = FuzzingConfig::default();
842 let orchestrator = SecurityFuzzingOrchestrator::new(config);
843
844 let report = orchestrator.generate_report();
845
846 assert!(report.contains("B4AE Security Fuzzing Report"));
848 assert!(report.contains("Total runs:"));
849 assert!(report.contains("Successful runs:"));
850 assert!(report.contains("Crashes:"));
851 }
852}