init_plugin.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. #include <furi_hal.h>
  2. #include <inttypes.h>
  3. #include <toolbox/keys_dict.h>
  4. #include <bit_lib/bit_lib.h>
  5. #include <toolbox/stream/buffered_file_stream.h>
  6. #include <nfc/protocols/mf_classic/mf_classic.h>
  7. #include "mfkey.h"
  8. #include "crypto1.h"
  9. #include "plugin_interface.h"
  10. #include <flipper_application/flipper_application.h>
  11. // TODO: Remove defines that are not needed
  12. #define MF_CLASSIC_NONCE_PATH EXT_PATH("nfc/.mfkey32.log")
  13. #define MF_CLASSIC_NESTED_NONCE_PATH EXT_PATH("nfc/.nested.log")
  14. #define TAG "MFKey"
  15. #define MAX_NAME_LEN 32
  16. #define MAX_PATH_LEN 64
  17. #define LF_POLY_ODD (0x29CE5C)
  18. #define LF_POLY_EVEN (0x870804)
  19. #define CONST_M1_1 (LF_POLY_EVEN << 1 | 1)
  20. #define CONST_M2_1 (LF_POLY_ODD << 1)
  21. #define CONST_M1_2 (LF_POLY_ODD)
  22. #define CONST_M2_2 (LF_POLY_EVEN << 1 | 1)
  23. #define BIT(x, n) ((x) >> (n) & 1)
  24. #define BEBIT(x, n) BIT(x, (n) ^ 24)
  25. #define SWAPENDIAN(x) \
  26. ((x) = ((x) >> 8 & 0xff00ff) | ((x) & 0xff00ff) << 8, (x) = (x) >> 16 | (x) << 16)
  27. bool key_already_found_for_nonce_in_dict(KeysDict* dict, MfClassicNonce* nonce) {
  28. bool found = false;
  29. uint8_t key_bytes[sizeof(MfClassicKey)];
  30. keys_dict_rewind(dict);
  31. while(keys_dict_get_next_key(dict, key_bytes, sizeof(MfClassicKey))) {
  32. uint64_t k = bit_lib_bytes_to_num_be(key_bytes, sizeof(MfClassicKey));
  33. struct Crypto1State temp = {0, 0};
  34. for(int i = 0; i < 24; i++) {
  35. (&temp)->odd |= (BIT(k, 2 * i + 1) << (i ^ 3));
  36. (&temp)->even |= (BIT(k, 2 * i) << (i ^ 3));
  37. }
  38. if(nonce->attack == mfkey32) {
  39. crypt_word_noret(&temp, nonce->uid_xor_nt1, 0);
  40. crypt_word_noret(&temp, nonce->nr1_enc, 1);
  41. if(nonce->ar1_enc == (crypt_word(&temp) ^ nonce->p64b)) {
  42. found = true;
  43. break;
  44. }
  45. } else if(nonce->attack == static_nested) {
  46. uint32_t expected_ks1 = crypt_word_ret(&temp, nonce->uid_xor_nt0, 0);
  47. if(nonce->ks1_1_enc == expected_ks1) {
  48. found = true;
  49. break;
  50. }
  51. }
  52. }
  53. return found;
  54. }
  55. bool napi_mf_classic_mfkey32_nonces_check_presence() {
  56. Storage* storage = furi_record_open(RECORD_STORAGE);
  57. bool nonces_present = storage_common_stat(storage, MF_CLASSIC_NONCE_PATH, NULL) == FSE_OK;
  58. furi_record_close(RECORD_STORAGE);
  59. return nonces_present;
  60. }
  61. bool napi_mf_classic_nested_nonces_check_presence() {
  62. Storage* storage = furi_record_open(RECORD_STORAGE);
  63. Stream* stream = buffered_file_stream_alloc(storage);
  64. bool nonces_present = false;
  65. FuriString* line = furi_string_alloc();
  66. do {
  67. if(!buffered_file_stream_open(
  68. stream, MF_CLASSIC_NESTED_NONCE_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) {
  69. break;
  70. }
  71. while(stream_read_line(stream, line)) {
  72. if(furi_string_search_str(line, "dist 0") != FURI_STRING_FAILURE) {
  73. nonces_present = true;
  74. break;
  75. }
  76. }
  77. } while(false);
  78. furi_string_free(line);
  79. buffered_file_stream_close(stream);
  80. stream_free(stream);
  81. furi_record_close(RECORD_STORAGE);
  82. return nonces_present;
  83. }
  84. int binaryStringToInt(const char* binStr) {
  85. int result = 0;
  86. while(*binStr) {
  87. result <<= 1;
  88. if(*binStr == '1') {
  89. result |= 1;
  90. }
  91. binStr++;
  92. }
  93. return result;
  94. }
  95. bool load_mfkey32_nonces(
  96. MfClassicNonceArray* nonce_array,
  97. ProgramState* program_state,
  98. KeysDict* system_dict,
  99. bool system_dict_exists,
  100. KeysDict* user_dict) {
  101. bool array_loaded = false;
  102. do {
  103. // https://github.com/flipperdevices/flipperzero-firmware/blob/5134f44c09d39344a8747655c0d59864bb574b96/applications/services/storage/filesystem_api_defines.h#L8-L22
  104. if(!buffered_file_stream_open(
  105. nonce_array->stream, MF_CLASSIC_NONCE_PATH, FSAM_READ_WRITE, FSOM_OPEN_EXISTING)) {
  106. buffered_file_stream_close(nonce_array->stream);
  107. break;
  108. }
  109. // Check for newline ending
  110. if(!stream_eof(nonce_array->stream)) {
  111. if(!stream_seek(nonce_array->stream, -1, StreamOffsetFromEnd)) break;
  112. uint8_t last_char = 0;
  113. if(stream_read(nonce_array->stream, &last_char, 1) != 1) break;
  114. if(last_char != '\n') {
  115. //FURI_LOG_D(TAG, "Adding new line ending");
  116. if(stream_write_char(nonce_array->stream, '\n') != 1) break;
  117. }
  118. if(!stream_rewind(nonce_array->stream)) break;
  119. }
  120. // Read total amount of nonces
  121. FuriString* next_line;
  122. next_line = furi_string_alloc();
  123. while(!(program_state->close_thread_please)) {
  124. if(!stream_read_line(nonce_array->stream, next_line)) {
  125. //FURI_LOG_T(TAG, "No nonces left");
  126. break;
  127. }
  128. /*
  129. FURI_LOG_T(
  130. TAG,
  131. "Read line: %s, len: %zu",
  132. furi_string_get_cstr(next_line),
  133. furi_string_size(next_line));
  134. */
  135. if(!furi_string_start_with_str(next_line, "Sec")) continue;
  136. const char* next_line_cstr = furi_string_get_cstr(next_line);
  137. MfClassicNonce res = {0};
  138. res.attack = mfkey32;
  139. int i = 0;
  140. char* endptr;
  141. for(i = 0; i <= 17; i++) {
  142. if(i != 0) {
  143. next_line_cstr = strchr(next_line_cstr, ' ');
  144. if(next_line_cstr) {
  145. next_line_cstr++;
  146. } else {
  147. break;
  148. }
  149. }
  150. unsigned long value = strtoul(next_line_cstr, &endptr, 16);
  151. switch(i) {
  152. case 5:
  153. res.uid = value;
  154. break;
  155. case 7:
  156. res.nt0 = value;
  157. break;
  158. case 9:
  159. res.nr0_enc = value;
  160. break;
  161. case 11:
  162. res.ar0_enc = value;
  163. break;
  164. case 13:
  165. res.nt1 = value;
  166. break;
  167. case 15:
  168. res.nr1_enc = value;
  169. break;
  170. case 17:
  171. res.ar1_enc = value;
  172. break;
  173. default:
  174. break; // Do nothing
  175. }
  176. next_line_cstr = endptr;
  177. }
  178. res.p64 = prng_successor(res.nt0, 64);
  179. res.p64b = prng_successor(res.nt1, 64);
  180. res.uid_xor_nt0 = res.uid ^ res.nt0;
  181. res.uid_xor_nt1 = res.uid ^ res.nt1;
  182. (program_state->total)++;
  183. if((system_dict_exists && key_already_found_for_nonce_in_dict(system_dict, &res)) ||
  184. (key_already_found_for_nonce_in_dict(user_dict, &res))) {
  185. (program_state->cracked)++;
  186. (program_state->num_completed)++;
  187. continue;
  188. }
  189. //FURI_LOG_I(TAG, "No key found for %8lx %8lx", res.uid, res.ar1_enc);
  190. // TODO: Refactor
  191. nonce_array->remaining_nonce_array = realloc( //-V701
  192. nonce_array->remaining_nonce_array,
  193. sizeof(MfClassicNonce) * ((nonce_array->remaining_nonces) + 1));
  194. nonce_array->remaining_nonces++;
  195. nonce_array->remaining_nonce_array[(nonce_array->remaining_nonces) - 1] = res;
  196. nonce_array->total_nonces++;
  197. }
  198. furi_string_free(next_line);
  199. buffered_file_stream_close(nonce_array->stream);
  200. //stream_free(nonce_array->stream);
  201. array_loaded = true;
  202. //FURI_LOG_I(TAG, "Loaded %lu Mfkey32 nonces", nonce_array->total_nonces);
  203. } while(false);
  204. return array_loaded;
  205. }
  206. bool load_nested_nonces(
  207. MfClassicNonceArray* nonce_array,
  208. ProgramState* program_state,
  209. KeysDict* system_dict,
  210. bool system_dict_exists,
  211. KeysDict* user_dict) {
  212. if(!buffered_file_stream_open(
  213. nonce_array->stream, MF_CLASSIC_NESTED_NONCE_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) {
  214. return false;
  215. }
  216. FuriString* next_line = furi_string_alloc();
  217. bool array_loaded = false;
  218. while(stream_read_line(nonce_array->stream, next_line)) {
  219. const char* line = furi_string_get_cstr(next_line);
  220. // Only process lines ending with "dist 0"
  221. if(!strstr(line, "dist 0")) {
  222. continue;
  223. }
  224. MfClassicNonce res = {0};
  225. res.attack = static_nested;
  226. int parsed = sscanf(
  227. line,
  228. "Sec %*d key %*c cuid %" PRIx32 " nt0 %" PRIx32 " ks0 %" PRIx32
  229. " par0 %4[01] nt1 %" PRIx32 " ks1 %" PRIx32 " par1 %4[01]",
  230. &res.uid,
  231. &res.nt0,
  232. &res.ks1_1_enc,
  233. res.par_1_str,
  234. &res.nt1,
  235. &res.ks1_2_enc,
  236. res.par_2_str);
  237. if(parsed >= 4) { // At least one nonce is present
  238. res.par_1 = binaryStringToInt(res.par_1_str);
  239. res.uid_xor_nt0 = res.uid ^ res.nt0;
  240. if(parsed == 7) { // Both nonces are present
  241. res.par_2 = binaryStringToInt(res.par_2_str);
  242. res.uid_xor_nt1 = res.uid ^ res.nt1;
  243. }
  244. (program_state->total)++;
  245. if((system_dict_exists && key_already_found_for_nonce_in_dict(system_dict, &res)) ||
  246. (key_already_found_for_nonce_in_dict(user_dict, &res))) {
  247. (program_state->cracked)++;
  248. (program_state->num_completed)++;
  249. continue;
  250. }
  251. nonce_array->remaining_nonce_array = realloc(
  252. nonce_array->remaining_nonce_array,
  253. sizeof(MfClassicNonce) * (nonce_array->remaining_nonces + 1));
  254. nonce_array->remaining_nonce_array[nonce_array->remaining_nonces] = res;
  255. nonce_array->remaining_nonces++;
  256. nonce_array->total_nonces++;
  257. array_loaded = true;
  258. }
  259. }
  260. furi_string_free(next_line);
  261. buffered_file_stream_close(nonce_array->stream);
  262. //FURI_LOG_I(TAG, "Loaded %lu Static Nested nonces", nonce_array->total_nonces);
  263. return array_loaded;
  264. }
  265. MfClassicNonceArray* napi_mf_classic_nonce_array_alloc(
  266. KeysDict* system_dict,
  267. bool system_dict_exists,
  268. KeysDict* user_dict,
  269. ProgramState* program_state) {
  270. MfClassicNonceArray* nonce_array = malloc(sizeof(MfClassicNonceArray));
  271. MfClassicNonce* remaining_nonce_array_init = malloc(sizeof(MfClassicNonce) * 1);
  272. nonce_array->remaining_nonce_array = remaining_nonce_array_init;
  273. Storage* storage = furi_record_open(RECORD_STORAGE);
  274. nonce_array->stream = buffered_file_stream_alloc(storage);
  275. furi_record_close(RECORD_STORAGE);
  276. bool array_loaded = false;
  277. if(program_state->mfkey32_present) {
  278. array_loaded = load_mfkey32_nonces(
  279. nonce_array, program_state, system_dict, system_dict_exists, user_dict);
  280. }
  281. if(program_state->nested_present) {
  282. array_loaded |= load_nested_nonces(
  283. nonce_array, program_state, system_dict, system_dict_exists, user_dict);
  284. }
  285. if(!array_loaded) {
  286. free(nonce_array);
  287. nonce_array = NULL;
  288. }
  289. return nonce_array;
  290. }
  291. void napi_mf_classic_nonce_array_free(MfClassicNonceArray* nonce_array) {
  292. // TODO: Track free state at the time this is called to ensure double free does not happen
  293. furi_assert(nonce_array);
  294. furi_assert(nonce_array->stream);
  295. buffered_file_stream_close(nonce_array->stream);
  296. stream_free(nonce_array->stream);
  297. free(nonce_array);
  298. }
  299. /* Actual implementation of app<>plugin interface */
  300. static const MfkeyPlugin init_plugin = {
  301. .name = "Initialization Plugin",
  302. .napi_mf_classic_mfkey32_nonces_check_presence =
  303. &napi_mf_classic_mfkey32_nonces_check_presence,
  304. .napi_mf_classic_nested_nonces_check_presence = &napi_mf_classic_nested_nonces_check_presence,
  305. .napi_mf_classic_nonce_array_alloc = &napi_mf_classic_nonce_array_alloc,
  306. .napi_mf_classic_nonce_array_free = &napi_mf_classic_nonce_array_free,
  307. };
  308. /* Plugin descriptor to comply with basic plugin specification */
  309. static const FlipperAppPluginDescriptor init_plugin_descriptor = {
  310. .appid = PLUGIN_APP_ID,
  311. .ep_api_version = PLUGIN_API_VERSION,
  312. .entry_point = &init_plugin,
  313. };
  314. /* Plugin entry point - must return a pointer to const descriptor */
  315. const FlipperAppPluginDescriptor* init_plugin_ep() {
  316. return &init_plugin_descriptor;
  317. }