seos_native_peripheral.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  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. int32_t seos_native_peripheral_task(void* context);
  10. typedef struct {
  11. size_t len;
  12. uint8_t buf[BLE_SVC_SEOS_CHAR_VALUE_LEN_MAX];
  13. } NativePeripheralMessage;
  14. static void seos_ble_connection_status_callback(BtStatus status, void* context) {
  15. furi_assert(context);
  16. SeosNativePeripheral* seos_native_peripheral = context;
  17. if(status == BtStatusConnected) {
  18. view_dispatcher_send_custom_event(
  19. seos_native_peripheral->seos->view_dispatcher, SeosCustomEventConnected);
  20. } else if(status == BtStatusAdvertising) {
  21. view_dispatcher_send_custom_event(
  22. seos_native_peripheral->seos->view_dispatcher, SeosCustomEventAdvertising);
  23. }
  24. }
  25. static uint16_t seos_svc_callback(SeosServiceEvent event, void* context) {
  26. SeosNativePeripheral* seos_native_peripheral = context;
  27. uint16_t bytes_available = 0;
  28. if(event.event == SeosServiceEventTypeDataReceived) {
  29. uint32_t space = furi_message_queue_get_space(seos_native_peripheral->messages);
  30. if(space > 0) {
  31. NativePeripheralMessage message = {.len = event.data.size};
  32. memcpy(message.buf, event.data.buffer, event.data.size);
  33. if(furi_mutex_acquire(seos_native_peripheral->mq_mutex, FuriWaitForever) ==
  34. FuriStatusOk) {
  35. furi_message_queue_put(
  36. seos_native_peripheral->messages, &message, FuriWaitForever);
  37. furi_mutex_release(seos_native_peripheral->mq_mutex);
  38. }
  39. if(space < MESSAGE_QUEUE_SIZE / 2) {
  40. FURI_LOG_D(TAG, "Queue message. %ld remaining", space);
  41. }
  42. bytes_available = (space - 1) * sizeof(NativePeripheralMessage);
  43. } else {
  44. FURI_LOG_E(TAG, "No space in message queue");
  45. }
  46. }
  47. return bytes_available;
  48. }
  49. SeosNativePeripheral* seos_native_peripheral_alloc(Seos* seos) {
  50. SeosNativePeripheral* seos_native_peripheral = malloc(sizeof(SeosNativePeripheral));
  51. memset(seos_native_peripheral, 0, sizeof(SeosNativePeripheral));
  52. seos_native_peripheral->seos = seos;
  53. seos_native_peripheral->credential = &seos->credential;
  54. seos_native_peripheral->bt = furi_record_open(RECORD_BT);
  55. seos_native_peripheral->phase = SELECT_AID;
  56. seos_native_peripheral->secure_messaging = NULL;
  57. seos_native_peripheral->params.key_no = 1;
  58. memset(
  59. seos_native_peripheral->params.cNonce,
  60. 0x0c,
  61. sizeof(seos_native_peripheral->params.cNonce));
  62. memset(seos_native_peripheral->params.UID, 0x0d, sizeof(seos_native_peripheral->params.UID));
  63. seos_native_peripheral->thread = furi_thread_alloc_ex(
  64. "SeosNativePeripheralWorker",
  65. 5 * 1024,
  66. seos_native_peripheral_task,
  67. seos_native_peripheral);
  68. seos_native_peripheral->messages =
  69. furi_message_queue_alloc(MESSAGE_QUEUE_SIZE, sizeof(NativePeripheralMessage));
  70. seos_native_peripheral->mq_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
  71. return seos_native_peripheral;
  72. }
  73. void seos_native_peripheral_free(SeosNativePeripheral* seos_native_peripheral) {
  74. furi_assert(seos_native_peripheral);
  75. furi_record_close(RECORD_BT);
  76. furi_message_queue_free(seos_native_peripheral->messages);
  77. furi_mutex_free(seos_native_peripheral->mq_mutex);
  78. furi_thread_free(seos_native_peripheral->thread);
  79. free(seos_native_peripheral);
  80. }
  81. void seos_native_peripheral_start(SeosNativePeripheral* seos_native_peripheral, FlowMode mode) {
  82. UNUSED(mode);
  83. bt_disconnect(seos_native_peripheral->bt);
  84. // Wait 2nd core to update nvm storage
  85. furi_delay_ms(200);
  86. seos_native_peripheral->ble_profile =
  87. bt_profile_start(seos_native_peripheral->bt, ble_profile_seos, NULL);
  88. furi_check(seos_native_peripheral->ble_profile);
  89. bt_set_status_changed_callback(
  90. seos_native_peripheral->bt, seos_ble_connection_status_callback, seos_native_peripheral);
  91. ble_profile_seos_set_event_callback(
  92. seos_native_peripheral->ble_profile,
  93. sizeof(seos_native_peripheral->event_buffer),
  94. seos_svc_callback,
  95. seos_native_peripheral);
  96. furi_hal_bt_start_advertising();
  97. view_dispatcher_send_custom_event(
  98. seos_native_peripheral->seos->view_dispatcher, SeosCustomEventAdvertising);
  99. furi_thread_start(seos_native_peripheral->thread);
  100. }
  101. void seos_native_peripheral_stop(SeosNativePeripheral* seos_native_peripheral) {
  102. furi_hal_bt_stop_advertising();
  103. bt_set_status_changed_callback(seos_native_peripheral->bt, NULL, NULL);
  104. bt_disconnect(seos_native_peripheral->bt);
  105. // Wait 2nd core to update nvm storage
  106. furi_delay_ms(200);
  107. bt_keys_storage_set_default_path(seos_native_peripheral->bt);
  108. furi_check(bt_profile_restore_default(seos_native_peripheral->bt));
  109. furi_thread_flags_set(furi_thread_get_id(seos_native_peripheral->thread), WorkerEvtStop);
  110. furi_thread_join(seos_native_peripheral->thread);
  111. }
  112. void seos_native_peripheral_process_message(
  113. SeosNativePeripheral* seos_native_peripheral,
  114. NativePeripheralMessage message) {
  115. BitBuffer* response = bit_buffer_alloc(128); // TODO: MTU
  116. uint8_t* data = message.buf;
  117. uint8_t* rx_data = data + 1; // Match name to nfc version for easier copying
  118. if(data[0] != BLE_START && data[0] != 0xe1) {
  119. FURI_LOG_W(TAG, "Unexpected start of BLE packet");
  120. }
  121. if(memcmp(data + 5, standard_seos_aid, sizeof(standard_seos_aid)) == 0) { // response to select
  122. FURI_LOG_I(TAG, "Select ADF");
  123. uint8_t select_adf_header[] = {
  124. 0x80, 0xa5, 0x04, 0x00, (uint8_t)SEOS_ADF_OID_LEN + 2, 0x06, (uint8_t)SEOS_ADF_OID_LEN};
  125. bit_buffer_append_bytes(response, select_adf_header, sizeof(select_adf_header));
  126. bit_buffer_append_bytes(response, SEOS_ADF_OID, SEOS_ADF_OID_LEN);
  127. seos_native_peripheral->phase = SELECT_ADF;
  128. } else if(memcmp(data + 1, cd02, sizeof(cd02)) == 0) {
  129. BitBuffer* attribute_value = bit_buffer_alloc(message.len);
  130. bit_buffer_append_bytes(attribute_value, message.buf, message.len);
  131. if(seos_reader_select_adf_response(
  132. attribute_value,
  133. 1,
  134. seos_native_peripheral->credential,
  135. &seos_native_peripheral->params)) {
  136. // Craft response
  137. general_authenticate_1[3] = seos_native_peripheral->params.key_no;
  138. bit_buffer_append_bytes(
  139. response, general_authenticate_1, sizeof(general_authenticate_1));
  140. seos_native_peripheral->phase = GENERAL_AUTHENTICATION_1;
  141. }
  142. bit_buffer_free(attribute_value);
  143. } else if(memcmp(data + 1, ga1_response, sizeof(ga1_response)) == 0) {
  144. memcpy(seos_native_peripheral->params.rndICC, data + 5, 8);
  145. // Craft response
  146. uint8_t cryptogram[32 + 8];
  147. memset(cryptogram, 0, sizeof(cryptogram));
  148. seos_reader_generate_cryptogram(
  149. seos_native_peripheral->credential, &seos_native_peripheral->params, cryptogram);
  150. uint8_t ga_header[] = {
  151. 0x00,
  152. 0x87,
  153. 0x00,
  154. seos_native_peripheral->params.key_no,
  155. sizeof(cryptogram) + 4,
  156. 0x7c,
  157. sizeof(cryptogram) + 2,
  158. 0x82,
  159. sizeof(cryptogram)};
  160. bit_buffer_append_bytes(response, ga_header, sizeof(ga_header));
  161. bit_buffer_append_bytes(response, cryptogram, sizeof(cryptogram));
  162. seos_native_peripheral->phase = GENERAL_AUTHENTICATION_2;
  163. } else if(rx_data[0] == 0x7C && rx_data[2] == 0x82) { // ga2 response
  164. if(rx_data[3] == 40) {
  165. if(!seos_reader_verify_cryptogram(&seos_native_peripheral->params, rx_data + 4)) {
  166. FURI_LOG_W(TAG, "Card cryptogram failed verification");
  167. bit_buffer_free(response);
  168. return;
  169. }
  170. FURI_LOG_I(TAG, "Authenticated");
  171. view_dispatcher_send_custom_event(
  172. seos_native_peripheral->seos->view_dispatcher, SeosCustomEventAuthenticated);
  173. } else {
  174. FURI_LOG_W(TAG, "Unhandled card cryptogram size %d", rx_data[3]);
  175. }
  176. seos_native_peripheral->secure_messaging =
  177. secure_messaging_alloc(&seos_native_peripheral->params);
  178. SecureMessaging* secure_messaging = seos_native_peripheral->secure_messaging;
  179. uint8_t message[] = {0x5c, 0x02, 0xff, 0x00};
  180. secure_messaging_wrap_apdu(secure_messaging, message, sizeof(message), response);
  181. seos_native_peripheral->phase = REQUEST_SIO;
  182. view_dispatcher_send_custom_event(
  183. seos_native_peripheral->seos->view_dispatcher, SeosCustomEventSIORequested);
  184. } else if(seos_native_peripheral->phase == REQUEST_SIO) {
  185. SecureMessaging* secure_messaging = seos_native_peripheral->secure_messaging;
  186. BitBuffer* rx_buffer = bit_buffer_alloc(message.len - 1);
  187. bit_buffer_append_bytes(rx_buffer, rx_data, message.len - 1);
  188. seos_log_bitbuffer(TAG, "BLE response(wrapped)", rx_buffer);
  189. secure_messaging_unwrap_rapdu(secure_messaging, rx_buffer);
  190. seos_log_bitbuffer(TAG, "BLE response(clear)", rx_buffer);
  191. // Skip fileId
  192. seos_native_peripheral->credential->sio_len = bit_buffer_get_byte(rx_buffer, 2);
  193. if(seos_native_peripheral->credential->sio_len >
  194. sizeof(seos_native_peripheral->credential->sio)) {
  195. FURI_LOG_W(TAG, "SIO too long to save");
  196. bit_buffer_free(response);
  197. return;
  198. }
  199. memcpy(
  200. seos_native_peripheral->credential->sio,
  201. bit_buffer_get_data(rx_buffer) + 3,
  202. seos_native_peripheral->credential->sio_len);
  203. FURI_LOG_I(TAG, "SIO Captured, %d bytes", seos_native_peripheral->credential->sio_len);
  204. Seos* seos = seos_native_peripheral->seos;
  205. view_dispatcher_send_custom_event(seos->view_dispatcher, SeosCustomEventReaderSuccess);
  206. bit_buffer_free(rx_buffer);
  207. seos_native_peripheral->phase = SELECT_AID;
  208. } else if(data[0] == 0xe1) {
  209. //ignore
  210. } else {
  211. FURI_LOG_W(TAG, "No match for write request");
  212. }
  213. if(bit_buffer_get_size_bytes(response) > 0) {
  214. BitBuffer* tx = bit_buffer_alloc(1 + 2 + 1 + bit_buffer_get_size_bytes(response));
  215. bit_buffer_append_byte(tx, BLE_START);
  216. bit_buffer_append_bytes(
  217. tx, bit_buffer_get_data(response), bit_buffer_get_size_bytes(response));
  218. ble_profile_seos_tx(
  219. seos_native_peripheral->ble_profile,
  220. (uint8_t*)bit_buffer_get_data(tx),
  221. bit_buffer_get_size_bytes(tx));
  222. bit_buffer_free(tx);
  223. }
  224. bit_buffer_free(response);
  225. }
  226. int32_t seos_native_peripheral_task(void* context) {
  227. SeosNativePeripheral* seos_native_peripheral = (SeosNativePeripheral*)context;
  228. bool running = true;
  229. while(running) {
  230. uint32_t events = furi_thread_flags_get();
  231. if(events & WorkerEvtStop) {
  232. running = false;
  233. break;
  234. }
  235. if(furi_mutex_acquire(seos_native_peripheral->mq_mutex, 1) == FuriStatusOk) {
  236. uint32_t count = furi_message_queue_get_count(seos_native_peripheral->messages);
  237. if(count > 0) {
  238. if(count > MESSAGE_QUEUE_SIZE / 2) {
  239. FURI_LOG_I(TAG, "Dequeue message [%ld messages]", count);
  240. }
  241. NativePeripheralMessage message = {};
  242. FuriStatus status = furi_message_queue_get(
  243. seos_native_peripheral->messages, &message, FuriWaitForever);
  244. if(status != FuriStatusOk) {
  245. FURI_LOG_W(TAG, "furi_message_queue_get fail %d", status);
  246. }
  247. seos_native_peripheral_process_message(seos_native_peripheral, message);
  248. }
  249. furi_mutex_release(seos_native_peripheral->mq_mutex);
  250. } else {
  251. FURI_LOG_W(TAG, "Failed to acquire mutex");
  252. }
  253. // A beat for event flags
  254. furi_delay_ms(1);
  255. }
  256. return 0;
  257. }