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. #include <furi.h>
  16. #include <furi_hal.h>
  17. #include <gui/gui.h>
  18. #include <gui/elements.h>
  19. #include "mfkey_icons.h"
  20. #include <inttypes.h>
  21. #include <toolbox/keys_dict.h>
  22. #include <bit_lib/bit_lib.h>
  23. #include <toolbox/stream/buffered_file_stream.h>
  24. #include <dolphin/dolphin.h>
  25. #include <notification/notification_messages.h>
  26. #include <nfc/protocols/mf_classic/mf_classic.h>
  27. #include "mfkey.h"
  28. #include "crypto1.h"
  29. #include "plugin_interface.h"
  30. #include <flipper_application/flipper_application.h>
  31. #include <loader/firmware_api/firmware_api.h>
  32. #include <storage/storage.h>
  33. // TODO: Remove defines that are not needed
  34. #define KEYS_DICT_SYSTEM_PATH EXT_PATH("nfc/assets/mf_classic_dict.nfc")
  35. #define KEYS_DICT_USER_PATH EXT_PATH("nfc/assets/mf_classic_dict_user.nfc")
  36. #define TAG "MFKey"
  37. #define MAX_NAME_LEN 32
  38. #define MAX_PATH_LEN 64
  39. #define LF_POLY_ODD (0x29CE5C)
  40. #define LF_POLY_EVEN (0x870804)
  41. #define CONST_M1_1 (LF_POLY_EVEN << 1 | 1)
  42. #define CONST_M2_1 (LF_POLY_ODD << 1)
  43. #define CONST_M1_2 (LF_POLY_ODD)
  44. #define CONST_M2_2 (LF_POLY_EVEN << 1 | 1)
  45. #define BIT(x, n) ((x) >> (n) & 1)
  46. #define BEBIT(x, n) BIT(x, (n) ^ 24)
  47. #define SWAPENDIAN(x) \
  48. ((x) = ((x) >> 8 & 0xff00ff) | ((x) & 0xff00ff) << 8, (x) = (x) >> 16 | (x) << 16)
  49. //#define SIZEOF(arr) sizeof(arr) / sizeof(*arr)
  50. static int eta_round_time = 44;
  51. static int eta_total_time = 705;
  52. // MSB_LIMIT: Chunk size (out of 256)
  53. static int MSB_LIMIT = 16;
  54. static inline int
  55. check_state(struct Crypto1State* t, MfClassicNonce* n, ProgramState* program_state) {
  56. if(!(t->odd | t->even)) return 0;
  57. if(n->attack == mfkey32) {
  58. uint32_t rb = (napi_lfsr_rollback_word(t, 0, 0) ^ n->p64);
  59. if(rb != n->ar0_enc) {
  60. return 0;
  61. }
  62. rollback_word_noret(t, n->nr0_enc, 1);
  63. rollback_word_noret(t, n->uid_xor_nt0, 0);
  64. struct Crypto1State temp = {t->odd, t->even};
  65. crypt_word_noret(t, n->uid_xor_nt1, 0);
  66. crypt_word_noret(t, n->nr1_enc, 1);
  67. if(n->ar1_enc == (crypt_word(t) ^ n->p64b)) {
  68. crypto1_get_lfsr(&temp, &(n->key));
  69. return 1;
  70. }
  71. } else if(n->attack == static_nested) {
  72. struct Crypto1State temp = {t->odd, t->even};
  73. rollback_word_noret(t, n->uid_xor_nt1, 0);
  74. if(n->ks1_1_enc == crypt_word_ret(t, n->uid_xor_nt0, 0)) {
  75. rollback_word_noret(&temp, n->uid_xor_nt1, 0);
  76. crypto1_get_lfsr(&temp, &(n->key));
  77. return 1;
  78. }
  79. } else if(n->attack == static_encrypted) {
  80. // TODO: Parity bits from rollback_word?
  81. if(n->ks1_1_enc == napi_lfsr_rollback_word(t, n->uid_xor_nt0, 0)) {
  82. // Reduce with parity
  83. uint8_t local_parity_keystream_bits;
  84. struct Crypto1State temp = {t->odd, t->even};
  85. if((crypt_word_par(&temp, n->uid_xor_nt0, 0, n->nt0, &local_parity_keystream_bits) ==
  86. n->ks1_1_enc) &&
  87. (local_parity_keystream_bits == n->par_1)) {
  88. // Found key candidate
  89. crypto1_get_lfsr(t, &(n->key));
  90. program_state->num_candidates++;
  91. keys_dict_add_key(program_state->cuid_dict, n->key.data, sizeof(MfClassicKey));
  92. }
  93. }
  94. }
  95. return 0;
  96. }
  97. static inline int state_loop(
  98. unsigned int* states_buffer,
  99. int xks,
  100. int m1,
  101. int m2,
  102. unsigned int in,
  103. uint8_t and_val) {
  104. int states_tail = 0;
  105. int round = 0, s = 0, xks_bit = 0, round_in = 0;
  106. for(round = 1; round <= 12; round++) {
  107. xks_bit = BIT(xks, round);
  108. if(round > 4) {
  109. round_in = ((in >> (2 * (round - 4))) & and_val) << 24;
  110. }
  111. for(s = 0; s <= states_tail; s++) {
  112. states_buffer[s] <<= 1;
  113. if((filter(states_buffer[s]) ^ filter(states_buffer[s] | 1)) != 0) {
  114. states_buffer[s] |= filter(states_buffer[s]) ^ xks_bit;
  115. if(round > 4) {
  116. update_contribution(states_buffer, s, m1, m2);
  117. states_buffer[s] ^= round_in;
  118. }
  119. } else if(filter(states_buffer[s]) == xks_bit) {
  120. // TODO: Refactor
  121. if(round > 4) {
  122. states_buffer[++states_tail] = states_buffer[s + 1];
  123. states_buffer[s + 1] = states_buffer[s] | 1;
  124. update_contribution(states_buffer, s, m1, m2);
  125. states_buffer[s++] ^= round_in;
  126. update_contribution(states_buffer, s, m1, m2);
  127. states_buffer[s] ^= round_in;
  128. } else {
  129. states_buffer[++states_tail] = states_buffer[++s];
  130. states_buffer[s] = states_buffer[s - 1] | 1;
  131. }
  132. } else {
  133. states_buffer[s--] = states_buffer[states_tail--];
  134. }
  135. }
  136. }
  137. return states_tail;
  138. }
  139. int binsearch(unsigned int data[], int start, int stop) {
  140. int mid, val = data[stop] & 0xff000000;
  141. while(start != stop) {
  142. mid = (stop - start) >> 1;
  143. if((data[start + mid] ^ 0x80000000) > (val ^ 0x80000000))
  144. stop = start + mid;
  145. else
  146. start += mid + 1;
  147. }
  148. return start;
  149. }
  150. void quicksort(unsigned int array[], int low, int high) {
  151. //if (SIZEOF(array) == 0)
  152. // return;
  153. if(low >= high) return;
  154. int middle = low + (high - low) / 2;
  155. unsigned int pivot = array[middle];
  156. int i = low, j = high;
  157. while(i <= j) {
  158. while(array[i] < pivot) {
  159. i++;
  160. }
  161. while(array[j] > pivot) {
  162. j--;
  163. }
  164. if(i <= j) { // swap
  165. int temp = array[i];
  166. array[i] = array[j];
  167. array[j] = temp;
  168. i++;
  169. j--;
  170. }
  171. }
  172. if(low < j) {
  173. quicksort(array, low, j);
  174. }
  175. if(high > i) {
  176. quicksort(array, i, high);
  177. }
  178. }
  179. int extend_table(unsigned int data[], int tbl, int end, int bit, int m1, int m2, unsigned int in) {
  180. in <<= 24;
  181. for(data[tbl] <<= 1; tbl <= end; data[++tbl] <<= 1) {
  182. if((filter(data[tbl]) ^ filter(data[tbl] | 1)) != 0) {
  183. data[tbl] |= filter(data[tbl]) ^ bit;
  184. update_contribution(data, tbl, m1, m2);
  185. data[tbl] ^= in;
  186. } else if(filter(data[tbl]) == bit) {
  187. data[++end] = data[tbl + 1];
  188. data[tbl + 1] = data[tbl] | 1;
  189. update_contribution(data, tbl, m1, m2);
  190. data[tbl++] ^= in;
  191. update_contribution(data, tbl, m1, m2);
  192. data[tbl] ^= in;
  193. } else {
  194. data[tbl--] = data[end--];
  195. }
  196. }
  197. return end;
  198. }
  199. int old_recover(
  200. unsigned int odd[],
  201. int o_head,
  202. int o_tail,
  203. int oks,
  204. unsigned int even[],
  205. int e_head,
  206. int e_tail,
  207. int eks,
  208. int rem,
  209. int s,
  210. MfClassicNonce* n,
  211. unsigned int in,
  212. int first_run,
  213. ProgramState* program_state) {
  214. int o, e, i;
  215. if(rem == -1) {
  216. for(e = e_head; e <= e_tail; ++e) {
  217. even[e] = (even[e] << 1) ^ evenparity32(even[e] & LF_POLY_EVEN) ^ (!!(in & 4));
  218. for(o = o_head; o <= o_tail; ++o, ++s) {
  219. struct Crypto1State temp = {0, 0};
  220. temp.even = odd[o];
  221. temp.odd = even[e] ^ evenparity32(odd[o] & LF_POLY_ODD);
  222. if(check_state(&temp, n, program_state)) {
  223. return -1;
  224. }
  225. }
  226. }
  227. return s;
  228. }
  229. if(first_run == 0) {
  230. for(i = 0; (i < 4) && (rem-- != 0); i++) {
  231. oks >>= 1;
  232. eks >>= 1;
  233. in >>= 2;
  234. o_tail = extend_table(
  235. odd, o_head, o_tail, oks & 1, LF_POLY_EVEN << 1 | 1, LF_POLY_ODD << 1, 0);
  236. if(o_head > o_tail) return s;
  237. e_tail = extend_table(
  238. even, e_head, e_tail, eks & 1, LF_POLY_ODD, LF_POLY_EVEN << 1 | 1, in & 3);
  239. if(e_head > e_tail) return s;
  240. }
  241. }
  242. first_run = 0;
  243. quicksort(odd, o_head, o_tail);
  244. quicksort(even, e_head, e_tail);
  245. while(o_tail >= o_head && e_tail >= e_head) {
  246. if(((odd[o_tail] ^ even[e_tail]) >> 24) == 0) {
  247. o_tail = binsearch(odd, o_head, o = o_tail);
  248. e_tail = binsearch(even, e_head, e = e_tail);
  249. s = old_recover(
  250. odd,
  251. o_tail--,
  252. o,
  253. oks,
  254. even,
  255. e_tail--,
  256. e,
  257. eks,
  258. rem,
  259. s,
  260. n,
  261. in,
  262. first_run,
  263. program_state);
  264. if(s == -1) {
  265. break;
  266. }
  267. } else if((odd[o_tail] ^ 0x80000000) > (even[e_tail] ^ 0x80000000)) {
  268. o_tail = binsearch(odd, o_head, o_tail) - 1;
  269. } else {
  270. e_tail = binsearch(even, e_head, e_tail) - 1;
  271. }
  272. }
  273. return s;
  274. }
  275. static inline int sync_state(ProgramState* program_state) {
  276. int ts = furi_hal_rtc_get_timestamp();
  277. int elapsed_time = ts - program_state->eta_timestamp;
  278. if(elapsed_time < program_state->eta_round) {
  279. program_state->eta_round -= elapsed_time;
  280. } else {
  281. program_state->eta_round = 0;
  282. }
  283. if(elapsed_time < program_state->eta_total) {
  284. program_state->eta_total -= elapsed_time;
  285. } else {
  286. program_state->eta_total = 0;
  287. }
  288. program_state->eta_timestamp = ts;
  289. if(program_state->close_thread_please) {
  290. return 1;
  291. }
  292. return 0;
  293. }
  294. int calculate_msb_tables(
  295. int oks,
  296. int eks,
  297. int msb_round,
  298. MfClassicNonce* n,
  299. unsigned int* states_buffer,
  300. struct Msb* odd_msbs,
  301. struct Msb* even_msbs,
  302. unsigned int* temp_states_odd,
  303. unsigned int* temp_states_even,
  304. unsigned int in,
  305. ProgramState* program_state) {
  306. //FURI_LOG_I(TAG, "MSB GO %i", msb_iter); // DEBUG
  307. unsigned int msb_head = (MSB_LIMIT * msb_round); // msb_iter ranges from 0 to (256/MSB_LIMIT)-1
  308. unsigned int msb_tail = (MSB_LIMIT * (msb_round + 1));
  309. int states_tail = 0, tail = 0;
  310. int i = 0, j = 0, semi_state = 0, found = 0;
  311. unsigned int msb = 0;
  312. in = ((in >> 16 & 0xff) | (in << 16) | (in & 0xff00)) << 1;
  313. // TODO: Why is this necessary?
  314. memset(odd_msbs, 0, MSB_LIMIT * sizeof(struct Msb));
  315. memset(even_msbs, 0, MSB_LIMIT * sizeof(struct Msb));
  316. for(semi_state = 1 << 20; semi_state >= 0; semi_state--) {
  317. if(semi_state % 32768 == 0) {
  318. if(sync_state(program_state) == 1) {
  319. return 0;
  320. }
  321. }
  322. if(filter(semi_state) == (oks & 1)) { //-V547
  323. states_buffer[0] = semi_state;
  324. states_tail = state_loop(states_buffer, oks, CONST_M1_1, CONST_M2_1, 0, 0);
  325. for(i = states_tail; i >= 0; i--) {
  326. msb = states_buffer[i] >> 24;
  327. if((msb >= msb_head) && (msb < msb_tail)) {
  328. found = 0;
  329. for(j = 0; j < odd_msbs[msb - msb_head].tail - 1; j++) {
  330. if(odd_msbs[msb - msb_head].states[j] == states_buffer[i]) {
  331. found = 1;
  332. break;
  333. }
  334. }
  335. if(!found) {
  336. tail = odd_msbs[msb - msb_head].tail++;
  337. odd_msbs[msb - msb_head].states[tail] = states_buffer[i];
  338. }
  339. }
  340. }
  341. }
  342. if(filter(semi_state) == (eks & 1)) { //-V547
  343. states_buffer[0] = semi_state;
  344. states_tail = state_loop(states_buffer, eks, CONST_M1_2, CONST_M2_2, in, 3);
  345. for(i = 0; i <= states_tail; i++) {
  346. msb = states_buffer[i] >> 24;
  347. if((msb >= msb_head) && (msb < msb_tail)) {
  348. found = 0;
  349. for(j = 0; j < even_msbs[msb - msb_head].tail; j++) {
  350. if(even_msbs[msb - msb_head].states[j] == states_buffer[i]) {
  351. found = 1;
  352. break;
  353. }
  354. }
  355. if(!found) {
  356. tail = even_msbs[msb - msb_head].tail++;
  357. even_msbs[msb - msb_head].states[tail] = states_buffer[i];
  358. }
  359. }
  360. }
  361. }
  362. }
  363. oks >>= 12;
  364. eks >>= 12;
  365. for(i = 0; i < MSB_LIMIT; i++) {
  366. if(sync_state(program_state) == 1) {
  367. return 0;
  368. }
  369. // TODO: Why is this necessary?
  370. memset(temp_states_even, 0, sizeof(unsigned int) * (1280));
  371. memset(temp_states_odd, 0, sizeof(unsigned int) * (1280));
  372. memcpy(temp_states_odd, odd_msbs[i].states, odd_msbs[i].tail * sizeof(unsigned int));
  373. memcpy(temp_states_even, even_msbs[i].states, even_msbs[i].tail * sizeof(unsigned int));
  374. int res = old_recover(
  375. temp_states_odd,
  376. 0,
  377. odd_msbs[i].tail,
  378. oks,
  379. temp_states_even,
  380. 0,
  381. even_msbs[i].tail,
  382. eks,
  383. 3,
  384. 0,
  385. n,
  386. in >> 16,
  387. 1,
  388. program_state);
  389. if(res == -1) {
  390. return 1;
  391. }
  392. //odd_msbs[i].tail = 0;
  393. //even_msbs[i].tail = 0;
  394. }
  395. return 0;
  396. }
  397. void** allocate_blocks(const size_t* block_sizes, int num_blocks) {
  398. void** block_pointers = malloc(num_blocks * sizeof(void*));
  399. for(int i = 0; i < num_blocks; i++) {
  400. if(memmgr_heap_get_max_free_block() < block_sizes[i]) {
  401. // Not enough memory, free previously allocated blocks
  402. for(int j = 0; j < i; j++) {
  403. free(block_pointers[j]);
  404. }
  405. free(block_pointers);
  406. return NULL;
  407. }
  408. block_pointers[i] = malloc(block_sizes[i]);
  409. }
  410. return block_pointers;
  411. }
  412. bool is_full_speed() {
  413. return MSB_LIMIT == 16;
  414. }
  415. bool recover(MfClassicNonce* n, int ks2, unsigned int in, ProgramState* program_state) {
  416. bool found = false;
  417. const size_t block_sizes[] = {49216, 49216, 5120, 5120, 4096};
  418. const size_t reduced_block_sizes[] = {24608, 24608, 5120, 5120, 4096};
  419. const int num_blocks = sizeof(block_sizes) / sizeof(block_sizes[0]);
  420. void** block_pointers = allocate_blocks(block_sizes, num_blocks);
  421. if(block_pointers == NULL) {
  422. // System has less than the guaranteed amount of RAM (140 KB) - adjust some parameters to run anyway at half speed
  423. if(is_full_speed()) {
  424. //eta_round_time *= 2;
  425. eta_total_time *= 2;
  426. MSB_LIMIT /= 2;
  427. }
  428. block_pointers = allocate_blocks(reduced_block_sizes, num_blocks);
  429. if(block_pointers == NULL) {
  430. // System has less than 70 KB of RAM - should never happen so we don't reduce speed further
  431. program_state->err = InsufficientRAM;
  432. program_state->mfkey_state = Error;
  433. return false;
  434. }
  435. }
  436. // Adjust estimates for static encrypted attacks
  437. if(n->attack == static_encrypted) {
  438. eta_round_time *= 4;
  439. eta_total_time *= 4;
  440. if(is_full_speed()) {
  441. eta_round_time *= 4;
  442. eta_total_time *= 4;
  443. }
  444. }
  445. struct Msb* odd_msbs = block_pointers[0];
  446. struct Msb* even_msbs = block_pointers[1];
  447. unsigned int* temp_states_odd = block_pointers[2];
  448. unsigned int* temp_states_even = block_pointers[3];
  449. unsigned int* states_buffer = block_pointers[4];
  450. int oks = 0, eks = 0;
  451. int i = 0, msb = 0;
  452. for(i = 31; i >= 0; i -= 2) {
  453. oks = oks << 1 | BEBIT(ks2, i);
  454. }
  455. for(i = 30; i >= 0; i -= 2) {
  456. eks = eks << 1 | BEBIT(ks2, i);
  457. }
  458. int bench_start = furi_hal_rtc_get_timestamp();
  459. program_state->eta_total = eta_total_time;
  460. program_state->eta_timestamp = bench_start;
  461. for(msb = 0; msb <= ((256 / MSB_LIMIT) - 1); msb++) {
  462. program_state->search = msb;
  463. program_state->eta_round = eta_round_time;
  464. program_state->eta_total = eta_total_time - (eta_round_time * msb);
  465. if(calculate_msb_tables(
  466. oks,
  467. eks,
  468. msb,
  469. n,
  470. states_buffer,
  471. odd_msbs,
  472. even_msbs,
  473. temp_states_odd,
  474. temp_states_even,
  475. in,
  476. program_state)) {
  477. //int bench_stop = furi_hal_rtc_get_timestamp();
  478. //FURI_LOG_I(TAG, "Cracked in %i seconds", bench_stop - bench_start);
  479. found = true;
  480. break;
  481. }
  482. if(program_state->close_thread_please) {
  483. break;
  484. }
  485. }
  486. // Free the allocated blocks
  487. for(int i = 0; i < num_blocks; i++) {
  488. free(block_pointers[i]);
  489. }
  490. free(block_pointers);
  491. return found;
  492. }
  493. bool key_already_found_for_nonce_in_solved(
  494. MfClassicKey* keyarray,
  495. int keyarray_size,
  496. MfClassicNonce* nonce) {
  497. for(int k = 0; k < keyarray_size; k++) {
  498. uint64_t key_as_int = bit_lib_bytes_to_num_be(keyarray[k].data, sizeof(MfClassicKey));
  499. struct Crypto1State temp = {0, 0};
  500. for(int i = 0; i < 24; i++) {
  501. (&temp)->odd |= (BIT(key_as_int, 2 * i + 1) << (i ^ 3));
  502. (&temp)->even |= (BIT(key_as_int, 2 * i) << (i ^ 3));
  503. }
  504. if(nonce->attack == mfkey32) {
  505. crypt_word_noret(&temp, nonce->uid_xor_nt1, 0);
  506. crypt_word_noret(&temp, nonce->nr1_enc, 1);
  507. if(nonce->ar1_enc == (crypt_word(&temp) ^ nonce->p64b)) {
  508. return true;
  509. }
  510. } else if(nonce->attack == static_nested) {
  511. uint32_t expected_ks1 = crypt_word_ret(&temp, nonce->uid_xor_nt0, 0);
  512. if(nonce->ks1_1_enc == expected_ks1) {
  513. return true;
  514. }
  515. }
  516. }
  517. return false;
  518. }
  519. #pragma GCC push_options
  520. #pragma GCC optimize("Os")
  521. static void finished_beep() {
  522. // Beep to indicate completion
  523. NotificationApp* notification = furi_record_open("notification");
  524. notification_message(notification, &sequence_audiovisual_alert);
  525. notification_message(notification, &sequence_display_backlight_on);
  526. furi_record_close("notification");
  527. }
  528. void mfkey(ProgramState* program_state) {
  529. uint32_t ks_enc = 0, nt_xor_uid = 0;
  530. MfClassicKey found_key; // Recovered key
  531. size_t keyarray_size = 0;
  532. MfClassicKey* keyarray = malloc(sizeof(MfClassicKey) * 1);
  533. uint32_t i = 0, j = 0;
  534. //FURI_LOG_I(TAG, "Free heap before alloc(): %zub", memmgr_get_free_heap());
  535. Storage* storage = furi_record_open(RECORD_STORAGE);
  536. FlipperApplication* app = flipper_application_alloc(storage, firmware_api_interface);
  537. flipper_application_preload(app, APP_ASSETS_PATH("plugins/mfkey_init_plugin.fal"));
  538. flipper_application_map_to_memory(app);
  539. const FlipperAppPluginDescriptor* app_descriptor =
  540. flipper_application_plugin_get_descriptor(app);
  541. const MfkeyPlugin* init_plugin = app_descriptor->entry_point;
  542. // Check for nonces
  543. program_state->mfkey32_present = init_plugin->napi_mf_classic_mfkey32_nonces_check_presence();
  544. program_state->nested_present = init_plugin->napi_mf_classic_nested_nonces_check_presence();
  545. if(!(program_state->mfkey32_present) && !(program_state->nested_present)) {
  546. program_state->err = MissingNonces;
  547. program_state->mfkey_state = Error;
  548. flipper_application_free(app);
  549. furi_record_close(RECORD_STORAGE);
  550. free(keyarray);
  551. return;
  552. }
  553. // Read dictionaries (optional)
  554. KeysDict* system_dict = {0};
  555. bool system_dict_exists = keys_dict_check_presence(KEYS_DICT_SYSTEM_PATH);
  556. KeysDict* user_dict = {0};
  557. bool user_dict_exists = keys_dict_check_presence(KEYS_DICT_USER_PATH);
  558. uint32_t total_dict_keys = 0;
  559. if(system_dict_exists) {
  560. system_dict =
  561. keys_dict_alloc(KEYS_DICT_SYSTEM_PATH, KeysDictModeOpenExisting, sizeof(MfClassicKey));
  562. total_dict_keys += keys_dict_get_total_keys(system_dict);
  563. }
  564. user_dict = keys_dict_alloc(KEYS_DICT_USER_PATH, KeysDictModeOpenAlways, sizeof(MfClassicKey));
  565. if(user_dict_exists) {
  566. total_dict_keys += keys_dict_get_total_keys(user_dict);
  567. }
  568. user_dict_exists = true;
  569. program_state->dict_count = total_dict_keys;
  570. program_state->mfkey_state = DictionaryAttack;
  571. // Read nonces
  572. MfClassicNonceArray* nonce_arr;
  573. nonce_arr = init_plugin->napi_mf_classic_nonce_array_alloc(
  574. system_dict, system_dict_exists, user_dict, program_state);
  575. if(system_dict_exists) {
  576. keys_dict_free(system_dict);
  577. }
  578. if(nonce_arr->total_nonces == 0) {
  579. // Nothing to crack
  580. program_state->err = ZeroNonces;
  581. program_state->mfkey_state = Error;
  582. init_plugin->napi_mf_classic_nonce_array_free(nonce_arr);
  583. flipper_application_free(app);
  584. furi_record_close(RECORD_STORAGE);
  585. keys_dict_free(user_dict);
  586. free(keyarray);
  587. return;
  588. }
  589. flipper_application_free(app);
  590. furi_record_close(RECORD_STORAGE);
  591. // TODO: Track free state at the time this is called to ensure double free does not happen
  592. furi_assert(nonce_arr);
  593. furi_assert(nonce_arr->stream);
  594. // TODO: Already closed?
  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