picopass_poller.c 25 KB

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