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.
- oscura/__init__.py +19 -19
- oscura/__main__.py +4 -0
- oscura/analyzers/__init__.py +2 -0
- oscura/analyzers/digital/extraction.py +2 -3
- oscura/analyzers/digital/quality.py +1 -1
- oscura/analyzers/digital/timing.py +1 -1
- oscura/analyzers/ml/signal_classifier.py +6 -0
- oscura/analyzers/patterns/__init__.py +66 -0
- oscura/analyzers/power/basic.py +3 -3
- oscura/analyzers/power/soa.py +1 -1
- oscura/analyzers/power/switching.py +3 -3
- oscura/analyzers/signal_classification.py +529 -0
- oscura/analyzers/signal_integrity/sparams.py +3 -3
- oscura/analyzers/statistics/basic.py +10 -7
- oscura/analyzers/validation.py +1 -1
- oscura/analyzers/waveform/measurements.py +200 -156
- oscura/analyzers/waveform/measurements_with_uncertainty.py +91 -35
- oscura/analyzers/waveform/spectral.py +182 -84
- oscura/api/dsl/commands.py +15 -6
- oscura/api/server/templates/base.html +137 -146
- oscura/api/server/templates/export.html +84 -110
- oscura/api/server/templates/home.html +248 -267
- oscura/api/server/templates/protocols.html +44 -48
- oscura/api/server/templates/reports.html +27 -35
- oscura/api/server/templates/session_detail.html +68 -78
- oscura/api/server/templates/sessions.html +62 -72
- oscura/api/server/templates/waveforms.html +54 -64
- oscura/automotive/__init__.py +1 -1
- oscura/automotive/can/session.py +1 -1
- oscura/automotive/dbc/generator.py +638 -23
- oscura/automotive/dtc/data.json +17 -102
- oscura/automotive/flexray/fibex.py +9 -1
- oscura/automotive/uds/decoder.py +99 -6
- oscura/cli/analyze.py +8 -2
- oscura/cli/batch.py +36 -5
- oscura/cli/characterize.py +18 -4
- oscura/cli/export.py +47 -5
- oscura/cli/main.py +2 -0
- oscura/cli/onboarding/wizard.py +10 -6
- oscura/cli/pipeline.py +585 -0
- oscura/cli/visualize.py +6 -4
- oscura/convenience.py +400 -32
- oscura/core/measurement_result.py +286 -0
- oscura/core/progress.py +1 -1
- oscura/core/schemas/device_mapping.json +2 -8
- oscura/core/schemas/packet_format.json +4 -24
- oscura/core/schemas/protocol_definition.json +2 -12
- oscura/core/types.py +232 -239
- oscura/correlation/multi_protocol.py +1 -1
- oscura/export/legacy/__init__.py +11 -0
- oscura/export/legacy/wav.py +75 -0
- oscura/exporters/__init__.py +19 -0
- oscura/exporters/wireshark.py +809 -0
- oscura/hardware/acquisition/file.py +5 -19
- oscura/hardware/acquisition/saleae.py +10 -10
- oscura/hardware/acquisition/socketcan.py +4 -6
- oscura/hardware/acquisition/synthetic.py +1 -5
- oscura/hardware/acquisition/visa.py +6 -6
- oscura/hardware/security/side_channel_detector.py +5 -508
- oscura/inference/message_format.py +686 -1
- oscura/jupyter/display.py +2 -2
- oscura/jupyter/magic.py +3 -3
- oscura/loaders/__init__.py +17 -12
- oscura/loaders/binary.py +1 -1
- oscura/loaders/chipwhisperer.py +1 -2
- oscura/loaders/configurable.py +1 -1
- oscura/loaders/csv_loader.py +2 -2
- oscura/loaders/hdf5_loader.py +1 -1
- oscura/loaders/lazy.py +6 -1
- oscura/loaders/mmap_loader.py +0 -1
- oscura/loaders/numpy_loader.py +8 -7
- oscura/loaders/preprocessing.py +3 -5
- oscura/loaders/rigol.py +21 -7
- oscura/loaders/sigrok.py +2 -5
- oscura/loaders/tdms.py +3 -2
- oscura/loaders/tektronix.py +38 -32
- oscura/loaders/tss.py +20 -27
- oscura/loaders/validation.py +17 -10
- oscura/loaders/vcd.py +13 -8
- oscura/loaders/wav.py +1 -6
- oscura/pipeline/__init__.py +76 -0
- oscura/pipeline/handlers/__init__.py +165 -0
- oscura/pipeline/handlers/analyzers.py +1045 -0
- oscura/pipeline/handlers/decoders.py +899 -0
- oscura/pipeline/handlers/exporters.py +1103 -0
- oscura/pipeline/handlers/filters.py +891 -0
- oscura/pipeline/handlers/loaders.py +640 -0
- oscura/pipeline/handlers/transforms.py +768 -0
- oscura/reporting/formatting/measurements.py +55 -14
- oscura/reporting/templates/enhanced/protocol_re.html +504 -503
- oscura/sessions/legacy.py +49 -1
- oscura/side_channel/__init__.py +38 -57
- oscura/utils/builders/signal_builder.py +5 -5
- oscura/utils/comparison/compare.py +7 -9
- oscura/utils/comparison/golden.py +1 -1
- oscura/utils/filtering/convenience.py +2 -2
- oscura/utils/math/arithmetic.py +38 -62
- oscura/utils/math/interpolation.py +20 -20
- oscura/utils/pipeline/__init__.py +4 -17
- oscura/utils/progressive.py +1 -4
- oscura/utils/triggering/edge.py +1 -1
- oscura/utils/triggering/pattern.py +2 -2
- oscura/utils/triggering/pulse.py +2 -2
- oscura/utils/triggering/window.py +3 -3
- oscura/validation/hil_testing.py +11 -11
- oscura/visualization/__init__.py +46 -284
- oscura/visualization/batch.py +72 -433
- oscura/visualization/plot.py +542 -53
- oscura/visualization/styles.py +184 -318
- oscura/workflows/batch/advanced.py +1 -1
- oscura/workflows/batch/aggregate.py +12 -9
- oscura/workflows/complete_re.py +251 -23
- oscura/workflows/digital.py +27 -4
- oscura/workflows/multi_trace.py +136 -17
- oscura/workflows/waveform.py +11 -6
- oscura-0.11.0.dist-info/METADATA +460 -0
- {oscura-0.8.0.dist-info → oscura-0.11.0.dist-info}/RECORD +120 -145
- oscura/side_channel/dpa.py +0 -1025
- oscura/utils/optimization/__init__.py +0 -19
- oscura/utils/optimization/parallel.py +0 -443
- oscura/utils/optimization/search.py +0 -532
- oscura/utils/pipeline/base.py +0 -338
- oscura/utils/pipeline/composition.py +0 -248
- oscura/utils/pipeline/parallel.py +0 -449
- oscura/utils/pipeline/pipeline.py +0 -375
- oscura/utils/search/__init__.py +0 -16
- oscura/utils/search/anomaly.py +0 -424
- oscura/utils/search/context.py +0 -294
- oscura/utils/search/pattern.py +0 -288
- oscura/utils/storage/__init__.py +0 -61
- oscura/utils/storage/database.py +0 -1166
- oscura/visualization/accessibility.py +0 -526
- oscura/visualization/annotations.py +0 -371
- oscura/visualization/axis_scaling.py +0 -305
- oscura/visualization/colors.py +0 -451
- oscura/visualization/digital.py +0 -436
- oscura/visualization/eye.py +0 -571
- oscura/visualization/histogram.py +0 -281
- oscura/visualization/interactive.py +0 -1035
- oscura/visualization/jitter.py +0 -1042
- oscura/visualization/keyboard.py +0 -394
- oscura/visualization/layout.py +0 -400
- oscura/visualization/optimization.py +0 -1079
- oscura/visualization/palettes.py +0 -446
- oscura/visualization/power.py +0 -508
- oscura/visualization/power_extended.py +0 -955
- oscura/visualization/presets.py +0 -469
- oscura/visualization/protocols.py +0 -1246
- oscura/visualization/render.py +0 -223
- oscura/visualization/rendering.py +0 -444
- oscura/visualization/reverse_engineering.py +0 -838
- oscura/visualization/signal_integrity.py +0 -989
- oscura/visualization/specialized.py +0 -643
- oscura/visualization/spectral.py +0 -1226
- oscura/visualization/thumbnails.py +0 -340
- oscura/visualization/time_axis.py +0 -351
- oscura/visualization/waveform.py +0 -454
- oscura-0.8.0.dist-info/METADATA +0 -661
- {oscura-0.8.0.dist-info → oscura-0.11.0.dist-info}/WHEEL +0 -0
- {oscura-0.8.0.dist-info → oscura-0.11.0.dist-info}/entry_points.txt +0 -0
- {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
|
|
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
|
-
)
|