picopass_poller.c 26 KB

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