Skip to main content

Vigenere cipher solver: FFT periodogram + Kasiski + twist++ + beam search + interpolated n-gram LM, with learned posterior weights and an adaptive 'best' ensemble.

Project description

vigenere

PyPI Python 3.10+ License: MIT

A production-grade Vigenere cipher solver that combines classical cryptanalysis (Kasiski, index of coincidence, FFT coincidence periodogram, twist / twist++) with modern decoding (per-strip beam search guided by an interpolated character n-gram language model) and a learned softmax posterior over key lengths. Pure-Python core, numpy for the FFT, optional rich for live progress.

On the easy regime (random English plaintext, key length 4–12, 400+ characters) the best decoder hits 100 % key recovery on 30 random samples and the learned key-length posterior reaches 96.7 % top-1 / 100 % top-3 out of 29 candidates.

Install

# from PyPI (recommended)
pip install vigenere
# or
uv pip install vigenere

# as a globally available CLI
uv tool install vigenere

Requires Python ≥ 3.10. Runtime deps: numpy, rich.

For a dev install (editable, with tests), see docs/BUILDING.md.

Quickstart — library

from vigenere import encrypt, decrypt, solve

ct = encrypt("the quick brown fox jumps over the lazy dog ...", "LEMON")

result = solve(ct, decoder="best")
print(result.key)               # "LEMON"
print(result.plaintext)
print(result.keylen_posterior)  # [(5, 0.41), (10, 0.18), ...]
print(result.confidence)        # 0.92 — strong unique winner
print(result.match_against("LEMON"))  # MatchResult(kind="exact", ...)
print(result.pretty())          # formatted summary

Force a known key length to skip estimation:

solve(ct, decoder="classic", forced_keylens=[7])

Custom n-gram order or weights:

solve(ct, decoder="classic", scorer_kwargs={
    "order": 5,
    "lambdas": [0.02, 0.08, 0.20, 0.30, 0.40],
    "alpha": 1e-4,
})

For a full end-to-end CTF-style walkthrough (Base64-wrapped ciphertext → clean → solve → round-trip verification) see src/core/cipher.py — runnable with python -m core.cipher.

Quickstart — CLI

# Encrypt
echo "the quick brown fox" | vigenere encrypt --key LEMON

# Solve (adaptive auto-mode: fast preset first, escalates only if confidence
# is below threshold).
vigenere solve --in ciphertext.txt
vigenere solve --in ciphertext.txt --no-progress --json
vigenere solve --in ciphertext.txt --decoder classic --keylens 7

# Inspect raw key-length signals
vigenere explain --in ciphertext.txt --max-k 40

Full subcommand reference (including bench, compare, tune, optimize, gen-corpus) lives in docs/CLI.md.

Public API at a glance

from vigenere import (
    encrypt, decrypt,
    clean_letters, random_key,
    solve, solve_auto,
    SolveResult,
)
symbol what it does
encrypt Vigenere encryption; preserves non-letters, normalizes case
decrypt inverse
clean_letters strip non-A–Z, upper-case (use this on raw challenge input)
random_key uniform random A–Z key (deterministic with a seeded Random)
solve full attack pipeline → SolveResult
solve_auto fast preset, escalates to best ensemble only if low confidence
SolveResult dataclass: key, plaintext, keylen_posterior, candidates, confidence, match_against, …

Detailed signatures and every internal module: docs/API.md.

How it works (one-paragraph summary)

For each candidate key length $k$ the solver computes four signals — average per-strip IoC, Kasiski divisor votes, FFT coincidence periodogram with harmonic suppression, and twist++ (Barr & Simoes 2015) — z-scores them, combines them with learned weights, and softmaxes to a posterior $P(m=k \mid \text{ct})$. The top key lengths feed a per-strip Caesar beam search guided by Jensen–Shannon divergence against the English unigram prior; the top keys per beam are re-ranked under an interpolated 1..5-gram character LM. The best decoder runs every scorer, pools their candidates, and re-ranks the union under the n-gram LM — strictly $\geq$ any single decoder on accuracy. Full derivations: docs/THEORY.md.

Measured impact

compare harness, 30 trials, key length 4–12, ≥500 chars:

decoder key_acc exact_acc char_acc mean_sec
best 1.000 1.000 1.000 0.12
classic 1.000 0.733 1.000 0.08
tiny-lm 0.967 0.700 0.999 0.05
legacy 0.93 0.000 0.93 0.04

On harder regimes (very long keys against short ciphertexts — each strip has <20 letters) accuracy degrades because the information isn't there; that's a property of the cipher problem, not the solver.

Build, test, release

See docs/BUILDING.md for editable installs, running the test suite (pytest -v — ~101 tests), building wheels (uv build), and publishing to PyPI (twine upload dist/*).

License

MIT. See LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

vigenere_solver_ng-0.1.0.tar.gz (53.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

vigenere_solver_ng-0.1.0-py3-none-any.whl (48.1 kB view details)

Uploaded Python 3

File details

Details for the file vigenere_solver_ng-0.1.0.tar.gz.

File metadata

  • Download URL: vigenere_solver_ng-0.1.0.tar.gz
  • Upload date:
  • Size: 53.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.12

File hashes

Hashes for vigenere_solver_ng-0.1.0.tar.gz
Algorithm Hash digest
SHA256 e03ae2f50096f9f4a91a2a1f2e800ce701634649f68e34ef0c7b03a0241f94b9
MD5 77370a148ae6d69568c493f7fb63ebe2
BLAKE2b-256 d6b614744319acee4c15210bb8832a1608c7dc947f1b119002e744cd8443161c

See more details on using hashes here.

File details

Details for the file vigenere_solver_ng-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for vigenere_solver_ng-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 4682e3e1eeb071fe3d44b7660592ab308ecdaadebc12631494e9814fefb91371
MD5 1f2eb5ed83099de2fe0909cc0273c2e3
BLAKE2b-256 9a7b396ff95e65623b3eb7721a559eff141e2eb06c237f2c581728c329a6bf34

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page