picopass_worker.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. #include "picopass_worker_i.h"
  2. #define TAG "PicopassWorker"
  3. const uint8_t picopass_iclass_key[] = {0xaf, 0xa7, 0x85, 0xa7, 0xda, 0xb3, 0x33, 0x78};
  4. const uint8_t picopass_factory_key[] = {0x76, 0x65, 0x54, 0x43, 0x32, 0x21, 0x10, 0x00};
  5. static void picopass_worker_enable_field() {
  6. furi_hal_nfc_ll_txrx_on();
  7. furi_hal_nfc_exit_sleep();
  8. furi_hal_nfc_ll_poll();
  9. }
  10. static ReturnCode picopass_worker_disable_field(ReturnCode rc) {
  11. furi_hal_nfc_ll_txrx_off();
  12. furi_hal_nfc_start_sleep();
  13. return rc;
  14. }
  15. /***************************** Picopass Worker API *******************************/
  16. PicopassWorker* picopass_worker_alloc() {
  17. PicopassWorker* picopass_worker = malloc(sizeof(PicopassWorker));
  18. // Worker thread attributes
  19. picopass_worker->thread = furi_thread_alloc();
  20. furi_thread_set_name(picopass_worker->thread, "PicopassWorker");
  21. furi_thread_set_stack_size(picopass_worker->thread, 8192);
  22. furi_thread_set_callback(picopass_worker->thread, picopass_worker_task);
  23. furi_thread_set_context(picopass_worker->thread, picopass_worker);
  24. picopass_worker->callback = NULL;
  25. picopass_worker->context = NULL;
  26. picopass_worker->storage = furi_record_open(RECORD_STORAGE);
  27. picopass_worker_change_state(picopass_worker, PicopassWorkerStateReady);
  28. return picopass_worker;
  29. }
  30. void picopass_worker_free(PicopassWorker* picopass_worker) {
  31. furi_assert(picopass_worker);
  32. furi_thread_free(picopass_worker->thread);
  33. furi_record_close(RECORD_STORAGE);
  34. free(picopass_worker);
  35. }
  36. PicopassWorkerState picopass_worker_get_state(PicopassWorker* picopass_worker) {
  37. return picopass_worker->state;
  38. }
  39. void picopass_worker_start(
  40. PicopassWorker* picopass_worker,
  41. PicopassWorkerState state,
  42. PicopassDeviceData* dev_data,
  43. PicopassWorkerCallback callback,
  44. void* context) {
  45. furi_assert(picopass_worker);
  46. furi_assert(dev_data);
  47. picopass_worker->callback = callback;
  48. picopass_worker->context = context;
  49. picopass_worker->dev_data = dev_data;
  50. picopass_worker_change_state(picopass_worker, state);
  51. furi_thread_start(picopass_worker->thread);
  52. }
  53. void picopass_worker_stop(PicopassWorker* picopass_worker) {
  54. furi_assert(picopass_worker);
  55. if(picopass_worker->state == PicopassWorkerStateBroken ||
  56. picopass_worker->state == PicopassWorkerStateReady) {
  57. return;
  58. }
  59. picopass_worker_disable_field(ERR_NONE);
  60. picopass_worker_change_state(picopass_worker, PicopassWorkerStateStop);
  61. furi_thread_join(picopass_worker->thread);
  62. }
  63. void picopass_worker_change_state(PicopassWorker* picopass_worker, PicopassWorkerState state) {
  64. picopass_worker->state = state;
  65. }
  66. /***************************** Picopass Worker Thread *******************************/
  67. ReturnCode picopass_detect_card(int timeout) {
  68. UNUSED(timeout);
  69. ReturnCode err;
  70. err = rfalPicoPassPollerInitialize();
  71. if(err != ERR_NONE) {
  72. FURI_LOG_E(TAG, "rfalPicoPassPollerInitialize error %d", err);
  73. return err;
  74. }
  75. err = rfalFieldOnAndStartGT();
  76. if(err != ERR_NONE) {
  77. FURI_LOG_E(TAG, "rfalFieldOnAndStartGT error %d", err);
  78. return err;
  79. }
  80. err = rfalPicoPassPollerCheckPresence();
  81. if(err != ERR_RF_COLLISION) {
  82. FURI_LOG_E(TAG, "rfalPicoPassPollerCheckPresence error %d", err);
  83. return err;
  84. }
  85. return ERR_NONE;
  86. }
  87. ReturnCode picopass_read_card(PicopassBlock* AA1) {
  88. rfalPicoPassIdentifyRes idRes;
  89. rfalPicoPassSelectRes selRes;
  90. rfalPicoPassReadCheckRes rcRes;
  91. rfalPicoPassCheckRes chkRes;
  92. ReturnCode err;
  93. uint8_t div_key[8] = {0};
  94. uint8_t mac[4] = {0};
  95. uint8_t ccnr[12] = {0};
  96. err = rfalPicoPassPollerIdentify(&idRes);
  97. if(err != ERR_NONE) {
  98. FURI_LOG_E(TAG, "rfalPicoPassPollerIdentify error %d", err);
  99. return err;
  100. }
  101. err = rfalPicoPassPollerSelect(idRes.CSN, &selRes);
  102. if(err != ERR_NONE) {
  103. FURI_LOG_E(TAG, "rfalPicoPassPollerSelect error %d", err);
  104. return err;
  105. }
  106. err = rfalPicoPassPollerReadCheck(&rcRes);
  107. if(err != ERR_NONE) {
  108. FURI_LOG_E(TAG, "rfalPicoPassPollerReadCheck error %d", err);
  109. return err;
  110. }
  111. memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0
  112. loclass_diversifyKey(selRes.CSN, picopass_iclass_key, div_key);
  113. loclass_opt_doReaderMAC(ccnr, div_key, mac);
  114. err = rfalPicoPassPollerCheck(mac, &chkRes);
  115. if(err != ERR_NONE) {
  116. FURI_LOG_E(TAG, "rfalPicoPassPollerCheck error %d", err);
  117. return err;
  118. }
  119. rfalPicoPassReadBlockRes csn;
  120. err = rfalPicoPassPollerReadBlock(PICOPASS_CSN_BLOCK_INDEX, &csn);
  121. memcpy(AA1[PICOPASS_CSN_BLOCK_INDEX].data, csn.data, sizeof(csn.data));
  122. rfalPicoPassReadBlockRes cfg;
  123. err = rfalPicoPassPollerReadBlock(PICOPASS_CONFIG_BLOCK_INDEX, &cfg);
  124. memcpy(AA1[PICOPASS_CONFIG_BLOCK_INDEX].data, cfg.data, sizeof(cfg.data));
  125. size_t app_limit = cfg.data[0] < PICOPASS_MAX_APP_LIMIT ? cfg.data[0] : PICOPASS_MAX_APP_LIMIT;
  126. for(size_t i = 2; i < app_limit; i++) {
  127. FURI_LOG_D(TAG, "rfalPicoPassPollerReadBlock block %d", i);
  128. rfalPicoPassReadBlockRes block;
  129. err = rfalPicoPassPollerReadBlock(i, &block);
  130. if(err != ERR_NONE) {
  131. FURI_LOG_E(TAG, "rfalPicoPassPollerReadBlock error %d", err);
  132. return err;
  133. }
  134. FURI_LOG_D(
  135. TAG,
  136. "rfalPicoPassPollerReadBlock %d %02x%02x%02x%02x%02x%02x%02x%02x",
  137. i,
  138. block.data[0],
  139. block.data[1],
  140. block.data[2],
  141. block.data[3],
  142. block.data[4],
  143. block.data[5],
  144. block.data[6],
  145. block.data[7]);
  146. memcpy(AA1[i].data, block.data, sizeof(block.data));
  147. }
  148. return ERR_NONE;
  149. }
  150. ReturnCode picopass_write_card(PicopassBlock* AA1) {
  151. rfalPicoPassIdentifyRes idRes;
  152. rfalPicoPassSelectRes selRes;
  153. rfalPicoPassReadCheckRes rcRes;
  154. rfalPicoPassCheckRes chkRes;
  155. ReturnCode err;
  156. uint8_t div_key[8] = {0};
  157. uint8_t mac[4] = {0};
  158. uint8_t ccnr[12] = {0};
  159. err = rfalPicoPassPollerIdentify(&idRes);
  160. if(err != ERR_NONE) {
  161. FURI_LOG_E(TAG, "rfalPicoPassPollerIdentify error %d", err);
  162. return err;
  163. }
  164. err = rfalPicoPassPollerSelect(idRes.CSN, &selRes);
  165. if(err != ERR_NONE) {
  166. FURI_LOG_E(TAG, "rfalPicoPassPollerSelect error %d", err);
  167. return err;
  168. }
  169. err = rfalPicoPassPollerReadCheck(&rcRes);
  170. if(err != ERR_NONE) {
  171. FURI_LOG_E(TAG, "rfalPicoPassPollerReadCheck error %d", err);
  172. return err;
  173. }
  174. memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0
  175. loclass_diversifyKey(selRes.CSN, picopass_iclass_key, div_key);
  176. loclass_opt_doReaderMAC(ccnr, div_key, mac);
  177. err = rfalPicoPassPollerCheck(mac, &chkRes);
  178. if(err != ERR_NONE) {
  179. FURI_LOG_E(TAG, "rfalPicoPassPollerCheck error %d", err);
  180. return err;
  181. }
  182. for(size_t i = 6; i < 10; i++) {
  183. FURI_LOG_D(TAG, "rfalPicoPassPollerWriteBlock %d", i);
  184. uint8_t data[9] = {0};
  185. data[0] = i;
  186. memcpy(data + 1, AA1[i].data, RFAL_PICOPASS_MAX_BLOCK_LEN);
  187. loclass_doMAC_N(data, sizeof(data), div_key, mac);
  188. FURI_LOG_D(
  189. TAG,
  190. "loclass_doMAC_N %d %02x%02x%02x%02x%02x%02x%02x%02x %02x%02x%02x%02x",
  191. i,
  192. data[1],
  193. data[2],
  194. data[3],
  195. data[4],
  196. data[5],
  197. data[6],
  198. data[7],
  199. data[8],
  200. mac[0],
  201. mac[1],
  202. mac[2],
  203. mac[3]);
  204. err = rfalPicoPassPollerWriteBlock(i, AA1[i].data, mac);
  205. if(err != ERR_NONE) {
  206. FURI_LOG_E(TAG, "rfalPicoPassPollerWriteBlock error %d", err);
  207. return err;
  208. }
  209. }
  210. return ERR_NONE;
  211. }
  212. int32_t picopass_worker_task(void* context) {
  213. PicopassWorker* picopass_worker = context;
  214. picopass_worker_enable_field();
  215. if(picopass_worker->state == PicopassWorkerStateDetect) {
  216. picopass_worker_detect(picopass_worker);
  217. } else if(picopass_worker->state == PicopassWorkerStateWrite) {
  218. picopass_worker_write(picopass_worker);
  219. }
  220. picopass_worker_disable_field(ERR_NONE);
  221. picopass_worker_change_state(picopass_worker, PicopassWorkerStateReady);
  222. return 0;
  223. }
  224. void picopass_worker_detect(PicopassWorker* picopass_worker) {
  225. picopass_device_data_clear(picopass_worker->dev_data);
  226. PicopassDeviceData* dev_data = picopass_worker->dev_data;
  227. PicopassBlock* AA1 = dev_data->AA1;
  228. PicopassPacs* pacs = &dev_data->pacs;
  229. ReturnCode err;
  230. PicopassWorkerEvent nextState = PicopassWorkerEventSuccess;
  231. while(picopass_worker->state == PicopassWorkerStateDetect) {
  232. if(picopass_detect_card(1000) == ERR_NONE) {
  233. // Process first found device
  234. err = picopass_read_card(AA1);
  235. if(err != ERR_NONE) {
  236. FURI_LOG_E(TAG, "picopass_read_card error %d", err);
  237. nextState = PicopassWorkerEventFail;
  238. }
  239. if(nextState == PicopassWorkerEventSuccess) {
  240. err = picopass_device_parse_credential(AA1, pacs);
  241. }
  242. if(err != ERR_NONE) {
  243. FURI_LOG_E(TAG, "picopass_device_parse_credential error %d", err);
  244. nextState = PicopassWorkerEventFail;
  245. }
  246. if(nextState == PicopassWorkerEventSuccess) {
  247. err = picopass_device_parse_wiegand(pacs->credential, &pacs->record);
  248. }
  249. if(err != ERR_NONE) {
  250. FURI_LOG_E(TAG, "picopass_device_parse_wiegand error %d", err);
  251. nextState = PicopassWorkerEventFail;
  252. }
  253. // Notify caller and exit
  254. if(picopass_worker->callback) {
  255. picopass_worker->callback(nextState, picopass_worker->context);
  256. }
  257. break;
  258. }
  259. furi_delay_ms(100);
  260. }
  261. }
  262. void picopass_worker_write(PicopassWorker* picopass_worker) {
  263. PicopassDeviceData* dev_data = picopass_worker->dev_data;
  264. PicopassBlock* AA1 = dev_data->AA1;
  265. ReturnCode err;
  266. PicopassWorkerEvent nextState = PicopassWorkerEventSuccess;
  267. while(picopass_worker->state == PicopassWorkerStateWrite) {
  268. if(picopass_detect_card(1000) == ERR_NONE) {
  269. err = picopass_write_card(AA1);
  270. if(err != ERR_NONE) {
  271. FURI_LOG_E(TAG, "picopass_write_card error %d", err);
  272. nextState = PicopassWorkerEventFail;
  273. }
  274. // Notify caller and exit
  275. if(picopass_worker->callback) {
  276. picopass_worker->callback(nextState, picopass_worker->context);
  277. }
  278. break;
  279. }
  280. furi_delay_ms(100);
  281. }
  282. }