aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJ08nY2023-02-20 20:31:29 +0100
committerJ08nY2023-07-24 14:35:50 +0200
commitd8ad9cfc06d0f399fadd9bf78048c4bcac98e8d1 (patch)
tree9ec202b6a5370d6fcba1ca5a0cff828a5a790bf4
parent1b115ca3cb9ef43f163bbb4a8a27b92f87d8f520 (diff)
downloadpyecsca-d8ad9cfc06d0f399fadd9bf78048c4bcac98e8d1.tar.gz
pyecsca-d8ad9cfc06d0f399fadd9bf78048c4bcac98e8d1.tar.zst
pyecsca-d8ad9cfc06d0f399fadd9bf78048c4bcac98e8d1.zip
-rw-r--r--Makefile2
-rw-r--r--pyecsca/sca/__init__.py1
-rw-r--r--pyecsca/sca/attack/__init__.py1
-rw-r--r--pyecsca/sca/attack/leakage_model.py67
-rw-r--r--test/sca/test_leakage_models.py48
5 files changed, 118 insertions, 1 deletions
diff --git a/Makefile b/Makefile
index 547797d..1ae4371 100644
--- a/Makefile
+++ b/Makefile
@@ -4,7 +4,7 @@ ec.test_mult ec.test_naf ec.test_op ec.test_point ec.test_signature ec.test_tran
SCA_TESTS = sca.test_align sca.test_combine sca.test_edit sca.test_filter sca.test_match sca.test_process \
sca.test_sampling sca.test_target sca.test_test sca.test_trace sca.test_traceset sca.test_plot sca.test_rpa \
-sca.test_stacked_combine
+sca.test_stacked_combine sca.test_leakage_models
TESTS = ${EC_TESTS} ${SCA_TESTS}
diff --git a/pyecsca/sca/__init__.py b/pyecsca/sca/__init__.py
index 5b359b8..89e8527 100644
--- a/pyecsca/sca/__init__.py
+++ b/pyecsca/sca/__init__.py
@@ -6,3 +6,4 @@ from .target import *
from .trace import *
from .trace_set import *
from .stacked_traces import *
+from .attack import *
diff --git a/pyecsca/sca/attack/__init__.py b/pyecsca/sca/attack/__init__.py
new file mode 100644
index 0000000..23e27cb
--- /dev/null
+++ b/pyecsca/sca/attack/__init__.py
@@ -0,0 +1 @@
+from .leakage_model import *
diff --git a/pyecsca/sca/attack/leakage_model.py b/pyecsca/sca/attack/leakage_model.py
new file mode 100644
index 0000000..8fdb2ca
--- /dev/null
+++ b/pyecsca/sca/attack/leakage_model.py
@@ -0,0 +1,67 @@
+import abc
+from typing import Literal, ClassVar
+
+
+class LeakageModel(abc.ABC):
+ num_args: ClassVar[int]
+
+ @abc.abstractmethod
+ def __call__(self, *args, **kwargs) -> int:
+ raise NotImplementedError
+
+
+class Identity(LeakageModel):
+ num_args = 1
+
+ def __call__(self, *args, **kwargs) -> int:
+ return int(args[0])
+
+
+class Bit(LeakageModel):
+ num_args = 1
+
+ def __init__(self, which: int):
+ if which < 0:
+ raise ValueError("which must be >= 0.")
+ self.which = which
+ self.mask = 1 << which
+
+ def __call__(self, *args, **kwargs) -> Literal[0, 1]:
+ return (int(args[0]) & self.mask) >> self.which # type: ignore
+
+
+class Slice(LeakageModel):
+ num_args = 1
+
+ def __init__(self, begin: int, end: int):
+ if begin > end:
+ raise ValueError("begin must be <= than end.")
+ self.begin = begin
+ self.end = end
+ self.mask = 0
+ for i in range(begin, end):
+ self.mask |= 1 << i
+
+ def __call__(self, *args, **kwargs) -> int:
+ return (int(args[0]) & self.mask) >> self.begin
+
+
+class HammingWeight(LeakageModel):
+ num_args = 1
+
+ def __call__(self, *args, **kwargs) -> int:
+ return int(args[0]).bit_count()
+
+
+class HammingDistance(LeakageModel):
+ num_args = 2
+
+ def __call__(self, *args, **kwargs) -> int:
+ return (int(args[0]) ^ int(args[1])).bit_count()
+
+
+class BitLength(LeakageModel):
+ num_args = 1
+
+ def __call__(self, *args, **kwargs) -> int:
+ return int(args[0]).bit_length()
diff --git a/test/sca/test_leakage_models.py b/test/sca/test_leakage_models.py
new file mode 100644
index 0000000..f951083
--- /dev/null
+++ b/test/sca/test_leakage_models.py
@@ -0,0 +1,48 @@
+from unittest import TestCase
+
+from pyecsca.ec.mod import Mod
+from pyecsca.sca.attack.leakage_model import Identity, Bit, Slice, HammingWeight, HammingDistance, BitLength
+
+
+class LeakageModelTests(TestCase):
+
+ def test_identity(self):
+ val = Mod(3, 7)
+ lm = Identity()
+ self.assertEqual(lm(val), 3)
+
+ def test_bit(self):
+ val = Mod(3, 7)
+ lm = Bit(0)
+ self.assertEqual(lm(val), 1)
+ lm = Bit(4)
+ self.assertEqual(lm(val), 0)
+ with self.assertRaises(ValueError):
+ Bit(-3)
+
+ def test_slice(self):
+ val = Mod(0b11110000, 0xf00)
+ lm = Slice(0, 4)
+ self.assertEqual(lm(val), 0)
+ lm = Slice(1, 5)
+ self.assertEqual(lm(val), 0b1000)
+ lm = Slice(4, 8)
+ self.assertEqual(lm(val), 0b1111)
+ with self.assertRaises(ValueError):
+ Slice(7, 1)
+
+ def test_hamming_weight(self):
+ val = Mod(0b11110000, 0xf00)
+ lm = HammingWeight()
+ self.assertEqual(lm(val), 4)
+
+ def test_hamming_distance(self):
+ a = Mod(0b11110000, 0xf00)
+ b = Mod(0b00010000, 0xf00)
+ lm = HammingDistance()
+ self.assertEqual(lm(a, b), 3)
+
+ def test_bit_length(self):
+ a = Mod(0b11110000, 0xf00)
+ lm = BitLength()
+ self.assertEqual(lm(a), 8)