oscura 0.0.1__py3-none-any.whl → 0.1.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.
- oscura/__init__.py +813 -8
- oscura/__main__.py +392 -0
- oscura/analyzers/__init__.py +37 -0
- oscura/analyzers/digital/__init__.py +177 -0
- oscura/analyzers/digital/bus.py +691 -0
- oscura/analyzers/digital/clock.py +805 -0
- oscura/analyzers/digital/correlation.py +720 -0
- oscura/analyzers/digital/edges.py +632 -0
- oscura/analyzers/digital/extraction.py +413 -0
- oscura/analyzers/digital/quality.py +878 -0
- oscura/analyzers/digital/signal_quality.py +877 -0
- oscura/analyzers/digital/thresholds.py +708 -0
- oscura/analyzers/digital/timing.py +1104 -0
- oscura/analyzers/eye/__init__.py +46 -0
- oscura/analyzers/eye/diagram.py +434 -0
- oscura/analyzers/eye/metrics.py +555 -0
- oscura/analyzers/jitter/__init__.py +83 -0
- oscura/analyzers/jitter/ber.py +333 -0
- oscura/analyzers/jitter/decomposition.py +759 -0
- oscura/analyzers/jitter/measurements.py +413 -0
- oscura/analyzers/jitter/spectrum.py +220 -0
- oscura/analyzers/measurements.py +40 -0
- oscura/analyzers/packet/__init__.py +171 -0
- oscura/analyzers/packet/daq.py +1077 -0
- oscura/analyzers/packet/metrics.py +437 -0
- oscura/analyzers/packet/parser.py +327 -0
- oscura/analyzers/packet/payload.py +2156 -0
- oscura/analyzers/packet/payload_analysis.py +1312 -0
- oscura/analyzers/packet/payload_extraction.py +236 -0
- oscura/analyzers/packet/payload_patterns.py +670 -0
- oscura/analyzers/packet/stream.py +359 -0
- oscura/analyzers/patterns/__init__.py +266 -0
- oscura/analyzers/patterns/clustering.py +1036 -0
- oscura/analyzers/patterns/discovery.py +539 -0
- oscura/analyzers/patterns/learning.py +797 -0
- oscura/analyzers/patterns/matching.py +1091 -0
- oscura/analyzers/patterns/periodic.py +650 -0
- oscura/analyzers/patterns/sequences.py +767 -0
- oscura/analyzers/power/__init__.py +116 -0
- oscura/analyzers/power/ac_power.py +391 -0
- oscura/analyzers/power/basic.py +383 -0
- oscura/analyzers/power/conduction.py +314 -0
- oscura/analyzers/power/efficiency.py +297 -0
- oscura/analyzers/power/ripple.py +356 -0
- oscura/analyzers/power/soa.py +372 -0
- oscura/analyzers/power/switching.py +479 -0
- oscura/analyzers/protocol/__init__.py +150 -0
- oscura/analyzers/protocols/__init__.py +150 -0
- oscura/analyzers/protocols/base.py +500 -0
- oscura/analyzers/protocols/can.py +620 -0
- oscura/analyzers/protocols/can_fd.py +448 -0
- oscura/analyzers/protocols/flexray.py +405 -0
- oscura/analyzers/protocols/hdlc.py +399 -0
- oscura/analyzers/protocols/i2c.py +368 -0
- oscura/analyzers/protocols/i2s.py +296 -0
- oscura/analyzers/protocols/jtag.py +393 -0
- oscura/analyzers/protocols/lin.py +445 -0
- oscura/analyzers/protocols/manchester.py +333 -0
- oscura/analyzers/protocols/onewire.py +501 -0
- oscura/analyzers/protocols/spi.py +334 -0
- oscura/analyzers/protocols/swd.py +325 -0
- oscura/analyzers/protocols/uart.py +393 -0
- oscura/analyzers/protocols/usb.py +495 -0
- oscura/analyzers/signal_integrity/__init__.py +63 -0
- oscura/analyzers/signal_integrity/embedding.py +294 -0
- oscura/analyzers/signal_integrity/equalization.py +370 -0
- oscura/analyzers/signal_integrity/sparams.py +484 -0
- oscura/analyzers/spectral/__init__.py +53 -0
- oscura/analyzers/spectral/chunked.py +273 -0
- oscura/analyzers/spectral/chunked_fft.py +571 -0
- oscura/analyzers/spectral/chunked_wavelet.py +391 -0
- oscura/analyzers/spectral/fft.py +92 -0
- oscura/analyzers/statistical/__init__.py +250 -0
- oscura/analyzers/statistical/checksum.py +923 -0
- oscura/analyzers/statistical/chunked_corr.py +228 -0
- oscura/analyzers/statistical/classification.py +778 -0
- oscura/analyzers/statistical/entropy.py +1113 -0
- oscura/analyzers/statistical/ngrams.py +614 -0
- oscura/analyzers/statistics/__init__.py +119 -0
- oscura/analyzers/statistics/advanced.py +885 -0
- oscura/analyzers/statistics/basic.py +263 -0
- oscura/analyzers/statistics/correlation.py +630 -0
- oscura/analyzers/statistics/distribution.py +298 -0
- oscura/analyzers/statistics/outliers.py +463 -0
- oscura/analyzers/statistics/streaming.py +93 -0
- oscura/analyzers/statistics/trend.py +520 -0
- oscura/analyzers/validation.py +598 -0
- oscura/analyzers/waveform/__init__.py +36 -0
- oscura/analyzers/waveform/measurements.py +943 -0
- oscura/analyzers/waveform/measurements_with_uncertainty.py +371 -0
- oscura/analyzers/waveform/spectral.py +1689 -0
- oscura/analyzers/waveform/wavelets.py +298 -0
- oscura/api/__init__.py +62 -0
- oscura/api/dsl.py +538 -0
- oscura/api/fluent.py +571 -0
- oscura/api/operators.py +498 -0
- oscura/api/optimization.py +392 -0
- oscura/api/profiling.py +396 -0
- oscura/automotive/__init__.py +73 -0
- oscura/automotive/can/__init__.py +52 -0
- oscura/automotive/can/analysis.py +356 -0
- oscura/automotive/can/checksum.py +250 -0
- oscura/automotive/can/correlation.py +212 -0
- oscura/automotive/can/discovery.py +355 -0
- oscura/automotive/can/message_wrapper.py +375 -0
- oscura/automotive/can/models.py +385 -0
- oscura/automotive/can/patterns.py +381 -0
- oscura/automotive/can/session.py +452 -0
- oscura/automotive/can/state_machine.py +300 -0
- oscura/automotive/can/stimulus_response.py +461 -0
- oscura/automotive/dbc/__init__.py +15 -0
- oscura/automotive/dbc/generator.py +156 -0
- oscura/automotive/dbc/parser.py +146 -0
- oscura/automotive/dtc/__init__.py +30 -0
- oscura/automotive/dtc/database.py +3036 -0
- oscura/automotive/j1939/__init__.py +14 -0
- oscura/automotive/j1939/decoder.py +745 -0
- oscura/automotive/loaders/__init__.py +35 -0
- oscura/automotive/loaders/asc.py +98 -0
- oscura/automotive/loaders/blf.py +77 -0
- oscura/automotive/loaders/csv_can.py +136 -0
- oscura/automotive/loaders/dispatcher.py +136 -0
- oscura/automotive/loaders/mdf.py +331 -0
- oscura/automotive/loaders/pcap.py +132 -0
- oscura/automotive/obd/__init__.py +14 -0
- oscura/automotive/obd/decoder.py +707 -0
- oscura/automotive/uds/__init__.py +48 -0
- oscura/automotive/uds/decoder.py +265 -0
- oscura/automotive/uds/models.py +64 -0
- oscura/automotive/visualization.py +369 -0
- oscura/batch/__init__.py +55 -0
- oscura/batch/advanced.py +627 -0
- oscura/batch/aggregate.py +300 -0
- oscura/batch/analyze.py +139 -0
- oscura/batch/logging.py +487 -0
- oscura/batch/metrics.py +556 -0
- oscura/builders/__init__.py +41 -0
- oscura/builders/signal_builder.py +1131 -0
- oscura/cli/__init__.py +14 -0
- oscura/cli/batch.py +339 -0
- oscura/cli/characterize.py +273 -0
- oscura/cli/compare.py +775 -0
- oscura/cli/decode.py +551 -0
- oscura/cli/main.py +247 -0
- oscura/cli/shell.py +350 -0
- oscura/comparison/__init__.py +66 -0
- oscura/comparison/compare.py +397 -0
- oscura/comparison/golden.py +487 -0
- oscura/comparison/limits.py +391 -0
- oscura/comparison/mask.py +434 -0
- oscura/comparison/trace_diff.py +30 -0
- oscura/comparison/visualization.py +481 -0
- oscura/compliance/__init__.py +70 -0
- oscura/compliance/advanced.py +756 -0
- oscura/compliance/masks.py +363 -0
- oscura/compliance/reporting.py +483 -0
- oscura/compliance/testing.py +298 -0
- oscura/component/__init__.py +38 -0
- oscura/component/impedance.py +365 -0
- oscura/component/reactive.py +598 -0
- oscura/component/transmission_line.py +312 -0
- oscura/config/__init__.py +191 -0
- oscura/config/defaults.py +254 -0
- oscura/config/loader.py +348 -0
- oscura/config/memory.py +271 -0
- oscura/config/migration.py +458 -0
- oscura/config/pipeline.py +1077 -0
- oscura/config/preferences.py +530 -0
- oscura/config/protocol.py +875 -0
- oscura/config/schema.py +713 -0
- oscura/config/settings.py +420 -0
- oscura/config/thresholds.py +599 -0
- oscura/convenience.py +457 -0
- oscura/core/__init__.py +299 -0
- oscura/core/audit.py +457 -0
- oscura/core/backend_selector.py +405 -0
- oscura/core/cache.py +590 -0
- oscura/core/cancellation.py +439 -0
- oscura/core/confidence.py +225 -0
- oscura/core/config.py +506 -0
- oscura/core/correlation.py +216 -0
- oscura/core/cross_domain.py +422 -0
- oscura/core/debug.py +301 -0
- oscura/core/edge_cases.py +541 -0
- oscura/core/exceptions.py +535 -0
- oscura/core/gpu_backend.py +523 -0
- oscura/core/lazy.py +832 -0
- oscura/core/log_query.py +540 -0
- oscura/core/logging.py +931 -0
- oscura/core/logging_advanced.py +952 -0
- oscura/core/memoize.py +171 -0
- oscura/core/memory_check.py +274 -0
- oscura/core/memory_guard.py +290 -0
- oscura/core/memory_limits.py +336 -0
- oscura/core/memory_monitor.py +453 -0
- oscura/core/memory_progress.py +465 -0
- oscura/core/memory_warnings.py +315 -0
- oscura/core/numba_backend.py +362 -0
- oscura/core/performance.py +352 -0
- oscura/core/progress.py +524 -0
- oscura/core/provenance.py +358 -0
- oscura/core/results.py +331 -0
- oscura/core/types.py +504 -0
- oscura/core/uncertainty.py +383 -0
- oscura/discovery/__init__.py +52 -0
- oscura/discovery/anomaly_detector.py +672 -0
- oscura/discovery/auto_decoder.py +415 -0
- oscura/discovery/comparison.py +497 -0
- oscura/discovery/quality_validator.py +528 -0
- oscura/discovery/signal_detector.py +769 -0
- oscura/dsl/__init__.py +73 -0
- oscura/dsl/commands.py +246 -0
- oscura/dsl/interpreter.py +455 -0
- oscura/dsl/parser.py +689 -0
- oscura/dsl/repl.py +172 -0
- oscura/exceptions.py +59 -0
- oscura/exploratory/__init__.py +111 -0
- oscura/exploratory/error_recovery.py +642 -0
- oscura/exploratory/fuzzy.py +513 -0
- oscura/exploratory/fuzzy_advanced.py +786 -0
- oscura/exploratory/legacy.py +831 -0
- oscura/exploratory/parse.py +358 -0
- oscura/exploratory/recovery.py +275 -0
- oscura/exploratory/sync.py +382 -0
- oscura/exploratory/unknown.py +707 -0
- oscura/export/__init__.py +25 -0
- oscura/export/wireshark/README.md +265 -0
- oscura/export/wireshark/__init__.py +47 -0
- oscura/export/wireshark/generator.py +312 -0
- oscura/export/wireshark/lua_builder.py +159 -0
- oscura/export/wireshark/templates/dissector.lua.j2 +92 -0
- oscura/export/wireshark/type_mapping.py +165 -0
- oscura/export/wireshark/validator.py +105 -0
- oscura/exporters/__init__.py +94 -0
- oscura/exporters/csv.py +303 -0
- oscura/exporters/exporters.py +44 -0
- oscura/exporters/hdf5.py +219 -0
- oscura/exporters/html_export.py +701 -0
- oscura/exporters/json_export.py +291 -0
- oscura/exporters/markdown_export.py +367 -0
- oscura/exporters/matlab_export.py +354 -0
- oscura/exporters/npz_export.py +219 -0
- oscura/exporters/spice_export.py +210 -0
- oscura/extensibility/__init__.py +131 -0
- oscura/extensibility/docs.py +752 -0
- oscura/extensibility/extensions.py +1125 -0
- oscura/extensibility/logging.py +259 -0
- oscura/extensibility/measurements.py +485 -0
- oscura/extensibility/plugins.py +414 -0
- oscura/extensibility/registry.py +346 -0
- oscura/extensibility/templates.py +913 -0
- oscura/extensibility/validation.py +651 -0
- oscura/filtering/__init__.py +89 -0
- oscura/filtering/base.py +563 -0
- oscura/filtering/convenience.py +564 -0
- oscura/filtering/design.py +725 -0
- oscura/filtering/filters.py +32 -0
- oscura/filtering/introspection.py +605 -0
- oscura/guidance/__init__.py +24 -0
- oscura/guidance/recommender.py +429 -0
- oscura/guidance/wizard.py +518 -0
- oscura/inference/__init__.py +251 -0
- oscura/inference/active_learning/README.md +153 -0
- oscura/inference/active_learning/__init__.py +38 -0
- oscura/inference/active_learning/lstar.py +257 -0
- oscura/inference/active_learning/observation_table.py +230 -0
- oscura/inference/active_learning/oracle.py +78 -0
- oscura/inference/active_learning/teachers/__init__.py +15 -0
- oscura/inference/active_learning/teachers/simulator.py +192 -0
- oscura/inference/adaptive_tuning.py +453 -0
- oscura/inference/alignment.py +653 -0
- oscura/inference/bayesian.py +943 -0
- oscura/inference/binary.py +1016 -0
- oscura/inference/crc_reverse.py +711 -0
- oscura/inference/logic.py +288 -0
- oscura/inference/message_format.py +1305 -0
- oscura/inference/protocol.py +417 -0
- oscura/inference/protocol_dsl.py +1084 -0
- oscura/inference/protocol_library.py +1230 -0
- oscura/inference/sequences.py +809 -0
- oscura/inference/signal_intelligence.py +1509 -0
- oscura/inference/spectral.py +215 -0
- oscura/inference/state_machine.py +634 -0
- oscura/inference/stream.py +918 -0
- oscura/integrations/__init__.py +59 -0
- oscura/integrations/llm.py +1827 -0
- oscura/jupyter/__init__.py +32 -0
- oscura/jupyter/display.py +268 -0
- oscura/jupyter/magic.py +334 -0
- oscura/loaders/__init__.py +526 -0
- oscura/loaders/binary.py +69 -0
- oscura/loaders/configurable.py +1255 -0
- oscura/loaders/csv.py +26 -0
- oscura/loaders/csv_loader.py +473 -0
- oscura/loaders/hdf5.py +9 -0
- oscura/loaders/hdf5_loader.py +510 -0
- oscura/loaders/lazy.py +370 -0
- oscura/loaders/mmap_loader.py +583 -0
- oscura/loaders/numpy_loader.py +436 -0
- oscura/loaders/pcap.py +432 -0
- oscura/loaders/preprocessing.py +368 -0
- oscura/loaders/rigol.py +287 -0
- oscura/loaders/sigrok.py +321 -0
- oscura/loaders/tdms.py +367 -0
- oscura/loaders/tektronix.py +711 -0
- oscura/loaders/validation.py +584 -0
- oscura/loaders/vcd.py +464 -0
- oscura/loaders/wav.py +233 -0
- oscura/math/__init__.py +45 -0
- oscura/math/arithmetic.py +824 -0
- oscura/math/interpolation.py +413 -0
- oscura/onboarding/__init__.py +39 -0
- oscura/onboarding/help.py +498 -0
- oscura/onboarding/tutorials.py +405 -0
- oscura/onboarding/wizard.py +466 -0
- oscura/optimization/__init__.py +19 -0
- oscura/optimization/parallel.py +440 -0
- oscura/optimization/search.py +532 -0
- oscura/pipeline/__init__.py +43 -0
- oscura/pipeline/base.py +338 -0
- oscura/pipeline/composition.py +242 -0
- oscura/pipeline/parallel.py +448 -0
- oscura/pipeline/pipeline.py +375 -0
- oscura/pipeline/reverse_engineering.py +1119 -0
- oscura/plugins/__init__.py +122 -0
- oscura/plugins/base.py +272 -0
- oscura/plugins/cli.py +497 -0
- oscura/plugins/discovery.py +411 -0
- oscura/plugins/isolation.py +418 -0
- oscura/plugins/lifecycle.py +959 -0
- oscura/plugins/manager.py +493 -0
- oscura/plugins/registry.py +421 -0
- oscura/plugins/versioning.py +372 -0
- oscura/py.typed +0 -0
- oscura/quality/__init__.py +65 -0
- oscura/quality/ensemble.py +740 -0
- oscura/quality/explainer.py +338 -0
- oscura/quality/scoring.py +616 -0
- oscura/quality/warnings.py +456 -0
- oscura/reporting/__init__.py +248 -0
- oscura/reporting/advanced.py +1234 -0
- oscura/reporting/analyze.py +448 -0
- oscura/reporting/argument_preparer.py +596 -0
- oscura/reporting/auto_report.py +507 -0
- oscura/reporting/batch.py +615 -0
- oscura/reporting/chart_selection.py +223 -0
- oscura/reporting/comparison.py +330 -0
- oscura/reporting/config.py +615 -0
- oscura/reporting/content/__init__.py +39 -0
- oscura/reporting/content/executive.py +127 -0
- oscura/reporting/content/filtering.py +191 -0
- oscura/reporting/content/minimal.py +257 -0
- oscura/reporting/content/verbosity.py +162 -0
- oscura/reporting/core.py +508 -0
- oscura/reporting/core_formats/__init__.py +17 -0
- oscura/reporting/core_formats/multi_format.py +210 -0
- oscura/reporting/engine.py +836 -0
- oscura/reporting/export.py +366 -0
- oscura/reporting/formatting/__init__.py +129 -0
- oscura/reporting/formatting/emphasis.py +81 -0
- oscura/reporting/formatting/numbers.py +403 -0
- oscura/reporting/formatting/standards.py +55 -0
- oscura/reporting/formatting.py +466 -0
- oscura/reporting/html.py +578 -0
- oscura/reporting/index.py +590 -0
- oscura/reporting/multichannel.py +296 -0
- oscura/reporting/output.py +379 -0
- oscura/reporting/pdf.py +373 -0
- oscura/reporting/plots.py +731 -0
- oscura/reporting/pptx_export.py +360 -0
- oscura/reporting/renderers/__init__.py +11 -0
- oscura/reporting/renderers/pdf.py +94 -0
- oscura/reporting/sections.py +471 -0
- oscura/reporting/standards.py +680 -0
- oscura/reporting/summary_generator.py +368 -0
- oscura/reporting/tables.py +397 -0
- oscura/reporting/template_system.py +724 -0
- oscura/reporting/templates/__init__.py +15 -0
- oscura/reporting/templates/definition.py +205 -0
- oscura/reporting/templates/index.html +649 -0
- oscura/reporting/templates/index.md +173 -0
- oscura/schemas/__init__.py +158 -0
- oscura/schemas/bus_configuration.json +322 -0
- oscura/schemas/device_mapping.json +182 -0
- oscura/schemas/packet_format.json +418 -0
- oscura/schemas/protocol_definition.json +363 -0
- oscura/search/__init__.py +16 -0
- oscura/search/anomaly.py +292 -0
- oscura/search/context.py +149 -0
- oscura/search/pattern.py +160 -0
- oscura/session/__init__.py +34 -0
- oscura/session/annotations.py +289 -0
- oscura/session/history.py +313 -0
- oscura/session/session.py +445 -0
- oscura/streaming/__init__.py +43 -0
- oscura/streaming/chunked.py +611 -0
- oscura/streaming/progressive.py +393 -0
- oscura/streaming/realtime.py +622 -0
- oscura/testing/__init__.py +54 -0
- oscura/testing/synthetic.py +808 -0
- oscura/triggering/__init__.py +68 -0
- oscura/triggering/base.py +229 -0
- oscura/triggering/edge.py +353 -0
- oscura/triggering/pattern.py +344 -0
- oscura/triggering/pulse.py +581 -0
- oscura/triggering/window.py +453 -0
- oscura/ui/__init__.py +48 -0
- oscura/ui/formatters.py +526 -0
- oscura/ui/progressive_display.py +340 -0
- oscura/utils/__init__.py +99 -0
- oscura/utils/autodetect.py +338 -0
- oscura/utils/buffer.py +389 -0
- oscura/utils/lazy.py +407 -0
- oscura/utils/lazy_imports.py +147 -0
- oscura/utils/memory.py +836 -0
- oscura/utils/memory_advanced.py +1326 -0
- oscura/utils/memory_extensions.py +465 -0
- oscura/utils/progressive.py +352 -0
- oscura/utils/windowing.py +362 -0
- oscura/visualization/__init__.py +321 -0
- oscura/visualization/accessibility.py +526 -0
- oscura/visualization/annotations.py +374 -0
- oscura/visualization/axis_scaling.py +305 -0
- oscura/visualization/colors.py +453 -0
- oscura/visualization/digital.py +337 -0
- oscura/visualization/eye.py +420 -0
- oscura/visualization/histogram.py +281 -0
- oscura/visualization/interactive.py +858 -0
- oscura/visualization/jitter.py +702 -0
- oscura/visualization/keyboard.py +394 -0
- oscura/visualization/layout.py +365 -0
- oscura/visualization/optimization.py +1028 -0
- oscura/visualization/palettes.py +446 -0
- oscura/visualization/plot.py +92 -0
- oscura/visualization/power.py +290 -0
- oscura/visualization/power_extended.py +626 -0
- oscura/visualization/presets.py +467 -0
- oscura/visualization/protocols.py +932 -0
- oscura/visualization/render.py +207 -0
- oscura/visualization/rendering.py +444 -0
- oscura/visualization/reverse_engineering.py +791 -0
- oscura/visualization/signal_integrity.py +808 -0
- oscura/visualization/specialized.py +553 -0
- oscura/visualization/spectral.py +811 -0
- oscura/visualization/styles.py +381 -0
- oscura/visualization/thumbnails.py +311 -0
- oscura/visualization/time_axis.py +351 -0
- oscura/visualization/waveform.py +367 -0
- oscura/workflow/__init__.py +13 -0
- oscura/workflow/dag.py +377 -0
- oscura/workflows/__init__.py +58 -0
- oscura/workflows/compliance.py +280 -0
- oscura/workflows/digital.py +272 -0
- oscura/workflows/multi_trace.py +502 -0
- oscura/workflows/power.py +178 -0
- oscura/workflows/protocol.py +492 -0
- oscura/workflows/reverse_engineering.py +639 -0
- oscura/workflows/signal_integrity.py +227 -0
- oscura-0.1.0.dist-info/METADATA +300 -0
- oscura-0.1.0.dist-info/RECORD +463 -0
- oscura-0.1.0.dist-info/entry_points.txt +2 -0
- {oscura-0.0.1.dist-info → oscura-0.1.0.dist-info}/licenses/LICENSE +1 -1
- oscura-0.0.1.dist-info/METADATA +0 -63
- oscura-0.0.1.dist-info/RECORD +0 -5
- {oscura-0.0.1.dist-info → oscura-0.1.0.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
|