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
@@ -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
+ ]