362 lines
12 KiB
Plaintext
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
|
||
|
}
|