aboutsummaryrefslogtreecommitdiffhomepage
path: root/pyecsca/ec
diff options
context:
space:
mode:
authorJ08nY2025-03-10 18:13:21 +0100
committerJ08nY2025-03-10 18:13:21 +0100
commit65cf6216291214c1a62dfcd41cbe2ca47464bf46 (patch)
tree04a7eae9e1dd73e778ee672605e257492c437bfb /pyecsca/ec
parentd171c72d2983d0cdb77f0d83bc4c2f472701ddcc (diff)
downloadpyecsca-65cf6216291214c1a62dfcd41cbe2ca47464bf46.tar.gz
pyecsca-65cf6216291214c1a62dfcd41cbe2ca47464bf46.tar.zst
pyecsca-65cf6216291214c1a62dfcd41cbe2ca47464bf46.zip
Add countermeasures.
Diffstat (limited to 'pyecsca/ec')
-rw-r--r--pyecsca/ec/countermeasures.py118
1 files changed, 118 insertions, 0 deletions
diff --git a/pyecsca/ec/countermeasures.py b/pyecsca/ec/countermeasures.py
new file mode 100644
index 0000000..ee07c2b
--- /dev/null
+++ b/pyecsca/ec/countermeasures.py
@@ -0,0 +1,118 @@
+"""Provides several countermeasures against side-channel attacks."""
+
+from abc import ABC, abstractmethod
+from typing import Optional
+
+from public import public
+
+from pyecsca.ec.formula import AdditionFormula
+from pyecsca.ec.mod import Mod, mod
+from pyecsca.ec.mult import ScalarMultiplier
+from pyecsca.ec.params import DomainParameters
+from pyecsca.ec.point import Point
+
+
+@public
+class ScalarMultiplierCountermeasure(ABC):
+ """
+ A scalar multiplier-based countermeasure.
+
+ This class behaves like a scalar multiplier, in fact it wraps one
+ and provides some scalar-splitting countermeasure.
+ """
+ mult: ScalarMultiplier
+ params: Optional[DomainParameters]
+ point: Optional[Point]
+
+ def __init__(self, mult: ScalarMultiplier):
+ self.mult = mult
+
+ def init(self, params: DomainParameters, point: Point):
+ self.params = params
+ self.point = point
+
+ @abstractmethod
+ def multiply(self, scalar: int) -> Point:
+ raise NotImplementedError
+
+
+@public
+class GroupScalarRandomization(ScalarMultiplierCountermeasure):
+ rand_bits: int
+
+ def __init__(self, mult: ScalarMultiplier, rand_bits: int = 32):
+ super().__init__(mult)
+ self.rand_bits = rand_bits
+
+ def multiply(self, scalar: int) -> Point:
+ self.mult.init(self.params, self.point)
+ order = self.params.order
+ mask = int(Mod.random(1 << self.rand_bits))
+ masked_scalar = scalar + mask * order
+ return self.mult.multiply(masked_scalar)
+
+@public
+class AdditiveSplitting(ScalarMultiplierCountermeasure):
+ add: Optional[AdditionFormula]
+
+ def __init__(self, mult: ScalarMultiplier, add: Optional[AdditionFormula] = None):
+ super().__init__(mult)
+ self.add = add
+
+ def multiply(self, scalar: int) -> Point:
+ self.mult.init(self.params, self.point)
+
+ order = self.params.order
+ r = Mod.random(order)
+ s = scalar - r
+ R = self.mult.multiply(int(r))
+ S = self.mult.multiply(int(s))
+ if self.add is None:
+ return self.mult._add(R, S) # noqa: This is OK.
+ else:
+ return self.add(self.params.curve.prime, R, S, **self.params.curve.parameters)[0]
+
+
+@public
+class MultiplicativeSplitting(ScalarMultiplierCountermeasure):
+ rand_bits: int
+
+ def __init__(self, mult: ScalarMultiplier, rand_bits: int = 32):
+ super().__init__(mult)
+ self.rand_bits = rand_bits
+
+ def multiply(self, scalar: int) -> Point:
+ self.mult.init(self.params, self.point)
+ r = Mod.random(1 << self.rand_bits)
+ R = self.mult.multiply(int(r))
+
+ self.mult.init(self.params, R)
+ kr_inv = scalar * mod(int(r), self.params.order).inverse()
+ return self.mult.multiply(int(kr_inv))
+
+@public
+class EuclideanSplitting(ScalarMultiplierCountermeasure):
+ add: Optional[AdditionFormula]
+
+ def __init__(self, mult: ScalarMultiplier, add: Optional[AdditionFormula] = None):
+ super().__init__(mult)
+ self.add = add
+
+ def multiply(self, scalar: int) -> Point:
+ order = self.params.order
+ half_bits = order.bit_length() // 2
+ r = Mod.random(1 << half_bits)
+ self.mult.init(self.params, self.point)
+ R = self.mult.multiply(int(r))
+
+ k1 = scalar % int(r)
+ k2 = scalar // int(r)
+ self.mult.init(self.params, R)
+ S = self.mult.multiply(k2)
+
+ self.mult.init(self.params, self.point)
+ T = self.mult.multiply(k1)
+ if self.add is None:
+ return self.mult._add(S, T) # noqa: This is OK.
+ else:
+ return self.add(self.params.curve.prime, S, T, **self.params.curve.parameters)[0] \ No newline at end of file