diff options
Diffstat (limited to 'aesgcmanalysis.py')
-rw-r--r-- | aesgcmanalysis.py | 150 |
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() |