diff options
| author | J08nY | 2024-01-26 18:58:53 +0100 |
|---|---|---|
| committer | J08nY | 2024-01-26 18:58:53 +0100 |
| commit | 30cfd49b2b3a6a5180819a20d004b22e37e3fb77 (patch) | |
| tree | b343c13e52411e9d281c7b289415db8d390ff43c /pyecsca | |
| parent | d3230129f0720e756b113f80ed77f652ad32b44e (diff) | |
| download | pyecsca-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.py | 83 | ||||
| -rw-r--r-- | pyecsca/sca/attack/DPA.py | 70 |
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 |
