picopass_poller.c 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670
  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(instance->data->AA1[PICOPASS_SECURE_KD_BLOCK_INDEX].data, 0xff, PICOPASS_BLOCK_LEN);
  251. memset(instance->data->AA1[PICOPASS_SECURE_KC_BLOCK_INDEX].data, 0xff, PICOPASS_BLOCK_LEN);
  252. }
  253. }
  254. } while(false);
  255. furi_string_free(temp_str);
  256. furi_string_free(filename);
  257. flipper_format_free(file);
  258. return command;
  259. }
  260. NfcCommand picopass_poller_auth_handler(PicopassPoller* instance) {
  261. NfcCommand command = NfcCommandContinue;
  262. do {
  263. // Request key
  264. instance->event.type = PicopassPollerEventTypeRequestKey;
  265. command = instance->callback(instance->event, instance->context);
  266. if(command != NfcCommandContinue) break;
  267. if(!instance->event_data.req_key.is_key_provided) {
  268. instance->state = PicopassPollerStateFail;
  269. break;
  270. }
  271. FURI_LOG_D(
  272. TAG,
  273. "Try to %s auth with key %02x%02x%02x%02x%02x%02x%02x%02x",
  274. instance->event_data.req_key.is_elite_key ? "elite" : "standard",
  275. instance->event_data.req_key.key[0],
  276. instance->event_data.req_key.key[1],
  277. instance->event_data.req_key.key[2],
  278. instance->event_data.req_key.key[3],
  279. instance->event_data.req_key.key[4],
  280. instance->event_data.req_key.key[5],
  281. instance->event_data.req_key.key[6],
  282. instance->event_data.req_key.key[7]);
  283. PicopassReadCheckResp read_check_resp = {};
  284. uint8_t* csn = instance->serial_num.data;
  285. memset(instance->div_key, 0, sizeof(instance->div_key));
  286. uint8_t* div_key = NULL;
  287. if(instance->mode == PicopassPollerModeRead) {
  288. div_key = instance->data->AA1[PICOPASS_SECURE_KD_BLOCK_INDEX].data;
  289. } else {
  290. div_key = instance->div_key;
  291. }
  292. uint8_t ccnr[12] = {};
  293. PicopassMac mac = {};
  294. PicopassError error = picopass_poller_read_check(instance, &read_check_resp);
  295. if(error == PicopassErrorTimeout) {
  296. instance->event.type = PicopassPollerEventTypeCardLost;
  297. command = instance->callback(instance->event, instance->context);
  298. instance->state = PicopassPollerStateDetect;
  299. break;
  300. } else if(error != PicopassErrorNone) {
  301. FURI_LOG_E(TAG, "Read check failed: %d", error);
  302. break;
  303. }
  304. memcpy(ccnr, read_check_resp.data, sizeof(PicopassReadCheckResp)); // last 4 bytes left 0
  305. loclass_iclass_calc_div_key(
  306. csn,
  307. instance->event_data.req_key.key,
  308. div_key,
  309. instance->event_data.req_key.is_elite_key);
  310. loclass_opt_doReaderMAC(ccnr, div_key, mac.data);
  311. PicopassCheckResp check_resp = {};
  312. error = picopass_poller_check(instance, NULL, &mac, &check_resp);
  313. if(error == PicopassErrorNone) {
  314. FURI_LOG_I(TAG, "Found key");
  315. memcpy(instance->mac.data, mac.data, sizeof(PicopassMac));
  316. if(instance->mode == PicopassPollerModeRead) {
  317. memcpy(
  318. instance->data->pacs.key, instance->event_data.req_key.key, PICOPASS_KEY_LEN);
  319. instance->data->pacs.elite_kdf = instance->event_data.req_key.is_elite_key;
  320. picopass_poller_prepare_read(instance);
  321. instance->state = PicopassPollerStateReadBlock;
  322. } else if(instance->mode == PicopassPollerModeWrite) {
  323. instance->state = PicopassPollerStateWriteBlock;
  324. } else {
  325. instance->state = PicopassPollerStateWriteKey;
  326. }
  327. }
  328. } while(false);
  329. return command;
  330. }
  331. NfcCommand picopass_poller_read_block_handler(PicopassPoller* instance) {
  332. NfcCommand command = NfcCommandContinue;
  333. do {
  334. if(instance->current_block == instance->app_limit) {
  335. instance->state = PicopassPollerStateParseCredential;
  336. break;
  337. }
  338. if(instance->current_block == PICOPASS_SECURE_KD_BLOCK_INDEX) {
  339. // Skip over Kd block which is populated earlier (READ of Kd returns all FF's)
  340. instance->current_block++;
  341. }
  342. PicopassBlock block = {};
  343. PicopassError error =
  344. picopass_poller_read_block(instance, instance->current_block, &block);
  345. if(error != PicopassErrorNone) {
  346. FURI_LOG_E(TAG, "Failed to read block %d: %d", instance->current_block, error);
  347. instance->state = PicopassPollerStateFail;
  348. break;
  349. }
  350. FURI_LOG_D(
  351. TAG,
  352. "Block %d: %02x%02x%02x%02x%02x%02x%02x%02x",
  353. instance->current_block,
  354. block.data[0],
  355. block.data[1],
  356. block.data[2],
  357. block.data[3],
  358. block.data[4],
  359. block.data[5],
  360. block.data[6],
  361. block.data[7]);
  362. memcpy(
  363. instance->data->AA1[instance->current_block].data, block.data, sizeof(PicopassBlock));
  364. instance->current_block++;
  365. } while(false);
  366. return command;
  367. }
  368. NfcCommand picopass_poller_parse_credential_handler(PicopassPoller* instance) {
  369. NfcCommand command = NfcCommandContinue;
  370. picopass_device_parse_credential(instance->data->AA1, &instance->data->pacs);
  371. instance->state = PicopassPollerStateParseWiegand;
  372. return command;
  373. }
  374. NfcCommand picopass_poller_parse_wiegand_handler(PicopassPoller* instance) {
  375. NfcCommand command = NfcCommandContinue;
  376. picopass_device_parse_wiegand(instance->data->pacs.credential, &instance->data->pacs);
  377. instance->state = PicopassPollerStateSuccess;
  378. return command;
  379. }
  380. NfcCommand picopass_poller_write_block_handler(PicopassPoller* instance) {
  381. NfcCommand command = NfcCommandContinue;
  382. PicopassError error = PicopassErrorNone;
  383. do {
  384. instance->event.type = PicopassPollerEventTypeRequestWriteBlock;
  385. command = instance->callback(instance->event, instance->context);
  386. if(command != NfcCommandContinue) break;
  387. PicopassPollerEventDataRequestWriteBlock* write_block = &instance->event_data.req_write;
  388. if(!write_block->perform_write) {
  389. instance->state = PicopassPollerStateSuccess;
  390. break;
  391. }
  392. FURI_LOG_D(TAG, "Writing %d block", write_block->block_num);
  393. uint8_t data[9] = {};
  394. data[0] = write_block->block_num;
  395. memcpy(&data[1], write_block->block->data, PICOPASS_BLOCK_LEN);
  396. loclass_doMAC_N(data, sizeof(data), instance->div_key, instance->mac.data);
  397. FURI_LOG_D(
  398. TAG,
  399. "loclass_doMAC_N %d %02x%02x%02x%02x%02x%02x%02x%02x %02x%02x%02x%02x",
  400. write_block->block_num,
  401. data[1],
  402. data[2],
  403. data[3],
  404. data[4],
  405. data[5],
  406. data[6],
  407. data[7],
  408. data[8],
  409. instance->mac.data[0],
  410. instance->mac.data[1],
  411. instance->mac.data[2],
  412. instance->mac.data[3]);
  413. error = picopass_poller_write_block(
  414. instance, write_block->block_num, write_block->block, &instance->mac);
  415. if(error != PicopassErrorNone) {
  416. FURI_LOG_E(TAG, "Failed to write block %d. Error %d", write_block->block_num, error);
  417. instance->state = PicopassPollerStateFail;
  418. break;
  419. }
  420. } while(false);
  421. return command;
  422. }
  423. NfcCommand picopass_poller_write_key_handler(PicopassPoller* instance) {
  424. NfcCommand command = NfcCommandContinue;
  425. PicopassError error = PicopassErrorNone;
  426. do {
  427. instance->event.type = PicopassPollerEventTypeRequestWriteKey;
  428. command = instance->callback(instance->event, instance->context);
  429. if(command != NfcCommandContinue) break;
  430. const PicopassDeviceData* picopass_data = instance->event_data.req_write_key.data;
  431. const uint8_t* new_key = instance->event_data.req_write_key.key;
  432. bool is_elite_key = instance->event_data.req_write_key.is_elite_key;
  433. const uint8_t* csn = picopass_data->AA1[PICOPASS_CSN_BLOCK_INDEX].data;
  434. const uint8_t* config_block = picopass_data->AA1[PICOPASS_CONFIG_BLOCK_INDEX].data;
  435. uint8_t fuses = config_block[7];
  436. const uint8_t* old_key = picopass_data->AA1[PICOPASS_SECURE_KD_BLOCK_INDEX].data;
  437. PicopassBlock new_block = {};
  438. loclass_iclass_calc_div_key(csn, new_key, new_block.data, is_elite_key);
  439. if((fuses & 0x80) == 0x80) {
  440. FURI_LOG_D(TAG, "Plain write for personalized mode key change");
  441. } else {
  442. FURI_LOG_D(TAG, "XOR write for application mode key change");
  443. // XOR when in application mode
  444. for(size_t i = 0; i < PICOPASS_BLOCK_LEN; i++) {
  445. new_block.data[i] ^= old_key[i];
  446. }
  447. }
  448. FURI_LOG_D(TAG, "Writing key to %d block", PICOPASS_SECURE_KD_BLOCK_INDEX);
  449. uint8_t data[9] = {};
  450. data[0] = PICOPASS_SECURE_KD_BLOCK_INDEX;
  451. memcpy(&data[1], new_block.data, PICOPASS_BLOCK_LEN);
  452. loclass_doMAC_N(data, sizeof(data), instance->div_key, instance->mac.data);
  453. FURI_LOG_D(
  454. TAG,
  455. "loclass_doMAC_N %d %02x%02x%02x%02x%02x%02x%02x%02x %02x%02x%02x%02x",
  456. PICOPASS_SECURE_KD_BLOCK_INDEX,
  457. data[1],
  458. data[2],
  459. data[3],
  460. data[4],
  461. data[5],
  462. data[6],
  463. data[7],
  464. data[8],
  465. instance->mac.data[0],
  466. instance->mac.data[1],
  467. instance->mac.data[2],
  468. instance->mac.data[3]);
  469. error = picopass_poller_write_block(
  470. instance, PICOPASS_SECURE_KD_BLOCK_INDEX, &new_block, &instance->mac);
  471. if(error != PicopassErrorNone) {
  472. FURI_LOG_E(
  473. TAG, "Failed to write block %d. Error %d", PICOPASS_SECURE_KD_BLOCK_INDEX, error);
  474. instance->state = PicopassPollerStateFail;
  475. break;
  476. }
  477. instance->state = PicopassPollerStateSuccess;
  478. } while(false);
  479. return command;
  480. }
  481. NfcCommand picopass_poller_success_handler(PicopassPoller* instance) {
  482. NfcCommand command = NfcCommandContinue;
  483. instance->event.type = PicopassPollerEventTypeSuccess;
  484. command = instance->callback(instance->event, instance->context);
  485. furi_delay_ms(100);
  486. return command;
  487. }
  488. NfcCommand picopass_poller_fail_handler(PicopassPoller* instance) {
  489. NfcCommand command = NfcCommandReset;
  490. instance->event.type = PicopassPollerEventTypeFail;
  491. command = instance->callback(instance->event, instance->context);
  492. picopass_poller_reset(instance);
  493. instance->state = PicopassPollerStateDetect;
  494. return command;
  495. }
  496. static const PicopassPollerStateHandler picopass_poller_state_handler[PicopassPollerStateNum] = {
  497. [PicopassPollerStateRequestMode] = picopass_poller_request_mode_handler,
  498. [PicopassPollerStateDetect] = picopass_poller_detect_handler,
  499. [PicopassPollerStateSelect] = picopass_poller_select_handler,
  500. [PicopassPollerStatePreAuth] = picopass_poller_pre_auth_handler,
  501. [PicopassPollerStateCheckSecurity] = picopass_poller_check_security,
  502. [PicopassPollerStateNrMacAuth] = picopass_poller_nr_mac_auth,
  503. [PicopassPollerStateAuth] = picopass_poller_auth_handler,
  504. [PicopassPollerStateReadBlock] = picopass_poller_read_block_handler,
  505. [PicopassPollerStateWriteBlock] = picopass_poller_write_block_handler,
  506. [PicopassPollerStateWriteKey] = picopass_poller_write_key_handler,
  507. [PicopassPollerStateParseCredential] = picopass_poller_parse_credential_handler,
  508. [PicopassPollerStateParseWiegand] = picopass_poller_parse_wiegand_handler,
  509. [PicopassPollerStateSuccess] = picopass_poller_success_handler,
  510. [PicopassPollerStateFail] = picopass_poller_fail_handler,
  511. };
  512. static NfcCommand picopass_poller_callback(NfcEvent event, void* context) {
  513. furi_assert(context);
  514. PicopassPoller* instance = context;
  515. NfcCommand command = NfcCommandContinue;
  516. if(event.type == NfcEventTypePollerReady) {
  517. command = picopass_poller_state_handler[instance->state](instance);
  518. }
  519. if(instance->session_state == PicopassPollerSessionStateStopRequest) {
  520. command = NfcCommandStop;
  521. }
  522. return command;
  523. }
  524. void picopass_poller_start(
  525. PicopassPoller* instance,
  526. PicopassPollerCallback callback,
  527. void* context) {
  528. furi_assert(instance);
  529. furi_assert(instance->session_state == PicopassPollerSessionStateIdle);
  530. instance->callback = callback;
  531. instance->context = context;
  532. instance->session_state = PicopassPollerSessionStateActive;
  533. nfc_start(instance->nfc, picopass_poller_callback, instance);
  534. }
  535. void picopass_poller_stop(PicopassPoller* instance) {
  536. furi_assert(instance);
  537. instance->session_state = PicopassPollerSessionStateStopRequest;
  538. nfc_stop(instance->nfc);
  539. instance->session_state = PicopassPollerSessionStateIdle;
  540. }
  541. PicopassPoller* picopass_poller_alloc(Nfc* nfc) {
  542. furi_assert(nfc);
  543. PicopassPoller* instance = malloc(sizeof(PicopassPoller));
  544. instance->nfc = nfc;
  545. nfc_config(instance->nfc, NfcModePoller, NfcTechIso15693);
  546. nfc_set_guard_time_us(instance->nfc, 10000);
  547. nfc_set_fdt_poll_fc(instance->nfc, 5000);
  548. nfc_set_fdt_poll_poll_us(instance->nfc, 1000);
  549. instance->event.data = &instance->event_data;
  550. instance->data = malloc(sizeof(PicopassDeviceData));
  551. instance->tx_buffer = bit_buffer_alloc(PICOPASS_POLLER_BUFFER_SIZE);
  552. instance->rx_buffer = bit_buffer_alloc(PICOPASS_POLLER_BUFFER_SIZE);
  553. instance->tmp_buffer = bit_buffer_alloc(PICOPASS_POLLER_BUFFER_SIZE);
  554. return instance;
  555. }
  556. void picopass_poller_free(PicopassPoller* instance) {
  557. furi_assert(instance);
  558. free(instance->data);
  559. bit_buffer_free(instance->tx_buffer);
  560. bit_buffer_free(instance->rx_buffer);
  561. bit_buffer_free(instance->tmp_buffer);
  562. free(instance);
  563. }
  564. const PicopassDeviceData* picopass_poller_get_data(PicopassPoller* instance) {
  565. furi_assert(instance);
  566. return instance->data;
  567. }