picopass_worker.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696
  1. #include "picopass_worker_i.h"
  2. #include <flipper_format/flipper_format.h>
  3. #define TAG "PicopassWorker"
  4. const uint8_t picopass_iclass_key[] = {0xaf, 0xa7, 0x85, 0xa7, 0xda, 0xb3, 0x33, 0x78};
  5. const uint8_t picopass_factory_credit_key[] = {0x76, 0x65, 0x54, 0x43, 0x32, 0x21, 0x10, 0x00};
  6. const uint8_t picopass_factory_debit_key[] = {0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87};
  7. const uint8_t picopass_xice_key[] = {0x20, 0x20, 0x66, 0x66, 0x66, 0x66, 0x88, 0x88};
  8. const uint8_t picopass_xicl_key[] = {0x20, 0x20, 0x66, 0x66, 0x66, 0x66, 0x88, 0x88};
  9. const uint8_t picopass_xics_key[] = {0x66, 0x66, 0x20, 0x20, 0x66, 0x66, 0x88, 0x88};
  10. static void picopass_worker_enable_field() {
  11. furi_hal_nfc_ll_txrx_on();
  12. furi_hal_nfc_exit_sleep();
  13. furi_hal_nfc_ll_poll();
  14. }
  15. static ReturnCode picopass_worker_disable_field(ReturnCode rc) {
  16. furi_hal_nfc_ll_txrx_off();
  17. furi_hal_nfc_start_sleep();
  18. return rc;
  19. }
  20. /***************************** Picopass Worker API *******************************/
  21. PicopassWorker* picopass_worker_alloc() {
  22. PicopassWorker* picopass_worker = malloc(sizeof(PicopassWorker));
  23. // Worker thread attributes
  24. picopass_worker->thread =
  25. furi_thread_alloc_ex("PicopassWorker", 8192, picopass_worker_task, picopass_worker);
  26. picopass_worker->callback = NULL;
  27. picopass_worker->context = NULL;
  28. picopass_worker->storage = furi_record_open(RECORD_STORAGE);
  29. picopass_worker_change_state(picopass_worker, PicopassWorkerStateReady);
  30. return picopass_worker;
  31. }
  32. void picopass_worker_free(PicopassWorker* picopass_worker) {
  33. furi_assert(picopass_worker);
  34. furi_thread_free(picopass_worker->thread);
  35. furi_record_close(RECORD_STORAGE);
  36. free(picopass_worker);
  37. }
  38. PicopassWorkerState picopass_worker_get_state(PicopassWorker* picopass_worker) {
  39. return picopass_worker->state;
  40. }
  41. void picopass_worker_start(
  42. PicopassWorker* picopass_worker,
  43. PicopassWorkerState state,
  44. PicopassDeviceData* dev_data,
  45. PicopassWorkerCallback callback,
  46. void* context) {
  47. furi_assert(picopass_worker);
  48. furi_assert(dev_data);
  49. picopass_worker->callback = callback;
  50. picopass_worker->context = context;
  51. picopass_worker->dev_data = dev_data;
  52. picopass_worker_change_state(picopass_worker, state);
  53. furi_thread_start(picopass_worker->thread);
  54. }
  55. void picopass_worker_stop(PicopassWorker* picopass_worker) {
  56. furi_assert(picopass_worker);
  57. if(picopass_worker->state == PicopassWorkerStateBroken ||
  58. picopass_worker->state == PicopassWorkerStateReady) {
  59. return;
  60. }
  61. picopass_worker_disable_field(ERR_NONE);
  62. picopass_worker_change_state(picopass_worker, PicopassWorkerStateStop);
  63. furi_thread_join(picopass_worker->thread);
  64. }
  65. void picopass_worker_change_state(PicopassWorker* picopass_worker, PicopassWorkerState state) {
  66. picopass_worker->state = state;
  67. }
  68. /***************************** Picopass Worker Thread *******************************/
  69. ReturnCode picopass_detect_card(int timeout) {
  70. UNUSED(timeout);
  71. ReturnCode err;
  72. err = rfalPicoPassPollerInitialize();
  73. if(err != ERR_NONE) {
  74. FURI_LOG_E(TAG, "rfalPicoPassPollerInitialize error %d", err);
  75. return err;
  76. }
  77. err = rfalFieldOnAndStartGT();
  78. if(err != ERR_NONE) {
  79. FURI_LOG_E(TAG, "rfalFieldOnAndStartGT error %d", err);
  80. return err;
  81. }
  82. err = rfalPicoPassPollerCheckPresence();
  83. if(err != ERR_RF_COLLISION) {
  84. FURI_LOG_E(TAG, "rfalPicoPassPollerCheckPresence error %d", err);
  85. return err;
  86. }
  87. return ERR_NONE;
  88. }
  89. ReturnCode picopass_read_preauth(PicopassBlock* AA1) {
  90. rfalPicoPassIdentifyRes idRes;
  91. rfalPicoPassSelectRes selRes;
  92. ReturnCode err;
  93. err = rfalPicoPassPollerIdentify(&idRes);
  94. if(err != ERR_NONE) {
  95. FURI_LOG_E(TAG, "rfalPicoPassPollerIdentify error %d", err);
  96. return err;
  97. }
  98. err = rfalPicoPassPollerSelect(idRes.CSN, &selRes);
  99. if(err != ERR_NONE) {
  100. FURI_LOG_E(TAG, "rfalPicoPassPollerSelect error %d", err);
  101. return err;
  102. }
  103. memcpy(AA1[PICOPASS_CSN_BLOCK_INDEX].data, selRes.CSN, sizeof(selRes.CSN));
  104. FURI_LOG_D(
  105. TAG,
  106. "csn %02x%02x%02x%02x%02x%02x%02x%02x",
  107. AA1[PICOPASS_CSN_BLOCK_INDEX].data[0],
  108. AA1[PICOPASS_CSN_BLOCK_INDEX].data[1],
  109. AA1[PICOPASS_CSN_BLOCK_INDEX].data[2],
  110. AA1[PICOPASS_CSN_BLOCK_INDEX].data[3],
  111. AA1[PICOPASS_CSN_BLOCK_INDEX].data[4],
  112. AA1[PICOPASS_CSN_BLOCK_INDEX].data[5],
  113. AA1[PICOPASS_CSN_BLOCK_INDEX].data[6],
  114. AA1[PICOPASS_CSN_BLOCK_INDEX].data[7]);
  115. rfalPicoPassReadBlockRes cfg = {0};
  116. rfalPicoPassPollerReadBlock(PICOPASS_CONFIG_BLOCK_INDEX, &cfg);
  117. memcpy(AA1[PICOPASS_CONFIG_BLOCK_INDEX].data, cfg.data, sizeof(cfg.data));
  118. FURI_LOG_D(
  119. TAG,
  120. "config %02x%02x%02x%02x%02x%02x%02x%02x",
  121. AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[0],
  122. AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[1],
  123. AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[2],
  124. AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[3],
  125. AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[4],
  126. AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[5],
  127. AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[6],
  128. AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[7]);
  129. rfalPicoPassReadBlockRes aia;
  130. rfalPicoPassPollerReadBlock(PICOPASS_AIA_BLOCK_INDEX, &aia);
  131. memcpy(AA1[PICOPASS_AIA_BLOCK_INDEX].data, aia.data, sizeof(aia.data));
  132. FURI_LOG_D(
  133. TAG,
  134. "aia %02x%02x%02x%02x%02x%02x%02x%02x",
  135. AA1[PICOPASS_AIA_BLOCK_INDEX].data[0],
  136. AA1[PICOPASS_AIA_BLOCK_INDEX].data[1],
  137. AA1[PICOPASS_AIA_BLOCK_INDEX].data[2],
  138. AA1[PICOPASS_AIA_BLOCK_INDEX].data[3],
  139. AA1[PICOPASS_AIA_BLOCK_INDEX].data[4],
  140. AA1[PICOPASS_AIA_BLOCK_INDEX].data[5],
  141. AA1[PICOPASS_AIA_BLOCK_INDEX].data[6],
  142. AA1[PICOPASS_AIA_BLOCK_INDEX].data[7]);
  143. return ERR_NONE;
  144. }
  145. static ReturnCode picopass_auth_standard(uint8_t* csn, uint8_t* div_key) {
  146. rfalPicoPassReadCheckRes rcRes;
  147. rfalPicoPassCheckRes chkRes;
  148. ReturnCode err;
  149. uint8_t mac[4] = {0};
  150. uint8_t ccnr[12] = {0};
  151. err = rfalPicoPassPollerReadCheck(&rcRes);
  152. if(err != ERR_NONE) {
  153. FURI_LOG_E(TAG, "rfalPicoPassPollerReadCheck error %d", err);
  154. return err;
  155. }
  156. memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0
  157. loclass_iclass_calc_div_key(csn, (uint8_t*)picopass_iclass_key, div_key, false);
  158. loclass_opt_doReaderMAC(ccnr, div_key, mac);
  159. return rfalPicoPassPollerCheck(mac, &chkRes);
  160. }
  161. static ReturnCode picopass_auth_factory(uint8_t* csn, uint8_t* div_key) {
  162. rfalPicoPassReadCheckRes rcRes;
  163. rfalPicoPassCheckRes chkRes;
  164. ReturnCode err;
  165. uint8_t mac[4] = {0};
  166. uint8_t ccnr[12] = {0};
  167. err = rfalPicoPassPollerReadCheck(&rcRes);
  168. if(err != ERR_NONE) {
  169. FURI_LOG_E(TAG, "rfalPicoPassPollerReadCheck error %d", err);
  170. return err;
  171. }
  172. memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0
  173. loclass_iclass_calc_div_key(csn, (uint8_t*)picopass_factory_debit_key, div_key, false);
  174. loclass_opt_doReaderMAC(ccnr, div_key, mac);
  175. return rfalPicoPassPollerCheck(mac, &chkRes);
  176. }
  177. static ReturnCode picopass_auth_dict(
  178. uint8_t* csn,
  179. PicopassPacs* pacs,
  180. uint8_t* div_key,
  181. IclassEliteDictType dict_type,
  182. bool elite) {
  183. rfalPicoPassReadCheckRes rcRes;
  184. rfalPicoPassCheckRes chkRes;
  185. ReturnCode err = ERR_PARAM;
  186. uint8_t mac[4] = {0};
  187. uint8_t ccnr[12] = {0};
  188. size_t index = 0;
  189. uint8_t key[PICOPASS_BLOCK_LEN] = {0};
  190. if(!iclass_elite_dict_check_presence(dict_type)) {
  191. FURI_LOG_E(TAG, "Dictionary not found");
  192. return ERR_PARAM;
  193. }
  194. IclassEliteDict* dict = iclass_elite_dict_alloc(dict_type);
  195. if(!dict) {
  196. FURI_LOG_E(TAG, "Dictionary not allocated");
  197. return ERR_PARAM;
  198. }
  199. FURI_LOG_D(TAG, "Loaded %lu keys", iclass_elite_dict_get_total_keys(dict));
  200. while(iclass_elite_dict_get_next_key(dict, key)) {
  201. FURI_LOG_D(
  202. TAG,
  203. "Try to auth with key %zu %02x%02x%02x%02x%02x%02x%02x%02x",
  204. index++,
  205. key[0],
  206. key[1],
  207. key[2],
  208. key[3],
  209. key[4],
  210. key[5],
  211. key[6],
  212. key[7]);
  213. err = rfalPicoPassPollerReadCheck(&rcRes);
  214. if(err != ERR_NONE) {
  215. FURI_LOG_E(TAG, "rfalPicoPassPollerReadCheck error %d", err);
  216. break;
  217. }
  218. memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0
  219. loclass_iclass_calc_div_key(csn, key, div_key, elite);
  220. loclass_opt_doReaderMAC(ccnr, div_key, mac);
  221. err = rfalPicoPassPollerCheck(mac, &chkRes);
  222. if(err == ERR_NONE) {
  223. memcpy(pacs->key, key, PICOPASS_BLOCK_LEN);
  224. break;
  225. }
  226. }
  227. iclass_elite_dict_free(dict);
  228. return err;
  229. }
  230. ReturnCode picopass_auth(PicopassBlock* AA1, PicopassPacs* pacs) {
  231. ReturnCode err;
  232. FURI_LOG_I(TAG, "Trying standard legacy key");
  233. err = picopass_auth_standard(
  234. AA1[PICOPASS_CSN_BLOCK_INDEX].data, AA1[PICOPASS_KD_BLOCK_INDEX].data);
  235. if(err == ERR_NONE) {
  236. memcpy(pacs->key, picopass_iclass_key, PICOPASS_BLOCK_LEN);
  237. return ERR_NONE;
  238. }
  239. FURI_LOG_I(TAG, "Trying factory default key");
  240. err = picopass_auth_factory(
  241. AA1[PICOPASS_CSN_BLOCK_INDEX].data, AA1[PICOPASS_KD_BLOCK_INDEX].data);
  242. if(err == ERR_NONE) {
  243. memcpy(pacs->key, picopass_factory_debit_key, PICOPASS_BLOCK_LEN);
  244. return ERR_NONE;
  245. }
  246. FURI_LOG_I(TAG, "Starting user dictionary attack [Elite KDF]");
  247. err = picopass_auth_dict(
  248. AA1[PICOPASS_CSN_BLOCK_INDEX].data,
  249. pacs,
  250. AA1[PICOPASS_KD_BLOCK_INDEX].data,
  251. IclassEliteDictTypeUser,
  252. true);
  253. if(err == ERR_NONE) {
  254. return ERR_NONE;
  255. }
  256. FURI_LOG_I(TAG, "Starting system dictionary attack [Elite KDF]");
  257. err = picopass_auth_dict(
  258. AA1[PICOPASS_CSN_BLOCK_INDEX].data,
  259. pacs,
  260. AA1[PICOPASS_KD_BLOCK_INDEX].data,
  261. IclassEliteDictTypeFlipper,
  262. true);
  263. if(err == ERR_NONE) {
  264. return ERR_NONE;
  265. }
  266. FURI_LOG_I(TAG, "Starting system dictionary attack [Standard KDF]");
  267. err = picopass_auth_dict(
  268. AA1[PICOPASS_CSN_BLOCK_INDEX].data,
  269. pacs,
  270. AA1[PICOPASS_KD_BLOCK_INDEX].data,
  271. IclassEliteDictTypeFlipper,
  272. false);
  273. if(err == ERR_NONE) {
  274. return ERR_NONE;
  275. }
  276. return err;
  277. }
  278. ReturnCode picopass_read_card(PicopassBlock* AA1) {
  279. ReturnCode err;
  280. size_t app_limit = AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[0] < PICOPASS_MAX_APP_LIMIT ?
  281. AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[0] :
  282. PICOPASS_MAX_APP_LIMIT;
  283. for(size_t i = 2; i < app_limit; i++) {
  284. if(i == PICOPASS_KD_BLOCK_INDEX) {
  285. // Skip over Kd block which is populated earlier (READ of Kd returns all FF's)
  286. continue;
  287. }
  288. rfalPicoPassReadBlockRes block;
  289. err = rfalPicoPassPollerReadBlock(i, &block);
  290. if(err != ERR_NONE) {
  291. FURI_LOG_E(TAG, "rfalPicoPassPollerReadBlock error %d", err);
  292. return err;
  293. }
  294. FURI_LOG_D(
  295. TAG,
  296. "rfalPicoPassPollerReadBlock %d %02x%02x%02x%02x%02x%02x%02x%02x",
  297. i,
  298. block.data[0],
  299. block.data[1],
  300. block.data[2],
  301. block.data[3],
  302. block.data[4],
  303. block.data[5],
  304. block.data[6],
  305. block.data[7]);
  306. memcpy(AA1[i].data, block.data, sizeof(block.data));
  307. }
  308. return ERR_NONE;
  309. }
  310. ReturnCode picopass_write_card(PicopassBlock* AA1) {
  311. rfalPicoPassIdentifyRes idRes;
  312. rfalPicoPassSelectRes selRes;
  313. rfalPicoPassReadCheckRes rcRes;
  314. rfalPicoPassCheckRes chkRes;
  315. ReturnCode err;
  316. uint8_t div_key[8] = {0};
  317. uint8_t mac[4] = {0};
  318. uint8_t ccnr[12] = {0};
  319. err = rfalPicoPassPollerIdentify(&idRes);
  320. if(err != ERR_NONE) {
  321. FURI_LOG_E(TAG, "rfalPicoPassPollerIdentify error %d", err);
  322. return err;
  323. }
  324. err = rfalPicoPassPollerSelect(idRes.CSN, &selRes);
  325. if(err != ERR_NONE) {
  326. FURI_LOG_E(TAG, "rfalPicoPassPollerSelect error %d", err);
  327. return err;
  328. }
  329. err = rfalPicoPassPollerReadCheck(&rcRes);
  330. if(err != ERR_NONE) {
  331. FURI_LOG_E(TAG, "rfalPicoPassPollerReadCheck error %d", err);
  332. return err;
  333. }
  334. memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0
  335. loclass_iclass_calc_div_key(selRes.CSN, (uint8_t*)picopass_iclass_key, div_key, false);
  336. loclass_opt_doReaderMAC(ccnr, div_key, mac);
  337. err = rfalPicoPassPollerCheck(mac, &chkRes);
  338. if(err != ERR_NONE) {
  339. FURI_LOG_E(TAG, "rfalPicoPassPollerCheck error %d", err);
  340. return err;
  341. }
  342. for(size_t i = 6; i < 10; i++) {
  343. FURI_LOG_D(TAG, "rfalPicoPassPollerWriteBlock %d", i);
  344. uint8_t data[9] = {0};
  345. data[0] = i;
  346. memcpy(data + 1, AA1[i].data, RFAL_PICOPASS_MAX_BLOCK_LEN);
  347. loclass_doMAC_N(data, sizeof(data), div_key, mac);
  348. FURI_LOG_D(
  349. TAG,
  350. "loclass_doMAC_N %d %02x%02x%02x%02x%02x%02x%02x%02x %02x%02x%02x%02x",
  351. i,
  352. data[1],
  353. data[2],
  354. data[3],
  355. data[4],
  356. data[5],
  357. data[6],
  358. data[7],
  359. data[8],
  360. mac[0],
  361. mac[1],
  362. mac[2],
  363. mac[3]);
  364. err = rfalPicoPassPollerWriteBlock(i, AA1[i].data, mac);
  365. if(err != ERR_NONE) {
  366. FURI_LOG_E(TAG, "rfalPicoPassPollerWriteBlock error %d", err);
  367. return err;
  368. }
  369. }
  370. return ERR_NONE;
  371. }
  372. ReturnCode picopass_write_block(PicopassBlock* AA1, uint8_t blockNo, uint8_t* newBlock) {
  373. rfalPicoPassIdentifyRes idRes;
  374. rfalPicoPassSelectRes selRes;
  375. rfalPicoPassReadCheckRes rcRes;
  376. rfalPicoPassCheckRes chkRes;
  377. ReturnCode err;
  378. uint8_t mac[4] = {0};
  379. uint8_t ccnr[12] = {0};
  380. err = rfalPicoPassPollerIdentify(&idRes);
  381. if(err != ERR_NONE) {
  382. FURI_LOG_E(TAG, "rfalPicoPassPollerIdentify error %d", err);
  383. return err;
  384. }
  385. err = rfalPicoPassPollerSelect(idRes.CSN, &selRes);
  386. if(err != ERR_NONE) {
  387. FURI_LOG_E(TAG, "rfalPicoPassPollerSelect error %d", err);
  388. return err;
  389. }
  390. err = rfalPicoPassPollerReadCheck(&rcRes);
  391. if(err != ERR_NONE) {
  392. FURI_LOG_E(TAG, "rfalPicoPassPollerReadCheck error %d", err);
  393. return err;
  394. }
  395. memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0
  396. if(memcmp(selRes.CSN, AA1[PICOPASS_CSN_BLOCK_INDEX].data, PICOPASS_BLOCK_LEN) != 0) {
  397. FURI_LOG_E(TAG, "Wrong CSN for write");
  398. return ERR_REQUEST;
  399. }
  400. loclass_opt_doReaderMAC(ccnr, AA1[PICOPASS_KD_BLOCK_INDEX].data, mac);
  401. err = rfalPicoPassPollerCheck(mac, &chkRes);
  402. if(err != ERR_NONE) {
  403. FURI_LOG_E(TAG, "rfalPicoPassPollerCheck error %d", err);
  404. return err;
  405. }
  406. FURI_LOG_D(TAG, "rfalPicoPassPollerWriteBlock %d", blockNo);
  407. uint8_t data[9] = {
  408. blockNo,
  409. newBlock[0],
  410. newBlock[1],
  411. newBlock[2],
  412. newBlock[3],
  413. newBlock[4],
  414. newBlock[5],
  415. newBlock[6],
  416. newBlock[7]};
  417. loclass_doMAC_N(data, sizeof(data), AA1[PICOPASS_KD_BLOCK_INDEX].data, mac);
  418. FURI_LOG_D(
  419. TAG,
  420. "loclass_doMAC_N %d %02x%02x%02x%02x%02x%02x%02x%02x %02x%02x%02x%02x",
  421. blockNo,
  422. data[1],
  423. data[2],
  424. data[3],
  425. data[4],
  426. data[5],
  427. data[6],
  428. data[7],
  429. data[8],
  430. mac[0],
  431. mac[1],
  432. mac[2],
  433. mac[3]);
  434. err = rfalPicoPassPollerWriteBlock(data[0], data + 1, mac);
  435. if(err != ERR_NONE) {
  436. FURI_LOG_E(TAG, "rfalPicoPassPollerWriteBlock error %d", err);
  437. return err;
  438. }
  439. return ERR_NONE;
  440. }
  441. int32_t picopass_worker_task(void* context) {
  442. PicopassWorker* picopass_worker = context;
  443. picopass_worker_enable_field();
  444. if(picopass_worker->state == PicopassWorkerStateDetect) {
  445. picopass_worker_detect(picopass_worker);
  446. } else if(picopass_worker->state == PicopassWorkerStateWrite) {
  447. picopass_worker_write(picopass_worker);
  448. } else if(picopass_worker->state == PicopassWorkerStateWriteKey) {
  449. picopass_worker_write_key(picopass_worker);
  450. }
  451. picopass_worker_disable_field(ERR_NONE);
  452. picopass_worker_change_state(picopass_worker, PicopassWorkerStateReady);
  453. return 0;
  454. }
  455. void picopass_worker_detect(PicopassWorker* picopass_worker) {
  456. picopass_device_data_clear(picopass_worker->dev_data);
  457. PicopassDeviceData* dev_data = picopass_worker->dev_data;
  458. PicopassBlock* AA1 = dev_data->AA1;
  459. PicopassPacs* pacs = &dev_data->pacs;
  460. ReturnCode err;
  461. // reset device data
  462. for(size_t i = 0; i < PICOPASS_MAX_APP_LIMIT; i++) {
  463. memset(AA1[i].data, 0, sizeof(AA1[i].data));
  464. }
  465. memset(pacs, 0, sizeof(PicopassPacs));
  466. PicopassWorkerEvent nextState = PicopassWorkerEventSuccess;
  467. while(picopass_worker->state == PicopassWorkerStateDetect) {
  468. if(picopass_detect_card(1000) == ERR_NONE) {
  469. // Process first found device
  470. err = picopass_read_preauth(AA1);
  471. if(err != ERR_NONE) {
  472. FURI_LOG_E(TAG, "picopass_read_preauth error %d", err);
  473. nextState = PicopassWorkerEventFail;
  474. }
  475. // Thank you proxmark!
  476. pacs->legacy = picopass_is_memset(AA1[5].data, 0xFF, 8);
  477. pacs->se_enabled = (memcmp(AA1[5].data, "\xff\xff\xff\x00\x06\xff\xff\xff", 8) == 0);
  478. if(pacs->se_enabled) {
  479. FURI_LOG_D(TAG, "SE enabled");
  480. nextState = PicopassWorkerEventFail;
  481. }
  482. if(nextState == PicopassWorkerEventSuccess) {
  483. err = picopass_auth(AA1, pacs);
  484. if(err != ERR_NONE) {
  485. FURI_LOG_E(TAG, "picopass_try_auth error %d", err);
  486. nextState = PicopassWorkerEventFail;
  487. }
  488. }
  489. if(nextState == PicopassWorkerEventSuccess) {
  490. err = picopass_read_card(AA1);
  491. if(err != ERR_NONE) {
  492. FURI_LOG_E(TAG, "picopass_read_card error %d", err);
  493. nextState = PicopassWorkerEventFail;
  494. }
  495. }
  496. if(nextState == PicopassWorkerEventSuccess) {
  497. err = picopass_device_parse_credential(AA1, pacs);
  498. if(err != ERR_NONE) {
  499. FURI_LOG_E(TAG, "picopass_device_parse_credential error %d", err);
  500. nextState = PicopassWorkerEventFail;
  501. }
  502. }
  503. if(nextState == PicopassWorkerEventSuccess) {
  504. err = picopass_device_parse_wiegand(pacs->credential, &pacs->record);
  505. if(err != ERR_NONE) {
  506. FURI_LOG_E(TAG, "picopass_device_parse_wiegand error %d", err);
  507. nextState = PicopassWorkerEventFail;
  508. }
  509. }
  510. // Notify caller and exit
  511. if(picopass_worker->callback) {
  512. picopass_worker->callback(nextState, picopass_worker->context);
  513. }
  514. break;
  515. }
  516. furi_delay_ms(100);
  517. }
  518. }
  519. void picopass_worker_write(PicopassWorker* picopass_worker) {
  520. PicopassDeviceData* dev_data = picopass_worker->dev_data;
  521. PicopassBlock* AA1 = dev_data->AA1;
  522. ReturnCode err;
  523. PicopassWorkerEvent nextState = PicopassWorkerEventSuccess;
  524. while(picopass_worker->state == PicopassWorkerStateWrite) {
  525. if(picopass_detect_card(1000) == ERR_NONE) {
  526. err = picopass_write_card(AA1);
  527. if(err != ERR_NONE) {
  528. FURI_LOG_E(TAG, "picopass_write_card error %d", err);
  529. nextState = PicopassWorkerEventFail;
  530. }
  531. // Notify caller and exit
  532. if(picopass_worker->callback) {
  533. picopass_worker->callback(nextState, picopass_worker->context);
  534. }
  535. break;
  536. }
  537. furi_delay_ms(100);
  538. }
  539. }
  540. void picopass_worker_write_key(PicopassWorker* picopass_worker) {
  541. PicopassDeviceData* dev_data = picopass_worker->dev_data;
  542. PicopassBlock* AA1 = dev_data->AA1;
  543. PicopassPacs* pacs = &dev_data->pacs;
  544. ReturnCode err;
  545. PicopassWorkerEvent nextState = PicopassWorkerEventSuccess;
  546. uint8_t* csn = AA1[PICOPASS_CSN_BLOCK_INDEX].data;
  547. uint8_t* configBlock = AA1[PICOPASS_CONFIG_BLOCK_INDEX].data;
  548. uint8_t fuses = configBlock[7];
  549. uint8_t* oldKey = AA1[PICOPASS_KD_BLOCK_INDEX].data;
  550. uint8_t newKey[PICOPASS_BLOCK_LEN] = {0};
  551. loclass_iclass_calc_div_key(csn, pacs->key, newKey, false);
  552. if((fuses & 0x80) == 0x80) {
  553. FURI_LOG_D(TAG, "Plain write for personalized mode key change");
  554. } else {
  555. FURI_LOG_D(TAG, "XOR write for application mode key change");
  556. // XOR when in application mode
  557. for(size_t i = 0; i < PICOPASS_BLOCK_LEN; i++) {
  558. newKey[i] ^= oldKey[i];
  559. }
  560. }
  561. while(picopass_worker->state == PicopassWorkerStateWriteKey) {
  562. if(picopass_detect_card(1000) == ERR_NONE) {
  563. err = picopass_write_block(AA1, PICOPASS_KD_BLOCK_INDEX, newKey);
  564. if(err != ERR_NONE) {
  565. FURI_LOG_E(TAG, "picopass_write_block error %d", err);
  566. nextState = PicopassWorkerEventFail;
  567. }
  568. // Notify caller and exit
  569. if(picopass_worker->callback) {
  570. picopass_worker->callback(nextState, picopass_worker->context);
  571. }
  572. break;
  573. }
  574. furi_delay_ms(100);
  575. }
  576. }