picopass_poller.c 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725
  1. #include "picopass_i.h"
  2. #include "picopass_poller_i.h"
  3. #include "../loclass/optimized_cipher.h"
  4. #include <furi/furi.h>
  5. #include <toolbox/version.h>
  6. #define TAG "Picopass"
  7. typedef NfcCommand (*PicopassPollerStateHandler)(PicopassPoller* instance);
  8. static void picopass_poller_reset(PicopassPoller* instance) {
  9. instance->current_block = 0;
  10. }
  11. static void picopass_poller_prepare_read(PicopassPoller* instance) {
  12. instance->app_limit = instance->data->card_data[PICOPASS_CONFIG_BLOCK_INDEX].data[0] <
  13. PICOPASS_MAX_APP_LIMIT ?
  14. instance->data->card_data[PICOPASS_CONFIG_BLOCK_INDEX].data[0] :
  15. PICOPASS_MAX_APP_LIMIT;
  16. instance->current_block = 2;
  17. }
  18. NfcCommand picopass_poller_request_mode_handler(PicopassPoller* instance) {
  19. NfcCommand command = NfcCommandContinue;
  20. instance->event.type = PicopassPollerEventTypeRequestMode;
  21. command = instance->callback(instance->event, instance->context);
  22. instance->mode = instance->event_data.req_mode.mode;
  23. instance->state = PicopassPollerStateDetect;
  24. return command;
  25. }
  26. NfcCommand picopass_poller_detect_handler(PicopassPoller* instance) {
  27. NfcCommand command = NfcCommandContinue;
  28. PicopassError error = picopass_poller_actall(instance);
  29. if(error == PicopassErrorNone) {
  30. instance->state = PicopassPollerStateSelect;
  31. instance->event.type = PicopassPollerEventTypeCardDetected;
  32. command = instance->callback(instance->event, instance->context);
  33. } else {
  34. furi_delay_ms(100);
  35. }
  36. return command;
  37. }
  38. NfcCommand picopass_poller_select_handler(PicopassPoller* instance) {
  39. NfcCommand command = NfcCommandContinue;
  40. do {
  41. PicopassError error = picopass_poller_identify(instance, &instance->col_res_serial_num);
  42. if(error != PicopassErrorNone) {
  43. instance->state = PicopassPollerStateFail;
  44. break;
  45. }
  46. error =
  47. picopass_poller_select(instance, &instance->col_res_serial_num, &instance->serial_num);
  48. if(error != PicopassErrorNone) {
  49. instance->state = PicopassPollerStateFail;
  50. break;
  51. }
  52. instance->state = PicopassPollerStatePreAuth;
  53. } while(false);
  54. return command;
  55. }
  56. void picopass_poller_print_block(char* str, PicopassBlock block) {
  57. FURI_LOG_D(
  58. TAG,
  59. str,
  60. block.data[0],
  61. block.data[1],
  62. block.data[2],
  63. block.data[3],
  64. block.data[4],
  65. block.data[5],
  66. block.data[6],
  67. block.data[7]);
  68. }
  69. NfcCommand picopass_poller_pre_auth_handler(PicopassPoller* instance) {
  70. NfcCommand command = NfcCommandContinue;
  71. PicopassError error = PicopassErrorNone;
  72. // Print version information for app and firmware for later review in log
  73. const Version* version = version_get();
  74. FURI_LOG_I(
  75. TAG,
  76. "Firmware origin: %s firmware version: %s app version: %s",
  77. version_get_firmware_origin(version),
  78. version_get_version(version),
  79. FAP_VERSION);
  80. do {
  81. memcpy(
  82. instance->data->card_data[PICOPASS_CSN_BLOCK_INDEX].data,
  83. instance->serial_num.data,
  84. sizeof(PicopassSerialNum));
  85. instance->data->card_data[PICOPASS_CSN_BLOCK_INDEX].valid = true;
  86. picopass_poller_print_block(
  87. "csn %02x%02x%02x%02x%02x%02x%02x%02x",
  88. instance->data->card_data[PICOPASS_CSN_BLOCK_INDEX]);
  89. PicopassBlock block = {};
  90. error = picopass_poller_read_block(instance, PICOPASS_CONFIG_BLOCK_INDEX, &block);
  91. if(error != PicopassErrorNone) {
  92. instance->state = PicopassPollerStateFail;
  93. break;
  94. }
  95. memcpy(
  96. instance->data->card_data[PICOPASS_CONFIG_BLOCK_INDEX].data,
  97. block.data,
  98. PICOPASS_BLOCK_LEN);
  99. instance->data->card_data[PICOPASS_CONFIG_BLOCK_INDEX].valid = true;
  100. picopass_poller_print_block(
  101. "config %02x%02x%02x%02x%02x%02x%02x%02x",
  102. instance->data->card_data[PICOPASS_CONFIG_BLOCK_INDEX]);
  103. error = picopass_poller_read_block(instance, PICOPASS_SECURE_EPURSE_BLOCK_INDEX, &block);
  104. if(error != PicopassErrorNone) {
  105. instance->state = PicopassPollerStateFail;
  106. break;
  107. }
  108. memcpy(
  109. instance->data->card_data[PICOPASS_SECURE_EPURSE_BLOCK_INDEX].data,
  110. block.data,
  111. PICOPASS_BLOCK_LEN);
  112. instance->data->card_data[PICOPASS_SECURE_EPURSE_BLOCK_INDEX].valid = true;
  113. picopass_poller_print_block(
  114. "epurse %02x%02x%02x%02x%02x%02x%02x%02x",
  115. instance->data->card_data[PICOPASS_SECURE_EPURSE_BLOCK_INDEX]);
  116. error = picopass_poller_read_block(instance, PICOPASS_SECURE_AIA_BLOCK_INDEX, &block);
  117. if(error != PicopassErrorNone) {
  118. instance->state = PicopassPollerStateFail;
  119. break;
  120. }
  121. memcpy(
  122. instance->data->card_data[PICOPASS_SECURE_AIA_BLOCK_INDEX].data,
  123. block.data,
  124. PICOPASS_BLOCK_LEN);
  125. instance->data->card_data[PICOPASS_SECURE_AIA_BLOCK_INDEX].valid = true;
  126. picopass_poller_print_block(
  127. "aia %02x%02x%02x%02x%02x%02x%02x%02x",
  128. instance->data->card_data[PICOPASS_SECURE_AIA_BLOCK_INDEX]);
  129. instance->state = PicopassPollerStateCheckSecurity;
  130. } while(false);
  131. return command;
  132. }
  133. NfcCommand picopass_poller_check_security(PicopassPoller* instance) {
  134. NfcCommand command = NfcCommandContinue;
  135. instance->secured = true;
  136. uint8_t crypt =
  137. (instance->data->card_data[PICOPASS_CONFIG_BLOCK_INDEX].data[7] & PICOPASS_FUSE_CRYPT10);
  138. switch(crypt) {
  139. case 0:
  140. FURI_LOG_D(TAG, "Secured page - Authentication disabled");
  141. // Well this is awkward... We can try anyway though I guess...
  142. break;
  143. case PICOPASS_FUSE_CRYPT0:
  144. FURI_LOG_D(TAG, "Non-secured page, skipping auth");
  145. instance->secured = false;
  146. instance->data->auth = PicopassDeviceAuthMethodNone;
  147. picopass_poller_prepare_read(instance);
  148. instance->state = PicopassPollerStateReadBlock;
  149. return command;
  150. case PICOPASS_FUSE_CRYPT0 | PICOPASS_FUSE_CRYPT1:
  151. FURI_LOG_D(TAG, "Secured page - keys modifiable");
  152. break;
  153. case PICOPASS_FUSE_CRYPT1:
  154. FURI_LOG_D(TAG, "Secured page - keys locked");
  155. }
  156. // Thank you proxmark!
  157. PicopassBlock temp_block = {};
  158. memset(temp_block.data, 0xff, PICOPASS_BLOCK_LEN);
  159. instance->data->pacs.legacy =
  160. (memcmp(
  161. instance->data->card_data[PICOPASS_SECURE_AIA_BLOCK_INDEX].data,
  162. temp_block.data,
  163. PICOPASS_BLOCK_LEN) == 0);
  164. temp_block.data[3] = 0x00;
  165. temp_block.data[4] = 0x06;
  166. instance->data->pacs.se_enabled =
  167. (memcmp(
  168. instance->data->card_data[PICOPASS_SECURE_AIA_BLOCK_INDEX].data,
  169. temp_block.data,
  170. PICOPASS_BLOCK_LEN) == 0);
  171. if(instance->data->pacs.se_enabled) {
  172. FURI_LOG_D(TAG, "SE enabled");
  173. }
  174. // Assume failure since we must auth, correct value will be set on success
  175. instance->data->auth = PicopassDeviceAuthMethodFailed;
  176. if(instance->mode == PicopassPollerModeRead) {
  177. // Always try the NR-MAC auth in case we have the file.
  178. instance->state = PicopassPollerStateNrMacAuth;
  179. } else {
  180. // NR-MAC auth doesn't allow for writing, so don't try
  181. instance->state = PicopassPollerStateAuth;
  182. }
  183. return command;
  184. }
  185. NfcCommand picopass_poller_nr_mac_auth(PicopassPoller* instance) {
  186. NfcCommand command = NfcCommandContinue;
  187. Picopass* picopass = instance->context;
  188. PicopassDevice* dev = picopass->dev;
  189. uint8_t* csn = instance->data->card_data[PICOPASS_CSN_BLOCK_INDEX].data;
  190. uint8_t* epurse = instance->data->card_data[PICOPASS_SECURE_EPURSE_BLOCK_INDEX].data;
  191. FuriString* temp_str = furi_string_alloc();
  192. FuriString* filename = furi_string_alloc();
  193. FlipperFormat* file = flipper_format_file_alloc(dev->storage);
  194. PicopassMac mac = {};
  195. for(size_t i = 0; i < PICOPASS_BLOCK_LEN; i++) {
  196. furi_string_cat_printf(filename, "%02x", csn[i]);
  197. }
  198. furi_string_cat_printf(filename, "_");
  199. for(size_t i = 0; i < PICOPASS_BLOCK_LEN; i++) {
  200. furi_string_cat_printf(filename, "%02x", epurse[i]);
  201. }
  202. furi_string_printf(
  203. temp_str, "%s/%s%s", STORAGE_APP_DATA_PATH_PREFIX, furi_string_get_cstr(filename), ".mac");
  204. FURI_LOG_D(TAG, "Looking for %s", furi_string_get_cstr(temp_str));
  205. uint8_t nr_mac[PICOPASS_BLOCK_LEN];
  206. // Set next state so breaking do/while will jump to it. If successful, do/while will set to ReadBlock
  207. if(instance->data->pacs.se_enabled) {
  208. instance->state = PicopassPollerStateAuthFail;
  209. } else {
  210. // For non-SE, run through normal key check
  211. instance->state = PicopassPollerStateAuth;
  212. }
  213. do {
  214. //check for file
  215. if(!flipper_format_file_open_existing(file, furi_string_get_cstr(temp_str))) break;
  216. // FURI_LOG_D(TAG, "Found %s", furi_string_get_cstr(temp_str));
  217. furi_string_printf(temp_str, "NR-MAC");
  218. if(!flipper_format_read_hex(
  219. file, furi_string_get_cstr(temp_str), nr_mac, PICOPASS_BLOCK_LEN))
  220. break;
  221. memcpy(mac.data, nr_mac + 4, PICOPASS_MAC_LEN);
  222. /*
  223. FURI_LOG_D(
  224. TAG,
  225. "Read nr-mac: %02x %02x %02x %02x %02x %02x %02x %02x",
  226. nr_mac[0],
  227. nr_mac[1],
  228. nr_mac[2],
  229. nr_mac[3],
  230. nr_mac[4],
  231. nr_mac[5],
  232. nr_mac[6],
  233. nr_mac[7]);
  234. FURI_LOG_D(
  235. TAG, "MAC: %02x %02x %02x %02x", mac.data[0], mac.data[1], mac.data[2], mac.data[3]);
  236. */
  237. uint8_t ccnr[12] = {};
  238. PicopassReadCheckResp read_check_resp = {};
  239. PicopassError error = picopass_poller_read_check(instance, &read_check_resp);
  240. if(error == PicopassErrorTimeout) {
  241. instance->event.type = PicopassPollerEventTypeCardLost;
  242. instance->callback(instance->event, instance->context);
  243. instance->state = PicopassPollerStateDetect;
  244. break;
  245. } else if(error != PicopassErrorNone) {
  246. FURI_LOG_E(TAG, "Read check failed: %d", error);
  247. break;
  248. }
  249. memcpy(ccnr, read_check_resp.data, sizeof(PicopassReadCheckResp)); // last 4 bytes left 0
  250. /*
  251. FURI_LOG_D(
  252. TAG,
  253. "CCNR: %02x %02x %02x %02x %02x %02x %02x %02x",
  254. ccnr[0],
  255. ccnr[1],
  256. ccnr[2],
  257. ccnr[3],
  258. ccnr[4],
  259. ccnr[5],
  260. ccnr[6],
  261. ccnr[7]);
  262. */
  263. //use mac
  264. PicopassCheckResp check_resp = {};
  265. error = picopass_poller_check(instance, nr_mac, &mac, &check_resp);
  266. if(error == PicopassErrorNone) {
  267. instance->data->auth = PicopassDeviceAuthMethodNrMac;
  268. memcpy(instance->mac.data, mac.data, sizeof(PicopassMac));
  269. if(instance->mode == PicopassPollerModeRead) {
  270. picopass_poller_prepare_read(instance);
  271. instance->state = PicopassPollerStateReadBlock;
  272. // Set to non-zero keys to allow emulation
  273. memset(
  274. instance->data->card_data[PICOPASS_SECURE_KD_BLOCK_INDEX].data,
  275. 0xff,
  276. PICOPASS_BLOCK_LEN);
  277. memset(
  278. instance->data->card_data[PICOPASS_SECURE_KC_BLOCK_INDEX].data,
  279. 0xff,
  280. PICOPASS_BLOCK_LEN);
  281. }
  282. }
  283. } while(false);
  284. furi_string_free(temp_str);
  285. furi_string_free(filename);
  286. flipper_format_free(file);
  287. return command;
  288. }
  289. NfcCommand picopass_poller_auth_handler(PicopassPoller* instance) {
  290. NfcCommand command = NfcCommandContinue;
  291. do {
  292. // Request key
  293. instance->event.type = PicopassPollerEventTypeRequestKey;
  294. command = instance->callback(instance->event, instance->context);
  295. if(command != NfcCommandContinue) break;
  296. if(!instance->event_data.req_key.is_key_provided) {
  297. instance->state = PicopassPollerStateAuthFail;
  298. break;
  299. }
  300. FURI_LOG_D(
  301. TAG,
  302. "Try to %s auth with key %02x%02x%02x%02x%02x%02x%02x%02x",
  303. instance->event_data.req_key.is_elite_key ? "elite" : "standard",
  304. instance->event_data.req_key.key[0],
  305. instance->event_data.req_key.key[1],
  306. instance->event_data.req_key.key[2],
  307. instance->event_data.req_key.key[3],
  308. instance->event_data.req_key.key[4],
  309. instance->event_data.req_key.key[5],
  310. instance->event_data.req_key.key[6],
  311. instance->event_data.req_key.key[7]);
  312. PicopassReadCheckResp read_check_resp = {};
  313. uint8_t* csn = instance->serial_num.data;
  314. memset(instance->div_key, 0, sizeof(instance->div_key));
  315. uint8_t* div_key = NULL;
  316. if(instance->mode == PicopassPollerModeRead) {
  317. div_key = instance->data->card_data[PICOPASS_SECURE_KD_BLOCK_INDEX].data;
  318. } else {
  319. div_key = instance->div_key;
  320. }
  321. uint8_t ccnr[12] = {};
  322. PicopassMac mac = {};
  323. PicopassError error = picopass_poller_read_check(instance, &read_check_resp);
  324. if(error == PicopassErrorTimeout) {
  325. instance->event.type = PicopassPollerEventTypeCardLost;
  326. command = instance->callback(instance->event, instance->context);
  327. instance->state = PicopassPollerStateDetect;
  328. break;
  329. } else if(error != PicopassErrorNone) {
  330. FURI_LOG_E(TAG, "Read check failed: %d", error);
  331. break;
  332. }
  333. memcpy(ccnr, read_check_resp.data, sizeof(PicopassReadCheckResp)); // last 4 bytes left 0
  334. loclass_iclass_calc_div_key(
  335. csn,
  336. instance->event_data.req_key.key,
  337. div_key,
  338. instance->event_data.req_key.is_elite_key);
  339. loclass_opt_doReaderMAC(ccnr, div_key, mac.data);
  340. PicopassCheckResp check_resp = {};
  341. error = picopass_poller_check(instance, NULL, &mac, &check_resp);
  342. if(error == PicopassErrorNone) {
  343. FURI_LOG_I(TAG, "Found key");
  344. instance->data->auth = PicopassDeviceAuthMethodKey;
  345. memcpy(instance->mac.data, mac.data, sizeof(PicopassMac));
  346. if(instance->mode == PicopassPollerModeRead) {
  347. memcpy(
  348. instance->data->pacs.key, instance->event_data.req_key.key, PICOPASS_KEY_LEN);
  349. instance->data->card_data[PICOPASS_SECURE_KD_BLOCK_INDEX].valid = true;
  350. instance->data->pacs.elite_kdf = instance->event_data.req_key.is_elite_key;
  351. picopass_poller_prepare_read(instance);
  352. instance->state = PicopassPollerStateReadBlock;
  353. } else if(instance->mode == PicopassPollerModeWrite) {
  354. instance->state = PicopassPollerStateWriteBlock;
  355. } else {
  356. instance->state = PicopassPollerStateWriteKey;
  357. }
  358. }
  359. } while(false);
  360. return command;
  361. }
  362. NfcCommand picopass_poller_read_block_handler(PicopassPoller* instance) {
  363. NfcCommand command = NfcCommandContinue;
  364. do {
  365. if(instance->current_block == instance->app_limit) {
  366. if(instance->secured) {
  367. instance->state = PicopassPollerStateParseCredential;
  368. } else {
  369. instance->state = PicopassPollerStateSuccess;
  370. }
  371. break;
  372. }
  373. if(instance->secured && instance->current_block == PICOPASS_SECURE_KD_BLOCK_INDEX) {
  374. // Skip over Kd block which is populated earlier (READ of Kd returns all FF's)
  375. instance->current_block++;
  376. }
  377. PicopassBlock block = {};
  378. PicopassError error =
  379. picopass_poller_read_block(instance, instance->current_block, &block);
  380. if(error != PicopassErrorNone) {
  381. FURI_LOG_E(TAG, "Failed to read block %d: %d", instance->current_block, error);
  382. instance->state = PicopassPollerStateFail;
  383. break;
  384. }
  385. FURI_LOG_D(
  386. TAG,
  387. "Block %d: %02x%02x%02x%02x%02x%02x%02x%02x",
  388. instance->current_block,
  389. block.data[0],
  390. block.data[1],
  391. block.data[2],
  392. block.data[3],
  393. block.data[4],
  394. block.data[5],
  395. block.data[6],
  396. block.data[7]);
  397. memcpy(
  398. instance->data->card_data[instance->current_block].data,
  399. block.data,
  400. PICOPASS_BLOCK_LEN);
  401. instance->data->card_data[instance->current_block].valid = true;
  402. instance->current_block++;
  403. } while(false);
  404. return command;
  405. }
  406. NfcCommand picopass_poller_parse_credential_handler(PicopassPoller* instance) {
  407. NfcCommand command = NfcCommandContinue;
  408. picopass_device_parse_credential(instance->data->card_data, &instance->data->pacs);
  409. instance->state = PicopassPollerStateParseWiegand;
  410. return command;
  411. }
  412. NfcCommand picopass_poller_parse_wiegand_handler(PicopassPoller* instance) {
  413. NfcCommand command = NfcCommandContinue;
  414. picopass_device_parse_wiegand(&instance->data->pacs);
  415. instance->state = PicopassPollerStateSuccess;
  416. return command;
  417. }
  418. NfcCommand picopass_poller_write_block_handler(PicopassPoller* instance) {
  419. NfcCommand command = NfcCommandContinue;
  420. PicopassError error = PicopassErrorNone;
  421. do {
  422. instance->event.type = PicopassPollerEventTypeRequestWriteBlock;
  423. command = instance->callback(instance->event, instance->context);
  424. if(command != NfcCommandContinue) break;
  425. PicopassPollerEventDataRequestWriteBlock* write_block = &instance->event_data.req_write;
  426. if(!write_block->perform_write) {
  427. instance->state = PicopassPollerStateSuccess;
  428. break;
  429. }
  430. FURI_LOG_D(TAG, "Writing %d block", write_block->block_num);
  431. uint8_t data[9] = {};
  432. data[0] = write_block->block_num;
  433. memcpy(&data[1], write_block->block->data, PICOPASS_BLOCK_LEN);
  434. loclass_doMAC_N(data, sizeof(data), instance->div_key, instance->mac.data);
  435. FURI_LOG_D(
  436. TAG,
  437. "loclass_doMAC_N %d %02x%02x%02x%02x%02x%02x%02x%02x %02x%02x%02x%02x",
  438. write_block->block_num,
  439. data[1],
  440. data[2],
  441. data[3],
  442. data[4],
  443. data[5],
  444. data[6],
  445. data[7],
  446. data[8],
  447. instance->mac.data[0],
  448. instance->mac.data[1],
  449. instance->mac.data[2],
  450. instance->mac.data[3]);
  451. error = picopass_poller_write_block(
  452. instance, write_block->block_num, write_block->block, &instance->mac);
  453. if(error != PicopassErrorNone) {
  454. FURI_LOG_E(TAG, "Failed to write block %d. Error %d", write_block->block_num, error);
  455. instance->state = PicopassPollerStateFail;
  456. break;
  457. }
  458. } while(false);
  459. return command;
  460. }
  461. NfcCommand picopass_poller_write_key_handler(PicopassPoller* instance) {
  462. NfcCommand command = NfcCommandContinue;
  463. PicopassError error = PicopassErrorNone;
  464. do {
  465. instance->event.type = PicopassPollerEventTypeRequestWriteKey;
  466. command = instance->callback(instance->event, instance->context);
  467. if(command != NfcCommandContinue) break;
  468. const PicopassDeviceData* picopass_data = instance->event_data.req_write_key.data;
  469. const uint8_t* new_key = instance->event_data.req_write_key.key;
  470. bool is_elite_key = instance->event_data.req_write_key.is_elite_key;
  471. const uint8_t* csn = picopass_data->card_data[PICOPASS_CSN_BLOCK_INDEX].data;
  472. const uint8_t* config_block = picopass_data->card_data[PICOPASS_CONFIG_BLOCK_INDEX].data;
  473. uint8_t fuses = config_block[7];
  474. const uint8_t* old_key = picopass_data->card_data[PICOPASS_SECURE_KD_BLOCK_INDEX].data;
  475. PicopassBlock new_block = {};
  476. loclass_iclass_calc_div_key(csn, new_key, new_block.data, is_elite_key);
  477. if((fuses & 0x80) == 0x80) {
  478. FURI_LOG_D(TAG, "Plain write for personalized mode key change");
  479. } else {
  480. FURI_LOG_D(TAG, "XOR write for application mode key change");
  481. // XOR when in application mode
  482. for(size_t i = 0; i < PICOPASS_BLOCK_LEN; i++) {
  483. new_block.data[i] ^= old_key[i];
  484. }
  485. }
  486. FURI_LOG_D(TAG, "Writing key to %d block", PICOPASS_SECURE_KD_BLOCK_INDEX);
  487. uint8_t data[9] = {};
  488. data[0] = PICOPASS_SECURE_KD_BLOCK_INDEX;
  489. memcpy(&data[1], new_block.data, PICOPASS_BLOCK_LEN);
  490. loclass_doMAC_N(data, sizeof(data), instance->div_key, instance->mac.data);
  491. FURI_LOG_D(
  492. TAG,
  493. "loclass_doMAC_N %d %02x%02x%02x%02x%02x%02x%02x%02x %02x%02x%02x%02x",
  494. PICOPASS_SECURE_KD_BLOCK_INDEX,
  495. data[1],
  496. data[2],
  497. data[3],
  498. data[4],
  499. data[5],
  500. data[6],
  501. data[7],
  502. data[8],
  503. instance->mac.data[0],
  504. instance->mac.data[1],
  505. instance->mac.data[2],
  506. instance->mac.data[3]);
  507. error = picopass_poller_write_block(
  508. instance, PICOPASS_SECURE_KD_BLOCK_INDEX, &new_block, &instance->mac);
  509. if(error != PicopassErrorNone) {
  510. FURI_LOG_E(
  511. TAG, "Failed to write block %d. Error %d", PICOPASS_SECURE_KD_BLOCK_INDEX, error);
  512. instance->state = PicopassPollerStateFail;
  513. break;
  514. }
  515. instance->state = PicopassPollerStateSuccess;
  516. } while(false);
  517. return command;
  518. }
  519. NfcCommand picopass_poller_success_handler(PicopassPoller* instance) {
  520. NfcCommand command = NfcCommandContinue;
  521. instance->event.type = PicopassPollerEventTypeSuccess;
  522. command = instance->callback(instance->event, instance->context);
  523. furi_delay_ms(100);
  524. return command;
  525. }
  526. NfcCommand picopass_poller_fail_handler(PicopassPoller* instance) {
  527. NfcCommand command = NfcCommandReset;
  528. instance->event.type = PicopassPollerEventTypeFail;
  529. command = instance->callback(instance->event, instance->context);
  530. picopass_poller_reset(instance);
  531. instance->state = PicopassPollerStateDetect;
  532. return command;
  533. }
  534. NfcCommand picopass_poller_auth_fail_handler(PicopassPoller* instance) {
  535. NfcCommand command = NfcCommandReset;
  536. instance->event.type = PicopassPollerEventTypeAuthFail;
  537. command = instance->callback(instance->event, instance->context);
  538. picopass_poller_reset(instance);
  539. instance->state = PicopassPollerStateDetect;
  540. return command;
  541. }
  542. static const PicopassPollerStateHandler picopass_poller_state_handler[PicopassPollerStateNum] = {
  543. [PicopassPollerStateRequestMode] = picopass_poller_request_mode_handler,
  544. [PicopassPollerStateDetect] = picopass_poller_detect_handler,
  545. [PicopassPollerStateSelect] = picopass_poller_select_handler,
  546. [PicopassPollerStatePreAuth] = picopass_poller_pre_auth_handler,
  547. [PicopassPollerStateCheckSecurity] = picopass_poller_check_security,
  548. [PicopassPollerStateNrMacAuth] = picopass_poller_nr_mac_auth,
  549. [PicopassPollerStateAuth] = picopass_poller_auth_handler,
  550. [PicopassPollerStateReadBlock] = picopass_poller_read_block_handler,
  551. [PicopassPollerStateWriteBlock] = picopass_poller_write_block_handler,
  552. [PicopassPollerStateWriteKey] = picopass_poller_write_key_handler,
  553. [PicopassPollerStateParseCredential] = picopass_poller_parse_credential_handler,
  554. [PicopassPollerStateParseWiegand] = picopass_poller_parse_wiegand_handler,
  555. [PicopassPollerStateSuccess] = picopass_poller_success_handler,
  556. [PicopassPollerStateFail] = picopass_poller_fail_handler,
  557. [PicopassPollerStateAuthFail] = picopass_poller_auth_fail_handler,
  558. };
  559. static NfcCommand picopass_poller_callback(NfcEvent event, void* context) {
  560. furi_assert(context);
  561. PicopassPoller* instance = context;
  562. NfcCommand command = NfcCommandContinue;
  563. if(event.type == NfcEventTypePollerReady) {
  564. command = picopass_poller_state_handler[instance->state](instance);
  565. }
  566. if(instance->session_state == PicopassPollerSessionStateStopRequest) {
  567. command = NfcCommandStop;
  568. }
  569. return command;
  570. }
  571. void picopass_poller_start(
  572. PicopassPoller* instance,
  573. PicopassPollerCallback callback,
  574. void* context) {
  575. furi_assert(instance);
  576. furi_assert(instance->session_state == PicopassPollerSessionStateIdle);
  577. instance->callback = callback;
  578. instance->context = context;
  579. instance->session_state = PicopassPollerSessionStateActive;
  580. nfc_start(instance->nfc, picopass_poller_callback, instance);
  581. }
  582. void picopass_poller_stop(PicopassPoller* instance) {
  583. furi_assert(instance);
  584. instance->session_state = PicopassPollerSessionStateStopRequest;
  585. nfc_stop(instance->nfc);
  586. instance->session_state = PicopassPollerSessionStateIdle;
  587. }
  588. PicopassPoller* picopass_poller_alloc(Nfc* nfc) {
  589. furi_assert(nfc);
  590. PicopassPoller* instance = malloc(sizeof(PicopassPoller));
  591. instance->nfc = nfc;
  592. nfc_config(instance->nfc, NfcModePoller, NfcTechIso15693);
  593. nfc_set_guard_time_us(instance->nfc, 10000);
  594. nfc_set_fdt_poll_fc(instance->nfc, 5000);
  595. nfc_set_fdt_poll_poll_us(instance->nfc, 1000);
  596. instance->event.data = &instance->event_data;
  597. instance->data = malloc(sizeof(PicopassDeviceData));
  598. instance->tx_buffer = bit_buffer_alloc(PICOPASS_POLLER_BUFFER_SIZE);
  599. instance->rx_buffer = bit_buffer_alloc(PICOPASS_POLLER_BUFFER_SIZE);
  600. instance->tmp_buffer = bit_buffer_alloc(PICOPASS_POLLER_BUFFER_SIZE);
  601. return instance;
  602. }
  603. void picopass_poller_free(PicopassPoller* instance) {
  604. furi_assert(instance);
  605. free(instance->data);
  606. bit_buffer_free(instance->tx_buffer);
  607. bit_buffer_free(instance->rx_buffer);
  608. bit_buffer_free(instance->tmp_buffer);
  609. free(instance);
  610. }
  611. const PicopassDeviceData* picopass_poller_get_data(PicopassPoller* instance) {
  612. furi_assert(instance);
  613. return instance->data;
  614. }