diff options
| author | J08nY | 2023-02-20 20:31:29 +0100 |
|---|---|---|
| committer | J08nY | 2023-07-24 14:35:50 +0200 |
| commit | d8ad9cfc06d0f399fadd9bf78048c4bcac98e8d1 (patch) | |
| tree | 9ec202b6a5370d6fcba1ca5a0cff828a5a790bf4 | |
| parent | 1b115ca3cb9ef43f163bbb4a8a27b92f87d8f520 (diff) | |
| download | pyecsca-d8ad9cfc06d0f399fadd9bf78048c4bcac98e8d1.tar.gz pyecsca-d8ad9cfc06d0f399fadd9bf78048c4bcac98e8d1.tar.zst pyecsca-d8ad9cfc06d0f399fadd9bf78048c4bcac98e8d1.zip | |
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | pyecsca/sca/__init__.py | 1 | ||||
| -rw-r--r-- | pyecsca/sca/attack/__init__.py | 1 | ||||
| -rw-r--r-- | pyecsca/sca/attack/leakage_model.py | 67 | ||||
| -rw-r--r-- | test/sca/test_leakage_models.py | 48 |
5 files changed, 118 insertions, 1 deletions
@@ -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) |
