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:
- Collect N differential ciphertext pairs
(cβ, cβ)wherepβ β pβ = Ξ. - 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.
- 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.
- 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:
uint32ciphertext 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_cudaforuint32); public API staysuint32for 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
- Spec:
docs/superpowers/specs/2026-04-22-phase3b-neural-cryptanalysis-design.md - Plan:
docs/superpowers/plans/2026-04-22-phase3b-neural-cryptanalysis-plan.md - Ambition outcome analysis (full narrative):
docs/phase3b-results/ambition_outcome.md - Horizon probe (raw data):
docs/phase3b-results/horizon_probe.md,docs/phase3b-results/horizon_probe_fine.md - v1-vs-v2 architecture comparison:
docs/phase3b-results/v2_experiment.md