Bacon Cipher Implementation in Python

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.

Tags: Bacon-cipher substitution-cipher python cryptography encoding

Posted on Sat, 10 Oct 2026 16:54:45 +0000 by JayNak