picopass_poller.c 25 KB

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