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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (513) hide show
  1. oscura/__init__.py +169 -167
  2. oscura/analyzers/__init__.py +3 -0
  3. oscura/analyzers/classification.py +659 -0
  4. oscura/analyzers/digital/__init__.py +0 -48
  5. oscura/analyzers/digital/edges.py +325 -65
  6. oscura/analyzers/digital/extraction.py +0 -195
  7. oscura/analyzers/digital/quality.py +293 -166
  8. oscura/analyzers/digital/timing.py +260 -115
  9. oscura/analyzers/digital/timing_numba.py +334 -0
  10. oscura/analyzers/entropy.py +605 -0
  11. oscura/analyzers/eye/diagram.py +176 -109
  12. oscura/analyzers/eye/metrics.py +5 -5
  13. oscura/analyzers/jitter/__init__.py +6 -4
  14. oscura/analyzers/jitter/ber.py +52 -52
  15. oscura/analyzers/jitter/classification.py +156 -0
  16. oscura/analyzers/jitter/decomposition.py +163 -113
  17. oscura/analyzers/jitter/spectrum.py +80 -64
  18. oscura/analyzers/ml/__init__.py +39 -0
  19. oscura/analyzers/ml/features.py +600 -0
  20. oscura/analyzers/ml/signal_classifier.py +604 -0
  21. oscura/analyzers/packet/daq.py +246 -158
  22. oscura/analyzers/packet/parser.py +12 -1
  23. oscura/analyzers/packet/payload.py +50 -2110
  24. oscura/analyzers/packet/payload_analysis.py +361 -181
  25. oscura/analyzers/packet/payload_patterns.py +133 -70
  26. oscura/analyzers/packet/stream.py +84 -23
  27. oscura/analyzers/patterns/__init__.py +26 -5
  28. oscura/analyzers/patterns/anomaly_detection.py +908 -0
  29. oscura/analyzers/patterns/clustering.py +169 -108
  30. oscura/analyzers/patterns/clustering_optimized.py +227 -0
  31. oscura/analyzers/patterns/discovery.py +1 -1
  32. oscura/analyzers/patterns/matching.py +581 -197
  33. oscura/analyzers/patterns/pattern_mining.py +778 -0
  34. oscura/analyzers/patterns/periodic.py +121 -38
  35. oscura/analyzers/patterns/sequences.py +175 -78
  36. oscura/analyzers/power/conduction.py +1 -1
  37. oscura/analyzers/power/soa.py +6 -6
  38. oscura/analyzers/power/switching.py +250 -110
  39. oscura/analyzers/protocol/__init__.py +17 -1
  40. oscura/analyzers/protocols/__init__.py +1 -22
  41. oscura/analyzers/protocols/base.py +6 -6
  42. oscura/analyzers/protocols/ble/__init__.py +38 -0
  43. oscura/analyzers/protocols/ble/analyzer.py +809 -0
  44. oscura/analyzers/protocols/ble/uuids.py +288 -0
  45. oscura/analyzers/protocols/can.py +257 -127
  46. oscura/analyzers/protocols/can_fd.py +107 -80
  47. oscura/analyzers/protocols/flexray.py +139 -80
  48. oscura/analyzers/protocols/hdlc.py +93 -58
  49. oscura/analyzers/protocols/i2c.py +247 -106
  50. oscura/analyzers/protocols/i2s.py +138 -86
  51. oscura/analyzers/protocols/industrial/__init__.py +40 -0
  52. oscura/analyzers/protocols/industrial/bacnet/__init__.py +33 -0
  53. oscura/analyzers/protocols/industrial/bacnet/analyzer.py +708 -0
  54. oscura/analyzers/protocols/industrial/bacnet/encoding.py +412 -0
  55. oscura/analyzers/protocols/industrial/bacnet/services.py +622 -0
  56. oscura/analyzers/protocols/industrial/ethercat/__init__.py +30 -0
  57. oscura/analyzers/protocols/industrial/ethercat/analyzer.py +474 -0
  58. oscura/analyzers/protocols/industrial/ethercat/mailbox.py +339 -0
  59. oscura/analyzers/protocols/industrial/ethercat/topology.py +166 -0
  60. oscura/analyzers/protocols/industrial/modbus/__init__.py +31 -0
  61. oscura/analyzers/protocols/industrial/modbus/analyzer.py +525 -0
  62. oscura/analyzers/protocols/industrial/modbus/crc.py +79 -0
  63. oscura/analyzers/protocols/industrial/modbus/functions.py +436 -0
  64. oscura/analyzers/protocols/industrial/opcua/__init__.py +21 -0
  65. oscura/analyzers/protocols/industrial/opcua/analyzer.py +552 -0
  66. oscura/analyzers/protocols/industrial/opcua/datatypes.py +446 -0
  67. oscura/analyzers/protocols/industrial/opcua/services.py +264 -0
  68. oscura/analyzers/protocols/industrial/profinet/__init__.py +23 -0
  69. oscura/analyzers/protocols/industrial/profinet/analyzer.py +441 -0
  70. oscura/analyzers/protocols/industrial/profinet/dcp.py +263 -0
  71. oscura/analyzers/protocols/industrial/profinet/ptcp.py +200 -0
  72. oscura/analyzers/protocols/jtag.py +180 -98
  73. oscura/analyzers/protocols/lin.py +219 -114
  74. oscura/analyzers/protocols/manchester.py +4 -4
  75. oscura/analyzers/protocols/onewire.py +253 -149
  76. oscura/analyzers/protocols/parallel_bus/__init__.py +20 -0
  77. oscura/analyzers/protocols/parallel_bus/centronics.py +92 -0
  78. oscura/analyzers/protocols/parallel_bus/gpib.py +137 -0
  79. oscura/analyzers/protocols/spi.py +192 -95
  80. oscura/analyzers/protocols/swd.py +321 -167
  81. oscura/analyzers/protocols/uart.py +267 -125
  82. oscura/analyzers/protocols/usb.py +235 -131
  83. oscura/analyzers/side_channel/power.py +17 -12
  84. oscura/analyzers/signal/__init__.py +15 -0
  85. oscura/analyzers/signal/timing_analysis.py +1086 -0
  86. oscura/analyzers/signal_integrity/__init__.py +4 -1
  87. oscura/analyzers/signal_integrity/sparams.py +2 -19
  88. oscura/analyzers/spectral/chunked.py +129 -60
  89. oscura/analyzers/spectral/chunked_fft.py +300 -94
  90. oscura/analyzers/spectral/chunked_wavelet.py +100 -80
  91. oscura/analyzers/statistical/checksum.py +376 -217
  92. oscura/analyzers/statistical/classification.py +229 -107
  93. oscura/analyzers/statistical/entropy.py +78 -53
  94. oscura/analyzers/statistics/correlation.py +407 -211
  95. oscura/analyzers/statistics/outliers.py +2 -2
  96. oscura/analyzers/statistics/streaming.py +30 -5
  97. oscura/analyzers/validation.py +216 -101
  98. oscura/analyzers/waveform/measurements.py +9 -0
  99. oscura/analyzers/waveform/measurements_with_uncertainty.py +31 -15
  100. oscura/analyzers/waveform/spectral.py +500 -228
  101. oscura/api/__init__.py +31 -5
  102. oscura/api/dsl/__init__.py +582 -0
  103. oscura/{dsl → api/dsl}/commands.py +43 -76
  104. oscura/{dsl → api/dsl}/interpreter.py +26 -51
  105. oscura/{dsl → api/dsl}/parser.py +107 -77
  106. oscura/{dsl → api/dsl}/repl.py +2 -2
  107. oscura/api/dsl.py +1 -1
  108. oscura/{integrations → api/integrations}/__init__.py +1 -1
  109. oscura/{integrations → api/integrations}/llm.py +201 -102
  110. oscura/api/operators.py +3 -3
  111. oscura/api/optimization.py +144 -30
  112. oscura/api/rest_server.py +921 -0
  113. oscura/api/server/__init__.py +17 -0
  114. oscura/api/server/dashboard.py +850 -0
  115. oscura/api/server/static/README.md +34 -0
  116. oscura/api/server/templates/base.html +181 -0
  117. oscura/api/server/templates/export.html +120 -0
  118. oscura/api/server/templates/home.html +284 -0
  119. oscura/api/server/templates/protocols.html +58 -0
  120. oscura/api/server/templates/reports.html +43 -0
  121. oscura/api/server/templates/session_detail.html +89 -0
  122. oscura/api/server/templates/sessions.html +83 -0
  123. oscura/api/server/templates/waveforms.html +73 -0
  124. oscura/automotive/__init__.py +8 -1
  125. oscura/automotive/can/__init__.py +10 -0
  126. oscura/automotive/can/checksum.py +3 -1
  127. oscura/automotive/can/dbc_generator.py +590 -0
  128. oscura/automotive/can/message_wrapper.py +121 -74
  129. oscura/automotive/can/patterns.py +98 -21
  130. oscura/automotive/can/session.py +292 -56
  131. oscura/automotive/can/state_machine.py +6 -3
  132. oscura/automotive/can/stimulus_response.py +97 -75
  133. oscura/automotive/dbc/__init__.py +10 -2
  134. oscura/automotive/dbc/generator.py +84 -56
  135. oscura/automotive/dbc/parser.py +6 -6
  136. oscura/automotive/dtc/data.json +2763 -0
  137. oscura/automotive/dtc/database.py +2 -2
  138. oscura/automotive/flexray/__init__.py +31 -0
  139. oscura/automotive/flexray/analyzer.py +504 -0
  140. oscura/automotive/flexray/crc.py +185 -0
  141. oscura/automotive/flexray/fibex.py +449 -0
  142. oscura/automotive/j1939/__init__.py +45 -8
  143. oscura/automotive/j1939/analyzer.py +605 -0
  144. oscura/automotive/j1939/spns.py +326 -0
  145. oscura/automotive/j1939/transport.py +306 -0
  146. oscura/automotive/lin/__init__.py +47 -0
  147. oscura/automotive/lin/analyzer.py +612 -0
  148. oscura/automotive/loaders/blf.py +13 -2
  149. oscura/automotive/loaders/csv_can.py +143 -72
  150. oscura/automotive/loaders/dispatcher.py +50 -2
  151. oscura/automotive/loaders/mdf.py +86 -45
  152. oscura/automotive/loaders/pcap.py +111 -61
  153. oscura/automotive/uds/__init__.py +4 -0
  154. oscura/automotive/uds/analyzer.py +725 -0
  155. oscura/automotive/uds/decoder.py +140 -58
  156. oscura/automotive/uds/models.py +7 -1
  157. oscura/automotive/visualization.py +1 -1
  158. oscura/cli/analyze.py +348 -0
  159. oscura/cli/batch.py +142 -122
  160. oscura/cli/benchmark.py +275 -0
  161. oscura/cli/characterize.py +137 -82
  162. oscura/cli/compare.py +224 -131
  163. oscura/cli/completion.py +250 -0
  164. oscura/cli/config_cmd.py +361 -0
  165. oscura/cli/decode.py +164 -87
  166. oscura/cli/export.py +286 -0
  167. oscura/cli/main.py +115 -31
  168. oscura/{onboarding → cli/onboarding}/__init__.py +3 -3
  169. oscura/{onboarding → cli/onboarding}/help.py +80 -58
  170. oscura/{onboarding → cli/onboarding}/tutorials.py +97 -72
  171. oscura/{onboarding → cli/onboarding}/wizard.py +55 -36
  172. oscura/cli/progress.py +147 -0
  173. oscura/cli/shell.py +157 -135
  174. oscura/cli/validate_cmd.py +204 -0
  175. oscura/cli/visualize.py +158 -0
  176. oscura/convenience.py +125 -79
  177. oscura/core/__init__.py +4 -2
  178. oscura/core/backend_selector.py +3 -3
  179. oscura/core/cache.py +126 -15
  180. oscura/core/cancellation.py +1 -1
  181. oscura/{config → core/config}/__init__.py +20 -11
  182. oscura/{config → core/config}/defaults.py +1 -1
  183. oscura/{config → core/config}/loader.py +7 -5
  184. oscura/{config → core/config}/memory.py +5 -5
  185. oscura/{config → core/config}/migration.py +1 -1
  186. oscura/{config → core/config}/pipeline.py +99 -23
  187. oscura/{config → core/config}/preferences.py +1 -1
  188. oscura/{config → core/config}/protocol.py +3 -3
  189. oscura/{config → core/config}/schema.py +426 -272
  190. oscura/{config → core/config}/settings.py +1 -1
  191. oscura/{config → core/config}/thresholds.py +195 -153
  192. oscura/core/correlation.py +5 -6
  193. oscura/core/cross_domain.py +0 -2
  194. oscura/core/debug.py +9 -5
  195. oscura/{extensibility → core/extensibility}/docs.py +158 -70
  196. oscura/{extensibility → core/extensibility}/extensions.py +160 -76
  197. oscura/{extensibility → core/extensibility}/logging.py +1 -1
  198. oscura/{extensibility → core/extensibility}/measurements.py +1 -1
  199. oscura/{extensibility → core/extensibility}/plugins.py +1 -1
  200. oscura/{extensibility → core/extensibility}/templates.py +73 -3
  201. oscura/{extensibility → core/extensibility}/validation.py +1 -1
  202. oscura/core/gpu_backend.py +11 -7
  203. oscura/core/log_query.py +101 -11
  204. oscura/core/logging.py +126 -54
  205. oscura/core/logging_advanced.py +5 -5
  206. oscura/core/memory_limits.py +108 -70
  207. oscura/core/memory_monitor.py +2 -2
  208. oscura/core/memory_progress.py +7 -7
  209. oscura/core/memory_warnings.py +1 -1
  210. oscura/core/numba_backend.py +13 -13
  211. oscura/{plugins → core/plugins}/__init__.py +9 -9
  212. oscura/{plugins → core/plugins}/base.py +7 -7
  213. oscura/{plugins → core/plugins}/cli.py +3 -3
  214. oscura/{plugins → core/plugins}/discovery.py +186 -106
  215. oscura/{plugins → core/plugins}/lifecycle.py +1 -1
  216. oscura/{plugins → core/plugins}/manager.py +7 -7
  217. oscura/{plugins → core/plugins}/registry.py +3 -3
  218. oscura/{plugins → core/plugins}/versioning.py +1 -1
  219. oscura/core/progress.py +16 -1
  220. oscura/core/provenance.py +8 -2
  221. oscura/{schemas → core/schemas}/__init__.py +2 -2
  222. oscura/core/schemas/bus_configuration.json +322 -0
  223. oscura/core/schemas/device_mapping.json +182 -0
  224. oscura/core/schemas/packet_format.json +418 -0
  225. oscura/core/schemas/protocol_definition.json +363 -0
  226. oscura/core/types.py +4 -0
  227. oscura/core/uncertainty.py +3 -3
  228. oscura/correlation/__init__.py +52 -0
  229. oscura/correlation/multi_protocol.py +811 -0
  230. oscura/discovery/auto_decoder.py +117 -35
  231. oscura/discovery/comparison.py +191 -86
  232. oscura/discovery/quality_validator.py +155 -68
  233. oscura/discovery/signal_detector.py +196 -79
  234. oscura/export/__init__.py +18 -20
  235. oscura/export/kaitai_struct.py +513 -0
  236. oscura/export/scapy_layer.py +801 -0
  237. oscura/export/wireshark/README.md +15 -15
  238. oscura/export/wireshark/generator.py +1 -1
  239. oscura/export/wireshark/templates/dissector.lua.j2 +2 -2
  240. oscura/export/wireshark_dissector.py +746 -0
  241. oscura/guidance/wizard.py +207 -111
  242. oscura/hardware/__init__.py +19 -0
  243. oscura/{acquisition → hardware/acquisition}/__init__.py +4 -4
  244. oscura/{acquisition → hardware/acquisition}/file.py +2 -2
  245. oscura/{acquisition → hardware/acquisition}/hardware.py +7 -7
  246. oscura/{acquisition → hardware/acquisition}/saleae.py +15 -12
  247. oscura/{acquisition → hardware/acquisition}/socketcan.py +1 -1
  248. oscura/{acquisition → hardware/acquisition}/streaming.py +2 -2
  249. oscura/{acquisition → hardware/acquisition}/synthetic.py +3 -3
  250. oscura/{acquisition → hardware/acquisition}/visa.py +33 -11
  251. oscura/hardware/firmware/__init__.py +29 -0
  252. oscura/hardware/firmware/pattern_recognition.py +874 -0
  253. oscura/hardware/hal_detector.py +736 -0
  254. oscura/hardware/security/__init__.py +37 -0
  255. oscura/hardware/security/side_channel_detector.py +1126 -0
  256. oscura/inference/__init__.py +4 -0
  257. oscura/inference/active_learning/README.md +7 -7
  258. oscura/inference/active_learning/observation_table.py +4 -1
  259. oscura/inference/alignment.py +216 -123
  260. oscura/inference/bayesian.py +113 -33
  261. oscura/inference/crc_reverse.py +101 -55
  262. oscura/inference/logic.py +6 -2
  263. oscura/inference/message_format.py +342 -183
  264. oscura/inference/protocol.py +95 -44
  265. oscura/inference/protocol_dsl.py +180 -82
  266. oscura/inference/signal_intelligence.py +1439 -706
  267. oscura/inference/spectral.py +99 -57
  268. oscura/inference/state_machine.py +810 -158
  269. oscura/inference/stream.py +270 -110
  270. oscura/iot/__init__.py +34 -0
  271. oscura/iot/coap/__init__.py +32 -0
  272. oscura/iot/coap/analyzer.py +668 -0
  273. oscura/iot/coap/options.py +212 -0
  274. oscura/iot/lorawan/__init__.py +21 -0
  275. oscura/iot/lorawan/crypto.py +206 -0
  276. oscura/iot/lorawan/decoder.py +801 -0
  277. oscura/iot/lorawan/mac_commands.py +341 -0
  278. oscura/iot/mqtt/__init__.py +27 -0
  279. oscura/iot/mqtt/analyzer.py +999 -0
  280. oscura/iot/mqtt/properties.py +315 -0
  281. oscura/iot/zigbee/__init__.py +31 -0
  282. oscura/iot/zigbee/analyzer.py +615 -0
  283. oscura/iot/zigbee/security.py +153 -0
  284. oscura/iot/zigbee/zcl.py +349 -0
  285. oscura/jupyter/display.py +125 -45
  286. oscura/{exploratory → jupyter/exploratory}/__init__.py +8 -8
  287. oscura/{exploratory → jupyter/exploratory}/error_recovery.py +298 -141
  288. oscura/jupyter/exploratory/fuzzy.py +746 -0
  289. oscura/{exploratory → jupyter/exploratory}/fuzzy_advanced.py +258 -100
  290. oscura/{exploratory → jupyter/exploratory}/legacy.py +464 -242
  291. oscura/{exploratory → jupyter/exploratory}/parse.py +167 -145
  292. oscura/{exploratory → jupyter/exploratory}/recovery.py +119 -87
  293. oscura/jupyter/exploratory/sync.py +612 -0
  294. oscura/{exploratory → jupyter/exploratory}/unknown.py +299 -176
  295. oscura/jupyter/magic.py +4 -4
  296. oscura/{ui → jupyter/ui}/__init__.py +2 -2
  297. oscura/{ui → jupyter/ui}/formatters.py +3 -3
  298. oscura/{ui → jupyter/ui}/progressive_display.py +153 -82
  299. oscura/loaders/__init__.py +171 -63
  300. oscura/loaders/binary.py +88 -1
  301. oscura/loaders/chipwhisperer.py +153 -137
  302. oscura/loaders/configurable.py +208 -86
  303. oscura/loaders/csv_loader.py +458 -215
  304. oscura/loaders/hdf5_loader.py +278 -119
  305. oscura/loaders/lazy.py +87 -54
  306. oscura/loaders/mmap_loader.py +1 -1
  307. oscura/loaders/numpy_loader.py +253 -116
  308. oscura/loaders/pcap.py +226 -151
  309. oscura/loaders/rigol.py +110 -49
  310. oscura/loaders/sigrok.py +201 -78
  311. oscura/loaders/tdms.py +81 -58
  312. oscura/loaders/tektronix.py +291 -174
  313. oscura/loaders/touchstone.py +182 -87
  314. oscura/loaders/vcd.py +215 -117
  315. oscura/loaders/wav.py +155 -68
  316. oscura/reporting/__init__.py +9 -7
  317. oscura/reporting/analyze.py +352 -146
  318. oscura/reporting/argument_preparer.py +69 -14
  319. oscura/reporting/auto_report.py +97 -61
  320. oscura/reporting/batch.py +131 -58
  321. oscura/reporting/chart_selection.py +57 -45
  322. oscura/reporting/comparison.py +63 -17
  323. oscura/reporting/content/executive.py +76 -24
  324. oscura/reporting/core_formats/multi_format.py +11 -8
  325. oscura/reporting/engine.py +312 -158
  326. oscura/reporting/enhanced_reports.py +949 -0
  327. oscura/reporting/export.py +86 -43
  328. oscura/reporting/formatting/numbers.py +69 -42
  329. oscura/reporting/html.py +139 -58
  330. oscura/reporting/index.py +137 -65
  331. oscura/reporting/output.py +158 -67
  332. oscura/reporting/pdf.py +67 -102
  333. oscura/reporting/plots.py +191 -112
  334. oscura/reporting/sections.py +88 -47
  335. oscura/reporting/standards.py +104 -61
  336. oscura/reporting/summary_generator.py +75 -55
  337. oscura/reporting/tables.py +138 -54
  338. oscura/reporting/templates/enhanced/protocol_re.html +525 -0
  339. oscura/reporting/templates/index.md +13 -13
  340. oscura/sessions/__init__.py +14 -23
  341. oscura/sessions/base.py +3 -3
  342. oscura/sessions/blackbox.py +106 -10
  343. oscura/sessions/generic.py +2 -2
  344. oscura/sessions/legacy.py +783 -0
  345. oscura/side_channel/__init__.py +63 -0
  346. oscura/side_channel/dpa.py +1025 -0
  347. oscura/utils/__init__.py +15 -1
  348. oscura/utils/autodetect.py +1 -5
  349. oscura/utils/bitwise.py +118 -0
  350. oscura/{builders → utils/builders}/__init__.py +1 -1
  351. oscura/{comparison → utils/comparison}/__init__.py +6 -6
  352. oscura/{comparison → utils/comparison}/compare.py +202 -101
  353. oscura/{comparison → utils/comparison}/golden.py +83 -63
  354. oscura/{comparison → utils/comparison}/limits.py +313 -89
  355. oscura/{comparison → utils/comparison}/mask.py +151 -45
  356. oscura/{comparison → utils/comparison}/trace_diff.py +1 -1
  357. oscura/{comparison → utils/comparison}/visualization.py +147 -89
  358. oscura/{component → utils/component}/__init__.py +3 -3
  359. oscura/{component → utils/component}/impedance.py +122 -58
  360. oscura/{component → utils/component}/reactive.py +165 -168
  361. oscura/{component → utils/component}/transmission_line.py +3 -3
  362. oscura/{filtering → utils/filtering}/__init__.py +6 -6
  363. oscura/{filtering → utils/filtering}/base.py +1 -1
  364. oscura/{filtering → utils/filtering}/convenience.py +2 -2
  365. oscura/{filtering → utils/filtering}/design.py +169 -93
  366. oscura/{filtering → utils/filtering}/filters.py +2 -2
  367. oscura/{filtering → utils/filtering}/introspection.py +2 -2
  368. oscura/utils/geometry.py +31 -0
  369. oscura/utils/imports.py +184 -0
  370. oscura/utils/lazy.py +1 -1
  371. oscura/{math → utils/math}/__init__.py +2 -2
  372. oscura/{math → utils/math}/arithmetic.py +114 -48
  373. oscura/{math → utils/math}/interpolation.py +139 -106
  374. oscura/utils/memory.py +129 -66
  375. oscura/utils/memory_advanced.py +92 -9
  376. oscura/utils/memory_extensions.py +10 -8
  377. oscura/{optimization → utils/optimization}/__init__.py +1 -1
  378. oscura/{optimization → utils/optimization}/search.py +2 -2
  379. oscura/utils/performance/__init__.py +58 -0
  380. oscura/utils/performance/caching.py +889 -0
  381. oscura/utils/performance/lsh_clustering.py +333 -0
  382. oscura/utils/performance/memory_optimizer.py +699 -0
  383. oscura/utils/performance/optimizations.py +675 -0
  384. oscura/utils/performance/parallel.py +654 -0
  385. oscura/utils/performance/profiling.py +661 -0
  386. oscura/{pipeline → utils/pipeline}/base.py +1 -1
  387. oscura/{pipeline → utils/pipeline}/composition.py +11 -3
  388. oscura/{pipeline → utils/pipeline}/parallel.py +3 -2
  389. oscura/{pipeline → utils/pipeline}/pipeline.py +1 -1
  390. oscura/{pipeline → utils/pipeline}/reverse_engineering.py +412 -221
  391. oscura/{search → utils/search}/__init__.py +3 -3
  392. oscura/{search → utils/search}/anomaly.py +188 -58
  393. oscura/utils/search/context.py +294 -0
  394. oscura/{search → utils/search}/pattern.py +138 -10
  395. oscura/utils/serial.py +51 -0
  396. oscura/utils/storage/__init__.py +61 -0
  397. oscura/utils/storage/database.py +1166 -0
  398. oscura/{streaming → utils/streaming}/chunked.py +302 -143
  399. oscura/{streaming → utils/streaming}/progressive.py +1 -1
  400. oscura/{streaming → utils/streaming}/realtime.py +3 -2
  401. oscura/{triggering → utils/triggering}/__init__.py +6 -6
  402. oscura/{triggering → utils/triggering}/base.py +6 -6
  403. oscura/{triggering → utils/triggering}/edge.py +2 -2
  404. oscura/{triggering → utils/triggering}/pattern.py +2 -2
  405. oscura/{triggering → utils/triggering}/pulse.py +115 -74
  406. oscura/{triggering → utils/triggering}/window.py +2 -2
  407. oscura/utils/validation.py +32 -0
  408. oscura/validation/__init__.py +121 -0
  409. oscura/{compliance → validation/compliance}/__init__.py +5 -5
  410. oscura/{compliance → validation/compliance}/advanced.py +5 -5
  411. oscura/{compliance → validation/compliance}/masks.py +1 -1
  412. oscura/{compliance → validation/compliance}/reporting.py +127 -53
  413. oscura/{compliance → validation/compliance}/testing.py +114 -52
  414. oscura/validation/compliance_tests.py +915 -0
  415. oscura/validation/fuzzer.py +990 -0
  416. oscura/validation/grammar_tests.py +596 -0
  417. oscura/validation/grammar_validator.py +904 -0
  418. oscura/validation/hil_testing.py +977 -0
  419. oscura/{quality → validation/quality}/__init__.py +4 -4
  420. oscura/{quality → validation/quality}/ensemble.py +251 -171
  421. oscura/{quality → validation/quality}/explainer.py +3 -3
  422. oscura/{quality → validation/quality}/scoring.py +1 -1
  423. oscura/{quality → validation/quality}/warnings.py +4 -4
  424. oscura/validation/regression_suite.py +808 -0
  425. oscura/validation/replay.py +788 -0
  426. oscura/{testing → validation/testing}/__init__.py +2 -2
  427. oscura/{testing → validation/testing}/synthetic.py +5 -5
  428. oscura/visualization/__init__.py +9 -0
  429. oscura/visualization/accessibility.py +1 -1
  430. oscura/visualization/annotations.py +64 -67
  431. oscura/visualization/colors.py +7 -7
  432. oscura/visualization/digital.py +180 -81
  433. oscura/visualization/eye.py +236 -85
  434. oscura/visualization/interactive.py +320 -143
  435. oscura/visualization/jitter.py +587 -247
  436. oscura/visualization/layout.py +169 -134
  437. oscura/visualization/optimization.py +103 -52
  438. oscura/visualization/palettes.py +1 -1
  439. oscura/visualization/power.py +427 -211
  440. oscura/visualization/power_extended.py +626 -297
  441. oscura/visualization/presets.py +2 -0
  442. oscura/visualization/protocols.py +495 -181
  443. oscura/visualization/render.py +79 -63
  444. oscura/visualization/reverse_engineering.py +171 -124
  445. oscura/visualization/signal_integrity.py +460 -279
  446. oscura/visualization/specialized.py +190 -100
  447. oscura/visualization/spectral.py +670 -255
  448. oscura/visualization/thumbnails.py +166 -137
  449. oscura/visualization/waveform.py +150 -63
  450. oscura/workflows/__init__.py +3 -0
  451. oscura/{batch → workflows/batch}/__init__.py +5 -5
  452. oscura/{batch → workflows/batch}/advanced.py +150 -75
  453. oscura/workflows/batch/aggregate.py +531 -0
  454. oscura/workflows/batch/analyze.py +236 -0
  455. oscura/{batch → workflows/batch}/logging.py +2 -2
  456. oscura/{batch → workflows/batch}/metrics.py +1 -1
  457. oscura/workflows/complete_re.py +1144 -0
  458. oscura/workflows/compliance.py +44 -54
  459. oscura/workflows/digital.py +197 -51
  460. oscura/workflows/legacy/__init__.py +12 -0
  461. oscura/{workflow → workflows/legacy}/dag.py +4 -1
  462. oscura/workflows/multi_trace.py +9 -9
  463. oscura/workflows/power.py +42 -62
  464. oscura/workflows/protocol.py +82 -49
  465. oscura/workflows/reverse_engineering.py +351 -150
  466. oscura/workflows/signal_integrity.py +157 -82
  467. oscura-0.6.0.dist-info/METADATA +643 -0
  468. oscura-0.6.0.dist-info/RECORD +590 -0
  469. oscura/analyzers/digital/ic_database.py +0 -498
  470. oscura/analyzers/digital/timing_paths.py +0 -339
  471. oscura/analyzers/digital/vintage.py +0 -377
  472. oscura/analyzers/digital/vintage_result.py +0 -148
  473. oscura/analyzers/protocols/parallel_bus.py +0 -449
  474. oscura/batch/aggregate.py +0 -300
  475. oscura/batch/analyze.py +0 -139
  476. oscura/dsl/__init__.py +0 -73
  477. oscura/exceptions.py +0 -59
  478. oscura/exploratory/fuzzy.py +0 -513
  479. oscura/exploratory/sync.py +0 -384
  480. oscura/export/wavedrom.py +0 -430
  481. oscura/exporters/__init__.py +0 -94
  482. oscura/exporters/csv.py +0 -303
  483. oscura/exporters/exporters.py +0 -44
  484. oscura/exporters/hdf5.py +0 -217
  485. oscura/exporters/html_export.py +0 -701
  486. oscura/exporters/json_export.py +0 -338
  487. oscura/exporters/markdown_export.py +0 -367
  488. oscura/exporters/matlab_export.py +0 -354
  489. oscura/exporters/npz_export.py +0 -219
  490. oscura/exporters/spice_export.py +0 -210
  491. oscura/exporters/vintage_logic_csv.py +0 -247
  492. oscura/reporting/vintage_logic_report.py +0 -523
  493. oscura/search/context.py +0 -149
  494. oscura/session/__init__.py +0 -34
  495. oscura/session/annotations.py +0 -289
  496. oscura/session/history.py +0 -313
  497. oscura/session/session.py +0 -520
  498. oscura/visualization/digital_advanced.py +0 -718
  499. oscura/visualization/figure_manager.py +0 -156
  500. oscura/workflow/__init__.py +0 -13
  501. oscura-0.5.0.dist-info/METADATA +0 -407
  502. oscura-0.5.0.dist-info/RECORD +0 -486
  503. /oscura/core/{config.py → config/legacy.py} +0 -0
  504. /oscura/{extensibility → core/extensibility}/__init__.py +0 -0
  505. /oscura/{extensibility → core/extensibility}/registry.py +0 -0
  506. /oscura/{plugins → core/plugins}/isolation.py +0 -0
  507. /oscura/{builders → utils/builders}/signal_builder.py +0 -0
  508. /oscura/{optimization → utils/optimization}/parallel.py +0 -0
  509. /oscura/{pipeline → utils/pipeline}/__init__.py +0 -0
  510. /oscura/{streaming → utils/streaming}/__init__.py +0 -0
  511. {oscura-0.5.0.dist-info → oscura-0.6.0.dist-info}/WHEEL +0 -0
  512. {oscura-0.5.0.dist-info → oscura-0.6.0.dist-info}/entry_points.txt +0 -0
  513. {oscura-0.5.0.dist-info → oscura-0.6.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,212 @@
1
+ """CoAP option definitions and parsing helpers.
2
+
3
+ Provides option number to name mappings, content format definitions,
4
+ and helper functions for decoding CoAP options.
5
+
6
+ References:
7
+ RFC 7252 Section 5.10: Option Definitions
8
+ RFC 7252 Section 12.3: CoAP Content-Formats Registry
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from typing import Any, ClassVar
14
+
15
+ # CoAP option number to name mapping (RFC 7252)
16
+ OPTIONS: dict[int, str] = {
17
+ 1: "If-Match",
18
+ 3: "Uri-Host",
19
+ 4: "ETag",
20
+ 5: "If-None-Match",
21
+ 6: "Observe", # RFC 7641
22
+ 7: "Uri-Port",
23
+ 8: "Location-Path",
24
+ 11: "Uri-Path",
25
+ 12: "Content-Format",
26
+ 14: "Max-Age",
27
+ 15: "Uri-Query",
28
+ 17: "Accept",
29
+ 20: "Location-Query",
30
+ 23: "Block2", # RFC 7959
31
+ 27: "Block1", # RFC 7959
32
+ 28: "Size2", # RFC 7959
33
+ 35: "Proxy-Uri",
34
+ 39: "Proxy-Scheme",
35
+ 60: "Size1", # RFC 7959
36
+ }
37
+
38
+ # CoAP content format codes (RFC 7252)
39
+ CONTENT_FORMATS: dict[int, str] = {
40
+ 0: "text/plain; charset=utf-8",
41
+ 40: "application/link-format",
42
+ 41: "application/xml",
43
+ 42: "application/octet-stream",
44
+ 47: "application/exi",
45
+ 50: "application/json",
46
+ 60: "application/cbor",
47
+ # Additional formats from IANA registry
48
+ 100: "application/senml+json",
49
+ 101: "application/sensml+json",
50
+ 110: "application/senml+cbor",
51
+ 111: "application/sensml+cbor",
52
+ 112: "application/senml-exi",
53
+ 113: "application/sensml-exi",
54
+ 256: "application/coap-group+json",
55
+ 10000: "application/pkcs7-mime; smime-type=server-generated-key",
56
+ 10001: "application/pkcs7-mime; smime-type=certs-only",
57
+ 10002: "application/pkcs8",
58
+ 10003: "application/csrattrs",
59
+ 10004: "application/pkcs10",
60
+ 10005: "application/pkix-cert",
61
+ }
62
+
63
+
64
+ class OptionParser:
65
+ """Helper class for parsing CoAP options.
66
+
67
+ Provides methods for decoding option values based on their option number
68
+ and handling CoAP option delta encoding.
69
+
70
+ Example:
71
+ >>> parser = OptionParser()
72
+ >>> value = parser.decode_value(11, b"temperature")
73
+ >>> print(value)
74
+ 'temperature'
75
+ """
76
+
77
+ # Empty format options (no value)
78
+ EMPTY_OPTIONS: ClassVar[set[int]] = {5} # If-None-Match
79
+
80
+ # Opaque options (bytes)
81
+ OPAQUE_OPTIONS: ClassVar[set[int]] = {1, 4} # If-Match, ETag
82
+
83
+ # String options (UTF-8)
84
+ STRING_OPTIONS: ClassVar[set[int]] = {3, 8, 11, 15, 20, 35, 39}
85
+
86
+ # Unsigned integer options
87
+ UINT_OPTIONS: ClassVar[set[int]] = {6, 7, 12, 14, 17, 23, 27, 28, 60}
88
+
89
+ @staticmethod
90
+ def decode_value(option_num: int, value: bytes) -> str | int | bytes:
91
+ """Decode option value based on option number.
92
+
93
+ Args:
94
+ option_num: CoAP option number.
95
+ value: Raw option value bytes.
96
+
97
+ Returns:
98
+ Decoded value as string, int, or bytes depending on option type.
99
+
100
+ Example:
101
+ >>> parser = OptionParser()
102
+ >>> parser.decode_value(11, b"sensor")
103
+ 'sensor'
104
+ >>> parser.decode_value(12, b"\\x00\\x32")
105
+ 50
106
+ """
107
+ if option_num in OptionParser.EMPTY_OPTIONS:
108
+ return b""
109
+
110
+ if not value:
111
+ # Empty value - return appropriate type
112
+ if option_num in OptionParser.STRING_OPTIONS:
113
+ return ""
114
+ if option_num in OptionParser.UINT_OPTIONS:
115
+ return 0
116
+ return b""
117
+
118
+ if option_num in OptionParser.STRING_OPTIONS:
119
+ try:
120
+ return value.decode("utf-8")
121
+ except UnicodeDecodeError:
122
+ return value
123
+
124
+ if option_num in OptionParser.UINT_OPTIONS:
125
+ return int.from_bytes(value, "big")
126
+
127
+ # Default to opaque (bytes)
128
+ return value
129
+
130
+ @staticmethod
131
+ def parse_extended_value(base: int, data: bytes, offset: int) -> tuple[int, int]:
132
+ """Parse extended delta or length value.
133
+
134
+ CoAP uses extended encoding for delta/length values >= 13:
135
+ - 13: value is (base value from next byte) + 13
136
+ - 14: value is (base value from next 2 bytes) + 269
137
+ - 15: reserved (payload marker or error)
138
+
139
+ Args:
140
+ base: Base value (0-15) from option header nibble.
141
+ data: Complete message data.
142
+ offset: Current offset in data.
143
+
144
+ Returns:
145
+ Tuple of (actual_value, bytes_consumed).
146
+
147
+ Raises:
148
+ ValueError: If extended format is invalid or data insufficient.
149
+
150
+ Example:
151
+ >>> parser = OptionParser()
152
+ >>> value, consumed = parser.parse_extended_value(13, b"\\x05\\x00", 0)
153
+ >>> value, consumed
154
+ (18, 1)
155
+ """
156
+ if base < 13:
157
+ return base, 0
158
+
159
+ if base == 13:
160
+ if len(data) < offset + 1:
161
+ raise ValueError("Insufficient data for extended option delta/length")
162
+ return data[offset] + 13, 1
163
+
164
+ if base == 14:
165
+ if len(data) < offset + 2:
166
+ raise ValueError("Insufficient data for extended option delta/length")
167
+ return int.from_bytes(data[offset : offset + 2], "big") + 269, 2
168
+
169
+ # base == 15
170
+ raise ValueError("Invalid option delta/length value (15)")
171
+
172
+
173
+ def format_block_option(value: int) -> dict[str, Any]:
174
+ """Parse Block1/Block2 option value.
175
+
176
+ Block options encode: NUM (block number), M (more flag), SZX (size exponent).
177
+
178
+ Format (variable length, 1-3 bytes):
179
+ 0 1 2 3 4 5 6 7
180
+ +-+-+-+-+-+-+-+-+
181
+ | NUM |M| SZX |
182
+ +-+-+-+-+-+-+-+-+
183
+
184
+ Args:
185
+ value: Block option value as unsigned integer.
186
+
187
+ Returns:
188
+ Dictionary with 'num' (block number), 'more' (more flag), and 'size' (block size).
189
+
190
+ Example:
191
+ >>> result = format_block_option(0x08) # Block 0, no more, size 16
192
+ >>> result['num'], result['more'], result['size']
193
+ (0, False, 16)
194
+ """
195
+ num = value >> 4
196
+ more = bool((value >> 3) & 0x01)
197
+ szx = value & 0x07
198
+ size = 2 ** (szx + 4) # Size = 2^(SZX+4), range: 16-1024 bytes
199
+
200
+ return {
201
+ "num": num,
202
+ "more": more,
203
+ "size": size,
204
+ }
205
+
206
+
207
+ __all__ = [
208
+ "CONTENT_FORMATS",
209
+ "OPTIONS",
210
+ "OptionParser",
211
+ "format_block_option",
212
+ ]
@@ -0,0 +1,21 @@
1
+ """LoRaWAN protocol decoder package.
2
+
3
+ Provides LoRaWAN MAC layer parsing and payload decryption support.
4
+ """
5
+
6
+ from oscura.iot.lorawan.decoder import (
7
+ LoRaWANDecoder,
8
+ LoRaWANFrame,
9
+ LoRaWANKeys,
10
+ decode_lorawan_frame,
11
+ )
12
+ from oscura.iot.lorawan.mac_commands import MAC_COMMANDS, parse_mac_command
13
+
14
+ __all__ = [
15
+ "MAC_COMMANDS",
16
+ "LoRaWANDecoder",
17
+ "LoRaWANFrame",
18
+ "LoRaWANKeys",
19
+ "decode_lorawan_frame",
20
+ "parse_mac_command",
21
+ ]
@@ -0,0 +1,206 @@
1
+ """LoRaWAN cryptographic operations.
2
+
3
+ This module provides AES-128 encryption/decryption and CMAC operations
4
+ for LoRaWAN payload security as defined in LoRaWAN Specification 1.0.3.
5
+
6
+ Security:
7
+ Uses cryptography library (modern, actively maintained) instead of deprecated PyCrypto.
8
+ All cryptographic operations follow LoRaWAN 1.0.3 specification.
9
+
10
+ References:
11
+ LoRaWAN Specification 1.0.3: https://lora-alliance.org/resource_hub/lorawan-specification-v1-0-3/
12
+ Section 4.3.3 - MAC Frame Payload Encryption (FRMPayload)
13
+ Section 4.4 - Message Integrity Code (MIC)
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ from typing import Literal
19
+
20
+
21
+ def decrypt_payload(
22
+ frm_payload: bytes,
23
+ key: bytes,
24
+ dev_addr: int,
25
+ fcnt: int,
26
+ direction: Literal["up", "down"],
27
+ ) -> bytes:
28
+ """Decrypt LoRaWAN FRMPayload using AES-128 in CTR mode.
29
+
30
+ Args:
31
+ frm_payload: Encrypted payload bytes.
32
+ key: AES-128 key (16 bytes) - AppSKey or NwkSKey.
33
+ dev_addr: Device address (4 bytes).
34
+ fcnt: Frame counter.
35
+ direction: Direction "up" (uplink) or "down" (downlink).
36
+
37
+ Returns:
38
+ Decrypted payload bytes.
39
+
40
+ Raises:
41
+ ValueError: If key is not 16 bytes.
42
+ ImportError: If cryptography library is not available.
43
+
44
+ Example:
45
+ >>> key = bytes.fromhex("2B7E151628AED2A6ABF7158809CF4F3C")
46
+ >>> encrypted = bytes.fromhex("0123456789ABCDEF")
47
+ >>> decrypted = decrypt_payload(encrypted, key, 0x01020304, 1, "up")
48
+ """
49
+ try:
50
+ from cryptography.hazmat.backends import default_backend
51
+ from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
52
+ except ImportError as exc:
53
+ msg = (
54
+ "cryptography library is required for LoRaWAN encryption. "
55
+ "Install with: pip install cryptography"
56
+ )
57
+ raise ImportError(msg) from exc
58
+
59
+ if len(key) != 16:
60
+ msg = f"Key must be 16 bytes, got {len(key)}"
61
+ raise ValueError(msg)
62
+
63
+ if len(frm_payload) == 0:
64
+ return b""
65
+
66
+ # Direction byte: 0x00 for uplink, 0x01 for downlink
67
+ dir_byte = 0x00 if direction == "up" else 0x01
68
+
69
+ # Number of 16-byte blocks needed
70
+ num_blocks = (len(frm_payload) + 15) // 16
71
+
72
+ # Generate keystream by encrypting counter blocks
73
+ keystream = b""
74
+ for i in range(num_blocks):
75
+ # Build encryption block A_i (16 bytes)
76
+ # A_i = 0x01 | 0x00000000 | Dir | DevAddr | FCnt | 0x00 | i
77
+ a = bytearray(16)
78
+ a[0] = 0x01 # Encryption flag
79
+ # a[1:5] = 0x00000000 (already zero)
80
+ a[5] = dir_byte
81
+ a[6:10] = dev_addr.to_bytes(4, "little")
82
+ a[10:14] = fcnt.to_bytes(4, "little")
83
+ # a[14] = 0x00 (already zero)
84
+ a[15] = i + 1
85
+
86
+ # Encrypt the block using AES in ECB mode
87
+ # Security: ECB mode is required by LoRaWAN spec (Section 4.3.3)
88
+ # Each counter block is unique, so ECB mode is safe here
89
+ cipher = Cipher(algorithms.AES(key), modes.ECB(), backend=default_backend())
90
+ encryptor = cipher.encryptor()
91
+ keystream += encryptor.update(bytes(a)) + encryptor.finalize()
92
+
93
+ # XOR payload with keystream
94
+ decrypted = bytes(
95
+ p ^ k for p, k in zip(frm_payload, keystream[: len(frm_payload)], strict=False)
96
+ )
97
+ return decrypted
98
+
99
+
100
+ def compute_mic(
101
+ data: bytes,
102
+ key: bytes,
103
+ dev_addr: int,
104
+ fcnt: int,
105
+ direction: Literal["up", "down"],
106
+ ) -> int:
107
+ """Compute LoRaWAN Message Integrity Code (MIC) using AES-CMAC.
108
+
109
+ Args:
110
+ data: Message data (MHDR | FHDR | FPort | FRMPayload).
111
+ key: Network session key (NwkSKey, 16 bytes).
112
+ dev_addr: Device address (4 bytes).
113
+ fcnt: Frame counter.
114
+ direction: Direction "up" (uplink) or "down" (downlink).
115
+
116
+ Returns:
117
+ 32-bit MIC value.
118
+
119
+ Raises:
120
+ ValueError: If key is not 16 bytes.
121
+ ImportError: If cryptography library is not available.
122
+
123
+ Example:
124
+ >>> key = bytes.fromhex("2B7E151628AED2A6ABF7158809CF4F3C")
125
+ >>> data = bytes.fromhex("40010203040001000100")
126
+ >>> mic = compute_mic(data, key, 0x01020304, 1, "up")
127
+ """
128
+ try:
129
+ from cryptography.hazmat.backends import default_backend
130
+ from cryptography.hazmat.primitives import cmac
131
+ from cryptography.hazmat.primitives.ciphers import algorithms
132
+ except ImportError as exc:
133
+ msg = (
134
+ "cryptography library is required for LoRaWAN MIC computation. "
135
+ "Install with: pip install cryptography"
136
+ )
137
+ raise ImportError(msg) from exc
138
+
139
+ if len(key) != 16:
140
+ msg = f"Key must be 16 bytes, got {len(key)}"
141
+ raise ValueError(msg)
142
+
143
+ # Direction byte: 0x00 for uplink, 0x01 for downlink
144
+ dir_byte = 0x00 if direction == "up" else 0x01
145
+
146
+ # Build MIC computation block B_0 (16 bytes)
147
+ # B_0 = 0x49 | 0x00000000 | Dir | DevAddr | FCnt | 0x00 | len(msg)
148
+ b0 = bytearray(16)
149
+ b0[0] = 0x49 # MIC flag
150
+ # b0[1:5] = 0x00000000 (already zero)
151
+ b0[5] = dir_byte
152
+ b0[6:10] = dev_addr.to_bytes(4, "little")
153
+ b0[10:14] = fcnt.to_bytes(4, "little")
154
+ # b0[14] = 0x00 (already zero)
155
+ b0[15] = len(data)
156
+
157
+ # Compute CMAC over B_0 | msg
158
+ c = cmac.CMAC(algorithms.AES(key), backend=default_backend())
159
+ c.update(bytes(b0))
160
+ c.update(data)
161
+ mac = c.finalize()
162
+
163
+ # Return first 4 bytes as 32-bit integer (little-endian)
164
+ mic = int.from_bytes(mac[:4], "little")
165
+ return mic
166
+
167
+
168
+ def verify_mic(
169
+ data: bytes,
170
+ received_mic: int,
171
+ key: bytes,
172
+ dev_addr: int,
173
+ fcnt: int,
174
+ direction: Literal["up", "down"],
175
+ ) -> bool:
176
+ """Verify LoRaWAN Message Integrity Code (MIC).
177
+
178
+ Args:
179
+ data: Message data (MHDR | FHDR | FPort | FRMPayload).
180
+ received_mic: Received MIC value.
181
+ key: Network session key (NwkSKey, 16 bytes).
182
+ dev_addr: Device address (4 bytes).
183
+ fcnt: Frame counter.
184
+ direction: Direction "up" (uplink) or "down" (downlink).
185
+
186
+ Returns:
187
+ True if MIC is valid, False otherwise.
188
+
189
+ Example:
190
+ >>> key = bytes.fromhex("2B7E151628AED2A6ABF7158809CF4F3C")
191
+ >>> data = bytes.fromhex("40010203040001000100")
192
+ >>> is_valid = verify_mic(data, 0x12345678, key, 0x01020304, 1, "up")
193
+ """
194
+ try:
195
+ computed_mic = compute_mic(data, key, dev_addr, fcnt, direction)
196
+ return computed_mic == received_mic
197
+ except (ImportError, ValueError):
198
+ # If crypto is unavailable, we can't verify
199
+ return False
200
+
201
+
202
+ __all__ = [
203
+ "compute_mic",
204
+ "decrypt_payload",
205
+ "verify_mic",
206
+ ]