oscura 0.5.0__py3-none-any.whl → 0.6.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (513) hide show
  1. oscura/__init__.py +169 -167
  2. oscura/analyzers/__init__.py +3 -0
  3. oscura/analyzers/classification.py +659 -0
  4. oscura/analyzers/digital/__init__.py +0 -48
  5. oscura/analyzers/digital/edges.py +325 -65
  6. oscura/analyzers/digital/extraction.py +0 -195
  7. oscura/analyzers/digital/quality.py +293 -166
  8. oscura/analyzers/digital/timing.py +260 -115
  9. oscura/analyzers/digital/timing_numba.py +334 -0
  10. oscura/analyzers/entropy.py +605 -0
  11. oscura/analyzers/eye/diagram.py +176 -109
  12. oscura/analyzers/eye/metrics.py +5 -5
  13. oscura/analyzers/jitter/__init__.py +6 -4
  14. oscura/analyzers/jitter/ber.py +52 -52
  15. oscura/analyzers/jitter/classification.py +156 -0
  16. oscura/analyzers/jitter/decomposition.py +163 -113
  17. oscura/analyzers/jitter/spectrum.py +80 -64
  18. oscura/analyzers/ml/__init__.py +39 -0
  19. oscura/analyzers/ml/features.py +600 -0
  20. oscura/analyzers/ml/signal_classifier.py +604 -0
  21. oscura/analyzers/packet/daq.py +246 -158
  22. oscura/analyzers/packet/parser.py +12 -1
  23. oscura/analyzers/packet/payload.py +50 -2110
  24. oscura/analyzers/packet/payload_analysis.py +361 -181
  25. oscura/analyzers/packet/payload_patterns.py +133 -70
  26. oscura/analyzers/packet/stream.py +84 -23
  27. oscura/analyzers/patterns/__init__.py +26 -5
  28. oscura/analyzers/patterns/anomaly_detection.py +908 -0
  29. oscura/analyzers/patterns/clustering.py +169 -108
  30. oscura/analyzers/patterns/clustering_optimized.py +227 -0
  31. oscura/analyzers/patterns/discovery.py +1 -1
  32. oscura/analyzers/patterns/matching.py +581 -197
  33. oscura/analyzers/patterns/pattern_mining.py +778 -0
  34. oscura/analyzers/patterns/periodic.py +121 -38
  35. oscura/analyzers/patterns/sequences.py +175 -78
  36. oscura/analyzers/power/conduction.py +1 -1
  37. oscura/analyzers/power/soa.py +6 -6
  38. oscura/analyzers/power/switching.py +250 -110
  39. oscura/analyzers/protocol/__init__.py +17 -1
  40. oscura/analyzers/protocols/__init__.py +1 -22
  41. oscura/analyzers/protocols/base.py +6 -6
  42. oscura/analyzers/protocols/ble/__init__.py +38 -0
  43. oscura/analyzers/protocols/ble/analyzer.py +809 -0
  44. oscura/analyzers/protocols/ble/uuids.py +288 -0
  45. oscura/analyzers/protocols/can.py +257 -127
  46. oscura/analyzers/protocols/can_fd.py +107 -80
  47. oscura/analyzers/protocols/flexray.py +139 -80
  48. oscura/analyzers/protocols/hdlc.py +93 -58
  49. oscura/analyzers/protocols/i2c.py +247 -106
  50. oscura/analyzers/protocols/i2s.py +138 -86
  51. oscura/analyzers/protocols/industrial/__init__.py +40 -0
  52. oscura/analyzers/protocols/industrial/bacnet/__init__.py +33 -0
  53. oscura/analyzers/protocols/industrial/bacnet/analyzer.py +708 -0
  54. oscura/analyzers/protocols/industrial/bacnet/encoding.py +412 -0
  55. oscura/analyzers/protocols/industrial/bacnet/services.py +622 -0
  56. oscura/analyzers/protocols/industrial/ethercat/__init__.py +30 -0
  57. oscura/analyzers/protocols/industrial/ethercat/analyzer.py +474 -0
  58. oscura/analyzers/protocols/industrial/ethercat/mailbox.py +339 -0
  59. oscura/analyzers/protocols/industrial/ethercat/topology.py +166 -0
  60. oscura/analyzers/protocols/industrial/modbus/__init__.py +31 -0
  61. oscura/analyzers/protocols/industrial/modbus/analyzer.py +525 -0
  62. oscura/analyzers/protocols/industrial/modbus/crc.py +79 -0
  63. oscura/analyzers/protocols/industrial/modbus/functions.py +436 -0
  64. oscura/analyzers/protocols/industrial/opcua/__init__.py +21 -0
  65. oscura/analyzers/protocols/industrial/opcua/analyzer.py +552 -0
  66. oscura/analyzers/protocols/industrial/opcua/datatypes.py +446 -0
  67. oscura/analyzers/protocols/industrial/opcua/services.py +264 -0
  68. oscura/analyzers/protocols/industrial/profinet/__init__.py +23 -0
  69. oscura/analyzers/protocols/industrial/profinet/analyzer.py +441 -0
  70. oscura/analyzers/protocols/industrial/profinet/dcp.py +263 -0
  71. oscura/analyzers/protocols/industrial/profinet/ptcp.py +200 -0
  72. oscura/analyzers/protocols/jtag.py +180 -98
  73. oscura/analyzers/protocols/lin.py +219 -114
  74. oscura/analyzers/protocols/manchester.py +4 -4
  75. oscura/analyzers/protocols/onewire.py +253 -149
  76. oscura/analyzers/protocols/parallel_bus/__init__.py +20 -0
  77. oscura/analyzers/protocols/parallel_bus/centronics.py +92 -0
  78. oscura/analyzers/protocols/parallel_bus/gpib.py +137 -0
  79. oscura/analyzers/protocols/spi.py +192 -95
  80. oscura/analyzers/protocols/swd.py +321 -167
  81. oscura/analyzers/protocols/uart.py +267 -125
  82. oscura/analyzers/protocols/usb.py +235 -131
  83. oscura/analyzers/side_channel/power.py +17 -12
  84. oscura/analyzers/signal/__init__.py +15 -0
  85. oscura/analyzers/signal/timing_analysis.py +1086 -0
  86. oscura/analyzers/signal_integrity/__init__.py +4 -1
  87. oscura/analyzers/signal_integrity/sparams.py +2 -19
  88. oscura/analyzers/spectral/chunked.py +129 -60
  89. oscura/analyzers/spectral/chunked_fft.py +300 -94
  90. oscura/analyzers/spectral/chunked_wavelet.py +100 -80
  91. oscura/analyzers/statistical/checksum.py +376 -217
  92. oscura/analyzers/statistical/classification.py +229 -107
  93. oscura/analyzers/statistical/entropy.py +78 -53
  94. oscura/analyzers/statistics/correlation.py +407 -211
  95. oscura/analyzers/statistics/outliers.py +2 -2
  96. oscura/analyzers/statistics/streaming.py +30 -5
  97. oscura/analyzers/validation.py +216 -101
  98. oscura/analyzers/waveform/measurements.py +9 -0
  99. oscura/analyzers/waveform/measurements_with_uncertainty.py +31 -15
  100. oscura/analyzers/waveform/spectral.py +500 -228
  101. oscura/api/__init__.py +31 -5
  102. oscura/api/dsl/__init__.py +582 -0
  103. oscura/{dsl → api/dsl}/commands.py +43 -76
  104. oscura/{dsl → api/dsl}/interpreter.py +26 -51
  105. oscura/{dsl → api/dsl}/parser.py +107 -77
  106. oscura/{dsl → api/dsl}/repl.py +2 -2
  107. oscura/api/dsl.py +1 -1
  108. oscura/{integrations → api/integrations}/__init__.py +1 -1
  109. oscura/{integrations → api/integrations}/llm.py +201 -102
  110. oscura/api/operators.py +3 -3
  111. oscura/api/optimization.py +144 -30
  112. oscura/api/rest_server.py +921 -0
  113. oscura/api/server/__init__.py +17 -0
  114. oscura/api/server/dashboard.py +850 -0
  115. oscura/api/server/static/README.md +34 -0
  116. oscura/api/server/templates/base.html +181 -0
  117. oscura/api/server/templates/export.html +120 -0
  118. oscura/api/server/templates/home.html +284 -0
  119. oscura/api/server/templates/protocols.html +58 -0
  120. oscura/api/server/templates/reports.html +43 -0
  121. oscura/api/server/templates/session_detail.html +89 -0
  122. oscura/api/server/templates/sessions.html +83 -0
  123. oscura/api/server/templates/waveforms.html +73 -0
  124. oscura/automotive/__init__.py +8 -1
  125. oscura/automotive/can/__init__.py +10 -0
  126. oscura/automotive/can/checksum.py +3 -1
  127. oscura/automotive/can/dbc_generator.py +590 -0
  128. oscura/automotive/can/message_wrapper.py +121 -74
  129. oscura/automotive/can/patterns.py +98 -21
  130. oscura/automotive/can/session.py +292 -56
  131. oscura/automotive/can/state_machine.py +6 -3
  132. oscura/automotive/can/stimulus_response.py +97 -75
  133. oscura/automotive/dbc/__init__.py +10 -2
  134. oscura/automotive/dbc/generator.py +84 -56
  135. oscura/automotive/dbc/parser.py +6 -6
  136. oscura/automotive/dtc/data.json +2763 -0
  137. oscura/automotive/dtc/database.py +2 -2
  138. oscura/automotive/flexray/__init__.py +31 -0
  139. oscura/automotive/flexray/analyzer.py +504 -0
  140. oscura/automotive/flexray/crc.py +185 -0
  141. oscura/automotive/flexray/fibex.py +449 -0
  142. oscura/automotive/j1939/__init__.py +45 -8
  143. oscura/automotive/j1939/analyzer.py +605 -0
  144. oscura/automotive/j1939/spns.py +326 -0
  145. oscura/automotive/j1939/transport.py +306 -0
  146. oscura/automotive/lin/__init__.py +47 -0
  147. oscura/automotive/lin/analyzer.py +612 -0
  148. oscura/automotive/loaders/blf.py +13 -2
  149. oscura/automotive/loaders/csv_can.py +143 -72
  150. oscura/automotive/loaders/dispatcher.py +50 -2
  151. oscura/automotive/loaders/mdf.py +86 -45
  152. oscura/automotive/loaders/pcap.py +111 -61
  153. oscura/automotive/uds/__init__.py +4 -0
  154. oscura/automotive/uds/analyzer.py +725 -0
  155. oscura/automotive/uds/decoder.py +140 -58
  156. oscura/automotive/uds/models.py +7 -1
  157. oscura/automotive/visualization.py +1 -1
  158. oscura/cli/analyze.py +348 -0
  159. oscura/cli/batch.py +142 -122
  160. oscura/cli/benchmark.py +275 -0
  161. oscura/cli/characterize.py +137 -82
  162. oscura/cli/compare.py +224 -131
  163. oscura/cli/completion.py +250 -0
  164. oscura/cli/config_cmd.py +361 -0
  165. oscura/cli/decode.py +164 -87
  166. oscura/cli/export.py +286 -0
  167. oscura/cli/main.py +115 -31
  168. oscura/{onboarding → cli/onboarding}/__init__.py +3 -3
  169. oscura/{onboarding → cli/onboarding}/help.py +80 -58
  170. oscura/{onboarding → cli/onboarding}/tutorials.py +97 -72
  171. oscura/{onboarding → cli/onboarding}/wizard.py +55 -36
  172. oscura/cli/progress.py +147 -0
  173. oscura/cli/shell.py +157 -135
  174. oscura/cli/validate_cmd.py +204 -0
  175. oscura/cli/visualize.py +158 -0
  176. oscura/convenience.py +125 -79
  177. oscura/core/__init__.py +4 -2
  178. oscura/core/backend_selector.py +3 -3
  179. oscura/core/cache.py +126 -15
  180. oscura/core/cancellation.py +1 -1
  181. oscura/{config → core/config}/__init__.py +20 -11
  182. oscura/{config → core/config}/defaults.py +1 -1
  183. oscura/{config → core/config}/loader.py +7 -5
  184. oscura/{config → core/config}/memory.py +5 -5
  185. oscura/{config → core/config}/migration.py +1 -1
  186. oscura/{config → core/config}/pipeline.py +99 -23
  187. oscura/{config → core/config}/preferences.py +1 -1
  188. oscura/{config → core/config}/protocol.py +3 -3
  189. oscura/{config → core/config}/schema.py +426 -272
  190. oscura/{config → core/config}/settings.py +1 -1
  191. oscura/{config → core/config}/thresholds.py +195 -153
  192. oscura/core/correlation.py +5 -6
  193. oscura/core/cross_domain.py +0 -2
  194. oscura/core/debug.py +9 -5
  195. oscura/{extensibility → core/extensibility}/docs.py +158 -70
  196. oscura/{extensibility → core/extensibility}/extensions.py +160 -76
  197. oscura/{extensibility → core/extensibility}/logging.py +1 -1
  198. oscura/{extensibility → core/extensibility}/measurements.py +1 -1
  199. oscura/{extensibility → core/extensibility}/plugins.py +1 -1
  200. oscura/{extensibility → core/extensibility}/templates.py +73 -3
  201. oscura/{extensibility → core/extensibility}/validation.py +1 -1
  202. oscura/core/gpu_backend.py +11 -7
  203. oscura/core/log_query.py +101 -11
  204. oscura/core/logging.py +126 -54
  205. oscura/core/logging_advanced.py +5 -5
  206. oscura/core/memory_limits.py +108 -70
  207. oscura/core/memory_monitor.py +2 -2
  208. oscura/core/memory_progress.py +7 -7
  209. oscura/core/memory_warnings.py +1 -1
  210. oscura/core/numba_backend.py +13 -13
  211. oscura/{plugins → core/plugins}/__init__.py +9 -9
  212. oscura/{plugins → core/plugins}/base.py +7 -7
  213. oscura/{plugins → core/plugins}/cli.py +3 -3
  214. oscura/{plugins → core/plugins}/discovery.py +186 -106
  215. oscura/{plugins → core/plugins}/lifecycle.py +1 -1
  216. oscura/{plugins → core/plugins}/manager.py +7 -7
  217. oscura/{plugins → core/plugins}/registry.py +3 -3
  218. oscura/{plugins → core/plugins}/versioning.py +1 -1
  219. oscura/core/progress.py +16 -1
  220. oscura/core/provenance.py +8 -2
  221. oscura/{schemas → core/schemas}/__init__.py +2 -2
  222. oscura/core/schemas/bus_configuration.json +322 -0
  223. oscura/core/schemas/device_mapping.json +182 -0
  224. oscura/core/schemas/packet_format.json +418 -0
  225. oscura/core/schemas/protocol_definition.json +363 -0
  226. oscura/core/types.py +4 -0
  227. oscura/core/uncertainty.py +3 -3
  228. oscura/correlation/__init__.py +52 -0
  229. oscura/correlation/multi_protocol.py +811 -0
  230. oscura/discovery/auto_decoder.py +117 -35
  231. oscura/discovery/comparison.py +191 -86
  232. oscura/discovery/quality_validator.py +155 -68
  233. oscura/discovery/signal_detector.py +196 -79
  234. oscura/export/__init__.py +18 -20
  235. oscura/export/kaitai_struct.py +513 -0
  236. oscura/export/scapy_layer.py +801 -0
  237. oscura/export/wireshark/README.md +15 -15
  238. oscura/export/wireshark/generator.py +1 -1
  239. oscura/export/wireshark/templates/dissector.lua.j2 +2 -2
  240. oscura/export/wireshark_dissector.py +746 -0
  241. oscura/guidance/wizard.py +207 -111
  242. oscura/hardware/__init__.py +19 -0
  243. oscura/{acquisition → hardware/acquisition}/__init__.py +4 -4
  244. oscura/{acquisition → hardware/acquisition}/file.py +2 -2
  245. oscura/{acquisition → hardware/acquisition}/hardware.py +7 -7
  246. oscura/{acquisition → hardware/acquisition}/saleae.py +15 -12
  247. oscura/{acquisition → hardware/acquisition}/socketcan.py +1 -1
  248. oscura/{acquisition → hardware/acquisition}/streaming.py +2 -2
  249. oscura/{acquisition → hardware/acquisition}/synthetic.py +3 -3
  250. oscura/{acquisition → hardware/acquisition}/visa.py +33 -11
  251. oscura/hardware/firmware/__init__.py +29 -0
  252. oscura/hardware/firmware/pattern_recognition.py +874 -0
  253. oscura/hardware/hal_detector.py +736 -0
  254. oscura/hardware/security/__init__.py +37 -0
  255. oscura/hardware/security/side_channel_detector.py +1126 -0
  256. oscura/inference/__init__.py +4 -0
  257. oscura/inference/active_learning/README.md +7 -7
  258. oscura/inference/active_learning/observation_table.py +4 -1
  259. oscura/inference/alignment.py +216 -123
  260. oscura/inference/bayesian.py +113 -33
  261. oscura/inference/crc_reverse.py +101 -55
  262. oscura/inference/logic.py +6 -2
  263. oscura/inference/message_format.py +342 -183
  264. oscura/inference/protocol.py +95 -44
  265. oscura/inference/protocol_dsl.py +180 -82
  266. oscura/inference/signal_intelligence.py +1439 -706
  267. oscura/inference/spectral.py +99 -57
  268. oscura/inference/state_machine.py +810 -158
  269. oscura/inference/stream.py +270 -110
  270. oscura/iot/__init__.py +34 -0
  271. oscura/iot/coap/__init__.py +32 -0
  272. oscura/iot/coap/analyzer.py +668 -0
  273. oscura/iot/coap/options.py +212 -0
  274. oscura/iot/lorawan/__init__.py +21 -0
  275. oscura/iot/lorawan/crypto.py +206 -0
  276. oscura/iot/lorawan/decoder.py +801 -0
  277. oscura/iot/lorawan/mac_commands.py +341 -0
  278. oscura/iot/mqtt/__init__.py +27 -0
  279. oscura/iot/mqtt/analyzer.py +999 -0
  280. oscura/iot/mqtt/properties.py +315 -0
  281. oscura/iot/zigbee/__init__.py +31 -0
  282. oscura/iot/zigbee/analyzer.py +615 -0
  283. oscura/iot/zigbee/security.py +153 -0
  284. oscura/iot/zigbee/zcl.py +349 -0
  285. oscura/jupyter/display.py +125 -45
  286. oscura/{exploratory → jupyter/exploratory}/__init__.py +8 -8
  287. oscura/{exploratory → jupyter/exploratory}/error_recovery.py +298 -141
  288. oscura/jupyter/exploratory/fuzzy.py +746 -0
  289. oscura/{exploratory → jupyter/exploratory}/fuzzy_advanced.py +258 -100
  290. oscura/{exploratory → jupyter/exploratory}/legacy.py +464 -242
  291. oscura/{exploratory → jupyter/exploratory}/parse.py +167 -145
  292. oscura/{exploratory → jupyter/exploratory}/recovery.py +119 -87
  293. oscura/jupyter/exploratory/sync.py +612 -0
  294. oscura/{exploratory → jupyter/exploratory}/unknown.py +299 -176
  295. oscura/jupyter/magic.py +4 -4
  296. oscura/{ui → jupyter/ui}/__init__.py +2 -2
  297. oscura/{ui → jupyter/ui}/formatters.py +3 -3
  298. oscura/{ui → jupyter/ui}/progressive_display.py +153 -82
  299. oscura/loaders/__init__.py +171 -63
  300. oscura/loaders/binary.py +88 -1
  301. oscura/loaders/chipwhisperer.py +153 -137
  302. oscura/loaders/configurable.py +208 -86
  303. oscura/loaders/csv_loader.py +458 -215
  304. oscura/loaders/hdf5_loader.py +278 -119
  305. oscura/loaders/lazy.py +87 -54
  306. oscura/loaders/mmap_loader.py +1 -1
  307. oscura/loaders/numpy_loader.py +253 -116
  308. oscura/loaders/pcap.py +226 -151
  309. oscura/loaders/rigol.py +110 -49
  310. oscura/loaders/sigrok.py +201 -78
  311. oscura/loaders/tdms.py +81 -58
  312. oscura/loaders/tektronix.py +291 -174
  313. oscura/loaders/touchstone.py +182 -87
  314. oscura/loaders/vcd.py +215 -117
  315. oscura/loaders/wav.py +155 -68
  316. oscura/reporting/__init__.py +9 -7
  317. oscura/reporting/analyze.py +352 -146
  318. oscura/reporting/argument_preparer.py +69 -14
  319. oscura/reporting/auto_report.py +97 -61
  320. oscura/reporting/batch.py +131 -58
  321. oscura/reporting/chart_selection.py +57 -45
  322. oscura/reporting/comparison.py +63 -17
  323. oscura/reporting/content/executive.py +76 -24
  324. oscura/reporting/core_formats/multi_format.py +11 -8
  325. oscura/reporting/engine.py +312 -158
  326. oscura/reporting/enhanced_reports.py +949 -0
  327. oscura/reporting/export.py +86 -43
  328. oscura/reporting/formatting/numbers.py +69 -42
  329. oscura/reporting/html.py +139 -58
  330. oscura/reporting/index.py +137 -65
  331. oscura/reporting/output.py +158 -67
  332. oscura/reporting/pdf.py +67 -102
  333. oscura/reporting/plots.py +191 -112
  334. oscura/reporting/sections.py +88 -47
  335. oscura/reporting/standards.py +104 -61
  336. oscura/reporting/summary_generator.py +75 -55
  337. oscura/reporting/tables.py +138 -54
  338. oscura/reporting/templates/enhanced/protocol_re.html +525 -0
  339. oscura/reporting/templates/index.md +13 -13
  340. oscura/sessions/__init__.py +14 -23
  341. oscura/sessions/base.py +3 -3
  342. oscura/sessions/blackbox.py +106 -10
  343. oscura/sessions/generic.py +2 -2
  344. oscura/sessions/legacy.py +783 -0
  345. oscura/side_channel/__init__.py +63 -0
  346. oscura/side_channel/dpa.py +1025 -0
  347. oscura/utils/__init__.py +15 -1
  348. oscura/utils/autodetect.py +1 -5
  349. oscura/utils/bitwise.py +118 -0
  350. oscura/{builders → utils/builders}/__init__.py +1 -1
  351. oscura/{comparison → utils/comparison}/__init__.py +6 -6
  352. oscura/{comparison → utils/comparison}/compare.py +202 -101
  353. oscura/{comparison → utils/comparison}/golden.py +83 -63
  354. oscura/{comparison → utils/comparison}/limits.py +313 -89
  355. oscura/{comparison → utils/comparison}/mask.py +151 -45
  356. oscura/{comparison → utils/comparison}/trace_diff.py +1 -1
  357. oscura/{comparison → utils/comparison}/visualization.py +147 -89
  358. oscura/{component → utils/component}/__init__.py +3 -3
  359. oscura/{component → utils/component}/impedance.py +122 -58
  360. oscura/{component → utils/component}/reactive.py +165 -168
  361. oscura/{component → utils/component}/transmission_line.py +3 -3
  362. oscura/{filtering → utils/filtering}/__init__.py +6 -6
  363. oscura/{filtering → utils/filtering}/base.py +1 -1
  364. oscura/{filtering → utils/filtering}/convenience.py +2 -2
  365. oscura/{filtering → utils/filtering}/design.py +169 -93
  366. oscura/{filtering → utils/filtering}/filters.py +2 -2
  367. oscura/{filtering → utils/filtering}/introspection.py +2 -2
  368. oscura/utils/geometry.py +31 -0
  369. oscura/utils/imports.py +184 -0
  370. oscura/utils/lazy.py +1 -1
  371. oscura/{math → utils/math}/__init__.py +2 -2
  372. oscura/{math → utils/math}/arithmetic.py +114 -48
  373. oscura/{math → utils/math}/interpolation.py +139 -106
  374. oscura/utils/memory.py +129 -66
  375. oscura/utils/memory_advanced.py +92 -9
  376. oscura/utils/memory_extensions.py +10 -8
  377. oscura/{optimization → utils/optimization}/__init__.py +1 -1
  378. oscura/{optimization → utils/optimization}/search.py +2 -2
  379. oscura/utils/performance/__init__.py +58 -0
  380. oscura/utils/performance/caching.py +889 -0
  381. oscura/utils/performance/lsh_clustering.py +333 -0
  382. oscura/utils/performance/memory_optimizer.py +699 -0
  383. oscura/utils/performance/optimizations.py +675 -0
  384. oscura/utils/performance/parallel.py +654 -0
  385. oscura/utils/performance/profiling.py +661 -0
  386. oscura/{pipeline → utils/pipeline}/base.py +1 -1
  387. oscura/{pipeline → utils/pipeline}/composition.py +11 -3
  388. oscura/{pipeline → utils/pipeline}/parallel.py +3 -2
  389. oscura/{pipeline → utils/pipeline}/pipeline.py +1 -1
  390. oscura/{pipeline → utils/pipeline}/reverse_engineering.py +412 -221
  391. oscura/{search → utils/search}/__init__.py +3 -3
  392. oscura/{search → utils/search}/anomaly.py +188 -58
  393. oscura/utils/search/context.py +294 -0
  394. oscura/{search → utils/search}/pattern.py +138 -10
  395. oscura/utils/serial.py +51 -0
  396. oscura/utils/storage/__init__.py +61 -0
  397. oscura/utils/storage/database.py +1166 -0
  398. oscura/{streaming → utils/streaming}/chunked.py +302 -143
  399. oscura/{streaming → utils/streaming}/progressive.py +1 -1
  400. oscura/{streaming → utils/streaming}/realtime.py +3 -2
  401. oscura/{triggering → utils/triggering}/__init__.py +6 -6
  402. oscura/{triggering → utils/triggering}/base.py +6 -6
  403. oscura/{triggering → utils/triggering}/edge.py +2 -2
  404. oscura/{triggering → utils/triggering}/pattern.py +2 -2
  405. oscura/{triggering → utils/triggering}/pulse.py +115 -74
  406. oscura/{triggering → utils/triggering}/window.py +2 -2
  407. oscura/utils/validation.py +32 -0
  408. oscura/validation/__init__.py +121 -0
  409. oscura/{compliance → validation/compliance}/__init__.py +5 -5
  410. oscura/{compliance → validation/compliance}/advanced.py +5 -5
  411. oscura/{compliance → validation/compliance}/masks.py +1 -1
  412. oscura/{compliance → validation/compliance}/reporting.py +127 -53
  413. oscura/{compliance → validation/compliance}/testing.py +114 -52
  414. oscura/validation/compliance_tests.py +915 -0
  415. oscura/validation/fuzzer.py +990 -0
  416. oscura/validation/grammar_tests.py +596 -0
  417. oscura/validation/grammar_validator.py +904 -0
  418. oscura/validation/hil_testing.py +977 -0
  419. oscura/{quality → validation/quality}/__init__.py +4 -4
  420. oscura/{quality → validation/quality}/ensemble.py +251 -171
  421. oscura/{quality → validation/quality}/explainer.py +3 -3
  422. oscura/{quality → validation/quality}/scoring.py +1 -1
  423. oscura/{quality → validation/quality}/warnings.py +4 -4
  424. oscura/validation/regression_suite.py +808 -0
  425. oscura/validation/replay.py +788 -0
  426. oscura/{testing → validation/testing}/__init__.py +2 -2
  427. oscura/{testing → validation/testing}/synthetic.py +5 -5
  428. oscura/visualization/__init__.py +9 -0
  429. oscura/visualization/accessibility.py +1 -1
  430. oscura/visualization/annotations.py +64 -67
  431. oscura/visualization/colors.py +7 -7
  432. oscura/visualization/digital.py +180 -81
  433. oscura/visualization/eye.py +236 -85
  434. oscura/visualization/interactive.py +320 -143
  435. oscura/visualization/jitter.py +587 -247
  436. oscura/visualization/layout.py +169 -134
  437. oscura/visualization/optimization.py +103 -52
  438. oscura/visualization/palettes.py +1 -1
  439. oscura/visualization/power.py +427 -211
  440. oscura/visualization/power_extended.py +626 -297
  441. oscura/visualization/presets.py +2 -0
  442. oscura/visualization/protocols.py +495 -181
  443. oscura/visualization/render.py +79 -63
  444. oscura/visualization/reverse_engineering.py +171 -124
  445. oscura/visualization/signal_integrity.py +460 -279
  446. oscura/visualization/specialized.py +190 -100
  447. oscura/visualization/spectral.py +670 -255
  448. oscura/visualization/thumbnails.py +166 -137
  449. oscura/visualization/waveform.py +150 -63
  450. oscura/workflows/__init__.py +3 -0
  451. oscura/{batch → workflows/batch}/__init__.py +5 -5
  452. oscura/{batch → workflows/batch}/advanced.py +150 -75
  453. oscura/workflows/batch/aggregate.py +531 -0
  454. oscura/workflows/batch/analyze.py +236 -0
  455. oscura/{batch → workflows/batch}/logging.py +2 -2
  456. oscura/{batch → workflows/batch}/metrics.py +1 -1
  457. oscura/workflows/complete_re.py +1144 -0
  458. oscura/workflows/compliance.py +44 -54
  459. oscura/workflows/digital.py +197 -51
  460. oscura/workflows/legacy/__init__.py +12 -0
  461. oscura/{workflow → workflows/legacy}/dag.py +4 -1
  462. oscura/workflows/multi_trace.py +9 -9
  463. oscura/workflows/power.py +42 -62
  464. oscura/workflows/protocol.py +82 -49
  465. oscura/workflows/reverse_engineering.py +351 -150
  466. oscura/workflows/signal_integrity.py +157 -82
  467. oscura-0.6.0.dist-info/METADATA +643 -0
  468. oscura-0.6.0.dist-info/RECORD +590 -0
  469. oscura/analyzers/digital/ic_database.py +0 -498
  470. oscura/analyzers/digital/timing_paths.py +0 -339
  471. oscura/analyzers/digital/vintage.py +0 -377
  472. oscura/analyzers/digital/vintage_result.py +0 -148
  473. oscura/analyzers/protocols/parallel_bus.py +0 -449
  474. oscura/batch/aggregate.py +0 -300
  475. oscura/batch/analyze.py +0 -139
  476. oscura/dsl/__init__.py +0 -73
  477. oscura/exceptions.py +0 -59
  478. oscura/exploratory/fuzzy.py +0 -513
  479. oscura/exploratory/sync.py +0 -384
  480. oscura/export/wavedrom.py +0 -430
  481. oscura/exporters/__init__.py +0 -94
  482. oscura/exporters/csv.py +0 -303
  483. oscura/exporters/exporters.py +0 -44
  484. oscura/exporters/hdf5.py +0 -217
  485. oscura/exporters/html_export.py +0 -701
  486. oscura/exporters/json_export.py +0 -338
  487. oscura/exporters/markdown_export.py +0 -367
  488. oscura/exporters/matlab_export.py +0 -354
  489. oscura/exporters/npz_export.py +0 -219
  490. oscura/exporters/spice_export.py +0 -210
  491. oscura/exporters/vintage_logic_csv.py +0 -247
  492. oscura/reporting/vintage_logic_report.py +0 -523
  493. oscura/search/context.py +0 -149
  494. oscura/session/__init__.py +0 -34
  495. oscura/session/annotations.py +0 -289
  496. oscura/session/history.py +0 -313
  497. oscura/session/session.py +0 -520
  498. oscura/visualization/digital_advanced.py +0 -718
  499. oscura/visualization/figure_manager.py +0 -156
  500. oscura/workflow/__init__.py +0 -13
  501. oscura-0.5.0.dist-info/METADATA +0 -407
  502. oscura-0.5.0.dist-info/RECORD +0 -486
  503. /oscura/core/{config.py → config/legacy.py} +0 -0
  504. /oscura/{extensibility → core/extensibility}/__init__.py +0 -0
  505. /oscura/{extensibility → core/extensibility}/registry.py +0 -0
  506. /oscura/{plugins → core/plugins}/isolation.py +0 -0
  507. /oscura/{builders → utils/builders}/signal_builder.py +0 -0
  508. /oscura/{optimization → utils/optimization}/parallel.py +0 -0
  509. /oscura/{pipeline → utils/pipeline}/__init__.py +0 -0
  510. /oscura/{streaming → utils/streaming}/__init__.py +0 -0
  511. {oscura-0.5.0.dist-info → oscura-0.6.0.dist-info}/WHEEL +0 -0
  512. {oscura-0.5.0.dist-info → oscura-0.6.0.dist-info}/entry_points.txt +0 -0
  513. {oscura-0.5.0.dist-info → oscura-0.6.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,605 @@
1
+ """Entropy analysis and cryptographic data detection.
2
+
3
+ This module provides tools for detecting encrypted, compressed, or random data
4
+ in protocol messages and binary streams using entropy analysis and statistical tests.
5
+
6
+ Key capabilities:
7
+ - Shannon entropy calculation (information content measurement)
8
+ - Chi-squared test for uniform distribution (randomness detection)
9
+ - Sliding window entropy analysis (find encrypted regions)
10
+ - Crypto field detection across multiple messages
11
+ - Compression vs encryption distinction
12
+
13
+ Typical use cases:
14
+ - Identify encrypted payload fields in unknown protocols
15
+ - Detect compression in protocol messages
16
+ - Find random/high-entropy regions in binary data
17
+ - Distinguish structured vs random data
18
+
19
+ Example:
20
+ >>> from oscura.analyzers.entropy import CryptoDetector
21
+ >>> detector = CryptoDetector()
22
+ >>> result = detector.analyze_entropy(data)
23
+ >>> if result.is_high_entropy:
24
+ ... print(f"Likely encrypted: {result.encryption_likelihood:.2%}")
25
+ """
26
+
27
+ from __future__ import annotations
28
+
29
+ import logging
30
+ from dataclasses import dataclass
31
+ from typing import TYPE_CHECKING, Any
32
+
33
+ import numpy as np
34
+ from scipy.stats import chisquare
35
+
36
+ if TYPE_CHECKING:
37
+ from numpy.typing import NDArray
38
+
39
+ logger = logging.getLogger(__name__)
40
+
41
+
42
+ @dataclass
43
+ class EntropyResult:
44
+ """Results from entropy analysis.
45
+
46
+ Attributes:
47
+ shannon_entropy: Shannon entropy in bits per byte (0.0-8.0).
48
+ Higher values indicate more randomness/information content.
49
+ is_high_entropy: True if entropy exceeds threshold for encryption/compression.
50
+ is_random: True if chi-squared test indicates uniform distribution.
51
+ compression_likelihood: Probability data is compressed (0.0-1.0).
52
+ encryption_likelihood: Probability data is encrypted (0.0-1.0).
53
+ confidence: Overall confidence score for classification (0.0-1.0).
54
+ Higher for larger samples with clear characteristics.
55
+ chi_squared_p_value: P-value from chi-squared test (high = random).
56
+
57
+ Example:
58
+ >>> result = detector.analyze_entropy(encrypted_data)
59
+ >>> print(f"Entropy: {result.shannon_entropy:.2f} bits/byte")
60
+ >>> print(f"Encrypted: {result.encryption_likelihood:.2%}")
61
+ >>> print(f"Confidence: {result.confidence:.2%}")
62
+ """
63
+
64
+ shannon_entropy: float
65
+ is_high_entropy: bool
66
+ is_random: bool
67
+ compression_likelihood: float
68
+ encryption_likelihood: float
69
+ confidence: float
70
+ chi_squared_p_value: float
71
+
72
+
73
+ class CryptoDetector:
74
+ """Detect encrypted, compressed, or random data using entropy analysis.
75
+
76
+ This class provides multiple analysis methods for identifying cryptographic
77
+ or compressed data in binary streams. It uses Shannon entropy, chi-squared
78
+ tests, and heuristics to distinguish between different data types.
79
+
80
+ Thresholds:
81
+ ENTROPY_THRESHOLD_ENCRYPTED: 7.5 bits/byte - typical for AES/ChaCha20
82
+ ENTROPY_THRESHOLD_COMPRESSED: 6.5 bits/byte - typical for gzip/zlib
83
+ CHI_SQUARED_ALPHA: 0.05 - significance level for randomness test
84
+
85
+ Example:
86
+ >>> detector = CryptoDetector()
87
+ >>> # Analyze single message
88
+ >>> result = detector.analyze_entropy(message)
89
+ >>> # Find encrypted regions in mixed data
90
+ >>> windows = detector.sliding_window_entropy(mixed_data, window_size=256)
91
+ >>> # Detect fields across multiple messages
92
+ >>> fields = detector.detect_crypto_fields(messages, min_field_size=16)
93
+ """
94
+
95
+ # Entropy thresholds (bits per byte)
96
+ ENTROPY_THRESHOLD_ENCRYPTED = 7.5 # Strong encryption: AES, ChaCha20
97
+ ENTROPY_THRESHOLD_COMPRESSED = 6.5 # Compression: gzip, zlib, deflate
98
+ ENTROPY_THRESHOLD_STRUCTURED = 3.0 # Structured data: text, low-entropy
99
+
100
+ # Statistical test parameters
101
+ CHI_SQUARED_ALPHA = 0.05 # Significance level for randomness test
102
+ MIN_SAMPLE_SIZE = 32 # Minimum bytes for reliable entropy analysis
103
+
104
+ def analyze_entropy(self, data: bytes, window_size: int | None = None) -> EntropyResult:
105
+ """Analyze entropy and randomness characteristics of data.
106
+
107
+ Performs comprehensive entropy analysis including Shannon entropy,
108
+ chi-squared test for randomness, and classification into
109
+ encrypted/compressed/structured categories.
110
+
111
+ Args:
112
+ data: Binary data to analyze.
113
+ window_size: Optional window size for localized analysis.
114
+ If None, analyzes entire data block.
115
+
116
+ Returns:
117
+ EntropyResult with entropy metrics and classification.
118
+
119
+ Raises:
120
+ ValueError: If data is empty or window_size is invalid.
121
+
122
+ Example:
123
+ >>> # Analyze encrypted data
124
+ >>> encrypted = os.urandom(256)
125
+ >>> result = detector.analyze_entropy(encrypted)
126
+ >>> assert result.is_high_entropy
127
+ >>> assert result.encryption_likelihood > 0.9
128
+ >>>
129
+ >>> # Analyze structured data
130
+ >>> text = b"Hello World" * 20
131
+ >>> result = detector.analyze_entropy(text)
132
+ >>> assert not result.is_high_entropy
133
+ >>> assert result.encryption_likelihood < 0.1
134
+ """
135
+ if not data:
136
+ raise ValueError("Cannot analyze empty data")
137
+
138
+ if window_size is not None and window_size < 1:
139
+ raise ValueError(f"window_size must be positive, got {window_size}")
140
+
141
+ # Use full data if no window specified
142
+ if window_size is None or window_size >= len(data):
143
+ analysis_data = data
144
+ else:
145
+ # Take first window for analysis
146
+ analysis_data = data[:window_size]
147
+
148
+ # Calculate Shannon entropy
149
+ shannon_ent = self._shannon_entropy(analysis_data)
150
+
151
+ # Perform chi-squared test for randomness
152
+ chi_p_value = self._chi_squared_test(analysis_data)
153
+ is_random = chi_p_value > self.CHI_SQUARED_ALPHA
154
+
155
+ # Determine if high entropy
156
+ is_high_entropy = shannon_ent > self.ENTROPY_THRESHOLD_ENCRYPTED
157
+
158
+ # Calculate compression vs encryption likelihood
159
+ compression_likelihood = self._estimate_compression_likelihood(
160
+ shannon_ent, is_random, chi_p_value
161
+ )
162
+ encryption_likelihood = self._estimate_encryption_likelihood(
163
+ shannon_ent, is_random, chi_p_value
164
+ )
165
+
166
+ # Calculate confidence based on sample size
167
+ confidence = self._calculate_confidence(len(analysis_data), shannon_ent)
168
+
169
+ logger.debug(
170
+ f"Entropy analysis: {shannon_ent:.2f} bits/byte, "
171
+ f"chi-squared p={chi_p_value:.4f}, "
172
+ f"encrypted={encryption_likelihood:.2%}, "
173
+ f"compressed={compression_likelihood:.2%}"
174
+ )
175
+
176
+ return EntropyResult(
177
+ shannon_entropy=shannon_ent,
178
+ is_high_entropy=is_high_entropy,
179
+ is_random=is_random,
180
+ compression_likelihood=compression_likelihood,
181
+ encryption_likelihood=encryption_likelihood,
182
+ confidence=confidence,
183
+ chi_squared_p_value=chi_p_value,
184
+ )
185
+
186
+ def sliding_window_entropy(
187
+ self, data: bytes, window_size: int = 256, stride: int = 64
188
+ ) -> list[tuple[int, float]]:
189
+ """Compute entropy across sliding windows to find regions of interest.
190
+
191
+ Useful for protocols with mixed plaintext/ciphertext regions:
192
+ - Header: low entropy, structured fields
193
+ - Payload: high entropy, encrypted data
194
+ - Footer: low entropy, checksums/padding
195
+
196
+ Args:
197
+ data: Binary data to analyze.
198
+ window_size: Size of sliding window in bytes. Default 256.
199
+ stride: Step size between windows in bytes. Default 64.
200
+
201
+ Returns:
202
+ List of (offset, entropy) tuples for each window.
203
+
204
+ Raises:
205
+ ValueError: If data is too short, window_size or stride invalid.
206
+
207
+ Example:
208
+ >>> # Analyze message with mixed content
209
+ >>> header = b"PROTOCOL_HEADER"
210
+ >>> payload = os.urandom(200) # Encrypted
211
+ >>> footer = b"END"
212
+ >>> data = header + payload + footer
213
+ >>>
214
+ >>> windows = detector.sliding_window_entropy(data, window_size=64)
215
+ >>> for offset, ent in windows:
216
+ ... if ent > 7.5:
217
+ ... print(f"Encrypted region at offset {offset}")
218
+ """
219
+ if not data:
220
+ raise ValueError("Cannot analyze empty data")
221
+
222
+ if window_size < 1:
223
+ raise ValueError(f"window_size must be positive, got {window_size}")
224
+
225
+ if stride < 1:
226
+ raise ValueError(f"stride must be positive, got {stride}")
227
+
228
+ if len(data) < window_size:
229
+ raise ValueError(f"Data length ({len(data)}) must be >= window_size ({window_size})")
230
+
231
+ results = []
232
+ for offset in range(0, len(data) - window_size + 1, stride):
233
+ window = data[offset : offset + window_size]
234
+ ent = self._shannon_entropy(window)
235
+ results.append((offset, ent))
236
+
237
+ logger.debug(f"Sliding window analysis: {len(results)} windows analyzed")
238
+
239
+ return results
240
+
241
+ def detect_crypto_fields(
242
+ self, messages: list[bytes], min_field_size: int = 8
243
+ ) -> list[dict[str, Any]]:
244
+ """Identify likely encrypted fields by analyzing multiple messages.
245
+
246
+ Strategy:
247
+ 1. Group messages by length (same protocol message type)
248
+ 2. Compute positional entropy (entropy at each byte offset)
249
+ 3. Find consecutive high-entropy regions
250
+ 4. Return field descriptors with offset, length, and characteristics
251
+
252
+ This works because:
253
+ - Plaintext fields vary between messages (low positional entropy)
254
+ - Encrypted fields appear random (high positional entropy)
255
+ - Field boundaries are consistent within a message type
256
+
257
+ Args:
258
+ messages: List of protocol messages to analyze.
259
+ min_field_size: Minimum field size in bytes to report. Default 8.
260
+
261
+ Returns:
262
+ List of detected crypto field descriptors, each containing:
263
+ - offset: Field start offset in bytes
264
+ - length: Field length in bytes
265
+ - type: Classification (e.g., 'encrypted_payload')
266
+ - entropy: Average entropy across the field
267
+ - message_length: Length of messages containing this field
268
+ - sample_count: Number of messages analyzed
269
+
270
+ Raises:
271
+ ValueError: If messages list is empty or min_field_size invalid.
272
+
273
+ Example:
274
+ >>> # Capture multiple protocol messages
275
+ >>> messages = [...] # List of bytes objects
276
+ >>>
277
+ >>> # Detect encrypted fields
278
+ >>> fields = detector.detect_crypto_fields(messages, min_field_size=16)
279
+ >>>
280
+ >>> for field in fields:
281
+ ... print(f"Encrypted field at offset {field['offset']}, "
282
+ ... f"length {field['length']} bytes, "
283
+ ... f"entropy {field['entropy']:.2f}")
284
+ """
285
+ if not messages:
286
+ raise ValueError("Cannot analyze empty message list")
287
+
288
+ if min_field_size < 1:
289
+ raise ValueError(f"min_field_size must be positive, got {min_field_size}")
290
+
291
+ # Group messages by length (same message type)
292
+ length_groups: dict[int, list[bytes]] = {}
293
+ for msg in messages:
294
+ length_groups.setdefault(len(msg), []).append(msg)
295
+
296
+ crypto_fields = []
297
+
298
+ for msg_len, msg_group in length_groups.items():
299
+ if msg_len < min_field_size:
300
+ logger.debug(
301
+ f"Skipping message group with length {msg_len} < "
302
+ f"min_field_size {min_field_size}"
303
+ )
304
+ continue
305
+
306
+ # Compute entropy at each position
307
+ position_entropy = self._compute_positional_entropy(msg_group)
308
+
309
+ # Find high-entropy regions
310
+ fields = self._extract_high_entropy_regions(
311
+ position_entropy,
312
+ msg_len,
313
+ min_field_size,
314
+ len(msg_group),
315
+ )
316
+
317
+ crypto_fields.extend(fields)
318
+
319
+ logger.info(f"Detected {len(crypto_fields)} crypto fields across {len(messages)} messages")
320
+
321
+ return crypto_fields
322
+
323
+ # =========================================================================
324
+ # Internal Helper Methods
325
+ # =========================================================================
326
+
327
+ def _shannon_entropy(self, data: bytes) -> float:
328
+ """Calculate Shannon entropy in bits per byte.
329
+
330
+ Shannon entropy measures the average information content per byte.
331
+ Formula: H = -sum(p(i) * log2(p(i))) for all byte values i
332
+
333
+ Args:
334
+ data: Binary data to analyze.
335
+
336
+ Returns:
337
+ Entropy in bits per byte (0.0 to 8.0).
338
+ 0.0: All bytes identical (no information)
339
+ 8.0: Perfect randomness (maximum information)
340
+
341
+ Example:
342
+ >>> # All zeros - no entropy
343
+ >>> assert detector._shannon_entropy(b"\x00" * 100) == 0.0
344
+ >>> # Random data - high entropy
345
+ >>> random_data = os.urandom(1000)
346
+ >>> assert detector._shannon_entropy(random_data) > 7.5
347
+ """
348
+ if not data:
349
+ return 0.0
350
+
351
+ # Count byte frequencies
352
+ byte_array = np.frombuffer(data, dtype=np.uint8)
353
+ byte_counts = np.bincount(byte_array, minlength=256)
354
+
355
+ # Calculate probabilities (exclude zero counts)
356
+ probabilities = byte_counts[byte_counts > 0] / len(data)
357
+
358
+ # Shannon entropy: -sum(p * log2(p))
359
+ entropy = float(-np.sum(probabilities * np.log2(probabilities)))
360
+
361
+ return entropy
362
+
363
+ def _chi_squared_test(self, data: bytes) -> float:
364
+ """Perform chi-squared test for uniform distribution.
365
+
366
+ Tests the null hypothesis: data is uniformly distributed (random).
367
+
368
+ Args:
369
+ data: Binary data to test.
370
+
371
+ Returns:
372
+ P-value from chi-squared test.
373
+ High p-value (>0.05): Data is likely random/uniform
374
+ Low p-value (<0.05): Data has structure/patterns
375
+
376
+ Example:
377
+ >>> # Random data passes test (high p-value)
378
+ >>> random_data = os.urandom(1000)
379
+ >>> p_value = detector._chi_squared_test(random_data)
380
+ >>> assert p_value > 0.05
381
+ >>>
382
+ >>> # Structured data fails test (low p-value)
383
+ >>> text = b"AAAA" * 100
384
+ >>> p_value = detector._chi_squared_test(text)
385
+ >>> assert p_value < 0.05
386
+ """
387
+ if not data:
388
+ return 0.0
389
+
390
+ # Count observed byte frequencies
391
+ byte_array = np.frombuffer(data, dtype=np.uint8)
392
+ observed = np.bincount(byte_array, minlength=256)
393
+
394
+ # Expected frequencies for uniform distribution
395
+ expected = np.full(256, len(data) / 256.0)
396
+
397
+ # Chi-squared test
398
+ _, p_value = chisquare(observed, expected)
399
+
400
+ return float(p_value)
401
+
402
+ def _estimate_compression_likelihood(
403
+ self, entropy: float, is_random: bool, chi_p_value: float
404
+ ) -> float:
405
+ """Estimate likelihood that data is compressed.
406
+
407
+ Compressed data characteristics:
408
+ - Medium-high entropy (6.5-7.5 bits/byte)
409
+ - Less uniformly random than encryption
410
+ - Some residual structure from compression algorithm
411
+
412
+ Args:
413
+ entropy: Shannon entropy in bits/byte.
414
+ is_random: Result of chi-squared test.
415
+ chi_p_value: P-value from chi-squared test.
416
+
417
+ Returns:
418
+ Compression likelihood (0.0-1.0).
419
+ """
420
+ if entropy < self.ENTROPY_THRESHOLD_COMPRESSED:
421
+ # Too low entropy for compression
422
+ return 0.0
423
+
424
+ if entropy > self.ENTROPY_THRESHOLD_ENCRYPTED:
425
+ # Too high entropy - likely encryption, not compression
426
+ # Compression rarely achieves >7.5 bits/byte
427
+ return max(0.0, 1.0 - (entropy - self.ENTROPY_THRESHOLD_ENCRYPTED) * 2.0)
428
+
429
+ # Medium entropy range - likely compression
430
+ # Peak likelihood at 7.0 bits/byte
431
+ compression_score = (entropy - self.ENTROPY_THRESHOLD_COMPRESSED) / (
432
+ self.ENTROPY_THRESHOLD_ENCRYPTED - self.ENTROPY_THRESHOLD_COMPRESSED
433
+ )
434
+
435
+ # Reduce likelihood if data is too uniformly random
436
+ # (compression has some structure)
437
+ if is_random and chi_p_value > 0.5:
438
+ compression_score *= 0.5
439
+
440
+ return min(1.0, max(0.0, compression_score))
441
+
442
+ def _estimate_encryption_likelihood(
443
+ self, entropy: float, is_random: bool, chi_p_value: float
444
+ ) -> float:
445
+ """Estimate likelihood that data is encrypted.
446
+
447
+ Encrypted data characteristics:
448
+ - Very high entropy (>7.5 bits/byte)
449
+ - Uniformly random distribution
450
+ - High chi-squared p-value
451
+
452
+ Args:
453
+ entropy: Shannon entropy in bits/byte.
454
+ is_random: Result of chi-squared test.
455
+ chi_p_value: P-value from chi-squared test.
456
+
457
+ Returns:
458
+ Encryption likelihood (0.0-1.0).
459
+ """
460
+ if entropy < self.ENTROPY_THRESHOLD_COMPRESSED:
461
+ # Too low for encryption
462
+ return 0.0
463
+
464
+ # Base score from entropy
465
+ if entropy >= self.ENTROPY_THRESHOLD_ENCRYPTED:
466
+ entropy_score = min(1.0, (entropy - 7.0) / 1.0) # Scale 7.0-8.0 to 0-1
467
+ else:
468
+ # Partial score for medium entropy
469
+ entropy_score = (entropy - self.ENTROPY_THRESHOLD_COMPRESSED) / (
470
+ self.ENTROPY_THRESHOLD_ENCRYPTED - self.ENTROPY_THRESHOLD_COMPRESSED
471
+ )
472
+ entropy_score *= 0.5 # Reduce confidence
473
+
474
+ # Boost score if uniformly random
475
+ if is_random:
476
+ # Very uniform = likely encryption
477
+ randomness_boost = min(0.5, chi_p_value)
478
+ encryption_score = min(1.0, entropy_score + randomness_boost)
479
+ else:
480
+ # Not uniform = less likely encryption
481
+ encryption_score = entropy_score * 0.7
482
+
483
+ return min(1.0, max(0.0, encryption_score))
484
+
485
+ def _calculate_confidence(self, sample_size: int, entropy: float) -> float:
486
+ """Calculate confidence score based on sample size and entropy clarity.
487
+
488
+ Args:
489
+ sample_size: Number of bytes analyzed.
490
+ entropy: Shannon entropy value.
491
+
492
+ Returns:
493
+ Confidence score (0.0-1.0).
494
+ """
495
+ # Sample size confidence
496
+ if sample_size < self.MIN_SAMPLE_SIZE:
497
+ size_confidence = sample_size / self.MIN_SAMPLE_SIZE
498
+ elif sample_size >= 256:
499
+ size_confidence = 1.0
500
+ else:
501
+ size_confidence = (
502
+ 0.5 + (sample_size - self.MIN_SAMPLE_SIZE) / (256 - self.MIN_SAMPLE_SIZE) * 0.5
503
+ )
504
+
505
+ # Entropy clarity (distance from thresholds)
506
+ if entropy < self.ENTROPY_THRESHOLD_STRUCTURED:
507
+ # Clearly structured
508
+ clarity = 1.0
509
+ elif entropy > self.ENTROPY_THRESHOLD_ENCRYPTED:
510
+ # Clearly encrypted
511
+ clarity = 1.0
512
+ elif self.ENTROPY_THRESHOLD_COMPRESSED < entropy < self.ENTROPY_THRESHOLD_ENCRYPTED:
513
+ # Ambiguous range (compression vs encryption)
514
+ clarity = 0.6
515
+ else:
516
+ # Between structured and compressed
517
+ clarity = 0.8
518
+
519
+ return size_confidence * clarity
520
+
521
+ def _compute_positional_entropy(self, messages: list[bytes]) -> NDArray[np.float64]:
522
+ """Compute entropy at each byte position across messages.
523
+
524
+ Args:
525
+ messages: List of messages (all same length).
526
+
527
+ Returns:
528
+ Array of entropy values, one per byte position.
529
+ """
530
+ msg_len = len(messages[0])
531
+ position_entropy = np.zeros(msg_len, dtype=np.float64)
532
+
533
+ for pos in range(msg_len):
534
+ # Extract bytes at this position from all messages
535
+ position_bytes = bytes([msg[pos] for msg in messages])
536
+ position_entropy[pos] = self._shannon_entropy(position_bytes)
537
+
538
+ return position_entropy
539
+
540
+ def _extract_high_entropy_regions(
541
+ self,
542
+ position_entropy: NDArray[np.float64],
543
+ msg_len: int,
544
+ min_field_size: int,
545
+ sample_count: int,
546
+ ) -> list[dict[str, Any]]:
547
+ """Extract contiguous high-entropy regions as crypto field candidates.
548
+
549
+ Args:
550
+ position_entropy: Entropy at each position.
551
+ msg_len: Message length in bytes.
552
+ min_field_size: Minimum field size to report.
553
+ sample_count: Number of messages analyzed.
554
+
555
+ Returns:
556
+ List of field descriptors.
557
+ """
558
+ fields = []
559
+ in_crypto = False
560
+ start = 0
561
+
562
+ # Use a lower threshold for positional entropy since we're analyzing
563
+ # entropy across limited samples (e.g., 20 messages = 20 bytes per position)
564
+ # Maximum positional entropy with N samples is log2(min(N, 256))
565
+ max_positional_entropy = min(np.log2(sample_count), 8.0) if sample_count > 1 else 0.0
566
+ # Use 70% of max as threshold for high positional entropy
567
+ positional_threshold = max_positional_entropy * 0.7
568
+
569
+ for pos in range(msg_len):
570
+ if position_entropy[pos] > positional_threshold:
571
+ if not in_crypto:
572
+ start = pos
573
+ in_crypto = True
574
+ else:
575
+ if in_crypto and (pos - start) >= min_field_size:
576
+ # Found a crypto field
577
+ fields.append(
578
+ {
579
+ "offset": start,
580
+ "length": pos - start,
581
+ "type": "encrypted_payload",
582
+ "entropy": float(np.mean(position_entropy[start:pos])),
583
+ "message_length": msg_len,
584
+ "sample_count": sample_count,
585
+ }
586
+ )
587
+ in_crypto = False
588
+
589
+ # Handle field at end of message
590
+ if in_crypto and (msg_len - start) >= min_field_size:
591
+ fields.append(
592
+ {
593
+ "offset": start,
594
+ "length": msg_len - start,
595
+ "type": "encrypted_payload",
596
+ "entropy": float(np.mean(position_entropy[start:])),
597
+ "message_length": msg_len,
598
+ "sample_count": sample_count,
599
+ }
600
+ )
601
+
602
+ return fields
603
+
604
+
605
+ __all__ = ["CryptoDetector", "EntropyResult"]