aboutsummaryrefslogtreecommitdiffhomepage
path: root/pyecsca
diff options
context:
space:
mode:
authorJ08nY2024-01-26 18:58:53 +0100
committerJ08nY2024-01-26 18:58:53 +0100
commit30cfd49b2b3a6a5180819a20d004b22e37e3fb77 (patch)
treeb343c13e52411e9d281c7b289415db8d390ff43c /pyecsca
parentd3230129f0720e756b113f80ed77f652ad32b44e (diff)
downloadpyecsca-30cfd49b2b3a6a5180819a20d004b22e37e3fb77.tar.gz
pyecsca-30cfd49b2b3a6a5180819a20d004b22e37e3fb77.tar.zst
pyecsca-30cfd49b2b3a6a5180819a20d004b22e37e3fb77.zip
Add tests for DPA and CPA.
Diffstat (limited to 'pyecsca')
-rw-r--r--pyecsca/sca/attack/CPA.py83
-rw-r--r--pyecsca/sca/attack/DPA.py70
2 files changed, 102 insertions, 51 deletions
diff --git a/pyecsca/sca/attack/CPA.py b/pyecsca/sca/attack/CPA.py
index 9a9cef0..8d4687a 100644
--- a/pyecsca/sca/attack/CPA.py
+++ b/pyecsca/sca/attack/CPA.py
@@ -1,20 +1,20 @@
-from pyecsca.ec.mult import ScalarMultiplier
-from pyecsca.ec.point import Point
-from pyecsca.ec.context import DefaultContext, local
-from pyecsca.ec.params import DomainParameters
-from pyecsca.ec.mod import Mod
-from pyecsca.sca.trace import Trace
from public import public
from scipy.stats import pearsonr
-from pyecsca.sca.trace.plot import plot_trace
-from pyecsca.sca.attack.leakage_model import LeakageModel
import numpy as np
from numpy.typing import NDArray
+from ...ec.mult import ScalarMultiplier
+from ...ec.point import Point
+from ...ec.context import DefaultContext, local
+from ...ec.params import DomainParameters
+from ...ec.mod import Mod
+from ..trace import Trace
+from ..trace.plot import plot_trace
+from ..attack.leakage_model import LeakageModel
-@public
-class CPA():
+@public
+class CPA:
traces: NDArray
points: list[Point]
mult: ScalarMultiplier
@@ -22,38 +22,51 @@ class CPA():
leakage_model: LeakageModel
correlations: dict[str, list[list[float]]]
- def __init__(self, points: list[Point], traces: list[Trace], leakage_model: LeakageModel, mult: ScalarMultiplier, params: DomainParameters):
- '''
+ def __init__(
+ self,
+ points: list[Point],
+ traces: list[Trace],
+ leakage_model: LeakageModel,
+ mult: ScalarMultiplier,
+ params: DomainParameters,
+ ):
+ """
:param points: Points on which scalar multiplication with secret scalar was performed
:param traces: Power traces corresponding to the scalar multiplication for each of the points
:param mult: Scalar multiplier used
:param params: Domain parameters used
- '''
+ """
self.points = points
self.traces = np.array([trace.samples for trace in traces]).transpose()
self.mult = mult
self.params = params
self.leakage_model = leakage_model
- self.correlations = {'guess_one' : [], 'guess_zero' : []}
+ self.correlations = {"guess_one": [], "guess_zero": []}
- def compute_intermediate_value(self, guessed_scalar: int, target_bit: int, point: Point) -> Mod:
- with (local(DefaultContext())) as ctx:
+ def compute_intermediate_value(
+ self, guessed_scalar: int, target_bit: int, point: Point
+ ) -> Mod:
+ with local(DefaultContext()) as ctx:
self.mult.init(self.params, point)
self.mult.multiply(guessed_scalar)
action_index = -1
- for bit in bin(guessed_scalar)[2:target_bit + 2]:
- if bit == '1':
+ for bit in bin(guessed_scalar)[2 : target_bit + 2]:
+ if bit == "1":
action_index += 2
- elif bit == '0':
+ elif bit == "0":
action_index += 1
result = ctx.actions.get_by_index([0, action_index])[0]
return result.output_points[0].X
- def compute_correlation_trace(self, guessed_scalar: int, target_bit: int) -> list[float]:
+ def compute_correlation_trace(
+ self, guessed_scalar: int, target_bit: int
+ ) -> list[float]:
correlation_trace = []
intermediate_values = []
for i in range(len(self.points)):
- intermediate_value = self.compute_intermediate_value(guessed_scalar, target_bit, self.points[i])
+ intermediate_value = self.compute_intermediate_value(
+ guessed_scalar, target_bit, self.points[i]
+ )
intermediate_values.append(self.leakage_model(intermediate_value))
for trace in self.traces:
correlation_trace.append(pearsonr(intermediate_values, trace)[0])
@@ -62,7 +75,13 @@ class CPA():
def plot_correlations(self, ct):
return plot_trace(Trace(np.array(ct))).opts(width=950, height=600)
- def recover_bit(self, recovered_scalar: int, target_bit: int, scalar_bit_length: int, real_pub_key: Point) -> int:
+ def recover_bit(
+ self,
+ recovered_scalar: int,
+ target_bit: int,
+ scalar_bit_length: int,
+ real_pub_key: Point,
+ ) -> int:
if target_bit == scalar_bit_length - 1:
self.mult.init(self.params, self.params.generator)
if real_pub_key == self.mult.multiply(recovered_scalar):
@@ -71,16 +90,24 @@ class CPA():
mask = 1 << (scalar_bit_length - target_bit - 1)
guessed_scalar_0 = recovered_scalar
guessed_scalar_1 = recovered_scalar | mask
- correlation_trace_0 = self.compute_correlation_trace(guessed_scalar_0, target_bit)
- correlation_trace_1 = self.compute_correlation_trace(guessed_scalar_1, target_bit)
- self.correlations['guess_zero'].append(correlation_trace_0)
- self.correlations['guess_one'].append(correlation_trace_1)
- if np.nanmax(np.abs(correlation_trace_0)) > np.nanmax(np.abs(correlation_trace_1)):
+ correlation_trace_0 = self.compute_correlation_trace(
+ guessed_scalar_0, target_bit
+ )
+ correlation_trace_1 = self.compute_correlation_trace(
+ guessed_scalar_1, target_bit
+ )
+ self.correlations["guess_zero"].append(correlation_trace_0)
+ self.correlations["guess_one"].append(correlation_trace_1)
+ if np.nanmax(np.abs(correlation_trace_0)) > np.nanmax(
+ np.abs(correlation_trace_1)
+ ):
return guessed_scalar_0
return guessed_scalar_1
def perform(self, scalar_bit_length: int, real_pub_key: Point) -> int:
recovered_scalar = 1 << (scalar_bit_length - 1)
for target_bit in range(1, scalar_bit_length):
- recovered_scalar = self.recover_bit(recovered_scalar, target_bit, scalar_bit_length, real_pub_key)
+ recovered_scalar = self.recover_bit(
+ recovered_scalar, target_bit, scalar_bit_length, real_pub_key
+ )
return recovered_scalar
diff --git a/pyecsca/sca/attack/DPA.py b/pyecsca/sca/attack/DPA.py
index b6be49c..d77b28b 100644
--- a/pyecsca/sca/attack/DPA.py
+++ b/pyecsca/sca/attack/DPA.py
@@ -1,61 +1,77 @@
-from pyecsca.ec.mult import ScalarMultiplier
-from pyecsca.ec.point import Point
-from pyecsca.ec.context import DefaultContext, local
-from pyecsca.ec.params import DomainParameters
-from pyecsca.sca.trace import Trace, average, subtract, absolute
-from pyecsca.sca.trace.plot import plot_trace
from typing import Tuple, Dict
from public import public
+from ...ec.mult import ScalarMultiplier
+from ...ec.point import Point
+from ...ec.context import DefaultContext, local
+from ...ec.params import DomainParameters
+from ..trace import Trace
+from ..trace.combine import average, subtract
+from ..trace.process import absolute
+from ..trace.plot import plot_trace
-@public
-class DPA():
+@public
+class DPA:
traces: list[Trace]
points: list[Point]
mult: ScalarMultiplier
params: DomainParameters
doms: Dict[str, list[Trace]]
- def __init__(self, points: list[Point], traces: list[Trace], mult: ScalarMultiplier, params: DomainParameters):
- '''
+ def __init__(
+ self,
+ points: list[Point],
+ traces: list[Trace],
+ mult: ScalarMultiplier,
+ params: DomainParameters,
+ ):
+ """
:param points: Points on which scalar multiplication with secret scalar was performed
:param traces: Power traces corresponding to the scalar multiplication for each of the points
:param mult: Scalar multiplier used
:param params: Domain parameters used
- '''
+ """
self.points = points
self.traces = traces
self.mult = mult
self.params = params
- self.doms = {'guess_one' : [], 'guess_zero' : []}
+ self.doms = {"guess_one": [], "guess_zero": []}
- def compute_split_point(self, guessed_scalar: int, target_bit: int, point: Point) -> Point:
+ def compute_split_point(
+ self, guessed_scalar: int, target_bit: int, point: Point
+ ) -> Point:
with local(DefaultContext()) as ctx:
self.mult.init(self.params, point)
self.mult.multiply(guessed_scalar)
action_index = -1
- for bit in bin(guessed_scalar)[2:target_bit + 2]:
- if bit == '1':
+ for bit in bin(guessed_scalar)[2 : target_bit + 2]:
+ if bit == "1":
action_index += 2
- elif bit == '0':
+ elif bit == "0":
action_index += 1
result = ctx.actions.get_by_index([0, action_index])[0]
return result.output_points[0]
- def split_traces(self, guessed_scalar: int, target_bit: int) -> Tuple[list[Trace], list[Trace]]:
+ def split_traces(
+ self, guessed_scalar: int, target_bit: int
+ ) -> Tuple[list[Trace], list[Trace]]:
one_traces = []
zero_traces = []
for i in range(len(self.points)):
# TODO: works only if the computed split point has "X" coordinate
- split_value = self.compute_split_point(guessed_scalar, target_bit, self.points[i]).X
+ split_value = self.compute_split_point(
+ guessed_scalar, target_bit, self.points[i]
+ ).X
if int(split_value) & 1 == 1:
one_traces.append(self.traces[i])
elif int(split_value) & 1 == 0:
zero_traces.append(self.traces[i])
return one_traces, zero_traces
- def calculate_difference_of_means(self, one_traces: list[Trace], zero_traces: list[Trace]) -> Trace:
+ def calculate_difference_of_means(
+ self, one_traces: list[Trace], zero_traces: list[Trace]
+ ) -> Trace:
avg_ones = average(*one_traces)
avg_zeros = average(*zero_traces)
return subtract(avg_ones, avg_zeros) # type: ignore
@@ -63,7 +79,13 @@ class DPA():
def plot_difference_of_means(self, dom):
return plot_trace(dom).opts(width=950, height=600)
- def recover_bit(self, recovered_scalar: int, target_bit: int, scalar_bit_length: int, real_pub_key: Point) -> int:
+ def recover_bit(
+ self,
+ recovered_scalar: int,
+ target_bit: int,
+ scalar_bit_length: int,
+ real_pub_key: Point,
+ ) -> int:
if target_bit == scalar_bit_length - 1:
self.mult.init(self.params, self.params.generator)
if real_pub_key == self.mult.multiply(recovered_scalar):
@@ -76,8 +98,8 @@ class DPA():
ones_1, zeros_1 = self.split_traces(guessed_scalar_1, target_bit)
dom_0 = self.calculate_difference_of_means(ones_0, zeros_0)
dom_1 = self.calculate_difference_of_means(ones_1, zeros_1)
- self.doms['guess_zero'].append(dom_0)
- self.doms['guess_one'].append(dom_1)
+ self.doms["guess_zero"].append(dom_0)
+ self.doms["guess_one"].append(dom_1)
if max(absolute(dom_0)) > max(absolute(dom_1)):
return guessed_scalar_0
return guessed_scalar_1
@@ -85,5 +107,7 @@ class DPA():
def perform(self, scalar_bit_length: int, real_pub_key: Point) -> int:
recovered_scalar = 1 << (scalar_bit_length - 1)
for target_bit in range(1, scalar_bit_length):
- recovered_scalar = self.recover_bit(recovered_scalar, target_bit, scalar_bit_length, real_pub_key)
+ recovered_scalar = self.recover_bit(
+ recovered_scalar, target_bit, scalar_bit_length, real_pub_key
+ )
return recovered_scalar