mfkey.c 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928
  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. buffered_file_stream_close(nonce_arr->stream);
  596. stream_free(nonce_arr->stream);
  597. //FURI_LOG_I(TAG, "Free heap after free(): %zub", memmgr_get_free_heap());
  598. program_state->mfkey_state = MFKeyAttack;
  599. // TODO: Work backwards on this array and free memory
  600. for(i = 0; i < nonce_arr->total_nonces; i++) {
  601. MfClassicNonce next_nonce = nonce_arr->remaining_nonce_array[i];
  602. if(key_already_found_for_nonce_in_solved(keyarray, keyarray_size, &next_nonce)) {
  603. nonce_arr->remaining_nonces--;
  604. (program_state->cracked)++;
  605. (program_state->num_completed)++;
  606. continue;
  607. }
  608. //FURI_LOG_I(TAG, "Beginning recovery for %8lx", next_nonce.uid);
  609. FuriString* cuid_dict_path;
  610. switch(next_nonce.attack) {
  611. case mfkey32:
  612. ks_enc = next_nonce.ar0_enc ^ next_nonce.p64;
  613. nt_xor_uid = 0;
  614. break;
  615. case static_nested:
  616. ks_enc = next_nonce.ks1_2_enc;
  617. nt_xor_uid = next_nonce.uid_xor_nt1;
  618. break;
  619. case static_encrypted:
  620. ks_enc = next_nonce.ks1_1_enc;
  621. nt_xor_uid = next_nonce.uid_xor_nt0;
  622. cuid_dict_path = furi_string_alloc_printf(
  623. "%s/mf_classic_dict_%08lx.nfc", EXT_PATH("nfc/assets"), next_nonce.uid);
  624. // May need RECORD_STORAGE?
  625. program_state->cuid_dict = keys_dict_alloc(
  626. furi_string_get_cstr(cuid_dict_path),
  627. KeysDictModeOpenAlways,
  628. sizeof(MfClassicKey));
  629. break;
  630. }
  631. if(!recover(&next_nonce, ks_enc, nt_xor_uid, program_state)) {
  632. if((next_nonce.attack == static_encrypted) && (program_state->cuid_dict)) {
  633. keys_dict_free(program_state->cuid_dict);
  634. }
  635. if(program_state->close_thread_please) {
  636. break;
  637. }
  638. // No key found in recover() or static encrypted
  639. (program_state->num_completed)++;
  640. continue;
  641. }
  642. (program_state->cracked)++;
  643. (program_state->num_completed)++;
  644. found_key = next_nonce.key;
  645. bool already_found = false;
  646. for(j = 0; j < keyarray_size; j++) {
  647. if(memcmp(keyarray[j].data, found_key.data, MF_CLASSIC_KEY_SIZE) == 0) {
  648. already_found = true;
  649. break;
  650. }
  651. }
  652. if(already_found == false) {
  653. // New key
  654. keyarray = realloc(keyarray, sizeof(MfClassicKey) * (keyarray_size + 1)); //-V701
  655. keyarray_size += 1;
  656. keyarray[keyarray_size - 1] = found_key;
  657. (program_state->unique_cracked)++;
  658. }
  659. }
  660. // TODO: Update display to show all keys were found
  661. // TODO: Prepend found key(s) to user dictionary file
  662. //FURI_LOG_I(TAG, "Unique keys found:");
  663. for(i = 0; i < keyarray_size; i++) {
  664. //FURI_LOG_I(TAG, "%012" PRIx64, keyarray[i]);
  665. keys_dict_add_key(user_dict, keyarray[i].data, sizeof(MfClassicKey));
  666. }
  667. if(keyarray_size > 0) {
  668. dolphin_deed(DolphinDeedNfcMfcAdd);
  669. }
  670. free(nonce_arr);
  671. keys_dict_free(user_dict);
  672. free(keyarray);
  673. if(program_state->mfkey_state == Error) {
  674. return;
  675. }
  676. //FURI_LOG_I(TAG, "mfkey function completed normally"); // DEBUG
  677. program_state->mfkey_state = Complete;
  678. // No need to alert the user if they asked it to stop
  679. if(!(program_state->close_thread_please)) {
  680. finished_beep();
  681. }
  682. return;
  683. }
  684. // Screen is 128x64 px
  685. static void render_callback(Canvas* const canvas, void* ctx) {
  686. furi_assert(ctx);
  687. ProgramState* program_state = ctx;
  688. furi_mutex_acquire(program_state->mutex, FuriWaitForever);
  689. char draw_str[44] = {};
  690. canvas_draw_frame(canvas, 0, 0, 128, 64);
  691. canvas_draw_frame(canvas, 0, 15, 128, 64);
  692. canvas_set_font(canvas, FontPrimary);
  693. canvas_draw_str_aligned(canvas, 5, 4, AlignLeft, AlignTop, "MFKey");
  694. snprintf(draw_str, sizeof(draw_str), "RAM: %zub", memmgr_get_free_heap());
  695. canvas_set_font(canvas, FontSecondary);
  696. canvas_draw_str_aligned(canvas, 48, 5, AlignLeft, AlignTop, draw_str);
  697. canvas_draw_icon(canvas, 114, 4, &I_mfkey);
  698. if(program_state->is_thread_running && program_state->mfkey_state == MFKeyAttack) {
  699. float eta_round = (float)1 - ((float)program_state->eta_round / (float)eta_round_time);
  700. float eta_total = (float)1 - ((float)program_state->eta_total / (float)eta_total_time);
  701. float progress = (float)program_state->num_completed / (float)program_state->total;
  702. if(eta_round < 0) {
  703. // Round ETA miscalculated
  704. eta_round = 1;
  705. program_state->eta_round = 0;
  706. }
  707. if(eta_total < 0) {
  708. // Total ETA miscalculated
  709. eta_total = 1;
  710. program_state->eta_total = 0;
  711. }
  712. canvas_set_font(canvas, FontSecondary);
  713. snprintf(
  714. draw_str,
  715. sizeof(draw_str),
  716. "Cracking: %d/%d - in prog.",
  717. program_state->num_completed,
  718. program_state->total);
  719. elements_progress_bar_with_text(canvas, 5, 18, 118, progress, draw_str);
  720. snprintf(
  721. draw_str,
  722. sizeof(draw_str),
  723. "Round: %d/%d - ETA %02d Sec",
  724. (program_state->search) + 1, // Zero indexed
  725. 256 / MSB_LIMIT,
  726. program_state->eta_round);
  727. elements_progress_bar_with_text(canvas, 5, 31, 118, eta_round, draw_str);
  728. snprintf(draw_str, sizeof(draw_str), "Total ETA %03d Sec", program_state->eta_total);
  729. elements_progress_bar_with_text(canvas, 5, 44, 118, eta_total, draw_str);
  730. } else if(program_state->is_thread_running && program_state->mfkey_state == DictionaryAttack) {
  731. canvas_set_font(canvas, FontSecondary);
  732. snprintf(
  733. draw_str, sizeof(draw_str), "Dict solves: %d (in progress)", program_state->cracked);
  734. canvas_draw_str_aligned(canvas, 10, 18, AlignLeft, AlignTop, draw_str);
  735. snprintf(draw_str, sizeof(draw_str), "Keys in dict: %d", program_state->dict_count);
  736. canvas_draw_str_aligned(canvas, 26, 28, AlignLeft, AlignTop, draw_str);
  737. } else if(program_state->mfkey_state == Complete) {
  738. // TODO: Scrollable list view to see cracked keys if user presses down
  739. elements_progress_bar(canvas, 5, 18, 118, 1);
  740. canvas_set_font(canvas, FontSecondary);
  741. snprintf(draw_str, sizeof(draw_str), "Complete");
  742. canvas_draw_str_aligned(canvas, 64, 31, AlignCenter, AlignTop, draw_str);
  743. snprintf(
  744. draw_str,
  745. sizeof(draw_str),
  746. "Keys added to user dict: %d",
  747. program_state->unique_cracked);
  748. canvas_draw_str_aligned(canvas, 64, 41, AlignCenter, AlignTop, draw_str);
  749. if(program_state->num_candidates > 0) {
  750. snprintf(
  751. draw_str,
  752. sizeof(draw_str),
  753. "SEN key candidates: %d",
  754. program_state->num_candidates);
  755. canvas_draw_str_aligned(canvas, 64, 51, AlignCenter, AlignTop, draw_str);
  756. }
  757. } else if(program_state->mfkey_state == Ready) {
  758. canvas_set_font(canvas, FontSecondary);
  759. canvas_draw_str_aligned(canvas, 50, 30, AlignLeft, AlignTop, "Ready");
  760. elements_button_center(canvas, "Start");
  761. elements_button_right(canvas, "Help");
  762. } else if(program_state->mfkey_state == Help) {
  763. canvas_set_font(canvas, FontSecondary);
  764. canvas_draw_str_aligned(canvas, 7, 20, AlignLeft, AlignTop, "Collect nonces by reading");
  765. canvas_draw_str_aligned(canvas, 7, 30, AlignLeft, AlignTop, "tag or reader in NFC app:");
  766. canvas_draw_str_aligned(canvas, 7, 40, AlignLeft, AlignTop, "https://docs.flipper.net/");
  767. canvas_draw_str_aligned(canvas, 7, 50, AlignLeft, AlignTop, "nfc/mfkey32");
  768. } else if(program_state->mfkey_state == Error) {
  769. canvas_draw_str_aligned(canvas, 50, 25, AlignLeft, AlignTop, "Error");
  770. canvas_set_font(canvas, FontSecondary);
  771. if(program_state->err == MissingNonces) {
  772. canvas_draw_str_aligned(canvas, 25, 36, AlignLeft, AlignTop, "No nonces found");
  773. } else if(program_state->err == ZeroNonces) {
  774. canvas_draw_str_aligned(canvas, 15, 36, AlignLeft, AlignTop, "Nonces already cracked");
  775. } else if(program_state->err == InsufficientRAM) {
  776. canvas_draw_str_aligned(canvas, 35, 36, AlignLeft, AlignTop, "No free RAM");
  777. } else {
  778. // Unhandled error
  779. }
  780. } else {
  781. // Unhandled program state
  782. }
  783. furi_mutex_release(program_state->mutex);
  784. }
  785. static void input_callback(InputEvent* input_event, void* event_queue) {
  786. furi_assert(event_queue);
  787. furi_message_queue_put((FuriMessageQueue*)event_queue, input_event, FuriWaitForever);
  788. }
  789. static void mfkey_state_init(ProgramState* program_state) {
  790. program_state->is_thread_running = false;
  791. program_state->mfkey_state = Ready;
  792. program_state->cracked = 0;
  793. program_state->unique_cracked = 0;
  794. program_state->num_completed = 0;
  795. program_state->num_candidates = 0;
  796. program_state->total = 0;
  797. program_state->dict_count = 0;
  798. }
  799. // Entrypoint for worker thread
  800. static int32_t mfkey_worker_thread(void* ctx) {
  801. ProgramState* program_state = ctx;
  802. program_state->is_thread_running = true;
  803. program_state->mfkey_state = Initializing;
  804. mfkey(program_state);
  805. program_state->is_thread_running = false;
  806. return 0;
  807. }
  808. int32_t mfkey_main() {
  809. FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
  810. ProgramState* program_state = malloc(sizeof(ProgramState));
  811. mfkey_state_init(program_state);
  812. program_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
  813. // Set system callbacks
  814. ViewPort* view_port = view_port_alloc();
  815. view_port_draw_callback_set(view_port, render_callback, program_state);
  816. view_port_input_callback_set(view_port, input_callback, event_queue);
  817. // Open GUI and register view_port
  818. Gui* gui = furi_record_open(RECORD_GUI);
  819. gui_add_view_port(gui, view_port, GuiLayerFullscreen);
  820. program_state->mfkeythread = furi_thread_alloc();
  821. furi_thread_set_name(program_state->mfkeythread, "MFKeyWorker");
  822. furi_thread_set_stack_size(program_state->mfkeythread, 2048);
  823. furi_thread_set_context(program_state->mfkeythread, program_state);
  824. furi_thread_set_callback(program_state->mfkeythread, mfkey_worker_thread);
  825. InputEvent input_event;
  826. for(bool main_loop = true; main_loop;) {
  827. FuriStatus event_status = furi_message_queue_get(event_queue, &input_event, 100);
  828. furi_mutex_acquire(program_state->mutex, FuriWaitForever);
  829. if(event_status == FuriStatusOk) {
  830. if(input_event.type == InputTypePress) {
  831. switch(input_event.key) {
  832. case InputKeyRight:
  833. if(!program_state->is_thread_running && program_state->mfkey_state == Ready) {
  834. program_state->mfkey_state = Help;
  835. }
  836. break;
  837. case InputKeyOk:
  838. if(!program_state->is_thread_running && program_state->mfkey_state == Ready) {
  839. furi_thread_start(program_state->mfkeythread);
  840. }
  841. break;
  842. case InputKeyBack:
  843. if(!program_state->is_thread_running && program_state->mfkey_state == Help) {
  844. program_state->mfkey_state = Ready;
  845. } else {
  846. program_state->close_thread_please = true;
  847. if(program_state->is_thread_running) {
  848. // Wait until thread is finished
  849. furi_thread_join(program_state->mfkeythread);
  850. }
  851. program_state->close_thread_please = false;
  852. main_loop = false;
  853. }
  854. break;
  855. default:
  856. break;
  857. }
  858. }
  859. }
  860. furi_mutex_release(program_state->mutex);
  861. view_port_update(view_port);
  862. }
  863. furi_thread_free(program_state->mfkeythread);
  864. view_port_enabled_set(view_port, false);
  865. gui_remove_view_port(gui, view_port);
  866. furi_record_close(RECORD_GUI);
  867. view_port_free(view_port);
  868. furi_message_queue_free(event_queue);
  869. furi_mutex_free(program_state->mutex);
  870. free(program_state);
  871. return 0;
  872. }
  873. #pragma GCC pop_options