mfkey.c 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841
  1. #pragma GCC optimize("O3")
  2. #pragma GCC optimize("-funroll-all-loops")
  3. // TODO: Add keys to top of the user dictionary, not the bottom
  4. // TODO: More efficient dictionary bruteforce by scanning through hardcoded very common keys and previously found dictionary keys first?
  5. // (a cache for key_already_found_for_nonce_in_dict)
  6. // TODO: Selectively unroll loops to reduce binary size
  7. // TODO: Collect parity during Mfkey32 attacks to further optimize the attack
  8. // TODO: Why different sscanf between Mfkey32 and Nested?
  9. // TODO: "Read tag again with NFC app" message upon completion, "Complete. Keys added: <n>"
  10. // TODO: Separate Mfkey32 and Nested functions where possible to reduce branch statements
  11. // TODO: More accurate timing for Nested
  12. // TODO: Unload and load specific attacks from memory, plugin_manager_free
  13. // TODO: Remove includes that are not needed
  14. #include <furi.h>
  15. #include <furi_hal.h>
  16. #include <gui/gui.h>
  17. #include <gui/elements.h>
  18. #include "mfkey_icons.h"
  19. #include <inttypes.h>
  20. #include <toolbox/keys_dict.h>
  21. #include <toolbox/stream/buffered_file_stream.h>
  22. #include <dolphin/dolphin.h>
  23. #include <notification/notification_messages.h>
  24. #include <nfc/protocols/mf_classic/mf_classic.h>
  25. #include "mfkey.h"
  26. #include "common.h"
  27. #include "crypto1.h"
  28. #include "plugin_interface.h"
  29. #include <flipper_application/flipper_application.h>
  30. #include <loader/firmware_api/firmware_api.h>
  31. #include <storage/storage.h>
  32. // TODO: Remove defines that are not needed
  33. #define KEYS_DICT_SYSTEM_PATH EXT_PATH("nfc/assets/mf_classic_dict.nfc")
  34. #define KEYS_DICT_USER_PATH EXT_PATH("nfc/assets/mf_classic_dict_user.nfc")
  35. #define MF_CLASSIC_NONCE_PATH EXT_PATH("nfc/.mfkey32.log")
  36. #define MF_CLASSIC_NESTED_NONCE_PATH EXT_PATH("nfc/.nested")
  37. #define TAG "MFKey"
  38. #define MAX_NAME_LEN 32
  39. #define MAX_PATH_LEN 64
  40. #define MIN_RAM 121000
  41. #define LF_POLY_ODD (0x29CE5C)
  42. #define LF_POLY_EVEN (0x870804)
  43. #define CONST_M1_1 (LF_POLY_EVEN << 1 | 1)
  44. #define CONST_M2_1 (LF_POLY_ODD << 1)
  45. #define CONST_M1_2 (LF_POLY_ODD)
  46. #define CONST_M2_2 (LF_POLY_EVEN << 1 | 1)
  47. #define BIT(x, n) ((x) >> (n) & 1)
  48. #define BEBIT(x, n) BIT(x, (n) ^ 24)
  49. #define SWAPENDIAN(x) \
  50. ((x) = ((x) >> 8 & 0xff00ff) | ((x) & 0xff00ff) << 8, (x) = (x) >> 16 | (x) << 16)
  51. //#define SIZEOF(arr) sizeof(arr) / sizeof(*arr)
  52. static int eta_round_time = 56;
  53. static int eta_total_time = 900;
  54. // MSB_LIMIT: Chunk size (out of 256)
  55. static int MSB_LIMIT = 16;
  56. int check_state(struct Crypto1State* t, MfClassicNonce* n) {
  57. if(!(t->odd | t->even)) return 0;
  58. if(n->attack == mfkey32) {
  59. rollback_word_noret(t, 0, 0);
  60. rollback_word_noret(t, n->nr0_enc, 1);
  61. rollback_word_noret(t, n->uid_xor_nt0, 0);
  62. struct Crypto1State temp = {t->odd, t->even};
  63. crypt_word_noret(t, n->uid_xor_nt1, 0);
  64. crypt_word_noret(t, n->nr1_enc, 1);
  65. if(n->ar1_enc == (crypt_word(t) ^ n->p64b)) {
  66. crypto1_get_lfsr(&temp, &(n->key));
  67. return 1;
  68. }
  69. return 0;
  70. } else if(n->attack == static_nested) {
  71. struct Crypto1State temp = {t->odd, t->even};
  72. rollback_word_noret(t, n->uid_xor_nt1, 0);
  73. if(n->ks1_1_enc == crypt_word_ret(t, n->uid_xor_nt0, 0)) {
  74. rollback_word_noret(&temp, n->uid_xor_nt1, 0);
  75. crypto1_get_lfsr(&temp, &(n->key));
  76. return 1;
  77. }
  78. return 0;
  79. }
  80. return 0;
  81. }
  82. static inline int state_loop(
  83. unsigned int* states_buffer,
  84. int xks,
  85. int m1,
  86. int m2,
  87. unsigned int in,
  88. uint8_t and_val) {
  89. int states_tail = 0;
  90. int round = 0, s = 0, xks_bit = 0, round_in = 0;
  91. for(round = 1; round <= 12; round++) {
  92. xks_bit = BIT(xks, round);
  93. if(round > 4) {
  94. round_in = ((in >> (2 * (round - 4))) & and_val) << 24;
  95. }
  96. for(s = 0; s <= states_tail; s++) {
  97. states_buffer[s] <<= 1;
  98. if((filter(states_buffer[s]) ^ filter(states_buffer[s] | 1)) != 0) {
  99. states_buffer[s] |= filter(states_buffer[s]) ^ xks_bit;
  100. if(round > 4) {
  101. update_contribution(states_buffer, s, m1, m2);
  102. states_buffer[s] ^= round_in;
  103. }
  104. } else if(filter(states_buffer[s]) == xks_bit) {
  105. // TODO: Refactor
  106. if(round > 4) {
  107. states_buffer[++states_tail] = states_buffer[s + 1];
  108. states_buffer[s + 1] = states_buffer[s] | 1;
  109. update_contribution(states_buffer, s, m1, m2);
  110. states_buffer[s++] ^= round_in;
  111. update_contribution(states_buffer, s, m1, m2);
  112. states_buffer[s] ^= round_in;
  113. } else {
  114. states_buffer[++states_tail] = states_buffer[++s];
  115. states_buffer[s] = states_buffer[s - 1] | 1;
  116. }
  117. } else {
  118. states_buffer[s--] = states_buffer[states_tail--];
  119. }
  120. }
  121. }
  122. return states_tail;
  123. }
  124. int binsearch(unsigned int data[], int start, int stop) {
  125. int mid, val = data[stop] & 0xff000000;
  126. while(start != stop) {
  127. mid = (stop - start) >> 1;
  128. if((data[start + mid] ^ 0x80000000) > (val ^ 0x80000000))
  129. stop = start + mid;
  130. else
  131. start += mid + 1;
  132. }
  133. return start;
  134. }
  135. void quicksort(unsigned int array[], int low, int high) {
  136. //if (SIZEOF(array) == 0)
  137. // return;
  138. if(low >= high) return;
  139. int middle = low + (high - low) / 2;
  140. unsigned int pivot = array[middle];
  141. int i = low, j = high;
  142. while(i <= j) {
  143. while(array[i] < pivot) {
  144. i++;
  145. }
  146. while(array[j] > pivot) {
  147. j--;
  148. }
  149. if(i <= j) { // swap
  150. int temp = array[i];
  151. array[i] = array[j];
  152. array[j] = temp;
  153. i++;
  154. j--;
  155. }
  156. }
  157. if(low < j) {
  158. quicksort(array, low, j);
  159. }
  160. if(high > i) {
  161. quicksort(array, i, high);
  162. }
  163. }
  164. int extend_table(unsigned int data[], int tbl, int end, int bit, int m1, int m2, unsigned int in) {
  165. in <<= 24;
  166. for(data[tbl] <<= 1; tbl <= end; data[++tbl] <<= 1) {
  167. if((filter(data[tbl]) ^ filter(data[tbl] | 1)) != 0) {
  168. data[tbl] |= filter(data[tbl]) ^ bit;
  169. update_contribution(data, tbl, m1, m2);
  170. data[tbl] ^= in;
  171. } else if(filter(data[tbl]) == bit) {
  172. data[++end] = data[tbl + 1];
  173. data[tbl + 1] = data[tbl] | 1;
  174. update_contribution(data, tbl, m1, m2);
  175. data[tbl++] ^= in;
  176. update_contribution(data, tbl, m1, m2);
  177. data[tbl] ^= in;
  178. } else {
  179. data[tbl--] = data[end--];
  180. }
  181. }
  182. return end;
  183. }
  184. int old_recover(
  185. unsigned int odd[],
  186. int o_head,
  187. int o_tail,
  188. int oks,
  189. unsigned int even[],
  190. int e_head,
  191. int e_tail,
  192. int eks,
  193. int rem,
  194. int s,
  195. MfClassicNonce* n,
  196. unsigned int in,
  197. int first_run) {
  198. int o, e, i;
  199. if(rem == -1) {
  200. for(e = e_head; e <= e_tail; ++e) {
  201. even[e] = (even[e] << 1) ^ evenparity32(even[e] & LF_POLY_EVEN) ^ (!!(in & 4));
  202. for(o = o_head; o <= o_tail; ++o, ++s) {
  203. struct Crypto1State temp = {0, 0};
  204. temp.even = odd[o];
  205. temp.odd = even[e] ^ evenparity32(odd[o] & LF_POLY_ODD);
  206. if(check_state(&temp, n)) {
  207. return -1;
  208. }
  209. }
  210. }
  211. return s;
  212. }
  213. if(first_run == 0) {
  214. for(i = 0; (i < 4) && (rem-- != 0); i++) {
  215. oks >>= 1;
  216. eks >>= 1;
  217. in >>= 2;
  218. o_tail = extend_table(
  219. odd, o_head, o_tail, oks & 1, LF_POLY_EVEN << 1 | 1, LF_POLY_ODD << 1, 0);
  220. if(o_head > o_tail) return s;
  221. e_tail = extend_table(
  222. even, e_head, e_tail, eks & 1, LF_POLY_ODD, LF_POLY_EVEN << 1 | 1, in & 3);
  223. if(e_head > e_tail) return s;
  224. }
  225. }
  226. first_run = 0;
  227. quicksort(odd, o_head, o_tail);
  228. quicksort(even, e_head, e_tail);
  229. while(o_tail >= o_head && e_tail >= e_head) {
  230. if(((odd[o_tail] ^ even[e_tail]) >> 24) == 0) {
  231. o_tail = binsearch(odd, o_head, o = o_tail);
  232. e_tail = binsearch(even, e_head, e = e_tail);
  233. s = old_recover(
  234. odd, o_tail--, o, oks, even, e_tail--, e, eks, rem, s, n, in, first_run);
  235. if(s == -1) {
  236. break;
  237. }
  238. } else if((odd[o_tail] ^ 0x80000000) > (even[e_tail] ^ 0x80000000)) {
  239. o_tail = binsearch(odd, o_head, o_tail) - 1;
  240. } else {
  241. e_tail = binsearch(even, e_head, e_tail) - 1;
  242. }
  243. }
  244. return s;
  245. }
  246. static inline int sync_state(ProgramState* program_state) {
  247. int ts = furi_hal_rtc_get_timestamp();
  248. program_state->eta_round = program_state->eta_round - (ts - program_state->eta_timestamp);
  249. program_state->eta_total = program_state->eta_total - (ts - program_state->eta_timestamp);
  250. program_state->eta_timestamp = ts;
  251. if(program_state->close_thread_please) {
  252. return 1;
  253. }
  254. return 0;
  255. }
  256. int calculate_msb_tables(
  257. int oks,
  258. int eks,
  259. int msb_round,
  260. MfClassicNonce* n,
  261. unsigned int* states_buffer,
  262. struct Msb* odd_msbs,
  263. struct Msb* even_msbs,
  264. unsigned int* temp_states_odd,
  265. unsigned int* temp_states_even,
  266. unsigned int in,
  267. ProgramState* program_state) {
  268. //FURI_LOG_I(TAG, "MSB GO %i", msb_iter); // DEBUG
  269. unsigned int msb_head = (MSB_LIMIT * msb_round); // msb_iter ranges from 0 to (256/MSB_LIMIT)-1
  270. unsigned int msb_tail = (MSB_LIMIT * (msb_round + 1));
  271. int states_tail = 0, tail = 0;
  272. int i = 0, j = 0, semi_state = 0, found = 0;
  273. unsigned int msb = 0;
  274. in = ((in >> 16 & 0xff) | (in << 16) | (in & 0xff00)) << 1;
  275. // TODO: Why is this necessary?
  276. memset(odd_msbs, 0, MSB_LIMIT * sizeof(struct Msb));
  277. memset(even_msbs, 0, MSB_LIMIT * sizeof(struct Msb));
  278. for(semi_state = 1 << 20; semi_state >= 0; semi_state--) {
  279. if(semi_state % 32768 == 0) {
  280. if(sync_state(program_state) == 1) {
  281. return 0;
  282. }
  283. }
  284. if(filter(semi_state) == (oks & 1)) { //-V547
  285. states_buffer[0] = semi_state;
  286. states_tail = state_loop(states_buffer, oks, CONST_M1_1, CONST_M2_1, 0, 0);
  287. for(i = states_tail; i >= 0; i--) {
  288. msb = states_buffer[i] >> 24;
  289. if((msb >= msb_head) && (msb < msb_tail)) {
  290. found = 0;
  291. for(j = 0; j < odd_msbs[msb - msb_head].tail - 1; j++) {
  292. if(odd_msbs[msb - msb_head].states[j] == states_buffer[i]) {
  293. found = 1;
  294. break;
  295. }
  296. }
  297. if(!found) {
  298. tail = odd_msbs[msb - msb_head].tail++;
  299. odd_msbs[msb - msb_head].states[tail] = states_buffer[i];
  300. }
  301. }
  302. }
  303. }
  304. if(filter(semi_state) == (eks & 1)) { //-V547
  305. states_buffer[0] = semi_state;
  306. states_tail = state_loop(states_buffer, eks, CONST_M1_2, CONST_M2_2, in, 3);
  307. for(i = 0; i <= states_tail; i++) {
  308. msb = states_buffer[i] >> 24;
  309. if((msb >= msb_head) && (msb < msb_tail)) {
  310. found = 0;
  311. for(j = 0; j < even_msbs[msb - msb_head].tail; j++) {
  312. if(even_msbs[msb - msb_head].states[j] == states_buffer[i]) {
  313. found = 1;
  314. break;
  315. }
  316. }
  317. if(!found) {
  318. tail = even_msbs[msb - msb_head].tail++;
  319. even_msbs[msb - msb_head].states[tail] = states_buffer[i];
  320. }
  321. }
  322. }
  323. }
  324. }
  325. oks >>= 12;
  326. eks >>= 12;
  327. for(i = 0; i < MSB_LIMIT; i++) {
  328. if(sync_state(program_state) == 1) {
  329. return 0;
  330. }
  331. // TODO: Why is this necessary?
  332. memset(temp_states_even, 0, sizeof(unsigned int) * (1280));
  333. memset(temp_states_odd, 0, sizeof(unsigned int) * (1280));
  334. memcpy(temp_states_odd, odd_msbs[i].states, odd_msbs[i].tail * sizeof(unsigned int));
  335. memcpy(temp_states_even, even_msbs[i].states, even_msbs[i].tail * sizeof(unsigned int));
  336. int res = old_recover(
  337. temp_states_odd,
  338. 0,
  339. odd_msbs[i].tail,
  340. oks,
  341. temp_states_even,
  342. 0,
  343. even_msbs[i].tail,
  344. eks,
  345. 3,
  346. 0,
  347. n,
  348. in >> 16,
  349. 1);
  350. if(res == -1) {
  351. return 1;
  352. }
  353. //odd_msbs[i].tail = 0;
  354. //even_msbs[i].tail = 0;
  355. }
  356. return 0;
  357. }
  358. bool recover(MfClassicNonce* n, int ks2, unsigned int in, ProgramState* program_state) {
  359. bool found = false;
  360. unsigned int* states_buffer = malloc(sizeof(unsigned int) * (2 << 9));
  361. struct Msb* odd_msbs = (struct Msb*)malloc(MSB_LIMIT * sizeof(struct Msb));
  362. struct Msb* even_msbs = (struct Msb*)malloc(MSB_LIMIT * sizeof(struct Msb));
  363. unsigned int* temp_states_odd = malloc(sizeof(unsigned int) * (1280));
  364. unsigned int* temp_states_even = malloc(sizeof(unsigned int) * (1280));
  365. int oks = 0, eks = 0;
  366. int i = 0, msb = 0;
  367. for(i = 31; i >= 0; i -= 2) {
  368. oks = oks << 1 | BEBIT(ks2, i);
  369. }
  370. for(i = 30; i >= 0; i -= 2) {
  371. eks = eks << 1 | BEBIT(ks2, i);
  372. }
  373. int bench_start = furi_hal_rtc_get_timestamp();
  374. program_state->eta_total = eta_total_time;
  375. program_state->eta_timestamp = bench_start;
  376. for(msb = 0; msb <= ((256 / MSB_LIMIT) - 1); msb++) {
  377. program_state->search = msb;
  378. program_state->eta_round = eta_round_time;
  379. program_state->eta_total = eta_total_time - (eta_round_time * msb);
  380. if(calculate_msb_tables(
  381. oks,
  382. eks,
  383. msb,
  384. n,
  385. states_buffer,
  386. odd_msbs,
  387. even_msbs,
  388. temp_states_odd,
  389. temp_states_even,
  390. in,
  391. program_state)) {
  392. //int bench_stop = furi_hal_rtc_get_timestamp();
  393. //FURI_LOG_I(TAG, "Cracked in %i seconds", bench_stop - bench_start);
  394. found = true;
  395. break;
  396. }
  397. if(program_state->close_thread_please) {
  398. break;
  399. }
  400. }
  401. free(states_buffer);
  402. free(odd_msbs);
  403. free(even_msbs);
  404. free(temp_states_odd);
  405. free(temp_states_even);
  406. return found;
  407. }
  408. bool key_already_found_for_nonce_in_solved(
  409. MfClassicKey* keyarray,
  410. int keyarray_size,
  411. MfClassicNonce* nonce) {
  412. for(int k = 0; k < keyarray_size; k++) {
  413. uint64_t key_as_int = napi_nfc_util_bytes2num(keyarray[k].data, sizeof(MfClassicKey));
  414. struct Crypto1State temp = {0, 0};
  415. for(int i = 0; i < 24; i++) {
  416. (&temp)->odd |= (BIT(key_as_int, 2 * i + 1) << (i ^ 3));
  417. (&temp)->even |= (BIT(key_as_int, 2 * i) << (i ^ 3));
  418. }
  419. if(nonce->attack == mfkey32) {
  420. crypt_word_noret(&temp, nonce->uid_xor_nt1, 0);
  421. crypt_word_noret(&temp, nonce->nr1_enc, 1);
  422. if(nonce->ar1_enc == (crypt_word(&temp) ^ nonce->p64b)) {
  423. return true;
  424. }
  425. } else if(nonce->attack == static_nested) {
  426. uint32_t expected_ks1 = crypt_word_ret(&temp, nonce->uid_xor_nt0, 0);
  427. if(nonce->ks1_1_enc == expected_ks1) {
  428. return true;
  429. }
  430. }
  431. }
  432. return false;
  433. }
  434. #pragma GCC push_options
  435. #pragma GCC optimize("Os")
  436. static void finished_beep() {
  437. // Beep to indicate completion
  438. NotificationApp* notification = furi_record_open("notification");
  439. notification_message(notification, &sequence_audiovisual_alert);
  440. notification_message(notification, &sequence_display_backlight_on);
  441. furi_record_close("notification");
  442. }
  443. void mfkey(ProgramState* program_state) {
  444. MfClassicKey found_key; // recovered key
  445. size_t keyarray_size = 0;
  446. MfClassicKey* keyarray = malloc(sizeof(MfClassicKey) * 1);
  447. uint32_t i = 0, j = 0;
  448. //FURI_LOG_I(TAG, "Free heap before alloc(): %zub", memmgr_get_free_heap());
  449. Storage* storage = furi_record_open(RECORD_STORAGE);
  450. FlipperApplication* app = flipper_application_alloc(storage, firmware_api_interface);
  451. flipper_application_preload(app, APP_DATA_PATH("plugins/mfkey_init_plugin.fal"));
  452. flipper_application_map_to_memory(app);
  453. const FlipperAppPluginDescriptor* app_descriptor =
  454. flipper_application_plugin_get_descriptor(app);
  455. const MfkeyPlugin* init_plugin = app_descriptor->entry_point;
  456. // Check for nonces
  457. program_state->mfkey32_present = init_plugin->napi_mf_classic_mfkey32_nonces_check_presence();
  458. program_state->nested_present = init_plugin->napi_mf_classic_nested_nonces_check_presence();
  459. if(!(program_state->mfkey32_present) && !(program_state->nested_present)) {
  460. program_state->err = MissingNonces;
  461. program_state->mfkey_state = Error;
  462. free(keyarray);
  463. return;
  464. }
  465. // Read dictionaries (optional)
  466. KeysDict* system_dict = {0};
  467. bool system_dict_exists = keys_dict_check_presence(KEYS_DICT_SYSTEM_PATH);
  468. KeysDict* user_dict = {0};
  469. bool user_dict_exists = keys_dict_check_presence(KEYS_DICT_USER_PATH);
  470. uint32_t total_dict_keys = 0;
  471. if(system_dict_exists) {
  472. system_dict =
  473. keys_dict_alloc(KEYS_DICT_SYSTEM_PATH, KeysDictModeOpenExisting, sizeof(MfClassicKey));
  474. total_dict_keys += keys_dict_get_total_keys(system_dict);
  475. }
  476. user_dict = keys_dict_alloc(KEYS_DICT_USER_PATH, KeysDictModeOpenAlways, sizeof(MfClassicKey));
  477. if(user_dict_exists) {
  478. total_dict_keys += keys_dict_get_total_keys(user_dict);
  479. }
  480. user_dict_exists = true;
  481. program_state->dict_count = total_dict_keys;
  482. program_state->mfkey_state = DictionaryAttack;
  483. // Read nonces
  484. MfClassicNonceArray* nonce_arr;
  485. nonce_arr = init_plugin->napi_mf_classic_nonce_array_alloc(
  486. system_dict, system_dict_exists, user_dict, program_state);
  487. if(system_dict_exists) {
  488. keys_dict_free(system_dict);
  489. }
  490. if(nonce_arr->total_nonces == 0) {
  491. // Nothing to crack
  492. program_state->err = ZeroNonces;
  493. program_state->mfkey_state = Error;
  494. init_plugin->napi_mf_classic_nonce_array_free(nonce_arr);
  495. keys_dict_free(user_dict);
  496. free(keyarray);
  497. return;
  498. }
  499. flipper_application_free(app);
  500. furi_record_close(RECORD_STORAGE);
  501. // TODO: Track free state at the time this is called to ensure double free does not happen
  502. furi_assert(nonce_arr);
  503. furi_assert(nonce_arr->stream);
  504. buffered_file_stream_close(nonce_arr->stream);
  505. stream_free(nonce_arr->stream);
  506. //FURI_LOG_I(TAG, "Free heap after free(): %zub", memmgr_get_free_heap());
  507. if(memmgr_get_free_heap() < MIN_RAM) {
  508. // System has less than the guaranteed amount of RAM (140 KB) - adjust some parameters to run anyway at half speed
  509. eta_round_time *= 2;
  510. eta_total_time *= 2;
  511. MSB_LIMIT /= 2;
  512. }
  513. program_state->mfkey_state = MFKeyAttack;
  514. // TODO: Work backwards on this array and free memory
  515. for(i = 0; i < nonce_arr->total_nonces; i++) {
  516. MfClassicNonce next_nonce = nonce_arr->remaining_nonce_array[i];
  517. if(key_already_found_for_nonce_in_solved(keyarray, keyarray_size, &next_nonce)) {
  518. nonce_arr->remaining_nonces--;
  519. (program_state->cracked)++;
  520. (program_state->num_completed)++;
  521. continue;
  522. }
  523. //FURI_LOG_I(TAG, "Beginning recovery for %8lx", next_nonce.uid);
  524. if(next_nonce.attack == mfkey32) {
  525. if(!recover(&next_nonce, next_nonce.ar0_enc ^ next_nonce.p64, 0, program_state)) {
  526. if(program_state->close_thread_please) {
  527. break;
  528. }
  529. // No key found in recover()
  530. (program_state->num_completed)++;
  531. continue;
  532. }
  533. } else if(next_nonce.attack == static_nested) {
  534. if(!recover(
  535. &next_nonce,
  536. next_nonce.ks1_2_enc,
  537. next_nonce.nt1 ^ next_nonce.uid,
  538. program_state)) {
  539. if(program_state->close_thread_please) {
  540. break;
  541. }
  542. // No key found in recover()
  543. (program_state->num_completed)++;
  544. continue;
  545. }
  546. }
  547. (program_state->cracked)++;
  548. (program_state->num_completed)++;
  549. found_key = next_nonce.key;
  550. bool already_found = false;
  551. for(j = 0; j < keyarray_size; j++) {
  552. if(memcmp(keyarray[j].data, found_key.data, MF_CLASSIC_KEY_SIZE) == 0) {
  553. already_found = true;
  554. break;
  555. }
  556. }
  557. if(already_found == false) {
  558. // New key
  559. keyarray = realloc(keyarray, sizeof(MfClassicKey) * (keyarray_size + 1)); //-V701
  560. keyarray_size += 1;
  561. keyarray[keyarray_size - 1] = found_key;
  562. (program_state->unique_cracked)++;
  563. }
  564. }
  565. // TODO: Update display to show all keys were found
  566. // TODO: Prepend found key(s) to user dictionary file
  567. //FURI_LOG_I(TAG, "Unique keys found:");
  568. for(i = 0; i < keyarray_size; i++) {
  569. //FURI_LOG_I(TAG, "%012" PRIx64, keyarray[i]);
  570. keys_dict_add_key(user_dict, keyarray[i].data, sizeof(MfClassicKey));
  571. }
  572. if(keyarray_size > 0) {
  573. dolphin_deed(DolphinDeedNfcMfcAdd);
  574. }
  575. free(nonce_arr);
  576. keys_dict_free(user_dict);
  577. free(keyarray);
  578. //FURI_LOG_I(TAG, "mfkey function completed normally"); // DEBUG
  579. program_state->mfkey_state = Complete;
  580. // No need to alert the user if they asked it to stop
  581. if(!(program_state->close_thread_please)) {
  582. finished_beep();
  583. }
  584. return;
  585. }
  586. // Screen is 128x64 px
  587. static void render_callback(Canvas* const canvas, void* ctx) {
  588. furi_assert(ctx);
  589. ProgramState* program_state = ctx;
  590. furi_mutex_acquire(program_state->mutex, FuriWaitForever);
  591. char draw_str[44] = {};
  592. canvas_clear(canvas);
  593. canvas_draw_frame(canvas, 0, 0, 128, 64);
  594. canvas_draw_frame(canvas, 0, 15, 128, 64);
  595. canvas_set_font(canvas, FontPrimary);
  596. canvas_draw_str_aligned(canvas, 5, 4, AlignLeft, AlignTop, "MFKey");
  597. snprintf(draw_str, sizeof(draw_str), "RAM: %zub", memmgr_get_free_heap());
  598. canvas_set_font(canvas, FontSecondary);
  599. canvas_draw_str_aligned(canvas, 48, 5, AlignLeft, AlignTop, draw_str);
  600. canvas_draw_icon(canvas, 114, 4, &I_mfkey);
  601. if(program_state->is_thread_running && program_state->mfkey_state == MFKeyAttack) {
  602. float eta_round = (float)1 - ((float)program_state->eta_round / (float)eta_round_time);
  603. float eta_total = (float)1 - ((float)program_state->eta_total / (float)eta_total_time);
  604. float progress = (float)program_state->num_completed / (float)program_state->total;
  605. if(eta_round < 0) {
  606. // Round ETA miscalculated
  607. eta_round = 1;
  608. program_state->eta_round = 0;
  609. }
  610. if(eta_total < 0) {
  611. // Total ETA miscalculated
  612. eta_total = 1;
  613. program_state->eta_total = 0;
  614. }
  615. canvas_set_font(canvas, FontSecondary);
  616. snprintf(
  617. draw_str,
  618. sizeof(draw_str),
  619. "Cracking: %d/%d - in prog.",
  620. program_state->num_completed,
  621. program_state->total);
  622. elements_progress_bar_with_text(canvas, 5, 18, 118, progress, draw_str);
  623. snprintf(
  624. draw_str,
  625. sizeof(draw_str),
  626. "Round: %d/%d - ETA %02d Sec",
  627. (program_state->search) + 1, // Zero indexed
  628. 256 / MSB_LIMIT,
  629. program_state->eta_round);
  630. elements_progress_bar_with_text(canvas, 5, 31, 118, eta_round, draw_str);
  631. snprintf(draw_str, sizeof(draw_str), "Total ETA %03d Sec", program_state->eta_total);
  632. elements_progress_bar_with_text(canvas, 5, 44, 118, eta_total, draw_str);
  633. } else if(program_state->is_thread_running && program_state->mfkey_state == DictionaryAttack) {
  634. canvas_set_font(canvas, FontSecondary);
  635. snprintf(
  636. draw_str, sizeof(draw_str), "Dict solves: %d (in progress)", program_state->cracked);
  637. canvas_draw_str_aligned(canvas, 10, 18, AlignLeft, AlignTop, draw_str);
  638. snprintf(draw_str, sizeof(draw_str), "Keys in dict: %d", program_state->dict_count);
  639. canvas_draw_str_aligned(canvas, 26, 28, AlignLeft, AlignTop, draw_str);
  640. } else if(program_state->mfkey_state == Complete) {
  641. // TODO: Scrollable list view to see cracked keys if user presses down
  642. elements_progress_bar_with_text(canvas, 5, 18, 118, 1, draw_str);
  643. canvas_set_font(canvas, FontSecondary);
  644. snprintf(draw_str, sizeof(draw_str), "Complete");
  645. canvas_draw_str_aligned(canvas, 40, 31, AlignLeft, AlignTop, draw_str);
  646. snprintf(
  647. draw_str,
  648. sizeof(draw_str),
  649. "Keys added to user dict: %d",
  650. program_state->unique_cracked);
  651. canvas_draw_str_aligned(canvas, 10, 41, AlignLeft, AlignTop, draw_str);
  652. } else if(program_state->mfkey_state == Ready) {
  653. canvas_set_font(canvas, FontSecondary);
  654. canvas_draw_str_aligned(canvas, 50, 30, AlignLeft, AlignTop, "Ready");
  655. elements_button_center(canvas, "Start");
  656. elements_button_right(canvas, "Help");
  657. } else if(program_state->mfkey_state == Help) {
  658. canvas_set_font(canvas, FontSecondary);
  659. canvas_draw_str_aligned(canvas, 7, 20, AlignLeft, AlignTop, "Collect nonces using Detect");
  660. canvas_draw_str_aligned(canvas, 7, 30, AlignLeft, AlignTop, "Reader or FlipperNested.");
  661. canvas_draw_str_aligned(canvas, 7, 40, AlignLeft, AlignTop, "Devs: noproto, AG, ALiberty");
  662. canvas_draw_str_aligned(canvas, 7, 50, AlignLeft, AlignTop, "Thanks: bettse, Foxushka");
  663. } else if(program_state->mfkey_state == Error) {
  664. canvas_draw_str_aligned(canvas, 50, 25, AlignLeft, AlignTop, "Error");
  665. canvas_set_font(canvas, FontSecondary);
  666. if(program_state->err == MissingNonces) {
  667. canvas_draw_str_aligned(canvas, 25, 36, AlignLeft, AlignTop, "No nonces found");
  668. } else if(program_state->err == ZeroNonces) {
  669. canvas_draw_str_aligned(canvas, 15, 36, AlignLeft, AlignTop, "Nonces already cracked");
  670. } else {
  671. // Unhandled error
  672. }
  673. } else {
  674. // Unhandled program state
  675. }
  676. furi_mutex_release(program_state->mutex);
  677. }
  678. static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
  679. furi_assert(event_queue);
  680. PluginEvent event = {.type = EventTypeKey, .input = *input_event};
  681. furi_message_queue_put(event_queue, &event, FuriWaitForever);
  682. }
  683. static void mfkey_state_init(ProgramState* program_state) {
  684. program_state->is_thread_running = false;
  685. program_state->mfkey_state = Ready;
  686. program_state->cracked = 0;
  687. program_state->unique_cracked = 0;
  688. program_state->num_completed = 0;
  689. program_state->total = 0;
  690. program_state->dict_count = 0;
  691. }
  692. // Entrypoint for worker thread
  693. static int32_t mfkey_worker_thread(void* ctx) {
  694. ProgramState* program_state = ctx;
  695. program_state->is_thread_running = true;
  696. program_state->mfkey_state = Initializing;
  697. //FURI_LOG_I(TAG, "Hello from the mfkey worker thread"); // DEBUG
  698. mfkey(program_state);
  699. program_state->is_thread_running = false;
  700. return 0;
  701. }
  702. void start_mfkey_thread(ProgramState* program_state) {
  703. if(!program_state->is_thread_running) {
  704. furi_thread_start(program_state->mfkeythread);
  705. }
  706. }
  707. int32_t mfkey_main() {
  708. FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent));
  709. ProgramState* program_state = malloc(sizeof(ProgramState));
  710. mfkey_state_init(program_state);
  711. program_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
  712. if(!program_state->mutex) {
  713. //FURI_LOG_E(TAG, "cannot create mutex\r\n");
  714. free(program_state);
  715. return 255;
  716. }
  717. // Set system callbacks
  718. ViewPort* view_port = view_port_alloc();
  719. view_port_draw_callback_set(view_port, render_callback, program_state);
  720. view_port_input_callback_set(view_port, input_callback, event_queue);
  721. // Open GUI and register view_port
  722. Gui* gui = furi_record_open(RECORD_GUI);
  723. gui_add_view_port(gui, view_port, GuiLayerFullscreen);
  724. program_state->mfkeythread = furi_thread_alloc();
  725. furi_thread_set_name(program_state->mfkeythread, "MFKey Worker");
  726. furi_thread_set_stack_size(program_state->mfkeythread, 2048);
  727. furi_thread_set_context(program_state->mfkeythread, program_state);
  728. furi_thread_set_callback(program_state->mfkeythread, mfkey_worker_thread);
  729. PluginEvent event;
  730. for(bool main_loop = true; main_loop;) {
  731. FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
  732. furi_mutex_acquire(program_state->mutex, FuriWaitForever);
  733. if(event_status == FuriStatusOk) {
  734. // press events
  735. if(event.type == EventTypeKey) {
  736. if(event.input.type == InputTypePress) {
  737. switch(event.input.key) {
  738. case InputKeyUp:
  739. break;
  740. case InputKeyDown:
  741. break;
  742. case InputKeyRight:
  743. if(!program_state->is_thread_running &&
  744. program_state->mfkey_state == Ready) {
  745. program_state->mfkey_state = Help;
  746. view_port_update(view_port);
  747. }
  748. break;
  749. case InputKeyLeft:
  750. break;
  751. case InputKeyOk:
  752. if(!program_state->is_thread_running &&
  753. program_state->mfkey_state == Ready) {
  754. start_mfkey_thread(program_state);
  755. view_port_update(view_port);
  756. }
  757. break;
  758. case InputKeyBack:
  759. if(!program_state->is_thread_running &&
  760. program_state->mfkey_state == Help) {
  761. program_state->mfkey_state = Ready;
  762. view_port_update(view_port);
  763. } else {
  764. program_state->close_thread_please = true;
  765. if(program_state->is_thread_running && program_state->mfkeythread) {
  766. // Wait until thread is finished
  767. furi_thread_join(program_state->mfkeythread);
  768. }
  769. program_state->close_thread_please = false;
  770. main_loop = false;
  771. }
  772. break;
  773. default:
  774. break;
  775. }
  776. }
  777. }
  778. }
  779. view_port_update(view_port);
  780. furi_mutex_release(program_state->mutex);
  781. }
  782. furi_thread_free(program_state->mfkeythread);
  783. view_port_enabled_set(view_port, false);
  784. gui_remove_view_port(gui, view_port);
  785. furi_record_close("gui");
  786. view_port_free(view_port);
  787. furi_message_queue_free(event_queue);
  788. furi_mutex_free(program_state->mutex);
  789. free(program_state);
  790. return 0;
  791. }
  792. #pragma GCC pop_options