oscura 0.8.0__py3-none-any.whl → 0.11.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. oscura/__init__.py +19 -19
  2. oscura/__main__.py +4 -0
  3. oscura/analyzers/__init__.py +2 -0
  4. oscura/analyzers/digital/extraction.py +2 -3
  5. oscura/analyzers/digital/quality.py +1 -1
  6. oscura/analyzers/digital/timing.py +1 -1
  7. oscura/analyzers/ml/signal_classifier.py +6 -0
  8. oscura/analyzers/patterns/__init__.py +66 -0
  9. oscura/analyzers/power/basic.py +3 -3
  10. oscura/analyzers/power/soa.py +1 -1
  11. oscura/analyzers/power/switching.py +3 -3
  12. oscura/analyzers/signal_classification.py +529 -0
  13. oscura/analyzers/signal_integrity/sparams.py +3 -3
  14. oscura/analyzers/statistics/basic.py +10 -7
  15. oscura/analyzers/validation.py +1 -1
  16. oscura/analyzers/waveform/measurements.py +200 -156
  17. oscura/analyzers/waveform/measurements_with_uncertainty.py +91 -35
  18. oscura/analyzers/waveform/spectral.py +182 -84
  19. oscura/api/dsl/commands.py +15 -6
  20. oscura/api/server/templates/base.html +137 -146
  21. oscura/api/server/templates/export.html +84 -110
  22. oscura/api/server/templates/home.html +248 -267
  23. oscura/api/server/templates/protocols.html +44 -48
  24. oscura/api/server/templates/reports.html +27 -35
  25. oscura/api/server/templates/session_detail.html +68 -78
  26. oscura/api/server/templates/sessions.html +62 -72
  27. oscura/api/server/templates/waveforms.html +54 -64
  28. oscura/automotive/__init__.py +1 -1
  29. oscura/automotive/can/session.py +1 -1
  30. oscura/automotive/dbc/generator.py +638 -23
  31. oscura/automotive/dtc/data.json +17 -102
  32. oscura/automotive/flexray/fibex.py +9 -1
  33. oscura/automotive/uds/decoder.py +99 -6
  34. oscura/cli/analyze.py +8 -2
  35. oscura/cli/batch.py +36 -5
  36. oscura/cli/characterize.py +18 -4
  37. oscura/cli/export.py +47 -5
  38. oscura/cli/main.py +2 -0
  39. oscura/cli/onboarding/wizard.py +10 -6
  40. oscura/cli/pipeline.py +585 -0
  41. oscura/cli/visualize.py +6 -4
  42. oscura/convenience.py +400 -32
  43. oscura/core/measurement_result.py +286 -0
  44. oscura/core/progress.py +1 -1
  45. oscura/core/schemas/device_mapping.json +2 -8
  46. oscura/core/schemas/packet_format.json +4 -24
  47. oscura/core/schemas/protocol_definition.json +2 -12
  48. oscura/core/types.py +232 -239
  49. oscura/correlation/multi_protocol.py +1 -1
  50. oscura/export/legacy/__init__.py +11 -0
  51. oscura/export/legacy/wav.py +75 -0
  52. oscura/exporters/__init__.py +19 -0
  53. oscura/exporters/wireshark.py +809 -0
  54. oscura/hardware/acquisition/file.py +5 -19
  55. oscura/hardware/acquisition/saleae.py +10 -10
  56. oscura/hardware/acquisition/socketcan.py +4 -6
  57. oscura/hardware/acquisition/synthetic.py +1 -5
  58. oscura/hardware/acquisition/visa.py +6 -6
  59. oscura/hardware/security/side_channel_detector.py +5 -508
  60. oscura/inference/message_format.py +686 -1
  61. oscura/jupyter/display.py +2 -2
  62. oscura/jupyter/magic.py +3 -3
  63. oscura/loaders/__init__.py +17 -12
  64. oscura/loaders/binary.py +1 -1
  65. oscura/loaders/chipwhisperer.py +1 -2
  66. oscura/loaders/configurable.py +1 -1
  67. oscura/loaders/csv_loader.py +2 -2
  68. oscura/loaders/hdf5_loader.py +1 -1
  69. oscura/loaders/lazy.py +6 -1
  70. oscura/loaders/mmap_loader.py +0 -1
  71. oscura/loaders/numpy_loader.py +8 -7
  72. oscura/loaders/preprocessing.py +3 -5
  73. oscura/loaders/rigol.py +21 -7
  74. oscura/loaders/sigrok.py +2 -5
  75. oscura/loaders/tdms.py +3 -2
  76. oscura/loaders/tektronix.py +38 -32
  77. oscura/loaders/tss.py +20 -27
  78. oscura/loaders/validation.py +17 -10
  79. oscura/loaders/vcd.py +13 -8
  80. oscura/loaders/wav.py +1 -6
  81. oscura/pipeline/__init__.py +76 -0
  82. oscura/pipeline/handlers/__init__.py +165 -0
  83. oscura/pipeline/handlers/analyzers.py +1045 -0
  84. oscura/pipeline/handlers/decoders.py +899 -0
  85. oscura/pipeline/handlers/exporters.py +1103 -0
  86. oscura/pipeline/handlers/filters.py +891 -0
  87. oscura/pipeline/handlers/loaders.py +640 -0
  88. oscura/pipeline/handlers/transforms.py +768 -0
  89. oscura/reporting/formatting/measurements.py +55 -14
  90. oscura/reporting/templates/enhanced/protocol_re.html +504 -503
  91. oscura/sessions/legacy.py +49 -1
  92. oscura/side_channel/__init__.py +38 -57
  93. oscura/utils/builders/signal_builder.py +5 -5
  94. oscura/utils/comparison/compare.py +7 -9
  95. oscura/utils/comparison/golden.py +1 -1
  96. oscura/utils/filtering/convenience.py +2 -2
  97. oscura/utils/math/arithmetic.py +38 -62
  98. oscura/utils/math/interpolation.py +20 -20
  99. oscura/utils/pipeline/__init__.py +4 -17
  100. oscura/utils/progressive.py +1 -4
  101. oscura/utils/triggering/edge.py +1 -1
  102. oscura/utils/triggering/pattern.py +2 -2
  103. oscura/utils/triggering/pulse.py +2 -2
  104. oscura/utils/triggering/window.py +3 -3
  105. oscura/validation/hil_testing.py +11 -11
  106. oscura/visualization/__init__.py +46 -284
  107. oscura/visualization/batch.py +72 -433
  108. oscura/visualization/plot.py +542 -53
  109. oscura/visualization/styles.py +184 -318
  110. oscura/workflows/batch/advanced.py +1 -1
  111. oscura/workflows/batch/aggregate.py +12 -9
  112. oscura/workflows/complete_re.py +251 -23
  113. oscura/workflows/digital.py +27 -4
  114. oscura/workflows/multi_trace.py +136 -17
  115. oscura/workflows/waveform.py +11 -6
  116. oscura-0.11.0.dist-info/METADATA +460 -0
  117. {oscura-0.8.0.dist-info → oscura-0.11.0.dist-info}/RECORD +120 -145
  118. oscura/side_channel/dpa.py +0 -1025
  119. oscura/utils/optimization/__init__.py +0 -19
  120. oscura/utils/optimization/parallel.py +0 -443
  121. oscura/utils/optimization/search.py +0 -532
  122. oscura/utils/pipeline/base.py +0 -338
  123. oscura/utils/pipeline/composition.py +0 -248
  124. oscura/utils/pipeline/parallel.py +0 -449
  125. oscura/utils/pipeline/pipeline.py +0 -375
  126. oscura/utils/search/__init__.py +0 -16
  127. oscura/utils/search/anomaly.py +0 -424
  128. oscura/utils/search/context.py +0 -294
  129. oscura/utils/search/pattern.py +0 -288
  130. oscura/utils/storage/__init__.py +0 -61
  131. oscura/utils/storage/database.py +0 -1166
  132. oscura/visualization/accessibility.py +0 -526
  133. oscura/visualization/annotations.py +0 -371
  134. oscura/visualization/axis_scaling.py +0 -305
  135. oscura/visualization/colors.py +0 -451
  136. oscura/visualization/digital.py +0 -436
  137. oscura/visualization/eye.py +0 -571
  138. oscura/visualization/histogram.py +0 -281
  139. oscura/visualization/interactive.py +0 -1035
  140. oscura/visualization/jitter.py +0 -1042
  141. oscura/visualization/keyboard.py +0 -394
  142. oscura/visualization/layout.py +0 -400
  143. oscura/visualization/optimization.py +0 -1079
  144. oscura/visualization/palettes.py +0 -446
  145. oscura/visualization/power.py +0 -508
  146. oscura/visualization/power_extended.py +0 -955
  147. oscura/visualization/presets.py +0 -469
  148. oscura/visualization/protocols.py +0 -1246
  149. oscura/visualization/render.py +0 -223
  150. oscura/visualization/rendering.py +0 -444
  151. oscura/visualization/reverse_engineering.py +0 -838
  152. oscura/visualization/signal_integrity.py +0 -989
  153. oscura/visualization/specialized.py +0 -643
  154. oscura/visualization/spectral.py +0 -1226
  155. oscura/visualization/thumbnails.py +0 -340
  156. oscura/visualization/time_axis.py +0 -351
  157. oscura/visualization/waveform.py +0 -454
  158. oscura-0.8.0.dist-info/METADATA +0 -661
  159. {oscura-0.8.0.dist-info → oscura-0.11.0.dist-info}/WHEEL +0 -0
  160. {oscura-0.8.0.dist-info → oscura-0.11.0.dist-info}/entry_points.txt +0 -0
  161. {oscura-0.8.0.dist-info → oscura-0.11.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,14 +1,10 @@
1
1
  """Side-Channel Attack Detection and Vulnerability Assessment.
2
2
 
3
3
  This module implements comprehensive side-channel vulnerability detection for
4
- cryptographic implementations including timing attacks, power analysis, EM
5
- emissions, and cache timing vulnerabilities.
4
+ cryptographic implementations including timing attacks and constant-time validation.
6
5
 
7
6
  Key capabilities:
8
7
  - Timing-based leakage detection (variable-time operations)
9
- - Power analysis vulnerability detection (data-dependent consumption)
10
- - EM emission analysis for information leakage
11
- - Cache timing attack detection
12
8
  - Constant-time operation validation
13
9
  - T-test for leakage detection (Welch's t-test)
14
10
  - Mutual information calculation
@@ -18,35 +14,25 @@ Typical use cases:
18
14
  - Evaluate cryptographic implementation security
19
15
  - Detect non-constant-time operations
20
16
  - Identify data-dependent branching
21
- - Assess power consumption leakage
22
17
  - Generate vulnerability reports for security audits
23
18
 
24
19
  Example:
25
20
  >>> from oscura.hardware.security.side_channel_detector import SideChannelDetector
26
- >>> from oscura.side_channel.dpa import PowerTrace
27
21
  >>> import numpy as np
28
22
  >>> # Create detector
29
23
  >>> detector = SideChannelDetector(
30
24
  ... timing_threshold=0.01,
31
- ... power_threshold=0.7,
32
25
  ... ttest_threshold=4.5
33
26
  ... )
34
- >>> # Analyze power traces
35
- >>> traces = [
36
- ... PowerTrace(
37
- ... timestamp=np.arange(1000),
38
- ... power=np.random.randn(1000),
39
- ... plaintext=bytes([i % 256 for i in range(16)])
40
- ... )
41
- ... for _ in range(100)
42
- ... ]
43
- >>> report = detector.analyze_power_traces(traces, fixed_key=bytes(16))
44
- >>> print(f"Found {len(report.vulnerabilities)} vulnerabilities")
45
27
  >>> # Check for timing vulnerabilities
46
28
  >>> timing_data = [(bytes([i]), 0.001 + i*1e-6) for i in range(256)]
47
29
  >>> result = detector.detect_timing_leakage(timing_data)
48
30
  >>> if result.severity != "low":
49
31
  ... print(f"Timing vulnerability: {result.evidence}")
32
+
33
+ Note:
34
+ For actual power analysis attacks (DPA/CPA), use ChipWhisperer:
35
+ https://chipwhisperer.com/
50
36
  """
51
37
 
52
38
  from __future__ import annotations
@@ -66,8 +52,6 @@ if TYPE_CHECKING:
66
52
 
67
53
  from numpy.typing import NDArray
68
54
 
69
- from oscura.side_channel.dpa import PowerTrace
70
-
71
55
  logger = logging.getLogger(__name__)
72
56
 
73
57
 
@@ -75,8 +59,6 @@ class VulnerabilityType(str, Enum):
75
59
  """Type of side-channel vulnerability detected."""
76
60
 
77
61
  TIMING = "timing" # Timing-based leakage
78
- POWER = "power" # Power consumption leakage
79
- EM = "electromagnetic" # EM emission leakage
80
62
  CACHE = "cache" # Cache timing leakage
81
63
  CONSTANT_TIME = "constant_time" # Non-constant-time operations
82
64
 
@@ -187,8 +169,6 @@ class SideChannelDetector:
187
169
  def __init__(
188
170
  self,
189
171
  timing_threshold: float = 0.01,
190
- power_threshold: float = 0.7,
191
- em_threshold: float = 0.6,
192
172
  cache_threshold: float = 0.05,
193
173
  ttest_threshold: float = 4.5,
194
174
  mutual_info_threshold: float = 0.1,
@@ -197,8 +177,6 @@ class SideChannelDetector:
197
177
 
198
178
  Args:
199
179
  timing_threshold: Timing correlation threshold for vulnerability (0.0-1.0).
200
- power_threshold: Power correlation threshold for vulnerability (0.0-1.0).
201
- em_threshold: EM emission correlation threshold (0.0-1.0).
202
180
  cache_threshold: Cache timing threshold for vulnerability (0.0-1.0).
203
181
  ttest_threshold: T-test statistic threshold (typically 4.5 for p<0.00001).
204
182
  mutual_info_threshold: Mutual information threshold in bits (0.0-8.0).
@@ -211,8 +189,6 @@ class SideChannelDetector:
211
189
  ... )
212
190
  """
213
191
  self.timing_threshold = timing_threshold
214
- self.power_threshold = power_threshold
215
- self.em_threshold = em_threshold
216
192
  self.cache_threshold = cache_threshold
217
193
  self.ttest_threshold = ttest_threshold
218
194
  self.mutual_info_threshold = mutual_info_threshold
@@ -392,302 +368,6 @@ class SideChannelDetector:
392
368
  )
393
369
  return f"Timing analysis of '{operation_name}' shows {significance} input-dependent execution time"
394
370
 
395
- def analyze_power_traces(
396
- self,
397
- traces: Sequence[PowerTrace],
398
- fixed_key: bytes | None = None,
399
- use_ttest: bool = True,
400
- ) -> VulnerabilityReport:
401
- """Analyze power traces for DPA/CPA vulnerabilities.
402
-
403
- Performs comprehensive power analysis to detect data-dependent power
404
- consumption patterns that could enable DPA or CPA attacks.
405
-
406
- Analysis Methods:
407
- 1. Welch's t-test (TVLA) for first-order leakage
408
- 2. Correlation analysis between power and hypothetical values
409
- 3. Variance analysis across different inputs
410
- 4. Frequency-domain analysis for EM leakage
411
-
412
- Args:
413
- traces: List of power consumption traces with plaintexts.
414
- fixed_key: Known key for hypothesis testing (optional).
415
- use_ttest: Whether to perform t-test analysis.
416
-
417
- Returns:
418
- VulnerabilityReport with all detected power-related vulnerabilities.
419
-
420
- Example:
421
- >>> # Analyze AES implementation for DPA vulnerabilities
422
- >>> traces = collect_power_traces(plaintexts, key)
423
- >>> report = detector.analyze_power_traces(traces, fixed_key=key)
424
- >>> critical = [v for v in report.vulnerabilities
425
- ... if v.severity == Severity.CRITICAL]
426
- >>> print(f"Critical vulnerabilities: {len(critical)}")
427
- """
428
- if not traces:
429
- return VulnerabilityReport(
430
- vulnerabilities=[],
431
- summary_statistics={"error": "No traces provided"},
432
- )
433
-
434
- power_matrix = np.array([t.power for t in traces])
435
- num_traces, num_samples = power_matrix.shape
436
-
437
- vulnerabilities = self._collect_power_vulnerabilities(
438
- traces, power_matrix, use_ttest, fixed_key
439
- )
440
- summary_stats = self._build_summary_statistics(vulnerabilities, num_traces, num_samples)
441
- recommendations = self._generate_recommendations(vulnerabilities)
442
-
443
- return VulnerabilityReport(
444
- vulnerabilities=vulnerabilities,
445
- summary_statistics=summary_stats,
446
- analysis_config={
447
- "power_threshold": self.power_threshold,
448
- "ttest_threshold": self.ttest_threshold,
449
- "use_ttest": use_ttest,
450
- },
451
- recommendations=recommendations,
452
- )
453
-
454
- def _collect_power_vulnerabilities(
455
- self,
456
- traces: Sequence[PowerTrace],
457
- power_matrix: np.ndarray[Any, Any],
458
- use_ttest: bool,
459
- fixed_key: bytes | None,
460
- ) -> list[SideChannelVulnerability]:
461
- """Collect all power-related vulnerabilities from traces.
462
-
463
- Args:
464
- traces: Power consumption traces.
465
- power_matrix: 2D array of power measurements.
466
- use_ttest: Whether to perform t-test analysis.
467
- fixed_key: Known key for CPA analysis.
468
-
469
- Returns:
470
- List of detected vulnerabilities.
471
- """
472
- vulnerabilities: list[SideChannelVulnerability] = []
473
-
474
- # T-test analysis
475
- if use_ttest and len(traces) >= 10:
476
- ttest_vuln = self._analyze_ttest_leakage(traces, power_matrix)
477
- if ttest_vuln is not None:
478
- vulnerabilities.append(ttest_vuln)
479
-
480
- # Correlation analysis
481
- if fixed_key is not None and len(traces) >= 10:
482
- cpa_vuln = self._analyze_cpa_vulnerability(traces, fixed_key)
483
- if cpa_vuln is not None:
484
- vulnerabilities.append(cpa_vuln)
485
-
486
- # EM leakage analysis
487
- em_vuln = self._analyze_em_leakage(power_matrix)
488
- if em_vuln.severity != Severity.LOW:
489
- vulnerabilities.append(em_vuln)
490
-
491
- # Variance analysis
492
- variance_vuln = self._analyze_power_variance(traces)
493
- if variance_vuln.severity != Severity.LOW:
494
- vulnerabilities.append(variance_vuln)
495
-
496
- return vulnerabilities
497
-
498
- def _analyze_ttest_leakage(
499
- self, traces: Sequence[PowerTrace], power_matrix: np.ndarray[Any, Any]
500
- ) -> SideChannelVulnerability | None:
501
- """Analyze power traces using Welch's t-test for leakage.
502
-
503
- Args:
504
- traces: Power consumption traces.
505
- power_matrix: 2D array of power measurements.
506
-
507
- Returns:
508
- Vulnerability if leakage detected, None otherwise.
509
- """
510
- t_stats = self._perform_ttest_leakage(traces)
511
- if t_stats is None:
512
- return None
513
-
514
- num_samples = power_matrix.shape[1]
515
- max_t_stat = float(np.max(np.abs(t_stats)))
516
- leakage_points = int(np.sum(np.abs(t_stats) > self.ttest_threshold))
517
-
518
- if max_t_stat <= self.ttest_threshold:
519
- return None
520
-
521
- severity = self._assess_ttest_severity(max_t_stat)
522
- confidence = min(1.0, len(traces) / 100.0)
523
-
524
- return SideChannelVulnerability(
525
- vulnerability_type=VulnerabilityType.POWER,
526
- severity=severity,
527
- confidence=confidence,
528
- evidence=(
529
- f"Max T-statistic: {max_t_stat:.2f}, Leakage points: {leakage_points}/{num_samples}"
530
- ),
531
- description="Welch's t-test reveals significant first-order power leakage",
532
- mitigation_suggestions=[
533
- "Implement power-balanced logic gates",
534
- "Add random noise to power consumption",
535
- "Use masking or hiding countermeasures",
536
- "Employ dual-rail precharge logic (DPL)",
537
- ],
538
- metadata={
539
- "max_t_statistic": max_t_stat,
540
- "leakage_points": leakage_points,
541
- "threshold": self.ttest_threshold,
542
- },
543
- )
544
-
545
- def _assess_ttest_severity(self, max_t_stat: float) -> Severity:
546
- """Assess severity based on t-test statistic magnitude.
547
-
548
- Args:
549
- max_t_stat: Maximum t-statistic value.
550
-
551
- Returns:
552
- Severity level.
553
- """
554
- if max_t_stat > 20.0:
555
- return Severity.CRITICAL
556
- if max_t_stat > 10.0:
557
- return Severity.HIGH
558
- if max_t_stat > self.ttest_threshold:
559
- return Severity.MEDIUM
560
- return Severity.LOW
561
-
562
- def _analyze_cpa_vulnerability(
563
- self, traces: Sequence[PowerTrace], fixed_key: bytes
564
- ) -> SideChannelVulnerability | None:
565
- """Analyze CPA vulnerability using correlation analysis.
566
-
567
- Args:
568
- traces: Power consumption traces.
569
- fixed_key: Known encryption key.
570
-
571
- Returns:
572
- Vulnerability if CPA attack successful, None otherwise.
573
- """
574
- from oscura.side_channel.dpa import DPAAnalyzer
575
-
576
- analyzer = DPAAnalyzer(attack_type="cpa", leakage_model="hamming_weight")
577
-
578
- try:
579
- result = analyzer.cpa_attack(list(traces), target_byte=0)
580
- if result.correlation_traces is None:
581
- return None
582
-
583
- max_correlation = float(np.max(result.correlation_traces))
584
- if max_correlation <= self.power_threshold:
585
- return None
586
-
587
- severity = self._assess_correlation_severity(max_correlation)
588
-
589
- return SideChannelVulnerability(
590
- vulnerability_type=VulnerabilityType.POWER,
591
- severity=severity,
592
- confidence=result.confidence,
593
- evidence=(
594
- f"Max correlation: {max_correlation:.4f}, "
595
- f"Attack confidence: {result.confidence:.2%}"
596
- ),
597
- description=(
598
- "CPA attack successful - power consumption correlates with Hamming weight"
599
- ),
600
- mitigation_suggestions=[
601
- "Implement algorithmic masking (boolean/arithmetic)",
602
- "Use shuffling to randomize operation order",
603
- "Add random delays between operations",
604
- "Employ constant-power hardware primitives",
605
- ],
606
- metadata={
607
- "max_correlation": max_correlation,
608
- "recovered_key_byte": int(result.recovered_key[0]),
609
- "attack_successful": result.successful,
610
- },
611
- )
612
- except Exception as e:
613
- logger.warning(f"CPA analysis failed: {e}")
614
- return None
615
-
616
- def _assess_correlation_severity(self, max_correlation: float) -> Severity:
617
- """Assess severity based on correlation magnitude.
618
-
619
- Args:
620
- max_correlation: Maximum correlation value.
621
-
622
- Returns:
623
- Severity level.
624
- """
625
- if max_correlation > 0.95:
626
- return Severity.CRITICAL
627
- if max_correlation > 0.85:
628
- return Severity.HIGH
629
- if max_correlation > self.power_threshold:
630
- return Severity.MEDIUM
631
- return Severity.LOW
632
-
633
- def _build_summary_statistics(
634
- self,
635
- vulnerabilities: list[SideChannelVulnerability],
636
- num_traces: int,
637
- num_samples: int,
638
- ) -> dict[str, Any]:
639
- """Build summary statistics from detected vulnerabilities.
640
-
641
- Args:
642
- vulnerabilities: List of detected vulnerabilities.
643
- num_traces: Number of power traces analyzed.
644
- num_samples: Number of samples per trace.
645
-
646
- Returns:
647
- Dictionary of summary statistics.
648
- """
649
- return {
650
- "total_vulnerabilities": len(vulnerabilities),
651
- "critical_count": sum(1 for v in vulnerabilities if v.severity == Severity.CRITICAL),
652
- "high_count": sum(1 for v in vulnerabilities if v.severity == Severity.HIGH),
653
- "medium_count": sum(1 for v in vulnerabilities if v.severity == Severity.MEDIUM),
654
- "low_count": sum(1 for v in vulnerabilities if v.severity == Severity.LOW),
655
- "num_traces": num_traces,
656
- "num_samples": num_samples,
657
- }
658
-
659
- def _generate_recommendations(
660
- self, vulnerabilities: list[SideChannelVulnerability]
661
- ) -> list[str]:
662
- """Generate security recommendations based on vulnerabilities.
663
-
664
- Args:
665
- vulnerabilities: List of detected vulnerabilities.
666
-
667
- Returns:
668
- List of recommendation strings.
669
- """
670
- recommendations = []
671
-
672
- if any(v.severity == Severity.CRITICAL for v in vulnerabilities):
673
- recommendations.append(
674
- "CRITICAL: Immediate countermeasures required - implementation is highly "
675
- "vulnerable to power analysis attacks"
676
- )
677
- if any(v.vulnerability_type == VulnerabilityType.POWER for v in vulnerabilities):
678
- recommendations.append(
679
- "Consider hardware countermeasures (noise generation, power filtering)"
680
- )
681
- if any(v.vulnerability_type == VulnerabilityType.EM for v in vulnerabilities):
682
- recommendations.append("Add EM shielding and filtering to reduce emissions")
683
-
684
- if not vulnerabilities:
685
- recommendations.append(
686
- "No significant vulnerabilities detected with current thresholds"
687
- )
688
-
689
- return recommendations
690
-
691
371
  def detect_constant_time_violation(
692
372
  self,
693
373
  timing_measurements: Sequence[tuple[Any, float]],
@@ -941,186 +621,3 @@ class SideChannelDetector:
941
621
  f.write("\n".join(html_content))
942
622
 
943
623
  logger.info(f"HTML report exported to {output_path}")
944
-
945
- def _perform_ttest_leakage(
946
- self,
947
- traces: Sequence[PowerTrace],
948
- ) -> NDArray[np.float64] | None:
949
- """Perform Welch's t-test for leakage detection (TVLA).
950
-
951
- Args:
952
- traces: Power traces with plaintexts.
953
-
954
- Returns:
955
- T-statistics for each sample point, or None if insufficient data.
956
- """
957
- if len(traces) < 10:
958
- return None
959
-
960
- # Partition traces by first plaintext byte (odd vs even)
961
- group0_traces = []
962
- group1_traces = []
963
-
964
- for trace in traces:
965
- if trace.plaintext is None or len(trace.plaintext) == 0:
966
- continue
967
-
968
- if trace.plaintext[0] % 2 == 0:
969
- group0_traces.append(trace.power)
970
- else:
971
- group1_traces.append(trace.power)
972
-
973
- if len(group0_traces) < 5 or len(group1_traces) < 5:
974
- return None
975
-
976
- power0 = np.array(group0_traces)
977
- power1 = np.array(group1_traces)
978
-
979
- # Perform Welch's t-test at each time point
980
- t_stats = np.zeros(power0.shape[1])
981
-
982
- for i in range(power0.shape[1]):
983
- t_stat, _ = stats.ttest_ind(power0[:, i], power1[:, i], equal_var=False)
984
- t_stats[i] = abs(t_stat) if not np.isnan(t_stat) else 0.0
985
-
986
- return t_stats
987
-
988
- def _analyze_em_leakage(
989
- self,
990
- power_matrix: NDArray[np.float64],
991
- ) -> SideChannelVulnerability:
992
- """Analyze electromagnetic emission leakage via frequency domain.
993
-
994
- Args:
995
- power_matrix: Power traces matrix (num_traces x num_samples).
996
-
997
- Returns:
998
- SideChannelVulnerability for EM analysis.
999
- """
1000
- # Compute FFT for each trace
1001
- fft_traces = np.fft.rfft(power_matrix, axis=1)
1002
- magnitude_spectrum = np.abs(fft_traces)
1003
-
1004
- # Calculate variance across traces at each frequency
1005
- freq_variance = np.var(magnitude_spectrum, axis=0)
1006
- max_variance = float(np.max(freq_variance))
1007
- mean_variance = float(np.mean(freq_variance))
1008
-
1009
- # Normalize by mean to get relative peaks
1010
- if mean_variance > 0:
1011
- peak_ratio = max_variance / mean_variance
1012
- else:
1013
- peak_ratio = 0.0
1014
-
1015
- # Assess EM leakage
1016
- if peak_ratio > 10.0:
1017
- severity = Severity.HIGH
1018
- elif peak_ratio > 5.0:
1019
- severity = Severity.MEDIUM
1020
- elif peak_ratio > 3.0:
1021
- severity = Severity.LOW
1022
- else:
1023
- severity = Severity.LOW
1024
-
1025
- confidence = min(1.0, power_matrix.shape[0] / 100.0)
1026
-
1027
- return SideChannelVulnerability(
1028
- vulnerability_type=VulnerabilityType.EM,
1029
- severity=severity,
1030
- confidence=confidence,
1031
- evidence=f"Peak frequency variance ratio: {peak_ratio:.2f}",
1032
- description=(
1033
- "Frequency-domain analysis shows potential EM emission leakage"
1034
- if severity != Severity.LOW
1035
- else "No significant EM leakage detected"
1036
- ),
1037
- mitigation_suggestions=[
1038
- "Add EM shielding to device enclosure",
1039
- "Use frequency-domain filtering",
1040
- "Implement spread-spectrum techniques",
1041
- ]
1042
- if severity != Severity.LOW
1043
- else [],
1044
- metadata={
1045
- "peak_variance_ratio": float(peak_ratio),
1046
- "max_variance": max_variance,
1047
- "mean_variance": mean_variance,
1048
- },
1049
- )
1050
-
1051
- def _analyze_power_variance(
1052
- self,
1053
- traces: Sequence[PowerTrace],
1054
- ) -> SideChannelVulnerability:
1055
- """Analyze power consumption variance across different inputs.
1056
-
1057
- Args:
1058
- traces: Power traces with plaintexts.
1059
-
1060
- Returns:
1061
- SideChannelVulnerability for variance analysis.
1062
- """
1063
- # Group traces by first plaintext byte
1064
- power_by_input: dict[int, list[NDArray[np.float64]]] = {}
1065
-
1066
- for trace in traces:
1067
- if trace.plaintext is None or len(trace.plaintext) == 0:
1068
- continue
1069
-
1070
- first_byte = trace.plaintext[0]
1071
- if first_byte not in power_by_input:
1072
- power_by_input[first_byte] = []
1073
- power_by_input[first_byte].append(trace.power)
1074
-
1075
- if len(power_by_input) < 2:
1076
- return SideChannelVulnerability(
1077
- vulnerability_type=VulnerabilityType.POWER,
1078
- severity=Severity.LOW,
1079
- confidence=0.0,
1080
- evidence="Insufficient input variation",
1081
- description="Cannot assess variance - need multiple input values",
1082
- )
1083
-
1084
- # Calculate mean power for each input
1085
- mean_powers = {}
1086
- for byte_val, powers in power_by_input.items():
1087
- power_array = np.array(powers)
1088
- mean_powers[byte_val] = np.mean(power_array, axis=0)
1089
-
1090
- # Calculate variance across different inputs
1091
- mean_power_matrix = np.array(list(mean_powers.values()))
1092
- inter_input_variance = np.var(mean_power_matrix, axis=0)
1093
- max_variance = float(np.max(inter_input_variance))
1094
-
1095
- # Assess severity
1096
- if max_variance > 0.1:
1097
- severity = Severity.MEDIUM
1098
- elif max_variance > 0.01:
1099
- severity = Severity.LOW
1100
- else:
1101
- severity = Severity.LOW
1102
-
1103
- confidence = min(1.0, len(traces) / 100.0)
1104
-
1105
- return SideChannelVulnerability(
1106
- vulnerability_type=VulnerabilityType.POWER,
1107
- severity=severity,
1108
- confidence=confidence,
1109
- evidence=f"Max power variance across inputs: {max_variance:.6f}",
1110
- description=(
1111
- "Power variance analysis shows data-dependent consumption"
1112
- if severity != Severity.LOW
1113
- else "Power variance across inputs is low"
1114
- ),
1115
- mitigation_suggestions=[
1116
- "Implement power balancing techniques",
1117
- "Use dual-rail encoding",
1118
- "Add noise injection",
1119
- ]
1120
- if severity != Severity.LOW
1121
- else [],
1122
- metadata={
1123
- "max_variance": max_variance,
1124
- "num_unique_inputs": len(power_by_input),
1125
- },
1126
- )