picopass_poller.c 25 KB

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