# coding: utf8
import numpy as np
from scipy.stats.stats import pearsonr
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
import logging as log
import scipy.io as sio
from random import randint
import argparse
import sys
sys.path.append('./../Correlation')
import corr as corr
import os
log.basicConfig(format="%(levelname)s: %(message)s", level=log.INFO)
# Hamming weight rray
HW_array = np.array([str(bin(byte)[2:]).count('1') for byte in range(256)], dtype=np.uint8)
nb_bytes = 16
nb_k_hyp = 256
Sbox_hex = [
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]
Sbox_dec = np.array([int(s) for s in Sbox_hex])
inv_Sbox_hex = [
0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,
0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,
0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25,
0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92,
0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06,
0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B,
0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E,
0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B,
0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F,
0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF,
0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D]
inv_Sbox_dec = np.array([int(s) for s in inv_Sbox_hex])
def compute_predictions(leakage_model,
attacked_round,
nb_traces,
plaintexts_path = './plaintexts',
ciphertexts_path = './ciphertexts',
predictions_path = './predictions',
save_mat = False):
k_hyps = np.array(range(nb_k_hyp)) #0 to 255
shift = randint(0, nb_traces)
if attacked_round.lower() == 'first':
for plaintexts_filename in os.listdir(plaintexts_path):
plaintexts = np.load(os.path.join(plaintexts_path, plaintexts_filename))
plaintexts = np.vstack((plaintexts[shift:], plaintexts[:shift]))
plaintexts = plaintexts[:nb_traces,:]
break
# Compute the reference value against which the Hamming distance is computed
if leakage_model.lower() =='hamming_weight':
ref_value = np.zeros((np.shape(plaintexts)[0], np.shape(plaintexts)[1], nb_k_hyp)).astype(np.uint8)
elif leakage_model.lower() == 'hamming_distance_plaintext':
ref_value = plaintexts[:, :, np.newaxis]
elif leakage_model.lower() == 'hamming_distance_sbox_input':
ref_value = np.bitwise_xor(plaintexts[:, :, np.newaxis], k_hyps)
predictions = HW_array[np.bitwise_xor(Sbox_dec[np.bitwise_xor(plaintexts[:, :, np.newaxis], k_hyps)], ref_value)]
elif attacked_round.lower() == 'last':
for ciphertexts_filename in os.listdir(ciphertexts_path):
ciphertexts = np.load(os.path.join(ciphertexts_path, ciphertexts_filename))
break
if leakage_model.lower() =='hamming_weight':
ref_value = np.zeros((np.shape(ciphertexts)[0], np.shape(ciphertexts)[1], nb_k_hyp)).astype(np.uint8)
ref_value = ref_value[:, :, np.newaxis]
predictions = HW_array[np.bitwise_xor(inv_Sbox_dec[np.bitwise_xor(ciphertexts[:, :, np.newaxis], k_hyps)], ref_value)]
np.save(os.path.join(predictions_path, 'prediction.npy'), predictions)
if save_mat:
sio.savemat(os.path.join(predictions_path, 'prediction.mat'), {'prediction':predictions.astype(np.float32)})
log.info("Predictions for intermediate value in {0} round computed".format(attacked_round))
return shift
def compute_correlation(nb_traces,
shift=0,
predictions_path = './predictions',
traces_path = './traces',
correlations_path = './correlations'):
for predictions_filename in os.listdir(predictions_path):
predictions = np.load(os.path.join(predictions_path, predictions_filename))
break
log.info("Loaded predictions matrix of type {0} and size {1}".format(predictions.dtype, np.shape(predictions)))
for traces_filename in os.listdir(traces_path):
traces = np.load(os.path.join(traces_path, traces_filename))
traces = np.vstack((traces[shift:], traces[:shift]))
traces = traces[:nb_traces,:]
break
log.info("Loaded traces ("+traces_filename+") matrix of type {0} and size {1}".format(traces.dtype, np.shape(traces)))
nb_samples = np.shape(traces)[1]
correlation = np.zeros((nb_bytes, nb_samples, nb_k_hyp))
for byte in range(nb_bytes):
log.info("Computing correlation for byte {0}".format(byte))
correlation[byte,:,:] = corr.corr(traces, predictions[:,byte,:])
np.save(os.path.join(correlations_path, 'corr_byte_'+str(byte)+'.npy'), correlation[byte,:,:])
def display_results(correct_key,
attacked_round = 'first',
correlations_path = './correlations'):
correct_key = [correct_key[i:i+2] for i in range(0, len(correct_key), 2)]
guessed_key = ""
for byte, correct_byte in enumerate(correct_key):
corr = np.load(os.path.join(correlations_path, 'corr_byte_'+str(byte)+'.npy'))
max_corr_per_key_byte = abs(corr).max(axis=0)
max_corr_samples = abs(corr).max(axis=1)
hex_key_byte = hex(np.argmax(max_corr_per_key_byte))[2:-1].zfill(2)
sample_of_interest = np.argmax(max_corr_samples)
corr = round(max(max_corr_per_key_byte), 3)
if attacked_round.lower() == 'first':
log.info("=> Guessed key byte #{0} : \"{1}\", found at sample {2}".format(str(byte).zfill(2), hex_key_byte, sample_of_interest))
position_correct_byte = list(np.sort(max_corr_per_key_byte)[::-1]).index(max_corr_per_key_byte[int(correct_byte, 16)])
log.info(" => Correct one is \"{0}\", ranked {1}/{2} with correlation={3}".format(correct_byte, position_correct_byte, nb_k_hyp, corr))
guessed_key+=hex_key_byte
elif attacked_round.lower() == 'last':
log.info("=> Guessed last round key byte #{0} : \"{1}\", found at sample {2}".format(str(byte).zfill(2), hex_key_byte, sample_of_interest))
position_correct_byte = list(np.sort(max_corr_per_key_byte)[::-1]).index(max_corr_per_key_byte[int(correct_byte, 16)])
log.info(" => Correct one is \"{0}\", ranked {1}/{2} with correlation={3}".format(correct_byte, position_correct_byte, nb_k_hyp, corr))
guessed_key+=hex_key_byte
if attacked_round.lower() =='first':
print "=> Guessed key is \"{0}\"".format(guessed_key)
elif attacked_round.lower() =='last':
print "=> Guessed last round key is \"{0}\"".format(guessed_key)
def plot_results(target_bytes,
correlations_path = './correlations',
plot_path="./plots"):
for byte in target_bytes:
log.info("Plotting for byte {0}".format(byte))
corr = abs(np.load(os.path.join(correlations_path, 'corr_byte_'+str(byte)+'.npy')))
nb_samples, nb_hyp = np.shape(corr)
max_corr_per_key_byte = abs(corr).max(axis=0)
max_corr_samples = abs(corr).max(axis=1)
key_byte = np.argmax(max_corr_per_key_byte)
hex_key_byte = hex(key_byte)[2:-1].zfill(2)
sample_of_interest = np.argmax(max_corr_samples)
plt.figure()
plt.plot(corr[:,:key_byte], color = 'grey', linewidth = 0, marker="o", markersize=4)
plt.plot(corr[:,-key_byte:], color = 'grey', linewidth = 0, marker="o", markersize=4)
plt.plot(corr[:,key_byte], color = 'blue', linewidth = 0, marker="o", markersize=4)
plt.xlim(0,nb_samples)
plt.ylim(0, 1)
plt.xlabel("Echantillons (temps)")
plt.ylabel("Correlation")
# plt.savefig(os.path.join(plot_path, 'corr_vs_samples_byte_'+str(byte)+'.png'))
# plt.show()
plt.figure()
plt.plot(corr.transpose(), color = 'grey', linewidth = 0, marker="o", markersize=4)
plt.plot(nb_samples*[key_byte], corr[:,key_byte], color = 'red', linewidth = 0, marker="o", markersize=4)
plt.xlim(-1,nb_hyp-1)
axes = plt.gca()
axes.get_xaxis().set_major_locator(ticker.MultipleLocator(16))
axes.get_xaxis().set_major_formatter(ticker.FormatStrFormatter("%x"))
plt.ylim(0, 1)
plt.xlabel("Hypotheses de cle")
plt.ylabel("Correlation")
plt.show()
# plt.savefig(os.path.join(plot_path, 'corr_vs_k_hyp_byte_'+str(byte)+'.png'))
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Preprocess traces')
parser.add_argument("nb_traces", type=int)
args = parser.parse_args()
nb_traces = args.nb_traces
compute_predictions(leakage_model = 'hamming_weight', attacked_round = 'first', nb_traces = nb_traces)
compute_correlation(nb_traces = nb_traces)
display_results(correct_key = '0123456789abcdef123456789abcdef0')
# for i in range(1):
# shift = compute_predictions(leakage_model = 'hamming_weight', attacked_round = 'first', nb_traces = nb_traces)
# compute_correlation(nb_traces = nb_traces, shift = shift)
# display_results(correct_key = "0123456789abcdef123456789abcdef0")
# display_results(correct_key = '4dfbe0f27221fe10a78d4adc8e490469')
# plot_results(range(15))
# compute_predictions(leakage_model = 'hamming_distance_plaintext', attacked_round = 'first')
# compute_correlation()
# display_results(correct_key = '0123456789abcdef123456789abcdef0')
# compute_predictions(leakage_model = 'hamming_distance_sbox_input', attacked_round = 'first')
# compute_correlation()
# display_results(correct_key = '0123456789abcdef123456789abcdef0')
# plot_results()
# compute_predictions(attacked_round = 'last')
# compute_correlation()
# display_results(correct_key = '0123456789abcdef123456789abcdef0')