Symmetric crypto: OTP, AES modes, CRIME
One-time pad (OTP)
From Wikipedia:
One-time pad is an encryption technique that can’t be cracked in cryptography. It requires the use of a single-use pre-shared key that is larger than, or equal to the message being encrypted. In this technique, the plaintext is paired with a secret key (also referred to as a one-time pad). Then, each bit or character of the plaintext is encrypted by combining it with the corresponding bit or character from the pad using modular addition.
There are a number of criteria to make it impossible to decrypt, some of which are not possible in practice, but we can come pretty close. Here are the criteria:
- The key must be at least as long as the plaintext
- The key must be truly random
- The key must never be reused in whole or in part.
- The key must be kept completely secret by the communicating parties.
These requirements make the OTP the only known encryption system that is mathematically proven to be unbreakable under the principles of information theory.
Tools
- Crib drag/Many-time pad:
- For when the key is reused in whole or in part, or a part of the plaintext is known and the key can be inferred.
- MTP interactive
- cribdrag.com
Advanced Encryption Standard (AES)
Electronic codebook (ECB)
Attacks
- Plaintext recovery with byte-at-a-time bruteforcing
Cipher block chaining (CBC)
Vulnerabilities
- Insecure IV (reuse, predictable, etc.) can enable what is known as a TLS CBC IV attack
Attacks
- TLS CBC IV attack for known IV
- Bit flipping
- Byte-at-a-time using a padding oracle
- Guide from NCCGroup on exploiting an AES CBC padding oracle
Counter (CTR)
Is vulnerable to bit flipping. A known plaintext will reveal a part of the keystream, and if the keystream is reused, it will allow for decrypting a second ciphertext, just like a many-time pad.
CRIME
Read about it on Wikipedia
Here is an implementation of the attack:
# pip install pwntools requests tqdm
from string import printable
from pwn import enhex, unhex
from requests import get
from tqdm import trange
def encrypt(pt: bytes) -> bytes:
"""encrypt plaintext using CTR mode with compression
Args:
pt (bytes): plaintext to encrypt
Returns:
bytes: the encrypted ciphertext
"""
return ...
def crime(plaintext: str, target_ct_length: int) -> list[tuple[str, int | None]]:
ct_lengths = {}
for i in trange(len(printable)):
c = printable[i]
ct_length = len(encrypt(bytes(plaintext + c, "utf-8")))
if target_ct_length is not None and ct_length == target_ct_length:
return [plaintext + c, target_ct_length]
if ct_length in ct_lengths:
ct_lengths[ct_length].append(c)
else:
ct_lengths[ct_length] = [c]
if len(ct_lengths[min(ct_lengths)]) > 1:
matches = ct_lengths[min(ct_lengths)]
print("Found multiple possible matches. To continue, please choose one of:")
print(", ".join(matches))
print("Or leave the input field empty to quit")
while True:
chosen_match = input()
if chosen_match in matches or chosen_match == "":
break
print("Choose one of the characters listed above")
else:
target_ct_length = min(ct_lengths)
chosen_match = ct_lengths[target_ct_length][0]
return [plaintext + chosen_match, target_ct_length]
plaintext = input("Known plaintext beginning: ")
target_ct_length = int(input("Target ciphertext length: "))
while True:
plaintext, target_ct_length = crime(plaintext, target_ct_length)
print("Plaintext:", plaintext)