oscura 0.5.1__py3-none-any.whl → 0.7.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 +169 -167
- oscura/analyzers/__init__.py +3 -0
- oscura/analyzers/classification.py +659 -0
- oscura/analyzers/digital/edges.py +325 -65
- oscura/analyzers/digital/quality.py +293 -166
- oscura/analyzers/digital/timing.py +260 -115
- oscura/analyzers/digital/timing_numba.py +334 -0
- oscura/analyzers/entropy.py +605 -0
- oscura/analyzers/eye/diagram.py +176 -109
- oscura/analyzers/eye/metrics.py +5 -5
- oscura/analyzers/jitter/__init__.py +6 -4
- oscura/analyzers/jitter/ber.py +52 -52
- oscura/analyzers/jitter/classification.py +156 -0
- oscura/analyzers/jitter/decomposition.py +163 -113
- oscura/analyzers/jitter/spectrum.py +80 -64
- oscura/analyzers/ml/__init__.py +39 -0
- oscura/analyzers/ml/features.py +600 -0
- oscura/analyzers/ml/signal_classifier.py +604 -0
- oscura/analyzers/packet/daq.py +246 -158
- oscura/analyzers/packet/parser.py +12 -1
- oscura/analyzers/packet/payload.py +50 -2110
- oscura/analyzers/packet/payload_analysis.py +361 -181
- oscura/analyzers/packet/payload_patterns.py +133 -70
- oscura/analyzers/packet/stream.py +84 -23
- oscura/analyzers/patterns/__init__.py +26 -5
- oscura/analyzers/patterns/anomaly_detection.py +908 -0
- oscura/analyzers/patterns/clustering.py +169 -108
- oscura/analyzers/patterns/clustering_optimized.py +227 -0
- oscura/analyzers/patterns/discovery.py +1 -1
- oscura/analyzers/patterns/matching.py +581 -197
- oscura/analyzers/patterns/pattern_mining.py +778 -0
- oscura/analyzers/patterns/periodic.py +121 -38
- oscura/analyzers/patterns/sequences.py +175 -78
- oscura/analyzers/power/conduction.py +1 -1
- oscura/analyzers/power/soa.py +6 -6
- oscura/analyzers/power/switching.py +250 -110
- oscura/analyzers/protocol/__init__.py +17 -1
- oscura/analyzers/protocols/base.py +6 -6
- oscura/analyzers/protocols/ble/__init__.py +38 -0
- oscura/analyzers/protocols/ble/analyzer.py +809 -0
- oscura/analyzers/protocols/ble/uuids.py +288 -0
- oscura/analyzers/protocols/can.py +257 -127
- oscura/analyzers/protocols/can_fd.py +107 -80
- oscura/analyzers/protocols/flexray.py +139 -80
- oscura/analyzers/protocols/hdlc.py +93 -58
- oscura/analyzers/protocols/i2c.py +247 -106
- oscura/analyzers/protocols/i2s.py +138 -86
- oscura/analyzers/protocols/industrial/__init__.py +40 -0
- oscura/analyzers/protocols/industrial/bacnet/__init__.py +33 -0
- oscura/analyzers/protocols/industrial/bacnet/analyzer.py +708 -0
- oscura/analyzers/protocols/industrial/bacnet/encoding.py +412 -0
- oscura/analyzers/protocols/industrial/bacnet/services.py +622 -0
- oscura/analyzers/protocols/industrial/ethercat/__init__.py +30 -0
- oscura/analyzers/protocols/industrial/ethercat/analyzer.py +474 -0
- oscura/analyzers/protocols/industrial/ethercat/mailbox.py +339 -0
- oscura/analyzers/protocols/industrial/ethercat/topology.py +166 -0
- oscura/analyzers/protocols/industrial/modbus/__init__.py +31 -0
- oscura/analyzers/protocols/industrial/modbus/analyzer.py +525 -0
- oscura/analyzers/protocols/industrial/modbus/crc.py +79 -0
- oscura/analyzers/protocols/industrial/modbus/functions.py +436 -0
- oscura/analyzers/protocols/industrial/opcua/__init__.py +21 -0
- oscura/analyzers/protocols/industrial/opcua/analyzer.py +552 -0
- oscura/analyzers/protocols/industrial/opcua/datatypes.py +446 -0
- oscura/analyzers/protocols/industrial/opcua/services.py +264 -0
- oscura/analyzers/protocols/industrial/profinet/__init__.py +23 -0
- oscura/analyzers/protocols/industrial/profinet/analyzer.py +441 -0
- oscura/analyzers/protocols/industrial/profinet/dcp.py +263 -0
- oscura/analyzers/protocols/industrial/profinet/ptcp.py +200 -0
- oscura/analyzers/protocols/jtag.py +180 -98
- oscura/analyzers/protocols/lin.py +219 -114
- oscura/analyzers/protocols/manchester.py +4 -4
- oscura/analyzers/protocols/onewire.py +253 -149
- oscura/analyzers/protocols/parallel_bus/__init__.py +20 -0
- oscura/analyzers/protocols/parallel_bus/centronics.py +92 -0
- oscura/analyzers/protocols/parallel_bus/gpib.py +137 -0
- oscura/analyzers/protocols/spi.py +192 -95
- oscura/analyzers/protocols/swd.py +321 -167
- oscura/analyzers/protocols/uart.py +267 -125
- oscura/analyzers/protocols/usb.py +235 -131
- oscura/analyzers/side_channel/power.py +17 -12
- oscura/analyzers/signal/__init__.py +15 -0
- oscura/analyzers/signal/timing_analysis.py +1086 -0
- oscura/analyzers/signal_integrity/__init__.py +4 -1
- oscura/analyzers/signal_integrity/sparams.py +2 -19
- oscura/analyzers/spectral/chunked.py +129 -60
- oscura/analyzers/spectral/chunked_fft.py +300 -94
- oscura/analyzers/spectral/chunked_wavelet.py +100 -80
- oscura/analyzers/statistical/checksum.py +376 -217
- oscura/analyzers/statistical/classification.py +229 -107
- oscura/analyzers/statistical/entropy.py +78 -53
- oscura/analyzers/statistics/correlation.py +407 -211
- oscura/analyzers/statistics/outliers.py +2 -2
- oscura/analyzers/statistics/streaming.py +30 -5
- oscura/analyzers/validation.py +216 -101
- oscura/analyzers/waveform/measurements.py +9 -0
- oscura/analyzers/waveform/measurements_with_uncertainty.py +31 -15
- oscura/analyzers/waveform/spectral.py +500 -228
- oscura/api/__init__.py +31 -5
- oscura/api/dsl/__init__.py +582 -0
- oscura/{dsl → api/dsl}/commands.py +43 -76
- oscura/{dsl → api/dsl}/interpreter.py +26 -51
- oscura/{dsl → api/dsl}/parser.py +107 -77
- oscura/{dsl → api/dsl}/repl.py +2 -2
- oscura/api/dsl.py +1 -1
- oscura/{integrations → api/integrations}/__init__.py +1 -1
- oscura/{integrations → api/integrations}/llm.py +201 -102
- oscura/api/operators.py +3 -3
- oscura/api/optimization.py +144 -30
- oscura/api/rest_server.py +921 -0
- oscura/api/server/__init__.py +17 -0
- oscura/api/server/dashboard.py +850 -0
- oscura/api/server/static/README.md +34 -0
- oscura/api/server/templates/base.html +181 -0
- oscura/api/server/templates/export.html +120 -0
- oscura/api/server/templates/home.html +284 -0
- oscura/api/server/templates/protocols.html +58 -0
- oscura/api/server/templates/reports.html +43 -0
- oscura/api/server/templates/session_detail.html +89 -0
- oscura/api/server/templates/sessions.html +83 -0
- oscura/api/server/templates/waveforms.html +73 -0
- oscura/automotive/__init__.py +8 -1
- oscura/automotive/can/__init__.py +10 -0
- oscura/automotive/can/checksum.py +3 -1
- oscura/automotive/can/dbc_generator.py +590 -0
- oscura/automotive/can/message_wrapper.py +121 -74
- oscura/automotive/can/patterns.py +98 -21
- oscura/automotive/can/session.py +292 -56
- oscura/automotive/can/state_machine.py +6 -3
- oscura/automotive/can/stimulus_response.py +97 -75
- oscura/automotive/dbc/__init__.py +10 -2
- oscura/automotive/dbc/generator.py +84 -56
- oscura/automotive/dbc/parser.py +6 -6
- oscura/automotive/dtc/data.json +17 -102
- oscura/automotive/dtc/database.py +2 -2
- oscura/automotive/flexray/__init__.py +31 -0
- oscura/automotive/flexray/analyzer.py +504 -0
- oscura/automotive/flexray/crc.py +185 -0
- oscura/automotive/flexray/fibex.py +449 -0
- oscura/automotive/j1939/__init__.py +45 -8
- oscura/automotive/j1939/analyzer.py +605 -0
- oscura/automotive/j1939/spns.py +326 -0
- oscura/automotive/j1939/transport.py +306 -0
- oscura/automotive/lin/__init__.py +47 -0
- oscura/automotive/lin/analyzer.py +612 -0
- oscura/automotive/loaders/blf.py +13 -2
- oscura/automotive/loaders/csv_can.py +143 -72
- oscura/automotive/loaders/dispatcher.py +50 -2
- oscura/automotive/loaders/mdf.py +86 -45
- oscura/automotive/loaders/pcap.py +111 -61
- oscura/automotive/uds/__init__.py +4 -0
- oscura/automotive/uds/analyzer.py +725 -0
- oscura/automotive/uds/decoder.py +140 -58
- oscura/automotive/uds/models.py +7 -1
- oscura/automotive/visualization.py +1 -1
- oscura/cli/analyze.py +348 -0
- oscura/cli/batch.py +142 -122
- oscura/cli/benchmark.py +275 -0
- oscura/cli/characterize.py +137 -82
- oscura/cli/compare.py +224 -131
- oscura/cli/completion.py +250 -0
- oscura/cli/config_cmd.py +361 -0
- oscura/cli/decode.py +164 -87
- oscura/cli/export.py +286 -0
- oscura/cli/main.py +115 -31
- oscura/{onboarding → cli/onboarding}/__init__.py +3 -3
- oscura/{onboarding → cli/onboarding}/help.py +80 -58
- oscura/{onboarding → cli/onboarding}/tutorials.py +97 -72
- oscura/{onboarding → cli/onboarding}/wizard.py +55 -36
- oscura/cli/progress.py +147 -0
- oscura/cli/shell.py +157 -135
- oscura/cli/validate_cmd.py +204 -0
- oscura/cli/visualize.py +158 -0
- oscura/convenience.py +125 -79
- oscura/core/__init__.py +4 -2
- oscura/core/backend_selector.py +3 -3
- oscura/core/cache.py +126 -15
- oscura/core/cancellation.py +1 -1
- oscura/{config → core/config}/__init__.py +20 -11
- oscura/{config → core/config}/defaults.py +1 -1
- oscura/{config → core/config}/loader.py +7 -5
- oscura/{config → core/config}/memory.py +5 -5
- oscura/{config → core/config}/migration.py +1 -1
- oscura/{config → core/config}/pipeline.py +99 -23
- oscura/{config → core/config}/preferences.py +1 -1
- oscura/{config → core/config}/protocol.py +3 -3
- oscura/{config → core/config}/schema.py +426 -272
- oscura/{config → core/config}/settings.py +1 -1
- oscura/{config → core/config}/thresholds.py +195 -153
- oscura/core/correlation.py +5 -6
- oscura/core/cross_domain.py +0 -2
- oscura/core/debug.py +9 -5
- oscura/{extensibility → core/extensibility}/docs.py +158 -70
- oscura/{extensibility → core/extensibility}/extensions.py +160 -76
- oscura/{extensibility → core/extensibility}/logging.py +1 -1
- oscura/{extensibility → core/extensibility}/measurements.py +1 -1
- oscura/{extensibility → core/extensibility}/plugins.py +1 -1
- oscura/{extensibility → core/extensibility}/templates.py +73 -3
- oscura/{extensibility → core/extensibility}/validation.py +1 -1
- oscura/core/gpu_backend.py +11 -7
- oscura/core/log_query.py +101 -11
- oscura/core/logging.py +126 -54
- oscura/core/logging_advanced.py +5 -5
- oscura/core/memory_limits.py +108 -70
- oscura/core/memory_monitor.py +2 -2
- oscura/core/memory_progress.py +7 -7
- oscura/core/memory_warnings.py +1 -1
- oscura/core/numba_backend.py +13 -13
- oscura/{plugins → core/plugins}/__init__.py +9 -9
- oscura/{plugins → core/plugins}/base.py +7 -7
- oscura/{plugins → core/plugins}/cli.py +3 -3
- oscura/{plugins → core/plugins}/discovery.py +186 -106
- oscura/{plugins → core/plugins}/lifecycle.py +1 -1
- oscura/{plugins → core/plugins}/manager.py +7 -7
- oscura/{plugins → core/plugins}/registry.py +3 -3
- oscura/{plugins → core/plugins}/versioning.py +1 -1
- oscura/core/progress.py +16 -1
- oscura/core/provenance.py +8 -2
- oscura/{schemas → core/schemas}/__init__.py +2 -2
- oscura/{schemas → core/schemas}/device_mapping.json +2 -8
- oscura/{schemas → core/schemas}/packet_format.json +4 -24
- oscura/{schemas → core/schemas}/protocol_definition.json +2 -12
- oscura/core/types.py +4 -0
- oscura/core/uncertainty.py +3 -3
- oscura/correlation/__init__.py +52 -0
- oscura/correlation/multi_protocol.py +811 -0
- oscura/discovery/auto_decoder.py +117 -35
- oscura/discovery/comparison.py +191 -86
- oscura/discovery/quality_validator.py +155 -68
- oscura/discovery/signal_detector.py +196 -79
- oscura/export/__init__.py +18 -8
- oscura/export/kaitai_struct.py +513 -0
- oscura/export/scapy_layer.py +801 -0
- oscura/export/wireshark/generator.py +1 -1
- oscura/export/wireshark/templates/dissector.lua.j2 +2 -2
- oscura/export/wireshark_dissector.py +746 -0
- oscura/guidance/wizard.py +207 -111
- oscura/hardware/__init__.py +19 -0
- oscura/{acquisition → hardware/acquisition}/__init__.py +4 -4
- oscura/{acquisition → hardware/acquisition}/file.py +2 -2
- oscura/{acquisition → hardware/acquisition}/hardware.py +7 -7
- oscura/{acquisition → hardware/acquisition}/saleae.py +15 -12
- oscura/{acquisition → hardware/acquisition}/socketcan.py +1 -1
- oscura/{acquisition → hardware/acquisition}/streaming.py +2 -2
- oscura/{acquisition → hardware/acquisition}/synthetic.py +3 -3
- oscura/{acquisition → hardware/acquisition}/visa.py +33 -11
- oscura/hardware/firmware/__init__.py +29 -0
- oscura/hardware/firmware/pattern_recognition.py +874 -0
- oscura/hardware/hal_detector.py +736 -0
- oscura/hardware/security/__init__.py +37 -0
- oscura/hardware/security/side_channel_detector.py +1126 -0
- oscura/inference/__init__.py +4 -0
- oscura/inference/active_learning/observation_table.py +4 -1
- oscura/inference/alignment.py +216 -123
- oscura/inference/bayesian.py +113 -33
- oscura/inference/crc_reverse.py +101 -55
- oscura/inference/logic.py +6 -2
- oscura/inference/message_format.py +342 -183
- oscura/inference/protocol.py +95 -44
- oscura/inference/protocol_dsl.py +180 -82
- oscura/inference/signal_intelligence.py +1439 -706
- oscura/inference/spectral.py +99 -57
- oscura/inference/state_machine.py +810 -158
- oscura/inference/stream.py +270 -110
- oscura/iot/__init__.py +34 -0
- oscura/iot/coap/__init__.py +32 -0
- oscura/iot/coap/analyzer.py +668 -0
- oscura/iot/coap/options.py +212 -0
- oscura/iot/lorawan/__init__.py +21 -0
- oscura/iot/lorawan/crypto.py +206 -0
- oscura/iot/lorawan/decoder.py +801 -0
- oscura/iot/lorawan/mac_commands.py +341 -0
- oscura/iot/mqtt/__init__.py +27 -0
- oscura/iot/mqtt/analyzer.py +999 -0
- oscura/iot/mqtt/properties.py +315 -0
- oscura/iot/zigbee/__init__.py +31 -0
- oscura/iot/zigbee/analyzer.py +615 -0
- oscura/iot/zigbee/security.py +153 -0
- oscura/iot/zigbee/zcl.py +349 -0
- oscura/jupyter/display.py +125 -45
- oscura/{exploratory → jupyter/exploratory}/__init__.py +8 -8
- oscura/{exploratory → jupyter/exploratory}/error_recovery.py +298 -141
- oscura/jupyter/exploratory/fuzzy.py +746 -0
- oscura/{exploratory → jupyter/exploratory}/fuzzy_advanced.py +258 -100
- oscura/{exploratory → jupyter/exploratory}/legacy.py +464 -242
- oscura/{exploratory → jupyter/exploratory}/parse.py +167 -145
- oscura/{exploratory → jupyter/exploratory}/recovery.py +119 -87
- oscura/jupyter/exploratory/sync.py +612 -0
- oscura/{exploratory → jupyter/exploratory}/unknown.py +299 -176
- oscura/jupyter/magic.py +4 -4
- oscura/{ui → jupyter/ui}/__init__.py +2 -2
- oscura/{ui → jupyter/ui}/formatters.py +3 -3
- oscura/{ui → jupyter/ui}/progressive_display.py +153 -82
- oscura/loaders/__init__.py +183 -67
- oscura/loaders/binary.py +88 -1
- oscura/loaders/chipwhisperer.py +153 -137
- oscura/loaders/configurable.py +208 -86
- oscura/loaders/csv_loader.py +458 -215
- oscura/loaders/hdf5_loader.py +278 -119
- oscura/loaders/lazy.py +87 -54
- oscura/loaders/mmap_loader.py +1 -1
- oscura/loaders/numpy_loader.py +253 -116
- oscura/loaders/pcap.py +226 -151
- oscura/loaders/rigol.py +110 -49
- oscura/loaders/sigrok.py +201 -78
- oscura/loaders/tdms.py +81 -58
- oscura/loaders/tektronix.py +291 -174
- oscura/loaders/touchstone.py +182 -87
- oscura/loaders/tss.py +456 -0
- oscura/loaders/vcd.py +215 -117
- oscura/loaders/wav.py +155 -68
- oscura/reporting/__init__.py +9 -0
- oscura/reporting/analyze.py +352 -146
- oscura/reporting/argument_preparer.py +69 -14
- oscura/reporting/auto_report.py +97 -61
- oscura/reporting/batch.py +131 -58
- oscura/reporting/chart_selection.py +57 -45
- oscura/reporting/comparison.py +63 -17
- oscura/reporting/content/executive.py +76 -24
- oscura/reporting/core_formats/multi_format.py +11 -8
- oscura/reporting/engine.py +312 -158
- oscura/reporting/enhanced_reports.py +949 -0
- oscura/reporting/export.py +86 -43
- oscura/reporting/formatting/numbers.py +69 -42
- oscura/reporting/html.py +139 -58
- oscura/reporting/index.py +137 -65
- oscura/reporting/output.py +158 -67
- oscura/reporting/pdf.py +67 -102
- oscura/reporting/plots.py +191 -112
- oscura/reporting/sections.py +88 -47
- oscura/reporting/standards.py +104 -61
- oscura/reporting/summary_generator.py +75 -55
- oscura/reporting/tables.py +138 -54
- oscura/reporting/templates/enhanced/protocol_re.html +525 -0
- oscura/sessions/__init__.py +14 -23
- oscura/sessions/base.py +3 -3
- oscura/sessions/blackbox.py +106 -10
- oscura/sessions/generic.py +2 -2
- oscura/sessions/legacy.py +783 -0
- oscura/side_channel/__init__.py +63 -0
- oscura/side_channel/dpa.py +1025 -0
- oscura/utils/__init__.py +15 -1
- oscura/utils/bitwise.py +118 -0
- oscura/{builders → utils/builders}/__init__.py +1 -1
- oscura/{comparison → utils/comparison}/__init__.py +6 -6
- oscura/{comparison → utils/comparison}/compare.py +202 -101
- oscura/{comparison → utils/comparison}/golden.py +83 -63
- oscura/{comparison → utils/comparison}/limits.py +313 -89
- oscura/{comparison → utils/comparison}/mask.py +151 -45
- oscura/{comparison → utils/comparison}/trace_diff.py +1 -1
- oscura/{comparison → utils/comparison}/visualization.py +147 -89
- oscura/{component → utils/component}/__init__.py +3 -3
- oscura/{component → utils/component}/impedance.py +122 -58
- oscura/{component → utils/component}/reactive.py +165 -168
- oscura/{component → utils/component}/transmission_line.py +3 -3
- oscura/{filtering → utils/filtering}/__init__.py +6 -6
- oscura/{filtering → utils/filtering}/base.py +1 -1
- oscura/{filtering → utils/filtering}/convenience.py +2 -2
- oscura/{filtering → utils/filtering}/design.py +169 -93
- oscura/{filtering → utils/filtering}/filters.py +2 -2
- oscura/{filtering → utils/filtering}/introspection.py +2 -2
- oscura/utils/geometry.py +31 -0
- oscura/utils/imports.py +184 -0
- oscura/utils/lazy.py +1 -1
- oscura/{math → utils/math}/__init__.py +2 -2
- oscura/{math → utils/math}/arithmetic.py +114 -48
- oscura/{math → utils/math}/interpolation.py +139 -106
- oscura/utils/memory.py +129 -66
- oscura/utils/memory_advanced.py +92 -9
- oscura/utils/memory_extensions.py +10 -8
- oscura/{optimization → utils/optimization}/__init__.py +1 -1
- oscura/{optimization → utils/optimization}/search.py +2 -2
- oscura/utils/performance/__init__.py +58 -0
- oscura/utils/performance/caching.py +889 -0
- oscura/utils/performance/lsh_clustering.py +333 -0
- oscura/utils/performance/memory_optimizer.py +699 -0
- oscura/utils/performance/optimizations.py +675 -0
- oscura/utils/performance/parallel.py +654 -0
- oscura/utils/performance/profiling.py +661 -0
- oscura/{pipeline → utils/pipeline}/base.py +1 -1
- oscura/{pipeline → utils/pipeline}/composition.py +1 -1
- oscura/{pipeline → utils/pipeline}/parallel.py +3 -2
- oscura/{pipeline → utils/pipeline}/pipeline.py +1 -1
- oscura/{pipeline → utils/pipeline}/reverse_engineering.py +412 -221
- oscura/{search → utils/search}/__init__.py +3 -3
- oscura/{search → utils/search}/anomaly.py +188 -58
- oscura/utils/search/context.py +294 -0
- oscura/{search → utils/search}/pattern.py +138 -10
- oscura/utils/serial.py +51 -0
- oscura/utils/storage/__init__.py +61 -0
- oscura/utils/storage/database.py +1166 -0
- oscura/{streaming → utils/streaming}/chunked.py +302 -143
- oscura/{streaming → utils/streaming}/progressive.py +1 -1
- oscura/{streaming → utils/streaming}/realtime.py +3 -2
- oscura/{triggering → utils/triggering}/__init__.py +6 -6
- oscura/{triggering → utils/triggering}/base.py +6 -6
- oscura/{triggering → utils/triggering}/edge.py +2 -2
- oscura/{triggering → utils/triggering}/pattern.py +2 -2
- oscura/{triggering → utils/triggering}/pulse.py +115 -74
- oscura/{triggering → utils/triggering}/window.py +2 -2
- oscura/utils/validation.py +32 -0
- oscura/validation/__init__.py +121 -0
- oscura/{compliance → validation/compliance}/__init__.py +5 -5
- oscura/{compliance → validation/compliance}/advanced.py +5 -5
- oscura/{compliance → validation/compliance}/masks.py +1 -1
- oscura/{compliance → validation/compliance}/reporting.py +127 -53
- oscura/{compliance → validation/compliance}/testing.py +114 -52
- oscura/validation/compliance_tests.py +915 -0
- oscura/validation/fuzzer.py +990 -0
- oscura/validation/grammar_tests.py +596 -0
- oscura/validation/grammar_validator.py +904 -0
- oscura/validation/hil_testing.py +977 -0
- oscura/{quality → validation/quality}/__init__.py +4 -4
- oscura/{quality → validation/quality}/ensemble.py +251 -171
- oscura/{quality → validation/quality}/explainer.py +3 -3
- oscura/{quality → validation/quality}/scoring.py +1 -1
- oscura/{quality → validation/quality}/warnings.py +4 -4
- oscura/validation/regression_suite.py +808 -0
- oscura/validation/replay.py +788 -0
- oscura/{testing → validation/testing}/__init__.py +2 -2
- oscura/{testing → validation/testing}/synthetic.py +5 -5
- oscura/visualization/__init__.py +9 -0
- oscura/visualization/accessibility.py +1 -1
- oscura/visualization/annotations.py +64 -67
- oscura/visualization/colors.py +7 -7
- oscura/visualization/digital.py +180 -81
- oscura/visualization/eye.py +236 -85
- oscura/visualization/interactive.py +320 -143
- oscura/visualization/jitter.py +587 -247
- oscura/visualization/layout.py +169 -134
- oscura/visualization/optimization.py +103 -52
- oscura/visualization/palettes.py +1 -1
- oscura/visualization/power.py +427 -211
- oscura/visualization/power_extended.py +626 -297
- oscura/visualization/presets.py +2 -0
- oscura/visualization/protocols.py +495 -181
- oscura/visualization/render.py +79 -63
- oscura/visualization/reverse_engineering.py +171 -124
- oscura/visualization/signal_integrity.py +460 -279
- oscura/visualization/specialized.py +190 -100
- oscura/visualization/spectral.py +670 -255
- oscura/visualization/thumbnails.py +166 -137
- oscura/visualization/waveform.py +150 -63
- oscura/workflows/__init__.py +3 -0
- oscura/{batch → workflows/batch}/__init__.py +5 -5
- oscura/{batch → workflows/batch}/advanced.py +150 -75
- oscura/workflows/batch/aggregate.py +531 -0
- oscura/workflows/batch/analyze.py +236 -0
- oscura/{batch → workflows/batch}/logging.py +2 -2
- oscura/{batch → workflows/batch}/metrics.py +1 -1
- oscura/workflows/complete_re.py +1144 -0
- oscura/workflows/compliance.py +44 -54
- oscura/workflows/digital.py +197 -51
- oscura/workflows/legacy/__init__.py +12 -0
- oscura/{workflow → workflows/legacy}/dag.py +4 -1
- oscura/workflows/multi_trace.py +9 -9
- oscura/workflows/power.py +42 -62
- oscura/workflows/protocol.py +82 -49
- oscura/workflows/reverse_engineering.py +351 -150
- oscura/workflows/signal_integrity.py +157 -82
- oscura-0.7.0.dist-info/METADATA +661 -0
- oscura-0.7.0.dist-info/RECORD +591 -0
- oscura/batch/aggregate.py +0 -300
- oscura/batch/analyze.py +0 -139
- oscura/dsl/__init__.py +0 -73
- oscura/exceptions.py +0 -59
- oscura/exploratory/fuzzy.py +0 -513
- oscura/exploratory/sync.py +0 -384
- oscura/exporters/__init__.py +0 -94
- oscura/exporters/csv.py +0 -303
- oscura/exporters/exporters.py +0 -44
- oscura/exporters/hdf5.py +0 -217
- oscura/exporters/html_export.py +0 -701
- oscura/exporters/json_export.py +0 -291
- oscura/exporters/markdown_export.py +0 -367
- oscura/exporters/matlab_export.py +0 -354
- oscura/exporters/npz_export.py +0 -219
- oscura/exporters/spice_export.py +0 -210
- oscura/search/context.py +0 -149
- oscura/session/__init__.py +0 -34
- oscura/session/annotations.py +0 -289
- oscura/session/history.py +0 -313
- oscura/session/session.py +0 -520
- oscura/workflow/__init__.py +0 -13
- oscura-0.5.1.dist-info/METADATA +0 -583
- oscura-0.5.1.dist-info/RECORD +0 -481
- /oscura/core/{config.py → config/legacy.py} +0 -0
- /oscura/{extensibility → core/extensibility}/__init__.py +0 -0
- /oscura/{extensibility → core/extensibility}/registry.py +0 -0
- /oscura/{plugins → core/plugins}/isolation.py +0 -0
- /oscura/{schemas → core/schemas}/bus_configuration.json +0 -0
- /oscura/{builders → utils/builders}/signal_builder.py +0 -0
- /oscura/{optimization → utils/optimization}/parallel.py +0 -0
- /oscura/{pipeline → utils/pipeline}/__init__.py +0 -0
- /oscura/{streaming → utils/streaming}/__init__.py +0 -0
- {oscura-0.5.1.dist-info → oscura-0.7.0.dist-info}/WHEEL +0 -0
- {oscura-0.5.1.dist-info → oscura-0.7.0.dist-info}/entry_points.txt +0 -0
- {oscura-0.5.1.dist-info → oscura-0.7.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
"""Locality-Sensitive Hashing for fast payload clustering.
|
|
2
|
+
|
|
3
|
+
This module implements LSH-based clustering to reduce payload clustering
|
|
4
|
+
from O(n²) to O(n log n) complexity. Uses length-based bucketing and
|
|
5
|
+
sampling for byte sequences.
|
|
6
|
+
|
|
7
|
+
Strategy:
|
|
8
|
+
1. Bucket payloads by length (exact edit distance requires similar lengths)
|
|
9
|
+
2. Within each bucket, use sample-based hashing for candidate generation
|
|
10
|
+
3. Verify candidates with full comparison
|
|
11
|
+
|
|
12
|
+
References:
|
|
13
|
+
- Indyk, P. & Motwani, R. (1998). "Approximate Nearest Neighbors"
|
|
14
|
+
- Leskovec et al. (2014). "Mining of Massive Datasets"
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import hashlib
|
|
20
|
+
from collections import defaultdict
|
|
21
|
+
from collections.abc import Sequence
|
|
22
|
+
from typing import TYPE_CHECKING
|
|
23
|
+
|
|
24
|
+
import numpy as np
|
|
25
|
+
|
|
26
|
+
if TYPE_CHECKING:
|
|
27
|
+
from oscura.analyzers.packet.payload_analysis import PayloadCluster
|
|
28
|
+
|
|
29
|
+
__all__ = [
|
|
30
|
+
"LSHClustering",
|
|
31
|
+
"cluster_payloads_lsh",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class LSHClustering:
|
|
36
|
+
"""Locality-Sensitive Hashing for fast payload clustering.
|
|
37
|
+
|
|
38
|
+
Uses MinHash with shingles to quickly identify candidate similar payloads,
|
|
39
|
+
reducing comparisons from O(n²) to O(n log n).
|
|
40
|
+
|
|
41
|
+
Example:
|
|
42
|
+
>>> lsh = LSHClustering(n_hash_functions=128, n_bands=16)
|
|
43
|
+
>>> clusters = lsh.cluster(payloads, threshold=0.8)
|
|
44
|
+
>>> print(f"Found {len(clusters)} clusters")
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
def __init__(
|
|
48
|
+
self,
|
|
49
|
+
n_hash_functions: int = 128,
|
|
50
|
+
n_bands: int = 8,
|
|
51
|
+
shingle_size: int = 4,
|
|
52
|
+
use_byte_shingles: bool = True,
|
|
53
|
+
) -> None:
|
|
54
|
+
"""Initialize LSH clustering.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
n_hash_functions: Number of hash functions for MinHash (more = better accuracy).
|
|
58
|
+
n_bands: Number of bands for LSH banding (fewer = more candidates, better recall).
|
|
59
|
+
shingle_size: Size of shingles in bytes.
|
|
60
|
+
use_byte_shingles: Use byte-level shingles instead of bit-level.
|
|
61
|
+
"""
|
|
62
|
+
self.n_hash_functions = n_hash_functions
|
|
63
|
+
self.n_bands = n_bands
|
|
64
|
+
self.rows_per_band = n_hash_functions // n_bands
|
|
65
|
+
self.shingle_size = shingle_size
|
|
66
|
+
self.use_byte_shingles = use_byte_shingles
|
|
67
|
+
|
|
68
|
+
# Pre-generate hash seeds for MinHash
|
|
69
|
+
self._hash_seeds = self._generate_hash_seeds(n_hash_functions)
|
|
70
|
+
|
|
71
|
+
def _generate_hash_seeds(self, n: int) -> list[int]:
|
|
72
|
+
"""Generate deterministic hash seeds.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
n: Number of seeds to generate.
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
List of integer seeds.
|
|
79
|
+
"""
|
|
80
|
+
# Use a fixed seed for reproducibility
|
|
81
|
+
rng = np.random.default_rng(42)
|
|
82
|
+
return [int(rng.integers(1, 2**31 - 1)) for _ in range(n)]
|
|
83
|
+
|
|
84
|
+
def _shingle(self, payload: bytes) -> set[bytes]:
|
|
85
|
+
"""Convert payload to set of shingles.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
payload: Payload bytes to shingle.
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
Set of shingles (byte sequences).
|
|
92
|
+
"""
|
|
93
|
+
if len(payload) < self.shingle_size:
|
|
94
|
+
# For very short payloads, use the whole payload
|
|
95
|
+
return {payload}
|
|
96
|
+
|
|
97
|
+
shingles = set()
|
|
98
|
+
for i in range(len(payload) - self.shingle_size + 1):
|
|
99
|
+
shingle = payload[i : i + self.shingle_size]
|
|
100
|
+
shingles.add(shingle)
|
|
101
|
+
|
|
102
|
+
return shingles
|
|
103
|
+
|
|
104
|
+
def _minhash_signature(self, shingles: set[bytes]) -> tuple[int, ...]:
|
|
105
|
+
"""Compute MinHash signature for a set of shingles.
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
shingles: Set of shingles from payload.
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
Tuple of MinHash signature values.
|
|
112
|
+
"""
|
|
113
|
+
if not shingles:
|
|
114
|
+
# Empty payload
|
|
115
|
+
return tuple([0] * self.n_hash_functions)
|
|
116
|
+
|
|
117
|
+
signature = []
|
|
118
|
+
|
|
119
|
+
for seed in self._hash_seeds:
|
|
120
|
+
min_hash = 2**32 - 1 # Max 32-bit value
|
|
121
|
+
|
|
122
|
+
for shingle in shingles:
|
|
123
|
+
# Hash shingle with this seed
|
|
124
|
+
h = hashlib.sha256(shingle + seed.to_bytes(4, "big")).digest()
|
|
125
|
+
hash_val = int.from_bytes(h[:4], "big")
|
|
126
|
+
min_hash = min(min_hash, hash_val)
|
|
127
|
+
|
|
128
|
+
signature.append(min_hash)
|
|
129
|
+
|
|
130
|
+
return tuple(signature)
|
|
131
|
+
|
|
132
|
+
def _lsh_buckets(self, signatures: list[tuple[int, ...]]) -> dict[int, list[int]]:
|
|
133
|
+
"""Assign signatures to LSH buckets using banding.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
signatures: List of MinHash signatures.
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
Dictionary mapping bucket IDs to payload indices.
|
|
140
|
+
"""
|
|
141
|
+
buckets: dict[int, list[int]] = defaultdict(list)
|
|
142
|
+
|
|
143
|
+
for idx, sig in enumerate(signatures):
|
|
144
|
+
# Split signature into bands
|
|
145
|
+
for band_idx in range(self.n_bands):
|
|
146
|
+
start = band_idx * self.rows_per_band
|
|
147
|
+
end = start + self.rows_per_band
|
|
148
|
+
band = sig[start:end]
|
|
149
|
+
|
|
150
|
+
# Hash the band to get bucket ID
|
|
151
|
+
band_bytes = b"".join(v.to_bytes(4, "big") for v in band)
|
|
152
|
+
bucket_id = int.from_bytes(
|
|
153
|
+
hashlib.sha256(band_bytes + band_idx.to_bytes(4, "big")).digest()[:4],
|
|
154
|
+
"big",
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
buckets[bucket_id].append(idx)
|
|
158
|
+
|
|
159
|
+
return buckets
|
|
160
|
+
|
|
161
|
+
def _estimate_similarity(self, sig_a: tuple[int, ...], sig_b: tuple[int, ...]) -> float:
|
|
162
|
+
"""Estimate Jaccard similarity from MinHash signatures.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
sig_a: MinHash signature of first payload.
|
|
166
|
+
sig_b: MinHash signature of second payload.
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
Estimated Jaccard similarity (0-1).
|
|
170
|
+
"""
|
|
171
|
+
matches = sum(1 for a, b in zip(sig_a, sig_b, strict=True) if a == b)
|
|
172
|
+
return matches / len(sig_a)
|
|
173
|
+
|
|
174
|
+
def cluster(
|
|
175
|
+
self,
|
|
176
|
+
payloads: Sequence[bytes],
|
|
177
|
+
threshold: float = 0.8,
|
|
178
|
+
verify_with_levenshtein: bool = True,
|
|
179
|
+
) -> list[PayloadCluster]:
|
|
180
|
+
"""Cluster payloads using LSH.
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
payloads: List of payloads to cluster.
|
|
184
|
+
threshold: Similarity threshold for clustering.
|
|
185
|
+
verify_with_levenshtein: Verify LSH candidates with full comparison.
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
List of PayloadCluster objects.
|
|
189
|
+
"""
|
|
190
|
+
|
|
191
|
+
if not payloads:
|
|
192
|
+
return []
|
|
193
|
+
|
|
194
|
+
# Compute signatures and find candidate pairs
|
|
195
|
+
signatures = self._compute_all_signatures(payloads)
|
|
196
|
+
candidate_pairs = self._find_candidate_pairs(signatures)
|
|
197
|
+
|
|
198
|
+
# Build similarity graph
|
|
199
|
+
graph = self._build_similarity_graph(
|
|
200
|
+
payloads, signatures, candidate_pairs, threshold, verify_with_levenshtein
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
# Find connected components
|
|
204
|
+
return self._extract_clusters(payloads, graph)
|
|
205
|
+
|
|
206
|
+
def _compute_all_signatures(self, payloads: Sequence[bytes]) -> list[tuple[int, ...]]:
|
|
207
|
+
"""Compute MinHash signatures for all payloads."""
|
|
208
|
+
signatures = []
|
|
209
|
+
for payload in payloads:
|
|
210
|
+
shingles = self._shingle(payload)
|
|
211
|
+
sig = self._minhash_signature(shingles)
|
|
212
|
+
signatures.append(sig)
|
|
213
|
+
return signatures
|
|
214
|
+
|
|
215
|
+
def _find_candidate_pairs(self, signatures: list[tuple[int, ...]]) -> set[tuple[int, int]]:
|
|
216
|
+
"""Find candidate pairs from LSH buckets."""
|
|
217
|
+
buckets = self._lsh_buckets(signatures)
|
|
218
|
+
candidate_pairs: set[tuple[int, int]] = set()
|
|
219
|
+
|
|
220
|
+
for bucket_indices in buckets.values():
|
|
221
|
+
for i in range(len(bucket_indices)):
|
|
222
|
+
for j in range(i + 1, len(bucket_indices)):
|
|
223
|
+
idx_a = bucket_indices[i]
|
|
224
|
+
idx_b = bucket_indices[j]
|
|
225
|
+
pair = (min(idx_a, idx_b), max(idx_a, idx_b))
|
|
226
|
+
candidate_pairs.add(pair)
|
|
227
|
+
|
|
228
|
+
return candidate_pairs
|
|
229
|
+
|
|
230
|
+
def _build_similarity_graph(
|
|
231
|
+
self,
|
|
232
|
+
payloads: Sequence[bytes],
|
|
233
|
+
signatures: list[tuple[int, ...]],
|
|
234
|
+
candidate_pairs: set[tuple[int, int]],
|
|
235
|
+
threshold: float,
|
|
236
|
+
verify: bool,
|
|
237
|
+
) -> dict[int, set[int]]:
|
|
238
|
+
"""Build adjacency graph from verified candidate pairs."""
|
|
239
|
+
from oscura.analyzers.packet.payload_analysis import compute_similarity
|
|
240
|
+
|
|
241
|
+
graph: dict[int, set[int]] = {i: set() for i in range(len(payloads))}
|
|
242
|
+
|
|
243
|
+
for i, j in candidate_pairs:
|
|
244
|
+
if verify:
|
|
245
|
+
similarity = compute_similarity(payloads[i], payloads[j])
|
|
246
|
+
else:
|
|
247
|
+
similarity = self._estimate_similarity(signatures[i], signatures[j])
|
|
248
|
+
|
|
249
|
+
if similarity >= threshold:
|
|
250
|
+
graph[i].add(j)
|
|
251
|
+
graph[j].add(i)
|
|
252
|
+
|
|
253
|
+
return graph
|
|
254
|
+
|
|
255
|
+
def _extract_clusters(
|
|
256
|
+
self, payloads: Sequence[bytes], graph: dict[int, set[int]]
|
|
257
|
+
) -> list[PayloadCluster]:
|
|
258
|
+
"""Extract clusters from similarity graph using BFS."""
|
|
259
|
+
|
|
260
|
+
from oscura.analyzers.packet.payload_analysis import PayloadCluster
|
|
261
|
+
|
|
262
|
+
clusters: list[PayloadCluster] = []
|
|
263
|
+
visited: set[int] = set()
|
|
264
|
+
|
|
265
|
+
for start in range(len(payloads)):
|
|
266
|
+
if start in visited:
|
|
267
|
+
continue
|
|
268
|
+
|
|
269
|
+
component = self._bfs_component(graph, start, visited)
|
|
270
|
+
|
|
271
|
+
cluster_payloads = [payloads[i] for i in component]
|
|
272
|
+
clusters.append(
|
|
273
|
+
PayloadCluster(
|
|
274
|
+
cluster_id=len(clusters),
|
|
275
|
+
payloads=cluster_payloads,
|
|
276
|
+
indices=component,
|
|
277
|
+
representative=payloads[component[0]],
|
|
278
|
+
size=len(component),
|
|
279
|
+
)
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
return clusters
|
|
283
|
+
|
|
284
|
+
def _bfs_component(
|
|
285
|
+
self, graph: dict[int, set[int]], start: int, visited: set[int]
|
|
286
|
+
) -> list[int]:
|
|
287
|
+
"""Find connected component using BFS."""
|
|
288
|
+
from collections import deque
|
|
289
|
+
|
|
290
|
+
component = []
|
|
291
|
+
queue = deque([start])
|
|
292
|
+
visited.add(start)
|
|
293
|
+
|
|
294
|
+
while queue:
|
|
295
|
+
node = queue.popleft()
|
|
296
|
+
component.append(node)
|
|
297
|
+
|
|
298
|
+
for neighbor in graph[node]:
|
|
299
|
+
if neighbor not in visited:
|
|
300
|
+
visited.add(neighbor)
|
|
301
|
+
queue.append(neighbor)
|
|
302
|
+
|
|
303
|
+
return component
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def cluster_payloads_lsh(
|
|
307
|
+
payloads: Sequence[bytes],
|
|
308
|
+
threshold: float = 0.8,
|
|
309
|
+
n_hash_functions: int = 128,
|
|
310
|
+
n_bands: int = 8, # Fewer bands = more candidates = better recall
|
|
311
|
+
verify: bool = True,
|
|
312
|
+
) -> list[PayloadCluster]:
|
|
313
|
+
"""Cluster payloads using LSH for O(n log n) performance.
|
|
314
|
+
|
|
315
|
+
Args:
|
|
316
|
+
payloads: List of payloads to cluster.
|
|
317
|
+
threshold: Similarity threshold for clustering.
|
|
318
|
+
n_hash_functions: Number of hash functions for MinHash.
|
|
319
|
+
n_bands: Number of bands for LSH banding (fewer = more lenient).
|
|
320
|
+
verify: Verify LSH candidates with Levenshtein distance.
|
|
321
|
+
|
|
322
|
+
Returns:
|
|
323
|
+
List of PayloadCluster objects.
|
|
324
|
+
|
|
325
|
+
Example:
|
|
326
|
+
>>> clusters = cluster_payloads_lsh(payloads, threshold=0.85)
|
|
327
|
+
>>> print(f"Found {len(clusters)} clusters in O(n log n) time")
|
|
328
|
+
"""
|
|
329
|
+
lsh = LSHClustering(
|
|
330
|
+
n_hash_functions=n_hash_functions,
|
|
331
|
+
n_bands=n_bands,
|
|
332
|
+
)
|
|
333
|
+
return lsh.cluster(payloads, threshold=threshold, verify_with_levenshtein=verify)
|