seos_native_peripheral.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. #include "seos_native_peripheral_i.h"
  2. #define TAG "SeosNativePeripheral"
  3. #define MESSAGE_QUEUE_SIZE 10
  4. static uint8_t standard_seos_aid[] = {0xa0, 0x00, 0x00, 0x04, 0x40, 0x00, 0x01, 0x01, 0x00, 0x01};
  5. static uint8_t cd02[] = {0xcd, 0x02};
  6. static uint8_t general_authenticate_1[] =
  7. {0x00, 0x87, 0x00, 0x01, 0x04, 0x7c, 0x02, 0x81, 0x00, 0x00};
  8. static uint8_t ga1_response[] = {0x7c, 0x0a, 0x81, 0x08};
  9. // Emulation
  10. static uint8_t success[] = {0x90, 0x00};
  11. static uint8_t file_not_found[] = {0x6A, 0x82};
  12. static uint8_t select_header[] = {0x00, 0xa4, 0x04, 0x00};
  13. static uint8_t select_adf_header[] = {0x80, 0xa5, 0x04, 0x00};
  14. static uint8_t general_authenticate_2_header[] = {0x00, 0x87, 0x00, 0x01};
  15. static uint8_t secure_messaging_header[] = {0x0c, 0xcb, 0x3f, 0xff};
  16. int32_t seos_native_peripheral_task(void* context);
  17. typedef struct {
  18. size_t len;
  19. uint8_t buf[BLE_SVC_SEOS_CHAR_VALUE_LEN_MAX];
  20. } NativePeripheralMessage;
  21. static void seos_ble_connection_status_callback(BtStatus status, void* context) {
  22. furi_assert(context);
  23. SeosNativePeripheral* seos_native_peripheral = context;
  24. if(status == BtStatusConnected) {
  25. view_dispatcher_send_custom_event(
  26. seos_native_peripheral->seos->view_dispatcher, SeosCustomEventConnected);
  27. } else if(status == BtStatusAdvertising) {
  28. view_dispatcher_send_custom_event(
  29. seos_native_peripheral->seos->view_dispatcher, SeosCustomEventAdvertising);
  30. }
  31. }
  32. static uint16_t seos_svc_callback(SeosServiceEvent event, void* context) {
  33. SeosNativePeripheral* seos_native_peripheral = context;
  34. uint16_t bytes_available = 0;
  35. if(event.event == SeosServiceEventTypeDataReceived) {
  36. uint32_t space = furi_message_queue_get_space(seos_native_peripheral->messages);
  37. if(space > 0) {
  38. NativePeripheralMessage message = {.len = event.data.size};
  39. memcpy(message.buf, event.data.buffer, event.data.size);
  40. if(furi_mutex_acquire(seos_native_peripheral->mq_mutex, FuriWaitForever) ==
  41. FuriStatusOk) {
  42. furi_message_queue_put(
  43. seos_native_peripheral->messages, &message, FuriWaitForever);
  44. furi_mutex_release(seos_native_peripheral->mq_mutex);
  45. }
  46. if(space < MESSAGE_QUEUE_SIZE / 2) {
  47. FURI_LOG_D(TAG, "Queue message. %ld remaining", space);
  48. }
  49. bytes_available = (space - 1) * sizeof(NativePeripheralMessage);
  50. } else {
  51. FURI_LOG_E(TAG, "No space in message queue");
  52. }
  53. }
  54. return bytes_available;
  55. }
  56. SeosNativePeripheral* seos_native_peripheral_alloc(Seos* seos) {
  57. SeosNativePeripheral* seos_native_peripheral = malloc(sizeof(SeosNativePeripheral));
  58. memset(seos_native_peripheral, 0, sizeof(SeosNativePeripheral));
  59. seos_native_peripheral->seos = seos;
  60. seos_native_peripheral->credential = &seos->credential;
  61. seos_native_peripheral->bt = furi_record_open(RECORD_BT);
  62. seos_native_peripheral->phase = SELECT_AID;
  63. seos_native_peripheral->secure_messaging = NULL;
  64. seos_native_peripheral->params.key_no = 1;
  65. memset(
  66. seos_native_peripheral->params.cNonce,
  67. 0x0c,
  68. sizeof(seos_native_peripheral->params.cNonce));
  69. memset(seos_native_peripheral->params.UID, 0x0d, sizeof(seos_native_peripheral->params.UID));
  70. seos_native_peripheral->thread = furi_thread_alloc_ex(
  71. "SeosNativePeripheralWorker",
  72. 5 * 1024,
  73. seos_native_peripheral_task,
  74. seos_native_peripheral);
  75. seos_native_peripheral->messages =
  76. furi_message_queue_alloc(MESSAGE_QUEUE_SIZE, sizeof(NativePeripheralMessage));
  77. seos_native_peripheral->mq_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
  78. return seos_native_peripheral;
  79. }
  80. void seos_native_peripheral_free(SeosNativePeripheral* seos_native_peripheral) {
  81. furi_assert(seos_native_peripheral);
  82. furi_record_close(RECORD_BT);
  83. furi_message_queue_free(seos_native_peripheral->messages);
  84. furi_mutex_free(seos_native_peripheral->mq_mutex);
  85. furi_thread_free(seos_native_peripheral->thread);
  86. free(seos_native_peripheral);
  87. }
  88. void seos_native_peripheral_start(SeosNativePeripheral* seos_native_peripheral, FlowMode mode) {
  89. seos_native_peripheral->flow_mode = mode;
  90. if(seos_native_peripheral->flow_mode == FLOW_CRED) {
  91. seos_native_peripheral->params.key_no = 0;
  92. seos_native_peripheral->params.cipher = TWO_KEY_3DES_CBC_MODE;
  93. seos_native_peripheral->params.hash = SHA1;
  94. memset(
  95. seos_native_peripheral->params.rndICC,
  96. 0x0d,
  97. sizeof(seos_native_peripheral->params.rndICC));
  98. memset(
  99. seos_native_peripheral->params.rNonce,
  100. 0x0c,
  101. sizeof(seos_native_peripheral->params.rNonce));
  102. memset(
  103. seos_native_peripheral->params.UID, 0x00, sizeof(seos_native_peripheral->params.UID));
  104. memset(
  105. seos_native_peripheral->params.cNonce,
  106. 0x00,
  107. sizeof(seos_native_peripheral->params.cNonce));
  108. }
  109. bt_disconnect(seos_native_peripheral->bt);
  110. BleProfileParams params = {
  111. .mode = mode,
  112. };
  113. // Wait 2nd core to update nvm storage
  114. furi_delay_ms(200);
  115. seos_native_peripheral->ble_profile =
  116. bt_profile_start(seos_native_peripheral->bt, ble_profile_seos, &params);
  117. furi_check(seos_native_peripheral->ble_profile);
  118. bt_set_status_changed_callback(
  119. seos_native_peripheral->bt, seos_ble_connection_status_callback, seos_native_peripheral);
  120. ble_profile_seos_set_event_callback(
  121. seos_native_peripheral->ble_profile,
  122. sizeof(seos_native_peripheral->event_buffer),
  123. seos_svc_callback,
  124. seos_native_peripheral);
  125. furi_hal_bt_start_advertising();
  126. view_dispatcher_send_custom_event(
  127. seos_native_peripheral->seos->view_dispatcher, SeosCustomEventAdvertising);
  128. furi_thread_start(seos_native_peripheral->thread);
  129. }
  130. void seos_native_peripheral_stop(SeosNativePeripheral* seos_native_peripheral) {
  131. furi_hal_bt_stop_advertising();
  132. bt_set_status_changed_callback(seos_native_peripheral->bt, NULL, NULL);
  133. bt_disconnect(seos_native_peripheral->bt);
  134. // Wait 2nd core to update nvm storage
  135. furi_delay_ms(200);
  136. bt_keys_storage_set_default_path(seos_native_peripheral->bt);
  137. furi_check(bt_profile_restore_default(seos_native_peripheral->bt));
  138. furi_thread_flags_set(furi_thread_get_id(seos_native_peripheral->thread), WorkerEvtStop);
  139. furi_thread_join(seos_native_peripheral->thread);
  140. }
  141. void seos_native_peripheral_process_message_cred(
  142. SeosNativePeripheral* seos_native_peripheral,
  143. NativePeripheralMessage message) {
  144. BitBuffer* response = bit_buffer_alloc(128); // TODO: MTU
  145. uint8_t* data = message.buf;
  146. if(data[0] != BLE_START && data[0] != 0xe1) {
  147. FURI_LOG_W(TAG, "Unexpected start of BLE packet");
  148. }
  149. const uint8_t* apdu = data + 1; // Match name to nfc version for easier copying
  150. if(memcmp(apdu, select_header, sizeof(select_header)) == 0) {
  151. if(memcmp(apdu + sizeof(select_header) + 1, standard_seos_aid, sizeof(standard_seos_aid)) ==
  152. 0) {
  153. seos_emulator_select_aid(response);
  154. bit_buffer_append_bytes(response, (uint8_t*)success, sizeof(success));
  155. } else {
  156. bit_buffer_append_bytes(response, (uint8_t*)file_not_found, sizeof(file_not_found));
  157. }
  158. } else if(memcmp(apdu, select_adf_header, sizeof(select_adf_header)) == 0) {
  159. // is our adf in the list?
  160. // +1 to skip APDU length byte
  161. void* p = memmem(
  162. apdu + sizeof(select_adf_header) + 1,
  163. apdu[sizeof(select_adf_header)],
  164. SEOS_ADF_OID,
  165. SEOS_ADF_OID_LEN);
  166. if(p) {
  167. seos_log_buffer(TAG, "Matched ADF", p, SEOS_ADF_OID_LEN);
  168. seos_emulator_select_adf(
  169. &seos_native_peripheral->params, seos_native_peripheral->credential, response);
  170. bit_buffer_append_bytes(response, (uint8_t*)success, sizeof(success));
  171. } else {
  172. FURI_LOG_W(TAG, "Failed to match any ADF OID");
  173. }
  174. } else if(memcmp(apdu, general_authenticate_1, sizeof(general_authenticate_1)) == 0) {
  175. seos_emulator_general_authenticate_1(response, seos_native_peripheral->params);
  176. bit_buffer_append_bytes(response, (uint8_t*)success, sizeof(success));
  177. } else if(memcmp(apdu, general_authenticate_2_header, sizeof(general_authenticate_2_header)) == 0) {
  178. if(!seos_emulator_general_authenticate_2(
  179. apdu,
  180. message.len - 1,
  181. seos_native_peripheral->credential,
  182. &seos_native_peripheral->params,
  183. response)) {
  184. FURI_LOG_W(TAG, "Failure in General Authenticate 2");
  185. } else {
  186. bit_buffer_append_bytes(response, (uint8_t*)success, sizeof(success));
  187. }
  188. view_dispatcher_send_custom_event(
  189. seos_native_peripheral->seos->view_dispatcher, SeosCustomEventAuthenticated);
  190. // Prepare for future communication
  191. seos_native_peripheral->secure_messaging =
  192. secure_messaging_alloc(&seos_native_peripheral->params);
  193. } else if(memcmp(apdu, secure_messaging_header, sizeof(secure_messaging_header)) == 0) {
  194. uint8_t request_sio[] = {0x5c, 0x02, 0xff, 0x00};
  195. if(seos_native_peripheral->secure_messaging) {
  196. FURI_LOG_D(TAG, "Unwrap secure message");
  197. // c0 0ccb3fff 16 8508fa8395d30de4e8e097008e085da7edbd833b002d00
  198. // Ignore 1 BLE_START byte
  199. size_t bytes_to_ignore = 1;
  200. BitBuffer* tmp = bit_buffer_alloc(message.len);
  201. bit_buffer_append_bytes(
  202. tmp, message.buf + bytes_to_ignore, message.len - bytes_to_ignore);
  203. seos_log_bitbuffer(TAG, "received(wrapped)", tmp);
  204. secure_messaging_unwrap_apdu(seos_native_peripheral->secure_messaging, tmp);
  205. seos_log_bitbuffer(TAG, "received(clear)", tmp);
  206. const uint8_t* message = bit_buffer_get_data(tmp);
  207. if(memcmp(message, request_sio, sizeof(request_sio)) == 0) {
  208. view_dispatcher_send_custom_event(
  209. seos_native_peripheral->seos->view_dispatcher, SeosCustomEventSIORequested);
  210. BitBuffer* sio_file = bit_buffer_alloc(128);
  211. bit_buffer_append_bytes(sio_file, message + 2, 2); // fileId
  212. bit_buffer_append_byte(sio_file, seos_native_peripheral->credential->sio_len);
  213. bit_buffer_append_bytes(
  214. sio_file,
  215. seos_native_peripheral->credential->sio,
  216. seos_native_peripheral->credential->sio_len);
  217. secure_messaging_wrap_rapdu(
  218. seos_native_peripheral->secure_messaging,
  219. (uint8_t*)bit_buffer_get_data(sio_file),
  220. bit_buffer_get_size_bytes(sio_file),
  221. response);
  222. bit_buffer_append_bytes(response, (uint8_t*)success, sizeof(success));
  223. bit_buffer_free(sio_file);
  224. }
  225. bit_buffer_free(tmp);
  226. } else {
  227. uint8_t no_sm[] = {0x69, 0x88};
  228. bit_buffer_append_bytes(response, no_sm, sizeof(no_sm));
  229. }
  230. } else if(data[0] == 0xe1) {
  231. // ignore
  232. } else {
  233. FURI_LOG_W(TAG, "no match for message");
  234. }
  235. if(bit_buffer_get_size_bytes(response) > 0) {
  236. BitBuffer* tx = bit_buffer_alloc(1 + 2 + 1 + bit_buffer_get_size_bytes(response));
  237. bit_buffer_append_byte(tx, BLE_START);
  238. bit_buffer_append_bytes(
  239. tx, bit_buffer_get_data(response), bit_buffer_get_size_bytes(response));
  240. ble_profile_seos_tx(
  241. seos_native_peripheral->ble_profile,
  242. (uint8_t*)bit_buffer_get_data(tx),
  243. bit_buffer_get_size_bytes(tx));
  244. bit_buffer_free(tx);
  245. }
  246. bit_buffer_free(response);
  247. }
  248. void seos_native_peripheral_process_message_reader(
  249. SeosNativePeripheral* seos_native_peripheral,
  250. NativePeripheralMessage message) {
  251. BitBuffer* response = bit_buffer_alloc(128); // TODO: MTU
  252. uint8_t* data = message.buf;
  253. uint8_t* rx_data = data + 1; // Match name to nfc version for easier copying
  254. if(data[0] != BLE_START && data[0] != 0xe1) {
  255. FURI_LOG_W(TAG, "Unexpected start of BLE packet");
  256. }
  257. if(memcmp(data + 5, standard_seos_aid, sizeof(standard_seos_aid)) == 0) { // response to select
  258. FURI_LOG_I(TAG, "Select ADF");
  259. uint8_t select_adf_header[] = {
  260. 0x80, 0xa5, 0x04, 0x00, (uint8_t)SEOS_ADF_OID_LEN + 2, 0x06, (uint8_t)SEOS_ADF_OID_LEN};
  261. bit_buffer_append_bytes(response, select_adf_header, sizeof(select_adf_header));
  262. bit_buffer_append_bytes(response, SEOS_ADF_OID, SEOS_ADF_OID_LEN);
  263. seos_native_peripheral->phase = SELECT_ADF;
  264. } else if(memcmp(data + 1, cd02, sizeof(cd02)) == 0) {
  265. BitBuffer* attribute_value = bit_buffer_alloc(message.len);
  266. bit_buffer_append_bytes(attribute_value, message.buf, message.len);
  267. if(seos_reader_select_adf_response(
  268. attribute_value,
  269. 1,
  270. seos_native_peripheral->credential,
  271. &seos_native_peripheral->params)) {
  272. // Craft response
  273. general_authenticate_1[3] = seos_native_peripheral->params.key_no;
  274. bit_buffer_append_bytes(
  275. response, general_authenticate_1, sizeof(general_authenticate_1));
  276. seos_native_peripheral->phase = GENERAL_AUTHENTICATION_1;
  277. }
  278. bit_buffer_free(attribute_value);
  279. } else if(memcmp(data + 1, ga1_response, sizeof(ga1_response)) == 0) {
  280. memcpy(seos_native_peripheral->params.rndICC, data + 5, 8);
  281. // Craft response
  282. uint8_t cryptogram[32 + 8];
  283. memset(cryptogram, 0, sizeof(cryptogram));
  284. seos_reader_generate_cryptogram(
  285. seos_native_peripheral->credential, &seos_native_peripheral->params, cryptogram);
  286. uint8_t ga_header[] = {
  287. 0x00,
  288. 0x87,
  289. 0x00,
  290. seos_native_peripheral->params.key_no,
  291. sizeof(cryptogram) + 4,
  292. 0x7c,
  293. sizeof(cryptogram) + 2,
  294. 0x82,
  295. sizeof(cryptogram)};
  296. bit_buffer_append_bytes(response, ga_header, sizeof(ga_header));
  297. bit_buffer_append_bytes(response, cryptogram, sizeof(cryptogram));
  298. seos_native_peripheral->phase = GENERAL_AUTHENTICATION_2;
  299. } else if(rx_data[0] == 0x7C && rx_data[2] == 0x82) { // ga2 response
  300. if(rx_data[3] == 40) {
  301. if(!seos_reader_verify_cryptogram(&seos_native_peripheral->params, rx_data + 4)) {
  302. FURI_LOG_W(TAG, "Card cryptogram failed verification");
  303. bit_buffer_free(response);
  304. return;
  305. }
  306. FURI_LOG_I(TAG, "Authenticated");
  307. view_dispatcher_send_custom_event(
  308. seos_native_peripheral->seos->view_dispatcher, SeosCustomEventAuthenticated);
  309. } else {
  310. FURI_LOG_W(TAG, "Unhandled card cryptogram size %d", rx_data[3]);
  311. }
  312. seos_native_peripheral->secure_messaging =
  313. secure_messaging_alloc(&seos_native_peripheral->params);
  314. SecureMessaging* secure_messaging = seos_native_peripheral->secure_messaging;
  315. uint8_t message[] = {0x5c, 0x02, 0xff, 0x00};
  316. secure_messaging_wrap_apdu(secure_messaging, message, sizeof(message), response);
  317. seos_native_peripheral->phase = REQUEST_SIO;
  318. view_dispatcher_send_custom_event(
  319. seos_native_peripheral->seos->view_dispatcher, SeosCustomEventSIORequested);
  320. } else if(seos_native_peripheral->phase == REQUEST_SIO) {
  321. SecureMessaging* secure_messaging = seos_native_peripheral->secure_messaging;
  322. BitBuffer* rx_buffer = bit_buffer_alloc(message.len - 1);
  323. bit_buffer_append_bytes(rx_buffer, rx_data, message.len - 1);
  324. seos_log_bitbuffer(TAG, "BLE response(wrapped)", rx_buffer);
  325. secure_messaging_unwrap_rapdu(secure_messaging, rx_buffer);
  326. seos_log_bitbuffer(TAG, "BLE response(clear)", rx_buffer);
  327. // Skip fileId
  328. seos_native_peripheral->credential->sio_len = bit_buffer_get_byte(rx_buffer, 2);
  329. if(seos_native_peripheral->credential->sio_len >
  330. sizeof(seos_native_peripheral->credential->sio)) {
  331. FURI_LOG_W(TAG, "SIO too long to save");
  332. bit_buffer_free(response);
  333. return;
  334. }
  335. memcpy(
  336. seos_native_peripheral->credential->sio,
  337. bit_buffer_get_data(rx_buffer) + 3,
  338. seos_native_peripheral->credential->sio_len);
  339. FURI_LOG_I(TAG, "SIO Captured, %d bytes", seos_native_peripheral->credential->sio_len);
  340. Seos* seos = seos_native_peripheral->seos;
  341. view_dispatcher_send_custom_event(seos->view_dispatcher, SeosCustomEventReaderSuccess);
  342. bit_buffer_free(rx_buffer);
  343. seos_native_peripheral->phase = SELECT_AID;
  344. } else if(data[0] == 0xe1) {
  345. //ignore
  346. } else {
  347. FURI_LOG_W(TAG, "No match for write request");
  348. seos_log_buffer(TAG, "No match for reader incoming", message.buf, message.len);
  349. }
  350. if(bit_buffer_get_size_bytes(response) > 0) {
  351. BitBuffer* tx = bit_buffer_alloc(1 + 2 + 1 + bit_buffer_get_size_bytes(response));
  352. bit_buffer_append_byte(tx, BLE_START);
  353. bit_buffer_append_bytes(
  354. tx, bit_buffer_get_data(response), bit_buffer_get_size_bytes(response));
  355. ble_profile_seos_tx(
  356. seos_native_peripheral->ble_profile,
  357. (uint8_t*)bit_buffer_get_data(tx),
  358. bit_buffer_get_size_bytes(tx));
  359. bit_buffer_free(tx);
  360. }
  361. bit_buffer_free(response);
  362. }
  363. int32_t seos_native_peripheral_task(void* context) {
  364. SeosNativePeripheral* seos_native_peripheral = (SeosNativePeripheral*)context;
  365. bool running = true;
  366. while(running) {
  367. uint32_t events = furi_thread_flags_get();
  368. if(events & WorkerEvtStop) {
  369. running = false;
  370. break;
  371. }
  372. if(furi_mutex_acquire(seos_native_peripheral->mq_mutex, 1) == FuriStatusOk) {
  373. uint32_t count = furi_message_queue_get_count(seos_native_peripheral->messages);
  374. if(count > 0) {
  375. if(count > MESSAGE_QUEUE_SIZE / 2) {
  376. FURI_LOG_I(TAG, "Dequeue message [%ld messages]", count);
  377. }
  378. NativePeripheralMessage message = {};
  379. FuriStatus status = furi_message_queue_get(
  380. seos_native_peripheral->messages, &message, FuriWaitForever);
  381. if(status != FuriStatusOk) {
  382. FURI_LOG_W(TAG, "furi_message_queue_get fail %d", status);
  383. }
  384. if(seos_native_peripheral->flow_mode == FLOW_READER) {
  385. seos_native_peripheral_process_message_reader(seos_native_peripheral, message);
  386. } else if(seos_native_peripheral->flow_mode == FLOW_CRED) {
  387. seos_native_peripheral_process_message_cred(seos_native_peripheral, message);
  388. }
  389. }
  390. furi_mutex_release(seos_native_peripheral->mq_mutex);
  391. } else {
  392. FURI_LOG_W(TAG, "Failed to acquire mutex");
  393. }
  394. // A beat for event flags
  395. furi_delay_ms(1);
  396. }
  397. return 0;
  398. }