| name | classical-ciphers |
| description | Implement and break classical ciphers including Caesar, Vigenere, substitution, and transposition ciphers. Use this skill when decoding historical ciphers, solving CTF challenges involving classical cryptography, or implementing cipher algorithms. |
Classical Ciphers
Encode and decode classical ciphers commonly found in CTF challenges.
Caesar Cipher
def caesar_decrypt(ciphertext: str, shift: int) -> str:
"""Decrypt Caesar cipher with given shift."""
result = []
for char in ciphertext:
if char.isalpha():
base = ord('A') if char.isupper() else ord('a')
result.append(chr((ord(char) - base - shift) % 26 + base))
else:
result.append(char)
return ''.join(result)
def caesar_brute_force(ciphertext: str) -> list:
"""Try all 26 possible shifts."""
return [(i, caesar_decrypt(ciphertext, i)) for i in range(26)]
# Example usage
ciphertext = "KHOOR ZRUOG"
for shift, plaintext in caesar_brute_force(ciphertext):
print(f"Shift {shift}: {plaintext}")
Vigenere Cipher
def vigenere_decrypt(ciphertext: str, key: str) -> str:
"""Decrypt Vigenere cipher with given key."""
result = []
key = key.upper()
key_index = 0
for char in ciphertext:
if char.isalpha():
shift = ord(key[key_index % len(key)]) - ord('A')
base = ord('A') if char.isupper() else ord('a')
result.append(chr((ord(char) - base - shift) % 26 + base))
key_index += 1
else:
result.append(char)
return ''.join(result)
# Example
ciphertext = "LXFOPVEFRNHR"
key = "LEMON"
plaintext = vigenere_decrypt(ciphertext, key)
print(f"Decrypted: {plaintext}")
Substitution Cipher
import string
def substitution_decrypt(ciphertext: str, key: str) -> str:
"""Decrypt using substitution key (26-char mapping)."""
cipher_alphabet = key.upper()
plain_alphabet = string.ascii_uppercase
table = str.maketrans(cipher_alphabet, plain_alphabet)
table.update(str.maketrans(cipher_alphabet.lower(), plain_alphabet.lower()))
return ciphertext.translate(table)
# The key is the cipher alphabet mapping A-Z
key = "QWERTYUIOPASDFGHJKLZXCVBNM"
ciphertext = "ITSSG VGKSR"
print(substitution_decrypt(ciphertext, key))
ROT13
import codecs
def rot13(text: str) -> str:
"""Apply ROT13 transformation."""
return codecs.encode(text, 'rot_13')
# ROT13 is its own inverse
encrypted = rot13("Hello World")
decrypted = rot13(encrypted)
Atbash Cipher
def atbash(text: str) -> str:
"""Atbash cipher - reverse alphabet substitution."""
result = []
for char in text:
if char.isalpha():
base = ord('A') if char.isupper() else ord('a')
result.append(chr(25 - (ord(char) - base) + base))
else:
result.append(char)
return ''.join(result)
# Atbash is its own inverse
ciphertext = atbash("HELLO") # Returns "SVOOL"
plaintext = atbash(ciphertext) # Returns "HELLO"
Rail Fence Cipher
def rail_fence_decrypt(ciphertext: str, rails: int) -> str:
"""Decrypt rail fence cipher."""
n = len(ciphertext)
fence = [[None] * n for _ in range(rails)]
# Mark positions
rail, direction = 0, 1
for i in range(n):
fence[rail][i] = True
rail += direction
if rail == 0 or rail == rails - 1:
direction *= -1
# Fill in ciphertext
idx = 0
for r in range(rails):
for c in range(n):
if fence[r][c]:
fence[r][c] = ciphertext[idx]
idx += 1
# Read off plaintext
result = []
rail, direction = 0, 1
for i in range(n):
result.append(fence[rail][i])
rail += direction
if rail == 0 or rail == rails - 1:
direction *= -1
return ''.join(result)
ciphertext = "WECRLTEERDSOEEFEAOCAIVDEN"
plaintext = rail_fence_decrypt(ciphertext, 3)
Using pycipher Library
from pycipher import Caesar, Vigenere, Playfair, Affine
# Caesar
plaintext = Caesar(key=3).decipher("KHOOR")
# Vigenere
plaintext = Vigenere(key="LEMON").decipher("LXFOPVEFRNHR")
# Playfair
plaintext = Playfair(key="MONARCHY").decipher("GATLMZCLRQXA")
# Affine
plaintext = Affine(a=5, b=8).decipher("IHHWVC")