Initial commit
This commit is contained in:
commit
de7b7536ce
8 changed files with 12532 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/target
|
||||
145
Cargo.lock
generated
Normal file
145
Cargo.lock
generated
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "aes"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cipher",
|
||||
"cpufeatures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "alan-side-channel"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"rayon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
|
||||
|
||||
[[package]]
|
||||
name = "cipher"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
|
||||
dependencies = [
|
||||
"crypto-common",
|
||||
"inout",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
|
||||
dependencies = [
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inout"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.174"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
|
||||
dependencies = [
|
||||
"either",
|
||||
"rayon-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
|
||||
dependencies = [
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
8
Cargo.toml
Normal file
8
Cargo.toml
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "alan-side-channel"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
aes = "0.8.4"
|
||||
rayon = "1.10.0"
|
||||
6000
alan/Task-3-example_traces/test_msgs.csv
Normal file
6000
alan/Task-3-example_traces/test_msgs.csv
Normal file
File diff suppressed because it is too large
Load diff
6000
alan/Task-3-example_traces/test_traces.csv
Normal file
6000
alan/Task-3-example_traces/test_traces.csv
Normal file
File diff suppressed because it is too large
Load diff
122
alan/attack.py
Executable file
122
alan/attack.py
Executable file
|
|
@ -0,0 +1,122 @@
|
|||
#!/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()
|
||||
83
alan/multi-processing.py
Normal file
83
alan/multi-processing.py
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
#!/usr/bin/python3
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
from multiprocessing import Pool
|
||||
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(
|
||||
ciphertext: np.ndarray[np.ndarray[np.uint8]],
|
||||
) -> np.ndarray[np.ndarray[np.ndarray[np.uint8]]]:
|
||||
|
||||
# duplicate each ciphertext 256 times to xor with all possible keys.
|
||||
models = np.repeat(ciphertext, 256).reshape(16,256)
|
||||
|
||||
# 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, 0, 1)
|
||||
|
||||
# c ⊕ k_hyp
|
||||
np.bitwise_xor(models, KEY_GUESSES, out=models)
|
||||
|
||||
# apply reverse rbox to all bytes. (rsbox(c ⊕ k_hyp))
|
||||
models = np.vectorize(lambda x: aes.core.rsbox(x))(models_view)
|
||||
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")
|
||||
with Pool() as pool:
|
||||
models = pool.map(calculate_models, msgs[:, 2])
|
||||
|
||||
models = np.stack(models)
|
||||
|
||||
# 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
|
||||
model = np.bitwise_and(models[:, :, bit//8], np.array([2**(bit % 8)], dtype=np.uint8))
|
||||
r = np.corrcoef(
|
||||
model,
|
||||
traces,
|
||||
rowvar=False
|
||||
)[:256, -87:]
|
||||
|
||||
guess = np.argmax(np.max(np.abs(r), axis=1))
|
||||
|
||||
# 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()
|
||||
173
src/main.rs
Normal file
173
src/main.rs
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
#![feature(int_from_ascii)]
|
||||
|
||||
use std::{
|
||||
io::{BufRead, Read},
|
||||
path::Path,
|
||||
};
|
||||
|
||||
use aes::{
|
||||
Aes128,
|
||||
cipher::{BlockEncrypt, KeyInit, generic_array::GenericArray},
|
||||
};
|
||||
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
||||
|
||||
static RSBOX: [u8; 256] = [
|
||||
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,
|
||||
];
|
||||
|
||||
fn calculate_models(ciphertexts: &[[u8; 16]]) -> Vec<[[u8; 16]; 256]> {
|
||||
ciphertexts
|
||||
.iter()
|
||||
.map(|c| {
|
||||
let mut row = [[0; 16]; 256];
|
||||
|
||||
for i in 0..256 {
|
||||
for j in 0..16 {
|
||||
row[i][j] = RSBOX[(c[j] ^ (i as u8)) as usize];
|
||||
}
|
||||
}
|
||||
|
||||
row
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn read_msgs(path: impl AsRef<Path>) -> Vec<[u8; 16]> {
|
||||
let file = std::fs::File::open(path).unwrap();
|
||||
|
||||
let mut bufreader = std::io::BufReader::new(file);
|
||||
|
||||
let mut line = [0; 66];
|
||||
|
||||
let mut r = Vec::new();
|
||||
while bufreader.read_exact(&mut line).is_ok() {
|
||||
let mut key = [0; 16];
|
||||
let mut msgs = [0; 16];
|
||||
|
||||
for i in 0..16 {
|
||||
key[i] = u8::from_ascii_radix(&line[2 * i..2 * i + 2], 16).unwrap();
|
||||
msgs[i] = u8::from_ascii_radix(&line[33 + 2 * i..33 + 2 * i + 2], 16).unwrap();
|
||||
}
|
||||
|
||||
let key = GenericArray::from(key);
|
||||
let mut block = GenericArray::from(msgs.clone());
|
||||
let cypher = Aes128::new(&key);
|
||||
|
||||
cypher.encrypt_block(&mut block);
|
||||
|
||||
r.push(*block.as_ref());
|
||||
}
|
||||
|
||||
r
|
||||
}
|
||||
|
||||
const TRACES: usize = 87;
|
||||
|
||||
fn read_traces(path: impl AsRef<Path>) -> Vec<[u8; TRACES]> {
|
||||
let file = std::fs::File::open(path).unwrap();
|
||||
|
||||
let bufreader = std::io::BufReader::new(file);
|
||||
|
||||
bufreader
|
||||
.lines()
|
||||
.map(|l| {
|
||||
let l = l.unwrap();
|
||||
|
||||
let mut trace = [0; TRACES];
|
||||
|
||||
for (i, t) in l
|
||||
.trim()
|
||||
.split(',')
|
||||
.map(|t| t.parse::<u8>().unwrap())
|
||||
.enumerate()
|
||||
{
|
||||
trace[i] = t;
|
||||
}
|
||||
|
||||
trace
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn correlation(
|
||||
bit: usize,
|
||||
key_hypothesis: usize,
|
||||
trace_index: usize,
|
||||
cyphtertext: &[[[u8; 16]; 256]],
|
||||
traces: &[[u8; TRACES]],
|
||||
) -> f64 {
|
||||
let mut x = 0i64;
|
||||
let mut y = 0i64;
|
||||
let mut xy = 0i64;
|
||||
let mut xsqr = 0i64;
|
||||
let mut ysqr = 0i64;
|
||||
|
||||
for i in 0..traces.len() {
|
||||
let xi = (cyphtertext[i][key_hypothesis][bit / 8] & (1 << (bit % 8))) as i64;
|
||||
let yi = traces[i][trace_index] as i64;
|
||||
|
||||
x += xi;
|
||||
y += yi;
|
||||
xy += xi * yi;
|
||||
xsqr += xi * xi;
|
||||
ysqr += yi * yi;
|
||||
}
|
||||
|
||||
let n = traces.len() as i64;
|
||||
let num = (n * xy - x * y) as f64;
|
||||
let denom = f64::sqrt((n * xsqr - x * x) as f64) * f64::sqrt((n * ysqr - y * y) as f64);
|
||||
|
||||
num / denom
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let start = std::time::Instant::now();
|
||||
let cyphertext = read_msgs("./alan/Task-3-example_traces/test_msgs.csv");
|
||||
println!("read msgs: {:?}", start.elapsed());
|
||||
|
||||
let start = std::time::Instant::now();
|
||||
let models = calculate_models(&cyphertext);
|
||||
println!("calculate models: {:?}", start.elapsed());
|
||||
|
||||
let start = std::time::Instant::now();
|
||||
let traces = read_traces("./alan/Task-3-example_traces/test_traces.csv");
|
||||
println!("read traces: {:?}", start.elapsed());
|
||||
|
||||
let start = std::time::Instant::now();
|
||||
for bit in 0..128 {
|
||||
let (max_index, max) = (0..256)
|
||||
.into_par_iter()
|
||||
.map(|key_hypothesis| {
|
||||
let m = (0..traces[0].len())
|
||||
.map(|trace_index| {
|
||||
correlation(bit, key_hypothesis, trace_index, &models, &traces).abs()
|
||||
})
|
||||
.reduce(f64::max)
|
||||
.unwrap_or(0.0);
|
||||
(key_hypothesis, m)
|
||||
})
|
||||
.reduce(
|
||||
|| (0, 0.0),
|
||||
|a, b| {
|
||||
if a.1 > b.1 { a } else { b }
|
||||
},
|
||||
);
|
||||
println!("bit: {bit}, key_hypothesis: {max_index}, max: {max}");
|
||||
}
|
||||
println!("calculate correlations: {:?}", start.elapsed());
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue