mfkey.c 58 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621
  1. #pragma GCC optimize("O3")
  2. #pragma GCC optimize("-funroll-all-loops")
  3. // TODO: Add keys to top of the user dictionary, not the bottom
  4. // TODO: More efficient dictionary bruteforce by scanning through hardcoded very common keys and previously found dictionary keys first?
  5. // (a cache for napi_key_already_found_for_nonce)
  6. // TODO: Remove icon from application to save 1.5 KB
  7. // TODO: Selectively unroll loops to reduce binary size
  8. // TODO: Optimize assembly of filter, state_loop, and/or evenparity32
  9. // TODO: Investigate collecting the parity during Mfkey32 attacks to further optimize the attack
  10. // TODO: Why different sscanf between Mfkey32 and Nested?
  11. // TODO: "Read tag again with NFC app" message upon completion, "Complete. Keys added: <n>"
  12. // TODO: Separate Mfkey32 and Nested functions where possible to reduce branch statements
  13. // TODO: More accurate timing for Nested
  14. // TODO: Eliminate OOM crashes (updated application is larger)
  15. #include <furi.h>
  16. #include <furi_hal.h>
  17. #include "time.h"
  18. #include <gui/gui.h>
  19. #include <gui/elements.h>
  20. #include <input/input.h>
  21. #include <stdlib.h>
  22. #include "mfkeynested_icons.h"
  23. #include <inttypes.h>
  24. #include <stdio.h>
  25. #include <string.h>
  26. #include <stdint.h>
  27. #include <unistd.h>
  28. #include <storage/storage.h>
  29. #include <lib/nfc/helpers/mf_classic_dict.h>
  30. #include <lib/toolbox/args.h>
  31. #include <lib/flipper_format/flipper_format.h>
  32. #include <dolphin/dolphin.h>
  33. #include <notification/notification_messages.h>
  34. #define MF_CLASSIC_DICT_FLIPPER_PATH EXT_PATH("nfc/assets/mf_classic_dict.nfc")
  35. #define MF_CLASSIC_DICT_USER_PATH EXT_PATH("nfc/assets/mf_classic_dict_user.nfc")
  36. #define MF_CLASSIC_NONCE_PATH EXT_PATH("nfc/.mfkey32.log")
  37. #define MF_CLASSIC_NESTED_NONCE_PATH EXT_PATH("nfc/.nested")
  38. #define TAG "MFKey"
  39. #define NFC_MF_CLASSIC_KEY_LEN (13)
  40. #define MAX_NAME_LEN 32
  41. #define MAX_PATH_LEN 64
  42. #define MIN_RAM 115632 // TODO: May no longer be accurate, crashes are getting frequent
  43. #define LF_POLY_ODD (0x29CE5C)
  44. #define LF_POLY_EVEN (0x870804)
  45. #define CONST_M1_1 (LF_POLY_EVEN << 1 | 1)
  46. #define CONST_M2_1 (LF_POLY_ODD << 1)
  47. #define CONST_M1_2 (LF_POLY_ODD)
  48. #define CONST_M2_2 (LF_POLY_EVEN << 1 | 1)
  49. #define BIT(x, n) ((x) >> (n) & 1)
  50. #define BEBIT(x, n) BIT(x, (n) ^ 24)
  51. #define SWAPENDIAN(x) \
  52. ((x) = ((x) >> 8 & 0xff00ff) | ((x) & 0xff00ff) << 8, (x) = (x) >> 16 | (x) << 16)
  53. //#define SIZEOF(arr) sizeof(arr) / sizeof(*arr)
  54. static int eta_round_time = 56;
  55. static int eta_total_time = 900;
  56. // MSB_LIMIT: Chunk size (out of 256)
  57. static int MSB_LIMIT = 16;
  58. struct Crypto1State {
  59. uint32_t odd, even;
  60. };
  61. struct Msb {
  62. int tail;
  63. uint32_t states[768];
  64. };
  65. typedef enum {
  66. EventTypeTick,
  67. EventTypeKey,
  68. } EventType;
  69. typedef struct {
  70. EventType type;
  71. InputEvent input;
  72. } PluginEvent;
  73. typedef enum {
  74. MissingNonces,
  75. ZeroNonces,
  76. } MFKeyError;
  77. typedef enum {
  78. Ready,
  79. Initializing,
  80. DictionaryAttack,
  81. MFKeyAttack,
  82. Complete,
  83. Error,
  84. Help,
  85. } MFKeyState;
  86. // TODO: Can we eliminate any of the members of this struct?
  87. typedef struct {
  88. FuriMutex* mutex;
  89. MFKeyError err;
  90. MFKeyState mfkey_state;
  91. int cracked;
  92. int unique_cracked;
  93. int num_completed;
  94. int total;
  95. int dict_count;
  96. int search;
  97. int eta_timestamp;
  98. int eta_total;
  99. int eta_round;
  100. bool mfkey32_present;
  101. bool nested_present;
  102. bool is_thread_running;
  103. bool close_thread_please;
  104. FuriThread* mfkeythread;
  105. } ProgramState;
  106. typedef enum { mfkey32, static_nested } AttackType;
  107. typedef struct {
  108. AttackType attack;
  109. uint64_t key; // key
  110. uint32_t uid; // serial number
  111. uint32_t nt0; // tag challenge first
  112. uint32_t nt1; // tag challenge second
  113. uint32_t uid_xor_nt0; // uid ^ nt0
  114. uint32_t uid_xor_nt1; // uid ^ nt1
  115. // Mfkey32
  116. uint32_t p64; // 64th successor of nt0
  117. uint32_t p64b; // 64th successor of nt1
  118. uint32_t nr0_enc; // first encrypted reader challenge
  119. uint32_t ar0_enc; // first encrypted reader response
  120. uint32_t nr1_enc; // second encrypted reader challenge
  121. uint32_t ar1_enc; // second encrypted reader response
  122. // Nested
  123. uint32_t ks1_1_enc; // first encrypted keystream
  124. uint32_t ks1_2_enc; // second encrypted keystream
  125. char par_1_str[5]; // first parity bits (string representation)
  126. char par_2_str[5]; // second parity bits (string representation)
  127. uint8_t par_1; // first parity bits
  128. uint8_t par_2; // second parity bits
  129. } MfClassicNonce;
  130. typedef struct {
  131. Stream* stream;
  132. uint32_t total_nonces;
  133. MfClassicNonce* remaining_nonce_array;
  134. size_t remaining_nonces;
  135. } MfClassicNonceArray;
  136. struct MfClassicDict {
  137. Stream* stream;
  138. uint32_t total_keys;
  139. };
  140. static const uint8_t table[256] = {
  141. 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3,
  142. 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4,
  143. 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4,
  144. 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5,
  145. 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2,
  146. 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5,
  147. 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4,
  148. 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6,
  149. 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8};
  150. static const uint8_t lookup1[256] = {
  151. 0, 0, 16, 16, 0, 16, 0, 0, 0, 16, 0, 0, 16, 16, 16, 16, 0, 0, 16, 16, 0, 16, 0, 0,
  152. 0, 16, 0, 0, 16, 16, 16, 16, 0, 0, 16, 16, 0, 16, 0, 0, 0, 16, 0, 0, 16, 16, 16, 16,
  153. 8, 8, 24, 24, 8, 24, 8, 8, 8, 24, 8, 8, 24, 24, 24, 24, 8, 8, 24, 24, 8, 24, 8, 8,
  154. 8, 24, 8, 8, 24, 24, 24, 24, 8, 8, 24, 24, 8, 24, 8, 8, 8, 24, 8, 8, 24, 24, 24, 24,
  155. 0, 0, 16, 16, 0, 16, 0, 0, 0, 16, 0, 0, 16, 16, 16, 16, 0, 0, 16, 16, 0, 16, 0, 0,
  156. 0, 16, 0, 0, 16, 16, 16, 16, 8, 8, 24, 24, 8, 24, 8, 8, 8, 24, 8, 8, 24, 24, 24, 24,
  157. 0, 0, 16, 16, 0, 16, 0, 0, 0, 16, 0, 0, 16, 16, 16, 16, 0, 0, 16, 16, 0, 16, 0, 0,
  158. 0, 16, 0, 0, 16, 16, 16, 16, 8, 8, 24, 24, 8, 24, 8, 8, 8, 24, 8, 8, 24, 24, 24, 24,
  159. 8, 8, 24, 24, 8, 24, 8, 8, 8, 24, 8, 8, 24, 24, 24, 24, 0, 0, 16, 16, 0, 16, 0, 0,
  160. 0, 16, 0, 0, 16, 16, 16, 16, 8, 8, 24, 24, 8, 24, 8, 8, 8, 24, 8, 8, 24, 24, 24, 24,
  161. 8, 8, 24, 24, 8, 24, 8, 8, 8, 24, 8, 8, 24, 24, 24, 24};
  162. static const uint8_t lookup2[256] = {
  163. 0, 0, 4, 4, 0, 4, 0, 0, 0, 4, 0, 0, 4, 4, 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 0, 4, 0, 0, 4,
  164. 4, 4, 4, 2, 2, 6, 6, 2, 6, 2, 2, 2, 6, 2, 2, 6, 6, 6, 6, 2, 2, 6, 6, 2, 6, 2, 2, 2, 6,
  165. 2, 2, 6, 6, 6, 6, 0, 0, 4, 4, 0, 4, 0, 0, 0, 4, 0, 0, 4, 4, 4, 4, 2, 2, 6, 6, 2, 6, 2,
  166. 2, 2, 6, 2, 2, 6, 6, 6, 6, 0, 0, 4, 4, 0, 4, 0, 0, 0, 4, 0, 0, 4, 4, 4, 4, 0, 0, 4, 4,
  167. 0, 4, 0, 0, 0, 4, 0, 0, 4, 4, 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 0, 4, 0, 0, 4, 4, 4, 4, 2,
  168. 2, 6, 6, 2, 6, 2, 2, 2, 6, 2, 2, 6, 6, 6, 6, 0, 0, 4, 4, 0, 4, 0, 0, 0, 4, 0, 0, 4, 4,
  169. 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 0, 4, 0, 0, 4, 4, 4, 4, 2, 2, 6, 6, 2, 6, 2, 2, 2, 6, 2,
  170. 2, 6, 6, 6, 6, 2, 2, 6, 6, 2, 6, 2, 2, 2, 6, 2, 2, 6, 6, 6, 6, 2, 2, 6, 6, 2, 6, 2, 2,
  171. 2, 6, 2, 2, 6, 6, 6, 6, 2, 2, 6, 6, 2, 6, 2, 2, 2, 6, 2, 2, 6, 6, 6, 6};
  172. uint32_t prng_successor(uint32_t x, uint32_t n) {
  173. SWAPENDIAN(x);
  174. while(n--) x = x >> 1 | (x >> 16 ^ x >> 18 ^ x >> 19 ^ x >> 21) << 31;
  175. return SWAPENDIAN(x);
  176. }
  177. static inline int filter(uint32_t const x) {
  178. uint32_t f;
  179. f = lookup1[x & 0xff] | lookup2[(x >> 8) & 0xff];
  180. f |= 0x0d938 >> (x >> 16 & 0xf) & 1;
  181. return BIT(0xEC57E80A, f);
  182. }
  183. static inline uint8_t evenparity32(uint32_t x) {
  184. if((table[x & 0xff] + table[(x >> 8) & 0xff] + table[(x >> 16) & 0xff] + table[x >> 24]) % 2 ==
  185. 0) {
  186. return 0;
  187. } else {
  188. return 1;
  189. }
  190. //return ((table[x & 0xff] + table[(x >> 8) & 0xff] + table[(x >> 16) & 0xff] + table[x >> 24]) % 2) & 0xFF;
  191. }
  192. static inline void update_contribution(unsigned int data[], int item, int mask1, int mask2) {
  193. int p = data[item] >> 25;
  194. p = p << 1 | evenparity32(data[item] & mask1);
  195. p = p << 1 | evenparity32(data[item] & mask2);
  196. data[item] = p << 24 | (data[item] & 0xffffff);
  197. }
  198. void crypto1_get_lfsr(struct Crypto1State* state, uint64_t* lfsr) {
  199. int i;
  200. for(*lfsr = 0, i = 23; i >= 0; --i) {
  201. *lfsr = *lfsr << 1 | BIT(state->odd, i ^ 3);
  202. *lfsr = *lfsr << 1 | BIT(state->even, i ^ 3);
  203. }
  204. }
  205. static inline uint32_t crypt_word(struct Crypto1State* s) {
  206. // "in" and "x" are always 0 (last iteration)
  207. uint32_t res_ret = 0;
  208. uint32_t feedin, t;
  209. for(int i = 0; i <= 31; i++) {
  210. res_ret |= (filter(s->odd) << (24 ^ i)); //-V629
  211. feedin = LF_POLY_EVEN & s->even;
  212. feedin ^= LF_POLY_ODD & s->odd;
  213. s->even = s->even << 1 | (evenparity32(feedin));
  214. t = s->odd, s->odd = s->even, s->even = t;
  215. }
  216. return res_ret;
  217. }
  218. static inline void crypt_word_noret(struct Crypto1State* s, uint32_t in, int x) {
  219. uint8_t ret;
  220. uint32_t feedin, t, next_in;
  221. for(int i = 0; i <= 31; i++) {
  222. next_in = BEBIT(in, i);
  223. ret = filter(s->odd);
  224. feedin = ret & (!!x);
  225. feedin ^= LF_POLY_EVEN & s->even;
  226. feedin ^= LF_POLY_ODD & s->odd;
  227. feedin ^= !!next_in;
  228. s->even = s->even << 1 | (evenparity32(feedin));
  229. t = s->odd, s->odd = s->even, s->even = t;
  230. }
  231. return;
  232. }
  233. static inline uint32_t crypt_word_ret(struct Crypto1State* s, uint32_t in, int x) {
  234. uint32_t ret = 0;
  235. uint32_t feedin, t, next_in;
  236. uint8_t next_ret;
  237. for(int i = 0; i <= 31; i++) {
  238. next_in = BEBIT(in, i);
  239. next_ret = filter(s->odd);
  240. feedin = next_ret & (!!x);
  241. feedin ^= LF_POLY_EVEN & s->even;
  242. feedin ^= LF_POLY_ODD & s->odd;
  243. feedin ^= !!next_in;
  244. s->even = s->even << 1 | (evenparity32(feedin));
  245. t = s->odd, s->odd = s->even, s->even = t;
  246. ret |= next_ret << (24 ^ i);
  247. }
  248. return ret;
  249. }
  250. static inline void rollback_word_noret(struct Crypto1State* s, uint32_t in, int x) {
  251. uint8_t ret;
  252. uint32_t feedin, t, next_in;
  253. for(int i = 31; i >= 0; i--) {
  254. next_in = BEBIT(in, i);
  255. s->odd &= 0xffffff;
  256. t = s->odd, s->odd = s->even, s->even = t;
  257. ret = filter(s->odd);
  258. feedin = ret & (!!x);
  259. feedin ^= s->even & 1;
  260. feedin ^= LF_POLY_EVEN & (s->even >>= 1);
  261. feedin ^= LF_POLY_ODD & s->odd;
  262. feedin ^= !!next_in;
  263. s->even |= (evenparity32(feedin)) << 23;
  264. }
  265. return;
  266. }
  267. bool key_already_found_for_nonce(uint64_t* keyarray, int keyarray_size, MfClassicNonce* nonce) {
  268. for(int k = 0; k < keyarray_size; k++) {
  269. struct Crypto1State temp = {0, 0};
  270. for(int i = 0; i < 24; i++) {
  271. (&temp)->odd |= (BIT(keyarray[k], 2 * i + 1) << (i ^ 3));
  272. (&temp)->even |= (BIT(keyarray[k], 2 * i) << (i ^ 3));
  273. }
  274. if(nonce->attack == mfkey32) {
  275. crypt_word_noret(&temp, nonce->uid_xor_nt1, 0);
  276. crypt_word_noret(&temp, nonce->nr1_enc, 1);
  277. if(nonce->ar1_enc == (crypt_word(&temp) ^ nonce->p64b)) {
  278. return true;
  279. }
  280. } else if(nonce->attack == static_nested) {
  281. uint32_t expected_ks1 = crypt_word_ret(&temp, nonce->uid_xor_nt0, 0);
  282. if(nonce->ks1_1_enc == expected_ks1) {
  283. return true;
  284. }
  285. }
  286. }
  287. return false;
  288. }
  289. int check_state(struct Crypto1State* t, MfClassicNonce* n) {
  290. if(!(t->odd | t->even)) return 0;
  291. if(n->attack == mfkey32) {
  292. rollback_word_noret(t, 0, 0);
  293. rollback_word_noret(t, n->nr0_enc, 1);
  294. rollback_word_noret(t, n->uid_xor_nt0, 0);
  295. struct Crypto1State temp = {t->odd, t->even};
  296. crypt_word_noret(t, n->uid_xor_nt1, 0);
  297. crypt_word_noret(t, n->nr1_enc, 1);
  298. if(n->ar1_enc == (crypt_word(t) ^ n->p64b)) {
  299. crypto1_get_lfsr(&temp, &(n->key));
  300. return 1;
  301. }
  302. return 0;
  303. } else if(n->attack == static_nested) {
  304. struct Crypto1State temp = {t->odd, t->even};
  305. rollback_word_noret(t, n->uid_xor_nt1, 0);
  306. if(n->ks1_1_enc == crypt_word_ret(t, n->uid_xor_nt0, 0)) {
  307. rollback_word_noret(&temp, n->uid_xor_nt1, 0);
  308. crypto1_get_lfsr(&temp, &(n->key));
  309. return 1;
  310. }
  311. return 0;
  312. }
  313. return 0;
  314. }
  315. static inline int state_loop(
  316. unsigned int* states_buffer,
  317. int xks,
  318. int m1,
  319. int m2,
  320. unsigned int in,
  321. uint8_t and_val) {
  322. int states_tail = 0;
  323. int round = 0, s = 0, xks_bit = 0, round_in = 0;
  324. for(round = 1; round <= 12; round++) {
  325. xks_bit = BIT(xks, round);
  326. if(round > 4) {
  327. round_in = ((in >> (2 * (round - 4))) & and_val) << 24;
  328. }
  329. for(s = 0; s <= states_tail; s++) {
  330. states_buffer[s] <<= 1;
  331. if((filter(states_buffer[s]) ^ filter(states_buffer[s] | 1)) != 0) {
  332. states_buffer[s] |= filter(states_buffer[s]) ^ xks_bit;
  333. if(round > 4) {
  334. update_contribution(states_buffer, s, m1, m2);
  335. states_buffer[s] ^= round_in;
  336. }
  337. } else if(filter(states_buffer[s]) == xks_bit) {
  338. // TODO: Refactor
  339. if(round > 4) {
  340. states_buffer[++states_tail] = states_buffer[s + 1];
  341. states_buffer[s + 1] = states_buffer[s] | 1;
  342. update_contribution(states_buffer, s, m1, m2);
  343. states_buffer[s++] ^= round_in;
  344. update_contribution(states_buffer, s, m1, m2);
  345. states_buffer[s] ^= round_in;
  346. } else {
  347. states_buffer[++states_tail] = states_buffer[++s];
  348. states_buffer[s] = states_buffer[s - 1] | 1;
  349. }
  350. } else {
  351. states_buffer[s--] = states_buffer[states_tail--];
  352. }
  353. }
  354. }
  355. return states_tail;
  356. }
  357. int binsearch(unsigned int data[], int start, int stop) {
  358. int mid, val = data[stop] & 0xff000000;
  359. while(start != stop) {
  360. mid = (stop - start) >> 1;
  361. if((data[start + mid] ^ 0x80000000) > (val ^ 0x80000000))
  362. stop = start + mid;
  363. else
  364. start += mid + 1;
  365. }
  366. return start;
  367. }
  368. void quicksort(unsigned int array[], int low, int high) {
  369. //if (SIZEOF(array) == 0)
  370. // return;
  371. if(low >= high) return;
  372. int middle = low + (high - low) / 2;
  373. unsigned int pivot = array[middle];
  374. int i = low, j = high;
  375. while(i <= j) {
  376. while(array[i] < pivot) {
  377. i++;
  378. }
  379. while(array[j] > pivot) {
  380. j--;
  381. }
  382. if(i <= j) { // swap
  383. int temp = array[i];
  384. array[i] = array[j];
  385. array[j] = temp;
  386. i++;
  387. j--;
  388. }
  389. }
  390. if(low < j) {
  391. quicksort(array, low, j);
  392. }
  393. if(high > i) {
  394. quicksort(array, i, high);
  395. }
  396. }
  397. int extend_table(unsigned int data[], int tbl, int end, int bit, int m1, int m2, unsigned int in) {
  398. in <<= 24;
  399. for(data[tbl] <<= 1; tbl <= end; data[++tbl] <<= 1) {
  400. if((filter(data[tbl]) ^ filter(data[tbl] | 1)) != 0) {
  401. data[tbl] |= filter(data[tbl]) ^ bit;
  402. update_contribution(data, tbl, m1, m2);
  403. data[tbl] ^= in;
  404. } else if(filter(data[tbl]) == bit) {
  405. data[++end] = data[tbl + 1];
  406. data[tbl + 1] = data[tbl] | 1;
  407. update_contribution(data, tbl, m1, m2);
  408. data[tbl++] ^= in;
  409. update_contribution(data, tbl, m1, m2);
  410. data[tbl] ^= in;
  411. } else {
  412. data[tbl--] = data[end--];
  413. }
  414. }
  415. return end;
  416. }
  417. int old_recover(
  418. unsigned int odd[],
  419. int o_head,
  420. int o_tail,
  421. int oks,
  422. unsigned int even[],
  423. int e_head,
  424. int e_tail,
  425. int eks,
  426. int rem,
  427. int s,
  428. MfClassicNonce* n,
  429. unsigned int in,
  430. int first_run) {
  431. int o, e, i;
  432. if(rem == -1) {
  433. for(e = e_head; e <= e_tail; ++e) {
  434. even[e] = (even[e] << 1) ^ evenparity32(even[e] & LF_POLY_EVEN) ^ (!!(in & 4));
  435. for(o = o_head; o <= o_tail; ++o, ++s) {
  436. struct Crypto1State temp = {0, 0};
  437. temp.even = odd[o];
  438. temp.odd = even[e] ^ evenparity32(odd[o] & LF_POLY_ODD);
  439. if(check_state(&temp, n)) {
  440. return -1;
  441. }
  442. }
  443. }
  444. return s;
  445. }
  446. if(first_run == 0) {
  447. for(i = 0; (i < 4) && (rem-- != 0); i++) {
  448. oks >>= 1;
  449. eks >>= 1;
  450. in >>= 2;
  451. o_tail = extend_table(
  452. odd, o_head, o_tail, oks & 1, LF_POLY_EVEN << 1 | 1, LF_POLY_ODD << 1, 0);
  453. if(o_head > o_tail) return s;
  454. e_tail = extend_table(
  455. even, e_head, e_tail, eks & 1, LF_POLY_ODD, LF_POLY_EVEN << 1 | 1, in & 3);
  456. if(e_head > e_tail) return s;
  457. }
  458. }
  459. first_run = 0;
  460. quicksort(odd, o_head, o_tail);
  461. quicksort(even, e_head, e_tail);
  462. while(o_tail >= o_head && e_tail >= e_head) {
  463. if(((odd[o_tail] ^ even[e_tail]) >> 24) == 0) {
  464. o_tail = binsearch(odd, o_head, o = o_tail);
  465. e_tail = binsearch(even, e_head, e = e_tail);
  466. s = old_recover(
  467. odd, o_tail--, o, oks, even, e_tail--, e, eks, rem, s, n, in, first_run);
  468. if(s == -1) {
  469. break;
  470. }
  471. } else if((odd[o_tail] ^ 0x80000000) > (even[e_tail] ^ 0x80000000)) {
  472. o_tail = binsearch(odd, o_head, o_tail) - 1;
  473. } else {
  474. e_tail = binsearch(even, e_head, e_tail) - 1;
  475. }
  476. }
  477. return s;
  478. }
  479. static inline int sync_state(ProgramState* program_state) {
  480. int ts = furi_hal_rtc_get_timestamp();
  481. program_state->eta_round = program_state->eta_round - (ts - program_state->eta_timestamp);
  482. program_state->eta_total = program_state->eta_total - (ts - program_state->eta_timestamp);
  483. program_state->eta_timestamp = ts;
  484. if(program_state->close_thread_please) {
  485. return 1;
  486. }
  487. return 0;
  488. }
  489. int calculate_msb_tables(
  490. int oks,
  491. int eks,
  492. int msb_round,
  493. MfClassicNonce* n,
  494. unsigned int* states_buffer,
  495. struct Msb* odd_msbs,
  496. struct Msb* even_msbs,
  497. unsigned int* temp_states_odd,
  498. unsigned int* temp_states_even,
  499. unsigned int in,
  500. ProgramState* program_state) {
  501. //FURI_LOG_I(TAG, "MSB GO %i", msb_iter); // DEBUG
  502. unsigned int msb_head = (MSB_LIMIT * msb_round); // msb_iter ranges from 0 to (256/MSB_LIMIT)-1
  503. unsigned int msb_tail = (MSB_LIMIT * (msb_round + 1));
  504. int states_tail = 0, tail = 0;
  505. int i = 0, j = 0, semi_state = 0, found = 0;
  506. unsigned int msb = 0;
  507. in = ((in >> 16 & 0xff) | (in << 16) | (in & 0xff00)) << 1;
  508. // TODO: Why is this necessary?
  509. memset(odd_msbs, 0, MSB_LIMIT * sizeof(struct Msb));
  510. memset(even_msbs, 0, MSB_LIMIT * sizeof(struct Msb));
  511. for(semi_state = 1 << 20; semi_state >= 0; semi_state--) {
  512. if(semi_state % 32768 == 0) {
  513. if(sync_state(program_state) == 1) {
  514. return 0;
  515. }
  516. }
  517. if(filter(semi_state) == (oks & 1)) { //-V547
  518. states_buffer[0] = semi_state;
  519. states_tail = state_loop(states_buffer, oks, CONST_M1_1, CONST_M2_1, 0, 0);
  520. for(i = states_tail; i >= 0; i--) {
  521. msb = states_buffer[i] >> 24;
  522. if((msb >= msb_head) && (msb < msb_tail)) {
  523. found = 0;
  524. for(j = 0; j < odd_msbs[msb - msb_head].tail - 1; j++) {
  525. if(odd_msbs[msb - msb_head].states[j] == states_buffer[i]) {
  526. found = 1;
  527. break;
  528. }
  529. }
  530. if(!found) {
  531. tail = odd_msbs[msb - msb_head].tail++;
  532. odd_msbs[msb - msb_head].states[tail] = states_buffer[i];
  533. }
  534. }
  535. }
  536. }
  537. if(filter(semi_state) == (eks & 1)) { //-V547
  538. states_buffer[0] = semi_state;
  539. states_tail = state_loop(states_buffer, eks, CONST_M1_2, CONST_M2_2, in, 3);
  540. for(i = 0; i <= states_tail; i++) {
  541. msb = states_buffer[i] >> 24;
  542. if((msb >= msb_head) && (msb < msb_tail)) {
  543. found = 0;
  544. for(j = 0; j < even_msbs[msb - msb_head].tail; j++) {
  545. if(even_msbs[msb - msb_head].states[j] == states_buffer[i]) {
  546. found = 1;
  547. break;
  548. }
  549. }
  550. if(!found) {
  551. tail = even_msbs[msb - msb_head].tail++;
  552. even_msbs[msb - msb_head].states[tail] = states_buffer[i];
  553. }
  554. }
  555. }
  556. }
  557. }
  558. oks >>= 12;
  559. eks >>= 12;
  560. for(i = 0; i < MSB_LIMIT; i++) {
  561. if(sync_state(program_state) == 1) {
  562. return 0;
  563. }
  564. // TODO: Why is this necessary?
  565. memset(temp_states_even, 0, sizeof(unsigned int) * (1280));
  566. memset(temp_states_odd, 0, sizeof(unsigned int) * (1280));
  567. memcpy(temp_states_odd, odd_msbs[i].states, odd_msbs[i].tail * sizeof(unsigned int));
  568. memcpy(temp_states_even, even_msbs[i].states, even_msbs[i].tail * sizeof(unsigned int));
  569. int res = old_recover(
  570. temp_states_odd,
  571. 0,
  572. odd_msbs[i].tail,
  573. oks,
  574. temp_states_even,
  575. 0,
  576. even_msbs[i].tail,
  577. eks,
  578. 3,
  579. 0,
  580. n,
  581. in >> 16,
  582. 1);
  583. if(res == -1) {
  584. return 1;
  585. }
  586. //odd_msbs[i].tail = 0;
  587. //even_msbs[i].tail = 0;
  588. }
  589. return 0;
  590. }
  591. bool recover(MfClassicNonce* n, int ks2, unsigned int in, ProgramState* program_state) {
  592. bool found = false;
  593. unsigned int* states_buffer = malloc(sizeof(unsigned int) * (2 << 9));
  594. struct Msb* odd_msbs = (struct Msb*)malloc(MSB_LIMIT * sizeof(struct Msb));
  595. struct Msb* even_msbs = (struct Msb*)malloc(MSB_LIMIT * sizeof(struct Msb));
  596. unsigned int* temp_states_odd = malloc(sizeof(unsigned int) * (1280));
  597. unsigned int* temp_states_even = malloc(sizeof(unsigned int) * (1280));
  598. int oks = 0, eks = 0;
  599. int i = 0, msb = 0;
  600. for(i = 31; i >= 0; i -= 2) {
  601. oks = oks << 1 | BEBIT(ks2, i);
  602. }
  603. for(i = 30; i >= 0; i -= 2) {
  604. eks = eks << 1 | BEBIT(ks2, i);
  605. }
  606. int bench_start = furi_hal_rtc_get_timestamp();
  607. program_state->eta_total = eta_total_time;
  608. program_state->eta_timestamp = bench_start;
  609. for(msb = 0; msb <= ((256 / MSB_LIMIT) - 1); msb++) {
  610. program_state->search = msb;
  611. program_state->eta_round = eta_round_time;
  612. program_state->eta_total = eta_total_time - (eta_round_time * msb);
  613. if(calculate_msb_tables(
  614. oks,
  615. eks,
  616. msb,
  617. n,
  618. states_buffer,
  619. odd_msbs,
  620. even_msbs,
  621. temp_states_odd,
  622. temp_states_even,
  623. in,
  624. program_state)) {
  625. int bench_stop = furi_hal_rtc_get_timestamp();
  626. FURI_LOG_I(TAG, "Cracked in %i seconds", bench_stop - bench_start);
  627. found = true;
  628. break;
  629. }
  630. if(program_state->close_thread_please) {
  631. break;
  632. }
  633. }
  634. free(states_buffer);
  635. free(odd_msbs);
  636. free(even_msbs);
  637. free(temp_states_odd);
  638. free(temp_states_even);
  639. return found;
  640. }
  641. bool napi_mf_classic_dict_check_presence(MfClassicDictType dict_type) {
  642. Storage* storage = furi_record_open(RECORD_STORAGE);
  643. bool dict_present = false;
  644. if(dict_type == MfClassicDictTypeSystem) {
  645. dict_present = storage_common_stat(storage, MF_CLASSIC_DICT_FLIPPER_PATH, NULL) == FSE_OK;
  646. } else if(dict_type == MfClassicDictTypeUser) {
  647. dict_present = storage_common_stat(storage, MF_CLASSIC_DICT_USER_PATH, NULL) == FSE_OK;
  648. }
  649. furi_record_close(RECORD_STORAGE);
  650. return dict_present;
  651. }
  652. MfClassicDict* napi_mf_classic_dict_alloc(MfClassicDictType dict_type) {
  653. MfClassicDict* dict = malloc(sizeof(MfClassicDict));
  654. Storage* storage = furi_record_open(RECORD_STORAGE);
  655. dict->stream = buffered_file_stream_alloc(storage);
  656. furi_record_close(RECORD_STORAGE);
  657. bool dict_loaded = false;
  658. do {
  659. if(dict_type == MfClassicDictTypeSystem) {
  660. if(!buffered_file_stream_open(
  661. dict->stream,
  662. MF_CLASSIC_DICT_FLIPPER_PATH,
  663. FSAM_READ_WRITE,
  664. FSOM_OPEN_EXISTING)) {
  665. buffered_file_stream_close(dict->stream);
  666. break;
  667. }
  668. } else if(dict_type == MfClassicDictTypeUser) {
  669. if(!buffered_file_stream_open(
  670. dict->stream, MF_CLASSIC_DICT_USER_PATH, FSAM_READ_WRITE, FSOM_OPEN_ALWAYS)) {
  671. buffered_file_stream_close(dict->stream);
  672. break;
  673. }
  674. }
  675. // Check for newline ending
  676. if(!stream_eof(dict->stream)) {
  677. if(!stream_seek(dict->stream, -1, StreamOffsetFromEnd)) break;
  678. uint8_t last_char = 0;
  679. if(stream_read(dict->stream, &last_char, 1) != 1) break;
  680. if(last_char != '\n') {
  681. FURI_LOG_D(TAG, "Adding new line ending");
  682. if(stream_write_char(dict->stream, '\n') != 1) break;
  683. }
  684. if(!stream_rewind(dict->stream)) break;
  685. }
  686. // Read total amount of keys
  687. FuriString* next_line;
  688. next_line = furi_string_alloc();
  689. while(true) {
  690. if(!stream_read_line(dict->stream, next_line)) {
  691. FURI_LOG_T(TAG, "No keys left in dict");
  692. break;
  693. }
  694. FURI_LOG_T(
  695. TAG,
  696. "Read line: %s, len: %zu",
  697. furi_string_get_cstr(next_line),
  698. furi_string_size(next_line));
  699. if(furi_string_get_char(next_line, 0) == '#') continue;
  700. if(furi_string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue;
  701. dict->total_keys++;
  702. }
  703. furi_string_free(next_line);
  704. stream_rewind(dict->stream);
  705. dict_loaded = true;
  706. FURI_LOG_I(TAG, "Loaded dictionary with %lu keys", dict->total_keys);
  707. } while(false);
  708. if(!dict_loaded) {
  709. // TODO: Does this close twice?
  710. buffered_file_stream_close(dict->stream);
  711. //stream_free(dict->stream);
  712. free(dict);
  713. dict = NULL;
  714. }
  715. return dict;
  716. }
  717. bool napi_mf_classic_dict_add_key_str(MfClassicDict* dict, FuriString* key) {
  718. furi_assert(dict);
  719. furi_assert(dict->stream);
  720. FURI_LOG_I(TAG, "Saving key: %s", furi_string_get_cstr(key));
  721. furi_string_cat_printf(key, "\n");
  722. bool key_added = false;
  723. do {
  724. if(!stream_seek(dict->stream, 0, StreamOffsetFromEnd)) break;
  725. if(!stream_insert_string(dict->stream, key)) break;
  726. dict->total_keys++;
  727. key_added = true;
  728. } while(false);
  729. furi_string_left(key, 12);
  730. return key_added;
  731. }
  732. void napi_mf_classic_dict_free(MfClassicDict* dict) {
  733. // TODO: Track free state at the time this is called to ensure double free does not happen
  734. furi_assert(dict);
  735. furi_assert(dict->stream);
  736. buffered_file_stream_close(dict->stream);
  737. stream_free(dict->stream);
  738. free(dict);
  739. }
  740. static void napi_mf_classic_dict_int_to_str(uint8_t* key_int, FuriString* key_str) {
  741. furi_string_reset(key_str);
  742. for(size_t i = 0; i < 6; i++) {
  743. furi_string_cat_printf(key_str, "%02X", key_int[i]);
  744. }
  745. }
  746. static void napi_mf_classic_dict_str_to_int(FuriString* key_str, uint64_t* key_int) {
  747. uint8_t key_byte_tmp;
  748. *key_int = 0ULL;
  749. for(uint8_t i = 0; i < 12; i += 2) {
  750. args_char_to_hex(
  751. furi_string_get_char(key_str, i), furi_string_get_char(key_str, i + 1), &key_byte_tmp);
  752. *key_int |= (uint64_t)key_byte_tmp << (8 * (5 - i / 2));
  753. }
  754. }
  755. uint32_t napi_mf_classic_dict_get_total_keys(MfClassicDict* dict) {
  756. furi_assert(dict);
  757. return dict->total_keys;
  758. }
  759. bool napi_mf_classic_dict_rewind(MfClassicDict* dict) {
  760. furi_assert(dict);
  761. furi_assert(dict->stream);
  762. return stream_rewind(dict->stream);
  763. }
  764. bool napi_mf_classic_dict_get_next_key_str(MfClassicDict* dict, FuriString* key) {
  765. furi_assert(dict);
  766. furi_assert(dict->stream);
  767. bool key_read = false;
  768. furi_string_reset(key);
  769. while(!key_read) {
  770. if(!stream_read_line(dict->stream, key)) break;
  771. if(furi_string_get_char(key, 0) == '#') continue;
  772. if(furi_string_size(key) != NFC_MF_CLASSIC_KEY_LEN) continue;
  773. furi_string_left(key, 12);
  774. key_read = true;
  775. }
  776. return key_read;
  777. }
  778. bool napi_mf_classic_dict_get_next_key(MfClassicDict* dict, uint64_t* key) {
  779. furi_assert(dict);
  780. furi_assert(dict->stream);
  781. FuriString* temp_key;
  782. temp_key = furi_string_alloc();
  783. bool key_read = napi_mf_classic_dict_get_next_key_str(dict, temp_key);
  784. if(key_read) {
  785. napi_mf_classic_dict_str_to_int(temp_key, key);
  786. }
  787. furi_string_free(temp_key);
  788. return key_read;
  789. }
  790. bool napi_mf_classic_dict_is_key_present_str(MfClassicDict* dict, FuriString* key) {
  791. furi_assert(dict);
  792. furi_assert(dict->stream);
  793. FuriString* next_line;
  794. next_line = furi_string_alloc();
  795. bool key_found = false;
  796. stream_rewind(dict->stream);
  797. while(!key_found) { //-V654
  798. if(!stream_read_line(dict->stream, next_line)) break;
  799. if(furi_string_get_char(next_line, 0) == '#') continue;
  800. if(furi_string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue;
  801. furi_string_left(next_line, 12);
  802. if(!furi_string_equal(key, next_line)) continue;
  803. key_found = true;
  804. }
  805. furi_string_free(next_line);
  806. return key_found;
  807. }
  808. bool napi_mf_classic_dict_is_key_present(MfClassicDict* dict, uint8_t* key) {
  809. FuriString* temp_key;
  810. temp_key = furi_string_alloc();
  811. napi_mf_classic_dict_int_to_str(key, temp_key);
  812. bool key_found = napi_mf_classic_dict_is_key_present_str(dict, temp_key);
  813. furi_string_free(temp_key);
  814. return key_found;
  815. }
  816. bool napi_key_already_found_for_nonce(MfClassicDict* dict, MfClassicNonce* nonce) {
  817. bool found = false;
  818. uint64_t k = 0;
  819. napi_mf_classic_dict_rewind(dict);
  820. while(napi_mf_classic_dict_get_next_key(dict, &k)) {
  821. struct Crypto1State temp = {0, 0};
  822. int i;
  823. for(i = 0; i < 24; i++) {
  824. (&temp)->odd |= (BIT(k, 2 * i + 1) << (i ^ 3));
  825. (&temp)->even |= (BIT(k, 2 * i) << (i ^ 3));
  826. }
  827. if(nonce->attack == mfkey32) {
  828. crypt_word_noret(&temp, nonce->uid_xor_nt1, 0);
  829. crypt_word_noret(&temp, nonce->nr1_enc, 1);
  830. if(nonce->ar1_enc == (crypt_word(&temp) ^ nonce->p64b)) {
  831. found = true;
  832. break;
  833. }
  834. } else if(nonce->attack == static_nested) {
  835. uint32_t expected_ks1 = crypt_word_ret(&temp, nonce->uid_xor_nt0, 0);
  836. if(nonce->ks1_1_enc == expected_ks1) {
  837. found = true;
  838. break;
  839. }
  840. }
  841. }
  842. return found;
  843. }
  844. bool napi_mf_classic_nonces_check_presence() {
  845. Storage* storage = furi_record_open(RECORD_STORAGE);
  846. bool nonces_present = storage_common_stat(storage, MF_CLASSIC_NONCE_PATH, NULL) == FSE_OK;
  847. furi_record_close(RECORD_STORAGE);
  848. return nonces_present;
  849. }
  850. bool distance_in_nonces_file(const char* file_path, const char* file_name) {
  851. char full_path[MAX_PATH_LEN];
  852. snprintf(full_path, sizeof(full_path), "%s/%s", file_path, file_name);
  853. bool distance_present = false;
  854. Storage* storage = furi_record_open(RECORD_STORAGE);
  855. Stream* file_stream = buffered_file_stream_alloc(storage);
  856. FuriString* line_str;
  857. line_str = furi_string_alloc();
  858. if(buffered_file_stream_open(file_stream, full_path, FSAM_READ, FSOM_OPEN_EXISTING)) {
  859. while(true) {
  860. if(!stream_read_line(file_stream, line_str)) break;
  861. if(furi_string_search_str(line_str, "distance") != FURI_STRING_FAILURE) {
  862. distance_present = true;
  863. break;
  864. }
  865. }
  866. }
  867. buffered_file_stream_close(file_stream);
  868. stream_free(file_stream);
  869. furi_string_free(line_str);
  870. furi_record_close(RECORD_STORAGE);
  871. return distance_present;
  872. }
  873. bool napi_mf_classic_nested_nonces_check_presence() {
  874. Storage* storage = furi_record_open(RECORD_STORAGE);
  875. if(!(storage_dir_exists(storage, MF_CLASSIC_NESTED_NONCE_PATH))) {
  876. furi_record_close(RECORD_STORAGE);
  877. return false;
  878. }
  879. bool nonces_present = false;
  880. File* dir = storage_file_alloc(storage);
  881. char filename_buffer[MAX_NAME_LEN];
  882. FileInfo file_info;
  883. if(storage_dir_open(dir, MF_CLASSIC_NESTED_NONCE_PATH)) {
  884. while(storage_dir_read(dir, &file_info, filename_buffer, MAX_NAME_LEN)) {
  885. // We only care about Static Nested files
  886. if(!(file_info.flags & FSF_DIRECTORY) && strstr(filename_buffer, ".nonces") &&
  887. !(distance_in_nonces_file(MF_CLASSIC_NESTED_NONCE_PATH, filename_buffer))) {
  888. nonces_present = true;
  889. break;
  890. }
  891. }
  892. }
  893. storage_dir_close(dir);
  894. storage_file_free(dir);
  895. furi_record_close(RECORD_STORAGE);
  896. return nonces_present;
  897. }
  898. bool load_mfkey32_nonces(
  899. MfClassicNonceArray* nonce_array,
  900. ProgramState* program_state,
  901. MfClassicDict* system_dict,
  902. bool system_dict_exists,
  903. MfClassicDict* user_dict) {
  904. bool array_loaded = false;
  905. do {
  906. // https://github.com/flipperdevices/flipperzero-firmware/blob/5134f44c09d39344a8747655c0d59864bb574b96/applications/services/storage/filesystem_api_defines.h#L8-L22
  907. if(!buffered_file_stream_open(
  908. nonce_array->stream, MF_CLASSIC_NONCE_PATH, FSAM_READ_WRITE, FSOM_OPEN_EXISTING)) {
  909. buffered_file_stream_close(nonce_array->stream);
  910. break;
  911. }
  912. // Check for newline ending
  913. if(!stream_eof(nonce_array->stream)) {
  914. if(!stream_seek(nonce_array->stream, -1, StreamOffsetFromEnd)) break;
  915. uint8_t last_char = 0;
  916. if(stream_read(nonce_array->stream, &last_char, 1) != 1) break;
  917. if(last_char != '\n') {
  918. FURI_LOG_D(TAG, "Adding new line ending");
  919. if(stream_write_char(nonce_array->stream, '\n') != 1) break;
  920. }
  921. if(!stream_rewind(nonce_array->stream)) break;
  922. }
  923. // Read total amount of nonces
  924. FuriString* next_line;
  925. next_line = furi_string_alloc();
  926. while(!(program_state->close_thread_please)) {
  927. if(!stream_read_line(nonce_array->stream, next_line)) {
  928. FURI_LOG_T(TAG, "No nonces left");
  929. break;
  930. }
  931. FURI_LOG_T(
  932. TAG,
  933. "Read line: %s, len: %zu",
  934. furi_string_get_cstr(next_line),
  935. furi_string_size(next_line));
  936. if(!furi_string_start_with_str(next_line, "Sec")) continue;
  937. const char* next_line_cstr = furi_string_get_cstr(next_line);
  938. MfClassicNonce res = {0};
  939. res.attack = mfkey32;
  940. int i = 0;
  941. char* endptr;
  942. for(i = 0; i <= 17; i++) {
  943. if(i != 0) {
  944. next_line_cstr = strchr(next_line_cstr, ' ');
  945. if(next_line_cstr) {
  946. next_line_cstr++;
  947. } else {
  948. break;
  949. }
  950. }
  951. unsigned long value = strtoul(next_line_cstr, &endptr, 16);
  952. switch(i) {
  953. case 5:
  954. res.uid = value;
  955. break;
  956. case 7:
  957. res.nt0 = value;
  958. break;
  959. case 9:
  960. res.nr0_enc = value;
  961. break;
  962. case 11:
  963. res.ar0_enc = value;
  964. break;
  965. case 13:
  966. res.nt1 = value;
  967. break;
  968. case 15:
  969. res.nr1_enc = value;
  970. break;
  971. case 17:
  972. res.ar1_enc = value;
  973. break;
  974. default:
  975. break; // Do nothing
  976. }
  977. next_line_cstr = endptr;
  978. }
  979. res.p64 = prng_successor(res.nt0, 64);
  980. res.p64b = prng_successor(res.nt1, 64);
  981. res.uid_xor_nt0 = res.uid ^ res.nt0;
  982. res.uid_xor_nt1 = res.uid ^ res.nt1;
  983. (program_state->total)++;
  984. if((system_dict_exists && napi_key_already_found_for_nonce(system_dict, &res)) ||
  985. (napi_key_already_found_for_nonce(user_dict, &res))) {
  986. (program_state->cracked)++;
  987. (program_state->num_completed)++;
  988. continue;
  989. }
  990. FURI_LOG_I(TAG, "No key found for %8lx %8lx", res.uid, res.ar1_enc);
  991. // TODO: Refactor
  992. nonce_array->remaining_nonce_array = realloc( //-V701
  993. nonce_array->remaining_nonce_array,
  994. sizeof(MfClassicNonce) * ((nonce_array->remaining_nonces) + 1));
  995. nonce_array->remaining_nonces++;
  996. nonce_array->remaining_nonce_array[(nonce_array->remaining_nonces) - 1] = res;
  997. nonce_array->total_nonces++;
  998. }
  999. furi_string_free(next_line);
  1000. buffered_file_stream_close(nonce_array->stream);
  1001. //stream_free(nonce_array->stream);
  1002. array_loaded = true;
  1003. FURI_LOG_I(TAG, "Loaded %lu Mfkey32 nonces", nonce_array->total_nonces);
  1004. } while(false);
  1005. return array_loaded;
  1006. }
  1007. int binaryStringToInt(const char* binStr) {
  1008. int result = 0;
  1009. while(*binStr) {
  1010. result <<= 1;
  1011. if(*binStr == '1') {
  1012. result |= 1;
  1013. }
  1014. binStr++;
  1015. }
  1016. return result;
  1017. }
  1018. bool load_nested_nonces(
  1019. MfClassicNonceArray* nonce_array,
  1020. ProgramState* program_state,
  1021. MfClassicDict* system_dict,
  1022. bool system_dict_exists,
  1023. MfClassicDict* user_dict) {
  1024. Storage* storage = furi_record_open(RECORD_STORAGE);
  1025. File* dir = storage_file_alloc(storage);
  1026. char filename_buffer[MAX_NAME_LEN];
  1027. FileInfo file_info;
  1028. FuriString* next_line = furi_string_alloc();
  1029. if(!storage_dir_open(dir, MF_CLASSIC_NESTED_NONCE_PATH)) {
  1030. storage_dir_close(dir);
  1031. storage_file_free(dir);
  1032. furi_record_close(RECORD_STORAGE);
  1033. furi_string_free(next_line);
  1034. return false;
  1035. }
  1036. while(storage_dir_read(dir, &file_info, filename_buffer, MAX_NAME_LEN)) {
  1037. if(!(file_info.flags & FSF_DIRECTORY) && strstr(filename_buffer, ".nonces") &&
  1038. !(distance_in_nonces_file(MF_CLASSIC_NESTED_NONCE_PATH, filename_buffer))) {
  1039. char full_path[MAX_PATH_LEN];
  1040. snprintf(
  1041. full_path,
  1042. sizeof(full_path),
  1043. "%s/%s",
  1044. MF_CLASSIC_NESTED_NONCE_PATH,
  1045. filename_buffer);
  1046. // TODO: We should only need READ_WRITE here if we plan on adding a newline to the end of the file if has none
  1047. if(!buffered_file_stream_open(
  1048. nonce_array->stream, full_path, FSAM_READ_WRITE, FSOM_OPEN_EXISTING)) {
  1049. buffered_file_stream_close(nonce_array->stream);
  1050. continue;
  1051. }
  1052. while(stream_read_line(nonce_array->stream, next_line)) {
  1053. if(furi_string_search_str(next_line, "Nested:") != FURI_STRING_FAILURE) {
  1054. MfClassicNonce res = {0};
  1055. res.attack = static_nested;
  1056. int parsed = sscanf(
  1057. furi_string_get_cstr(next_line),
  1058. "Nested: %*s %*s cuid 0x%" PRIx32 " nt0 0x%" PRIx32 " ks0 0x%" PRIx32
  1059. " par0 %4[01] nt1 0x%" PRIx32 " ks1 0x%" PRIx32 " par1 %4[01]",
  1060. &res.uid,
  1061. &res.nt0,
  1062. &res.ks1_1_enc,
  1063. res.par_1_str,
  1064. &res.nt1,
  1065. &res.ks1_2_enc,
  1066. res.par_2_str);
  1067. if(parsed != 7) continue;
  1068. res.par_1 = binaryStringToInt(res.par_1_str);
  1069. res.par_2 = binaryStringToInt(res.par_2_str);
  1070. res.uid_xor_nt0 = res.uid ^ res.nt0;
  1071. res.uid_xor_nt1 = res.uid ^ res.nt1;
  1072. (program_state->total)++;
  1073. if((system_dict_exists &&
  1074. napi_key_already_found_for_nonce(system_dict, &res)) ||
  1075. (napi_key_already_found_for_nonce(user_dict, &res))) {
  1076. (program_state->cracked)++;
  1077. (program_state->num_completed)++;
  1078. continue;
  1079. }
  1080. nonce_array->remaining_nonce_array = realloc(
  1081. nonce_array->remaining_nonce_array,
  1082. sizeof(MfClassicNonce) * (nonce_array->remaining_nonces + 1));
  1083. nonce_array->remaining_nonce_array[nonce_array->remaining_nonces] = res;
  1084. nonce_array->remaining_nonces++;
  1085. nonce_array->total_nonces++;
  1086. }
  1087. }
  1088. buffered_file_stream_close(nonce_array->stream);
  1089. }
  1090. }
  1091. storage_dir_close(dir);
  1092. storage_file_free(dir);
  1093. furi_record_close(RECORD_STORAGE);
  1094. furi_string_free(next_line);
  1095. FURI_LOG_I(TAG, "Loaded %lu Static Nested nonces", nonce_array->total_nonces);
  1096. return true;
  1097. }
  1098. MfClassicNonceArray* napi_mf_classic_nonce_array_alloc(
  1099. MfClassicDict* system_dict,
  1100. bool system_dict_exists,
  1101. MfClassicDict* user_dict,
  1102. ProgramState* program_state) {
  1103. MfClassicNonceArray* nonce_array = malloc(sizeof(MfClassicNonceArray));
  1104. MfClassicNonce* remaining_nonce_array_init = malloc(sizeof(MfClassicNonce) * 1);
  1105. nonce_array->remaining_nonce_array = remaining_nonce_array_init;
  1106. Storage* storage = furi_record_open(RECORD_STORAGE);
  1107. nonce_array->stream = buffered_file_stream_alloc(storage);
  1108. furi_record_close(RECORD_STORAGE);
  1109. bool array_loaded = false;
  1110. if(program_state->mfkey32_present) {
  1111. array_loaded = load_mfkey32_nonces(
  1112. nonce_array, program_state, system_dict, system_dict_exists, user_dict);
  1113. }
  1114. if(program_state->nested_present) {
  1115. array_loaded |= load_nested_nonces(
  1116. nonce_array, program_state, system_dict, system_dict_exists, user_dict);
  1117. }
  1118. if(!array_loaded) {
  1119. free(nonce_array);
  1120. nonce_array = NULL;
  1121. }
  1122. return nonce_array;
  1123. }
  1124. void napi_mf_classic_nonce_array_free(MfClassicNonceArray* nonce_array) {
  1125. // TODO: Track free state at the time this is called to ensure double free does not happen
  1126. furi_assert(nonce_array);
  1127. furi_assert(nonce_array->stream);
  1128. buffered_file_stream_close(nonce_array->stream);
  1129. stream_free(nonce_array->stream);
  1130. free(nonce_array);
  1131. }
  1132. static void finished_beep() {
  1133. // Beep to indicate completion
  1134. NotificationApp* notification = furi_record_open("notification");
  1135. notification_message(notification, &sequence_audiovisual_alert);
  1136. notification_message(notification, &sequence_display_backlight_on);
  1137. furi_record_close("notification");
  1138. }
  1139. void mfkey(ProgramState* program_state) {
  1140. uint64_t found_key; // recovered key
  1141. size_t keyarray_size = 0;
  1142. uint64_t* keyarray = malloc(sizeof(uint64_t) * 1);
  1143. uint32_t i = 0, j = 0;
  1144. // Check for nonces
  1145. program_state->mfkey32_present = napi_mf_classic_nonces_check_presence();
  1146. program_state->nested_present = napi_mf_classic_nested_nonces_check_presence();
  1147. if(!(program_state->mfkey32_present) && !(program_state->nested_present)) {
  1148. program_state->err = MissingNonces;
  1149. program_state->mfkey_state = Error;
  1150. free(keyarray);
  1151. return;
  1152. }
  1153. // Read dictionaries (optional)
  1154. MfClassicDict* system_dict = {0};
  1155. bool system_dict_exists = napi_mf_classic_dict_check_presence(MfClassicDictTypeSystem);
  1156. MfClassicDict* user_dict = {0};
  1157. bool user_dict_exists = napi_mf_classic_dict_check_presence(MfClassicDictTypeUser);
  1158. uint32_t total_dict_keys = 0;
  1159. if(system_dict_exists) {
  1160. system_dict = napi_mf_classic_dict_alloc(MfClassicDictTypeSystem);
  1161. total_dict_keys += napi_mf_classic_dict_get_total_keys(system_dict);
  1162. }
  1163. user_dict = napi_mf_classic_dict_alloc(MfClassicDictTypeUser);
  1164. if(user_dict_exists) {
  1165. total_dict_keys += napi_mf_classic_dict_get_total_keys(user_dict);
  1166. }
  1167. user_dict_exists = true;
  1168. program_state->dict_count = total_dict_keys;
  1169. program_state->mfkey_state = DictionaryAttack;
  1170. // Read nonces
  1171. MfClassicNonceArray* nonce_arr;
  1172. nonce_arr = napi_mf_classic_nonce_array_alloc(
  1173. system_dict, system_dict_exists, user_dict, program_state);
  1174. if(system_dict_exists) {
  1175. napi_mf_classic_dict_free(system_dict);
  1176. }
  1177. if(nonce_arr->total_nonces == 0) {
  1178. // Nothing to crack
  1179. program_state->err = ZeroNonces;
  1180. program_state->mfkey_state = Error;
  1181. napi_mf_classic_nonce_array_free(nonce_arr);
  1182. napi_mf_classic_dict_free(user_dict);
  1183. free(keyarray);
  1184. return;
  1185. }
  1186. if(memmgr_get_free_heap() < MIN_RAM) {
  1187. // System has less than the guaranteed amount of RAM (140 KB) - adjust some parameters to run anyway at half speed
  1188. eta_round_time *= 2;
  1189. eta_total_time *= 2;
  1190. MSB_LIMIT /= 2;
  1191. }
  1192. program_state->mfkey_state = MFKeyAttack;
  1193. // TODO: Work backwards on this array and free memory
  1194. for(i = 0; i < nonce_arr->total_nonces; i++) {
  1195. MfClassicNonce next_nonce = nonce_arr->remaining_nonce_array[i];
  1196. if(key_already_found_for_nonce(keyarray, keyarray_size, &next_nonce)) {
  1197. nonce_arr->remaining_nonces--;
  1198. (program_state->cracked)++;
  1199. (program_state->num_completed)++;
  1200. continue;
  1201. }
  1202. FURI_LOG_I(TAG, "Beginning recovery for %8lx", next_nonce.uid);
  1203. if(next_nonce.attack == mfkey32) {
  1204. if(!recover(&next_nonce, next_nonce.ar0_enc ^ next_nonce.p64, 0, program_state)) {
  1205. if(program_state->close_thread_please) {
  1206. break;
  1207. }
  1208. // No key found in recover()
  1209. (program_state->num_completed)++;
  1210. continue;
  1211. }
  1212. } else if(next_nonce.attack == static_nested) {
  1213. if(!recover(
  1214. &next_nonce,
  1215. next_nonce.ks1_2_enc,
  1216. next_nonce.nt1 ^ next_nonce.uid,
  1217. program_state)) {
  1218. if(program_state->close_thread_please) {
  1219. break;
  1220. }
  1221. // No key found in recover()
  1222. (program_state->num_completed)++;
  1223. continue;
  1224. }
  1225. }
  1226. (program_state->cracked)++;
  1227. (program_state->num_completed)++;
  1228. found_key = next_nonce.key;
  1229. bool already_found = false;
  1230. for(j = 0; j < keyarray_size; j++) {
  1231. if(keyarray[j] == found_key) {
  1232. already_found = true;
  1233. break;
  1234. }
  1235. }
  1236. if(already_found == false) {
  1237. // New key
  1238. keyarray = realloc(keyarray, sizeof(uint64_t) * (keyarray_size + 1)); //-V701
  1239. keyarray_size += 1;
  1240. keyarray[keyarray_size - 1] = found_key;
  1241. (program_state->unique_cracked)++;
  1242. }
  1243. }
  1244. // TODO: Update display to show all keys were found
  1245. // TODO: Prepend found key(s) to user dictionary file
  1246. //FURI_LOG_I(TAG, "Unique keys found:");
  1247. for(i = 0; i < keyarray_size; i++) {
  1248. //FURI_LOG_I(TAG, "%012" PRIx64, keyarray[i]);
  1249. FuriString* temp_key = furi_string_alloc();
  1250. furi_string_cat_printf(temp_key, "%012" PRIX64, keyarray[i]);
  1251. napi_mf_classic_dict_add_key_str(user_dict, temp_key);
  1252. furi_string_free(temp_key);
  1253. }
  1254. if(keyarray_size > 0) {
  1255. // TODO: Should we use DolphinDeedNfcMfcAdd?
  1256. dolphin_deed(DolphinDeedNfcMfcAdd);
  1257. }
  1258. napi_mf_classic_nonce_array_free(nonce_arr);
  1259. napi_mf_classic_dict_free(user_dict);
  1260. free(keyarray);
  1261. //FURI_LOG_I(TAG, "mfkey function completed normally"); // DEBUG
  1262. program_state->mfkey_state = Complete;
  1263. // No need to alert the user if they asked it to stop
  1264. if(!(program_state->close_thread_please)) {
  1265. finished_beep();
  1266. }
  1267. return;
  1268. }
  1269. // Screen is 128x64 px
  1270. static void render_callback(Canvas* const canvas, void* ctx) {
  1271. furi_assert(ctx);
  1272. ProgramState* program_state = ctx;
  1273. furi_mutex_acquire(program_state->mutex, FuriWaitForever);
  1274. char draw_str[44] = {};
  1275. canvas_clear(canvas);
  1276. canvas_draw_frame(canvas, 0, 0, 128, 64);
  1277. canvas_draw_frame(canvas, 0, 15, 128, 64);
  1278. canvas_set_font(canvas, FontPrimary);
  1279. canvas_draw_str_aligned(canvas, 5, 4, AlignLeft, AlignTop, "MFKey");
  1280. canvas_draw_icon(canvas, 114, 4, &I_mfkey);
  1281. if(program_state->is_thread_running && program_state->mfkey_state == MFKeyAttack) {
  1282. float eta_round = (float)1 - ((float)program_state->eta_round / (float)eta_round_time);
  1283. float eta_total = (float)1 - ((float)program_state->eta_total / (float)eta_total_time);
  1284. float progress = (float)program_state->num_completed / (float)program_state->total;
  1285. if(eta_round < 0) {
  1286. // Round ETA miscalculated
  1287. eta_round = 1;
  1288. program_state->eta_round = 0;
  1289. }
  1290. if(eta_total < 0) {
  1291. // Total ETA miscalculated
  1292. eta_total = 1;
  1293. program_state->eta_total = 0;
  1294. }
  1295. canvas_set_font(canvas, FontSecondary);
  1296. snprintf(
  1297. draw_str,
  1298. sizeof(draw_str),
  1299. "Cracking: %d/%d - in prog.",
  1300. program_state->num_completed,
  1301. program_state->total);
  1302. elements_progress_bar_with_text(canvas, 5, 18, 118, progress, draw_str);
  1303. snprintf(
  1304. draw_str,
  1305. sizeof(draw_str),
  1306. "Round: %d/%d - ETA %02d Sec",
  1307. (program_state->search) + 1, // Zero indexed
  1308. 256 / MSB_LIMIT,
  1309. program_state->eta_round);
  1310. elements_progress_bar_with_text(canvas, 5, 31, 118, eta_round, draw_str);
  1311. snprintf(draw_str, sizeof(draw_str), "Total ETA %03d Sec", program_state->eta_total);
  1312. elements_progress_bar_with_text(canvas, 5, 44, 118, eta_total, draw_str);
  1313. } else if(program_state->is_thread_running && program_state->mfkey_state == DictionaryAttack) {
  1314. canvas_set_font(canvas, FontSecondary);
  1315. snprintf(
  1316. draw_str, sizeof(draw_str), "Dict solves: %d (in progress)", program_state->cracked);
  1317. canvas_draw_str_aligned(canvas, 10, 18, AlignLeft, AlignTop, draw_str);
  1318. snprintf(draw_str, sizeof(draw_str), "Keys in dict: %d", program_state->dict_count);
  1319. canvas_draw_str_aligned(canvas, 26, 28, AlignLeft, AlignTop, draw_str);
  1320. } else if(program_state->mfkey_state == Complete) {
  1321. // TODO: Scrollable list view to see cracked keys if user presses down
  1322. elements_progress_bar_with_text(canvas, 5, 18, 118, 1, draw_str);
  1323. canvas_set_font(canvas, FontSecondary);
  1324. snprintf(draw_str, sizeof(draw_str), "Complete");
  1325. canvas_draw_str_aligned(canvas, 40, 31, AlignLeft, AlignTop, draw_str);
  1326. snprintf(
  1327. draw_str,
  1328. sizeof(draw_str),
  1329. "Keys added to user dict: %d",
  1330. program_state->unique_cracked);
  1331. canvas_draw_str_aligned(canvas, 10, 41, AlignLeft, AlignTop, draw_str);
  1332. } else if(program_state->mfkey_state == Ready) {
  1333. canvas_set_font(canvas, FontSecondary);
  1334. canvas_draw_str_aligned(canvas, 50, 30, AlignLeft, AlignTop, "Ready");
  1335. elements_button_center(canvas, "Start");
  1336. elements_button_right(canvas, "Help");
  1337. } else if(program_state->mfkey_state == Help) {
  1338. canvas_set_font(canvas, FontSecondary);
  1339. canvas_draw_str_aligned(canvas, 7, 20, AlignLeft, AlignTop, "Collect nonces using Detect");
  1340. canvas_draw_str_aligned(canvas, 7, 30, AlignLeft, AlignTop, "Reader or FlipperNested.");
  1341. canvas_draw_str_aligned(canvas, 7, 40, AlignLeft, AlignTop, "Devs: noproto, AG, ALiberty");
  1342. canvas_draw_str_aligned(canvas, 7, 50, AlignLeft, AlignTop, "Thanks: bettse, Foxushka");
  1343. } else if(program_state->mfkey_state == Error) {
  1344. canvas_draw_str_aligned(canvas, 50, 25, AlignLeft, AlignTop, "Error");
  1345. canvas_set_font(canvas, FontSecondary);
  1346. if(program_state->err == MissingNonces) {
  1347. canvas_draw_str_aligned(canvas, 25, 36, AlignLeft, AlignTop, "No nonces found");
  1348. } else if(program_state->err == ZeroNonces) {
  1349. canvas_draw_str_aligned(canvas, 15, 36, AlignLeft, AlignTop, "Nonces already cracked");
  1350. } else {
  1351. // Unhandled error
  1352. }
  1353. } else {
  1354. // Unhandled program state
  1355. }
  1356. furi_mutex_release(program_state->mutex);
  1357. }
  1358. static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
  1359. furi_assert(event_queue);
  1360. PluginEvent event = {.type = EventTypeKey, .input = *input_event};
  1361. furi_message_queue_put(event_queue, &event, FuriWaitForever);
  1362. }
  1363. static void mfkey_state_init(ProgramState* program_state) {
  1364. program_state->is_thread_running = false;
  1365. program_state->mfkey_state = Ready;
  1366. program_state->cracked = 0;
  1367. program_state->unique_cracked = 0;
  1368. program_state->num_completed = 0;
  1369. program_state->total = 0;
  1370. program_state->dict_count = 0;
  1371. }
  1372. // Entrypoint for worker thread
  1373. static int32_t mfkey_worker_thread(void* ctx) {
  1374. ProgramState* program_state = ctx;
  1375. program_state->is_thread_running = true;
  1376. program_state->mfkey_state = Initializing;
  1377. //FURI_LOG_I(TAG, "Hello from the mfkey worker thread"); // DEBUG
  1378. mfkey(program_state);
  1379. program_state->is_thread_running = false;
  1380. return 0;
  1381. }
  1382. void start_mfkey_thread(ProgramState* program_state) {
  1383. if(!program_state->is_thread_running) {
  1384. furi_thread_start(program_state->mfkeythread);
  1385. }
  1386. }
  1387. int32_t mfkey_main() {
  1388. FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent));
  1389. ProgramState* program_state = malloc(sizeof(ProgramState));
  1390. mfkey_state_init(program_state);
  1391. program_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
  1392. if(!program_state->mutex) {
  1393. FURI_LOG_E(TAG, "cannot create mutex\r\n");
  1394. free(program_state);
  1395. return 255;
  1396. }
  1397. // Set system callbacks
  1398. ViewPort* view_port = view_port_alloc();
  1399. view_port_draw_callback_set(view_port, render_callback, program_state);
  1400. view_port_input_callback_set(view_port, input_callback, event_queue);
  1401. // Open GUI and register view_port
  1402. Gui* gui = furi_record_open(RECORD_GUI);
  1403. gui_add_view_port(gui, view_port, GuiLayerFullscreen);
  1404. program_state->mfkeythread = furi_thread_alloc();
  1405. furi_thread_set_name(program_state->mfkeythread, "MFKey Worker");
  1406. furi_thread_set_stack_size(program_state->mfkeythread, 2048);
  1407. furi_thread_set_context(program_state->mfkeythread, program_state);
  1408. furi_thread_set_callback(program_state->mfkeythread, mfkey_worker_thread);
  1409. PluginEvent event;
  1410. for(bool main_loop = true; main_loop;) {
  1411. FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
  1412. furi_mutex_acquire(program_state->mutex, FuriWaitForever);
  1413. if(event_status == FuriStatusOk) {
  1414. // press events
  1415. if(event.type == EventTypeKey) {
  1416. if(event.input.type == InputTypePress) {
  1417. switch(event.input.key) {
  1418. case InputKeyUp:
  1419. break;
  1420. case InputKeyDown:
  1421. break;
  1422. case InputKeyRight:
  1423. if(!program_state->is_thread_running &&
  1424. program_state->mfkey_state == Ready) {
  1425. program_state->mfkey_state = Help;
  1426. view_port_update(view_port);
  1427. }
  1428. break;
  1429. case InputKeyLeft:
  1430. break;
  1431. case InputKeyOk:
  1432. if(!program_state->is_thread_running &&
  1433. program_state->mfkey_state == Ready) {
  1434. start_mfkey_thread(program_state);
  1435. view_port_update(view_port);
  1436. }
  1437. break;
  1438. case InputKeyBack:
  1439. if(!program_state->is_thread_running &&
  1440. program_state->mfkey_state == Help) {
  1441. program_state->mfkey_state = Ready;
  1442. view_port_update(view_port);
  1443. } else {
  1444. program_state->close_thread_please = true;
  1445. if(program_state->is_thread_running && program_state->mfkeythread) {
  1446. // Wait until thread is finished
  1447. furi_thread_join(program_state->mfkeythread);
  1448. }
  1449. program_state->close_thread_please = false;
  1450. main_loop = false;
  1451. }
  1452. break;
  1453. default:
  1454. break;
  1455. }
  1456. }
  1457. }
  1458. }
  1459. view_port_update(view_port);
  1460. furi_mutex_release(program_state->mutex);
  1461. }
  1462. furi_thread_free(program_state->mfkeythread);
  1463. view_port_enabled_set(view_port, false);
  1464. gui_remove_view_port(gui, view_port);
  1465. furi_record_close("gui");
  1466. view_port_free(view_port);
  1467. furi_message_queue_free(event_queue);
  1468. furi_mutex_free(program_state->mutex);
  1469. free(program_state);
  1470. return 0;
  1471. }