mfkey.c 34 KB

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