KeeLoq Neural Differential Distinguishers

Gohr-style ResNet-1D-CNN distinguishers for reduced-round KeeLoq, trained as part of the cahlen/keeloq-python modernization project β€” a 2026 Python 3 / RTX 5090 port of a 2015 SAT-only cryptanalysis effort.

These are auxiliary models used to guide a Bayesian key-recovery search. They produce a scalar score indicating whether a ciphertext pair was generated from a chosen-input-difference distribution (vs. random), which the attack pipeline then uses to peel rounds off an M-round cipher back to a depth where a SAT solver can finish the job. See the GitHub repo for the attack pipeline and the keeloq neural CLI.

Checkpoints

File Trained Depth Attack Target Val Accuracy ROC-AUC TPR @ FPR=1% Status
d64.pt 56 64 rounds (peel K=8) 0.752 0.828 0.207 βœ… Viable β€” 64-round full-key recovery in 1.6 s

Metrics are on 1 M held-out samples with a seed disjoint from training. Confusion matrix: TN=400997, FP=99003, FN=149119, TP=350881. Wall clock: 62 min on an RTX 5090. This is the only checkpoint we ship; we investigated deeper trained depths and found a sharp signal horizon that prevents them (see below).

Signal horizon β€” why d72 / d80 / d96 / d128 were not produced

We tested the distinguisher architecture at every depth from 56 to 120. Tiny-budget val-accuracies (best across multiple Ξ” candidates per depth):

Trained depth Best val-acc Status
56 0.688 Signal
57 0.539 Boundary (barely above noise)
58 0.534 Collapse
59 0.534 Collapse
60 0.528 Collapse
64, 68, 72, 76, 80 0.50 – 0.52 Collapse
88 0.517 Collapse
120 0.514 Collapse

The cliff is one round wide. Signal drops from 0.69 at depth 56 to 0.54 at depth 57 β€” essentially a step function. A single additional round of KeeLoq's NLF after depth 56 takes the residual differential feature below what this model family can discover, and the collapse is permanent for every deeper depth we tested.

We tested this with two distinct architectures side-by-side:

  • v1 (1Γ—1-conv MLP-style, depth=5, width=512, ~3M params) β€” the architecture of d64.pt.
  • v2 (kernel-3 spatial-conv ResNet, depth=5, width=256, ~2M params) β€” the Gohr SPECK shape with explicit bit-neighbor inductive bias.

Both architectures reach val-acc 0.688 / 0.703 at depth 56 (equivalent signal), and both collapse identically at depths 88 and 120 (within statistical noise of 0.500). Adding spatial inductive bias does not unlock deeper depths. This strongly suggests the horizon is a property of KeeLoq's diffusion geometry at round counts β‰₯ 57, not an architectural choice.

Full analysis: docs/phase3b-results/ambition_outcome.md. Raw probe data: horizon_probe.md, horizon_probe_fine.md, v2_experiment.md.

Pushing the horizon further is still theoretically possible β€” the leading open directions are (1) 10–100Γ— more training data at depth 57+, (2) distinguisher families spanning multiple depths combined with depth-matched beam search, (3) higher-order or multi-Ξ” differential approaches, (4) direct quantitative differential-trail analysis of KeeLoq to locate the theoretical minimum horizon β€” but all are out of scope for this release.

How the attack works (Gohr pattern on KeeLoq)

A distinguisher trained at depth D gives strong signal on D-round ciphertext pairs. For an M = D + K-round target cipher:

  1. Collect N differential ciphertext pairs (cβ‚€, c₁) where pβ‚€ βŠ• p₁ = Ξ”.
  2. For each of the outer K key bits, try both guesses: partially-decrypt all N pairs by one round, score the resulting (Mβˆ’i)-round pairs with the distinguisher, accumulate log-evidence. Beam-search on accumulated evidence.
  3. After K peels, the remaining D-round suffix has K key bits recovered (via beam) and 64βˆ’K still unknown; the XOR-aware ANF encoder + CryptoMiniSat handles those in seconds.
  4. Every recovered key is cipher-verified (cipher.encrypt(p, k, M) == c) before reporting SUCCESS.

KeeLoq's 1-bit-per-round key schedule simplifies this substantially vs. Gohr's original SPECK attack (which guesses a 16-bit subkey chunk per peel): here every peel step is a binary decision, so the distinguisher only needs to resolve a binary signal.

Usage

git clone https://github.com/cahlen/keeloq-python
cd keeloq-python
uv sync --all-extras

# Download the viable distinguisher
mkdir -p checkpoints
hf download cahlen/keeloq-neural-distinguishers d64.pt --local-dir checkpoints/

# Attack a 64-round ciphertext (replace <c0>/<c1>/<pt>/<ct> with your bit strings)
uv run keeloq neural recover-key --checkpoint checkpoints/d64.pt \
    --rounds 64 --diff-pair "<c0>:<c1>" --sat-pair "<pt>:<ct>" \
    --beam-width 16 --sat-timeout 120

The CLI output is JSON with the recovered key, neural/SAT wall-clock split, bits recovered neurally, and verification status. Exit code 0 = SUCCESS, non-zero = terminal status (UNSAT / TIMEOUT / BACKTRACK_EXHAUSTED / etc).

Reproducibility

Each .pt file embeds its full TrainingConfig (rounds, delta, samples, batch_size, epochs, lr, weight_decay, seed, depth, width). Reproduce d64.pt from scratch with:

uv run keeloq neural train --rounds 56 --delta 0x00000002 \
    --samples 10000000 --batch-size 4096 --epochs 20 \
    --depth 5 --width 512 --seed 1729 \
    --out checkpoints/d64.pt

Wall clock: ~60 min on an RTX 5090.

Architecture (v1 β€” as shipped)

  • Input: uint32 ciphertext pair (cβ‚€, c₁) of shape (N, 2) β†’ MSB-first bit vector (N, 64, 1) of float 0/1.
  • Embedding: Conv1d(64, 512, kernel_size=1).
  • Backbone: 5 Γ— Residual[Conv1d + BN + ReLU + Conv1d + BN + skip + ReLU] blocks at width 512.
  • Head: Flatten β†’ Linear(512, 512) β†’ ReLU β†’ Linear(512, 1) β†’ Sigmoid.
  • Parameters: ~3 M.
  • Loss: BCE. Optimizer: AdamW (wd=1e-5). LR schedule: cosine annealing from 2e-3.
  • Internal computation uses int64 lanes (PyTorch 2.11 + CUDA 13 doesn't implement rshift_cuda for uint32); public API stays uint32 for bit-pattern clarity.

Training data

Generated on-the-fly by keeloq.gpu_cipher.encrypt_batch β€” a bit-sliced CUDA KeeLoq that produces ~10⁢ encryptions/sec on a 5090. Per mini-batch of size N: the first N/2 rows are "real" (random key, random pβ‚€, p₁ = pβ‚€ βŠ• Ξ”, both encrypted under the same key); the second N/2 are "random" (independent plaintexts, independent keys). Labels balanced 50/50.

License

MIT β€” see the LICENSE in the GitHub repo.

Citation

@misc{keeloq-python-2026,
  author = {Cahlen Humphreys},
  title  = {keeloq-python: Algebraic + neural cryptanalysis of reduced-round KeeLoq},
  year   = {2026},
  url    = {https://github.com/cahlen/keeloq-python}
}

Design, plan, and empirical evidence

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support