Skip to content

Commit

Permalink
Initial batch of attacks
Browse files Browse the repository at this point in the history
  • Loading branch information
jvdsn committed Jun 19, 2020
1 parent 68e9370 commit c99686e
Show file tree
Hide file tree
Showing 37 changed files with 1,457 additions and 0 deletions.
77 changes: 77 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Crypto attacks
Python implementations of cryptographic attacks and utilities.

## Requirements
* PyCryptodome
* SageMath

## Implementations
### CBC
* [x] [Bit flipping attack](cbc/bit_flipping.py)
* [x] [IV recovery attack](cbc/iv_recovery.py)
* [x] [Padding oracle attack](cbc/padding_oracle.py)

### CBC + CBC-MAC
* [x] [Key reuse attack (encrypt-and-MAC)](cbc_and_cbc_mac/eam_key_reuse.py)
* [x] [Key reuse attack (encrypt-then-MAC)](cbc_and_cbc_mac/eam_key_reuse.py)
* [x] [Key reuse attack (MAC-then-encrypt)](cbc_and_cbc_mac/eam_key_reuse.py)

### CBC-MAC
* [x] [Length extension attack](cbc_mac/length_extension.py)

### CTR
* [x] [CRIME attack](ctr/crime.py)

### ECB
* [x] [Plaintext recovery attack](ecb/plaintext_recovery.py)

### ElGamal Encryption
* [x] [Nonce reuse attack](elgamal_encryption/nonce_reuse.py)

### ElgGamal Signature
* [ ] Bleichenbacher's attack
* [ ] Khadir's attack
* [x] [Nonce reuse attack](elgamal_signature/nonce_reuse.py)

### Factorization
* [x] [Coppersmith factorization](factorization/coppersmith.py)
* [x] [Fermat factorization](factorization/fermat.py)
* [x] [Pollard's Rho factorization](factorization/pollard_rho.py)
* [x] [ROCA](factorization/roca.py) [More information: Nemec M. et al., "The Return of Coppersmith’s A‚ttack: Practical Factorization of Widely Used RSA Moduli"]
* [x] [Twin primes factorization](factorization/twin_primes.py)

### GCM
* [x] [Forbidden attack](gcm/forbidden_attack.py) [More information: Joux A., "Authentication Failures in NIST version of GCM"]

##### OFB
* [x] [CRIME attack](ofb/crime.py)

### Pseudoprimes
* [x] [Generating Miller-Rabin pseudoprimes](pseudoprimes/miller_rabin.py)

### RSA
* [ ] Bleichenbacher's CCA attack
* [x] [Bleichenbacher's signature forgery attack](rsa/bleichenbacher_signature_forgery.py)
* [x] [Boneh-Durfee attack](rsa/boneh_durfee.py) [More information: Boneh D., Durfee G., "Cryptanalysis of RSA with Private Key d Less than N^0.292"]
* [x] [Common modulus attack](rsa/common_modulus.py)
* [x] [Common prime factor attack](rsa/common_prime_factor.py)
* [x] [CRT fault attack](rsa/crt_fault_attack.py)
* [x] [Extended Wiener's attack](rsa/extended_wiener_attack.py) [More information: Dujella A., "Continued fractions and RSA with small secret exponent"]
* [x] [Hastad's broadcast attack](rsa/hastad_attack.py)
* [x] [Low public exponent attack](rsa/low_exponent.py)
* [ ] LSB oracle attack
* [ ] Manger's attack
* [x] [Partial key exposure attack for low public exponents](rsa/partial_key_exposure.py) [More information: Boneh D., Durfee G., Frankel Y., "An Attack on RSA Given a Small Fraction of the Private Key Bits"]
* [x] [Related message attack](rsa/related_message.py)
* [x] [Stereotyped message attack](rsa/stereotyped_message.py)
* [x] [Wiener's attack](rsa/wiener_attack.py)

### Shamir's Secret Sharing
* [x] [Deterministic coefficients](shamir_secret_sharing/deterministic_coefficients.py)
* [x] [Share forgery](shamir_secret_sharing/share_forgery.py)

### Small roots
* [x] [Boneh-Durfee method](small_roots/boneh_durfee.py) [More information: Boneh D., Durfee G., "Cryptanalysis of RSA with Private Key d Less than N^0.292"]
* [x] [Coron method](small_roots/coron.py) [More information: Coron J., "Finding Small Roots of Bivariate Integer Polynomial Equations: a Direct Approach"]
* [x] [Howgrave-Graham method](small_roots/howgrave_graham.py) [More information: May A., "New RSA Vulnerabilities Using Lattice Reduction Methods"]
* [ ] Jochemsz-May method [More information: Jochemsz E., May A., "A Strategy for Finding Roots of Multivariate Polynomials with New Applications in Attacking RSA Variants"]
19 changes: 19 additions & 0 deletions cbc/bit_flipping.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
def attack(iv, c, pos, p, p_):
"""
Replaces the original plaintext with a new plaintext at a position in the ciphertext
:param iv: the initialization vector
:param c: the ciphertext
:param pos: the position to modify at
:param p: the original plaintext
:param p_: the new plaintext
:return: a tuple containing the modified initialization vector and the modified ciphertext
"""
iv_ = bytearray(iv)
c_ = bytearray(c)
for i in range(len(p)):
if pos + i < 16:
iv_[pos + i] = iv[pos + i] ^ p[i] ^ p_[i]
else:
c_[pos + i - 16] = c[pos + i - 16] ^ p[i] ^ p_[i]

return bytes(iv_), bytes(c_)
22 changes: 22 additions & 0 deletions cbc/iv_recovery.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
from Crypto.Util.strxor import strxor

key = get_random_bytes(16)


def _encrypt(p):
return AES.new(key, AES.MODE_CBC, key).encrypt(p)


def _decrypt(c):
return AES.new(key, AES.MODE_CBC, key).decrypt(c)


def attack():
"""
Recovers the initialization vector using a chosen-ciphertext attack.
:return: the initialization vector
"""
p = _decrypt(bytes(32))
return strxor(p[:16], p[16:])
66 changes: 66 additions & 0 deletions cbc/padding_oracle.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
from Crypto.Util.Padding import pad
from Crypto.Util.Padding import unpad

key = get_random_bytes(16)


def _encrypt(p):
iv = get_random_bytes(16)
return iv, AES.new(key, AES.MODE_CBC, iv).encrypt(pad(p, 16))


def _valid_padding(iv, c):
try:
unpad(AES.new(key, AES.MODE_CBC, iv).decrypt(c), 16)
return True
except ValueError:
return False


def _correct_padding(iv, c, i):
if not _valid_padding(iv, c):
return False

# Special handling for last byte of last block
if i == 15:
iv[14] ^= 1
return _valid_padding(iv, c)

return True


def _attack_block(iv, c):
dc = bytearray(16)
p = bytearray(16)
iv_ = bytearray(iv)
for i in reversed(range(16)):
# The padding byte for this position.
pb = 16 - i
# Apply padding byte to iv.
for j in reversed(range(i + 1, 16)):
iv_[j] = dc[j] ^ pb

# Try every byte until padding is correct.
for b in range(256):
iv_[i] = b
if _correct_padding(iv_, c, i):
dc[i] = b ^ pb
p[i] = dc[i] ^ iv[i]

return p


def attack(iv, c):
"""
Recovers the plaintext using the padding oracle attack.
:param iv: the initialization vector
:param c: the ciphertext
:return: the plaintext
"""
p = _attack_block(iv, c)
for i in range(16, len(c), 16):
p += _attack_block(c[i - 16:i], c[i:i + 16])

return unpad(p, 16)
40 changes: 40 additions & 0 deletions cbc_and_cbc_mac/eam_key_reuse.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
from Crypto.Util.Padding import pad
from Crypto.Util.Padding import unpad

zero_iv = bytes(16)
key = get_random_bytes(16)


# Notice how the key is used for encryption and authentication...
def _encrypt(p):
p = pad(p, 16)
iv = get_random_bytes(16)
c = AES.new(key, AES.MODE_CBC, iv).encrypt(p)
# Encrypt-and-MAC using CBC-MAC to prevent chosen-ciphertext attacks.
t = AES.new(key, AES.MODE_CBC, zero_iv).encrypt(p)[-16:]
return iv, c, t


def _decrypt(iv, c, t):
p = AES.new(key, AES.MODE_CBC, iv).decrypt(c)
t_ = AES.new(key, AES.MODE_CBC, zero_iv).encrypt(p)[-16:]
# Check the MAC to be sure the message isn't forged.
if t != t_:
return None

return unpad(p, 16)


def attack(iv, c, t):
"""
Uses a chosen-ciphertext attack to decrypt the ciphertext.
:param iv: the initialization vector
:param c: the ciphertext
:param t: the tag corresponding to the ciphertext
:return: the plaintext
"""
c_ = iv + c
p_ = _decrypt(bytes(16), c_, c[-16:])
return p_[16:]
41 changes: 41 additions & 0 deletions cbc_and_cbc_mac/etm_key_reuse.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
from Crypto.Util.Padding import pad
from Crypto.Util.Padding import unpad

zero_iv = bytes(16)
key = get_random_bytes(16)


# Notice how the key is used for encryption and authentication...
def _encrypt(p):
p = pad(p, 16)
iv = get_random_bytes(16)
c = AES.new(key, AES.MODE_CBC, iv).encrypt(p)
# Encrypt-then-MAC using CBC-MAC to prevent chosen-ciphertext attacks.
t = AES.new(key, AES.MODE_CBC, zero_iv).encrypt(iv + c)[-16:]
return iv, c, t


def _decrypt(iv, c, t):
t_ = AES.new(key, AES.MODE_CBC, zero_iv).encrypt(iv + c)[-16:]
# Check the MAC to be sure the message isn't forged.
if t != t_:
return None

return unpad(AES.new(key, AES.MODE_CBC, iv).decrypt(c), 16)


def attack(iv, c, t):
"""
Uses a chosen-ciphertext attack to decrypt the ciphertext.
:param iv: the initialization vector
:param c: the ciphertext
:param t: the tag corresponding to the ciphertext
:return: the plaintext
"""
p_ = bytes(16) + iv + c
iv_, c_, t_ = _encrypt(p_)
c__ = iv + c
p__ = _decrypt(iv_, c__, c_[-32:-16])
return p__[16:]
43 changes: 43 additions & 0 deletions cbc_and_cbc_mac/mte_key_reuse.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
from Crypto.Util.Padding import pad
from Crypto.Util.Padding import unpad

zero_iv = bytes(16)
key = get_random_bytes(16)


# Notice how the key is used for encryption and authentication...
def _encrypt(p):
p = pad(p, 16)
iv = get_random_bytes(16)
# MAC-then-encrypt using CBC-MAC to prevent chosen-ciphertext attacks.
t = AES.new(key, AES.MODE_CBC, zero_iv).encrypt(p)[-16:]
c = AES.new(key, AES.MODE_CBC, iv).encrypt(p + t)
return iv, c


def _decrypt(iv, c):
d = AES.new(key, AES.MODE_CBC, iv).decrypt(c)
p = d[:-16]
t = d[-16:]
t_ = AES.new(key, AES.MODE_CBC, zero_iv).encrypt(p)[-16:]
# Check the MAC to be sure the message isn't forged.
if t != t_:
return None

return unpad(p, 16)


def attack(iv, c, encrypted_zeroes):
"""
Uses a chosen-ciphertext attack to decrypt the ciphertext.
Prior knowledge of E_k(0^16) is required for this attack to work.
:param iv: the initialization vector
:param c: the ciphertext
:param encrypted_zeroes: a full zero block encrypted using the key
:return: the plaintext
"""
c_ = iv + c[:-16] + encrypted_zeroes
p_ = _decrypt(bytes(16), c_)
return p_[16:]
9 changes: 9 additions & 0 deletions cbc_mac/length_extension.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from Crypto.Util.strxor import strxor


def attack(m1, t1, m2, t2):
m3 = bytearray(m1)
m3 += strxor(t1, m2[:16])
for i in range(16, len(m2), 16):
m3 += m2[i:i + 16]
return m3, t2
35 changes: 35 additions & 0 deletions ctr/crime.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import zlib

from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
from Crypto.Util import Counter

key = get_random_bytes(16)
secret = get_random_bytes(16)


def _encrypt(p):
return AES.new(key, AES.MODE_CTR, counter=Counter.new(128)).encrypt(zlib.compress(p + secret))


def attack(secret_len):
"""
Recovers a secret using the CRIME attack (CTR version).
:param secret_len: the length of the secret to recover
:return: the secret
"""
padding = bytearray()
for i in range(secret_len):
padding.append(i)

s = bytearray()
for i in range(secret_len):
min = None
for j in range(256):
l = len(_encrypt(padding + s + bytes([j]) + padding))
if min is None or l < min[0]:
min = (l, j)

s.append(min[1])

return bytes(s)
Loading

0 comments on commit c99686e

Please sign in to comment.