summaryrefslogtreecommitdiff
path: root/aesgcmanalysis.py
diff options
context:
space:
mode:
Diffstat (limited to 'aesgcmanalysis.py')
-rw-r--r--aesgcmanalysis.py150
1 files changed, 143 insertions, 7 deletions
diff --git a/aesgcmanalysis.py b/aesgcmanalysis.py
index 259cf69..b833376 100644
--- a/aesgcmanalysis.py
+++ b/aesgcmanalysis.py
@@ -1,3 +1,4 @@
+from binascii import unhexlify
import random, struct, hmac, itertools, math
from Crypto.Cipher import AES
import cryptography
@@ -600,7 +601,7 @@ def nonce_reuse_recover_secrets(nonce, aad1, aad2, c1, c2, mac1, mac2):
secrets.append((h, s))
return secrets
-## Nonce Truncation Attack
+## MAC Truncation Attack
def gen_blocks(n, js):
blocks = b'\x00'*16*n
@@ -689,7 +690,7 @@ def find_b(n, basis, ct, mac, nonce, aad, oracle):
base = orig_base.copy()
idx += 1
-def nonce_truncation_recover_secrets(ct, mac, nonce, mac_bytes, aad, oracle, compute_T_once=False):
+def mac_truncation_recover_secrets(ct, mac, nonce, mac_bytes, aad, oracle, compute_T_once=False):
orig_ct = ct
ct = aad + ct
n = compute_n(ct)
@@ -743,6 +744,134 @@ def nonce_truncation_recover_secrets(ct, mac, nonce, mac_bytes, aad, oracle, com
s = (np.array(zero_tag) - np.array(mac_vec)) % 2
return vec_to_gf128(h), vec_to_gf128(s)
+# Key Commitment Attack
+
+def gmac_blind(k, nonce):
+ return bytes_to_gf128(ecb_encrypt(k, nonce + b'\x00\x00\x00\x01'))
+def encode_lengths(ad_length, ct_length):
+ return struct.pack('>QQ', ad_length*8, ct_length*8)
+def collide(k1, k2, nonce, c):
+ h1 = gmac_key(k1)
+ h2 = gmac_key(k2)
+ p1 = gmac_blind(k1, nonce)
+ p2 = gmac_blind(k2, nonce)
+ assert len(c) % 16 == 0
+ mlen = len(c)//16+1
+ lens = bytes_to_gf128(encode_lengths(0, len(c) + 16))
+ acc = gf128_mul(lens, gf128_add(h1, h2))
+ acc = gf128_add(acc, gf128_add(p1, p2))
+ for i in range(1, mlen):
+ if i % 2048 == 0:
+ print(i, end=', ')
+ hi = gf128_add(gf128_exp(h1, mlen+2-i), gf128_exp(h2, mlen+2-i))
+ acc = gf128_add(acc, gf128_mul(bytes_to_gf128(c[(i-1)*16:((i-1)+1)*16]), hi))
+ inv = gf128_inv(gf128_add(gf128_mul(h1, h1), gf128_mul(h2, h2)))
+ c_append = gf128_mul(acc, inv)
+ c_ = c + gf128_to_bytes(c_append)
+ mac = gmac(h1, p1, b'', c_)
+ return (c_, mac)
+
+def collide_penultimate(k1, k2, nonce, c):
+ h1 = authentication_key(k1)
+ h2 = authentication_key(k2)
+ p1 = gmac_blind(k1, nonce)
+ p2 = gmac_blind(k2, nonce)
+ assert len(c) % 16 == 0
+ mlen = len(c)//16
+ lens = bytes_to_gf128(encode_lengths(0, len(c)))
+ acc = gf128_mul(lens, gf128_add(h1, h2))
+ acc = gf128_add(acc, gf128_add(p1, p2))
+ n=4
+ h1Running = gf128_exp(h1, 4)
+ h2Running = gf128_exp(h2, 4)
+ for i in reversed(range(0, mlen-2)):
+ # print(mlen+1-(i))
+ # i = mlen-2-1-i
+ hi = gf128_add(h1Running, h2Running)
+ h1Running = gf128_mul(h1Running, h1)
+ h2Running = gf128_mul(h2Running, h2)
+ n+=1
+ # hi = gf128_add(gf128_exp(h1, mlen+1-i), gf128_exp(h2, mlen+1-i))
+ #print('block', i, pt[i*16:(i+1)*16], 'exp', mlen+1-i)
+ acc = gf128_add(acc, gf128_mul(bytes_to_gf128(c[i*16:(i+1)*16]), hi))
+ # for i in range(0, mlen-2):
+ # print(i,mlen+1-i)
+ # hi = gf128_add(gf128_exp(h1, mlen+1-i), gf128_exp(h2, mlen+1-i))
+ # acc = gf128_add(acc, gf128_mul(bytes_to_gf128(c[i*16:(i+1)*16]), hi))
+ hi = gf128_add(gf128_exp(h1, 2), gf128_exp(h2, 2))
+ i = mlen-1
+ #print('block', i, pt[i*16:(i+1)*16], 'exp', 2)
+ acc = gf128_add(acc, gf128_mul(bytes_to_gf128(c[i*16:(i+1)*16]), hi))
+ inv = gf128_inv(gf128_add(gf128_exp(h1, 3), gf128_exp(h2, 3)))
+ c_append = gf128_mul(acc, inv)
+ c_ = c[:-32] + gf128_to_bytes(c_append) + c[-16:]
+ mac = gmac(h1, p1, b'', c_)
+ return (c_, mac)
+
+def gctr_oneblock(k, pt, nonce):
+ enckeyval = nonce + b'\x00\x00\x00\x02'
+ stream = ecb_encrypt(k, enckeyval)
+ return xor(pt, stream)
+
+def key_search(nonce, init_bytes1, init_bytes2):
+ seen1 = dict()
+ seen2 = dict()
+ while True:
+ k1 = secrets.token_bytes(16)
+ k2 = secrets.token_bytes(16)
+ ct1 = gctr_oneblock(k1, init_bytes1, nonce)
+ ct2 = gctr_oneblock(k2, init_bytes2, nonce)
+ seen1[ct1] = k1
+ seen2[ct2] = k2
+ if ct1 in seen2:
+ return k1, seen2[ct1]
+ if ct2 in seen1:
+ return seen1[ct2], k2
+
+def att_merge_jpg_bmp(jpg, bmp, aad):
+ # Precomputed with key_search; works for any files
+ k1 = unhexlify('5c3cb198432b0903e58de9c9647bd241')
+ k2 = unhexlify('df923ae8976230008a081d23205d7a4f')
+ nonce = b'JORGELBORGES'
+
+ total_len = 6 + (0xff<<8) + 0xff + len(jpg)
+ jpgstream, _ = gcm_encrypt(k1, nonce, aad, b'\x00'*total_len)
+ bmpstream, _ = gcm_encrypt(k2, nonce, aad, b'\x00'*total_len)
+ print("enc")
+
+ # 5 bytes
+ r = xor(jpgstream, b'\xff\xd8\xff\xfe\xff')
+
+ # 1 byte
+ r += bmpstream[5:6]
+
+ # len(bmp) bytes
+ bmpenc = xor(bmp[6:], bmpstream[6:6+len(bmp)])
+ r += bmpenc
+
+ comlen = (0xff << 8) + (jpgstream[5] ^ bmpstream[5])
+
+ # finish comment with padding
+ r += b'\x00'*(comlen - len(bmpenc))
+
+ # jpg
+ r += xor(jpg[2:-2], jpgstream[6+comlen:])
+
+ # comment; include penultimate block to be overwritten; therefore must be at least 3 blocks long
+ # also serves to block-align to 14 bytes so the final ciphertext will be complete blocks
+
+ endcomlen = (28 - (len(r) % 16)) + 16 + 14
+
+ tail = b'\xff\xfe' + struct.pack('>H', endcomlen) + b'\x00'*endcomlen + b'\xff\xd9'
+ tailx = xor(tail, jpgstream[6+comlen+len(jpg)-4:])
+ r += tailx
+ assert len(r) % 16 == 0
+ print("collide")
+
+ cfin, macfin = collide_penultimate(k1, k2, nonce, r)
+
+ return cfin, macfin
+
# Demos
def forbidden_attack_demo():
@@ -777,7 +906,7 @@ def forbidden_attack_demo():
pass
assert succeeded
-def nonce_truncation_demo():
+def mac_truncation_demo():
# Doesn't work with non-block size multiples.
# Need to modify to consider padding, but we can't mess with the bits in the padding,
# nor can we extend ad/ct unless we also change length block.
@@ -795,10 +924,17 @@ def nonce_truncation_demo():
decryptor.authenticate_additional_data(aad)
decryptor.update(base) + decryptor.finalize()
- h, s = nonce_truncation_recover_secrets(ct, mac, nonce, mac_bytes, aad, oracle, compute_T_once=mac_bytes==1)
+ h, s = mac_truncation_recover_secrets(ct, mac, nonce, mac_bytes, aad, oracle, compute_T_once=mac_bytes==1)
assert h == authentication_key(k)
if __name__ == "__main__":
- import resource
- nonce_truncation_demo()
- print(resource.getrusage(resource.RUSAGE_SELF).ru_maxrss)
+ pass
+ # mac_truncation_demo()
+ jpg = open('static/axolotl.jpg', 'rb').read()
+ bmp = open('static/kitten.bmp', 'rb').read()
+ c, mac = att_merge_jpg_bmp(jpg, bmp, aad=b"")
+ print(mac.hex())
+ f = open('c.txt', 'wb')
+ f.write(c)
+ f.write(mac)
+ f.close()