Procházet zdrojové kódy

Transform into a Python package

master
Thibauld Feneuil před 4 roky
rodič
revize
93d76ecfc4
47 změnil soubory, kde provedl 696 přidání a 349 odebrání
  1. 5
    2
      .gitignore
  2. 3
    0
      MANIFEST.in
  3. 20
    24
      README.md
  4. 0
    41
      create-project.py
  5. 2
    0
      elmo/.gitignore
  6. 2
    0
      elmo/__init__.py
  7. 89
    0
      elmo/__main__.py
  8. 14
    0
      elmo/config.py
  9. 11
    24
      elmo/engine.py
  10. 252
    0
      elmo/manage.py
  11. 27
    37
      elmo/project_base.py
  12. 0
    0
      elmo/projects/.gitignore
  13. 0
    1
      elmo/projects/Examples/KyberNTT/.gitignore
  14. 0
    0
      elmo/projects/Examples/KyberNTT/Makefile
  15. 0
    0
      elmo/projects/Examples/KyberNTT/elmoasmfunctions.o
  16. 0
    0
      elmo/projects/Examples/KyberNTT/elmoasmfunctions.s
  17. 0
    0
      elmo/projects/Examples/KyberNTT/elmoasmfunctionsdef-extension.h
  18. 0
    0
      elmo/projects/Examples/KyberNTT/elmoasmfunctionsdef.h
  19. 0
    0
      elmo/projects/Examples/KyberNTT/ntt.c
  20. 0
    0
      elmo/projects/Examples/KyberNTT/ntt.h
  21. 0
    0
      elmo/projects/Examples/KyberNTT/params.h
  22. 0
    0
      elmo/projects/Examples/KyberNTT/poly.c
  23. 0
    0
      elmo/projects/Examples/KyberNTT/poly.h
  24. 0
    0
      elmo/projects/Examples/KyberNTT/polyvec.c
  25. 0
    0
      elmo/projects/Examples/KyberNTT/polyvec.h
  26. binární
      elmo/projects/Examples/KyberNTT/project.bin
  27. 0
    0
      elmo/projects/Examples/KyberNTT/project.c
  28. 0
    0
      elmo/projects/Examples/KyberNTT/project.ld
  29. 24
    4
      elmo/projects/Examples/KyberNTT/projectclass.py
  30. 0
    0
      elmo/projects/Examples/KyberNTT/reduce.c
  31. 0
    0
      elmo/projects/Examples/KyberNTT/reduce.h
  32. 0
    0
      elmo/projects/Examples/KyberNTT/vector.o
  33. 0
    0
      elmo/server/__init__.py
  34. 1
    1
      elmo/server/executorthread.py
  35. 0
    0
      elmo/server/protocol.py
  36. 1
    1
      elmo/server/servicethread.py
  37. 0
    0
      elmo/templates/Makefile
  38. 0
    0
      elmo/templates/elmoasmfunctionsdef-extension.h
  39. 0
    0
      elmo/templates/project.c
  40. 2
    3
      elmo/templates/projectclass.txt
  41. 64
    0
      elmo/utils.py
  42. 0
    18
      install
  43. 0
    163
      manage.py
  44. 0
    1
      requirements.txt
  45. 0
    29
      run_server.py
  46. 96
    0
      setup.py
  47. 83
    0
      test.py

+ 5
- 2
.gitignore Zobrazit soubor

@@ -1,2 +1,5 @@
*.pyc
elmo/*
**/__pycache__/**
build/*
python_elmo.egg-info/*
*.pyc
.DS_Store

+ 3
- 0
MANIFEST.in Zobrazit soubor

@@ -0,0 +1,3 @@
recursive-include elmo/projects *
recursive-include elmo/templates *
recursive-exclude elmo/elmo-tool *

+ 20
- 24
README.md Zobrazit soubor

@@ -1,6 +1,6 @@
# Online ELMO
# Python ELMO
_Online ELMO_ is a Python library which proposes an encapsulation of the project _ELMO_.
_Python ELMO_ is a Python library which proposes an encapsulation of the project _ELMO_.
[MOW17] **Towards Practical Tools for Side
Channel Aware Software Engineering : ’Grey Box’ Modelling for Instruction Leakages**
@@ -11,13 +11,9 @@ https://www.usenix.org/conference/usenixsecurity17/technical-sessions/presentati
## Requirements
To use _Online ELMO_, you need at least Python3.5 and the packages present in the file requirements.txt
To use _Python ELMO_, you need at least Python3.5 and ```numpy```.
```bash
pip3 install -r requirements.txt
```
The library will install and compile ELMO. So, you need the GCC compiler collection and the command/utility 'make' (for more details, see the documentation of ELMO). On Ubuntu/Debian,
The library will install and compile ELMO. So, you need the GCC compiler collection and the command/utility 'make' (for more details, see the documentation of ELMO). On Ubuntu/Debian,
```bash
sudo apt install build-essential
@@ -27,16 +23,16 @@ To use ELMO on a leaking binary program, you need to compile the C implementatio
## Installation
First, download _Online ELMO_.
First, download _Python ELMO_.
```bash
git clone https://git.aprilas.fr/tfeneuil/OnlineELMO
git clone https://git.aprilas.fr/tfeneuil/python-elmo
```
And then, install ELMO thanks to the script of installation.
```bash
./install
python setup.py install
```
## Usage
@@ -51,7 +47,7 @@ What is a _simulation project_ ? It is a project to simulate the traces of _one_
To start a new project, you can use the following function.
```python
from elmo_online.manage import create_simulation
from elmo.manage import create_simulation
create_simulation(
'dilithium', # The (relative) path of the project
'DilithiumSimulation' # The classname of the simulation
@@ -66,7 +62,7 @@ This function will create a repository _dilithium_ with all the complete squelet
### List all the available simulation
```python
from elmo_online.manage import search_simulations
from elmo.manage import search_simulations
search_simulations('.')
```
@@ -75,14 +71,14 @@ search_simulations('.')
'KyberNTTSimulation': <class 'KyberNTTSimulation'>}
```
_Online ELMO_ offers a example project to you in the repository _projects/Examples_ of 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/).
_Python ELMO_ offers a example project to you in the repository _projects/Examples_ of 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/).
### Use a simulation project
Warning! Before using it, you have to compile your project thanks to the provided Makefile.
```python
from elmo_online.manage import get_simulation
from elmo.manage import get_simulation
KyberNTTSimulation = get_simulation_via_classname('KyberNTTSimulation')
import numpy as np
@@ -97,21 +93,21 @@ traces = simulation.get_traces()
# And now, I can draw and analyse the traces
```
### Use a simulution project thanks to a server
### Use a simulation project thanks to a server
Sometimes, it is impossible to run the simulation thanks the simple method _run_ of the project class. Indeed, sometimes the Python script is executed in the environment where _Online ELMO_ cannot launch the ELMO tool. For example, it is the case where _Online ELMO_ is used in SageMath on Windows. On Windows, SageMath installation relies on the Cygwin POSIX emulation system and it can be a problem.
Sometimes, it is impossible to run the simulation thanks the simple method _run_ of the project class. Indeed, sometimes the Python script is executed in the environment where _Python ELMO_ cannot launch the ELMO tool. For example, it is the case where _Python ELMO_ is used in SageMath on Windows. On Windows, SageMath installation relies on the Cygwin POSIX emulation system and it can be a problem.
To offer a solution, _Online ELMO_ can be used thanks to a link client-server. The idea is you must launch the script _run_server.py_ which will listen (by default) at port 5000 in localhost.
To offer a solution, _Online ELMO_ can be used thanks to a link client-server. The idea is you must launch the script _run\_server.py_ which will listen (by default) at port 5000 in localhost.
```bash
python3 run_server.py
python -m elmo run-server
```
And after, you can manipulate the projects as described in the previous section by replacing _run_ to _run_online_.
And after, you can manipulate the projects as described in the previous section by replacing _run_ to _run\_online_.
```python
from elmo_online.manage import get_simulation
KyberNTTSimulation = get_simulation_via_classname('KyberNTTSimulation')
from elmo.manage import get_simulation
KyberNTTSimulation = get_simulation('KyberNTTSimulation')
import numpy as np
Kyber512 = {'k': 2, 'n': 256}
@@ -125,7 +121,7 @@ traces = simulation.get_traces()
# And now, I can draw and analyse the traces
```
Warning! Using the _run_online_ method doesn't exempt you from compiling the project with the provided Makefile.
Warning! Using the _run\_online_ method doesn't exempt you from compiling the project with the provided Makefile.
### Use the ELMO Engine
@@ -143,7 +139,7 @@ The type of the instructions are:
- "_**OTHER**_" for the other instructions.
```python
from elmo_online.engine import ELMOEngine, Instr
from elmo.engine import ELMOEngine, Instr
engine = ELMOEngine()
for i in range(0, 256):
engine.add_point(

+ 0
- 41
create-project.py Zobrazit soubor

@@ -1,41 +0,0 @@
import os, shutil
import re

from .manage import create_simulation

print('Creation of a new simulation project...')

### Create the repository of the projects
global_path = 'projects'
os.makedirs(global_path, exist_ok=True)

### Get the project classname
project_classname = ''
search = re.compile(r'[^a-zA-Z0-9_]').search
while not project_classname:
classname = input(' - What is the project classname? ')
if search(classname):
print(' > Illegal characters detected! Please enter a name with only the following characters : a-z, A-Z, 0-9, and "_".')
else:
project_classname = classname.strip()

### Get and create the project repository
search = re.compile(r'[^a-zA-Z0-9.-_/]').search
project_repository = ''
while not project_repository:
repository = input(' - What is the project repository? ')
if search(repository):
print('Illegal characters detected! Please enter a name with only the following characters : a-z, A-Z, 0-9, ".", "-", "_" and "/".')
else:
project_repository = repository
project_path = global_path+'/'+repository

project_path = create_simulation(project_path, classname)

print('')
print('Creation complete !')
print(' - Project repository: {}'.format(project_path))
print(' - Project class "{}" in {}'.format(project_classname, project_path+'/projectclass.py'))
print(' - Linker script: {}'.format(project_path+'/project.ld')))
print('')
print('Please don\'t to compile the project with the present Makefile before using it!')

+ 2
- 0
elmo/.gitignore Zobrazit soubor

@@ -0,0 +1,2 @@
elmo-tool/*
*.pyc

+ 2
- 0
elmo/__init__.py Zobrazit soubor

@@ -0,0 +1,2 @@
from .manage import *
from .engine import *

+ 89
- 0
elmo/__main__.py Zobrazit soubor

@@ -0,0 +1,89 @@
import sys
from .utils import Color

if len(sys.argv) <= 1:
print(Color.FAIL + 'Please enter an instruction.' + Color.ENDC)
exit()

command = sys.argv[1]

if command == 'create-simulation':
import os, shutil
import re

print('Creation of a new simulation project...')

### Create the repository of the projects
global_path = 'projects'
os.makedirs(global_path, exist_ok=True)

### Get the project classname
project_classname = ''
search = re.compile(r'[^a-zA-Z0-9_]').search
while not project_classname:
classname = input(' - What is the project classname? ')
if search(classname):
print(' > Illegal characters detected! Please enter a name with only the following characters : a-z, A-Z, 0-9, and "_".')
else:
project_classname = classname.strip()

### Get and create the project repository
search = re.compile(r'[^a-zA-Z0-9.-_/]').search
project_repository = ''
while not project_repository:
repository = input(' - What is the project repository? ')
if search(repository):
print('Illegal characters detected! Please enter a name with only the following characters : a-z, A-Z, 0-9, ".", "-", "_" and "/".')
else:
project_repository = repository
project_path = global_path+'/'+repository

from .manage import create_simulation
project_path = create_simulation(project_path, classname)

print('')
print('Creation complete !')
print(' - Project repository: {}'.format(project_path))
print(' - Project class "{}" in {}'.format(project_classname, project_path+'/projectclass.py'))
print(' - Linker script: {}'.format(project_path+'/project.ld'))
print('')
print('Please don\'t to compile the project with the present Makefile before using it!')
exit()

if command == 'run-server':
from .server.servicethread import ListeningThread
from .executorthread import ExecutorThread

def do_main_program():
global thread, stop
thread = ListeningThread('localhost', 5000, ExecutorThread)
thread.start()

def program_cleanup(signum, frame):
global thread, stop
thread.stop()
stop = True

thread = None
stop = False

# Execute
print("Executing...")
do_main_program()
print("Done ! And now, listening...")

import signal
signal.signal(signal.SIGINT, program_cleanup)
signal.signal(signal.SIGTERM, program_cleanup)

# Wait
import time
while not stop:
time.sleep(1)
exit()


print(Color.FAIL + 'Unknown Command.' + Color.ENDC)
exit()

+ 14
- 0
elmo/config.py Zobrazit soubor

@@ -0,0 +1,14 @@
import os

### Configuration of the module

TEMPLATE_REPOSITORY = 'templates'
PROJECTS_REPOSITORY = 'projects'
ELMO_TOOL_REPOSITORY = 'elmo-tool'
ELMO_EXECUTABLE_NAME = 'elmo'
ELMO_OUTPUT_ENCODING = 'latin-1'
ELMO_INPUT_FILE_NAME = 'input.txt'

MODULE_PATH = os.path.dirname(os.path.abspath(__file__))

SEARCH_EXCLUSION_TAG = 'EXCLUDE-FROM-SIMULATION-SEARCH'

engine.py → elmo/engine.py Zobrazit soubor

@@ -2,37 +2,20 @@ import numpy as np
import os
from enum import IntEnum, unique
def hweight(n):
c = 0
while n>0:
c += (n & 1)
n >>= 1
return c
def hdistance(x,y):
return hweight(x^y)
def binary_writing(n, nb_bits=32, with_hamming=False):
n = np.array(n)
w, h = np.zeros((nb_bits, len(n))), np.zeros((len(n)))
for ind in range(nb_bits):
w[ind] = (n & 1)
h += w[ind]
n >>= 1
ind += 1
return (w, h) if with_hamming else w
from .utils import binary_writing
from .config import ELMO_TOOL_REPOSITORY
@unique
class Instr(IntEnum):
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
@@ -49,7 +32,11 @@ class ELMOEngine:
return coeffs
def load_coefficients(self):
filename = os.path.dirname(os.path.abspath(__file__))+'/elmo/coeffs.txt'
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]])

+ 252
- 0
elmo/manage.py Zobrazit soubor

@@ -0,0 +1,252 @@
import os, shutil
import re
import inspect
import subprocess
import sys
from os.path import join as pjoin
from .config import (
TEMPLATE_REPOSITORY,
PROJECTS_REPOSITORY,
ELMO_TOOL_REPOSITORY,
ELMO_EXECUTABLE_NAME,
MODULE_PATH,
ELMO_OUTPUT_ENCODING,
SEARCH_EXCLUSION_TAG,
)
from .project_base import SimulationProject
from .utils import Color
############ GETTERS ############
def search_simulations_in_repository(repository, criteria=lambda x:True):
""" Search simulation classes in the 'repository' verifying the 'criteria'
Return a list of 'SimulationProject' subclasses
:repository: Repository of the searched simulation classes (string)
:criteria: Boolean function with 'SimulationProject' subclasses for input
"""
projects = {}
from .utils import write
for root, repositories, files in os.walk(repository):
for filename in files:
if re.fullmatch(r'.*project.*\.py', filename):
complete_filename = pjoin(root, filename)
# Encapsulate the project
globals = {
#'__builtins__': {'__build_class__': __build_class__},
'SimulationProject': SimulationProject,
'write': write,
}
locals = {}
# Read the project code
with open(complete_filename, 'r') as _file:
project = ''.join(_file.read())
if ('SimulationProject' not in project) or (SEARCH_EXCLUSION_TAG in project):
continue # Exclude this file
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(Color.WARNING + \
'Warning! Many simulations with the same name. ' + \
'Simulation ignored: {} in {}'.format(
key, complete_filename[len(repository)+1:]) + \
Color.ENDC
)
else:
obj.set_project_directory(os.path.abspath(root))
projects[key] = obj
return projects
def search_simulations_in_module(criteria=lambda x:True):
""" Search simulation classes among the module projects verifying the 'criteria'
Return a list of 'SimulationProject' subclasses
:criteria: Boolean function with 'SimulationProject' subclasses for input
"""
projects_path = pjoin(MODULE_PATH, PROJECTS_REPOSITORY)
return search_simulations_in_repository(projects_path, criteria)
def search_simulations(repository='.', criteria=lambda x:True):
""" Search simulation classes in the 'repository' and among
the module projects verifying the 'criteria'
Return a list of 'SimulationProject' subclasses
:repository: Repository of the searched simulation classes (string)
:criteria: Boolean function with 'SimulationProject' subclasses for input
"""
projects = search_simulations_in_repository(repository, criteria)
module_projects = search_simulations_in_module(criteria)
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(classname=None, repository='.'):
""" Get a simulation class in the 'repository' with the specific 'classname'
Return a subclass of 'SimulationProject' class
:classname: Name of the searched simulation class (string, optional)
:repository: Repository of the searched simulation class (string, optional)
"""
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()
############ SETTERS ############
def create_simulation(repository, classname, is_a_module_project=False):
""" Create a Simulation Project.
It builds a repository with the standard content for the project.
Return the absolute path of the created repository
:repository: Name of repository which will be created (string)
:classname: Name of the Python class to manage the project (string)
:is_a_module_project: If True and :repository: is a relative path,
create the project among the module projects.
"""
# Update the repository, if necessary
if is_a_module_project and (not os.path.isabs(repository)):
repository = pjoin(
MODULE_PATH,
PROJECTS_REPOSITORY,
repository,
)
# Build the project repository
try:
os.makedirs(repository, exist_ok=False)
except FileExistsError as err:
raise FileExistsError('Error, a project with this repository already exists!') from err
elmo_path = pjoin(MODULE_PATH, ELMO_TOOL_REPOSITORY)
template_path = pjoin(MODULE_PATH, TEMPLATE_REPOSITORY)
project_path = repository
# Add standard content in the project
files_from_ELMO = [
pjoin('Examples', 'elmoasmfunctions.o'),
pjoin('Examples', 'elmoasmfunctions.s'),
pjoin('Examples', 'elmoasmfunctionsdef.h'),
pjoin('Examples', 'DPATraces', 'MBedAES', 'vector.o'),
]
files_from_templates = [
'elmoasmfunctionsdef-extension.h',
'Makefile',
'project.c'
]
for filename in files_from_ELMO:
shutil.copy(pjoin(elmo_path, filename), project_path)
for filename in files_from_templates:
shutil.copy(pjoin(template_path, filename), project_path)
shutil.copy(
pjoin(elmo_path, 'Examples', 'DPATraces', 'MBedAES', 'MBedAES.ld'),
pjoin(project_path, 'project.ld')
)
# Create the project class
with open(pjoin(template_path, 'projectclass.txt')) as _source:
code = ''.join(_source.readlines())
code = code.replace('{{PROJECTCLASSNAME}}', classname)
with open(pjoin(project_path, 'projectclass.py'), 'w') as _dest:
_dest.write(code)
# Return the path of the created repository
return os.path.abspath(project_path)
############ USAGE FUNCTIONS ############
class DontFindBinaryError(Exception):
pass
def execute_simulation(project):
""" Execute a simulation of the power leakage using ELMO tool
Return the output and the errors of the execution of ELMO tool
:project: Subclass of 'SimulationProject' defining all the parameters of the simulation
"""
elmo_path = pjoin(MODULE_PATH, ELMO_TOOL_REPOSITORY)
# Some checking before executing
if not isinstance(project, SimulationProject):
raise TypeError('The project is not an instance of \'SimulationProject\' class.')
leaking_binary_path = pjoin(project.get_project_directory(), project.get_binary_path())
if not os.path.isfile(leaking_binary_path):
raise BinaryNotFoundError('Binary not found. Did you compile your project?')
if not os.path.isfile(pjoin(elmo_path, ELMO_EXECUTABLE_NAME)):
raise Exception('Installation Error: the executable of the ELMO tool is not found.')
# Launch generation of the traces by launching ELMO
command = '{} "{}"'.format(
pjoin('.', ELMO_EXECUTABLE_NAME),
leaking_binary_path,
)
process = subprocess.Popen(command, shell=True,
cwd=elmo_path, executable='/bin/bash',
stdout=subprocess.PIPE, stderr=subprocess.PIPE
)
# Follow the generation
output, error = b'', b''
num_trace = 0
while True:
output_line = process.stdout.readline()
error_line = process.stderr.readline()
if (not output_line) and (not error_line) and (process.poll() is not None):
break
if error_line:
error += error_line
if output_line:
output += output_line
if 'TRACE NO' in output_line.decode(ELMO_OUTPUT_ENCODING):
num_trace += 1
return_code = process.poll()
# Treat data
output = output.decode(ELMO_OUTPUT_ENCODING) if output else None
error = error.decode(ELMO_OUTPUT_ENCODING) if error else None
nb_traces = None
nb_instructions = None
if output:
nb_traces = output.count('TRACE NO')
nb_instructions_pattern = re.compile(r'\s*instructions/cy..es\s+(\d+)\s*')
for line in output.split('\n'):
res = nb_instructions_pattern.fullmatch(line)
if res:
nb_instructions = res.group(1)
# Return results
return {
'nb_traces': nb_traces,
'nb_instructions': nb_instructions,
'output': output,
'error': error,
}

project_base.py → elmo/project_base.py Zobrazit soubor

@@ -1,41 +1,21 @@
import os, re
import socket
from .protocol import SocketTool
import numpy as np
def to_hex(v, nb_bits=16):
try:
v_hex = v.hex()
except AttributeError:
v_hex = hex(v)[2:]
return '0'*(nb_bits//4-len(v_hex)) + v_hex
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):
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)
for i in range(nb_bits//8):
_input.write(uintXX[i]+'\n')
def write_list(_input, uint16_list):
for uint16 in uint16_list:
write(_input, uint16)
from .config import MODULE_PATH, ELMO_TOOL_REPOSITORY
from .utils import write
class SimulationProject:
# TAG: EXCLUDE-FROM-SIMULATION-SEARCH
""" Class to manage a simultion
It contains all the parameters of the simulation and has method to use it
"""
_nb_bits_for_nb_challenges = 16
_project_directory = None
### Define the project
@classmethod
def get_project_directory(cl):
""" """
if cl._project_directory:
return cl._project_directory
else:
@@ -54,24 +34,20 @@ class SimulationProject:
return ''
@classmethod
def get_binary(cl):
def get_binary_path(cl):
raise NotImplementedError()
@classmethod
def get_parameters_names(cl):
return set()
@classmethod
def adapt_project(cl, parameters):
return
def get_challenge_format(self):
raise NotImplementedError()
### Tools to realize the simulation of the project
def __init__(self, challenges=None):
self.elmo_folder = os.path.dirname(os.path.abspath(__file__))+'/elmo'
self.elmo_folder = os.path.join(MODULE_PATH, ELMO_TOOL_REPOSITORY)
self.challenges = challenges
self.reset()
@@ -83,6 +59,12 @@ class SimulationProject:
self._complete_results = None
self._complete_printed_data = None
def get_test_challenges(self):
raise NotImplementedError()
def get_random_challenges(self, nb_challenges):
raise NotImplementedError()
def set_challenges(self, challenges):
self.challenges = challenges
@@ -104,10 +86,15 @@ class SimulationProject:
aux(format[num_part], challenge[num_part])
def set_input(self, input):
assert len(self.challenges) < 2**16, 'The number of challenges must be strictly lower than 65536. Currently, there are {} challenges.'.format(len(self.challenges))
write(input, len(self.challenges), nb_bits=self._nb_bits_for_nb_challenges)
for challenge in self.challenges:
self.set_input_for_each_challenge(input, challenge)
if self.challenges:
assert len(self.challenges) < (1 << self._nb_bits_for_nb_challenges), \
'The number of challenges must be strictly lower than {}. Currently, there are {} challenges.'.format(
1 << self._nb_bits_for_nb_challenges,
len(self.challenges),
)
write(input, len(self.challenges), nb_bits=self._nb_bits_for_nb_challenges)
for challenge in self.challenges:
self.set_input_for_each_challenge(input, challenge)
def run(self):
self.reset()
@@ -120,6 +107,9 @@ class SimulationProject:
return res
def run_online(self, host='localhost', port=5000):
from .server.protocol import SocketTool
import socket
class TempInput:
def __init__(self):
self._buffer = ''

projects/.gitignore → elmo/projects/.gitignore Zobrazit soubor


projects/Examples/KyberNTT/.gitignore → elmo/projects/Examples/KyberNTT/.gitignore Zobrazit soubor

@@ -1,6 +1,5 @@
*.elf
*.list
*.bin
*.map
*.d
*.o

projects/Examples/KyberNTT/Makefile → elmo/projects/Examples/KyberNTT/Makefile Zobrazit soubor


projects/Examples/KyberNTT/elmoasmfunctions.o → elmo/projects/Examples/KyberNTT/elmoasmfunctions.o Zobrazit soubor


projects/Examples/KyberNTT/elmoasmfunctions.s → elmo/projects/Examples/KyberNTT/elmoasmfunctions.s Zobrazit soubor


projects/Examples/KyberNTT/elmoasmfunctionsdef-extension.h → elmo/projects/Examples/KyberNTT/elmoasmfunctionsdef-extension.h Zobrazit soubor


projects/Examples/KyberNTT/elmoasmfunctionsdef.h → elmo/projects/Examples/KyberNTT/elmoasmfunctionsdef.h Zobrazit soubor


projects/Examples/KyberNTT/ntt.c → elmo/projects/Examples/KyberNTT/ntt.c Zobrazit soubor


projects/Examples/KyberNTT/ntt.h → elmo/projects/Examples/KyberNTT/ntt.h Zobrazit soubor


projects/Examples/KyberNTT/params.h → elmo/projects/Examples/KyberNTT/params.h Zobrazit soubor


projects/Examples/KyberNTT/poly.c → elmo/projects/Examples/KyberNTT/poly.c Zobrazit soubor


projects/Examples/KyberNTT/poly.h → elmo/projects/Examples/KyberNTT/poly.h Zobrazit soubor


projects/Examples/KyberNTT/polyvec.c → elmo/projects/Examples/KyberNTT/polyvec.c Zobrazit soubor


projects/Examples/KyberNTT/polyvec.h → elmo/projects/Examples/KyberNTT/polyvec.h Zobrazit soubor


binární
elmo/projects/Examples/KyberNTT/project.bin Zobrazit soubor


projects/Examples/KyberNTT/project.c → elmo/projects/Examples/KyberNTT/project.c Zobrazit soubor


projects/Examples/KyberNTT/project.ld → elmo/projects/Examples/KyberNTT/project.ld Zobrazit soubor


projects/Examples/KyberNTT/projectclass.py → elmo/projects/Examples/KyberNTT/projectclass.py Zobrazit soubor

@@ -8,8 +8,11 @@
### - get_simulation_via_classname(classname)

class KyberNTTSimulation(SimulationProject):
KYBER_K = 2 #k=2 for Kyber512
KYBER_N = 256 #n=256 for Kyber512
@classmethod
def get_binary(cl):
def get_binary_path(cl):
return 'project.bin'

def __init__(self, *args, **kwargs):
@@ -26,6 +29,23 @@ class KyberNTTSimulation(SimulationProject):
secret = challenge

# Write the secret vector
for j in range(2): #k=2 for Kyber512
for k in range(256): #n=256 for Kyber512
write(input, secret[j,k])
for j in range(self.KYBER_K):
for k in range(self.KYBER_N):
write(input, secret[j,k])
def get_test_challenges(self):
import numpy as np
just_ones = np.ones((self.KYBER_K, self.KYBER_N), dtype=int)
return [
0 * just_ones,
1 * just_ones,
-2 * just_ones,
]
def get_random_challenges(self, nb_challenges=5):
import numpy as np
return [ np.random.choice(
[-2, -1, 0, 1, 2],
(self.KYBER_K, self.KYBER_N),
p=[1/16, 4/16, 6/16, 4/16, 1/16],
) for _ in range(nb_challenges) ]

projects/Examples/KyberNTT/reduce.c → elmo/projects/Examples/KyberNTT/reduce.c Zobrazit soubor


projects/Examples/KyberNTT/reduce.h → elmo/projects/Examples/KyberNTT/reduce.h Zobrazit soubor


projects/Examples/KyberNTT/vector.o → elmo/projects/Examples/KyberNTT/vector.o Zobrazit soubor


__init__.py → elmo/server/__init__.py Zobrazit soubor


executorthread.py → elmo/server/executorthread.py Zobrazit soubor

@@ -1,4 +1,4 @@
from servicethread import OneShotServiceThread
from .server.servicethread import OneShotServiceThread
import subprocess
import shutil

protocol.py → elmo/server/protocol.py Zobrazit soubor


servicethread.py → elmo/server/servicethread.py Zobrazit soubor

@@ -1,5 +1,5 @@
import threading
from protocol import Protocol, ClosureException
from .server.protocol import Protocol, ClosureException
import socket

templates/Makefile → elmo/templates/Makefile Zobrazit soubor


templates/elmoasmfunctionsdef-extension.h → elmo/templates/elmoasmfunctionsdef-extension.h Zobrazit soubor


templates/project.c → elmo/templates/project.c Zobrazit soubor


templates/projectclass.py → elmo/templates/projectclass.txt Zobrazit soubor

@@ -4,12 +4,11 @@
### to write an integer of 'nb_bits' bits in the 'input_file'.
### To get this simulation class in Python scripts, please use the functions in manage.py as
### - search_simulations(repository)
### - get_simulation(repository, classname=None)
### - get_simulation_via_classname(classname)
### - get_simulation(repository='.', classname=None)

class {{PROJECTCLASSNAME}}(SimulationProject):
@classmethod
def get_binary(cl):
def get_binary_path(cl):
return 'project.bin'

def __init__(self, *args, **kwargs):

+ 64
- 0
elmo/utils.py Zobrazit soubor

@@ -0,0 +1,64 @@
import numpy as np

### Binary operations
def hweight(n):
c = 0
while n>0:
c += (n & 1)
n >>= 1
return c

def hdistance(x,y):
return hweight(x^y)

def binary_writing(n, nb_bits=32, with_hamming=False):
n = np.array(n)
w, h = np.zeros((nb_bits, len(n))), np.zeros((len(n)))

for ind in range(nb_bits):
w[ind] = (n & 1)
h += w[ind]

n >>= 1
ind += 1
return (w, h) if with_hamming else w
### Conversion
def to_hex(v, nb_bits=16):
try:
v_hex = v.hex()
except AttributeError:
v_hex = hex(v)[2:]
return '0'*(nb_bits//4-len(v_hex)) + v_hex

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):
try:
return split_octet(to_hex(v & ((1<<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

### Write function
def write(_input, uint, nb_bits=16):
uint = to_signed_hex(uint, nb_bits=nb_bits)
for i in range(nb_bits//8):
_input.write(uint[i]+'\n')
def write_list(_input, uint_list, nb_bits=16):
for uint in uint_list:
write(_input, uint, nb_bits=nb_bits)

### Print Utils
class Color:
HEADER = '\033[95m'
OKBLUE = '\033[94m'
OKCYAN = '\033[96m'
OKGREEN = '\033[92m'
WARNING = '\033[93m'
FAIL = '\033[91m'
ENDC = '\033[0m'
BOLD = '\033[1m'
UNDERLINE = '\033[4m'

+ 0
- 18
install Zobrazit soubor

@@ -1,18 +0,0 @@
#!/bin/bash

elmo_repository="elmo"
elmo_source="https://github.com/sca-research/ELMO.git"

if [ -d ${elmo_repository} ]; then
echo "ELMO tool already installed."
else
echo "ELMO tool NOT installed... Installing via online resources..."
# Download the tool
git clone --depth=1 --branch=master ${elmo_source} ${elmo_repository}
rm -rf ./${elmo_repository}/.git

# Compile the tool
cd ./${elmo_repository}
make
cd ..
fi

+ 0
- 163
manage.py Zobrazit soubor

@@ -1,163 +0,0 @@
import os, shutil
import re
import inspect
import subprocess
def search_simulations_in_repository(repository, criteria=lambda x: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
return projects
def search_simulations_in_module(criteria=lambda x:True):
module_path = os.path.dirname(os.path.abspath(__file__))
projects_path = module_path+'/projects'
return search_simulations_in_repository(projects_path, criteria)
def search_simulations(repository, criteria=lambda x:True):
projects = search_simulations_in_repository(repository, criteria)
module_projects = search_simulations_in_module(criteria)
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):
""" 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)
return os.path.abspath(project_path)
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, stderr=subprocess.PIPE)
output, error = process.communicate()
output = output.decode('latin-1') if output else None
error = error.decode('latin-1') if error else None
nb_traces = output.count('TRACE NO')
# Return results
return {
'output': output,
'error': error,
'nb_traces': nb_traces,
}

+ 0
- 1
requirements.txt Zobrazit soubor

@@ -1 +0,0 @@
numpy==1.19.1

+ 0
- 29
run_server.py Zobrazit soubor

@@ -1,29 +0,0 @@
from servicethread import ListeningThread
from executorthread import ExecutorThread
def do_main_program():
global thread, stop
thread = ListeningThread('localhost', 5000, ExecutorThread)
thread.start()
def program_cleanup(signum, frame):
global thread, stop
thread.stop()
stop = True
thread = None
stop = False
# Execute
print("Executing...")
do_main_program()
print("Done ! And now, listening...")
import signal
signal.signal(signal.SIGINT, program_cleanup)
signal.signal(signal.SIGTERM, program_cleanup)
# Wait
import time
while not stop:
time.sleep(1)

+ 96
- 0
setup.py Zobrazit soubor

@@ -0,0 +1,96 @@
import setuptools
from setuptools.command.build_py import build_py
from setuptools.command.install import install
from subprocess import check_call
import os, shutil
import re


### CONFIGURATION
PELMO_SOURCE = 'https://github.com/ThFeneuil/python-elmo'
ELMO_MODULE_NAME = 'elmo'
ELMO_SOURCE = 'https://github.com/sca-research/ELMO.git'

# Import configuration of ELMO_MODULE,
# cannot use 'import elmo' because it can be ambiguous which module it will call
with open(os.path.join(ELMO_MODULE_NAME, 'config.py')) as _file:
globals = {'__file__': os.path.join(
os.path.abspath(ELMO_MODULE_NAME),
'config.py'
)}
exec(_file.read(), globals)
ELMO_TOOL_REPOSITORY = globals['ELMO_TOOL_REPOSITORY']
ELMO_INPUT_FILE_NAME = globals['ELMO_INPUT_FILE_NAME']


def install_elmo_tool(elmo_complete_path):
## Download the tool
elmo_download_command = "git clone --depth=1 --branch=master {url} {elmo_path}".format(
url=ELMO_SOURCE,
elmo_path=elmo_complete_path,
)
check_call(elmo_download_command.split())
shutil.rmtree(os.path.join(elmo_complete_path, '.git'))
# 'test' contains a Python2 test, and it raises an error during byte-compiling
shutil.rmtree(os.path.join(elmo_complete_path, 'test'))

## Setup the tool
elmodefines_h = None
elmodefines_h_path = os.path.join(elmo_complete_path, 'elmodefines.h')
with open(elmodefines_h_path, 'r') as _file:
elmodefines_lines = _file.readlines()
for i, line in enumerate(elmodefines_lines):
if re.match(r'\s*#define\s+DATAFILEPATH', line):
elmodefines_lines[i] = '#define DATAFILEPATH "{}"'.format(ELMO_INPUT_FILE_NAME)
elmodefines_h = ''.join(elmodefines_lines)
with open(elmodefines_h_path, 'w') as _file:
_file.write(elmodefines_h)
# Compile the tool
check_call("make clean".split(), cwd=elmo_complete_path)
check_call("make".split(), cwd=elmo_complete_path)

class PostBuildCommand(build_py):
""" Build Command to add the ELMO installation """
def run(self):
build_py.run(self)
# ELMO Installation
elmo_complete_path = os.path.join(
self.build_lib,
ELMO_MODULE_NAME,
ELMO_TOOL_REPOSITORY,
)
shutil.rmtree(elmo_complete_path, ignore_errors=True)
install_elmo_tool(elmo_complete_path)


if __name__ == '__main__':
with open("README.md", "r") as fh:
long_description = fh.read()

setuptools.setup(
name="python-elmo",
version="0.0.1",
author="Thibauld Feneuil",
author_email="thibauld.feneuil@cryptoexperts.com",
description="Emulator for power Leakage for the M0",
long_description=long_description,
long_description_content_type="text/markdown",
url=PELMO_SOURCE,
project_urls={
'ELMO Source': ELMO_SOURCE,
'pELMO Source': PELMO_SOURCE,
},
packages=setuptools.find_packages(),
classifiers=[
"Programming Language :: Python :: 3",
],
python_requires='>=3.5',
cmdclass={
'build_py': PostBuildCommand,
},
install_requires=['numpy'],
include_package_data=True,
)

+ 83
- 0
test.py Zobrazit soubor

@@ -0,0 +1,83 @@
import os, shutil
from subprocess import check_call

### Install ELMO
from elmo.config import ELMO_TOOL_REPOSITORY
ELMO_SOURCE = 'https://github.com/sca-research/ELMO.git'
elmo_complete_path = os.path.join('elmo', ELMO_TOOL_REPOSITORY)
if not os.path.isdir(elmo_complete_path):
from setup import install_elmo_tool
install_elmo_tool(elmo_complete_path)

def print_success(text):
OKGREEN = '\033[92m'
ENDC = '\033[0m'
print(OKGREEN+text+ENDC)

#########################################################
# TEST 1 : MANAGE A FRESH SIMULATION #
#########################################################

foldername = 'test-project'
classname = 'TestSimu'

### Create Simulation
from elmo.manage import create_simulation
shutil.rmtree('test-project', ignore_errors=True) # Clean before
create_simulation(foldername, classname)

assert os.path.isdir(foldername), 'Folder not created'

### List Available Simulations
from elmo.manage import search_simulations
simulations = search_simulations(foldername)

assert classname in simulations, 'Test Simulation not found'

### Use a Simulation
from elmo.manage import get_simulation
simu = get_simulation(classname, foldername)

shutil.rmtree(foldername)
print_success(' - Test 1 "Manage A Fresh Simulation": Success!')

#########################################################
# TEST 2 : USE ELMO ENGINE #
#########################################################

### Use the ELMO Engine
from elmo.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

assert power.shape == (256, )

print_success(' - Test 2 "Use ELMO Engine": Success!')

#########################################################
# TEST 3 : USE A REAL SIMULATION #
#########################################################

from elmo import get_simulation
KyberNTTSimulation = get_simulation('KyberNTTSimulation')
simulation = KyberNTTSimulation()
simulation.set_challenges(simulation.get_random_challenges(10))
res = simulation.run()

assert not res['error']
assert res['nb_traces'] == 10
assert res['nb_instructions']

print_success(' - Test 3 "Use A Real Simulation": Success!')

#########################################################

print_success('All seems fine!')

Načítá se…
Zrušit
Uložit