#!/usr/bin/env python3 import numpy as np import matplotlib.pyplot as plt import aes D = 6000 # Number of power traces (Number of Samples) T = 87 # Number of data points per power trace (Points in time) KEY_GUESSES = np.arange(256, dtype=np.uint8) def calculate_models( ciphertexts: np.ndarray[np.ndarray[np.uint8]], ) -> np.ndarray[np.ndarray[np.ndarray[np.uint8]]]: """ Calculates for each sample ciphertext c the state just before the last SubBytes operation of the AddKey step in the last round of AES. In order to do this we calculate: sbox^-1 (c ⊕ k_hyp) and return an numpy array with this form [ [ # first ciphertext sample c1 [c_1_0 ⊕ 0, c_1_0 ⊕ 1, ..., c_1_0 ⊕ 255], # first byte of c1 [c_1_1 ⊕ 0, c_1_1 ⊕ 1, ..., c_1_1 ⊕ 255], # second byte of c1 ..., [c_1_15 ⊕ 0, c_1_15 ⊕ 1, ..., c_1_15 ⊕ 255], # 16'th byte of c1 ], [ # second ciphertext sample c2 [c_2_0 ⊕ 0, c_2_0 ⊕ 1, ..., c_2_0 ⊕ 255], [c_2_1 ⊕ 0, c_2_1 ⊕ 1, ..., c_2_1 ⊕ 255], ..., [c_2_15 ⊕ 0, c_2_15 ⊕ 1, ..., c_2_15 ⊕ 255], ], ..., [ # last ciphertext sample cD [c_D_0 ⊕ 0, c_D_0 ⊕ 1, ..., c_D_0 ⊕ 255], [c_D_1 ⊕ 0, c_D_1 ⊕ 1, ..., c_D_1 ⊕ 255], ..., [c_D_15 ⊕ 0, c_D_15 ⊕ 1, ..., c_D_15 ⊕ 255], ], ] Args: ciphertexts: An array of all samples (ciphertexts) as an np.ndarray of np.ndarrays for each byte as np.uint8. Returns: models: An np.ndarray with each model of the state before the SubBytes operation of the AddKey step in the last round of AES. - axis 0: Samples/Ciphertexts - axis 1: Bytes per sample - axis 2: Byte xor k_hyp """ # duplicate each ciphertext 256 times to xor with all possible keys. models = np.repeat(ciphertexts, 256, axis=0).reshape((D, 256, 16)) # create a view of models with the inner axis swaped, so when we xor with # KEY_GUESSES numpy can use broadcast. models_view = np.swapaxes(models, 1, 2) # c ⊕ k_hyp np.bitwise_xor(models_view, KEY_GUESSES, out=models_view) # apply reverse rbox to all bytes. (rsbox(c ⊕ k_hyp)) models = np.vectorize(lambda x: aes.core.rsbox(x))(models) return models def read_msgs(file_name: str) -> np.ndarray[np.ndarray[np.ndarray[np.uint8]]]: msgs = np.empty((D, 3, 16), dtype=np.uint8) with open(file_name, 'r') as fd: for idx, (key, plain_text) in enumerate( (line.strip().split(',') for line in fd) ): msgs[idx][0] = np.frombuffer(bytes.fromhex(key), dtype=np.uint8) # key msgs[idx][1] = np.frombuffer(bytes.fromhex(plain_text), dtype=np.uint8) # plain text msgs[idx][2] = np.array( # ciphertext aes.aes(int(key, 16), 128).enc_once(int(plain_text, 16)), dtype=np.uint8 ) return msgs def read_traces(file_name: str) -> np.ndarray[np.ndarray[np.uint8]]: return np.loadtxt(file_name, delimiter=",", dtype=np.uint8) if __name__ == "__main__": msgs = read_msgs("Task-3-example_traces/test_msgs.csv") traces = read_traces("Task-3-example_traces/test_traces.csv") models = calculate_models(msgs[:, 2]) np.set_printoptions(formatter={"int": hex}) last_round_key = aes.core.key_expansion(msgs[0][0].tolist())[-16:] for bit in range(128): # i'th row, and j'th col is the correlation coefficient of key_hyp = i and time sample j r = np.corrcoef( np.bitwise_and(models[:, :, bit//8], np.array([2**(bit % 8)], dtype=np.uint8)), traces, rowvar=False )[:256, -87:] guess = np.argmax(np.max(np.abs(r), axis=1)) # print(guess) exit(0) # tmp = np.sort(r.flatten()) # confidence = max(abs(tmp[0] - tmp[1]), abs(tmp[-2] - tmp[-1])) # if confidence > 0.005: # fig, axs = plt.subplots(1, 1, layout='constrained') # axs.set_title(f"Bit {bit%8 + 1} of Byte {bit//8 + 1} (Confidence: {confidence:.6f})") # axs.plot(r.transpose(), alpha=0.3, color='grey') # axs.plot(r[last_round_key[bit//8]], color="blue") # axs.plot(r[guess], color="red") # axs.set_xlabel("Time Samples") # axs.set_ylabel("Correlation") # axs.grid(True) # plt.show()