implement replay analysis #7
File diff suppressed because one or more lines are too long
116
src/benchmark.py
116
src/benchmark.py
@@ -8,9 +8,8 @@ from src.utils import ShuffleTypes, observations, passcode_generator
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Benchmark:
|
class Benchmark:
|
||||||
mean: int
|
iterations_to_break: list[int]
|
||||||
variance: int
|
iterations_to_replay: list[int]
|
||||||
runs: list[int]
|
|
||||||
|
|
||||||
|
|
||||||
def shuffle_benchmark(
|
def shuffle_benchmark(
|
||||||
@@ -25,20 +24,21 @@ def shuffle_benchmark(
|
|||||||
file_path: str = '../output',
|
file_path: str = '../output',
|
||||||
overwrite: bool = False
|
overwrite: bool = False
|
||||||
) -> Benchmark:
|
) -> Benchmark:
|
||||||
file_name = f"{shuffle_type.name.lower()}-{number_of_keys}-{properties_per_key}-{passcode_len}-{max_tries_before_lockout}-{complexity}-{disparity}-{run_count}.txt"
|
# file_name_break = f"{shuffle_type.name.lower()}-{number_of_keys}-{properties_per_key}-{passcode_len}-{max_tries_before_lockout}-{complexity}-{disparity}-{run_count}.txt"
|
||||||
full_path = Path(file_path) / file_name
|
# full_path_iter_break = Path(file_path) / "iterations_to_break" /file_name_break
|
||||||
if not overwrite and full_path.exists():
|
# if not overwrite and full_path_iter_break.exists():
|
||||||
print(f"file exists {file_path}")
|
# print(f"file exists {file_path}")
|
||||||
with open(full_path, "r") as fp:
|
# with open(full_path_iter_break, "r") as fp:
|
||||||
runs = fp.readline()
|
# iterations_to_break = fp.readline()
|
||||||
runs = runs.split(',')
|
# iterations_to_break = iterations_to_break.split(',')
|
||||||
runs = [int(i) for i in runs]
|
# iterations_to_break = [int(i) for i in iterations_to_break]
|
||||||
return Benchmark(
|
# return Benchmark(
|
||||||
mean=mean(runs),
|
# mean=mean(iterations_to_break),
|
||||||
variance=variance(runs),
|
# variance=variance(iterations_to_break),
|
||||||
runs=runs
|
# iterations_to_break=iterations_to_break
|
||||||
)
|
# )
|
||||||
runs = []
|
iterations_to_break = []
|
||||||
|
iterations_to_replay = []
|
||||||
for _ in range(run_count):
|
for _ in range(run_count):
|
||||||
passcode = passcode_generator(number_of_keys, properties_per_key, passcode_len, complexity, disparity)
|
passcode = passcode_generator(number_of_keys, properties_per_key, passcode_len, complexity, disparity)
|
||||||
evilkode = Evilkode(
|
evilkode = Evilkode(
|
||||||
@@ -56,50 +56,50 @@ def shuffle_benchmark(
|
|||||||
max_tries_before_lockout=max_tries_before_lockout,
|
max_tries_before_lockout=max_tries_before_lockout,
|
||||||
)
|
)
|
||||||
evilout = evilkode.run()
|
evilout = evilkode.run()
|
||||||
runs.append(evilout.iterations)
|
iterations_to_break.append(evilout.iterations_to_break)
|
||||||
|
iterations_to_replay.append(evilout.iterations_to_replay)
|
||||||
|
|
||||||
full_path.parent.mkdir(parents=True, exist_ok=True)
|
# full_path_iter_break.parent.mkdir(parents=True, exist_ok=True)
|
||||||
with open(full_path, "w") as fp:
|
# with open(full_path_iter_break, "w") as fp:
|
||||||
fp.write(",".join([str(i) for i in runs])),
|
# fp.write(",".join([str(i) for i in iterations_to_break])),
|
||||||
|
|
||||||
return Benchmark(
|
return Benchmark(
|
||||||
mean=mean(runs),
|
iterations_to_break=iterations_to_break,
|
||||||
variance=variance(runs),
|
iterations_to_replay=iterations_to_replay
|
||||||
runs=runs
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def full_shuffle_benchmark(
|
# def full_shuffle_benchmark(
|
||||||
number_of_keys: int,
|
# number_of_keys: int,
|
||||||
properties_per_key: int,
|
# properties_per_key: int,
|
||||||
passcode_len: int,
|
# passcode_len: int,
|
||||||
max_tries_before_lockout: int,
|
# max_tries_before_lockout: int,
|
||||||
run_count: int,
|
# run_count: int,
|
||||||
complexity: int,
|
# complexity: int,
|
||||||
disparity: int,
|
# disparity: int,
|
||||||
) -> Benchmark:
|
# ) -> Benchmark:
|
||||||
runs = []
|
# runs = []
|
||||||
for _ in range(run_count):
|
# for _ in range(run_count):
|
||||||
passcode = passcode_generator(number_of_keys, properties_per_key, passcode_len, complexity, disparity)
|
# passcode = passcode_generator(number_of_keys, properties_per_key, passcode_len, complexity, disparity)
|
||||||
evilkode = Evilkode(
|
# evilkode = Evilkode(
|
||||||
observations=observations(
|
# observations=observations(
|
||||||
target_passcode=passcode,
|
# target_passcode=passcode,
|
||||||
number_of_keys=number_of_keys,
|
# number_of_keys=number_of_keys,
|
||||||
properties_per_key=properties_per_key,
|
# properties_per_key=properties_per_key,
|
||||||
min_complexity=complexity,
|
# min_complexity=complexity,
|
||||||
min_disparity=disparity,
|
# min_disparity=disparity,
|
||||||
shuffle_type=ShuffleTypes.FULL_SHUFFLE,
|
# shuffle_type=ShuffleTypes.FULL_SHUFFLE,
|
||||||
),
|
# ),
|
||||||
number_of_keys=number_of_keys,
|
# number_of_keys=number_of_keys,
|
||||||
properties_per_key=properties_per_key,
|
# properties_per_key=properties_per_key,
|
||||||
passcode_len=passcode_len,
|
# passcode_len=passcode_len,
|
||||||
max_tries_before_lockout=max_tries_before_lockout,
|
# max_tries_before_lockout=max_tries_before_lockout,
|
||||||
)
|
# )
|
||||||
evilout = evilkode.run()
|
# evilout = evilkode.run()
|
||||||
runs.append(evilout.iterations)
|
# runs.append(evilout.iterations_to_break)
|
||||||
|
#
|
||||||
return Benchmark(
|
# return Benchmark(
|
||||||
mean=mean(runs),
|
# mean=mean(runs),
|
||||||
variance=variance(runs),
|
# variance=variance(runs),
|
||||||
runs=runs
|
# iterations_to_break=runs
|
||||||
)
|
# )
|
||||||
|
|||||||
@@ -12,15 +12,20 @@ class Observation:
|
|||||||
def property_list(self) -> list[set[int]]:
|
def property_list(self) -> list[set[int]]:
|
||||||
return [set(self.keypad[idx]) for idx in self.key_selection]
|
return [set(self.keypad[idx]) for idx in self.key_selection]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def flat_keypad(self) -> list[int]:
|
||||||
|
return [num for row in self.keypad for num in row]
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class EvilOutput:
|
class EvilOutput:
|
||||||
possible_nkodes: list[list[int]]
|
# possible_nkodes: list[list[int]]
|
||||||
iterations: int
|
iterations_to_break: int
|
||||||
|
iterations_to_replay: int
|
||||||
|
|
||||||
@property
|
# @property
|
||||||
def number_of_possible_nkode(self):
|
# def number_of_possible_nkode(self):
|
||||||
return math.prod([len(el) for el in self.possible_nkodes])
|
# return math.prod([len(el) for el in self.possible_nkodes])
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@@ -39,9 +44,25 @@ class Evilkode:
|
|||||||
|
|
||||||
def run(self) -> EvilOutput:
|
def run(self) -> EvilOutput:
|
||||||
self.initialize()
|
self.initialize()
|
||||||
|
iterations_to_replay = None
|
||||||
for idx, obs in enumerate(self.observations):
|
for idx, obs in enumerate(self.observations):
|
||||||
|
if iterations_to_replay is None:
|
||||||
|
replay_possibilities = self.replay_attack(obs)
|
||||||
|
if replay_possibilities <= self.max_tries_before_lockout:
|
||||||
|
iterations_to_replay = idx + 1
|
||||||
if math.prod([len(el) for el in self.possible_nkode]) <= self.max_tries_before_lockout:
|
if math.prod([len(el) for el in self.possible_nkode]) <= self.max_tries_before_lockout:
|
||||||
return EvilOutput(possible_nkodes=[list(el) for el in self.possible_nkode], iterations=idx+1)
|
assert iterations_to_replay <= idx +1
|
||||||
|
return EvilOutput(
|
||||||
|
# possible_nkodes=[list(el) for el in self.possible_nkode],
|
||||||
|
iterations_to_break=idx + 1,
|
||||||
|
iterations_to_replay=iterations_to_replay
|
||||||
|
)
|
||||||
for jdx, props in enumerate(obs.property_list):
|
for jdx, props in enumerate(obs.property_list):
|
||||||
self.possible_nkode[jdx] = props.intersection(self.possible_nkode[jdx])
|
self.possible_nkode[jdx] = props.intersection(self.possible_nkode[jdx])
|
||||||
raise Exception("error in Evilkode, observations stopped yielding")
|
raise Exception("error in Evilkode, observations stopped yielding")
|
||||||
|
|
||||||
|
def replay_attack(self, obs: Observation) -> int:
|
||||||
|
possible_combos = 1
|
||||||
|
for el in self.possible_nkode:
|
||||||
|
possible_combos *= len({obs.flat_keypad.index(el2) // self.properties_per_key for el2 in el})
|
||||||
|
return possible_combos
|
||||||
@@ -59,7 +59,7 @@ class TowerShuffle:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def new(cls, total_pos:int):
|
def new(cls, total_pos:int):
|
||||||
assert total_pos >= 4
|
assert total_pos >= 3
|
||||||
rand_pos = np.random.permutation(total_pos)
|
rand_pos = np.random.permutation(total_pos)
|
||||||
return TowerShuffle(
|
return TowerShuffle(
|
||||||
total_positions=total_pos,
|
total_positions=total_pos,
|
||||||
|
|||||||
@@ -42,4 +42,4 @@ def test_evilkode(number_of_keys, properties_per_key, passcode_len, observations
|
|||||||
)
|
)
|
||||||
|
|
||||||
evilout = evilkode.run()
|
evilout = evilkode.run()
|
||||||
assert evilout.iterations > 1
|
assert evilout.iterations_to_break > 1
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from src.keypad import Keypad
|
from src.keypad import Keypad
|
||||||
|
from src.tower_shuffle import TowerShuffle
|
||||||
|
|
||||||
|
|
||||||
def test_keypad():
|
def test_keypad():
|
||||||
keypad = Keypad(
|
keypad = Keypad(
|
||||||
@@ -8,7 +10,7 @@ def test_keypad():
|
|||||||
[8, 9, 10, 11],
|
[8, 9, 10, 11],
|
||||||
[0, 5, 2, 3],
|
[0, 5, 2, 3],
|
||||||
[4, 1, 6,7]
|
[4, 1, 6,7]
|
||||||
]), k= 3, p=4, keypad_cache=[])
|
]), k= 3, p=4, keypad_cache=[], tower_shuffler=TowerShuffle.new(3*4))
|
||||||
|
|
||||||
assert keypad.key_entry([8, 5, 6, 11]) == [0,1,2,0]
|
assert keypad.key_entry([8, 5, 6, 11]) == [0,1,2,0]
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user