implement replay analysis #7

Merged
dkelly merged 1 commits from ReplayAttack into main 2025-09-05 16:02:30 +00:00
6 changed files with 205 additions and 118 deletions
Showing only changes of commit 12938ef1d1 - Show all commits

File diff suppressed because one or more lines are too long

View File

@@ -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
) # )

View File

@@ -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

View File

@@ -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,

View File

@@ -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

View File

@@ -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]