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
@@ -182,71 +182,134 @@ class UDPStreamReassembler:
182
182
  key = flow_key or f"{segment.src}-{segment.dst}"
183
183
  self._segments[key].append(segment)
184
184
 
185
- def get_stream(self, flow_key: str | None = None) -> ReassembledStream:
186
- """Get reassembled stream for a flow.
185
+ def _get_empty_stream(self) -> ReassembledStream:
186
+ """Create empty stream result.
187
187
 
188
- Implements RE-STR-001: UDP stream reconstruction.
188
+ Returns:
189
+ Empty ReassembledStream.
190
+ """
191
+ return ReassembledStream(
192
+ data=b"",
193
+ src="",
194
+ dst="",
195
+ start_time=0.0,
196
+ end_time=0.0,
197
+ segments=0,
198
+ )
199
+
200
+ def _resolve_flow_key(self, flow_key: str | None) -> str | None:
201
+ """Resolve flow key to actual key or None if empty.
189
202
 
190
203
  Args:
191
- flow_key: Flow identifier.
204
+ flow_key: Requested flow key.
192
205
 
193
206
  Returns:
194
- ReassembledStream with ordered data.
207
+ Resolved flow key or None.
195
208
  """
196
209
  if flow_key is None:
197
- # Get first/only flow
198
210
  if not self._segments:
199
- return ReassembledStream(
200
- data=b"",
201
- src="",
202
- dst="",
203
- start_time=0.0,
204
- end_time=0.0,
205
- segments=0,
206
- )
207
- flow_key = next(iter(self._segments.keys()))
211
+ return None
212
+ return next(iter(self._segments.keys()))
213
+ return flow_key
208
214
 
209
- segments = self._segments.get(flow_key, [])
210
- if not segments:
211
- return ReassembledStream(
212
- data=b"",
213
- src="",
214
- dst="",
215
- start_time=0.0,
216
- end_time=0.0,
217
- segments=0,
218
- )
219
-
220
- # Sort by sequence number
221
- sorted_segments = sorted(segments, key=lambda s: s.sequence_number)
215
+ def _count_out_of_order(self, segments: list[StreamSegment]) -> int:
216
+ """Count out-of-order segments.
222
217
 
223
- # Concatenate data
224
- data = b"".join(s.data for s in sorted_segments)
218
+ Args:
219
+ segments: List of segments (unsorted).
225
220
 
226
- # Detect out-of-order: count segments that arrived after a higher sequence number
221
+ Returns:
222
+ Number of out-of-order segments.
223
+ """
227
224
  out_of_order = 0
228
225
  max_seq_seen = -1
229
226
  for segment in segments:
230
227
  if segment.sequence_number < max_seq_seen:
231
228
  out_of_order += 1
232
229
  max_seq_seen = max(max_seq_seen, segment.sequence_number)
230
+ return out_of_order
233
231
 
234
- # Detect gaps
235
- gaps = []
232
+ def _detect_gaps(self, sorted_segments: list[StreamSegment]) -> list[tuple[int, int]]:
233
+ """Detect gaps in sequence numbers.
234
+
235
+ Args:
236
+ sorted_segments: Segments sorted by sequence number.
237
+
238
+ Returns:
239
+ List of (expected, actual) gap tuples.
240
+ """
241
+ gaps: list[tuple[int, int]] = []
236
242
  for i in range(1, len(sorted_segments)):
237
243
  expected = sorted_segments[i - 1].sequence_number + len(sorted_segments[i - 1].data)
238
244
  actual = sorted_segments[i].sequence_number
239
245
  if actual > expected:
240
246
  gaps.append((expected, actual))
247
+ return gaps
248
+
249
+ def _get_time_range(self, sorted_segments: list[StreamSegment]) -> tuple[float, float]:
250
+ """Get start and end times from segments.
241
251
 
252
+ Args:
253
+ sorted_segments: List of segments.
254
+
255
+ Returns:
256
+ Tuple of (start_time, end_time).
257
+ """
242
258
  timestamps = [s.timestamp for s in sorted_segments if s.timestamp > 0]
259
+ if timestamps:
260
+ return min(timestamps), max(timestamps)
261
+ return 0.0, 0.0
262
+
263
+ def _extract_addresses(self, sorted_segments: list[StreamSegment]) -> tuple[str, str]:
264
+ """Extract source and destination addresses from segments.
265
+
266
+ Args:
267
+ sorted_segments: List of segments.
268
+
269
+ Returns:
270
+ Tuple of (src, dst).
271
+ """
272
+ if sorted_segments:
273
+ return sorted_segments[0].src, sorted_segments[0].dst
274
+ return "", ""
275
+
276
+ def get_stream(self, flow_key: str | None = None) -> ReassembledStream:
277
+ """Get reassembled stream for a flow.
278
+
279
+ Implements RE-STR-001: UDP stream reconstruction.
280
+
281
+ Args:
282
+ flow_key: Flow identifier.
283
+
284
+ Returns:
285
+ ReassembledStream with ordered data.
286
+ """
287
+ # Resolve flow key
288
+ resolved_key = self._resolve_flow_key(flow_key)
289
+ if resolved_key is None:
290
+ return self._get_empty_stream()
291
+
292
+ # Get segments
293
+ segments = self._segments.get(resolved_key, [])
294
+ if not segments:
295
+ return self._get_empty_stream()
296
+
297
+ # Sort by sequence number
298
+ sorted_segments = sorted(segments, key=lambda s: s.sequence_number)
299
+
300
+ # Build stream components
301
+ data = b"".join(s.data for s in sorted_segments)
302
+ out_of_order = self._count_out_of_order(segments)
303
+ gaps = self._detect_gaps(sorted_segments)
304
+ start_time, end_time = self._get_time_range(sorted_segments)
305
+ src, dst = self._extract_addresses(sorted_segments)
243
306
 
244
307
  return ReassembledStream(
245
308
  data=data,
246
- src=sorted_segments[0].src if sorted_segments else "",
247
- dst=sorted_segments[0].dst if sorted_segments else "",
248
- start_time=min(timestamps) if timestamps else 0.0,
249
- end_time=max(timestamps) if timestamps else 0.0,
309
+ src=src,
310
+ dst=dst,
311
+ start_time=start_time,
312
+ end_time=end_time,
250
313
  segments=len(sorted_segments),
251
314
  gaps=gaps,
252
315
  retransmits=0,
@@ -354,77 +417,84 @@ class TCPStreamReassembler:
354
417
 
355
418
  self._segments[key].append(seg)
356
419
 
357
- def get_stream(self, flow_key: str | None = None) -> ReassembledStream:
358
- """Get reassembled TCP stream.
359
-
360
- Implements RE-STR-002: TCP stream reassembly.
420
+ def _extract_addresses(self, sorted_segments: list[StreamSegment]) -> tuple[str, str]:
421
+ """Extract source and destination addresses from segments.
361
422
 
362
423
  Args:
363
- flow_key: Flow identifier.
424
+ sorted_segments: Sorted list of stream segments.
364
425
 
365
426
  Returns:
366
- ReassembledStream with complete data.
427
+ Tuple of (source, destination) addresses.
367
428
  """
368
- if flow_key is None:
369
- if not self._segments:
370
- return ReassembledStream(
371
- data=b"",
372
- src="",
373
- dst="",
374
- start_time=0.0,
375
- end_time=0.0,
376
- segments=0,
377
- )
378
- flow_key = next(iter(self._segments.keys()))
429
+ if not sorted_segments:
430
+ return ("", "")
431
+ return (sorted_segments[0].src, sorted_segments[0].dst)
379
432
 
380
- segments = self._segments.get(flow_key, [])
381
- if not segments:
382
- return ReassembledStream(
383
- data=b"",
384
- src="",
385
- dst="",
386
- start_time=0.0,
387
- end_time=0.0,
388
- segments=0,
389
- )
433
+ def _count_anomalies(self, segments: list[StreamSegment]) -> tuple[int, int]:
434
+ """Count retransmits and out-of-order segments.
390
435
 
391
- isn = self._isn.get(flow_key, 0) or 0
436
+ Args:
437
+ segments: List of segments in arrival order.
392
438
 
393
- # Count retransmits first (before filtering)
439
+ Returns:
440
+ Tuple of (retransmits, out_of_order) counts.
441
+ """
442
+ # Count retransmits
394
443
  retransmits = sum(1 for seg in segments if seg.is_retransmit)
395
444
 
396
- # If ISN wasn't detected via SYN, use minimum sequence number
397
- if isn == 0 or isn > min(s.sequence_number for s in segments):
398
- isn = min(s.sequence_number for s in segments)
399
-
400
- # Detect out-of-order by checking arrival order vs sequence order
401
- # Count segments that arrived before a segment with lower sequence
445
+ # Count out-of-order segments
402
446
  out_of_order = 0
403
447
  for i, seg in enumerate(segments):
404
- # Check if any earlier segment has higher sequence
405
448
  for j in range(i):
406
449
  if segments[j].sequence_number > seg.sequence_number:
407
450
  out_of_order += 1
408
451
  break
409
452
 
410
- # Sort by relative sequence number
411
- sorted_segments = sorted(segments, key=lambda s: (s.sequence_number - isn) % (2**32))
453
+ return (retransmits, out_of_order)
454
+
455
+ def _detect_isn(self, flow_key: str, segments: list[StreamSegment]) -> int:
456
+ """Detect initial sequence number for flow.
457
+
458
+ Args:
459
+ flow_key: Flow identifier.
460
+ segments: List of segments.
461
+
462
+ Returns:
463
+ Initial sequence number.
464
+ """
465
+ isn = self._isn.get(flow_key, 0) or 0
466
+
467
+ # If ISN wasn't detected via SYN, use minimum sequence number
468
+ if isn == 0 or isn > min(s.sequence_number for s in segments):
469
+ isn = min(s.sequence_number for s in segments)
412
470
 
413
- # Build stream handling overlaps and gaps
471
+ return isn
472
+
473
+ def _build_data_buffer(
474
+ self, sorted_segments: list[StreamSegment], isn: int
475
+ ) -> tuple[bytes, list[tuple[int, int]]]:
476
+ """Build data buffer from sorted segments, handling gaps and overlaps.
477
+
478
+ Args:
479
+ sorted_segments: Segments sorted by sequence number.
480
+ isn: Initial sequence number.
481
+
482
+ Returns:
483
+ Tuple of (reassembled_data, gaps).
484
+ """
414
485
  data_buffer = bytearray()
415
486
  current_offset = 0
416
487
  gaps = []
417
488
 
418
489
  for seg in sorted_segments:
419
490
  if seg.is_retransmit:
420
- continue # Skip retransmits when building data
491
+ continue
421
492
 
422
493
  rel_seq = (seg.sequence_number - isn) % (2**32)
423
494
 
424
495
  if rel_seq > current_offset:
425
496
  # Gap detected
426
497
  gaps.append((current_offset, rel_seq))
427
- # Fill gap with zeros
428
498
  data_buffer.extend(b"\x00" * (rel_seq - current_offset))
429
499
  current_offset = rel_seq
430
500
 
@@ -438,12 +508,59 @@ class TCPStreamReassembler:
438
508
  data_buffer.extend(seg.data)
439
509
  current_offset += len(seg.data)
440
510
 
511
+ return (bytes(data_buffer), gaps)
512
+
513
+ def get_stream(self, flow_key: str | None = None) -> ReassembledStream:
514
+ """Get reassembled TCP stream.
515
+
516
+ Implements RE-STR-002: TCP stream reassembly.
517
+
518
+ Args:
519
+ flow_key: Flow identifier.
520
+
521
+ Returns:
522
+ ReassembledStream with complete data.
523
+ """
524
+ # Handle empty or default flow key
525
+ if flow_key is None:
526
+ if not self._segments:
527
+ return ReassembledStream(
528
+ data=b"",
529
+ src="",
530
+ dst="",
531
+ start_time=0.0,
532
+ end_time=0.0,
533
+ segments=0,
534
+ )
535
+ flow_key = next(iter(self._segments.keys()))
536
+
537
+ segments = self._segments.get(flow_key, [])
538
+ if not segments:
539
+ return ReassembledStream(
540
+ data=b"",
541
+ src="",
542
+ dst="",
543
+ start_time=0.0,
544
+ end_time=0.0,
545
+ segments=0,
546
+ )
547
+
548
+ # Detect ISN and count anomalies
549
+ isn = self._detect_isn(flow_key, segments)
550
+ retransmits, out_of_order = self._count_anomalies(segments)
551
+
552
+ # Sort and reassemble
553
+ sorted_segments = sorted(segments, key=lambda s: (s.sequence_number - isn) % (2**32))
554
+ data, gaps = self._build_data_buffer(sorted_segments, isn)
555
+
556
+ # Extract metadata
557
+ src, dst = self._extract_addresses(sorted_segments)
441
558
  timestamps = [s.timestamp for s in sorted_segments if s.timestamp > 0]
442
559
 
443
560
  return ReassembledStream(
444
- data=bytes(data_buffer),
445
- src=sorted_segments[0].src if sorted_segments else "",
446
- dst=sorted_segments[0].dst if sorted_segments else "",
561
+ data=data,
562
+ src=src,
563
+ dst=dst,
447
564
  start_time=min(timestamps) if timestamps else 0.0,
448
565
  end_time=max(timestamps) if timestamps else 0.0,
449
566
  segments=len(sorted_segments),
@@ -542,7 +659,29 @@ class MessageFramer:
542
659
  Returns:
543
660
  Detected framing type string.
544
661
  """
545
- # Check for common delimiters
662
+ # Check for delimiter-based framing
663
+ if self._is_delimiter_framed(data):
664
+ return "delimiter"
665
+
666
+ # Check for length-prefixed framing
667
+ if self._is_length_prefixed(data):
668
+ return "length_prefix"
669
+
670
+ # Check for fixed-size framing
671
+ if self._is_fixed_size(data):
672
+ return "fixed"
673
+
674
+ return "unknown"
675
+
676
+ def _is_delimiter_framed(self, data: bytes) -> bool:
677
+ """Check if data uses delimiter-based framing.
678
+
679
+ Args:
680
+ data: Sample data.
681
+
682
+ Returns:
683
+ True if delimiter framing detected.
684
+ """
546
685
  common_delimiters = [b"\r\n", b"\n", b"\x00", b"\r"]
547
686
  for delim in common_delimiters:
548
687
  count = data.count(delim)
@@ -550,35 +689,56 @@ class MessageFramer:
550
689
  # Check for regular spacing
551
690
  parts = data.split(delim)
552
691
  if parts and len({len(p) for p in parts if p}) <= 3:
553
- return "delimiter"
554
-
555
- # Check for length-prefixed
556
- if len(data) >= 4:
557
- # Try big-endian 2-byte length
558
- for offset in range(min(8, len(data) - 2)):
559
- length = int.from_bytes(data[offset : offset + 2], "big")
560
- if 4 < length < len(data) and length < 65536:
561
- # Check if data continues with similar pattern
562
- next_offset = offset + length
563
- if next_offset + 2 < len(data):
564
- next_length = int.from_bytes(data[next_offset : next_offset + 2], "big")
565
- if 4 < next_length < len(data):
566
- return "length_prefix"
567
-
568
- # Check for fixed size
569
- if len(data) >= 32:
570
- # Look for repeating pattern
571
- for size in range(4, 128):
572
- if len(data) % size == 0:
573
- chunks = [data[i : i + size] for i in range(0, len(data), size)]
574
- if len(chunks) >= 3:
575
- # Check structural similarity
576
- first = chunks[0][:4] if len(chunks[0]) >= 4 else chunks[0]
577
- matches = sum(1 for c in chunks[1:] if c[: len(first)] == first)
578
- if matches >= len(chunks) * 0.5:
579
- return "fixed"
692
+ return True
693
+ return False
580
694
 
581
- return "unknown"
695
+ def _is_length_prefixed(self, data: bytes) -> bool:
696
+ """Check if data uses length-prefixed framing.
697
+
698
+ Args:
699
+ data: Sample data.
700
+
701
+ Returns:
702
+ True if length-prefixed framing detected.
703
+ """
704
+ if len(data) < 4:
705
+ return False
706
+
707
+ # Try big-endian 2-byte length
708
+ for offset in range(min(8, len(data) - 2)):
709
+ length = int.from_bytes(data[offset : offset + 2], "big")
710
+ if 4 < length < len(data) and length < 65536:
711
+ # Check if data continues with similar pattern
712
+ next_offset = offset + length
713
+ if next_offset + 2 < len(data):
714
+ next_length = int.from_bytes(data[next_offset : next_offset + 2], "big")
715
+ if 4 < next_length < len(data):
716
+ return True
717
+ return False
718
+
719
+ def _is_fixed_size(self, data: bytes) -> bool:
720
+ """Check if data uses fixed-size framing.
721
+
722
+ Args:
723
+ data: Sample data.
724
+
725
+ Returns:
726
+ True if fixed-size framing detected.
727
+ """
728
+ if len(data) < 32:
729
+ return False
730
+
731
+ # Look for repeating pattern
732
+ for size in range(4, 128):
733
+ if len(data) % size == 0:
734
+ chunks = [data[i : i + size] for i in range(0, len(data), size)]
735
+ if len(chunks) >= 3:
736
+ # Check structural similarity
737
+ first = chunks[0][:4] if len(chunks[0]) >= 4 else chunks[0]
738
+ matches = sum(1 for c in chunks[1:] if c[: len(first)] == first)
739
+ if matches >= len(chunks) * 0.5:
740
+ return True
741
+ return False
582
742
 
583
743
  def _auto_frame(self, data: bytes) -> FramingResult:
584
744
  """Automatically detect and apply framing.
oscura/iot/__init__.py ADDED
@@ -0,0 +1,34 @@
1
+ """IoT protocol decoders package.
2
+
3
+ Provides decoders for IoT wireless protocols including LoRaWAN, Zigbee, BLE, MQTT, CoAP, and more.
4
+ """
5
+
6
+ from oscura.iot.coap.analyzer import (
7
+ CoAPAnalyzer,
8
+ CoAPExchange,
9
+ CoAPMessage,
10
+ )
11
+ from oscura.iot.lorawan.decoder import (
12
+ LoRaWANDecoder,
13
+ LoRaWANFrame,
14
+ LoRaWANKeys,
15
+ decode_lorawan_frame,
16
+ )
17
+ from oscura.iot.mqtt.analyzer import (
18
+ MQTTAnalyzer,
19
+ MQTTPacket,
20
+ MQTTSession,
21
+ )
22
+
23
+ __all__ = [
24
+ "CoAPAnalyzer",
25
+ "CoAPExchange",
26
+ "CoAPMessage",
27
+ "LoRaWANDecoder",
28
+ "LoRaWANFrame",
29
+ "LoRaWANKeys",
30
+ "MQTTAnalyzer",
31
+ "MQTTPacket",
32
+ "MQTTSession",
33
+ "decode_lorawan_frame",
34
+ ]
@@ -0,0 +1,32 @@
1
+ """CoAP (Constrained Application Protocol) analyzer.
2
+
3
+ This module provides comprehensive CoAP (RFC 7252) protocol analysis including
4
+ message parsing, request/response matching, blockwise transfer support, and
5
+ observe extension support.
6
+
7
+ Example:
8
+ >>> from oscura.iot.coap import CoAPAnalyzer, CoAPMessage
9
+ >>> analyzer = CoAPAnalyzer()
10
+ >>> data = bytes([0x40, 0x01, 0x00, 0x01]) # CON GET message
11
+ >>> message = analyzer.parse_message(data, timestamp=0.0)
12
+ >>> print(message.msg_type, message.code)
13
+ CON GET
14
+
15
+ References:
16
+ RFC 7252: CoAP (Constrained Application Protocol)
17
+ RFC 7959: Blockwise Transfer in CoAP
18
+ RFC 7641: Observing Resources in CoAP
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ from oscura.iot.coap.analyzer import CoAPAnalyzer, CoAPExchange, CoAPMessage
24
+ from oscura.iot.coap.options import CONTENT_FORMATS, OPTIONS
25
+
26
+ __all__ = [
27
+ "CONTENT_FORMATS",
28
+ "OPTIONS",
29
+ "CoAPAnalyzer",
30
+ "CoAPExchange",
31
+ "CoAPMessage",
32
+ ]