The substitution ciphers are one of the oldest families of cryptographic algorithms. Its principle is simple: each letter (or group of letters) in the original text is replaced by another letter according to a defined rule. Although these ciphers are trivial to break with modern techniques, their study is essential to understanding the principles of cryptography and the historical evolution of secure communications. In this first part we will cover three classic ciphers: Cease, Polybios and ROT13.
Caesar Cipher
He Caesar cipher It is the best-known substitution cipher in history. Used by Julius Caesar to communicate with his generals, it consists of moving each letter of the alphabet a fixed number of positions. If the offset is 3 (the one Caesar used), A becomes D, B becomes E, and so on:
Alfabeto original: A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
Desplazamiento +3: D E F G H I J K L M N O P Q R S T U V W X Y Z A B C
Texto original: ATACAR AL AMANECER
Texto cifrado: DWDFDU DO DPDQHFHUThe mathematical formula of the Caesar cipher is:
Cifrado: C(x) = (x + k) mod 26
Descifrado: D(x) = (x - k) mod 26
Donde:
x = posición de la letra (A=0, B=1, ..., Z=25)
k = clave (desplazamiento)Implementation in Python:
def cifrado_cesar(texto, desplazamiento, descifrar=False):
"""Cifrado/descifrado César."""
if descifrar:
desplazamiento = -desplazamiento
resultado = []
for char in texto:
if char.isalpha():
base = ord('A') if char.isupper() else ord('a')
resultado.append(chr((ord(char) - base + desplazamiento) % 26 + base))
else:
resultado.append(char)
return ''.join(resultado)
# Cifrar
texto = "ATACAR AL AMANECER"
cifrado = cifrado_cesar(texto, 3)
print(f"Cifrado: {cifrado}") # DWDFDU DO DPDQHFHU
# Descifrar
descifrado = cifrado_cesar(cifrado, 3, descifrar=True)
print(f"Descifrado: {descifrado}") # ATACAR AL AMANECERCryptanalysis of the Caesar Cipher
The Caesar cipher is extremely weak because it only has 25 possible keys (displacements from 1 to 25). A brute force attack is trivial:
def fuerza_bruta_cesar(texto_cifrado):
"""Probar todas las claves posibles."""
print("=== Fuerza bruta César ===")
for k in range(1, 26):
descifrado = cifrado_cesar(texto_cifrado, k, descifrar=True)
print(f" Clave {k:2d}: {descifrado}")
fuerza_bruta_cesar("DWDFDU DO DPDQHFHU")It can also be broken by frequency analysis: in Spanish, the most frequent letter is E (~13.7%). If the most frequent letter in the ciphertext is H, the probable offset is 3 (H - E = 3).
Polybios encryption
He Polybios square It was invented by the Greek historian Polybios in the 2nd century BC. Convert each letter to a pair of numerical coordinates using a 5x5 grid:
Cuadrado de Polybios (sin Ñ, I=J):
1 2 3 4 5
1 A B C D E
2 F G H I K
3 L M N O P
4 Q R S T U
5 V W X Y Z
Ejemplo:
H = fila 2, columna 3 = 23
O = fila 3, columna 4 = 34
L = fila 3, columna 1 = 31
A = fila 1, columna 1 = 11
HOLA = 23 34 31 11Implementation in Python:
def polybios_cifrar(texto):
"""Cifrar texto usando el cuadrado de Polybios."""
cuadrado = "ABCDEFGHIKLMNOPQRSTUVWXYZ" # Sin J (I=J)
resultado = []
for char in texto.upper():
if char == 'J':
char = 'I'
if char in cuadrado:
idx = cuadrado.index(char)
fila = idx // 5 + 1
col = idx % 5 + 1
resultado.append(f"{fila}{col}")
elif char == ' ':
resultado.append(' ')
return ' '.join(resultado) if ' ' not in resultado else ''.join(resultado)
def polybios_descifrar(cifrado):
"""Descifrar texto cifrado con Polybios."""
cuadrado = "ABCDEFGHIKLMNOPQRSTUVWXYZ"
numeros = cifrado.replace(' ', '')
resultado = []
for i in range(0, len(numeros), 2):
fila = int(numeros[i]) - 1
col = int(numeros[i+1]) - 1
resultado.append(cuadrado[fila * 5 + col])
return ''.join(resultado)
cifrado = polybios_cifrar("HOLA MUNDO")
print(f"Cifrado: {cifrado}")
descifrado = polybios_descifrar("2334311132453334")
print(f"Descifrado: {descifrado}")The Polybios cipher is historically interesting because it converts letters into numbers, which facilitated transmission by signals (torches, flags). It is the basis of more complex ciphers such as ADFGVX, used by the German army in World War I.
ROT13
ROT13 It is a particular case of the Caesar cipher with a 13-position shift. Its most notable property is that it is involutive: Applying ROT13 twice returns the original text, since 13 + 13 = 26 (the length of the alphabet):
Original: A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
ROT13: N O P Q R S T U V W X Y Z A B C D E F G H I J K L M
Texto: SECRETO
ROT13: FRPERGB
ROT13x2: SECRETO (vuelve al original)import codecs
# ROT13 en Python (método estándar)
texto = "SECRETO"
cifrado = codecs.encode(texto, 'rot_13')
print(f"ROT13: {cifrado}") # FRPERGB
# Descifrar es aplicar ROT13 de nuevo
descifrado = codecs.encode(cifrado, 'rot_13')
print(f"Original: {descifrado}") # SECRETO
# También funciona con la función César que ya definimos
rot13 = cifrado_cesar("SECRETO", 13)
print(f"ROT13: {rot13}") # FRPERGBROT13 is not considered a security encryption — it is a obfuscation. It is historically used on internet forums to hide spoilers, answers to puzzles, or content that the reader must consciously choose to decipher. It also frequently appears in CTF (Capture The Flag) challenges as an initial decoding step.
Comparison of the three ciphers
Cifrado | Claves posibles | Tipo | Resistencia
------------|-----------------|----------------|------------------
César | 25 | Monoalfabético | Fuerza bruta trivial
Polybios | 1 (fijo) | Fraccional | Sin clave, solo ofuscación
ROT13 | 1 (fijo, k=13) | Monoalfabético | Sin seguridad realThese three ciphers share a fundamental weakness: they are monoalphabetic, meaning that each letter in the original text is always replaced by the same letter in the ciphertext. This makes them vulnerable to frequency analysis. In the second part of this series we will explore encryption polyalphabetic such as Vigenère and Playfair, which partially resolve this weakness.
:wq!
Comments