import numpy as np import os from enum import IntEnum, unique from .utils import binary_writing from .config import ELMO_TOOL_REPOSITORY @unique class Instruction(IntEnum): EOR = 0 LSL = 1 STR = 2 LDR = 3 MUL = 4 OTHER = 5 # Short name for 'Instruction' class Instr = Instruction PREVIOUS = 0 CURRENT = 1 SUBSEQUENT = 2 class ELMOEngine: ### Initialization def __init__(self): self.load_coefficients() self.reset_points() def _extract_data(self, nb): coeffs = self.coefficients[self.pos:self.pos+nb] self.pos += nb return coeffs def load_coefficients(self): """ Load the coefficients for the ELMO model about power leakage """ filename = os.path.join( os.path.dirname(os.path.abspath(__file__)), ELMO_TOOL_REPOSITORY, 'coeffs.txt', ) self.coefficients = None with open(filename, 'r') as _file: self.coefficients = np.array([list(map(float, line.split())) for line in _file.readlines()[:2153]]) if self.coefficients is None: raise IOError('Problem to read the coefficients.') self.pos = 0 self.constant = np.squeeze(self._extract_data(1)) self.PrvInstr = self._extract_data(4) self.SubInstr = self._extract_data(4) self.Operand1 = self._extract_data(32) self.Operand2 = self._extract_data(32) self.BitFlip1 = self._extract_data(32) self.BitFlip2 = self._extract_data(32) self.HWOp1PrvInstr = self._extract_data(4) self.HWOp2PrvInstr = self._extract_data(4) self.HDOp1PrvInstr = self._extract_data(4) self.HDOp2PrvInstr = self._extract_data(4) self.HWOp1SubInstr = self._extract_data(4) self.HWOp2SubInstr = self._extract_data(4) self.HDOp1SubInstr = self._extract_data(4) self.HDOp2SubInstr = self._extract_data(4) self.Operand1_bitinteractions = self._extract_data(496) self.Operand2_bitinteractions = self._extract_data(496) self.BitFlip1_bitinteractions = self._extract_data(496) self.BitFlip2_bitinteractions = self._extract_data(496) ### Computation core def _dot(self, a, b): return np.sum(a * b, axis=0) def calculate_point(self, triplet, previous_ops, current_ops, debug=False): nb_points = triplet.shape[1] instructiontype = triplet[CURRENT] instructiontype = instructiontype % 5 # Type 5 = Instruction was not profiled # Previous previous_instruction_typedec = triplet[PREVIOUS] previous_instruction_type = np.zeros((5, nb_points)) for i in range(nb_points): if previous_instruction_typedec[i] < 5: previous_instruction_type[previous_instruction_typedec[i],i] = 1 # Current (current_op1_binary, hw_op1) = binary_writing(current_ops[0], with_hamming=True) (current_op2_binary, hw_op2) = binary_writing(current_ops[1], with_hamming=True) (current_op1_bitflip, hd_op1) = binary_writing(previous_ops[0] ^ current_ops[0], with_hamming=True) (current_op2_bitflip, hd_op2) = binary_writing(previous_ops[1] ^ current_ops[1], with_hamming=True) current_instruction_typedec = instructiontype current_instruction_type = np.zeros((5, nb_points)) for i in range(nb_points): if triplet[CURRENT,i] < 5: current_instruction_type[current_instruction_typedec[i],i] = 1 # Subsequent subsequent_instruction_typedec = triplet[SUBSEQUENT] subsequent_instruction_type = np.zeros((5, nb_points)) for i in range(nb_points): if subsequent_instruction_typedec[i] < 5: subsequent_instruction_type[subsequent_instruction_typedec[i],i] = 1 # Component variables PrvInstr_data = self._dot( previous_instruction_type[1:], self.PrvInstr[:,instructiontype] ) SubInstr_data = self._dot( subsequent_instruction_type[1:], self.SubInstr[:,instructiontype] ) Operand1_data = self._dot( current_op1_binary, self.Operand1[:,instructiontype] ) Operand2_data = self._dot( current_op2_binary, self.Operand2[:,instructiontype] ) BitFlip1_data = self._dot( current_op1_bitflip, self.BitFlip1[:,instructiontype] ) BitFlip2_data = self._dot( current_op2_bitflip, self.BitFlip2[:,instructiontype] ) HWOp1PrvInstr_data = hw_op1 * self._dot(previous_instruction_type[1:], self.HWOp1PrvInstr[:,instructiontype]) HWOp2PrvInstr_data = hw_op2 * self._dot(previous_instruction_type[1:], self.HWOp2PrvInstr[:,instructiontype]) HDOp1PrvInstr_data = hd_op1 * self._dot(previous_instruction_type[1:], self.HDOp1PrvInstr[:,instructiontype]) HDOp2PrvInstr_data = hd_op2 * self._dot(previous_instruction_type[1:], self.HDOp2PrvInstr[:,instructiontype]) HWOp1SubInstr_data = hw_op1 * self._dot(subsequent_instruction_type[1:], self.HWOp1SubInstr[:,instructiontype]) HWOp2SubInstr_data = hw_op2 * self._dot(subsequent_instruction_type[1:], self.HWOp2SubInstr[:,instructiontype]) HDOp1SubInstr_data = hd_op1 * self._dot(subsequent_instruction_type[1:], self.HDOp1SubInstr[:,instructiontype]) HDOp2SubInstr_data = hd_op2 * self._dot(subsequent_instruction_type[1:], self.HDOp2SubInstr[:,instructiontype]) Operand1_bitinteractions_data = np.zeros((nb_points)) Operand2_bitinteractions_data = np.zeros((nb_points)) BitFlip1_bitinteractions_data = np.zeros((nb_points)) BitFlip2_bitinteractions_data = np.zeros((nb_points)) count = 0 for i in range(32): for j in range(i+1,32): Operand1_bitinteractions_data += self.Operand1_bitinteractions[count,instructiontype] * current_op1_binary[i] * current_op1_binary[j] Operand2_bitinteractions_data += self.Operand2_bitinteractions[count,instructiontype] * current_op2_binary[i] * current_op2_binary[j] BitFlip1_bitinteractions_data += self.BitFlip1_bitinteractions[count,instructiontype] * current_op1_bitflip[i] * current_op1_bitflip[j] BitFlip2_bitinteractions_data += self.BitFlip2_bitinteractions[count,instructiontype] * current_op2_bitflip[i] * current_op2_bitflip[j] count += 1 power = self.constant[instructiontype] \ + PrvInstr_data + SubInstr_data \ + Operand1_data + Operand2_data \ + BitFlip1_data + BitFlip2_data \ + HWOp1PrvInstr_data + HWOp2PrvInstr_data \ + HDOp1PrvInstr_data + HDOp2PrvInstr_data \ + HWOp1SubInstr_data + HWOp2SubInstr_data \ + HDOp1SubInstr_data + HDOp2SubInstr_data \ + Operand1_bitinteractions_data + Operand2_bitinteractions_data \ + BitFlip1_bitinteractions_data + BitFlip2_bitinteractions_data for i in range(nb_points): if triplet[CURRENT,i] == 5: power[i] = self.constant[triplet[CURRENT,i]] if debug: print([self.constant[instructiontype], \ PrvInstr_data, SubInstr_data, \ Operand1_data, Operand2_data, \ BitFlip1_data, BitFlip2_data, \ HWOp1PrvInstr_data, HWOp2PrvInstr_data, \ HDOp1PrvInstr_data, HDOp2PrvInstr_data, \ HWOp1SubInstr_data, HWOp2SubInstr_data, \ HDOp1SubInstr_data, HDOp2SubInstr_data, \ Operand1_bitinteractions_data, Operand2_bitinteractions_data, \ BitFlip1_bitinteractions_data, BitFlip2_bitinteractions_data]) return power ### To manage studied points def reset_points(self): """ Reset all the points previously added """ self.points = [] self.power = None def add_point(self, triplet, previous_ops, current_ops): """ Add a new point to analyse """ self.points.append((triplet, previous_ops, current_ops)) def run(self): """ Compute the power leakage of all the points previously added Store the results in 'self.power' """ nb_points = len(self.points) triplet = np.array([p[0] for p in self.points]).T # shape = (3, nb_points) previous_ops = np.array([p[1] for p in self.points]).T # shape = (2, nb_points) current_ops = np.array([p[2] for p in self.points]).T # shape = (2, nb_points) self.power = self.calculate_point(triplet, previous_ops, current_ops) def oneshot_point(self, triplet, previous_ops, current_ops): """ Compute the power of a single point defined by 'triplet', 'previous_ops', and 'current_ops' """ self.reset_points() self.add_point(triplet, previous_ops, current_ops) self.run() return self.power