|
|
|
|
|
|
|
|
import os, re
|
|
|
import os, re
|
|
|
import numpy as np
|
|
|
import numpy as np
|
|
|
|
|
|
from os.path import join as pjoin
|
|
|
|
|
|
|
|
|
from .config import MODULE_PATH, ELMO_TOOL_REPOSITORY
|
|
|
|
|
|
|
|
|
from .config import (
|
|
|
|
|
|
MODULE_PATH,
|
|
|
|
|
|
ELMO_TOOL_REPOSITORY,
|
|
|
|
|
|
ELMO_INPUT_FILE_NAME,
|
|
|
|
|
|
DEFAULT_HOST,
|
|
|
|
|
|
DEFAULT_PORT,
|
|
|
|
|
|
)
|
|
|
from .utils import write
|
|
|
from .utils import write
|
|
|
|
|
|
|
|
|
class SimulationProject:
|
|
|
class SimulationProject:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### Tools to realize the simulation of the project
|
|
|
### Tools to realize the simulation of the project
|
|
|
def __init__(self, challenges=None):
|
|
|
def __init__(self, challenges=None):
|
|
|
self.elmo_folder = os.path.join(MODULE_PATH, ELMO_TOOL_REPOSITORY)
|
|
|
|
|
|
|
|
|
self.elmo_folder = pjoin(MODULE_PATH, ELMO_TOOL_REPOSITORY)
|
|
|
self.challenges = challenges
|
|
|
self.challenges = challenges
|
|
|
self.reset()
|
|
|
self.reset()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.is_executed = False
|
|
|
self.is_executed = False
|
|
|
self.has_been_online = False
|
|
|
self.has_been_online = False
|
|
|
|
|
|
|
|
|
|
|
|
self._nb_traces = None
|
|
|
self._complete_asmtrace = None
|
|
|
self._complete_asmtrace = None
|
|
|
self._complete_results = None
|
|
|
self._complete_results = None
|
|
|
self._complete_printed_data = None
|
|
|
self._complete_printed_data = None
|
|
|
|
|
|
|
|
|
raise NotImplementedError()
|
|
|
raise NotImplementedError()
|
|
|
|
|
|
|
|
|
def set_challenges(self, challenges):
|
|
|
def set_challenges(self, challenges):
|
|
|
|
|
|
self.reset()
|
|
|
self.challenges = challenges
|
|
|
self.challenges = challenges
|
|
|
|
|
|
|
|
|
def get_writable_input_file(self):
|
|
|
|
|
|
return open('{}/input.txt'.format(self.elmo_folder), 'w')
|
|
|
|
|
|
|
|
|
def get_input_filename(self):
|
|
|
|
|
|
return pjoin(self.elmo_folder, ELMO_INPUT_FILE_NAME)
|
|
|
|
|
|
|
|
|
|
|
|
def get_printed_data_filename(self):
|
|
|
|
|
|
return pjoin(self.elmo_folder, 'output', 'printdata.txt')
|
|
|
|
|
|
|
|
|
|
|
|
def get_asmtrace_filename(self):
|
|
|
|
|
|
return pjoin(self.elmo_folder, 'output', 'asmoutput', 'asmtrace00001.txt')
|
|
|
|
|
|
|
|
|
def set_input_for_each_challenge(self, input, challenge):
|
|
|
def set_input_for_each_challenge(self, input, challenge):
|
|
|
format = self.get_challenge_format()
|
|
|
format = self.get_challenge_format()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def run(self):
|
|
|
def run(self):
|
|
|
self.reset()
|
|
|
self.reset()
|
|
|
with open('{}/input.txt'.format(self.elmo_folder), 'w') as _input:
|
|
|
|
|
|
|
|
|
with open(self.get_input_filename(), 'w') as _input:
|
|
|
self.set_input(_input)
|
|
|
self.set_input(_input)
|
|
|
|
|
|
|
|
|
from .manage import execute_simulation
|
|
|
from .manage import execute_simulation
|
|
|
res = execute_simulation(self)
|
|
|
res = execute_simulation(self)
|
|
|
|
|
|
|
|
|
self.is_executed = True
|
|
|
self.is_executed = True
|
|
|
self.has_been_online = False
|
|
|
self.has_been_online = False
|
|
|
|
|
|
self._nb_traces = res['nb_traces']
|
|
|
return res
|
|
|
return res
|
|
|
|
|
|
|
|
|
def run_online(self, host='localhost', port=5000):
|
|
|
|
|
|
|
|
|
def run_online(self, host=DEFAULT_HOST, port=DEFAULT_PORT):
|
|
|
from .server.protocol import SocketTool
|
|
|
from .server.protocol import SocketTool
|
|
|
import socket
|
|
|
import socket
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.is_executed = True
|
|
|
self.is_executed = True
|
|
|
self.has_been_online = True
|
|
|
self.has_been_online = True
|
|
|
|
|
|
self._nb_traces = data['nb_traces']
|
|
|
self._complete_asmtrace = data['asmtrace']
|
|
|
self._complete_asmtrace = data['asmtrace']
|
|
|
self._complete_results = data['results']
|
|
|
self._complete_results = data['results']
|
|
|
self._complete_printed_data = data['printed_data']
|
|
|
self._complete_printed_data = data['printed_data']
|
|
|
|
|
|
|
|
|
if key not in ['results', 'asmtrace', 'printed_data']
|
|
|
if key not in ['results', 'asmtrace', 'printed_data']
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
### Manipulate the ASM trace
|
|
|
|
|
|
def get_asmtrace_filename(self):
|
|
|
|
|
|
return '{}/output/asmoutput/asmtrace00001.txt'.format(self.elmo_folder)
|
|
|
|
|
|
|
|
|
|
|
|
def get_asmtrace(self):
|
|
|
|
|
|
if self._complete_asmtrace is None:
|
|
|
|
|
|
with open(self.get_asmtrace_filename(), 'r') as _file:
|
|
|
|
|
|
self._complete_asmtrace = ''.join(_file.readlines())
|
|
|
|
|
|
|
|
|
|
|
|
return self._complete_asmtrace.split('\n')
|
|
|
|
|
|
|
|
|
|
|
|
def get_indexes_of(self, condition):
|
|
|
|
|
|
return [i for i, instr in enumerate(self.get_asmtrace()) if condition(instr)]
|
|
|
|
|
|
|
|
|
|
|
|
### Manipulate the results
|
|
|
### Manipulate the results
|
|
|
def get_number_of_traces(self):
|
|
|
|
|
|
|
|
|
def get_number_of_challenges(self):
|
|
|
return len(self.challenges)
|
|
|
return len(self.challenges)
|
|
|
|
|
|
|
|
|
|
|
|
def get_number_of_traces(self):
|
|
|
|
|
|
assert self.is_executed
|
|
|
|
|
|
return self._nb_traces
|
|
|
|
|
|
|
|
|
def get_results(self, only_filenames=False, reorganise=None, indexes=None):
|
|
|
|
|
|
|
|
|
def get_results_filenames(self):
|
|
|
|
|
|
assert self.is_executed
|
|
|
|
|
|
assert not self.has_been_online
|
|
|
|
|
|
nb_traces = self.get_number_of_traces()
|
|
|
|
|
|
output_path = os.path.join(self.elmo_folder, 'output')
|
|
|
|
|
|
|
|
|
|
|
|
filenames = []
|
|
|
|
|
|
for i in range(nb_traces):
|
|
|
|
|
|
filename = os.path.join(output_path, 'traces', 'trace%05d.trc' % (i+1))
|
|
|
|
|
|
assert os.path.isfile(filename)
|
|
|
|
|
|
filenames.append(filename)
|
|
|
|
|
|
|
|
|
|
|
|
return filenames
|
|
|
|
|
|
|
|
|
|
|
|
def get_results(self):
|
|
|
|
|
|
"""
|
|
|
|
|
|
Warning: The output list is the same object stored in the instance.
|
|
|
|
|
|
If you change this object, it will change in the instance too, and the
|
|
|
|
|
|
next call to 'get_results' will return the changed object.
|
|
|
|
|
|
"""
|
|
|
assert self.is_executed
|
|
|
assert self.is_executed
|
|
|
nb_traces = self.get_number_of_traces()
|
|
|
nb_traces = self.get_number_of_traces()
|
|
|
|
|
|
|
|
|
if only_filenames and self.has_been_online:
|
|
|
|
|
|
raise Exception('Impossible to get the filenames for an online execution')
|
|
|
|
|
|
|
|
|
|
|
|
if only_filenames or self._complete_results is None:
|
|
|
|
|
|
trace_filenames = []
|
|
|
|
|
|
for filename in os.listdir('{}/output/traces/'.format(self.elmo_folder)):
|
|
|
|
|
|
if re.search(r'^trace\d+\.trc$', filename):
|
|
|
|
|
|
trace_filenames.append('{}/output/traces/{}'.format(self.elmo_folder, filename))
|
|
|
|
|
|
if len(trace_filenames) >= nb_traces:
|
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
|
|
assert len(trace_filenames) == nb_traces
|
|
|
|
|
|
if only_filenames:
|
|
|
|
|
|
return reorganise(trace_filenames) if reorganise is not None else trace_filenames
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Load the power traces
|
|
|
|
|
|
if self._complete_results is None:
|
|
|
self._complete_results = []
|
|
|
self._complete_results = []
|
|
|
for filename in trace_filenames:
|
|
|
|
|
|
|
|
|
for filename in self.get_results_filenames():
|
|
|
with open(filename, 'r') as _file:
|
|
|
with open(filename, 'r') as _file:
|
|
|
self._complete_results.append(list(map(float, _file.readlines())))
|
|
|
self._complete_results.append(list(map(float, _file.readlines())))
|
|
|
|
|
|
|
|
|
results = self._complete_results
|
|
|
|
|
|
if indexes is not None:
|
|
|
|
|
|
for i in range(len(self._complete_results)):
|
|
|
|
|
|
results[i] = results[i][indexes]
|
|
|
|
|
|
|
|
|
|
|
|
if reorganise is not None:
|
|
|
|
|
|
results = reorganise(results)
|
|
|
|
|
|
return results
|
|
|
|
|
|
|
|
|
return self._complete_results
|
|
|
|
|
|
|
|
|
def get_traces(self, reorganise=None, indexes=None):
|
|
|
|
|
|
results = self.get_results(only_filenames=False, reorganise=reorganise, indexes=indexes)
|
|
|
|
|
|
|
|
|
def get_traces(self, indexes=None):
|
|
|
|
|
|
"""
|
|
|
|
|
|
"""
|
|
|
|
|
|
assert self.is_executed
|
|
|
|
|
|
results = self.get_results()
|
|
|
|
|
|
|
|
|
nb_traces = self.get_number_of_traces()
|
|
|
nb_traces = self.get_number_of_traces()
|
|
|
trace_length = len(results[0])
|
|
|
trace_length = len(results[0])
|
|
|
|
|
|
|
|
|
|
|
|
if indexes is None:
|
|
|
|
|
|
traces = np.zeros((nb_traces, trace_length))
|
|
|
|
|
|
for i in range(nb_traces):
|
|
|
|
|
|
traces[i,:] = results[i]
|
|
|
|
|
|
return traces
|
|
|
|
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
traces = np.zeros((nb_traces, len(indexes)))
|
|
|
|
|
|
for i in range(nb_traces):
|
|
|
|
|
|
traces[i,:] = results[i][indexes]
|
|
|
|
|
|
return traces
|
|
|
|
|
|
|
|
|
traces = np.zeros((nb_traces, trace_length))
|
|
|
|
|
|
for i in range(nb_traces):
|
|
|
|
|
|
traces[i,:] = results[i]
|
|
|
|
|
|
|
|
|
### Manipulate the ASM trace
|
|
|
|
|
|
def get_asmtrace(self):
|
|
|
|
|
|
""" Get the ASM trace of the last simulation
|
|
|
|
|
|
The ASM trace is the list of the leaking assembler instructions,
|
|
|
|
|
|
one instruction each point of the leakage power trace
|
|
|
|
|
|
"""
|
|
|
|
|
|
assert self.is_executed
|
|
|
|
|
|
|
|
|
if reorganise is not None:
|
|
|
|
|
|
traces = reorganise(traces)
|
|
|
|
|
|
|
|
|
# Load the ASM trace
|
|
|
|
|
|
if self._complete_asmtrace is None:
|
|
|
|
|
|
with open(self.get_asmtrace_filename(), 'r') as _file:
|
|
|
|
|
|
self._complete_asmtrace = _file.read()
|
|
|
|
|
|
if type(self._complete_asmtrace):
|
|
|
|
|
|
self._complete_asmtrace = self._complete_asmtrace.split('\n')
|
|
|
|
|
|
|
|
|
return traces
|
|
|
|
|
|
|
|
|
return self._complete_asmtrace
|
|
|
|
|
|
|
|
|
|
|
|
def get_indexes_of(self, condition):
|
|
|
|
|
|
""" Get the list of indexes of the instructions
|
|
|
|
|
|
verifying the 'condition' in the ASM trace
|
|
|
|
|
|
:condition: Boolean function with ASM instruction (string) for input
|
|
|
|
|
|
"""
|
|
|
|
|
|
assert self.is_executed
|
|
|
|
|
|
return [i for i, instr in enumerate(self.get_asmtrace()) if condition(instr)]
|
|
|
|
|
|
|
|
|
### Manipulate the Printed Data
|
|
|
### Manipulate the Printed Data
|
|
|
def get_printed_data(self):
|
|
|
|
|
|
|
|
|
def get_printed_data(self, per_trace=True):
|
|
|
|
|
|
""" Get the printed data of the last simulation
|
|
|
|
|
|
A printed data is a data which has been given to the function 'printbyte'
|
|
|
|
|
|
during the simulation
|
|
|
|
|
|
:per_trace: If True (default), split equally the printed data in a table
|
|
|
|
|
|
with a length equal to the number of simulated power traces
|
|
|
|
|
|
"""
|
|
|
|
|
|
assert self.is_executed
|
|
|
|
|
|
|
|
|
|
|
|
# Load the printed data
|
|
|
if self._complete_printed_data is None:
|
|
|
if self._complete_printed_data is None:
|
|
|
with open('{}/output/printdata.txt'.format(self.elmo_folder), 'r') as _file:
|
|
|
|
|
|
|
|
|
with open(self.get_printed_data_filename(), 'r') as _file:
|
|
|
self._complete_printed_data = list(map(lambda x: int(x, 16), _file.readlines()))
|
|
|
self._complete_printed_data = list(map(lambda x: int(x, 16), _file.readlines()))
|
|
|
|
|
|
|
|
|
data = self._complete_printed_data
|
|
|
|
|
|
nb_traces = self.get_number_of_traces()
|
|
|
|
|
|
nb_data_per_trace = len(data) // nb_traces
|
|
|
|
|
|
|
|
|
|
|
|
return [data[nb_data_per_trace*i:nb_data_per_trace*(i+1)] for i in range(nb_traces)]
|
|
|
|
|
|
|
|
|
if per_trace:
|
|
|
|
|
|
# Return printed data for each trace
|
|
|
|
|
|
data = self._complete_printed_data
|
|
|
|
|
|
nb_traces = self.get_number_of_traces()
|
|
|
|
|
|
nb_data_per_trace = len(data) // nb_traces
|
|
|
|
|
|
|
|
|
### Other
|
|
|
|
|
|
def analyse_operands(self, num_line, num_trace=1):
|
|
|
|
|
|
num_str = str(num_trace)
|
|
|
|
|
|
num_str = '0'*(5-len(num_str)) + num_str
|
|
|
|
|
|
|
|
|
return [data[nb_data_per_trace*i:nb_data_per_trace*(i+1)] for i in range(nb_traces)]
|
|
|
|
|
|
|
|
|
operands_filename = self.elmo_folder + '/output/operands/operands{}.txt'.format(num_str)
|
|
|
|
|
|
trace_filename = self.elmo_folder + '/output/traces/trace0000{}.trc'.format(num_trace)
|
|
|
|
|
|
|
|
|
|
|
|
is_multiple = (type(num_line) is list)
|
|
|
|
|
|
if not is_multiple:
|
|
|
|
|
|
num_line = [num_line]
|
|
|
|
|
|
output = [{}]*len(num_line)
|
|
|
|
|
|
|
|
|
|
|
|
with open(operands_filename, 'r') as _file:
|
|
|
|
|
|
lines = _file.readlines()
|
|
|
|
|
|
for i, num in enumerate(num_line):
|
|
|
|
|
|
line = lines[num].split()
|
|
|
|
|
|
data = list(map(int, line[0:7]))
|
|
|
|
|
|
output[i]['previous'] = data[0:2]
|
|
|
|
|
|
output[i]['current'] = data[2:4]
|
|
|
|
|
|
output[i]['triplet'] = data[4:7]
|
|
|
|
|
|
output[i]['other'] = list(map(float, line[7:]))
|
|
|
|
|
|
|
|
|
|
|
|
with open(trace_filename, 'r') as _file:
|
|
|
|
|
|
lines = _file.readlines()
|
|
|
|
|
|
for i, num in enumerate(num_line):
|
|
|
|
|
|
line = lines[num]
|
|
|
|
|
|
output[i]['power'] = float(line)
|
|
|
|
|
|
|
|
|
|
|
|
return output if is_multiple else output[0]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
# Return printed data
|
|
|
|
|
|
return self._complete_printed_data
|