oscura 0.1.2__py3-none-any.whl → 0.4.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 +1 -7
- oscura/acquisition/__init__.py +147 -0
- oscura/acquisition/file.py +255 -0
- oscura/acquisition/hardware.py +186 -0
- oscura/acquisition/saleae.py +340 -0
- oscura/acquisition/socketcan.py +315 -0
- oscura/acquisition/streaming.py +38 -0
- oscura/acquisition/synthetic.py +229 -0
- oscura/acquisition/visa.py +376 -0
- oscura/analyzers/__init__.py +3 -0
- oscura/analyzers/digital/clock.py +9 -1
- oscura/analyzers/digital/edges.py +1 -1
- oscura/analyzers/digital/timing.py +41 -11
- oscura/analyzers/packet/payload_extraction.py +2 -4
- oscura/analyzers/packet/stream.py +5 -5
- oscura/analyzers/patterns/__init__.py +4 -3
- oscura/analyzers/patterns/clustering.py +3 -1
- oscura/analyzers/power/ac_power.py +0 -2
- oscura/analyzers/power/basic.py +0 -2
- oscura/analyzers/power/ripple.py +0 -2
- oscura/analyzers/side_channel/__init__.py +52 -0
- oscura/analyzers/side_channel/power.py +690 -0
- oscura/analyzers/side_channel/timing.py +369 -0
- oscura/analyzers/signal_integrity/embedding.py +0 -2
- oscura/analyzers/signal_integrity/sparams.py +28 -206
- oscura/analyzers/spectral/fft.py +0 -2
- oscura/analyzers/statistical/__init__.py +3 -3
- oscura/analyzers/statistical/checksum.py +2 -0
- oscura/analyzers/statistical/classification.py +2 -0
- oscura/analyzers/statistical/entropy.py +11 -9
- oscura/analyzers/statistical/ngrams.py +4 -2
- oscura/api/fluent.py +2 -2
- oscura/automotive/__init__.py +4 -4
- oscura/automotive/can/__init__.py +0 -2
- oscura/automotive/can/patterns.py +3 -1
- oscura/automotive/can/session.py +277 -78
- oscura/automotive/can/state_machine.py +5 -2
- oscura/automotive/dbc/__init__.py +0 -2
- oscura/automotive/dtc/__init__.py +0 -2
- oscura/automotive/dtc/data.json +2763 -0
- oscura/automotive/dtc/database.py +37 -2769
- oscura/automotive/j1939/__init__.py +0 -2
- oscura/automotive/loaders/__init__.py +0 -2
- oscura/automotive/loaders/asc.py +0 -2
- oscura/automotive/loaders/blf.py +0 -2
- oscura/automotive/loaders/csv_can.py +0 -2
- oscura/automotive/obd/__init__.py +0 -2
- oscura/automotive/uds/__init__.py +0 -2
- oscura/automotive/uds/models.py +0 -2
- oscura/builders/__init__.py +9 -11
- oscura/builders/signal_builder.py +99 -191
- oscura/cli/main.py +0 -2
- oscura/cli/shell.py +0 -2
- oscura/config/loader.py +0 -2
- oscura/core/backend_selector.py +1 -1
- oscura/core/correlation.py +0 -2
- oscura/core/exceptions.py +61 -3
- oscura/core/lazy.py +5 -3
- oscura/core/memory_limits.py +0 -2
- oscura/core/numba_backend.py +5 -7
- oscura/core/uncertainty.py +3 -3
- oscura/dsl/interpreter.py +2 -0
- oscura/dsl/parser.py +8 -6
- oscura/exploratory/error_recovery.py +3 -3
- oscura/exploratory/parse.py +2 -0
- oscura/exploratory/recovery.py +2 -0
- oscura/exploratory/sync.py +2 -0
- oscura/export/wireshark/generator.py +1 -1
- oscura/export/wireshark/type_mapping.py +2 -0
- oscura/exporters/hdf5.py +1 -3
- oscura/extensibility/templates.py +0 -8
- oscura/inference/active_learning/lstar.py +2 -4
- oscura/inference/active_learning/observation_table.py +0 -2
- oscura/inference/active_learning/oracle.py +3 -1
- oscura/inference/active_learning/teachers/simulator.py +1 -3
- oscura/inference/alignment.py +2 -0
- oscura/inference/message_format.py +2 -0
- oscura/inference/protocol_dsl.py +7 -5
- oscura/inference/sequences.py +12 -14
- oscura/inference/state_machine.py +2 -0
- oscura/integrations/llm.py +3 -1
- oscura/jupyter/display.py +0 -2
- oscura/loaders/__init__.py +68 -51
- oscura/loaders/chipwhisperer.py +393 -0
- oscura/loaders/pcap.py +1 -1
- oscura/loaders/touchstone.py +221 -0
- oscura/math/arithmetic.py +0 -2
- oscura/optimization/parallel.py +9 -6
- oscura/pipeline/composition.py +0 -2
- oscura/plugins/cli.py +0 -2
- oscura/reporting/comparison.py +0 -2
- oscura/reporting/config.py +1 -1
- oscura/reporting/formatting/emphasis.py +2 -0
- oscura/reporting/formatting/numbers.py +0 -2
- oscura/reporting/output.py +1 -3
- oscura/reporting/sections.py +0 -2
- oscura/search/anomaly.py +2 -0
- oscura/session/session.py +91 -16
- oscura/sessions/__init__.py +70 -0
- oscura/sessions/base.py +323 -0
- oscura/sessions/blackbox.py +640 -0
- oscura/sessions/generic.py +189 -0
- oscura/testing/synthetic.py +2 -0
- oscura/ui/formatters.py +4 -2
- oscura/utils/buffer.py +2 -2
- oscura/utils/lazy.py +5 -5
- oscura/utils/memory_advanced.py +2 -2
- oscura/utils/memory_extensions.py +2 -2
- oscura/visualization/colors.py +0 -2
- oscura/visualization/power.py +2 -0
- oscura/workflows/multi_trace.py +2 -0
- {oscura-0.1.2.dist-info → oscura-0.4.0.dist-info}/METADATA +122 -20
- {oscura-0.1.2.dist-info → oscura-0.4.0.dist-info}/RECORD +116 -98
- {oscura-0.1.2.dist-info → oscura-0.4.0.dist-info}/WHEEL +0 -0
- {oscura-0.1.2.dist-info → oscura-0.4.0.dist-info}/entry_points.txt +0 -0
- {oscura-0.1.2.dist-info → oscura-0.4.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"""Generic analysis session implementation.
|
|
2
|
+
|
|
3
|
+
This module provides GenericSession - a concrete implementation of AnalysisSession
|
|
4
|
+
for general-purpose signal analysis. It wraps the existing session.Session class
|
|
5
|
+
to provide the unified AnalysisSession interface while maintaining backward
|
|
6
|
+
compatibility.
|
|
7
|
+
|
|
8
|
+
Example:
|
|
9
|
+
>>> from oscura.sessions import GenericSession
|
|
10
|
+
>>> from oscura.acquisition import FileSource
|
|
11
|
+
>>>
|
|
12
|
+
>>> # Create generic session
|
|
13
|
+
>>> session = GenericSession(name="Debug Session")
|
|
14
|
+
>>> session.add_recording("capture1", FileSource("signal1.wfm"))
|
|
15
|
+
>>> session.add_recording("capture2", FileSource("signal2.wfm"))
|
|
16
|
+
>>>
|
|
17
|
+
>>> # Compare recordings
|
|
18
|
+
>>> diff = session.compare("capture1", "capture2")
|
|
19
|
+
>>> print(f"Similarity: {diff.similarity_score:.2%}")
|
|
20
|
+
>>>
|
|
21
|
+
>>> # Analyze
|
|
22
|
+
>>> results = session.analyze() # Generic waveform analysis
|
|
23
|
+
|
|
24
|
+
Pattern:
|
|
25
|
+
GenericSession is the default session type for non-domain-specific
|
|
26
|
+
analysis. Use domain-specific sessions (CANSession, SerialSession, etc.)
|
|
27
|
+
when working within a specific protocol or analysis domain.
|
|
28
|
+
|
|
29
|
+
Migration:
|
|
30
|
+
The existing oscura.session.Session class continues to work unchanged.
|
|
31
|
+
GenericSession provides the new AnalysisSession interface while
|
|
32
|
+
delegating to the existing Session implementation internally.
|
|
33
|
+
|
|
34
|
+
References:
|
|
35
|
+
Architecture Plan Phase 0.3: AnalysisSession Generic Implementation
|
|
36
|
+
src/oscura/session/session.py: Legacy Session class
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
from __future__ import annotations
|
|
40
|
+
|
|
41
|
+
from pathlib import Path
|
|
42
|
+
from typing import Any
|
|
43
|
+
|
|
44
|
+
from oscura.sessions.base import AnalysisSession
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class GenericSession(AnalysisSession):
|
|
48
|
+
"""Generic analysis session for general-purpose signal analysis.
|
|
49
|
+
|
|
50
|
+
Provides the unified AnalysisSession interface for non-domain-specific
|
|
51
|
+
analysis. This is the default session type when you don't need CAN,
|
|
52
|
+
Serial, or other specialized functionality.
|
|
53
|
+
|
|
54
|
+
Example:
|
|
55
|
+
>>> from oscura.sessions import GenericSession
|
|
56
|
+
>>> from oscura.acquisition import FileSource
|
|
57
|
+
>>>
|
|
58
|
+
>>> session = GenericSession()
|
|
59
|
+
>>> session.add_recording("test1", FileSource("capture1.wfm"))
|
|
60
|
+
>>> session.add_recording("test2", FileSource("capture2.wfm"))
|
|
61
|
+
>>>
|
|
62
|
+
>>> # Compare traces
|
|
63
|
+
>>> diff = session.compare("test1", "test2")
|
|
64
|
+
>>> print(f"Changed: {diff.changed_bytes} samples")
|
|
65
|
+
>>>
|
|
66
|
+
>>> # Generic analysis
|
|
67
|
+
>>> results = session.analyze()
|
|
68
|
+
>>> print(results["num_recordings"])
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
def analyze(self) -> dict[str, Any]:
|
|
72
|
+
"""Perform generic waveform analysis on all recordings.
|
|
73
|
+
|
|
74
|
+
Analyzes all loaded recordings and provides summary statistics,
|
|
75
|
+
basic waveform measurements, and comparison results.
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
Dictionary with analysis results:
|
|
79
|
+
- num_recordings: Number of recordings
|
|
80
|
+
- recordings: List of recording names
|
|
81
|
+
- summary: Summary statistics for each recording
|
|
82
|
+
|
|
83
|
+
Example:
|
|
84
|
+
>>> session = GenericSession()
|
|
85
|
+
>>> session.add_recording("sig", FileSource("capture.wfm"))
|
|
86
|
+
>>> results = session.analyze()
|
|
87
|
+
>>> print(results["summary"]["sig"]["mean"])
|
|
88
|
+
"""
|
|
89
|
+
import numpy as np
|
|
90
|
+
|
|
91
|
+
results: dict[str, Any] = {
|
|
92
|
+
"num_recordings": len(self.recordings),
|
|
93
|
+
"recordings": self.list_recordings(),
|
|
94
|
+
"summary": {},
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
# Analyze each recording
|
|
98
|
+
from oscura.core.types import IQTrace
|
|
99
|
+
|
|
100
|
+
for name in self.list_recordings():
|
|
101
|
+
trace = self.get_recording(name)
|
|
102
|
+
|
|
103
|
+
# Handle IQTrace separately
|
|
104
|
+
if isinstance(trace, IQTrace):
|
|
105
|
+
raise TypeError("IQTrace analysis not yet supported in GenericSession")
|
|
106
|
+
|
|
107
|
+
# Basic statistics
|
|
108
|
+
summary = {
|
|
109
|
+
"num_samples": len(trace.data),
|
|
110
|
+
"sample_rate": trace.metadata.sample_rate,
|
|
111
|
+
"duration": len(trace.data) / trace.metadata.sample_rate,
|
|
112
|
+
"mean": float(np.mean(trace.data)),
|
|
113
|
+
"std": float(np.std(trace.data)),
|
|
114
|
+
"min": float(np.min(trace.data)),
|
|
115
|
+
"max": float(np.max(trace.data)),
|
|
116
|
+
"rms": float(np.sqrt(np.mean(trace.data**2))),
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
results["summary"][name] = summary
|
|
120
|
+
|
|
121
|
+
# If multiple recordings, add comparisons
|
|
122
|
+
if len(self.recordings) >= 2:
|
|
123
|
+
results["comparisons"] = {}
|
|
124
|
+
names = self.list_recordings()
|
|
125
|
+
for i in range(len(names)):
|
|
126
|
+
for j in range(i + 1, len(names)):
|
|
127
|
+
comparison_key = f"{names[i]}_vs_{names[j]}"
|
|
128
|
+
comp_result = self.compare(names[i], names[j])
|
|
129
|
+
results["comparisons"][comparison_key] = {
|
|
130
|
+
"similarity": comp_result.similarity_score,
|
|
131
|
+
"changed_samples": comp_result.changed_bytes,
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return results
|
|
135
|
+
|
|
136
|
+
def export_results(self, format: str, path: str | Path) -> None:
|
|
137
|
+
"""Export analysis results to file.
|
|
138
|
+
|
|
139
|
+
Extends base export with additional formats for generic analysis.
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
format: Export format ("report", "json", "csv").
|
|
143
|
+
path: Output file path.
|
|
144
|
+
|
|
145
|
+
Raises:
|
|
146
|
+
ValueError: If format not supported.
|
|
147
|
+
|
|
148
|
+
Example:
|
|
149
|
+
>>> session.export_results("report", "analysis.txt")
|
|
150
|
+
>>> session.export_results("json", "results.json")
|
|
151
|
+
"""
|
|
152
|
+
path = Path(path)
|
|
153
|
+
|
|
154
|
+
if format == "json":
|
|
155
|
+
# Export as JSON
|
|
156
|
+
import json
|
|
157
|
+
|
|
158
|
+
results = self.analyze()
|
|
159
|
+
|
|
160
|
+
with open(path, "w") as f:
|
|
161
|
+
json.dump(results, f, indent=2)
|
|
162
|
+
|
|
163
|
+
elif format == "csv":
|
|
164
|
+
# Export summary as CSV
|
|
165
|
+
import csv
|
|
166
|
+
|
|
167
|
+
results = self.analyze()
|
|
168
|
+
|
|
169
|
+
with open(path, "w", newline="") as f:
|
|
170
|
+
if not results["summary"]:
|
|
171
|
+
return # No data to export
|
|
172
|
+
|
|
173
|
+
# Get fieldnames from first recording
|
|
174
|
+
first_rec = next(iter(results["summary"].values()))
|
|
175
|
+
fieldnames = ["recording"] + list(first_rec.keys())
|
|
176
|
+
|
|
177
|
+
writer = csv.DictWriter(f, fieldnames=fieldnames)
|
|
178
|
+
writer.writeheader()
|
|
179
|
+
|
|
180
|
+
for name, summary in results["summary"].items():
|
|
181
|
+
row = {"recording": name, **summary}
|
|
182
|
+
writer.writerow(row)
|
|
183
|
+
|
|
184
|
+
else:
|
|
185
|
+
# Fall back to base implementation (text report)
|
|
186
|
+
super().export_results(format, path)
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
__all__ = ["GenericSession"]
|
oscura/testing/synthetic.py
CHANGED
oscura/ui/formatters.py
CHANGED
|
@@ -19,10 +19,12 @@ from __future__ import annotations
|
|
|
19
19
|
|
|
20
20
|
from dataclasses import dataclass
|
|
21
21
|
from enum import Enum
|
|
22
|
-
from typing import Any, Literal
|
|
22
|
+
from typing import Any, Literal, TypeAlias
|
|
23
23
|
|
|
24
24
|
# Type alias for color names accepted by colorize()
|
|
25
|
-
|
|
25
|
+
ColorName: TypeAlias = Literal[
|
|
26
|
+
"black", "red", "green", "yellow", "blue", "magenta", "cyan", "white"
|
|
27
|
+
]
|
|
26
28
|
|
|
27
29
|
|
|
28
30
|
class Color(Enum):
|
oscura/utils/buffer.py
CHANGED
|
@@ -16,7 +16,7 @@ References:
|
|
|
16
16
|
|
|
17
17
|
from __future__ import annotations
|
|
18
18
|
|
|
19
|
-
from typing import TYPE_CHECKING, Any, TypeVar, overload
|
|
19
|
+
from typing import TYPE_CHECKING, Any, Generic, TypeVar, overload
|
|
20
20
|
|
|
21
21
|
import numpy as np
|
|
22
22
|
|
|
@@ -26,7 +26,7 @@ if TYPE_CHECKING:
|
|
|
26
26
|
T = TypeVar("T")
|
|
27
27
|
|
|
28
28
|
|
|
29
|
-
class CircularBuffer[T]:
|
|
29
|
+
class CircularBuffer(Generic[T]):
|
|
30
30
|
"""Fixed-size circular buffer with O(1) operations.
|
|
31
31
|
|
|
32
32
|
Thread-safe for single producer, single consumer pattern.
|
oscura/utils/lazy.py
CHANGED
|
@@ -18,18 +18,18 @@ References:
|
|
|
18
18
|
from __future__ import annotations
|
|
19
19
|
|
|
20
20
|
from abc import ABC, abstractmethod
|
|
21
|
-
from typing import TYPE_CHECKING, Any, TypeVar
|
|
21
|
+
from typing import TYPE_CHECKING, Any, Generic, TypeVar
|
|
22
22
|
|
|
23
23
|
import numpy as np
|
|
24
24
|
from numpy.typing import NDArray
|
|
25
25
|
|
|
26
|
+
T = TypeVar("T")
|
|
27
|
+
|
|
26
28
|
if TYPE_CHECKING:
|
|
27
29
|
from collections.abc import Callable
|
|
28
30
|
|
|
29
|
-
T = TypeVar("T")
|
|
30
|
-
|
|
31
31
|
|
|
32
|
-
class LazyProxy[T]
|
|
32
|
+
class LazyProxy(ABC, Generic[T]):
|
|
33
33
|
"""Abstract base class for lazy evaluation proxies.
|
|
34
34
|
|
|
35
35
|
Defers computation until explicitly requested via .compute().
|
|
@@ -169,7 +169,7 @@ class LazyOperation(LazyProxy[Any]):
|
|
|
169
169
|
return self._operation(*evaluated_operands, **self._kwargs)
|
|
170
170
|
|
|
171
171
|
|
|
172
|
-
def lazy_operation
|
|
172
|
+
def lazy_operation(
|
|
173
173
|
func: Callable[..., T],
|
|
174
174
|
*args: Any,
|
|
175
175
|
**kwargs: Any,
|
oscura/utils/memory_advanced.py
CHANGED
|
@@ -20,7 +20,7 @@ from collections import OrderedDict
|
|
|
20
20
|
from dataclasses import dataclass
|
|
21
21
|
from enum import Enum
|
|
22
22
|
from pathlib import Path
|
|
23
|
-
from typing import TYPE_CHECKING, Any, TypeVar
|
|
23
|
+
from typing import TYPE_CHECKING, Any, Generic, TypeVar
|
|
24
24
|
|
|
25
25
|
import numpy as np
|
|
26
26
|
|
|
@@ -664,7 +664,7 @@ T = TypeVar("T")
|
|
|
664
664
|
|
|
665
665
|
|
|
666
666
|
@dataclass
|
|
667
|
-
class CacheEntry[T]:
|
|
667
|
+
class CacheEntry(Generic[T]):
|
|
668
668
|
"""Cache entry with metadata.
|
|
669
669
|
|
|
670
670
|
Attributes:
|
|
@@ -22,7 +22,7 @@ import hashlib
|
|
|
22
22
|
import os
|
|
23
23
|
import time
|
|
24
24
|
from collections import OrderedDict
|
|
25
|
-
from typing import TYPE_CHECKING, Any, TypeVar
|
|
25
|
+
from typing import TYPE_CHECKING, Any, Generic, TypeVar
|
|
26
26
|
|
|
27
27
|
import numpy as np
|
|
28
28
|
|
|
@@ -102,7 +102,7 @@ class ArrayManager(ResourceManager):
|
|
|
102
102
|
# =============================================================================
|
|
103
103
|
|
|
104
104
|
|
|
105
|
-
class LRUCache[T]:
|
|
105
|
+
class LRUCache(Generic[T]):
|
|
106
106
|
"""Least-Recently-Used cache with memory-based eviction.
|
|
107
107
|
|
|
108
108
|
Caches intermediate results with automatic eviction when
|
oscura/visualization/colors.py
CHANGED
oscura/visualization/power.py
CHANGED
oscura/workflows/multi_trace.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: oscura
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Unified hardware reverse engineering framework. Extract all information from any system through signals and data. Unknown protocol discovery, state machine extraction, CRC recovery, security analysis. 16+ protocols, IEEE-compliant measurements.
|
|
5
5
|
Project-URL: Homepage, https://github.com/oscura-re/oscura
|
|
6
6
|
Project-URL: Documentation, https://github.com/oscura-re/oscura/tree/main/docs
|
|
@@ -39,7 +39,7 @@ Requires-Dist: pyyaml<7.0.0,>=6.0
|
|
|
39
39
|
Requires-Dist: scipy<2.0.0,>=1.10.0
|
|
40
40
|
Requires-Dist: tm-data-types<1.0.0,>=0.3.0
|
|
41
41
|
Provides-Extra: all
|
|
42
|
-
Requires-Dist: asammdf<
|
|
42
|
+
Requires-Dist: asammdf<9.0.0,>=8.0.0; extra == 'all'
|
|
43
43
|
Requires-Dist: cantools<40.0.0,>=39.4.0; extra == 'all'
|
|
44
44
|
Requires-Dist: check-jsonschema<1.0.0,>=0.29.0; extra == 'all'
|
|
45
45
|
Requires-Dist: h5py<4.0.0,>=3.0.0; extra == 'all'
|
|
@@ -51,11 +51,14 @@ Requires-Dist: nbconvert<8.0.0,>=7.0.0; extra == 'all'
|
|
|
51
51
|
Requires-Dist: networkx<4.0.0,>=3.0; extra == 'all'
|
|
52
52
|
Requires-Dist: nptdms<2.0.0,>=1.7.0; extra == 'all'
|
|
53
53
|
Requires-Dist: openpyxl<4.0.0,>=3.0.0; extra == 'all'
|
|
54
|
+
Requires-Dist: pytest-benchmark<6.0.0,>=4.0.0; extra == 'all'
|
|
54
55
|
Requires-Dist: pytest-cov<8.0.0,>=6.0; extra == 'all'
|
|
55
56
|
Requires-Dist: pytest-timeout<3.0.0,>=2.3.0; extra == 'all'
|
|
56
57
|
Requires-Dist: pytest<10.0.0,>=8.0; extra == 'all'
|
|
57
58
|
Requires-Dist: python-can<5.0.0,>=4.4.0; extra == 'all'
|
|
58
59
|
Requires-Dist: python-pptx<1.0.0,>=0.6.21; extra == 'all'
|
|
60
|
+
Requires-Dist: pyvisa-py<1.0.0,>=0.7.0; extra == 'all'
|
|
61
|
+
Requires-Dist: pyvisa<2.0.0,>=1.13.0; extra == 'all'
|
|
59
62
|
Requires-Dist: pywavelets<2.0.0,>=1.0.0; extra == 'all'
|
|
60
63
|
Requires-Dist: reportlab<6.0.0,>=4.4.7; extra == 'all'
|
|
61
64
|
Requires-Dist: rigolwfm<2.0.0,>=1.0.0; extra == 'all'
|
|
@@ -67,7 +70,7 @@ Requires-Dist: networkx<4.0.0,>=3.0; extra == 'analysis'
|
|
|
67
70
|
Requires-Dist: openpyxl<4.0.0,>=3.0.0; extra == 'analysis'
|
|
68
71
|
Requires-Dist: pywavelets<2.0.0,>=1.0.0; extra == 'analysis'
|
|
69
72
|
Provides-Extra: automotive
|
|
70
|
-
Requires-Dist: asammdf<
|
|
73
|
+
Requires-Dist: asammdf<9.0.0,>=8.0.0; extra == 'automotive'
|
|
71
74
|
Requires-Dist: cantools<40.0.0,>=39.4.0; extra == 'automotive'
|
|
72
75
|
Requires-Dist: python-can<5.0.0,>=4.4.0; extra == 'automotive'
|
|
73
76
|
Requires-Dist: scapy<3.0.0,>=2.5.0; extra == 'automotive'
|
|
@@ -75,11 +78,15 @@ Provides-Extra: dev
|
|
|
75
78
|
Requires-Dist: check-jsonschema<1.0.0,>=0.29.0; extra == 'dev'
|
|
76
79
|
Requires-Dist: hypothesis<7.0.0,>=6.0.0; extra == 'dev'
|
|
77
80
|
Requires-Dist: interrogate<2.0.0,>=1.7.0; extra == 'dev'
|
|
81
|
+
Requires-Dist: pytest-benchmark<6.0.0,>=4.0.0; extra == 'dev'
|
|
78
82
|
Requires-Dist: pytest-cov<8.0.0,>=6.0; extra == 'dev'
|
|
79
83
|
Requires-Dist: pytest-timeout<3.0.0,>=2.3.0; extra == 'dev'
|
|
80
84
|
Requires-Dist: pytest<10.0.0,>=8.0; extra == 'dev'
|
|
81
85
|
Requires-Dist: types-pyyaml<7.0.0,>=6.0; extra == 'dev'
|
|
82
86
|
Requires-Dist: yamllint<2.0.0,>=1.35; extra == 'dev'
|
|
87
|
+
Provides-Extra: hardware
|
|
88
|
+
Requires-Dist: pyvisa-py<1.0.0,>=0.7.0; extra == 'hardware'
|
|
89
|
+
Requires-Dist: pyvisa<2.0.0,>=1.13.0; extra == 'hardware'
|
|
83
90
|
Provides-Extra: hdf5
|
|
84
91
|
Requires-Dist: h5py<4.0.0,>=3.0.0; extra == 'hdf5'
|
|
85
92
|
Provides-Extra: jupyter
|
|
@@ -98,10 +105,26 @@ Description-Content-Type: text/markdown
|
|
|
98
105
|
|
|
99
106
|
**Unified hardware reverse engineering framework. Extract all information from any system through signals and data.**
|
|
100
107
|
|
|
101
|
-
|
|
108
|
+
**Build Status:**
|
|
109
|
+
[](https://github.com/oscura-re/oscura/actions/workflows/ci.yml)
|
|
110
|
+
[](https://github.com/oscura-re/oscura/actions/workflows/code-quality.yml)
|
|
111
|
+
[](https://github.com/oscura-re/oscura/actions/workflows/docs.yml)
|
|
112
|
+
[](https://github.com/oscura-re/oscura/actions/workflows/test-quality.yml)
|
|
113
|
+
|
|
114
|
+
**Package:**
|
|
115
|
+
[](https://pypi.org/project/oscura/)
|
|
102
116
|
[](https://www.python.org/downloads/)
|
|
103
117
|
[](https://opensource.org/licenses/MIT)
|
|
104
|
-
[](https://pypi.org/project/oscura/)
|
|
119
|
+
|
|
120
|
+
**Code Quality:**
|
|
121
|
+
[](https://codecov.io/gh/oscura-re/oscura)
|
|
122
|
+
[](https://github.com/astral-sh/ruff)
|
|
123
|
+
[](https://github.com/oscura-re/oscura/tree/main/docs)
|
|
124
|
+
|
|
125
|
+
**Project Status:**
|
|
126
|
+
[](https://github.com/oscura-re/oscura/graphs/commit-activity)
|
|
127
|
+
[](https://github.com/oscura-re/oscura/commits/main)
|
|
105
128
|
|
|
106
129
|
---
|
|
107
130
|
|
|
@@ -109,11 +132,15 @@ Description-Content-Type: text/markdown
|
|
|
109
132
|
|
|
110
133
|
Oscura is a hardware reverse engineering framework for security researchers, right-to-repair advocates, defense analysts, and commercial intelligence teams. From oscilloscope captures to complete system understanding.
|
|
111
134
|
|
|
112
|
-
**Reverse Engineering**: Unknown protocol discovery • State machine extraction • CRC/checksum recovery • Proprietary device replication • Security vulnerability analysis
|
|
135
|
+
**Reverse Engineering**: Unknown protocol discovery • State machine extraction • CRC/checksum recovery • Proprietary device replication • Security vulnerability analysis • Black-box protocol analysis
|
|
136
|
+
|
|
137
|
+
**Signal Analysis**: IEEE-compliant measurements (181/1241/1459/2414) • Comprehensive protocol decoding (16+ protocols) • Spectral analysis • Timing characterization • Side-channel analysis (DPA/CPA/timing attacks)
|
|
138
|
+
|
|
139
|
+
**Unified Acquisition**: File-based • Hardware sources (SocketCAN, Saleae, PyVISA - Phase 2) • Synthetic generation • Polymorphic Source protocol
|
|
113
140
|
|
|
114
|
-
**
|
|
141
|
+
**Interactive Sessions**: Domain-specific analysis sessions • BlackBoxSession for protocol RE • Differential analysis • Field hypothesis generation • Protocol specification export
|
|
115
142
|
|
|
116
|
-
**Built For**: Exploitation • Replication • Defense analysis • Commercial intelligence • Right-to-repair
|
|
143
|
+
**Built For**: Exploitation • Replication • Defense analysis • Commercial intelligence • Right-to-repair • Cryptographic research
|
|
117
144
|
|
|
118
145
|
---
|
|
119
146
|
|
|
@@ -126,17 +153,19 @@ uv pip install oscura
|
|
|
126
153
|
# Or with pip
|
|
127
154
|
pip install oscura
|
|
128
155
|
|
|
129
|
-
# Development install
|
|
156
|
+
# Development install (RECOMMENDED)
|
|
130
157
|
git clone https://github.com/oscura-re/oscura.git
|
|
131
158
|
cd oscura
|
|
132
|
-
|
|
133
|
-
./scripts/setup
|
|
159
|
+
./scripts/setup.sh # Complete setup (dependencies + hooks)
|
|
160
|
+
./scripts/verify-setup.sh # Verify environment is ready
|
|
134
161
|
```
|
|
135
162
|
|
|
136
163
|
---
|
|
137
164
|
|
|
138
165
|
## Quick Start
|
|
139
166
|
|
|
167
|
+
### Signal Analysis
|
|
168
|
+
|
|
140
169
|
```python
|
|
141
170
|
import oscura as osc
|
|
142
171
|
|
|
@@ -153,6 +182,57 @@ decoder = UARTDecoder(baud_rate=115200)
|
|
|
153
182
|
messages = decoder.decode(trace)
|
|
154
183
|
```
|
|
155
184
|
|
|
185
|
+
### Black-Box Protocol Reverse Engineering
|
|
186
|
+
|
|
187
|
+
```python
|
|
188
|
+
from oscura.sessions import BlackBoxSession
|
|
189
|
+
from oscura.acquisition import FileSource
|
|
190
|
+
|
|
191
|
+
# Create analysis session
|
|
192
|
+
session = BlackBoxSession(name="IoT Device RE")
|
|
193
|
+
|
|
194
|
+
# Add recordings from different stimuli
|
|
195
|
+
session.add_recording("idle", FileSource("idle.bin"))
|
|
196
|
+
session.add_recording("button_press", FileSource("button.bin"))
|
|
197
|
+
|
|
198
|
+
# Differential analysis
|
|
199
|
+
diff = session.compare("idle", "button_press")
|
|
200
|
+
print(f"Changed bytes: {diff.changed_bytes}")
|
|
201
|
+
|
|
202
|
+
# Generate protocol specification
|
|
203
|
+
spec = session.generate_protocol_spec()
|
|
204
|
+
print(f"Detected {len(spec['fields'])} protocol fields")
|
|
205
|
+
|
|
206
|
+
# Export Wireshark dissector
|
|
207
|
+
session.export_results("dissector", "protocol.lua")
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### CAN Protocol Analysis
|
|
211
|
+
|
|
212
|
+
```python
|
|
213
|
+
from oscura.automotive.can import CANSession
|
|
214
|
+
from oscura.acquisition import FileSource
|
|
215
|
+
|
|
216
|
+
# Create session
|
|
217
|
+
session = CANSession(name="Vehicle Analysis")
|
|
218
|
+
|
|
219
|
+
# Add recordings from CAN bus captures
|
|
220
|
+
session.add_recording("idle", FileSource("idle.blf"))
|
|
221
|
+
session.add_recording("accelerate", FileSource("accelerate.blf"))
|
|
222
|
+
|
|
223
|
+
# Analyze traffic
|
|
224
|
+
analysis = session.analyze()
|
|
225
|
+
print(f"Messages: {analysis['inventory']['total_messages']}")
|
|
226
|
+
print(f"Unique IDs: {len(analysis['inventory']['message_ids'])}")
|
|
227
|
+
|
|
228
|
+
# Compare recordings
|
|
229
|
+
diff = session.compare("idle", "accelerate")
|
|
230
|
+
print(f"Changed IDs: {len(diff.details['changed_ids'])}")
|
|
231
|
+
|
|
232
|
+
# Export DBC file
|
|
233
|
+
session.export_dbc("vehicle.dbc")
|
|
234
|
+
```
|
|
235
|
+
|
|
156
236
|
---
|
|
157
237
|
|
|
158
238
|
## Learn by Example
|
|
@@ -192,7 +272,7 @@ messages = decoder.decode(trace)
|
|
|
192
272
|
|
|
193
273
|
```bash
|
|
194
274
|
# Generate demo data
|
|
195
|
-
python demos/
|
|
275
|
+
python demos/generate_all_demo_data.py
|
|
196
276
|
|
|
197
277
|
# Run a demo
|
|
198
278
|
uv run python demos/01_waveform_analysis/comprehensive_wfm_analysis.py
|
|
@@ -225,17 +305,20 @@ Tektronix WFM • Rigol WFM • LeCroy TRC • Sigrok • VCD • CSV • NumPy
|
|
|
225
305
|
## Command Line Interface
|
|
226
306
|
|
|
227
307
|
```bash
|
|
228
|
-
#
|
|
229
|
-
oscura
|
|
308
|
+
# Characterize signal measurements
|
|
309
|
+
oscura characterize capture.wfm
|
|
230
310
|
|
|
231
311
|
# Decode protocol
|
|
232
|
-
oscura decode
|
|
312
|
+
oscura decode uart.wfm --protocol uart
|
|
233
313
|
|
|
234
|
-
#
|
|
235
|
-
oscura
|
|
314
|
+
# Batch process multiple files
|
|
315
|
+
oscura batch '*.wfm' --analysis characterize
|
|
236
316
|
|
|
237
|
-
#
|
|
238
|
-
oscura
|
|
317
|
+
# Compare two signals
|
|
318
|
+
oscura compare before.wfm after.wfm
|
|
319
|
+
|
|
320
|
+
# Interactive shell
|
|
321
|
+
oscura shell
|
|
239
322
|
```
|
|
240
323
|
|
|
241
324
|
See [CLI Reference](docs/cli.md) for complete documentation.
|
|
@@ -264,9 +347,28 @@ uv sync --all-extras
|
|
|
264
347
|
|
|
265
348
|
## Documentation
|
|
266
349
|
|
|
267
|
-
|
|
350
|
+
### Getting Started
|
|
351
|
+
|
|
352
|
+
- **[Quick Start Guide](docs/guides/quick-start.md)** - Begin here
|
|
353
|
+
- **[Demos](demos/)** - Working examples for every feature
|
|
354
|
+
- **[Migration Guide](docs/migration/v0-to-v1.md)** - Upgrade from older versions
|
|
355
|
+
|
|
356
|
+
### User Guides
|
|
357
|
+
|
|
358
|
+
- **[Black-Box Protocol Analysis](docs/guides/blackbox-analysis.md)** - Unknown protocol reverse engineering
|
|
359
|
+
- **[Hardware Acquisition](docs/guides/hardware-acquisition.md)** - Direct hardware integration (Phase 2)
|
|
360
|
+
- **[Side-Channel Analysis](docs/guides/side-channel-analysis.md)** - Power/timing/EM attacks
|
|
361
|
+
- **[Workflows](docs/guides/workflows.md)** - Complete analysis workflows
|
|
362
|
+
|
|
363
|
+
### API Reference
|
|
364
|
+
|
|
268
365
|
- **[API Reference](docs/api/)** - Complete API documentation
|
|
366
|
+
- **[Session Management](docs/api/session-management.md)** - Interactive analysis sessions
|
|
269
367
|
- **[CLI Reference](docs/cli.md)** - Command line usage
|
|
368
|
+
|
|
369
|
+
### Development
|
|
370
|
+
|
|
371
|
+
- **[Architecture](docs/architecture/)** - Design principles and patterns
|
|
270
372
|
- **[Testing Guide](docs/testing/)** - Test suite architecture
|
|
271
373
|
- **[CHANGELOG](CHANGELOG.md)** - Version history
|
|
272
374
|
|