HWBTutorials/dpa-attack/dpa_student.ipynb

362 lines
12 KiB
Plaintext

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Break AES using DPA with correlations\n",
"\n",
"You need:\n",
"* `plaintext.txt`: all PT blocks, (one block per line, in hex, bytes separated by spaces)\n",
"* `ciphertext.txt`: all CT blocks, (one block per line, in hex, bytes separated by spaces)\n",
"* `traceLength.txt`: how many samples per trace (one decimal number)\n",
"* `traces.bin`: raw measured traces, one byte per sample (uint8), all traces together continuously\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "GEwwR12Gupsi"
},
"outputs": [],
"source": [
"import matplotlib.pyplot as plt\n",
"import numpy as np"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "8fW8nPQ5uyEO"
},
"outputs": [],
"source": [
"# AES SBOX\n",
"sbox = np.array([\n",
" 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,\n",
" 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,\n",
" 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,\n",
" 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,\n",
" 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,\n",
" 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,\n",
" 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,\n",
" 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,\n",
" 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,\n",
" 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,\n",
" 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,\n",
" 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,\n",
" 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,\n",
" 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,\n",
" 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,\n",
" 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16\n",
" ], dtype='uint8')\n",
"\n",
"# Hamming weight lookup table\n",
"hw_table = []\n",
"for i in range(256):\n",
" s = '{0:08b}'.format(i)\n",
" hw_table.append(s.count('1'))\n",
"hw_table = np.array(hw_table, 'uint8')\n",
"\n",
"# Correlation of two matrices\n",
"def correlate(x, y):\n",
" \"\"\"\n",
" Correlate all columns from matrix x of shape (a,b)\n",
" with all columns from matrix y of shape (a,c),\n",
" creating correlation matrix C of shape (b,c).\n",
" \n",
" Originally matlab script by Jiri Bucek in NI-HWB.\n",
" \"\"\"\n",
" x = x - np.average(x, 0) # remove vertical averages\n",
" y = y - np.average(y, 0) # remove vertical averages\n",
" C = x.T @ y # (n-1) Cov(x,y)\n",
" C = C / (np.sum(x**2, 0)**(1/2))[:,np.newaxis] # divide by (n-1) Var(x)\n",
" C = C / (np.sum(y**2, 0)**(1/2)) # divide by (n-1) Var(y)\n",
" return C\n",
"\n",
"# Load PT of CT from file\n",
"def load_text(file_name):\n",
" \"\"\"\n",
" Load any text PT/CT from file containing hex strings with bytes \n",
" separated by spaces, one block per line\n",
" Output is a matrix of bytes (np.array)\n",
" \"\"\"\n",
" txt_str = open(file_name).readlines()\n",
" del txt_str[-1] #discard last empty line\n",
" #split each line into bytes and convert from hex\n",
" txt_bytes_list = list(\n",
" map(lambda line: \n",
" list(\n",
" map(lambda s: int(s, 16),\n",
" line.rstrip().split(\" \"))\n",
" ),\n",
" txt_str)\n",
" )\n",
" return np.array(txt_bytes_list, 'uint8')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "--PH16eNuz_H"
},
"outputs": [],
"source": [
"# read plaintext inputs\n",
"inputs = load_text(\"plaintext.txt\")\n",
"\n",
"# read length of one complete trace (number of samples per trace)\n",
"with open(\"traceLength.txt\", \"r\") as fin:\n",
" trace_length = int(fin.readline())\n",
"\n",
"# trim each trace - select interesting part\n",
"start = 0\n",
"len = trace_length # CHANGE to the length of the first round; \n",
"\n",
"# read traces from binary file\n",
"traces = np.fromfile(\"traces.bin\", dtype='uint8') # read as linear array\n",
"traces = np.reshape(traces, (traces.size // trace_length, trace_length)) # reshape into matrix\n",
"traces = traces[:, start:len] # select only the interesting part of each trace"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "ZVJ_Tk55u1wu"
},
"outputs": [],
"source": [
"print(inputs.shape) # dimensions of inputs\n",
"print(trace_length)\n",
"print(traces.shape) # dimensions of matrix of traces"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "6hzUcHiWxyH0"
},
"outputs": [],
"source": [
"# If you feel brave enough -- interactive plots\n",
"#!pip install ipympl\n",
"#from google.colab import output\n",
"#output.enable_custom_widget_manager()\n",
"#%matplotlib widget"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "wDAUVmNOu3BP"
},
"outputs": [],
"source": [
"# Plot one trace\n",
"fig = plt.figure()\n",
"plt.plot(traces[0])\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "w6boaqAQvF1G"
},
"source": [
"## **Attack the first key byte**\n",
"![Intermediate value](dpa-aes-v.png)\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "WaKiOUmbvbQR"
},
"outputs": [],
"source": [
"# Generate key hypotheses (all possible byte values)\n",
"keys = np.arange(start=0, stop=256, step=1, dtype='uint8')\n",
"# Select the first byte of each input block\n",
"inp = inputs[:, 0]\n",
"# XOR each data byte with each key\n",
"xmat = inp[:, np.newaxis] ^ keys"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Examine the inputs matrix. Does it contain the data from plaintext.txt?\n",
"print(inputs)\n",
"# What is the shape of all the operands from the previous cell?\n",
"print(inputs.shape)\n",
"print(inp.shape)\n",
"print(inp[:, np.newaxis].shape)\n",
"print(keys.shape)\n",
"print(xmat.shape)\n",
"# Do you understand the values after the XOR operation? What AES operation do they represent?\n",
"print(xmat)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "VrBZd18VwBOH"
},
"outputs": [],
"source": [
"# Substitute with SBOX all XORed values -- matrix of intermediate values\n",
"smat = sbox[?]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "4GfR9BU-wT4G"
},
"outputs": [],
"source": [
"# Compute Hamming Weights -- the matrix of hypothetical power consumption\n",
"hmat = ?[?]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "J8TTPk-WwjQH"
},
"outputs": [],
"source": [
"# Compute the correlation matrix -- correlate the hypotheses with measured traces\n",
"print(hmat.shape)\n",
"print(traces.shape)\n",
"corr = correlate(?, ?)\n",
"# What is the shape and contents of the correlation matrix?"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "iOqbuNAKxCvP"
},
"outputs": [],
"source": [
"# Find the absolute maximum correlation\n",
"acorr = abs(?)\n",
"max_acorr = ?.max()\n",
"(k, j) = np.where(acorr == ?) # find idices of maximum\n",
"print(\"key: %d time: %d\" % (k[0], j[0]))\n",
"print(\"key: %1c, %02x\" % (k[0], k[0]))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Plot the correlation traces for the right key byte guess and one wrong key byte guess\n",
"# Do you see the correlation peaks?\n",
"fig = plt.figure()\n",
"plt.plot(?)\n",
"plt.plot(?)\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "Z62RVYJYzncZ"
},
"source": [
"## **Break all key bytes!**"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "T7HhwO-ezpoQ"
},
"outputs": [],
"source": [
"keys = np.array(range(0, 256))\n",
"kk = np.zeros(16, dtype='uint8')\n",
"for i in range(0, 16):\n",
" inp = inputs[:, ?]\n",
" ????\n",
" kk[i] = k\n",
" print(\"%1c, %02x @ %d\" % (k[0], k[0], j[0]))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## **Verify the key on a PT, CT pair!**"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"key_bytes = bytes(kk)\n",
"outputs = ?"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# !pip install pycryptodome\n",
"from Crypto.Cipher import AES\n",
"cipher = AES.new(key_bytes, AES.MODE_ECB)\n",
"??"
]
}
],
"metadata": {
"colab": {
"collapsed_sections": [],
"name": "dpa_student.ipynb",
"provenance": []
},
"kernelspec": {
"display_name": "Python 3",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.1"
}
},
"nbformat": 4,
"nbformat_minor": 0
}