picopass_poller.c 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719
  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. }
  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. instance->current_block == PICOPASS_SECURE_KC_BLOCK_INDEX)) {
  366. // Kd and Kc blocks cannot be read (card always returns FF's)
  367. // Key blocks we authed as would have been already set earlier
  368. instance->current_block++;
  369. continue;
  370. }
  371. PicopassBlock block = {};
  372. PicopassError error =
  373. picopass_poller_read_block(instance, instance->current_block, &block);
  374. if(error != PicopassErrorNone) {
  375. FURI_LOG_E(TAG, "Failed to read block %d: %d", instance->current_block, error);
  376. instance->state = PicopassPollerStateFail;
  377. break;
  378. }
  379. FURI_LOG_D(
  380. TAG,
  381. "Block %d: %02x%02x%02x%02x%02x%02x%02x%02x",
  382. instance->current_block,
  383. block.data[0],
  384. block.data[1],
  385. block.data[2],
  386. block.data[3],
  387. block.data[4],
  388. block.data[5],
  389. block.data[6],
  390. block.data[7]);
  391. memcpy(
  392. instance->data->card_data[instance->current_block].data,
  393. block.data,
  394. PICOPASS_BLOCK_LEN);
  395. instance->data->card_data[instance->current_block].valid = true;
  396. instance->current_block++;
  397. } while(false);
  398. return command;
  399. }
  400. NfcCommand picopass_poller_parse_credential_handler(PicopassPoller* instance) {
  401. NfcCommand command = NfcCommandContinue;
  402. picopass_device_parse_credential(instance->data->card_data, &instance->data->pacs);
  403. instance->state = PicopassPollerStateParseWiegand;
  404. return command;
  405. }
  406. NfcCommand picopass_poller_parse_wiegand_handler(PicopassPoller* instance) {
  407. NfcCommand command = NfcCommandContinue;
  408. picopass_device_parse_wiegand(&instance->data->pacs);
  409. instance->state = PicopassPollerStateSuccess;
  410. return command;
  411. }
  412. NfcCommand picopass_poller_write_block_handler(PicopassPoller* instance) {
  413. NfcCommand command = NfcCommandContinue;
  414. PicopassError error = PicopassErrorNone;
  415. do {
  416. instance->event.type = PicopassPollerEventTypeRequestWriteBlock;
  417. command = instance->callback(instance->event, instance->context);
  418. if(command != NfcCommandContinue) break;
  419. PicopassPollerEventDataRequestWriteBlock* write_block = &instance->event_data.req_write;
  420. if(!write_block->perform_write) {
  421. instance->state = PicopassPollerStateSuccess;
  422. break;
  423. }
  424. FURI_LOG_D(TAG, "Writing %d block", write_block->block_num);
  425. uint8_t data[9] = {};
  426. data[0] = write_block->block_num;
  427. memcpy(&data[1], write_block->block->data, PICOPASS_BLOCK_LEN);
  428. loclass_doMAC_N(data, sizeof(data), instance->div_key, instance->mac.data);
  429. FURI_LOG_D(
  430. TAG,
  431. "loclass_doMAC_N %d %02x%02x%02x%02x%02x%02x%02x%02x %02x%02x%02x%02x",
  432. write_block->block_num,
  433. data[1],
  434. data[2],
  435. data[3],
  436. data[4],
  437. data[5],
  438. data[6],
  439. data[7],
  440. data[8],
  441. instance->mac.data[0],
  442. instance->mac.data[1],
  443. instance->mac.data[2],
  444. instance->mac.data[3]);
  445. error = picopass_poller_write_block(
  446. instance, write_block->block_num, write_block->block, &instance->mac);
  447. if(error != PicopassErrorNone) {
  448. FURI_LOG_E(TAG, "Failed to write block %d. Error %d", write_block->block_num, error);
  449. instance->state = PicopassPollerStateFail;
  450. break;
  451. }
  452. } while(false);
  453. return command;
  454. }
  455. NfcCommand picopass_poller_write_key_handler(PicopassPoller* instance) {
  456. NfcCommand command = NfcCommandContinue;
  457. PicopassError error = PicopassErrorNone;
  458. do {
  459. instance->event.type = PicopassPollerEventTypeRequestWriteKey;
  460. command = instance->callback(instance->event, instance->context);
  461. if(command != NfcCommandContinue) break;
  462. const PicopassDeviceData* picopass_data = instance->event_data.req_write_key.data;
  463. const uint8_t* new_key = instance->event_data.req_write_key.key;
  464. bool is_elite_key = instance->event_data.req_write_key.is_elite_key;
  465. const uint8_t* csn = instance->serial_num.data;
  466. const uint8_t* old_key = instance->div_key;
  467. PicopassBlock new_block = {};
  468. loclass_iclass_calc_div_key(csn, new_key, new_block.data, is_elite_key);
  469. const uint8_t* config_block = picopass_data->card_data[PICOPASS_CONFIG_BLOCK_INDEX].data;
  470. uint8_t fuses = config_block[7];
  471. if((fuses & 0x80) == 0x80) {
  472. FURI_LOG_D(TAG, "Plain write for personalized mode key change");
  473. } else {
  474. FURI_LOG_D(TAG, "XOR write for application mode key change");
  475. // XOR when in application mode
  476. for(size_t i = 0; i < PICOPASS_BLOCK_LEN; i++) {
  477. new_block.data[i] ^= old_key[i];
  478. }
  479. }
  480. FURI_LOG_D(TAG, "Writing key to %d block", PICOPASS_SECURE_KD_BLOCK_INDEX);
  481. uint8_t data[9] = {};
  482. data[0] = PICOPASS_SECURE_KD_BLOCK_INDEX;
  483. memcpy(&data[1], new_block.data, PICOPASS_BLOCK_LEN);
  484. loclass_doMAC_N(data, sizeof(data), instance->div_key, instance->mac.data);
  485. FURI_LOG_D(
  486. TAG,
  487. "loclass_doMAC_N %d %02x%02x%02x%02x%02x%02x%02x%02x %02x%02x%02x%02x",
  488. PICOPASS_SECURE_KD_BLOCK_INDEX,
  489. data[1],
  490. data[2],
  491. data[3],
  492. data[4],
  493. data[5],
  494. data[6],
  495. data[7],
  496. data[8],
  497. instance->mac.data[0],
  498. instance->mac.data[1],
  499. instance->mac.data[2],
  500. instance->mac.data[3]);
  501. error = picopass_poller_write_block(
  502. instance, PICOPASS_SECURE_KD_BLOCK_INDEX, &new_block, &instance->mac);
  503. if(error != PicopassErrorNone) {
  504. FURI_LOG_E(
  505. TAG, "Failed to write block %d. Error %d", PICOPASS_SECURE_KD_BLOCK_INDEX, error);
  506. instance->state = PicopassPollerStateFail;
  507. break;
  508. }
  509. instance->state = PicopassPollerStateSuccess;
  510. } while(false);
  511. return command;
  512. }
  513. NfcCommand picopass_poller_success_handler(PicopassPoller* instance) {
  514. NfcCommand command = NfcCommandContinue;
  515. instance->event.type = PicopassPollerEventTypeSuccess;
  516. command = instance->callback(instance->event, instance->context);
  517. furi_delay_ms(100);
  518. return command;
  519. }
  520. NfcCommand picopass_poller_fail_handler(PicopassPoller* instance) {
  521. NfcCommand command = NfcCommandReset;
  522. instance->event.type = PicopassPollerEventTypeFail;
  523. command = instance->callback(instance->event, instance->context);
  524. picopass_poller_reset(instance);
  525. instance->state = PicopassPollerStateDetect;
  526. return command;
  527. }
  528. NfcCommand picopass_poller_auth_fail_handler(PicopassPoller* instance) {
  529. NfcCommand command = NfcCommandReset;
  530. instance->event.type = PicopassPollerEventTypeAuthFail;
  531. command = instance->callback(instance->event, instance->context);
  532. picopass_poller_reset(instance);
  533. instance->state = PicopassPollerStateDetect;
  534. return command;
  535. }
  536. static const PicopassPollerStateHandler picopass_poller_state_handler[PicopassPollerStateNum] = {
  537. [PicopassPollerStateRequestMode] = picopass_poller_request_mode_handler,
  538. [PicopassPollerStateDetect] = picopass_poller_detect_handler,
  539. [PicopassPollerStateSelect] = picopass_poller_select_handler,
  540. [PicopassPollerStatePreAuth] = picopass_poller_pre_auth_handler,
  541. [PicopassPollerStateCheckSecurity] = picopass_poller_check_security,
  542. [PicopassPollerStateNrMacAuth] = picopass_poller_nr_mac_auth,
  543. [PicopassPollerStateAuth] = picopass_poller_auth_handler,
  544. [PicopassPollerStateReadBlock] = picopass_poller_read_block_handler,
  545. [PicopassPollerStateWriteBlock] = picopass_poller_write_block_handler,
  546. [PicopassPollerStateWriteKey] = picopass_poller_write_key_handler,
  547. [PicopassPollerStateParseCredential] = picopass_poller_parse_credential_handler,
  548. [PicopassPollerStateParseWiegand] = picopass_poller_parse_wiegand_handler,
  549. [PicopassPollerStateSuccess] = picopass_poller_success_handler,
  550. [PicopassPollerStateFail] = picopass_poller_fail_handler,
  551. [PicopassPollerStateAuthFail] = picopass_poller_auth_fail_handler,
  552. };
  553. static NfcCommand picopass_poller_callback(NfcEvent event, void* context) {
  554. furi_assert(context);
  555. PicopassPoller* instance = context;
  556. NfcCommand command = NfcCommandContinue;
  557. if(event.type == NfcEventTypePollerReady) {
  558. command = picopass_poller_state_handler[instance->state](instance);
  559. }
  560. if(instance->session_state == PicopassPollerSessionStateStopRequest) {
  561. command = NfcCommandStop;
  562. }
  563. return command;
  564. }
  565. void picopass_poller_start(
  566. PicopassPoller* instance,
  567. PicopassPollerCallback callback,
  568. void* context) {
  569. furi_assert(instance);
  570. furi_assert(instance->session_state == PicopassPollerSessionStateIdle);
  571. instance->callback = callback;
  572. instance->context = context;
  573. instance->session_state = PicopassPollerSessionStateActive;
  574. nfc_start(instance->nfc, picopass_poller_callback, instance);
  575. }
  576. void picopass_poller_stop(PicopassPoller* instance) {
  577. furi_assert(instance);
  578. instance->session_state = PicopassPollerSessionStateStopRequest;
  579. nfc_stop(instance->nfc);
  580. instance->session_state = PicopassPollerSessionStateIdle;
  581. }
  582. PicopassPoller* picopass_poller_alloc(Nfc* nfc) {
  583. furi_assert(nfc);
  584. PicopassPoller* instance = malloc(sizeof(PicopassPoller));
  585. instance->nfc = nfc;
  586. nfc_config(instance->nfc, NfcModePoller, NfcTechIso15693);
  587. nfc_set_guard_time_us(instance->nfc, 10000);
  588. nfc_set_fdt_poll_fc(instance->nfc, 5000);
  589. nfc_set_fdt_poll_poll_us(instance->nfc, 1000);
  590. instance->event.data = &instance->event_data;
  591. instance->data = malloc(sizeof(PicopassDeviceData));
  592. instance->tx_buffer = bit_buffer_alloc(PICOPASS_POLLER_BUFFER_SIZE);
  593. instance->rx_buffer = bit_buffer_alloc(PICOPASS_POLLER_BUFFER_SIZE);
  594. instance->tmp_buffer = bit_buffer_alloc(PICOPASS_POLLER_BUFFER_SIZE);
  595. return instance;
  596. }
  597. void picopass_poller_free(PicopassPoller* instance) {
  598. furi_assert(instance);
  599. free(instance->data);
  600. bit_buffer_free(instance->tx_buffer);
  601. bit_buffer_free(instance->rx_buffer);
  602. bit_buffer_free(instance->tmp_buffer);
  603. free(instance);
  604. }
  605. const PicopassDeviceData* picopass_poller_get_data(PicopassPoller* instance) {
  606. furi_assert(instance);
  607. return instance->data;
  608. }