From 36164dde3e8b9bda17a01852557c92b7b1250c6e Mon Sep 17 00:00:00 2001 From: Maxime Vorwerk Date: Wed, 30 Oct 2024 16:08:01 +0100 Subject: [PATCH] PowerAnalysis: warmup --- poweranalysis_warmup/encrypt.py | 53 ++++++++++++++++++++++ poweranalysis_warmup/key.txt | 1 + poweranalysis_warmup/out | 1 + poweranalysis_warmup/sol.py | 78 +++++++++++++++++++++++++++++++++ 4 files changed, 133 insertions(+) create mode 100755 poweranalysis_warmup/encrypt.py create mode 100644 poweranalysis_warmup/key.txt create mode 100644 poweranalysis_warmup/out create mode 100755 poweranalysis_warmup/sol.py diff --git a/poweranalysis_warmup/encrypt.py b/poweranalysis_warmup/encrypt.py new file mode 100755 index 0000000..7ac4334 --- /dev/null +++ b/poweranalysis_warmup/encrypt.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 +import random, sys, time + +with open("key.txt", "r") as f: + SECRET_KEY = bytes.fromhex(f.read().strip()) + +Sbox = ( + 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, + 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, + 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, + 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, + 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, + 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, + 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, + 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, + 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, + 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, + 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, + 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, + 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, + 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, + 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, + 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16, +) + +# Leaks one bit of information every operation +leak_buf = [] +def leaky_aes_secret(data_byte, key_byte): + out = Sbox[data_byte ^ key_byte] + leak_buf.append(out & 0x01) + return out + +# Simplified version of AES with only a single encryption stage +def encrypt(plaintext, key): + global leak_buf + leak_buf = [] + ciphertext = [leaky_aes_secret(plaintext[i], key[i]) for i in range(16)] + return ciphertext + +# Leak the number of 1 bits in the lowest bit of every SBox output +def encrypt_and_leak(plaintext): + ciphertext = encrypt(plaintext, SECRET_KEY) + ciphertext = None # throw away result + time.sleep(0.01) + return leak_buf.count(1) + +pt = input("Please provide 16 bytes of plaintext encoded as hex: ") +if len(pt) != 32: + print("Invalid length") + sys.exit(0) + +pt = bytes.fromhex(pt) +print("leakage result:", encrypt_and_leak(pt)) diff --git a/poweranalysis_warmup/key.txt b/poweranalysis_warmup/key.txt new file mode 100644 index 0000000..e329e58 --- /dev/null +++ b/poweranalysis_warmup/key.txt @@ -0,0 +1 @@ +00112233445566778899aabbccddeeff diff --git a/poweranalysis_warmup/out b/poweranalysis_warmup/out new file mode 100644 index 0000000..7de1df8 --- /dev/null +++ b/poweranalysis_warmup/out @@ -0,0 +1 @@ +[[171], [135], [131], [202], [184], [245], [134], [92]] \ No newline at end of file diff --git a/poweranalysis_warmup/sol.py b/poweranalysis_warmup/sol.py new file mode 100755 index 0000000..f0123cb --- /dev/null +++ b/poweranalysis_warmup/sol.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python3 +from pwn import * + +host = "saturn.picoctf.net" +port = 52574 + +known_prefix = "810aa1112892fc24ab8783cab8f5865c" + +Sbox = ( + 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, + 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, + 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, + 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, + 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, + 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, + 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, + 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, + 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, + 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, + 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, + 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, + 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, + 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, + 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, + 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16, +) + +def fetch_leakage(text): + conn = remote(host, port, level="warn") + #conn = process(["python", "encrypt.py"], level="warn") + conn.recvuntil(b"hex:") + + hex_text = enhex(text) + conn.sendline(hex_text.encode()) + + conn.recvuntil(b"result:") + leak = int(conn.recvline().strip()) + conn.close() + + return leak + +log.info("preprocessing Sbox") +evens = [i for i, x in enumerate(Sbox) if x&0x01 == 0] +odds = [i for i, x in enumerate(Sbox) if x&0x01 == 1] + +text = [b"0"]*16 +solution = known_prefix +solution_complete = [] + +P=log.progress("solving characters") +for i in range(len(known_prefix)//2, len(text)): + leakages = [0]*256 + for j in range(256): + P.status(f"{i+1}/16, {j+1}/256, {solution}") + probe = text + probe[i] = p8(j) + probe = b"".join(probe) + leakages[j] = fetch_leakage(probe) + offset = min(leakages) + values = [n-offset for n in leakages] + possible_key_values=set(range(256)) + for j in range(256): + if values[j] == 0: + possible_key_values = possible_key_values.intersection(set([i^j for i in evens])) + else: + possible_key_values = possible_key_values.intersection(set([i^j for i in odds])) + if len(possible_key_values) == 1: + key = possible_key_values.pop() + solution += enhex(p8(key)) + possible_key_values.add(key) + else: + solution.append("XX") + solution_complete.append(list(possible_key_values)); +P.success(f"found solution {solution}") + +with open("out", "w") as f: + f.write(str(solution_complete)) +