picopass_poller.c 25 KB

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