Quellcode durchsuchen

Add offline run

master
Thibauld Feneuil vor 4 Jahren
Ursprung
Commit
f6600795d0
8 geänderte Dateien mit 250 neuen und 55 gelöschten Zeilen
  1. 64
    40
      README.md
  2. 11
    1
      engine.py
  3. 148
    0
      manage.py
  4. 21
    7
      project_base.py
  5. 1
    1
      project_reader.py
  6. 3
    2
      projects/.gitignore
  7. 2
    3
      templates/Makefile
  8. 0
    1
      templates/project.c

+ 64
- 40
README.md Datei anzeigen

@@ -5,8 +5,7 @@ _Online ELMO_ is a Python library which proposes an encapsulation of the project
[MOW17] **Towards Practical Tools for Side
Channel Aware Software Engineering : ’Grey Box’ Modelling for Instruction Leakages**
by _David McCann, Elisabeth Oswald et Carolyn Whitnall_.
https://www.usenix.org/conference/
usenixsecurity17/technical-sessions/presentation/mccann.
https://www.usenix.org/conference/usenixsecurity17/technical-sessions/presentation/mccann
**ELMO GitHub**: https://github.com/sca-research/ELMO
@@ -24,22 +23,20 @@ The library will install and compile ELMO. So, you need the GCC compiler collect
sudo apt install build-essential
```
To use ELMO on a leaking binary program, you need to compile the C implementations to binary programs (a ".bin" file). "ELMO is not linked to any ARM specific tools, so users should be fine to utilise whatever they want for this purpose."
ELMO Documentation: "A minimal working platform for compiling your code into an ARM Thumb binary would be to use the GNU ARM Embedded Toolchain (tested version: arm-none-eabi-gcc version 7.3.1 20180622, it can be downloaded from https://developer.arm.com/open-source/gnu-toolchain/gnu-rm).", see the documentation for more details.
To use ELMO on a leaking binary program, you need to compile the C implementations to binary programs (a ".bin" file). "ELMO is not linked to any ARM specific tools, so users should be fine to utilise whatever they want for this purpose. A minimal working platform for compiling your code into an ARM Thumb binary would be to use the GNU ARM Embedded Toolchain (tested version: arm-none-eabi-gcc version 7.3.1 20180622, it can be downloaded from https://developer.arm.com/open-source/gnu-toolchain/gnu-rm).", see the [documentation of ELMO](https://github.com/sca-research/ELMO) for more details.
## Installation
First, download _ELMO Online_.
First, download _Online ELMO_.
```bash
git clone https://git.aprilas.fr/tfeneuil/OnlineELMO
```
And then, install ELMO by launching the ELMO run_server (you must have GCC to compile ELMO, if it is not the case, please do "sudo apt install build-essential" on Ubuntu/Debian)
And then, install ELMO thanks to the script of installation.
```bash
./run
./install
```
## Usage
@@ -47,56 +44,83 @@ And then, install ELMO by launching the ELMO run_server (you must have GCC to co
### Create a new simulation project
What is a _simulation project_ ? It is a project to simulate the traces of _one_ binary program. It includes
- A Python class which enable to generate traces in Python ;
- The C program which will be compile to have the binary program for the analysis ;
- A linker script where the configuration of the simulated device are defined
- A Python class which enable to generate traces in Python;
- The C program which will be compile to have the binary program for the analysis;
- A linker script where the configuration of the simulated device are defined.
To start a new project, you can use the following function.
```python3
> from elmo_online.manage import create_simulation
> create_simulation(
> 'dilithium', # The (relative) path of the project
> 'DilithiumSimulation' # The classname of the simulation
>)
```
```bash
python3 create-project.py
> Creation of a new simulation project...
> - What is the project classname? KyberSimulation # Please enter here the name of the Python class of the simulation
> - What is the project repository? Kyber/SimuOne # Please enter here the relative repository of the simulation
This function will create a repository _dilithium_ with all the complete squeleton of the project. In this repository, you can find:
- The file _project.c_ where you must put the leaking code;
- The file _projectclass.py_ where there is the class of the simulation which will enable you to generate traces of the project in Python scripts;
- A _Makefile_ ready to be used with a compiler _arm-none-eabi-gcc_.
_Online ELMO_ offers a example project to you in the repository _projects/Examples_ in the module. This example is a project to generate traces of the execution of the NTT implemented in the cryptosystem [Kyber](https://pq-crystals.org/kyber/).
### List all the available simulation
```python3
>from elmo_online.manage import search_simulations
>search_simulations('.')
{'DilithiumSimulation': <class 'DilithiumSimulation'>,
'KyberNTTSimulation': <class 'KyberNTTSimulation'>}
```
### Use a simulation project in a Python script
### Use a simulation project
Warning! Before using it, you have to compile your project thanks to the provided Makefile.
```python
from elmo_online.launch import KyberSimulation
simulation = KyberSimulation(challenges)
simulation.run() # Launch the simulation
traces = simulation.get_traces()
# And now, I can analyse the traces
> from elmo_online.manage import get_simulation
> KyberSimulation = get_simulation_via_classname('KyberNTTSimulation')
>
> import numpy as np
> Kyber512 = {'k': 2, 'n': 256}
> challenges = [
> np.ones((Kyber512['k'], Kyber512['n']), dtype=int),
> ]
>
> simulation = KyberSimulation(challenges)
> simulation.run() # Launch the simulation
> traces = simulation.get_traces()
> # And now, I can draw and analyse the traces
```
### Use the ELMO Engine
The engine use the model of ELMO to directly give the power consumption of an assembler instruction. In the model, to have the power consumption of an assembler instruction, it needs
The engine exploits the model of ELMO to directly give the power consumption of an assembler instruction. In the model, to have the power consumption of an assembler instruction, it needs
- the type and the operands of the previous assembler instruction
- the type and the operands of the current assembler instruction
- the type of the next assembler instruction
The type of the instructions are:
- "0" for ADD(1-4), AND, CMP, CPY, EOR, MOV, ORR, ROR, SUB
- "1" for LSL(2), LSR(2)
- "2" for STR, STRB, STRH
- "3" for LDR, LDRB, LDRH
- "4" for MUL
- "5" for the other instructions
- "_**EOR**_" for ADD(1-4), AND, CMP, CPY, EOR, MOV, ORR, ROR, SUB;
- "_**LSL**_" for LSL(2), LSR(2);
- "_**STR**_" for STR, STRB, STRH;
- "_**LDR**_" for LDR, LDRB, LDRH;
- "_**MUL**_" for MUL;
- "_**OTHER**_" for the other instructions.
```python
from elmo_online.engine import ELMOEngine
engine = ELMOEngine()
for i in range(0, 256):
engine.add_point(
(3, 4, 5), # Types of the previous, current and next instructions
(0x0000, i), # Operands of the previous instructions
(0x2BAC, i) # Operands of the current instructions
)
engine.run() # Compute the power consumption of all these points
power = engine.power # Numpy 1D array with an entry for each previous point
engine.reset_points() # Reset the engine to study other points
> from elmo_online.engine import ELMOEngine, Instr
> engine = ELMOEngine()
> for i in range(0, 256):
> engine.add_point(
> (Instr.LDR, Instr.MUL, Instr.OTHER), # Types of the previous, current and next instructions
> (0x0000, i), # Operands of the previous instructions
> (0x2BAC, i) # Operands of the current instructions
> )
> engine.run() # Compute the power consumption of all these points
> power = engine.power # Numpy 1D array with an entry for each previous point
> engine.reset_points() # Reset the engine to study other points
```
## Licences

+ 11
- 1
engine.py Datei anzeigen

@@ -1,5 +1,6 @@
import numpy as np
import os
from enum import IntEnum, unique
def hweight(n):
c = 0
@@ -23,7 +24,16 @@ def binary_writing(n, nb_bits=32, with_hamming=False):
ind += 1
return (w, h) if with_hamming else w
@unique
class Instr(IntEnum):
EOR = 0
LSL = 1
STR = 2
LDR = 3
MUL = 4
OTHER = 5
PREVIOUS = 0
CURRENT = 1
SUBSEQUENT = 2

+ 148
- 0
manage.py Datei anzeigen

@@ -0,0 +1,148 @@
import os, shutil
import re
import inspect
import subprocess
def search_simulations(repository, criteria=lambda x:True, search_also_in_module=True):
""" To search simulation classes in the 'repository' verifying the 'criteria' """
projects = {}
from .project_base import SimulationProject, write
for root, repositories, files in os.walk(repository):
for filename in files:
if re.fullmatch(r'.*project.*\.py', filename):
# Encapsulation the project
complete_filename = root+'/'+filename
globals = {
#'__builtins__': {'__build_class__': __build_class__},
'SimulationProject': SimulationProject,
'write': write,
}
locals = {}
# Read the project code
with open(complete_filename, 'r') as _file:
project = '\n'.join(_file.readlines())
exec(project, globals, locals)
# Extract the simulations
for key, obj in locals.items():
if inspect.isclass(obj) and issubclass(obj, SimulationProject):
if criteria(obj):
if key in projects:
print('Warning! Multiplie simulation with the same name. Simulation ignored: {} in {}'.format(key, complete_filename[len(repository)+1:]))
else:
obj.set_project_directory(os.path.abspath(root))
projects[key] = obj
if search_also_in_module:
module_path = os.path.dirname(os.path.abspath(__file__))
module_projects = search_simulations(module_path+'/projects', criteria=lambda x:True, search_also_in_module=False)
for key, project in module_projects.items():
if key not in projects:
projects[key] = project
return projects
class SimulationNotFoundError(Exception):
pass
class TooManySimulationsError(Exception):
pass
def get_simulation(repository, classname=None):
""" Get a simulation class in the 'repository' with the specific 'classname' """
criteria = lambda x: True
if classname is not None:
criteria = lambda x: x.__name__ == classname.strip()
projects = search_simulations(repository, criteria)
if len(projects) == 1:
return list(projects.values())[0]
elif len(projects) < 1:
raise SimulationNotFoundError()
else:
raise TooManySimulationsError()
def get_simulation_via_classname(classname):
return get_simulation('.', classname)
def create_simulation(repository, classname, verbose=True):
""" Create a simulation class """
try:
os.makedirs(repository, exist_ok=False)
except FileExistsError as err:
raise FileExistsError('Error, a project with this repository already exists!') from err
module_path = os.path.dirname(os.path.abspath(__file__))
elmo_path = module_path+'/elmo'
template_path = module_path+'/templates'
project_path = repository
### Add contents in the project
files_from_ELMO = [
'Examples/elmoasmfunctions.o',
'Examples/elmoasmfunctions.s',
'Examples/elmoasmfunctionsdef.h',
'Examples/DPATraces/MBedAES/vector.o',
]
files_from_templates = [
'elmoasmfunctionsdef-extension.h',
'Makefile',
'project.c'
]
for filename in files_from_ELMO:
shutil.copy(elmo_path+'/'+filename, project_path)
for filename in files_from_templates:
shutil.copy(template_path+'/'+filename, project_path)
shutil.copy(elmo_path+'/'+'Examples/DPATraces/MBedAES/MBedAES.ld', project_path+'/'+'project.ld')
### Create the project class
with open(template_path+'/projectclass.py') as _source:
code = ''.join(_source.readlines())
code = code.replace('{{PROJECTCLASSNAME}}', classname)
with open(project_path+'/'+'projectclass.py', 'w') as _dest:
_dest.write(code)
def execute_simulation(project, data=None):
""" Execute a simulation with 'data' """
# Make the compilation
if False:
print('Compiling binary...')
# Adapt the project source
with open('binaries/Frodo/frodo-base.c', 'r') as _src_file:
content = _src_file.read()
with open('binaries/Frodo/frodo.c', 'w') as _dst_file:
_dst_file.write(content.replace('%NEED_TO_FILL%', str(n)))
# Compile the project
make_directory = 'projects/{}/{}'.format(project.get_project_directory(), project.get_make_directory())
process = subprocess.Popen('make', shell=True, cwd=make_directory, executable='/bin/bash', stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output, error = process.communicate()
if error and ('error' in error.decode('latin-1')):
print("Error to compile")
print(error)
raise Exception()
# Save last compilation data
global_variables[project_name] = {
'last_n': n,
}
# Generate the trace by launching ELMO
command = './elmo {}/{}'.format(
project.get_project_directory(),
project.get_binary()
)
cwd = os.path.dirname(os.path.abspath(__file__))+'/elmo'
process = subprocess.Popen(command, shell=True, cwd=cwd, executable='/bin/bash', stdout=subprocess.PIPE)
output, error = process.communicate()
# Return results
return (
output.decode('latin-1') if output else None,
error.decode('latin-1') if error else None,
)

+ 21
- 7
project_base.py Datei anzeigen

@@ -1,6 +1,6 @@
import os, re
import socket
from protocol import SocketTool
from .protocol import SocketTool
import numpy as np
def to_hex(v, nb_bits=16):
@@ -14,7 +14,10 @@ def split_octet(hexstr):
return [hexstr[i:i+2] for i in range(0, len(hexstr), 2)]
def to_signed_hex(v, nb_bits=16):
return split_octet(to_hex(v & (2**nb_bits-1), nb_bits=nb_bits))
try:
return split_octet(to_hex(v & (2**nb_bits-1), nb_bits=nb_bits))
except TypeError as err:
raise TypeError('Error to transform a <{}> into signed hex.'.format(type(v))) from err
def write(_input, uintXX, nb_bits=16):
uintXX = to_signed_hex(uintXX, nb_bits=nb_bits)
@@ -31,15 +34,15 @@ def launch_simulation(quiet=False, **kwargs):
s.connect(('localhost', 5000))
SocketTool.send_data(s, kwargs)
if not SocketTool.get_ack(s):
raise RuntimeError("The request has been refused !")
raise RuntimeError("NACK received: The request has been refused !")
else:
data = SocketTool.get_data(s)
if data['error'] and not quiet:
raise Exception("The simulation return an error")
raise Exception("The simulation return an error.")
return data['output'], data['error']
s.close()
except IOError:
raise RuntimeError("The connection refused. Has the ELMO server been switch on ?")
except IOError as err:
raise RuntimeError("The connection refused. Has the ELMO server been switch on ?") from err
class SimulationProject:
@@ -117,12 +120,23 @@ class SimulationProject:
def run(self):
with open('{}/input.txt'.format(self.elmo_folder), 'w') as _input:
self.set_input(_input)
from .manage import execute_simulation
execute_simulation(self)
self.is_executed = True
def run_online(self):
with open('{}/input.txt'.format(self.elmo_folder), 'w') as _input:
self.set_input(_input)
launch_simulation(project=self.get_project_label(), quiet=False)
self.is_executed = True
def get_asmtrace_filename(self):
return '{}/output/asmoutput/asmtrace00001.txt'.format(self.elmo_folder)
def get_asmtrace(self):
with open(self.get_asmtrace_filename(), 'r') as _file:
return [line.strip() for line in _file.readlines()]
def get_indexes_of(self, condition):
with open(self.get_asmtrace_filename(), 'r') as _file:
asmtrace = _file.readlines()

+ 1
- 1
project_reader.py Datei anzeigen

@@ -28,7 +28,7 @@ class ProjectReader:
exec(project, globals, locals)
# Extract the simulations
for key, obj in locals.item():
for key, obj in locals.items():
if inspect.isclass(obj) and issubclass(obj, SimulationProject):
if key in projects:
print('Warning! Multiplie simulation with the same name. Simulation ignored: {} in {}'.format(key, complete_filename[len(PROJECTS_REPOSITORY)+1:]))

+ 3
- 2
projects/.gitignore Datei anzeigen

@@ -1,2 +1,3 @@
*
!.gitignore
/**
!/.gitignore
!/Examples/

+ 2
- 3
templates/Makefile Datei anzeigen

@@ -82,7 +82,6 @@ OBJDUMP := $(PREFIX)-objdump
# Source files

LDSCRIPT = $(BINARY).ld
#OBJS += $(BINARY).o
ELMOASMFUNCTIONS = elmoasmfunctions.o

#!!! PLEASE ADD YOUR SOURCES, HEADERS AND OBJECTS HERE !!!
@@ -137,8 +136,8 @@ $(BINARY).bin: $(BINARY).elf
$(BINARY).list: $(BINARY).elf
$(Q)$(OBJDUMP) -S $(BINARY).elf > $(BINARY).list

$(BINARY).elf $(BINARY).map: $(OBJS) $(LDSCRIPT)
$(Q)$(LD) $(LDFLAGS) vector.o $(ARCH_FLAGS) $(OBJS) $(ELMOASMFUNCTIONS) $(LDLIBS) -o $(BINARY).elf
$(BINARY).elf $(BINARY).map: $(OBJECTS) $(LDSCRIPT)
$(Q)$(LD) $(LDFLAGS) vector.o $(ARCH_FLAGS) $(OBJECTS) $(ELMOASMFUNCTIONS) $(LDLIBS) -o $(BINARY).elf

%.o: %.c $(HEADER)
$(Q)$(CC) $(CFLAGS) $(CPPFLAGS) $(ARCH_FLAGS) -c -o $@ $<

+ 0
- 1
templates/project.c Datei anzeigen

@@ -1,7 +1,6 @@
#include <stdio.h>
#include <stdlib.h>

#include "elmoasmfunctionsdef.h"
#include "elmoasmfunctionsdef-extension.h"

// ELMO API :

Laden…
Abbrechen
Speichern