oscura 0.0.1__py3-none-any.whl → 0.1.1__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 (465) hide show
  1. oscura/__init__.py +813 -8
  2. oscura/__main__.py +392 -0
  3. oscura/analyzers/__init__.py +37 -0
  4. oscura/analyzers/digital/__init__.py +177 -0
  5. oscura/analyzers/digital/bus.py +691 -0
  6. oscura/analyzers/digital/clock.py +805 -0
  7. oscura/analyzers/digital/correlation.py +720 -0
  8. oscura/analyzers/digital/edges.py +632 -0
  9. oscura/analyzers/digital/extraction.py +413 -0
  10. oscura/analyzers/digital/quality.py +878 -0
  11. oscura/analyzers/digital/signal_quality.py +877 -0
  12. oscura/analyzers/digital/thresholds.py +708 -0
  13. oscura/analyzers/digital/timing.py +1104 -0
  14. oscura/analyzers/eye/__init__.py +46 -0
  15. oscura/analyzers/eye/diagram.py +434 -0
  16. oscura/analyzers/eye/metrics.py +555 -0
  17. oscura/analyzers/jitter/__init__.py +83 -0
  18. oscura/analyzers/jitter/ber.py +333 -0
  19. oscura/analyzers/jitter/decomposition.py +759 -0
  20. oscura/analyzers/jitter/measurements.py +413 -0
  21. oscura/analyzers/jitter/spectrum.py +220 -0
  22. oscura/analyzers/measurements.py +40 -0
  23. oscura/analyzers/packet/__init__.py +171 -0
  24. oscura/analyzers/packet/daq.py +1077 -0
  25. oscura/analyzers/packet/metrics.py +437 -0
  26. oscura/analyzers/packet/parser.py +327 -0
  27. oscura/analyzers/packet/payload.py +2156 -0
  28. oscura/analyzers/packet/payload_analysis.py +1312 -0
  29. oscura/analyzers/packet/payload_extraction.py +236 -0
  30. oscura/analyzers/packet/payload_patterns.py +670 -0
  31. oscura/analyzers/packet/stream.py +359 -0
  32. oscura/analyzers/patterns/__init__.py +266 -0
  33. oscura/analyzers/patterns/clustering.py +1036 -0
  34. oscura/analyzers/patterns/discovery.py +539 -0
  35. oscura/analyzers/patterns/learning.py +797 -0
  36. oscura/analyzers/patterns/matching.py +1091 -0
  37. oscura/analyzers/patterns/periodic.py +650 -0
  38. oscura/analyzers/patterns/sequences.py +767 -0
  39. oscura/analyzers/power/__init__.py +116 -0
  40. oscura/analyzers/power/ac_power.py +391 -0
  41. oscura/analyzers/power/basic.py +383 -0
  42. oscura/analyzers/power/conduction.py +314 -0
  43. oscura/analyzers/power/efficiency.py +297 -0
  44. oscura/analyzers/power/ripple.py +356 -0
  45. oscura/analyzers/power/soa.py +372 -0
  46. oscura/analyzers/power/switching.py +479 -0
  47. oscura/analyzers/protocol/__init__.py +150 -0
  48. oscura/analyzers/protocols/__init__.py +150 -0
  49. oscura/analyzers/protocols/base.py +500 -0
  50. oscura/analyzers/protocols/can.py +620 -0
  51. oscura/analyzers/protocols/can_fd.py +448 -0
  52. oscura/analyzers/protocols/flexray.py +405 -0
  53. oscura/analyzers/protocols/hdlc.py +399 -0
  54. oscura/analyzers/protocols/i2c.py +368 -0
  55. oscura/analyzers/protocols/i2s.py +296 -0
  56. oscura/analyzers/protocols/jtag.py +393 -0
  57. oscura/analyzers/protocols/lin.py +445 -0
  58. oscura/analyzers/protocols/manchester.py +333 -0
  59. oscura/analyzers/protocols/onewire.py +501 -0
  60. oscura/analyzers/protocols/spi.py +334 -0
  61. oscura/analyzers/protocols/swd.py +325 -0
  62. oscura/analyzers/protocols/uart.py +393 -0
  63. oscura/analyzers/protocols/usb.py +495 -0
  64. oscura/analyzers/signal_integrity/__init__.py +63 -0
  65. oscura/analyzers/signal_integrity/embedding.py +294 -0
  66. oscura/analyzers/signal_integrity/equalization.py +370 -0
  67. oscura/analyzers/signal_integrity/sparams.py +484 -0
  68. oscura/analyzers/spectral/__init__.py +53 -0
  69. oscura/analyzers/spectral/chunked.py +273 -0
  70. oscura/analyzers/spectral/chunked_fft.py +571 -0
  71. oscura/analyzers/spectral/chunked_wavelet.py +391 -0
  72. oscura/analyzers/spectral/fft.py +92 -0
  73. oscura/analyzers/statistical/__init__.py +250 -0
  74. oscura/analyzers/statistical/checksum.py +923 -0
  75. oscura/analyzers/statistical/chunked_corr.py +228 -0
  76. oscura/analyzers/statistical/classification.py +778 -0
  77. oscura/analyzers/statistical/entropy.py +1113 -0
  78. oscura/analyzers/statistical/ngrams.py +614 -0
  79. oscura/analyzers/statistics/__init__.py +119 -0
  80. oscura/analyzers/statistics/advanced.py +885 -0
  81. oscura/analyzers/statistics/basic.py +263 -0
  82. oscura/analyzers/statistics/correlation.py +630 -0
  83. oscura/analyzers/statistics/distribution.py +298 -0
  84. oscura/analyzers/statistics/outliers.py +463 -0
  85. oscura/analyzers/statistics/streaming.py +93 -0
  86. oscura/analyzers/statistics/trend.py +520 -0
  87. oscura/analyzers/validation.py +598 -0
  88. oscura/analyzers/waveform/__init__.py +36 -0
  89. oscura/analyzers/waveform/measurements.py +943 -0
  90. oscura/analyzers/waveform/measurements_with_uncertainty.py +371 -0
  91. oscura/analyzers/waveform/spectral.py +1689 -0
  92. oscura/analyzers/waveform/wavelets.py +298 -0
  93. oscura/api/__init__.py +62 -0
  94. oscura/api/dsl.py +538 -0
  95. oscura/api/fluent.py +571 -0
  96. oscura/api/operators.py +498 -0
  97. oscura/api/optimization.py +392 -0
  98. oscura/api/profiling.py +396 -0
  99. oscura/automotive/__init__.py +73 -0
  100. oscura/automotive/can/__init__.py +52 -0
  101. oscura/automotive/can/analysis.py +356 -0
  102. oscura/automotive/can/checksum.py +250 -0
  103. oscura/automotive/can/correlation.py +212 -0
  104. oscura/automotive/can/discovery.py +355 -0
  105. oscura/automotive/can/message_wrapper.py +375 -0
  106. oscura/automotive/can/models.py +385 -0
  107. oscura/automotive/can/patterns.py +381 -0
  108. oscura/automotive/can/session.py +452 -0
  109. oscura/automotive/can/state_machine.py +300 -0
  110. oscura/automotive/can/stimulus_response.py +461 -0
  111. oscura/automotive/dbc/__init__.py +15 -0
  112. oscura/automotive/dbc/generator.py +156 -0
  113. oscura/automotive/dbc/parser.py +146 -0
  114. oscura/automotive/dtc/__init__.py +30 -0
  115. oscura/automotive/dtc/database.py +3036 -0
  116. oscura/automotive/j1939/__init__.py +14 -0
  117. oscura/automotive/j1939/decoder.py +745 -0
  118. oscura/automotive/loaders/__init__.py +35 -0
  119. oscura/automotive/loaders/asc.py +98 -0
  120. oscura/automotive/loaders/blf.py +77 -0
  121. oscura/automotive/loaders/csv_can.py +136 -0
  122. oscura/automotive/loaders/dispatcher.py +136 -0
  123. oscura/automotive/loaders/mdf.py +331 -0
  124. oscura/automotive/loaders/pcap.py +132 -0
  125. oscura/automotive/obd/__init__.py +14 -0
  126. oscura/automotive/obd/decoder.py +707 -0
  127. oscura/automotive/uds/__init__.py +48 -0
  128. oscura/automotive/uds/decoder.py +265 -0
  129. oscura/automotive/uds/models.py +64 -0
  130. oscura/automotive/visualization.py +369 -0
  131. oscura/batch/__init__.py +55 -0
  132. oscura/batch/advanced.py +627 -0
  133. oscura/batch/aggregate.py +300 -0
  134. oscura/batch/analyze.py +139 -0
  135. oscura/batch/logging.py +487 -0
  136. oscura/batch/metrics.py +556 -0
  137. oscura/builders/__init__.py +41 -0
  138. oscura/builders/signal_builder.py +1131 -0
  139. oscura/cli/__init__.py +14 -0
  140. oscura/cli/batch.py +339 -0
  141. oscura/cli/characterize.py +273 -0
  142. oscura/cli/compare.py +775 -0
  143. oscura/cli/decode.py +551 -0
  144. oscura/cli/main.py +247 -0
  145. oscura/cli/shell.py +350 -0
  146. oscura/comparison/__init__.py +66 -0
  147. oscura/comparison/compare.py +397 -0
  148. oscura/comparison/golden.py +487 -0
  149. oscura/comparison/limits.py +391 -0
  150. oscura/comparison/mask.py +434 -0
  151. oscura/comparison/trace_diff.py +30 -0
  152. oscura/comparison/visualization.py +481 -0
  153. oscura/compliance/__init__.py +70 -0
  154. oscura/compliance/advanced.py +756 -0
  155. oscura/compliance/masks.py +363 -0
  156. oscura/compliance/reporting.py +483 -0
  157. oscura/compliance/testing.py +298 -0
  158. oscura/component/__init__.py +38 -0
  159. oscura/component/impedance.py +365 -0
  160. oscura/component/reactive.py +598 -0
  161. oscura/component/transmission_line.py +312 -0
  162. oscura/config/__init__.py +191 -0
  163. oscura/config/defaults.py +254 -0
  164. oscura/config/loader.py +348 -0
  165. oscura/config/memory.py +271 -0
  166. oscura/config/migration.py +458 -0
  167. oscura/config/pipeline.py +1077 -0
  168. oscura/config/preferences.py +530 -0
  169. oscura/config/protocol.py +875 -0
  170. oscura/config/schema.py +713 -0
  171. oscura/config/settings.py +420 -0
  172. oscura/config/thresholds.py +599 -0
  173. oscura/convenience.py +457 -0
  174. oscura/core/__init__.py +299 -0
  175. oscura/core/audit.py +457 -0
  176. oscura/core/backend_selector.py +405 -0
  177. oscura/core/cache.py +590 -0
  178. oscura/core/cancellation.py +439 -0
  179. oscura/core/confidence.py +225 -0
  180. oscura/core/config.py +506 -0
  181. oscura/core/correlation.py +216 -0
  182. oscura/core/cross_domain.py +422 -0
  183. oscura/core/debug.py +301 -0
  184. oscura/core/edge_cases.py +541 -0
  185. oscura/core/exceptions.py +535 -0
  186. oscura/core/gpu_backend.py +523 -0
  187. oscura/core/lazy.py +832 -0
  188. oscura/core/log_query.py +540 -0
  189. oscura/core/logging.py +931 -0
  190. oscura/core/logging_advanced.py +952 -0
  191. oscura/core/memoize.py +171 -0
  192. oscura/core/memory_check.py +274 -0
  193. oscura/core/memory_guard.py +290 -0
  194. oscura/core/memory_limits.py +336 -0
  195. oscura/core/memory_monitor.py +453 -0
  196. oscura/core/memory_progress.py +465 -0
  197. oscura/core/memory_warnings.py +315 -0
  198. oscura/core/numba_backend.py +362 -0
  199. oscura/core/performance.py +352 -0
  200. oscura/core/progress.py +524 -0
  201. oscura/core/provenance.py +358 -0
  202. oscura/core/results.py +331 -0
  203. oscura/core/types.py +504 -0
  204. oscura/core/uncertainty.py +383 -0
  205. oscura/discovery/__init__.py +52 -0
  206. oscura/discovery/anomaly_detector.py +672 -0
  207. oscura/discovery/auto_decoder.py +415 -0
  208. oscura/discovery/comparison.py +497 -0
  209. oscura/discovery/quality_validator.py +528 -0
  210. oscura/discovery/signal_detector.py +769 -0
  211. oscura/dsl/__init__.py +73 -0
  212. oscura/dsl/commands.py +246 -0
  213. oscura/dsl/interpreter.py +455 -0
  214. oscura/dsl/parser.py +689 -0
  215. oscura/dsl/repl.py +172 -0
  216. oscura/exceptions.py +59 -0
  217. oscura/exploratory/__init__.py +111 -0
  218. oscura/exploratory/error_recovery.py +642 -0
  219. oscura/exploratory/fuzzy.py +513 -0
  220. oscura/exploratory/fuzzy_advanced.py +786 -0
  221. oscura/exploratory/legacy.py +831 -0
  222. oscura/exploratory/parse.py +358 -0
  223. oscura/exploratory/recovery.py +275 -0
  224. oscura/exploratory/sync.py +382 -0
  225. oscura/exploratory/unknown.py +707 -0
  226. oscura/export/__init__.py +25 -0
  227. oscura/export/wireshark/README.md +265 -0
  228. oscura/export/wireshark/__init__.py +47 -0
  229. oscura/export/wireshark/generator.py +312 -0
  230. oscura/export/wireshark/lua_builder.py +159 -0
  231. oscura/export/wireshark/templates/dissector.lua.j2 +92 -0
  232. oscura/export/wireshark/type_mapping.py +165 -0
  233. oscura/export/wireshark/validator.py +105 -0
  234. oscura/exporters/__init__.py +94 -0
  235. oscura/exporters/csv.py +303 -0
  236. oscura/exporters/exporters.py +44 -0
  237. oscura/exporters/hdf5.py +219 -0
  238. oscura/exporters/html_export.py +701 -0
  239. oscura/exporters/json_export.py +291 -0
  240. oscura/exporters/markdown_export.py +367 -0
  241. oscura/exporters/matlab_export.py +354 -0
  242. oscura/exporters/npz_export.py +219 -0
  243. oscura/exporters/spice_export.py +210 -0
  244. oscura/extensibility/__init__.py +131 -0
  245. oscura/extensibility/docs.py +752 -0
  246. oscura/extensibility/extensions.py +1125 -0
  247. oscura/extensibility/logging.py +259 -0
  248. oscura/extensibility/measurements.py +485 -0
  249. oscura/extensibility/plugins.py +414 -0
  250. oscura/extensibility/registry.py +346 -0
  251. oscura/extensibility/templates.py +913 -0
  252. oscura/extensibility/validation.py +651 -0
  253. oscura/filtering/__init__.py +89 -0
  254. oscura/filtering/base.py +563 -0
  255. oscura/filtering/convenience.py +564 -0
  256. oscura/filtering/design.py +725 -0
  257. oscura/filtering/filters.py +32 -0
  258. oscura/filtering/introspection.py +605 -0
  259. oscura/guidance/__init__.py +24 -0
  260. oscura/guidance/recommender.py +429 -0
  261. oscura/guidance/wizard.py +518 -0
  262. oscura/inference/__init__.py +251 -0
  263. oscura/inference/active_learning/README.md +153 -0
  264. oscura/inference/active_learning/__init__.py +38 -0
  265. oscura/inference/active_learning/lstar.py +257 -0
  266. oscura/inference/active_learning/observation_table.py +230 -0
  267. oscura/inference/active_learning/oracle.py +78 -0
  268. oscura/inference/active_learning/teachers/__init__.py +15 -0
  269. oscura/inference/active_learning/teachers/simulator.py +192 -0
  270. oscura/inference/adaptive_tuning.py +453 -0
  271. oscura/inference/alignment.py +653 -0
  272. oscura/inference/bayesian.py +943 -0
  273. oscura/inference/binary.py +1016 -0
  274. oscura/inference/crc_reverse.py +711 -0
  275. oscura/inference/logic.py +288 -0
  276. oscura/inference/message_format.py +1305 -0
  277. oscura/inference/protocol.py +417 -0
  278. oscura/inference/protocol_dsl.py +1084 -0
  279. oscura/inference/protocol_library.py +1230 -0
  280. oscura/inference/sequences.py +809 -0
  281. oscura/inference/signal_intelligence.py +1509 -0
  282. oscura/inference/spectral.py +215 -0
  283. oscura/inference/state_machine.py +634 -0
  284. oscura/inference/stream.py +918 -0
  285. oscura/integrations/__init__.py +59 -0
  286. oscura/integrations/llm.py +1827 -0
  287. oscura/jupyter/__init__.py +32 -0
  288. oscura/jupyter/display.py +268 -0
  289. oscura/jupyter/magic.py +334 -0
  290. oscura/loaders/__init__.py +526 -0
  291. oscura/loaders/binary.py +69 -0
  292. oscura/loaders/configurable.py +1255 -0
  293. oscura/loaders/csv.py +26 -0
  294. oscura/loaders/csv_loader.py +473 -0
  295. oscura/loaders/hdf5.py +9 -0
  296. oscura/loaders/hdf5_loader.py +510 -0
  297. oscura/loaders/lazy.py +370 -0
  298. oscura/loaders/mmap_loader.py +583 -0
  299. oscura/loaders/numpy_loader.py +436 -0
  300. oscura/loaders/pcap.py +432 -0
  301. oscura/loaders/preprocessing.py +368 -0
  302. oscura/loaders/rigol.py +287 -0
  303. oscura/loaders/sigrok.py +321 -0
  304. oscura/loaders/tdms.py +367 -0
  305. oscura/loaders/tektronix.py +711 -0
  306. oscura/loaders/validation.py +584 -0
  307. oscura/loaders/vcd.py +464 -0
  308. oscura/loaders/wav.py +233 -0
  309. oscura/math/__init__.py +45 -0
  310. oscura/math/arithmetic.py +824 -0
  311. oscura/math/interpolation.py +413 -0
  312. oscura/onboarding/__init__.py +39 -0
  313. oscura/onboarding/help.py +498 -0
  314. oscura/onboarding/tutorials.py +405 -0
  315. oscura/onboarding/wizard.py +466 -0
  316. oscura/optimization/__init__.py +19 -0
  317. oscura/optimization/parallel.py +440 -0
  318. oscura/optimization/search.py +532 -0
  319. oscura/pipeline/__init__.py +43 -0
  320. oscura/pipeline/base.py +338 -0
  321. oscura/pipeline/composition.py +242 -0
  322. oscura/pipeline/parallel.py +448 -0
  323. oscura/pipeline/pipeline.py +375 -0
  324. oscura/pipeline/reverse_engineering.py +1119 -0
  325. oscura/plugins/__init__.py +122 -0
  326. oscura/plugins/base.py +272 -0
  327. oscura/plugins/cli.py +497 -0
  328. oscura/plugins/discovery.py +411 -0
  329. oscura/plugins/isolation.py +418 -0
  330. oscura/plugins/lifecycle.py +959 -0
  331. oscura/plugins/manager.py +493 -0
  332. oscura/plugins/registry.py +421 -0
  333. oscura/plugins/versioning.py +372 -0
  334. oscura/py.typed +0 -0
  335. oscura/quality/__init__.py +65 -0
  336. oscura/quality/ensemble.py +740 -0
  337. oscura/quality/explainer.py +338 -0
  338. oscura/quality/scoring.py +616 -0
  339. oscura/quality/warnings.py +456 -0
  340. oscura/reporting/__init__.py +248 -0
  341. oscura/reporting/advanced.py +1234 -0
  342. oscura/reporting/analyze.py +448 -0
  343. oscura/reporting/argument_preparer.py +596 -0
  344. oscura/reporting/auto_report.py +507 -0
  345. oscura/reporting/batch.py +615 -0
  346. oscura/reporting/chart_selection.py +223 -0
  347. oscura/reporting/comparison.py +330 -0
  348. oscura/reporting/config.py +615 -0
  349. oscura/reporting/content/__init__.py +39 -0
  350. oscura/reporting/content/executive.py +127 -0
  351. oscura/reporting/content/filtering.py +191 -0
  352. oscura/reporting/content/minimal.py +257 -0
  353. oscura/reporting/content/verbosity.py +162 -0
  354. oscura/reporting/core.py +508 -0
  355. oscura/reporting/core_formats/__init__.py +17 -0
  356. oscura/reporting/core_formats/multi_format.py +210 -0
  357. oscura/reporting/engine.py +836 -0
  358. oscura/reporting/export.py +366 -0
  359. oscura/reporting/formatting/__init__.py +129 -0
  360. oscura/reporting/formatting/emphasis.py +81 -0
  361. oscura/reporting/formatting/numbers.py +403 -0
  362. oscura/reporting/formatting/standards.py +55 -0
  363. oscura/reporting/formatting.py +466 -0
  364. oscura/reporting/html.py +578 -0
  365. oscura/reporting/index.py +590 -0
  366. oscura/reporting/multichannel.py +296 -0
  367. oscura/reporting/output.py +379 -0
  368. oscura/reporting/pdf.py +373 -0
  369. oscura/reporting/plots.py +731 -0
  370. oscura/reporting/pptx_export.py +360 -0
  371. oscura/reporting/renderers/__init__.py +11 -0
  372. oscura/reporting/renderers/pdf.py +94 -0
  373. oscura/reporting/sections.py +471 -0
  374. oscura/reporting/standards.py +680 -0
  375. oscura/reporting/summary_generator.py +368 -0
  376. oscura/reporting/tables.py +397 -0
  377. oscura/reporting/template_system.py +724 -0
  378. oscura/reporting/templates/__init__.py +15 -0
  379. oscura/reporting/templates/definition.py +205 -0
  380. oscura/reporting/templates/index.html +649 -0
  381. oscura/reporting/templates/index.md +173 -0
  382. oscura/schemas/__init__.py +158 -0
  383. oscura/schemas/bus_configuration.json +322 -0
  384. oscura/schemas/device_mapping.json +182 -0
  385. oscura/schemas/packet_format.json +418 -0
  386. oscura/schemas/protocol_definition.json +363 -0
  387. oscura/search/__init__.py +16 -0
  388. oscura/search/anomaly.py +292 -0
  389. oscura/search/context.py +149 -0
  390. oscura/search/pattern.py +160 -0
  391. oscura/session/__init__.py +34 -0
  392. oscura/session/annotations.py +289 -0
  393. oscura/session/history.py +313 -0
  394. oscura/session/session.py +445 -0
  395. oscura/streaming/__init__.py +43 -0
  396. oscura/streaming/chunked.py +611 -0
  397. oscura/streaming/progressive.py +393 -0
  398. oscura/streaming/realtime.py +622 -0
  399. oscura/testing/__init__.py +54 -0
  400. oscura/testing/synthetic.py +808 -0
  401. oscura/triggering/__init__.py +68 -0
  402. oscura/triggering/base.py +229 -0
  403. oscura/triggering/edge.py +353 -0
  404. oscura/triggering/pattern.py +344 -0
  405. oscura/triggering/pulse.py +581 -0
  406. oscura/triggering/window.py +453 -0
  407. oscura/ui/__init__.py +48 -0
  408. oscura/ui/formatters.py +526 -0
  409. oscura/ui/progressive_display.py +340 -0
  410. oscura/utils/__init__.py +99 -0
  411. oscura/utils/autodetect.py +338 -0
  412. oscura/utils/buffer.py +389 -0
  413. oscura/utils/lazy.py +407 -0
  414. oscura/utils/lazy_imports.py +147 -0
  415. oscura/utils/memory.py +836 -0
  416. oscura/utils/memory_advanced.py +1326 -0
  417. oscura/utils/memory_extensions.py +465 -0
  418. oscura/utils/progressive.py +352 -0
  419. oscura/utils/windowing.py +362 -0
  420. oscura/visualization/__init__.py +321 -0
  421. oscura/visualization/accessibility.py +526 -0
  422. oscura/visualization/annotations.py +374 -0
  423. oscura/visualization/axis_scaling.py +305 -0
  424. oscura/visualization/colors.py +453 -0
  425. oscura/visualization/digital.py +337 -0
  426. oscura/visualization/eye.py +420 -0
  427. oscura/visualization/histogram.py +281 -0
  428. oscura/visualization/interactive.py +858 -0
  429. oscura/visualization/jitter.py +702 -0
  430. oscura/visualization/keyboard.py +394 -0
  431. oscura/visualization/layout.py +365 -0
  432. oscura/visualization/optimization.py +1028 -0
  433. oscura/visualization/palettes.py +446 -0
  434. oscura/visualization/plot.py +92 -0
  435. oscura/visualization/power.py +290 -0
  436. oscura/visualization/power_extended.py +626 -0
  437. oscura/visualization/presets.py +467 -0
  438. oscura/visualization/protocols.py +932 -0
  439. oscura/visualization/render.py +207 -0
  440. oscura/visualization/rendering.py +444 -0
  441. oscura/visualization/reverse_engineering.py +791 -0
  442. oscura/visualization/signal_integrity.py +808 -0
  443. oscura/visualization/specialized.py +553 -0
  444. oscura/visualization/spectral.py +811 -0
  445. oscura/visualization/styles.py +381 -0
  446. oscura/visualization/thumbnails.py +311 -0
  447. oscura/visualization/time_axis.py +351 -0
  448. oscura/visualization/waveform.py +367 -0
  449. oscura/workflow/__init__.py +13 -0
  450. oscura/workflow/dag.py +377 -0
  451. oscura/workflows/__init__.py +58 -0
  452. oscura/workflows/compliance.py +280 -0
  453. oscura/workflows/digital.py +272 -0
  454. oscura/workflows/multi_trace.py +502 -0
  455. oscura/workflows/power.py +178 -0
  456. oscura/workflows/protocol.py +492 -0
  457. oscura/workflows/reverse_engineering.py +639 -0
  458. oscura/workflows/signal_integrity.py +227 -0
  459. oscura-0.1.1.dist-info/METADATA +300 -0
  460. oscura-0.1.1.dist-info/RECORD +463 -0
  461. oscura-0.1.1.dist-info/entry_points.txt +2 -0
  462. {oscura-0.0.1.dist-info → oscura-0.1.1.dist-info}/licenses/LICENSE +1 -1
  463. oscura-0.0.1.dist-info/METADATA +0 -63
  464. oscura-0.0.1.dist-info/RECORD +0 -5
  465. {oscura-0.0.1.dist-info → oscura-0.1.1.dist-info}/WHEEL +0 -0
@@ -0,0 +1,711 @@
1
+ """CRC polynomial reverse engineering.
2
+
3
+ This module implements CRC parameter recovery from message-CRC pairs using
4
+ the XOR differential technique (Greg Ewing method). It can recover CRC
5
+ polynomials, initialization values, and other parameters without prior
6
+ knowledge of the CRC algorithm.
7
+
8
+ References:
9
+ - Greg Ewing's CRC Reverse Engineering Essay:
10
+ https://www.csse.canterbury.ac.nz/greg.ewing/essays/CRC-Reverse-Engineering.html
11
+ - CRC RevEng: https://reveng.sourceforge.io/
12
+ - CRC Beagle: https://github.com/colinoflynn/crcbeagle
13
+
14
+ Example:
15
+ >>> # Capture some messages with CRC-16-CCITT
16
+ >>> messages = [
17
+ ... (b"Hello", b"\x1a\x2b"),
18
+ ... (b"World", b"\x3c\x4d"),
19
+ ... ]
20
+ >>> reverser = CRCReverser()
21
+ >>> params = reverser.reverse(messages)
22
+ >>> print(f"Polynomial: 0x{params.polynomial:04x}")
23
+ Polynomial: 0x1021
24
+ """
25
+
26
+ from __future__ import annotations
27
+
28
+ from dataclasses import dataclass
29
+
30
+ __all__ = ["CRCParameters", "CRCReverser", "verify_crc"]
31
+
32
+
33
+ @dataclass
34
+ class CRCParameters:
35
+ """Recovered CRC parameters.
36
+
37
+ Attributes:
38
+ polynomial: CRC polynomial (without leading 1 bit).
39
+ width: CRC width in bits (8, 16, 32, 64).
40
+ init: Initial CRC register value.
41
+ xor_out: Final XOR value applied to CRC.
42
+ reflect_in: Whether input bytes are bit-reflected.
43
+ reflect_out: Whether output CRC is bit-reflected.
44
+ confidence: Confidence score (0.0-1.0) based on validation.
45
+ test_pass_rate: Percentage of test messages that validate correctly.
46
+ algorithm_name: Name of matching standard algorithm (if identified).
47
+ """
48
+
49
+ polynomial: int
50
+ width: int
51
+ init: int
52
+ xor_out: int
53
+ reflect_in: bool
54
+ reflect_out: bool
55
+ confidence: float
56
+ test_pass_rate: float = 1.0
57
+ algorithm_name: str | None = None
58
+
59
+ def __repr__(self) -> str:
60
+ """Human-readable representation."""
61
+ return (
62
+ f"CRCParameters(polynomial=0x{self.polynomial:0{self.width // 4}x}, "
63
+ f"width={self.width}, init=0x{self.init:0{self.width // 4}x}, "
64
+ f"xor_out=0x{self.xor_out:0{self.width // 4}x}, "
65
+ f"reflect_in={self.reflect_in}, reflect_out={self.reflect_out}, "
66
+ f"confidence={self.confidence:.2f})"
67
+ )
68
+
69
+
70
+ # Standard CRC algorithms for identification
71
+ STANDARD_CRCS = {
72
+ "CRC-8": {
73
+ "width": 8,
74
+ "poly": 0x07,
75
+ "init": 0x00,
76
+ "xor_out": 0x00,
77
+ "refin": False,
78
+ "refout": False,
79
+ },
80
+ "CRC-8-CCITT": {
81
+ "width": 8,
82
+ "poly": 0x07,
83
+ "init": 0x00,
84
+ "xor_out": 0x00,
85
+ "refin": False,
86
+ "refout": False,
87
+ },
88
+ "CRC-8-MAXIM": {
89
+ "width": 8,
90
+ "poly": 0x31,
91
+ "init": 0x00,
92
+ "xor_out": 0x00,
93
+ "refin": True,
94
+ "refout": True,
95
+ },
96
+ "CRC-16-CCITT": {
97
+ "width": 16,
98
+ "poly": 0x1021,
99
+ "init": 0xFFFF,
100
+ "xor_out": 0x0000,
101
+ "refin": False,
102
+ "refout": False,
103
+ },
104
+ "CRC-16-IBM": {
105
+ "width": 16,
106
+ "poly": 0x8005,
107
+ "init": 0x0000,
108
+ "xor_out": 0x0000,
109
+ "refin": True,
110
+ "refout": True,
111
+ },
112
+ "CRC-16-XMODEM": {
113
+ "width": 16,
114
+ "poly": 0x1021,
115
+ "init": 0x0000,
116
+ "xor_out": 0x0000,
117
+ "refin": False,
118
+ "refout": False,
119
+ },
120
+ "CRC-16-MODBUS": {
121
+ "width": 16,
122
+ "poly": 0x8005,
123
+ "init": 0xFFFF,
124
+ "xor_out": 0x0000,
125
+ "refin": True,
126
+ "refout": True,
127
+ },
128
+ "CRC-32": {
129
+ "width": 32,
130
+ "poly": 0x04C11DB7,
131
+ "init": 0xFFFFFFFF,
132
+ "xor_out": 0xFFFFFFFF,
133
+ "refin": True,
134
+ "refout": True,
135
+ },
136
+ "CRC-32-BZIP2": {
137
+ "width": 32,
138
+ "poly": 0x04C11DB7,
139
+ "init": 0xFFFFFFFF,
140
+ "xor_out": 0xFFFFFFFF,
141
+ "refin": False,
142
+ "refout": False,
143
+ },
144
+ }
145
+
146
+
147
+ class CRCReverser:
148
+ """Reverse engineer CRC parameters from message-CRC pairs.
149
+
150
+ This class implements the XOR differential technique to recover CRC
151
+ polynomials and parameters from observed message-CRC pairs without
152
+ prior knowledge of the algorithm.
153
+
154
+ The algorithm works by:
155
+ 1. XORing message pairs to eliminate init/xor_out effects
156
+ 2. Brute-forcing polynomial candidates
157
+ 3. Determining reflect_in/reflect_out flags
158
+ 4. Recovering init and xor_out values
159
+ 5. Validating against all messages
160
+
161
+ Minimum Requirements:
162
+ - At least 4 message-CRC pairs (more is better)
163
+ - Messages should have varying content
164
+ - All pairs must use the same CRC algorithm
165
+ """
166
+
167
+ def __init__(self, verbose: bool = False) -> None:
168
+ """Initialize CRC reverser.
169
+
170
+ Args:
171
+ verbose: Enable verbose logging during analysis.
172
+ """
173
+ self.verbose = verbose
174
+
175
+ def reverse(
176
+ self,
177
+ messages: list[tuple[bytes, bytes]],
178
+ width: int | None = None,
179
+ ) -> CRCParameters | None:
180
+ """Reverse engineer CRC parameters from message-CRC pairs.
181
+
182
+ Args:
183
+ messages: List of (data, crc) tuples. Minimum 4 pairs required.
184
+ width: CRC width in bits (8, 16, 32). Auto-detect if None.
185
+
186
+ Returns:
187
+ Recovered CRC parameters, or None if recovery failed.
188
+
189
+ Raises:
190
+ ValueError: If fewer than 4 message pairs provided.
191
+
192
+ Example:
193
+ >>> messages = [
194
+ ... (b"test1", b"\\x12\\x34"),
195
+ ... (b"test2", b"\\x56\\x78"),
196
+ ... (b"test3", b"\\x9a\\xbc"),
197
+ ... (b"test4", b"\\xde\\xf0"),
198
+ ... ]
199
+ >>> reverser = CRCReverser()
200
+ >>> params = reverser.reverse(messages)
201
+ """
202
+ if len(messages) < 4:
203
+ raise ValueError(f"Need at least 4 message pairs, got {len(messages)}")
204
+
205
+ # Step 1: Detect CRC width if not provided
206
+ if width is None:
207
+ width = self._detect_width(messages)
208
+ if self.verbose:
209
+ print(f"Detected CRC width: {width} bits")
210
+
211
+ # Step 2: Find polynomial using XOR differential
212
+ polynomial = self._find_polynomial(messages, width)
213
+ if polynomial is None:
214
+ return None
215
+
216
+ if self.verbose:
217
+ print(f"Found polynomial: 0x{polynomial:0{width // 4}x}")
218
+
219
+ # Step 3: Determine reflect_in and reflect_out
220
+ reflect_in, reflect_out = self._find_reflect_flags(messages, polynomial, width)
221
+ if self.verbose:
222
+ print(f"Reflect flags: refin={reflect_in}, refout={reflect_out}")
223
+
224
+ # Step 4: Recover init and xor_out
225
+ init, xor_out = self._find_init_xorout(messages, polynomial, width, reflect_in, reflect_out)
226
+ if self.verbose:
227
+ print(f"Init: 0x{init:0{width // 4}x}, XorOut: 0x{xor_out:0{width // 4}x}")
228
+
229
+ # Step 5: Validate against all messages
230
+ test_pass_rate = self._validate(
231
+ messages, polynomial, width, init, xor_out, reflect_in, reflect_out
232
+ )
233
+ confidence = test_pass_rate
234
+
235
+ # Try to identify standard algorithm
236
+ algorithm_name = self._identify_standard(
237
+ polynomial, width, init, xor_out, reflect_in, reflect_out
238
+ )
239
+
240
+ return CRCParameters(
241
+ polynomial=polynomial,
242
+ width=width,
243
+ init=init,
244
+ xor_out=xor_out,
245
+ reflect_in=reflect_in,
246
+ reflect_out=reflect_out,
247
+ confidence=confidence,
248
+ test_pass_rate=test_pass_rate,
249
+ algorithm_name=algorithm_name,
250
+ )
251
+
252
+ def _detect_width(self, messages: list[tuple[bytes, bytes]]) -> int:
253
+ """Detect CRC width from CRC field size.
254
+
255
+ Args:
256
+ messages: List of (data, crc) tuples.
257
+
258
+ Returns:
259
+ Detected width in bits.
260
+ """
261
+ crc_bytes = len(messages[0][1])
262
+ return crc_bytes * 8
263
+
264
+ def _find_polynomial(
265
+ self,
266
+ messages: list[tuple[bytes, bytes]],
267
+ width: int,
268
+ ) -> int | None:
269
+ """Find CRC polynomial using XOR differential technique.
270
+
271
+ Args:
272
+ messages: List of (data, crc) tuples.
273
+ width: CRC width in bits.
274
+
275
+ Returns:
276
+ Polynomial (without leading 1), or None if not found.
277
+ """
278
+ # Generate differentials by XORing message pairs of SAME LENGTH
279
+ # The XOR differential technique only works when both messages have the same length
280
+ differentials = []
281
+ for i in range(len(messages)):
282
+ for j in range(i + 1, len(messages)):
283
+ data1, crc1 = messages[i]
284
+ data2, crc2 = messages[j]
285
+
286
+ # Only use pairs with same message length
287
+ if len(data1) != len(data2):
288
+ continue
289
+
290
+ # XOR data and CRC
291
+ data_xor = self._xor_bytes(data1, data2)
292
+ crc_xor = self._xor_bytes(crc1, crc2)
293
+
294
+ differentials.append((data_xor, crc_xor))
295
+
296
+ # If we have enough differentials, use the XOR differential technique
297
+ if len(differentials) >= 2:
298
+ # Try common polynomials first with different reflection modes
299
+ common_polys = self._get_common_polynomials(width)
300
+ for poly in common_polys:
301
+ # Try all reflection combinations for polynomial testing
302
+ for refin in [False, True]:
303
+ for refout in [False, True]:
304
+ if self._test_polynomial(differentials, poly, width, refin, refout):
305
+ return poly
306
+
307
+ # For 32-bit, only try common ones due to performance
308
+ if width >= 32:
309
+ return None
310
+
311
+ # Brute-force polynomial search for smaller widths only
312
+ max_poly = (1 << width) - 1
313
+ for poly in range(1, max_poly + 1):
314
+ if poly in common_polys:
315
+ continue # Already tested
316
+ # Try all reflection combinations
317
+ for refin in [False, True]:
318
+ for refout in [False, True]:
319
+ if self._test_polynomial(differentials, poly, width, refin, refout):
320
+ return poly
321
+
322
+ # Fallback: Not enough differentials (e.g., all messages have different lengths)
323
+ # Try direct matching with common polynomials
324
+ common_polys = self._get_common_polynomials(width)
325
+ max_val = (1 << width) - 1
326
+ common_inits = [0x0000, 0xFFFF, 0xFFFFFFFF]
327
+ common_xorouts = [0x0000, 0xFFFF, 0xFFFFFFFF]
328
+
329
+ for poly in common_polys:
330
+ for refin in [False, True]:
331
+ for refout in [False, True]:
332
+ for init in common_inits:
333
+ if init > max_val:
334
+ continue
335
+ for xor_out in common_xorouts:
336
+ if xor_out > max_val:
337
+ continue
338
+
339
+ # Check if this combination matches all messages
340
+ matches = sum(
341
+ 1
342
+ for data, crc_bytes in messages
343
+ if self._calculate_crc(
344
+ data, poly, width, init, xor_out, refin, refout
345
+ )
346
+ == int.from_bytes(crc_bytes, "big")
347
+ )
348
+
349
+ if matches == len(messages):
350
+ return poly
351
+
352
+ return None
353
+
354
+ def _get_common_polynomials(self, width: int) -> list[int]:
355
+ """Get list of common CRC polynomials for a given width.
356
+
357
+ Args:
358
+ width: CRC width in bits.
359
+
360
+ Returns:
361
+ List of common polynomial values.
362
+ """
363
+ common = {
364
+ 8: [0x07, 0x31, 0x9B, 0xD5],
365
+ 16: [0x1021, 0x8005, 0x8BB7, 0xA001, 0xC867],
366
+ 32: [0x04C11DB7, 0x1EDC6F41, 0x741B8CD7, 0x814141AB],
367
+ }
368
+ return common.get(width, [])
369
+
370
+ def _test_polynomial(
371
+ self,
372
+ differentials: list[tuple[bytes, bytes]],
373
+ poly: int,
374
+ width: int,
375
+ refin: bool,
376
+ refout: bool,
377
+ ) -> bool:
378
+ """Test if polynomial matches all differentials.
379
+
380
+ Args:
381
+ differentials: List of (data_xor, crc_xor) tuples.
382
+ poly: Polynomial to test.
383
+ width: CRC width in bits.
384
+ refin: Reflect input flag.
385
+ refout: Reflect output flag.
386
+
387
+ Returns:
388
+ True if polynomial matches all differentials.
389
+ """
390
+ for data_xor, crc_xor in differentials:
391
+ # Calculate CRC of XORed data with this polynomial
392
+ # Using init=0 and xor_out=0 for differential
393
+ calc_crc = self._calculate_crc(data_xor, poly, width, 0, 0, refin, refout)
394
+ expected_crc = int.from_bytes(crc_xor, "big")
395
+
396
+ if calc_crc != expected_crc:
397
+ return False
398
+
399
+ return True
400
+
401
+ def _find_reflect_flags(
402
+ self,
403
+ messages: list[tuple[bytes, bytes]],
404
+ poly: int,
405
+ width: int,
406
+ ) -> tuple[bool, bool]:
407
+ """Determine reflect_in and reflect_out flags.
408
+
409
+ Args:
410
+ messages: List of (data, crc) tuples.
411
+ poly: CRC polynomial.
412
+ width: CRC width in bits.
413
+
414
+ Returns:
415
+ Tuple of (reflect_in, reflect_out).
416
+ """
417
+ # Try all reflection combinations with common init/xor_out values
418
+ # and see which combination produces the best results
419
+ max_val = (1 << width) - 1
420
+ common_inits = [0x0000, 0xFFFF, 0xFFFFFFFF]
421
+ common_xorouts = [0x0000, 0xFFFF, 0xFFFFFFFF]
422
+
423
+ best_match = (False, False)
424
+ best_score = 0
425
+
426
+ for refin in [False, True]:
427
+ for refout in [False, True]:
428
+ # Try common init/xor_out combinations
429
+ for init in common_inits:
430
+ if init > max_val:
431
+ continue
432
+ for xor_out in common_xorouts:
433
+ if xor_out > max_val:
434
+ continue
435
+
436
+ score = sum(
437
+ 1
438
+ for data, crc_bytes in messages
439
+ if self._calculate_crc(data, poly, width, init, xor_out, refin, refout)
440
+ == int.from_bytes(crc_bytes, "big")
441
+ )
442
+
443
+ if score > best_score:
444
+ best_score = score
445
+ best_match = (refin, refout)
446
+
447
+ return best_match
448
+
449
+ def _find_init_xorout(
450
+ self,
451
+ messages: list[tuple[bytes, bytes]],
452
+ poly: int,
453
+ width: int,
454
+ refin: bool,
455
+ refout: bool,
456
+ ) -> tuple[int, int]:
457
+ """Find init and xor_out values.
458
+
459
+ Args:
460
+ messages: List of (data, crc) tuples.
461
+ poly: CRC polynomial.
462
+ width: CRC width in bits.
463
+ refin: Reflect input flag.
464
+ refout: Reflect output flag.
465
+
466
+ Returns:
467
+ Tuple of (init, xor_out).
468
+ """
469
+ max_val = (1 << width) - 1
470
+
471
+ # Common init values to try first
472
+ common_inits = [0x0000, 0xFFFF, 0xFFFFFFFF]
473
+ common_xorouts = [0x0000, 0xFFFF, 0xFFFFFFFF]
474
+
475
+ # Try common combinations first
476
+ for init in common_inits:
477
+ if init > max_val:
478
+ continue
479
+ for xor_out in common_xorouts:
480
+ if xor_out > max_val:
481
+ continue
482
+
483
+ matches = sum(
484
+ 1
485
+ for data, crc_bytes in messages
486
+ if self._calculate_crc(data, poly, width, init, xor_out, refin, refout)
487
+ == int.from_bytes(crc_bytes, "big")
488
+ )
489
+
490
+ if matches == len(messages):
491
+ return init, xor_out
492
+
493
+ # If common values don't work, brute-force (expensive for 32-bit)
494
+ if width <= 16:
495
+ for init in range(max_val + 1):
496
+ for xor_out in range(max_val + 1):
497
+ matches = sum(
498
+ 1
499
+ for data, crc_bytes in messages
500
+ if self._calculate_crc(data, poly, width, init, xor_out, refin, refout)
501
+ == int.from_bytes(crc_bytes, "big")
502
+ )
503
+
504
+ if matches == len(messages):
505
+ return init, xor_out
506
+
507
+ # Default to 0 if not found
508
+ return 0, 0
509
+
510
+ def _validate(
511
+ self,
512
+ messages: list[tuple[bytes, bytes]],
513
+ poly: int,
514
+ width: int,
515
+ init: int,
516
+ xor_out: int,
517
+ refin: bool,
518
+ refout: bool,
519
+ ) -> float:
520
+ """Validate CRC parameters against all messages.
521
+
522
+ Args:
523
+ messages: List of (data, crc) tuples.
524
+ poly: CRC polynomial.
525
+ width: CRC width in bits.
526
+ init: Initial value.
527
+ xor_out: Final XOR value.
528
+ refin: Reflect input flag.
529
+ refout: Reflect output flag.
530
+
531
+ Returns:
532
+ Pass rate (0.0 to 1.0).
533
+ """
534
+ matches = 0
535
+ for data, crc_bytes in messages:
536
+ calc_crc = self._calculate_crc(data, poly, width, init, xor_out, refin, refout)
537
+ expected_crc = int.from_bytes(crc_bytes, "big")
538
+ if calc_crc == expected_crc:
539
+ matches += 1
540
+
541
+ return matches / len(messages)
542
+
543
+ def _identify_standard(
544
+ self,
545
+ poly: int,
546
+ width: int,
547
+ init: int,
548
+ xor_out: int,
549
+ refin: bool,
550
+ refout: bool,
551
+ ) -> str | None:
552
+ """Identify if parameters match a standard CRC algorithm.
553
+
554
+ Args:
555
+ poly: CRC polynomial.
556
+ width: CRC width in bits.
557
+ init: Initial value.
558
+ xor_out: Final XOR value.
559
+ refin: Reflect input flag.
560
+ refout: Reflect output flag.
561
+
562
+ Returns:
563
+ Name of standard algorithm, or None if not recognized.
564
+ """
565
+ for name, params in STANDARD_CRCS.items():
566
+ if (
567
+ params["width"] == width
568
+ and params["poly"] == poly
569
+ and params["init"] == init
570
+ and params["xor_out"] == xor_out
571
+ and params["refin"] == refin
572
+ and params["refout"] == refout
573
+ ):
574
+ return name
575
+ return None
576
+
577
+ def _calculate_crc(
578
+ self,
579
+ data: bytes,
580
+ poly: int,
581
+ width: int,
582
+ init: int,
583
+ xor_out: int,
584
+ refin: bool,
585
+ refout: bool,
586
+ ) -> int:
587
+ """Calculate CRC with given parameters.
588
+
589
+ Args:
590
+ data: Input data.
591
+ poly: CRC polynomial.
592
+ width: CRC width in bits.
593
+ init: Initial value.
594
+ xor_out: Final XOR value.
595
+ refin: Reflect input bytes.
596
+ refout: Reflect output CRC.
597
+
598
+ Returns:
599
+ Calculated CRC value.
600
+ """
601
+ crc = init
602
+ mask = (1 << width) - 1
603
+
604
+ for byte in data:
605
+ if refin:
606
+ byte = self._reflect_byte(byte)
607
+
608
+ crc ^= byte << (width - 8)
609
+
610
+ for _ in range(8):
611
+ if crc & (1 << (width - 1)):
612
+ crc = (crc << 1) ^ poly
613
+ else:
614
+ crc = crc << 1
615
+
616
+ crc &= mask
617
+
618
+ if refout:
619
+ crc = self._reflect(crc, width)
620
+
621
+ return crc ^ xor_out
622
+
623
+ def _reflect_byte(self, byte: int) -> int:
624
+ """Reflect bits in a byte.
625
+
626
+ Args:
627
+ byte: Input byte (0-255).
628
+
629
+ Returns:
630
+ Bit-reflected byte.
631
+ """
632
+ result = 0
633
+ for i in range(8):
634
+ if byte & (1 << i):
635
+ result |= 1 << (7 - i)
636
+ return result
637
+
638
+ def _reflect(self, value: int, width: int) -> int:
639
+ """Reflect bits in a value.
640
+
641
+ Args:
642
+ value: Input value.
643
+ width: Number of bits to reflect.
644
+
645
+ Returns:
646
+ Bit-reflected value.
647
+ """
648
+ result = 0
649
+ for i in range(width):
650
+ if value & (1 << i):
651
+ result |= 1 << (width - 1 - i)
652
+ return result
653
+
654
+ def _xor_bytes(self, b1: bytes, b2: bytes) -> bytes:
655
+ """XOR two byte sequences.
656
+
657
+ Args:
658
+ b1: First byte sequence.
659
+ b2: Second byte sequence.
660
+
661
+ Returns:
662
+ XORed result (length = max(len(b1), len(b2))).
663
+ """
664
+ # Pad shorter sequence with zeros
665
+ max_len = max(len(b1), len(b2))
666
+ b1_padded = b1.ljust(max_len, b"\x00")
667
+ b2_padded = b2.ljust(max_len, b"\x00")
668
+
669
+ return bytes(a ^ b for a, b in zip(b1_padded, b2_padded, strict=True))
670
+
671
+
672
+ def verify_crc(
673
+ data: bytes,
674
+ crc: bytes,
675
+ params: CRCParameters,
676
+ ) -> bool:
677
+ """Verify if CRC is correct for given data.
678
+
679
+ Args:
680
+ data: Input data.
681
+ crc: Expected CRC value.
682
+ params: CRC parameters.
683
+
684
+ Returns:
685
+ True if CRC is correct.
686
+
687
+ Example:
688
+ >>> params = CRCParameters(
689
+ ... polynomial=0x1021,
690
+ ... width=16,
691
+ ... init=0xFFFF,
692
+ ... xor_out=0x0000,
693
+ ... reflect_in=False,
694
+ ... reflect_out=False,
695
+ ... confidence=1.0,
696
+ ... )
697
+ >>> verify_crc(b"Hello", b"\\x1a\\x2b", params)
698
+ True
699
+ """
700
+ reverser = CRCReverser()
701
+ calc_crc = reverser._calculate_crc(
702
+ data,
703
+ params.polynomial,
704
+ params.width,
705
+ params.init,
706
+ params.xor_out,
707
+ params.reflect_in,
708
+ params.reflect_out,
709
+ )
710
+ expected_crc = int.from_bytes(crc, "big")
711
+ return calc_crc == expected_crc