diff --git a/README.md b/README.md index 19eafee..bb5c9ce 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,15 @@ pip install pytest pytest ``` +## run linear and differential bias empirical tests + +in the venv + +```bash +tea3-linear-bias +tea3-differential-bias +``` + ## run sagemath script `deactivate` the venv first, then with sagemath installed: diff --git a/pyproject.toml b/pyproject.toml index 60a7a8d..c5f11d6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,3 +10,4 @@ testpaths = ["tests"] [project.scripts] tea3-linear-bias = "tea3.tea3linearbias:main" +tea3-differential-bias = "tea3.tea3differentialbias:main" diff --git a/src/tea3/tea3differentialbias.py b/src/tea3/tea3differentialbias.py new file mode 100644 index 0000000..26c525c --- /dev/null +++ b/src/tea3/tea3differentialbias.py @@ -0,0 +1,97 @@ +from __future__ import annotations + +from dataclasses import dataclass +from random import Random + +from .tea3 import Tea3 + + +def rand_key(rng: Random) -> list[int]: + return [rng.getrandbits(8) for _ in range(10)] + + +def xor_key_with_delta(key: list[int], delta: bytes | list[int], offset: int = 0) -> list[int]: + """ + XOR a delta into a key starting at offset. + """ + out = list(key) + for i, d in enumerate(delta): + j = offset + i + if j >= len(out): + break + out[j] ^= d & 0xFF + return out + + +@dataclass +class DiffBiasResult: + samples: int + matches: int + p_match: float + bias: float + estimated_needed_samples: float + + +def estimate_black_box_differential_bias( + *, + frame_number: int, + key_delta: bytes | list[int], + key_delta_offset: int = 0, + output_byte_index: int = 0, + target_diff: int = 0x00, + samples: int = 10000, + seed: int = 1, +) -> DiffBiasResult: + rng = Random(seed) + matches = 0 + target_diff &= 0xFF + + for _ in range(samples): + key1 = rand_key(rng) + key2 = xor_key_with_delta(key1, key_delta, key_delta_offset) + + tea1 = Tea3(frame_number, key1) + tea2 = Tea3(frame_number, key2) + + out1 = 0 + out2 = 0 + for _ in range(output_byte_index + 1): + out1 = tea1.next_byte() + out2 = tea2.next_byte() + + diff = (out1 ^ out2) & 0xFF + if diff == target_diff: + matches += 1 + + p_match = matches / samples + random_baseline = 1.0 / 256.0 + bias = abs(p_match - random_baseline) + + needed = float("inf") if bias == 0 else 1.0 / (bias * bias) + + return DiffBiasResult( + samples=samples, + matches=matches, + p_match=p_match, + bias=bias, + estimated_needed_samples=needed, + ) + + +def main() -> None: + frame = 0x12345678 + + key_delta = [0x01] + + res = estimate_black_box_differential_bias( + frame_number=frame, + key_delta=key_delta, + key_delta_offset=0, + output_byte_index=0, + target_diff=0x00, + samples=20000, + seed=123, + ) + print("black-box differential bias") + print("expected non-biased p_match: 1/256 = 0.00390625") # 256 possibilities for 1 Byte + print(res) diff --git a/src/tea3/tea3linearbias.py b/src/tea3/tea3linearbias.py index c599989..d372b18 100644 --- a/src/tea3/tea3linearbias.py +++ b/src/tea3/tea3linearbias.py @@ -63,4 +63,5 @@ def main(): seed=123, ) print("black-box output bias") + print("expected non-biased p_match: 1/2") # 2 possibilities for 1 bit print(res)