The Bacon cipher is a classical substitution cipher that encodes each letter of the plaintext into a 5-character sequence using two symbols—commonly A and B. These represent binary digits: A ≡ 0, B ≡ 1 (or vice versa, depending on convention). The standard mapping follows a systematic pattern that corresponds to the positions of letters in the alphabet (excluding J and U in the original scheme, though modern implementations often merge them with I and V).
Core Implementation
Below is a clean and modular Python implementation supporting both encryption and decryption. Variable names and logic have been restructured for clarity and maintainability.
from typing import Dict, List
# Symbol mapping: 0 → 'A', 1 → 'B'
SYMBOLS = {'A': '0', 'B': '1'}
# Reverse mapping for decryption
CHARS = {v: k for k, v in SYMBOLS.items()}
# Generate 26-letter Bacon lookup table (A–Z, merging I/J and U/V)
def build_bacon_table() -> Dict[str, str]:
table: Dict[str, str] = {}
letters = 'ABCDEFGHIKLMNOPQRSTVWXYZ' # 24 letters; I=J, U=V handled separately
for i, ch in enumerate(letters):
binary = format(i, '05b') # 5-bit binary string
bacon = ''.join('B' if bit == '1' else 'A' for bit in binary)
table[ch] = bacon
# Merge J with I, V with U
if ch == 'I':
table['J'] = bacon
elif ch == 'U':
table['V'] = bacon
return table
# Construct the encoding map once at module load time
BAcon_MAP = build_bacon_table()
def encode_to_bacon(plaintext: str) -> str:
"""Encode uppercase English letters to Bacon cipher sequences (space-separated)."""
sequences: List[str] = []
for letter in plaintext.upper():
if letter in BAcon_MAP:
sequences.append(BAcon_MAP[letter])
else:
sequences.append(letter) # preserve non-alphabetic characters
return ' '.join(sequences)
def decode_from_bacon(ciphertext: str) -> str:
"""Decode Bacon cipher sequences back to plaintext."""
plaintext_chars: List[str] = []
for code in ciphertext.split():
if len(code) == 5 and all(c in 'AB' for c in code):
binary = ''.join('1' if c == 'B' else '0' for c in code)
idx = int(binary, 2)
letter = chr(ord('A') + idx)
# Adjust for I/J and U/V merge
if letter == 'I':
plaintext_chars.append('I')
elif letter == 'U':
plaintext_chars.append('U')
elif letter == 'J':
plaintext_chars.append('J')
elif letter == 'V':
plaintext_chars.append('V')
elif letter in BAcon_MAP:
plaintext_chars.append(letter)
else:
plaintext_chars.append('?')
else:
plaintext_chars.append(code)
return ''.join(plaintext_chars)
Example Usage
Here’s how to use the functions:
# Encryption demonstration
msg = "SECRET"
cipher = encode_to_bacon(msg)
print(f"Plaintext: {msg}")
print(f"Ciphertext: {cipher}")
# Output:
# Plaintext: SECRET
# Ciphertext: ABBAA BAABA AABAB AABAB ABBAB AABBA
# Decryption demonstration
encoded = "AAAAA AAAAB AAABA AAABB AABAA"
plain = decode_from_bacon(encoded)
print(f"Ciphertext: {encoded}")
print(f"Plaintext: {plain}")
# Output:
# Ciphertext: AAAAA AAAAB AAABA AAABB AABAA
# Plaintext: HELLO
Key Implementation Details
- The cipher table is generated programmatically to ensure consistency and reduce hardcoedd data.
- Non-alphabetic characters are preservde during encryption and passed through unchanged during decryption.
- I/J and U/V are treated as identical for encoding, reflecting historical Bacon conventions.
- The implementation avoids external dependencies and supports Python 3.6+.
This approach ensures correctness, extensibility, and readability for educational or cryptographic prototyping purposes.