Python-ELMO is a Python library which offers an encapsulation of the binary tool ELMO, in order to manipulate it easily in Python and SageMath script.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

project_base.py 8.2KB

  1. import os, re
  2. import socket
  3. from .protocol import SocketTool
  4. import numpy as np
  5. def to_hex(v, nb_bits=16):
  6. try:
  7. v_hex = v.hex()
  8. except AttributeError:
  9. v_hex = hex(v)[2:]
  10. return '0'*(nb_bits//4-len(v_hex)) + v_hex
  11. def split_octet(hexstr):
  12. return [hexstr[i:i+2] for i in range(0, len(hexstr), 2)]
  13. def to_signed_hex(v, nb_bits=16):
  14. try:
  15. return split_octet(to_hex(v & (2**nb_bits-1), nb_bits=nb_bits))
  16. except TypeError as err:
  17. raise TypeError('Error to transform a <{}> into signed hex.'.format(type(v))) from err
  18. def write(_input, uintXX, nb_bits=16):
  19. uintXX = to_signed_hex(uintXX, nb_bits=nb_bits)
  20. for i in range(nb_bits//8):
  21. _input.write(uintXX[i]+'\n')
  22. def write_list(_input, uint16_list):
  23. for uint16 in uint16_list:
  24. write(_input, uint16)
  25. def launch_simulation(quiet=False, **kwargs):
  26. try:
  27. s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  28. s.connect(('localhost', 5000))
  29. SocketTool.send_data(s, kwargs)
  30. if not SocketTool.get_ack(s):
  31. raise RuntimeError("NACK received: The request has been refused !")
  32. else:
  33. data = SocketTool.get_data(s)
  34. if data['error'] and not quiet:
  35. raise Exception("The simulation return an error.")
  36. return data['output'], data['error']
  37. s.close()
  38. except IOError as err:
  39. raise RuntimeError("The connection refused. Has the ELMO server been switch on ?") from err
  40. class SimulationProject:
  41. _nb_bits_for_nb_challenges = 16
  42. _project_directory = None
  43. ### Define the project
  44. @classmethod
  45. def get_project_directory(cl):
  46. if cl._project_directory:
  47. return cl._project_directory
  48. else:
  49. raise NotImplementedError()
  50. @classmethod
  51. def set_project_directory(cl, project_directory):
  52. cl._project_directory = project_directory
  53. @classmethod
  54. def get_project_label(cl):
  55. return cl.get_project_directory()
  56. @classmethod
  57. def get_make_directory(cl):
  58. return ''
  59. @classmethod
  60. def get_binary(cl):
  61. raise NotImplementedError()
  62. @classmethod
  63. def get_parameters_names(cl):
  64. return set()
  65. @classmethod
  66. def adapt_project(cl, parameters):
  67. return
  68. def get_challenge_format(self):
  69. raise NotImplementedError()
  70. ### Tools to realize the simulation of the project
  71. def __init__(self, challenges=None):
  72. self.elmo_folder = os.path.dirname(os.path.abspath(__file__))+'/elmo'
  73. self.challenges = challenges
  74. self.is_executed = False
  75. def set_challenges(self, challenges):
  76. self.challenges = challenges
  77. def get_writable_input_file(self):
  78. return open('{}/input.txt'.format(self.elmo_folder), 'w')
  79. def set_input_for_each_challenge(self, input, challenge):
  80. format = self.get_challenge_format()
  81. def aux(sizes, data):
  82. if len(sizes) == 0:
  83. write(input, data)
  84. else:
  85. assert len(data) == sizes[0], 'Incorrect format for challenge. Get {} instead of {}'.format(len(data), sizes[0])
  86. for i in range(sizes[0]):
  87. aux(sizes[1:], data[i])
  88. for num_part in range(len(format)):
  89. aux(format[num_part], challenge[num_part])
  90. def set_input(self, input):
  91. assert len(self.challenges) < 2**16, 'The number of challenges must be strictly lower than 65536. Currently, there are {} challenges.'.format(len(self.challenges))
  92. write(input, len(self.challenges), nb_bits=self._nb_bits_for_nb_challenges)
  93. for challenge in self.challenges:
  94. self.set_input_for_each_challenge(input, challenge)
  95. def run(self):
  96. with open('{}/input.txt'.format(self.elmo_folder), 'w') as _input:
  97. self.set_input(_input)
  98. from .manage import execute_simulation
  99. execute_simulation(self)
  100. self.is_executed = True
  101. def run_online(self):
  102. with open('{}/input.txt'.format(self.elmo_folder), 'w') as _input:
  103. self.set_input(_input)
  104. launch_simulation(project=self.get_project_label(), quiet=False)
  105. self.is_executed = True
  106. def get_asmtrace_filename(self):
  107. return '{}/output/asmoutput/asmtrace00001.txt'.format(self.elmo_folder)
  108. def get_asmtrace(self):
  109. with open(self.get_asmtrace_filename(), 'r') as _file:
  110. return [line.strip() for line in _file.readlines()]
  111. def get_indexes_of(self, condition):
  112. with open(self.get_asmtrace_filename(), 'r') as _file:
  113. asmtrace = _file.readlines()
  114. return [i for i, instr in enumerate(asmtrace) if condition(instr)]
  115. def get_number_of_traces(self):
  116. return len(self.challenges)
  117. def get_results(self, only_filenames=False, reorganise=None, indexes=None):
  118. assert self.is_executed
  119. nb_traces = self.get_number_of_traces()
  120. trace_filenames = []
  121. for filename in os.listdir('{}/output/traces/'.format(self.elmo_folder)):
  122. if re.search(r'^trace\d+\.trc$', filename):
  123. trace_filenames.append('{}/output/traces/{}'.format(self.elmo_folder, filename))
  124. if len(trace_filenames) >= nb_traces:
  125. break
  126. assert len(trace_filenames) == nb_traces
  127. results = trace_filenames
  128. if not only_filenames:
  129. for i in range(len(results)):
  130. with open(results[i], 'r') as _file:
  131. if indexes is not None:
  132. results[i] = list(map(float, _file.readlines()[indexes]))
  133. else:
  134. results[i] = list(map(float, _file.readlines()))
  135. if reorganise is not None:
  136. results = reorganise(results)
  137. return results
  138. def get_traces(self, reorganise=None, indexes=None):
  139. results = self.get_results(only_filenames=False, reorganise=reorganise,indexes=indexes)
  140. nb_traces = self.get_number_of_traces()
  141. trace_length = len(results[0])
  142. traces = np.zeros((nb_traces, trace_length))
  143. for i in range(nb_traces):
  144. traces[i,:] = results[i]
  145. if reorganise is not None:
  146. traces = reorganise(traces)
  147. return traces
  148. def get_printed_data(self):
  149. with open('{}/output/printdata.txt'.format(self.elmo_folder), 'r') as _file:
  150. data = list(map(lambda x: int(x, 16), _file.readlines()))
  151. nb_traces = self.get_number_of_traces()
  152. nb_data_per_trace = len(data) // nb_traces
  153. return [data[nb_data_per_trace*i:nb_data_per_trace*(i+1)] for i in range(nb_traces)]
  154. def analyse_operands(self, num_line, num_trace=1):
  155. num_str = str(num_trace)
  156. num_str = '0'*(5-len(num_str)) + num_str
  157. operands_filename = self.elmo_folder + '/output/operands/operands{}.txt'.format(num_str)
  158. trace_filename = self.elmo_folder + '/output/traces/trace0000{}.trc'.format(num_trace)
  159. is_multiple = (type(num_line) is list)
  160. if not is_multiple:
  161. num_line = [num_line]
  162. output = [{}]*len(num_line)
  163. with open(operands_filename, 'r') as _file:
  164. lines = _file.readlines()
  165. for i, num in enumerate(num_line):
  166. line = lines[num].split()
  167. data = list(map(int, line[0:7]))
  168. output[i]['previous'] = data[0:2]
  169. output[i]['current'] = data[2:4]
  170. output[i]['triplet'] = data[4:7]
  171. output[i]['other'] = list(map(float, line[7:]))
  172. with open(trace_filename, 'r') as _file:
  173. lines = _file.readlines()
  174. for i, num in enumerate(num_line):
  175. line = lines[num]
  176. output[i]['power'] = float(line)
  177. return output if is_multiple else output[0]