seos_characteristic.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. #include "seos_characteristic_i.h"
  2. #define TAG "SeosCharacteristic"
  3. static uint8_t standard_seos_aid[] = {0xa0, 0x00, 0x00, 0x04, 0x40, 0x00, 0x01, 0x01, 0x00, 0x01};
  4. static uint8_t general_authenticate_1[] =
  5. {0x00, 0x87, 0x00, 0x01, 0x04, 0x7c, 0x02, 0x81, 0x00, 0x00};
  6. static uint8_t cd02[] = {0xcd, 0x02};
  7. static uint8_t ga1_response[] = {0x7c, 0x0a, 0x81, 0x08};
  8. // Emulation
  9. static uint8_t success[] = {0x90, 0x00};
  10. static uint8_t file_not_found[] = {0x6A, 0x82};
  11. static uint8_t select_header[] = {0x00, 0xa4, 0x04, 0x00};
  12. static uint8_t select_adf_header[] = {0x80, 0xa5, 0x04, 0x00};
  13. static uint8_t general_authenticate_2_header[] = {0x00, 0x87, 0x00, 0x01};
  14. static uint8_t secure_messaging_header[] = {0x0c, 0xcb, 0x3f, 0xff};
  15. SeosCharacteristic* seos_characteristic_alloc(Seos* seos) {
  16. SeosCharacteristic* seos_characteristic = malloc(sizeof(SeosCharacteristic));
  17. memset(seos_characteristic, 0, sizeof(SeosCharacteristic));
  18. seos_characteristic->seos = seos;
  19. seos_characteristic->credential = seos->credential;
  20. seos_characteristic->phase = SELECT_AID;
  21. seos_characteristic->secure_messaging = NULL;
  22. seos_characteristic->params.key_no = 1;
  23. memset(seos_characteristic->params.cNonce, 0x0c, sizeof(seos_characteristic->params.cNonce));
  24. memset(seos_characteristic->params.UID, 0x0d, sizeof(seos_characteristic->params.UID));
  25. seos_characteristic->seos_att = seos_att_alloc(seos);
  26. seos_att_set_on_subscribe_callback(
  27. seos_characteristic->seos_att, seos_characteristic_on_subscribe, seos_characteristic);
  28. seos_att_set_write_request_callback(
  29. seos_characteristic->seos_att, seos_characteristic_write_request, seos_characteristic);
  30. return seos_characteristic;
  31. }
  32. void seos_characteristic_free(SeosCharacteristic* seos_characteristic) {
  33. furi_assert(seos_characteristic);
  34. seos_att_free(seos_characteristic->seos_att);
  35. free(seos_characteristic);
  36. }
  37. void seos_characteristic_start(SeosCharacteristic* seos_characteristic, FlowMode mode) {
  38. seos_characteristic->flow_mode = mode;
  39. if(seos_characteristic->flow_mode == FLOW_CRED) {
  40. seos_characteristic->params.key_no = 0;
  41. seos_characteristic->params.cipher = TWO_KEY_3DES_CBC_MODE;
  42. seos_characteristic->params.hash = SHA1;
  43. memset(
  44. seos_characteristic->params.rndICC, 0x0d, sizeof(seos_characteristic->params.rndICC));
  45. memset(
  46. seos_characteristic->params.rNonce, 0x0c, sizeof(seos_characteristic->params.rNonce));
  47. memset(seos_characteristic->params.UID, 0x00, sizeof(seos_characteristic->params.UID));
  48. memset(
  49. seos_characteristic->params.cNonce, 0x00, sizeof(seos_characteristic->params.cNonce));
  50. }
  51. seos_att_start(seos_characteristic->seos_att, BLE_PERIPHERAL, mode);
  52. }
  53. void seos_characteristic_stop(SeosCharacteristic* seos_characteristic) {
  54. seos_att_stop(seos_characteristic->seos_att);
  55. }
  56. void seos_characteristic_reader_flow(
  57. SeosCharacteristic* seos_characteristic,
  58. BitBuffer* attribute_value,
  59. BitBuffer* payload) {
  60. const uint8_t* data = bit_buffer_get_data(attribute_value);
  61. const uint8_t* rx_data = data + 1; // Match name to nfc version for easier copying
  62. // 022f20180014000400
  63. // 520c00
  64. // c0 6f0c840a a0000004400001010001
  65. // 9000
  66. if(memcmp(data + 5, standard_seos_aid, sizeof(standard_seos_aid)) == 0) { // response to select
  67. FURI_LOG_I(TAG, "Select ADF");
  68. uint8_t select_adf_header[] = {
  69. 0x80, 0xa5, 0x04, 0x00, (uint8_t)SEOS_ADF_OID_LEN + 2, 0x06, (uint8_t)SEOS_ADF_OID_LEN};
  70. bit_buffer_append_bytes(payload, select_adf_header, sizeof(select_adf_header));
  71. bit_buffer_append_bytes(payload, SEOS_ADF_OID, SEOS_ADF_OID_LEN);
  72. seos_characteristic->phase = SELECT_ADF;
  73. } else if(memcmp(data + 1, cd02, sizeof(cd02)) == 0) {
  74. if(seos_reader_select_adf_response(
  75. attribute_value, 1, seos_characteristic->credential, &seos_characteristic->params)) {
  76. // Craft response
  77. general_authenticate_1[3] = seos_characteristic->params.key_no;
  78. bit_buffer_append_bytes(
  79. payload, general_authenticate_1, sizeof(general_authenticate_1));
  80. seos_characteristic->phase = GENERAL_AUTHENTICATION_1;
  81. }
  82. } else if(memcmp(data + 1, ga1_response, sizeof(ga1_response)) == 0) {
  83. memcpy(seos_characteristic->params.rndICC, data + 5, 8);
  84. // Craft response
  85. uint8_t cryptogram[32 + 8];
  86. memset(cryptogram, 0, sizeof(cryptogram));
  87. seos_reader_generate_cryptogram(
  88. seos_characteristic->credential, &seos_characteristic->params, cryptogram);
  89. uint8_t ga_header[] = {
  90. 0x00,
  91. 0x87,
  92. 0x00,
  93. seos_characteristic->params.key_no,
  94. sizeof(cryptogram) + 4,
  95. 0x7c,
  96. sizeof(cryptogram) + 2,
  97. 0x82,
  98. sizeof(cryptogram)};
  99. bit_buffer_append_bytes(payload, ga_header, sizeof(ga_header));
  100. bit_buffer_append_bytes(payload, cryptogram, sizeof(cryptogram));
  101. seos_characteristic->phase = GENERAL_AUTHENTICATION_2;
  102. } else if(rx_data[0] == 0x7C && rx_data[2] == 0x82) { // ga2 response
  103. if(rx_data[3] == 40) {
  104. if(!seos_reader_verify_cryptogram(&seos_characteristic->params, rx_data + 4)) {
  105. FURI_LOG_W(TAG, "Card cryptogram failed verification");
  106. return;
  107. }
  108. FURI_LOG_I(TAG, "Authenticated");
  109. view_dispatcher_send_custom_event(
  110. seos_characteristic->seos->view_dispatcher, SeosCustomEventAuthenticated);
  111. } else {
  112. FURI_LOG_W(TAG, "Unhandled card cryptogram size %d", rx_data[3]);
  113. }
  114. seos_characteristic->secure_messaging =
  115. secure_messaging_alloc(&seos_characteristic->params);
  116. SecureMessaging* secure_messaging = seos_characteristic->secure_messaging;
  117. uint8_t message[] = {0x5c, 0x02, 0xff, 0x00};
  118. secure_messaging_wrap_apdu(secure_messaging, message, sizeof(message), payload);
  119. seos_characteristic->phase = REQUEST_SIO;
  120. view_dispatcher_send_custom_event(
  121. seos_characteristic->seos->view_dispatcher, SeosCustomEventSIORequested);
  122. } else if(seos_characteristic->phase == REQUEST_SIO) {
  123. SecureMessaging* secure_messaging = seos_characteristic->secure_messaging;
  124. BitBuffer* rx_buffer = bit_buffer_alloc(bit_buffer_get_size_bytes(attribute_value) - 1);
  125. bit_buffer_append_bytes(
  126. rx_buffer, rx_data, bit_buffer_get_size_bytes(attribute_value) - 1);
  127. seos_log_bitbuffer(TAG, "BLE response(wrapped)", rx_buffer);
  128. secure_messaging_unwrap_rapdu(secure_messaging, rx_buffer);
  129. seos_log_bitbuffer(TAG, "BLE response(clear)", rx_buffer);
  130. // Skip fileId
  131. seos_characteristic->credential->sio_len = bit_buffer_get_byte(rx_buffer, 2);
  132. if(seos_characteristic->credential->sio_len >
  133. sizeof(seos_characteristic->credential->sio)) {
  134. FURI_LOG_W(TAG, "SIO too long to save");
  135. return;
  136. }
  137. memcpy(
  138. seos_characteristic->credential->sio,
  139. bit_buffer_get_data(rx_buffer) + 3,
  140. seos_characteristic->credential->sio_len);
  141. FURI_LOG_I(TAG, "SIO Captured, %d bytes", seos_characteristic->credential->sio_len);
  142. Seos* seos = seos_characteristic->seos;
  143. view_dispatcher_send_custom_event(seos->view_dispatcher, SeosCustomEventReaderSuccess);
  144. bit_buffer_free(rx_buffer);
  145. seos_characteristic->phase = SELECT_AID;
  146. } else if(data[0] == 0xe1) {
  147. //ignore
  148. } else {
  149. FURI_LOG_W(TAG, "No match for write request");
  150. }
  151. }
  152. void seos_characteristic_cred_flow(
  153. SeosCharacteristic* seos_characteristic,
  154. BitBuffer* attribute_value,
  155. BitBuffer* payload) {
  156. const uint8_t* data = bit_buffer_get_data(attribute_value);
  157. const uint8_t* apdu = data + 1; // Match name to nfc version for easier copying
  158. if(memcmp(apdu, select_header, sizeof(select_header)) == 0) {
  159. if(memcmp(apdu + sizeof(select_header) + 1, standard_seos_aid, sizeof(standard_seos_aid)) ==
  160. 0) {
  161. seos_emulator_select_aid(payload);
  162. bit_buffer_append_bytes(payload, (uint8_t*)success, sizeof(success));
  163. } else {
  164. bit_buffer_append_bytes(payload, (uint8_t*)file_not_found, sizeof(file_not_found));
  165. }
  166. } else if(memcmp(apdu, select_adf_header, sizeof(select_adf_header)) == 0) {
  167. const uint8_t* oid_list = apdu + sizeof(select_adf_header) + 1;
  168. size_t oid_list_len = apdu[sizeof(select_adf_header)];
  169. if(seos_emulator_select_adf(
  170. oid_list,
  171. oid_list_len,
  172. &seos_characteristic->params,
  173. seos_characteristic->credential,
  174. payload)) {
  175. bit_buffer_append_bytes(payload, (uint8_t*)success, sizeof(success));
  176. } else {
  177. FURI_LOG_W(TAG, "Failed to match any ADF OID");
  178. }
  179. } else if(memcmp(apdu, general_authenticate_1, sizeof(general_authenticate_1)) == 0) {
  180. seos_emulator_general_authenticate_1(payload, seos_characteristic->params);
  181. bit_buffer_append_bytes(payload, (uint8_t*)success, sizeof(success));
  182. } else if(memcmp(apdu, general_authenticate_2_header, sizeof(general_authenticate_2_header)) == 0) {
  183. if(!seos_emulator_general_authenticate_2(
  184. apdu,
  185. bit_buffer_get_size_bytes(attribute_value),
  186. seos_characteristic->credential,
  187. &seos_characteristic->params,
  188. payload)) {
  189. FURI_LOG_W(TAG, "Failure in General Authenticate 2");
  190. } else {
  191. bit_buffer_append_bytes(payload, (uint8_t*)success, sizeof(success));
  192. }
  193. view_dispatcher_send_custom_event(
  194. seos_characteristic->seos->view_dispatcher, SeosCustomEventAuthenticated);
  195. // Prepare for future communication
  196. seos_characteristic->secure_messaging =
  197. secure_messaging_alloc(&seos_characteristic->params);
  198. } else if(memcmp(apdu, secure_messaging_header, sizeof(secure_messaging_header)) == 0) {
  199. uint8_t request_sio[] = {0x5c, 0x02, 0xff, 0x00};
  200. if(seos_characteristic->secure_messaging) {
  201. FURI_LOG_D(TAG, "Unwrap secure message");
  202. // c0 0ccb3fff 16 8508fa8395d30de4e8e097008e085da7edbd833b002d00
  203. // Ignore 1 BLE_START byte
  204. size_t bytes_to_ignore = 1;
  205. BitBuffer* tmp = bit_buffer_alloc(bit_buffer_get_size_bytes(attribute_value));
  206. bit_buffer_append_bytes(
  207. tmp,
  208. bit_buffer_get_data(attribute_value) + bytes_to_ignore,
  209. bit_buffer_get_size_bytes(attribute_value) - bytes_to_ignore);
  210. seos_log_bitbuffer(TAG, "received(wrapped)", tmp);
  211. secure_messaging_unwrap_apdu(seos_characteristic->secure_messaging, tmp);
  212. seos_log_bitbuffer(TAG, "received(clear)", tmp);
  213. const uint8_t* message = bit_buffer_get_data(tmp);
  214. if(memcmp(message, request_sio, sizeof(request_sio)) == 0) {
  215. view_dispatcher_send_custom_event(
  216. seos_characteristic->seos->view_dispatcher, SeosCustomEventSIORequested);
  217. BitBuffer* sio_file = bit_buffer_alloc(128);
  218. bit_buffer_append_bytes(sio_file, message + 2, 2); // fileId
  219. bit_buffer_append_byte(sio_file, seos_characteristic->credential->sio_len);
  220. bit_buffer_append_bytes(
  221. sio_file,
  222. seos_characteristic->credential->sio,
  223. seos_characteristic->credential->sio_len);
  224. secure_messaging_wrap_rapdu(
  225. seos_characteristic->secure_messaging,
  226. (uint8_t*)bit_buffer_get_data(sio_file),
  227. bit_buffer_get_size_bytes(sio_file),
  228. payload);
  229. bit_buffer_append_bytes(payload, (uint8_t*)success, sizeof(success));
  230. bit_buffer_free(sio_file);
  231. }
  232. bit_buffer_free(tmp);
  233. } else {
  234. uint8_t no_sm[] = {0x69, 0x88};
  235. bit_buffer_append_bytes(payload, no_sm, sizeof(no_sm));
  236. }
  237. } else if(data[0] == 0xe1) {
  238. // ignore
  239. } else {
  240. FURI_LOG_W(TAG, "no match for attribute_value");
  241. }
  242. }
  243. void seos_characteristic_write_request(void* context, BitBuffer* attribute_value) {
  244. SeosCharacteristic* seos_characteristic = (SeosCharacteristic*)context;
  245. seos_log_bitbuffer(TAG, "write request", attribute_value);
  246. BitBuffer* payload = bit_buffer_alloc(128); // TODO: MTU
  247. const uint8_t* data = bit_buffer_get_data(attribute_value);
  248. if(data[0] != BLE_START && data[0] != 0xe1) {
  249. FURI_LOG_W(TAG, "Unexpected start of BLE packet");
  250. }
  251. if(seos_characteristic->flow_mode == FLOW_READER) {
  252. seos_characteristic_reader_flow(seos_characteristic, attribute_value, payload);
  253. } else if(seos_characteristic->flow_mode == FLOW_CRED) {
  254. seos_characteristic_cred_flow(seos_characteristic, attribute_value, payload);
  255. }
  256. if(bit_buffer_get_size_bytes(payload) > 0) {
  257. BitBuffer* tx = bit_buffer_alloc(1 + 2 + 1 + bit_buffer_get_size_bytes(payload));
  258. bit_buffer_append_byte(tx, BLE_START);
  259. bit_buffer_append_bytes(
  260. tx, bit_buffer_get_data(payload), bit_buffer_get_size_bytes(payload));
  261. seos_att_notify(seos_characteristic->seos_att, seos_characteristic->handle, tx);
  262. bit_buffer_free(tx);
  263. }
  264. bit_buffer_free(payload);
  265. }
  266. void seos_characteristic_on_subscribe(void* context, uint16_t handle) {
  267. SeosCharacteristic* seos_characteristic = (SeosCharacteristic*)context;
  268. FURI_LOG_D(TAG, "seos_characteristic_on_subscribe %04x", handle);
  269. /*
  270. if(seos_characteristic->handle != 0) {
  271. FURI_LOG_W(TAG, "Ignoring subscribe; already subscribed");
  272. return;
  273. }
  274. */
  275. seos_characteristic->handle = handle;
  276. // Send initial select
  277. uint8_t select_header[] = {0x00, 0xa4, 0x04, 0x00, (uint8_t)sizeof(standard_seos_aid)};
  278. BitBuffer* tx = bit_buffer_alloc(1 + sizeof(select_header) + sizeof(standard_seos_aid));
  279. bit_buffer_append_byte(tx, BLE_START);
  280. bit_buffer_append_bytes(tx, select_header, sizeof(select_header));
  281. bit_buffer_append_bytes(tx, standard_seos_aid, sizeof(standard_seos_aid));
  282. seos_log_bitbuffer(TAG, "initial select", tx);
  283. seos_att_notify(seos_characteristic->seos_att, seos_characteristic->handle, tx);
  284. seos_characteristic->phase = SELECT_AID;
  285. bit_buffer_free(tx);
  286. }