-
Notifications
You must be signed in to change notification settings - Fork 56
/
Copy path01-discovering-containers.py
132 lines (107 loc) · 4.61 KB
/
01-discovering-containers.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
from lascar.container import Trace, TraceBatchContainer
import numpy as np
# In lascar, side-channel data are stored within `Trace` instances. This class
# is a named tuple with two attributes:
#
# - The first item is `leakage` and represents the observable side-channel, such
# as a power trace.
# - The second item is `value` and represents the handled values during the
# observation of "leakage".
#
# Both attributes must always be `numpy.ndarray` of any shape.
#
# The `__str__` method of Trace displays the `shape` and `dtype` for both
# leakage and value.
#
# The following creates a trace with (fake) side channel leakage and associated
# data. The leakage is a vector of 10 time samples, stored in a `np.ndarray`.
# The value is an array of 16 bytes, which can represent for instance a key.
leakage = np.random.rand(100)
value = np.random.randint(0, 256, (16,), dtype=np.uint8)
trace = Trace(leakage, value)
print("Trace:", trace)
# Most of time, side-channel analysis requires multiple traces. In Lascar,
# containers are used to group all the traces as a collection. Side-channel data
# can arises from:
#
# - a measurement device coupled to the device under test,
# - an acquisition campaign already saved on hard drive,
# - the simulation of an algorithm on a device,
# - or any other source of leakage information.
#
# The `Container` class provides an interface for accessing the traces (leakage
# and values). A class may implement this interface to adapt to any source of
# leakage and provide the traces when requested by the *Lascar* processing
# pipeline.
#
# Lascar already defines multiple `Container` implementations, and the most
# important is `TraceBatchContainer`, which stores in RAM both the leakages and
# values as `numpy.ndarray`, sharing the same first dimension: the number of
# traces in the container.
#
# In the following, a `TraceBatchContainer` is instanciated from two
# `numpy.ndarray`: `leakages` and `values`.
leakages = np.random.rand(10, 100) # 10 leakages, 100 time samples each.
values = np.random.randint(0, 256, (10, 16)) # 10 associated values, 16 bytes each.
batch = TraceBatchContainer(leakages, values)
print("Batch container:", batch)
# Containers implement the index operator, which returns either a `Trace` when
# an index is given, or a `TraceBatchContainer` with multiple traces when a
# slice is given. Furthermore, containers are iterable.
print("batch[0]:", batch[0])
print("batch[:5]:", batch[:5])
print("batch[range(3)]:", batch[range(3)])
for trace in batch:
print("Batch iteration:", trace)
print()
# Containers offer different mechanisms to limit the data to subsets.
# `leakage_section` (resp. `value_section`) is a `Container` attribute
# that will select the specified samples from the original leakage
# (resp. `value`). It is supposed to minimize the reading part, by specifying
# points of interests for instance.
print("Leakage section example:")
# To work only on leakage sample 10 and 15:
batch.leakage_section = [10, 15]
print(batch)
# To work only with the first 10 samples:
batch.leakage_section = range(10)
print(batch)
# To work with only with one tenth of the sample:
batch.leakage_section = range(0, 100, 10)
print(batch)
# To cancel `leakage_section`:
batch.leakage_section = None # cancelling leakage_section
print(batch)
print()
# `leakage_processing` (resp. `value_processing`) is a `Container` attribute,
# which can be a function that will be applied on the leakage (resp. value)
# after `leakage_section` (resp. value_section).
#
# Leakage processing can be used for instance for side-channel trace
# resynchronisation, signal filtering, etc.
#
# See lascar/tools/processing for a list of existing processing.
from lascar.tools.processing import *
print("Leakage processing example:")
# Any function or callable is accepted, provided it fits with the original
# leakage shape.
batch.leakage_processing = lambda leakage: leakage**2
print(batch)
# Centered product for high-order side-channel attacks: recombine samples
# [0, 1, 2] with [3, 4, 5]
batch.leakage_processing = CenteredProductProcessing(batch, [[0, 1, 2], [3, 4, 5]])
print(batch)
# Principal component analysis on leakage with 3 components
batch.leakage_processing = PcaProcessing(trace_batch, 3)
print(batch)
# No leakage processing
batch.leakage_processing = None
print()
# All container children implement a logger (from the logging module).
# By default, the loglevel is set to `INFO`, but it can be set at any time to
# display more or less informations.
# Note: other lascar classes implement a logger as well: Session, Engine,
# OutputMethod.
batch.logger.setLevel("DEBUG")
print(batch[::2])
batch.logger.setLevel("INFO")