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.
Files changed (497) 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/edges.py +325 -65
  5. oscura/analyzers/digital/quality.py +293 -166
  6. oscura/analyzers/digital/timing.py +260 -115
  7. oscura/analyzers/digital/timing_numba.py +334 -0
  8. oscura/analyzers/entropy.py +605 -0
  9. oscura/analyzers/eye/diagram.py +176 -109
  10. oscura/analyzers/eye/metrics.py +5 -5
  11. oscura/analyzers/jitter/__init__.py +6 -4
  12. oscura/analyzers/jitter/ber.py +52 -52
  13. oscura/analyzers/jitter/classification.py +156 -0
  14. oscura/analyzers/jitter/decomposition.py +163 -113
  15. oscura/analyzers/jitter/spectrum.py +80 -64
  16. oscura/analyzers/ml/__init__.py +39 -0
  17. oscura/analyzers/ml/features.py +600 -0
  18. oscura/analyzers/ml/signal_classifier.py +604 -0
  19. oscura/analyzers/packet/daq.py +246 -158
  20. oscura/analyzers/packet/parser.py +12 -1
  21. oscura/analyzers/packet/payload.py +50 -2110
  22. oscura/analyzers/packet/payload_analysis.py +361 -181
  23. oscura/analyzers/packet/payload_patterns.py +133 -70
  24. oscura/analyzers/packet/stream.py +84 -23
  25. oscura/analyzers/patterns/__init__.py +26 -5
  26. oscura/analyzers/patterns/anomaly_detection.py +908 -0
  27. oscura/analyzers/patterns/clustering.py +169 -108
  28. oscura/analyzers/patterns/clustering_optimized.py +227 -0
  29. oscura/analyzers/patterns/discovery.py +1 -1
  30. oscura/analyzers/patterns/matching.py +581 -197
  31. oscura/analyzers/patterns/pattern_mining.py +778 -0
  32. oscura/analyzers/patterns/periodic.py +121 -38
  33. oscura/analyzers/patterns/sequences.py +175 -78
  34. oscura/analyzers/power/conduction.py +1 -1
  35. oscura/analyzers/power/soa.py +6 -6
  36. oscura/analyzers/power/switching.py +250 -110
  37. oscura/analyzers/protocol/__init__.py +17 -1
  38. oscura/analyzers/protocols/base.py +6 -6
  39. oscura/analyzers/protocols/ble/__init__.py +38 -0
  40. oscura/analyzers/protocols/ble/analyzer.py +809 -0
  41. oscura/analyzers/protocols/ble/uuids.py +288 -0
  42. oscura/analyzers/protocols/can.py +257 -127
  43. oscura/analyzers/protocols/can_fd.py +107 -80
  44. oscura/analyzers/protocols/flexray.py +139 -80
  45. oscura/analyzers/protocols/hdlc.py +93 -58
  46. oscura/analyzers/protocols/i2c.py +247 -106
  47. oscura/analyzers/protocols/i2s.py +138 -86
  48. oscura/analyzers/protocols/industrial/__init__.py +40 -0
  49. oscura/analyzers/protocols/industrial/bacnet/__init__.py +33 -0
  50. oscura/analyzers/protocols/industrial/bacnet/analyzer.py +708 -0
  51. oscura/analyzers/protocols/industrial/bacnet/encoding.py +412 -0
  52. oscura/analyzers/protocols/industrial/bacnet/services.py +622 -0
  53. oscura/analyzers/protocols/industrial/ethercat/__init__.py +30 -0
  54. oscura/analyzers/protocols/industrial/ethercat/analyzer.py +474 -0
  55. oscura/analyzers/protocols/industrial/ethercat/mailbox.py +339 -0
  56. oscura/analyzers/protocols/industrial/ethercat/topology.py +166 -0
  57. oscura/analyzers/protocols/industrial/modbus/__init__.py +31 -0
  58. oscura/analyzers/protocols/industrial/modbus/analyzer.py +525 -0
  59. oscura/analyzers/protocols/industrial/modbus/crc.py +79 -0
  60. oscura/analyzers/protocols/industrial/modbus/functions.py +436 -0
  61. oscura/analyzers/protocols/industrial/opcua/__init__.py +21 -0
  62. oscura/analyzers/protocols/industrial/opcua/analyzer.py +552 -0
  63. oscura/analyzers/protocols/industrial/opcua/datatypes.py +446 -0
  64. oscura/analyzers/protocols/industrial/opcua/services.py +264 -0
  65. oscura/analyzers/protocols/industrial/profinet/__init__.py +23 -0
  66. oscura/analyzers/protocols/industrial/profinet/analyzer.py +441 -0
  67. oscura/analyzers/protocols/industrial/profinet/dcp.py +263 -0
  68. oscura/analyzers/protocols/industrial/profinet/ptcp.py +200 -0
  69. oscura/analyzers/protocols/jtag.py +180 -98
  70. oscura/analyzers/protocols/lin.py +219 -114
  71. oscura/analyzers/protocols/manchester.py +4 -4
  72. oscura/analyzers/protocols/onewire.py +253 -149
  73. oscura/analyzers/protocols/parallel_bus/__init__.py +20 -0
  74. oscura/analyzers/protocols/parallel_bus/centronics.py +92 -0
  75. oscura/analyzers/protocols/parallel_bus/gpib.py +137 -0
  76. oscura/analyzers/protocols/spi.py +192 -95
  77. oscura/analyzers/protocols/swd.py +321 -167
  78. oscura/analyzers/protocols/uart.py +267 -125
  79. oscura/analyzers/protocols/usb.py +235 -131
  80. oscura/analyzers/side_channel/power.py +17 -12
  81. oscura/analyzers/signal/__init__.py +15 -0
  82. oscura/analyzers/signal/timing_analysis.py +1086 -0
  83. oscura/analyzers/signal_integrity/__init__.py +4 -1
  84. oscura/analyzers/signal_integrity/sparams.py +2 -19
  85. oscura/analyzers/spectral/chunked.py +129 -60
  86. oscura/analyzers/spectral/chunked_fft.py +300 -94
  87. oscura/analyzers/spectral/chunked_wavelet.py +100 -80
  88. oscura/analyzers/statistical/checksum.py +376 -217
  89. oscura/analyzers/statistical/classification.py +229 -107
  90. oscura/analyzers/statistical/entropy.py +78 -53
  91. oscura/analyzers/statistics/correlation.py +407 -211
  92. oscura/analyzers/statistics/outliers.py +2 -2
  93. oscura/analyzers/statistics/streaming.py +30 -5
  94. oscura/analyzers/validation.py +216 -101
  95. oscura/analyzers/waveform/measurements.py +9 -0
  96. oscura/analyzers/waveform/measurements_with_uncertainty.py +31 -15
  97. oscura/analyzers/waveform/spectral.py +500 -228
  98. oscura/api/__init__.py +31 -5
  99. oscura/api/dsl/__init__.py +582 -0
  100. oscura/{dsl → api/dsl}/commands.py +43 -76
  101. oscura/{dsl → api/dsl}/interpreter.py +26 -51
  102. oscura/{dsl → api/dsl}/parser.py +107 -77
  103. oscura/{dsl → api/dsl}/repl.py +2 -2
  104. oscura/api/dsl.py +1 -1
  105. oscura/{integrations → api/integrations}/__init__.py +1 -1
  106. oscura/{integrations → api/integrations}/llm.py +201 -102
  107. oscura/api/operators.py +3 -3
  108. oscura/api/optimization.py +144 -30
  109. oscura/api/rest_server.py +921 -0
  110. oscura/api/server/__init__.py +17 -0
  111. oscura/api/server/dashboard.py +850 -0
  112. oscura/api/server/static/README.md +34 -0
  113. oscura/api/server/templates/base.html +181 -0
  114. oscura/api/server/templates/export.html +120 -0
  115. oscura/api/server/templates/home.html +284 -0
  116. oscura/api/server/templates/protocols.html +58 -0
  117. oscura/api/server/templates/reports.html +43 -0
  118. oscura/api/server/templates/session_detail.html +89 -0
  119. oscura/api/server/templates/sessions.html +83 -0
  120. oscura/api/server/templates/waveforms.html +73 -0
  121. oscura/automotive/__init__.py +8 -1
  122. oscura/automotive/can/__init__.py +10 -0
  123. oscura/automotive/can/checksum.py +3 -1
  124. oscura/automotive/can/dbc_generator.py +590 -0
  125. oscura/automotive/can/message_wrapper.py +121 -74
  126. oscura/automotive/can/patterns.py +98 -21
  127. oscura/automotive/can/session.py +292 -56
  128. oscura/automotive/can/state_machine.py +6 -3
  129. oscura/automotive/can/stimulus_response.py +97 -75
  130. oscura/automotive/dbc/__init__.py +10 -2
  131. oscura/automotive/dbc/generator.py +84 -56
  132. oscura/automotive/dbc/parser.py +6 -6
  133. oscura/automotive/dtc/data.json +17 -102
  134. oscura/automotive/dtc/database.py +2 -2
  135. oscura/automotive/flexray/__init__.py +31 -0
  136. oscura/automotive/flexray/analyzer.py +504 -0
  137. oscura/automotive/flexray/crc.py +185 -0
  138. oscura/automotive/flexray/fibex.py +449 -0
  139. oscura/automotive/j1939/__init__.py +45 -8
  140. oscura/automotive/j1939/analyzer.py +605 -0
  141. oscura/automotive/j1939/spns.py +326 -0
  142. oscura/automotive/j1939/transport.py +306 -0
  143. oscura/automotive/lin/__init__.py +47 -0
  144. oscura/automotive/lin/analyzer.py +612 -0
  145. oscura/automotive/loaders/blf.py +13 -2
  146. oscura/automotive/loaders/csv_can.py +143 -72
  147. oscura/automotive/loaders/dispatcher.py +50 -2
  148. oscura/automotive/loaders/mdf.py +86 -45
  149. oscura/automotive/loaders/pcap.py +111 -61
  150. oscura/automotive/uds/__init__.py +4 -0
  151. oscura/automotive/uds/analyzer.py +725 -0
  152. oscura/automotive/uds/decoder.py +140 -58
  153. oscura/automotive/uds/models.py +7 -1
  154. oscura/automotive/visualization.py +1 -1
  155. oscura/cli/analyze.py +348 -0
  156. oscura/cli/batch.py +142 -122
  157. oscura/cli/benchmark.py +275 -0
  158. oscura/cli/characterize.py +137 -82
  159. oscura/cli/compare.py +224 -131
  160. oscura/cli/completion.py +250 -0
  161. oscura/cli/config_cmd.py +361 -0
  162. oscura/cli/decode.py +164 -87
  163. oscura/cli/export.py +286 -0
  164. oscura/cli/main.py +115 -31
  165. oscura/{onboarding → cli/onboarding}/__init__.py +3 -3
  166. oscura/{onboarding → cli/onboarding}/help.py +80 -58
  167. oscura/{onboarding → cli/onboarding}/tutorials.py +97 -72
  168. oscura/{onboarding → cli/onboarding}/wizard.py +55 -36
  169. oscura/cli/progress.py +147 -0
  170. oscura/cli/shell.py +157 -135
  171. oscura/cli/validate_cmd.py +204 -0
  172. oscura/cli/visualize.py +158 -0
  173. oscura/convenience.py +125 -79
  174. oscura/core/__init__.py +4 -2
  175. oscura/core/backend_selector.py +3 -3
  176. oscura/core/cache.py +126 -15
  177. oscura/core/cancellation.py +1 -1
  178. oscura/{config → core/config}/__init__.py +20 -11
  179. oscura/{config → core/config}/defaults.py +1 -1
  180. oscura/{config → core/config}/loader.py +7 -5
  181. oscura/{config → core/config}/memory.py +5 -5
  182. oscura/{config → core/config}/migration.py +1 -1
  183. oscura/{config → core/config}/pipeline.py +99 -23
  184. oscura/{config → core/config}/preferences.py +1 -1
  185. oscura/{config → core/config}/protocol.py +3 -3
  186. oscura/{config → core/config}/schema.py +426 -272
  187. oscura/{config → core/config}/settings.py +1 -1
  188. oscura/{config → core/config}/thresholds.py +195 -153
  189. oscura/core/correlation.py +5 -6
  190. oscura/core/cross_domain.py +0 -2
  191. oscura/core/debug.py +9 -5
  192. oscura/{extensibility → core/extensibility}/docs.py +158 -70
  193. oscura/{extensibility → core/extensibility}/extensions.py +160 -76
  194. oscura/{extensibility → core/extensibility}/logging.py +1 -1
  195. oscura/{extensibility → core/extensibility}/measurements.py +1 -1
  196. oscura/{extensibility → core/extensibility}/plugins.py +1 -1
  197. oscura/{extensibility → core/extensibility}/templates.py +73 -3
  198. oscura/{extensibility → core/extensibility}/validation.py +1 -1
  199. oscura/core/gpu_backend.py +11 -7
  200. oscura/core/log_query.py +101 -11
  201. oscura/core/logging.py +126 -54
  202. oscura/core/logging_advanced.py +5 -5
  203. oscura/core/memory_limits.py +108 -70
  204. oscura/core/memory_monitor.py +2 -2
  205. oscura/core/memory_progress.py +7 -7
  206. oscura/core/memory_warnings.py +1 -1
  207. oscura/core/numba_backend.py +13 -13
  208. oscura/{plugins → core/plugins}/__init__.py +9 -9
  209. oscura/{plugins → core/plugins}/base.py +7 -7
  210. oscura/{plugins → core/plugins}/cli.py +3 -3
  211. oscura/{plugins → core/plugins}/discovery.py +186 -106
  212. oscura/{plugins → core/plugins}/lifecycle.py +1 -1
  213. oscura/{plugins → core/plugins}/manager.py +7 -7
  214. oscura/{plugins → core/plugins}/registry.py +3 -3
  215. oscura/{plugins → core/plugins}/versioning.py +1 -1
  216. oscura/core/progress.py +16 -1
  217. oscura/core/provenance.py +8 -2
  218. oscura/{schemas → core/schemas}/__init__.py +2 -2
  219. oscura/{schemas → core/schemas}/device_mapping.json +2 -8
  220. oscura/{schemas → core/schemas}/packet_format.json +4 -24
  221. oscura/{schemas → core/schemas}/protocol_definition.json +2 -12
  222. oscura/core/types.py +4 -0
  223. oscura/core/uncertainty.py +3 -3
  224. oscura/correlation/__init__.py +52 -0
  225. oscura/correlation/multi_protocol.py +811 -0
  226. oscura/discovery/auto_decoder.py +117 -35
  227. oscura/discovery/comparison.py +191 -86
  228. oscura/discovery/quality_validator.py +155 -68
  229. oscura/discovery/signal_detector.py +196 -79
  230. oscura/export/__init__.py +18 -8
  231. oscura/export/kaitai_struct.py +513 -0
  232. oscura/export/scapy_layer.py +801 -0
  233. oscura/export/wireshark/generator.py +1 -1
  234. oscura/export/wireshark/templates/dissector.lua.j2 +2 -2
  235. oscura/export/wireshark_dissector.py +746 -0
  236. oscura/guidance/wizard.py +207 -111
  237. oscura/hardware/__init__.py +19 -0
  238. oscura/{acquisition → hardware/acquisition}/__init__.py +4 -4
  239. oscura/{acquisition → hardware/acquisition}/file.py +2 -2
  240. oscura/{acquisition → hardware/acquisition}/hardware.py +7 -7
  241. oscura/{acquisition → hardware/acquisition}/saleae.py +15 -12
  242. oscura/{acquisition → hardware/acquisition}/socketcan.py +1 -1
  243. oscura/{acquisition → hardware/acquisition}/streaming.py +2 -2
  244. oscura/{acquisition → hardware/acquisition}/synthetic.py +3 -3
  245. oscura/{acquisition → hardware/acquisition}/visa.py +33 -11
  246. oscura/hardware/firmware/__init__.py +29 -0
  247. oscura/hardware/firmware/pattern_recognition.py +874 -0
  248. oscura/hardware/hal_detector.py +736 -0
  249. oscura/hardware/security/__init__.py +37 -0
  250. oscura/hardware/security/side_channel_detector.py +1126 -0
  251. oscura/inference/__init__.py +4 -0
  252. oscura/inference/active_learning/observation_table.py +4 -1
  253. oscura/inference/alignment.py +216 -123
  254. oscura/inference/bayesian.py +113 -33
  255. oscura/inference/crc_reverse.py +101 -55
  256. oscura/inference/logic.py +6 -2
  257. oscura/inference/message_format.py +342 -183
  258. oscura/inference/protocol.py +95 -44
  259. oscura/inference/protocol_dsl.py +180 -82
  260. oscura/inference/signal_intelligence.py +1439 -706
  261. oscura/inference/spectral.py +99 -57
  262. oscura/inference/state_machine.py +810 -158
  263. oscura/inference/stream.py +270 -110
  264. oscura/iot/__init__.py +34 -0
  265. oscura/iot/coap/__init__.py +32 -0
  266. oscura/iot/coap/analyzer.py +668 -0
  267. oscura/iot/coap/options.py +212 -0
  268. oscura/iot/lorawan/__init__.py +21 -0
  269. oscura/iot/lorawan/crypto.py +206 -0
  270. oscura/iot/lorawan/decoder.py +801 -0
  271. oscura/iot/lorawan/mac_commands.py +341 -0
  272. oscura/iot/mqtt/__init__.py +27 -0
  273. oscura/iot/mqtt/analyzer.py +999 -0
  274. oscura/iot/mqtt/properties.py +315 -0
  275. oscura/iot/zigbee/__init__.py +31 -0
  276. oscura/iot/zigbee/analyzer.py +615 -0
  277. oscura/iot/zigbee/security.py +153 -0
  278. oscura/iot/zigbee/zcl.py +349 -0
  279. oscura/jupyter/display.py +125 -45
  280. oscura/{exploratory → jupyter/exploratory}/__init__.py +8 -8
  281. oscura/{exploratory → jupyter/exploratory}/error_recovery.py +298 -141
  282. oscura/jupyter/exploratory/fuzzy.py +746 -0
  283. oscura/{exploratory → jupyter/exploratory}/fuzzy_advanced.py +258 -100
  284. oscura/{exploratory → jupyter/exploratory}/legacy.py +464 -242
  285. oscura/{exploratory → jupyter/exploratory}/parse.py +167 -145
  286. oscura/{exploratory → jupyter/exploratory}/recovery.py +119 -87
  287. oscura/jupyter/exploratory/sync.py +612 -0
  288. oscura/{exploratory → jupyter/exploratory}/unknown.py +299 -176
  289. oscura/jupyter/magic.py +4 -4
  290. oscura/{ui → jupyter/ui}/__init__.py +2 -2
  291. oscura/{ui → jupyter/ui}/formatters.py +3 -3
  292. oscura/{ui → jupyter/ui}/progressive_display.py +153 -82
  293. oscura/loaders/__init__.py +183 -67
  294. oscura/loaders/binary.py +88 -1
  295. oscura/loaders/chipwhisperer.py +153 -137
  296. oscura/loaders/configurable.py +208 -86
  297. oscura/loaders/csv_loader.py +458 -215
  298. oscura/loaders/hdf5_loader.py +278 -119
  299. oscura/loaders/lazy.py +87 -54
  300. oscura/loaders/mmap_loader.py +1 -1
  301. oscura/loaders/numpy_loader.py +253 -116
  302. oscura/loaders/pcap.py +226 -151
  303. oscura/loaders/rigol.py +110 -49
  304. oscura/loaders/sigrok.py +201 -78
  305. oscura/loaders/tdms.py +81 -58
  306. oscura/loaders/tektronix.py +291 -174
  307. oscura/loaders/touchstone.py +182 -87
  308. oscura/loaders/tss.py +456 -0
  309. oscura/loaders/vcd.py +215 -117
  310. oscura/loaders/wav.py +155 -68
  311. oscura/reporting/__init__.py +9 -0
  312. oscura/reporting/analyze.py +352 -146
  313. oscura/reporting/argument_preparer.py +69 -14
  314. oscura/reporting/auto_report.py +97 -61
  315. oscura/reporting/batch.py +131 -58
  316. oscura/reporting/chart_selection.py +57 -45
  317. oscura/reporting/comparison.py +63 -17
  318. oscura/reporting/content/executive.py +76 -24
  319. oscura/reporting/core_formats/multi_format.py +11 -8
  320. oscura/reporting/engine.py +312 -158
  321. oscura/reporting/enhanced_reports.py +949 -0
  322. oscura/reporting/export.py +86 -43
  323. oscura/reporting/formatting/numbers.py +69 -42
  324. oscura/reporting/html.py +139 -58
  325. oscura/reporting/index.py +137 -65
  326. oscura/reporting/output.py +158 -67
  327. oscura/reporting/pdf.py +67 -102
  328. oscura/reporting/plots.py +191 -112
  329. oscura/reporting/sections.py +88 -47
  330. oscura/reporting/standards.py +104 -61
  331. oscura/reporting/summary_generator.py +75 -55
  332. oscura/reporting/tables.py +138 -54
  333. oscura/reporting/templates/enhanced/protocol_re.html +525 -0
  334. oscura/sessions/__init__.py +14 -23
  335. oscura/sessions/base.py +3 -3
  336. oscura/sessions/blackbox.py +106 -10
  337. oscura/sessions/generic.py +2 -2
  338. oscura/sessions/legacy.py +783 -0
  339. oscura/side_channel/__init__.py +63 -0
  340. oscura/side_channel/dpa.py +1025 -0
  341. oscura/utils/__init__.py +15 -1
  342. oscura/utils/bitwise.py +118 -0
  343. oscura/{builders → utils/builders}/__init__.py +1 -1
  344. oscura/{comparison → utils/comparison}/__init__.py +6 -6
  345. oscura/{comparison → utils/comparison}/compare.py +202 -101
  346. oscura/{comparison → utils/comparison}/golden.py +83 -63
  347. oscura/{comparison → utils/comparison}/limits.py +313 -89
  348. oscura/{comparison → utils/comparison}/mask.py +151 -45
  349. oscura/{comparison → utils/comparison}/trace_diff.py +1 -1
  350. oscura/{comparison → utils/comparison}/visualization.py +147 -89
  351. oscura/{component → utils/component}/__init__.py +3 -3
  352. oscura/{component → utils/component}/impedance.py +122 -58
  353. oscura/{component → utils/component}/reactive.py +165 -168
  354. oscura/{component → utils/component}/transmission_line.py +3 -3
  355. oscura/{filtering → utils/filtering}/__init__.py +6 -6
  356. oscura/{filtering → utils/filtering}/base.py +1 -1
  357. oscura/{filtering → utils/filtering}/convenience.py +2 -2
  358. oscura/{filtering → utils/filtering}/design.py +169 -93
  359. oscura/{filtering → utils/filtering}/filters.py +2 -2
  360. oscura/{filtering → utils/filtering}/introspection.py +2 -2
  361. oscura/utils/geometry.py +31 -0
  362. oscura/utils/imports.py +184 -0
  363. oscura/utils/lazy.py +1 -1
  364. oscura/{math → utils/math}/__init__.py +2 -2
  365. oscura/{math → utils/math}/arithmetic.py +114 -48
  366. oscura/{math → utils/math}/interpolation.py +139 -106
  367. oscura/utils/memory.py +129 -66
  368. oscura/utils/memory_advanced.py +92 -9
  369. oscura/utils/memory_extensions.py +10 -8
  370. oscura/{optimization → utils/optimization}/__init__.py +1 -1
  371. oscura/{optimization → utils/optimization}/search.py +2 -2
  372. oscura/utils/performance/__init__.py +58 -0
  373. oscura/utils/performance/caching.py +889 -0
  374. oscura/utils/performance/lsh_clustering.py +333 -0
  375. oscura/utils/performance/memory_optimizer.py +699 -0
  376. oscura/utils/performance/optimizations.py +675 -0
  377. oscura/utils/performance/parallel.py +654 -0
  378. oscura/utils/performance/profiling.py +661 -0
  379. oscura/{pipeline → utils/pipeline}/base.py +1 -1
  380. oscura/{pipeline → utils/pipeline}/composition.py +1 -1
  381. oscura/{pipeline → utils/pipeline}/parallel.py +3 -2
  382. oscura/{pipeline → utils/pipeline}/pipeline.py +1 -1
  383. oscura/{pipeline → utils/pipeline}/reverse_engineering.py +412 -221
  384. oscura/{search → utils/search}/__init__.py +3 -3
  385. oscura/{search → utils/search}/anomaly.py +188 -58
  386. oscura/utils/search/context.py +294 -0
  387. oscura/{search → utils/search}/pattern.py +138 -10
  388. oscura/utils/serial.py +51 -0
  389. oscura/utils/storage/__init__.py +61 -0
  390. oscura/utils/storage/database.py +1166 -0
  391. oscura/{streaming → utils/streaming}/chunked.py +302 -143
  392. oscura/{streaming → utils/streaming}/progressive.py +1 -1
  393. oscura/{streaming → utils/streaming}/realtime.py +3 -2
  394. oscura/{triggering → utils/triggering}/__init__.py +6 -6
  395. oscura/{triggering → utils/triggering}/base.py +6 -6
  396. oscura/{triggering → utils/triggering}/edge.py +2 -2
  397. oscura/{triggering → utils/triggering}/pattern.py +2 -2
  398. oscura/{triggering → utils/triggering}/pulse.py +115 -74
  399. oscura/{triggering → utils/triggering}/window.py +2 -2
  400. oscura/utils/validation.py +32 -0
  401. oscura/validation/__init__.py +121 -0
  402. oscura/{compliance → validation/compliance}/__init__.py +5 -5
  403. oscura/{compliance → validation/compliance}/advanced.py +5 -5
  404. oscura/{compliance → validation/compliance}/masks.py +1 -1
  405. oscura/{compliance → validation/compliance}/reporting.py +127 -53
  406. oscura/{compliance → validation/compliance}/testing.py +114 -52
  407. oscura/validation/compliance_tests.py +915 -0
  408. oscura/validation/fuzzer.py +990 -0
  409. oscura/validation/grammar_tests.py +596 -0
  410. oscura/validation/grammar_validator.py +904 -0
  411. oscura/validation/hil_testing.py +977 -0
  412. oscura/{quality → validation/quality}/__init__.py +4 -4
  413. oscura/{quality → validation/quality}/ensemble.py +251 -171
  414. oscura/{quality → validation/quality}/explainer.py +3 -3
  415. oscura/{quality → validation/quality}/scoring.py +1 -1
  416. oscura/{quality → validation/quality}/warnings.py +4 -4
  417. oscura/validation/regression_suite.py +808 -0
  418. oscura/validation/replay.py +788 -0
  419. oscura/{testing → validation/testing}/__init__.py +2 -2
  420. oscura/{testing → validation/testing}/synthetic.py +5 -5
  421. oscura/visualization/__init__.py +9 -0
  422. oscura/visualization/accessibility.py +1 -1
  423. oscura/visualization/annotations.py +64 -67
  424. oscura/visualization/colors.py +7 -7
  425. oscura/visualization/digital.py +180 -81
  426. oscura/visualization/eye.py +236 -85
  427. oscura/visualization/interactive.py +320 -143
  428. oscura/visualization/jitter.py +587 -247
  429. oscura/visualization/layout.py +169 -134
  430. oscura/visualization/optimization.py +103 -52
  431. oscura/visualization/palettes.py +1 -1
  432. oscura/visualization/power.py +427 -211
  433. oscura/visualization/power_extended.py +626 -297
  434. oscura/visualization/presets.py +2 -0
  435. oscura/visualization/protocols.py +495 -181
  436. oscura/visualization/render.py +79 -63
  437. oscura/visualization/reverse_engineering.py +171 -124
  438. oscura/visualization/signal_integrity.py +460 -279
  439. oscura/visualization/specialized.py +190 -100
  440. oscura/visualization/spectral.py +670 -255
  441. oscura/visualization/thumbnails.py +166 -137
  442. oscura/visualization/waveform.py +150 -63
  443. oscura/workflows/__init__.py +3 -0
  444. oscura/{batch → workflows/batch}/__init__.py +5 -5
  445. oscura/{batch → workflows/batch}/advanced.py +150 -75
  446. oscura/workflows/batch/aggregate.py +531 -0
  447. oscura/workflows/batch/analyze.py +236 -0
  448. oscura/{batch → workflows/batch}/logging.py +2 -2
  449. oscura/{batch → workflows/batch}/metrics.py +1 -1
  450. oscura/workflows/complete_re.py +1144 -0
  451. oscura/workflows/compliance.py +44 -54
  452. oscura/workflows/digital.py +197 -51
  453. oscura/workflows/legacy/__init__.py +12 -0
  454. oscura/{workflow → workflows/legacy}/dag.py +4 -1
  455. oscura/workflows/multi_trace.py +9 -9
  456. oscura/workflows/power.py +42 -62
  457. oscura/workflows/protocol.py +82 -49
  458. oscura/workflows/reverse_engineering.py +351 -150
  459. oscura/workflows/signal_integrity.py +157 -82
  460. oscura-0.7.0.dist-info/METADATA +661 -0
  461. oscura-0.7.0.dist-info/RECORD +591 -0
  462. oscura/batch/aggregate.py +0 -300
  463. oscura/batch/analyze.py +0 -139
  464. oscura/dsl/__init__.py +0 -73
  465. oscura/exceptions.py +0 -59
  466. oscura/exploratory/fuzzy.py +0 -513
  467. oscura/exploratory/sync.py +0 -384
  468. oscura/exporters/__init__.py +0 -94
  469. oscura/exporters/csv.py +0 -303
  470. oscura/exporters/exporters.py +0 -44
  471. oscura/exporters/hdf5.py +0 -217
  472. oscura/exporters/html_export.py +0 -701
  473. oscura/exporters/json_export.py +0 -291
  474. oscura/exporters/markdown_export.py +0 -367
  475. oscura/exporters/matlab_export.py +0 -354
  476. oscura/exporters/npz_export.py +0 -219
  477. oscura/exporters/spice_export.py +0 -210
  478. oscura/search/context.py +0 -149
  479. oscura/session/__init__.py +0 -34
  480. oscura/session/annotations.py +0 -289
  481. oscura/session/history.py +0 -313
  482. oscura/session/session.py +0 -520
  483. oscura/workflow/__init__.py +0 -13
  484. oscura-0.5.1.dist-info/METADATA +0 -583
  485. oscura-0.5.1.dist-info/RECORD +0 -481
  486. /oscura/core/{config.py → config/legacy.py} +0 -0
  487. /oscura/{extensibility → core/extensibility}/__init__.py +0 -0
  488. /oscura/{extensibility → core/extensibility}/registry.py +0 -0
  489. /oscura/{plugins → core/plugins}/isolation.py +0 -0
  490. /oscura/{schemas → core/schemas}/bus_configuration.json +0 -0
  491. /oscura/{builders → utils/builders}/signal_builder.py +0 -0
  492. /oscura/{optimization → utils/optimization}/parallel.py +0 -0
  493. /oscura/{pipeline → utils/pipeline}/__init__.py +0 -0
  494. /oscura/{streaming → utils/streaming}/__init__.py +0 -0
  495. {oscura-0.5.1.dist-info → oscura-0.7.0.dist-info}/WHEEL +0 -0
  496. {oscura-0.5.1.dist-info → oscura-0.7.0.dist-info}/entry_points.txt +0 -0
  497. {oscura-0.5.1.dist-info → oscura-0.7.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"]