Skip to content

Commit e3cbd59

Browse files
committed
CLI: skip already used items in hf mf elog --decrypt
This (often largely) improves the speed of the decrypt process. On my laptop, with the same logs (37 records for one block and 37 records for another block), here are the performances, as measuerd using a simple command: ```bash time echo -e "hw connect\nhf mf elog --decrypt\nhw disconnect" | ./chameleon_cli_main.py ``` - Before parallelisation (RfidResearchGroup#187): 14m59,277s - With parallelisation (current main): 6m13,513s - With item skipping (this PR): 2m42,491s
1 parent 0124709 commit e3cbd59

2 files changed

Lines changed: 55 additions & 15 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file.
33
This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log...
44

55
## [unreleased][unreleased]
6+
- Skip already used items `hf mf elog --decrypt` (@p-l-)
67
- Parallelize mfkey32v2 processes called from CLI (@p-l-)
78
- Added support for mifare classic value block operations (@taichunmin)
89
- Added regression tests (@doegox)

software/script/chameleon_cli_unit.py

Lines changed: 54 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1044,10 +1044,57 @@ def _run_mfkey32v2(items):
10441044
).stdout
10451045
sea_obj = _KEY.search(output_str)
10461046
if sea_obj is not None:
1047-
return sea_obj[0]
1047+
return sea_obj[0], items
10481048
return None
10491049

10501050

1051+
class ItemGenerator:
1052+
def __init__(self, rs, i=0, j=1):
1053+
self.rs = rs
1054+
self.i = 0
1055+
self.j = 1
1056+
self.found = set()
1057+
self.keys = set()
1058+
1059+
def __iter__(self):
1060+
return self
1061+
1062+
def __next__(self):
1063+
try:
1064+
item_i = self.rs[self.i]
1065+
except IndexError:
1066+
raise StopIteration
1067+
if self.key_from_item(item_i) in self.found:
1068+
self.i += 1
1069+
self.j = self.i + 1
1070+
return next(self)
1071+
try:
1072+
item_j = self.rs[self.j]
1073+
except IndexError:
1074+
self.i += 1
1075+
self.j = self.i + 1
1076+
return next(self)
1077+
self.j += 1
1078+
if self.key_from_item(item_j) in self.found:
1079+
return next(self)
1080+
return item_i, item_j
1081+
1082+
@staticmethod
1083+
def key_from_item(item):
1084+
return "{uid}-{nt}-{nr}-{ar}".format(**item)
1085+
1086+
def key_found(self, key, items):
1087+
self.keys.add(key)
1088+
for item in items:
1089+
try:
1090+
if item == self.rs[self.i]:
1091+
self.i += 1
1092+
self.j = self.i + 1
1093+
except IndexError:
1094+
break
1095+
self.found.update(self.key_from_item(item) for item in items)
1096+
1097+
10511098
@hf_mf.command('elog')
10521099
class HFMFELog(DeviceRequiredUnit):
10531100
detection_log_size = 18
@@ -1069,24 +1116,16 @@ def decrypt_by_list(self, rs: list):
10691116
msg2 = f"/{(len(rs)*(len(rs)-1))//2} combinations. "
10701117
msg3 = " key(s) found"
10711118
n = 1
1072-
keys = set()
1119+
gen = ItemGenerator(rs)
10731120
with Pool(cpu_count()) as pool:
1074-
for key in pool.imap(
1075-
_run_mfkey32v2,
1076-
(
1077-
(item0, rs[j])
1078-
for i, item0 in enumerate(rs)
1079-
for j in range(i + 1, len(rs))
1080-
),
1081-
):
1121+
for result in pool.imap(_run_mfkey32v2, gen):
10821122
# TODO: if some keys already recovered, test them on item before running mfkey32 on item
1083-
# TODO: if some keys already recovered, remove corresponding items
1084-
if key is not None:
1085-
keys.add(key)
1086-
print(f"{msg1}{n}{msg2}{len(keys)}{msg3}\r", end="")
1123+
if result is not None:
1124+
gen.key_found(*result)
1125+
print(f"{msg1}{n}{msg2}{len(gen.keys)}{msg3}\r", end="")
10871126
n += 1
10881127
print()
1089-
return keys
1128+
return gen.keys
10901129

10911130
def on_exec(self, args: argparse.Namespace):
10921131
if not args.decrypt:

0 commit comments

Comments
 (0)