1 /* 2 * Copyright (c) 2020-2022 Yubico AB. All rights reserved. 3 * Use of this source code is governed by a BSD-style 4 * license that can be found in the LICENSE file. 5 * SPDX-License-Identifier: BSD-2-Clause 6 */ 7 8 #include <stdio.h> 9 #include <string.h> 10 11 #include "fido.h" 12 #include "fido/param.h" 13 #include "iso7816.h" 14 15 #define TX_CHUNK_SIZE 240 16 17 static const uint8_t aid[] = { 0xa0, 0x00, 0x00, 0x06, 0x47, 0x2f, 0x00, 0x01 }; 18 static const uint8_t v_u2f[] = { 'U', '2', 'F', '_', 'V', '2' }; 19 static const uint8_t v_fido[] = { 'F', 'I', 'D', 'O', '_', '2', '_', '0' }; 20 21 static int 22 tx_short_apdu(fido_dev_t *d, const iso7816_header_t *h, const uint8_t *payload, 23 uint8_t payload_len, uint8_t cla_flags) 24 { 25 uint8_t apdu[5 + UINT8_MAX + 1]; 26 uint8_t sw[2]; 27 size_t apdu_len; 28 int ok = -1; 29 30 memset(&apdu, 0, sizeof(apdu)); 31 apdu[0] = h->cla | cla_flags; 32 apdu[1] = h->ins; 33 apdu[2] = h->p1; 34 apdu[3] = h->p2; 35 apdu[4] = payload_len; 36 memcpy(&apdu[5], payload, payload_len); 37 apdu_len = (size_t)(5 + payload_len + 1); 38 39 if (d->io.write(d->io_handle, apdu, apdu_len) < 0) { 40 fido_log_debug("%s: write", __func__); 41 goto fail; 42 } 43 44 if (cla_flags & 0x10) { 45 if (d->io.read(d->io_handle, sw, sizeof(sw), -1) != 2) { 46 fido_log_debug("%s: read", __func__); 47 goto fail; 48 } 49 if ((sw[0] << 8 | sw[1]) != SW_NO_ERROR) { 50 fido_log_debug("%s: unexpected sw", __func__); 51 goto fail; 52 } 53 } 54 55 ok = 0; 56 fail: 57 explicit_bzero(apdu, sizeof(apdu)); 58 59 return ok; 60 } 61 62 static int 63 nfc_do_tx(fido_dev_t *d, const uint8_t *apdu_ptr, size_t apdu_len) 64 { 65 iso7816_header_t h; 66 67 if (fido_buf_read(&apdu_ptr, &apdu_len, &h, sizeof(h)) < 0) { 68 fido_log_debug("%s: header", __func__); 69 return -1; 70 } 71 if (apdu_len < 2) { 72 fido_log_debug("%s: apdu_len %zu", __func__, apdu_len); 73 return -1; 74 } 75 76 apdu_len -= 2; /* trim le1 le2 */ 77 78 while (apdu_len > TX_CHUNK_SIZE) { 79 if (tx_short_apdu(d, &h, apdu_ptr, TX_CHUNK_SIZE, 0x10) < 0) { 80 fido_log_debug("%s: chain", __func__); 81 return -1; 82 } 83 apdu_ptr += TX_CHUNK_SIZE; 84 apdu_len -= TX_CHUNK_SIZE; 85 } 86 87 if (tx_short_apdu(d, &h, apdu_ptr, (uint8_t)apdu_len, 0) < 0) { 88 fido_log_debug("%s: tx_short_apdu", __func__); 89 return -1; 90 } 91 92 return 0; 93 } 94 95 int 96 fido_nfc_tx(fido_dev_t *d, uint8_t cmd, const unsigned char *buf, size_t count) 97 { 98 iso7816_apdu_t *apdu = NULL; 99 const uint8_t *ptr; 100 size_t len; 101 int ok = -1; 102 103 switch (cmd) { 104 case CTAP_CMD_INIT: /* select */ 105 if ((apdu = iso7816_new(0, 0xa4, 0x04, sizeof(aid))) == NULL || 106 iso7816_add(apdu, aid, sizeof(aid)) < 0) { 107 fido_log_debug("%s: iso7816", __func__); 108 goto fail; 109 } 110 break; 111 case CTAP_CMD_CBOR: /* wrap cbor */ 112 if (count > UINT16_MAX || (apdu = iso7816_new(0x80, 0x10, 0x00, 113 (uint16_t)count)) == NULL || 114 iso7816_add(apdu, buf, count) < 0) { 115 fido_log_debug("%s: iso7816", __func__); 116 goto fail; 117 } 118 break; 119 case CTAP_CMD_MSG: /* already an apdu */ 120 break; 121 default: 122 fido_log_debug("%s: cmd=%02x", __func__, cmd); 123 goto fail; 124 } 125 126 if (apdu != NULL) { 127 ptr = iso7816_ptr(apdu); 128 len = iso7816_len(apdu); 129 } else { 130 ptr = buf; 131 len = count; 132 } 133 134 if (nfc_do_tx(d, ptr, len) < 0) { 135 fido_log_debug("%s: nfc_do_tx", __func__); 136 goto fail; 137 } 138 139 ok = 0; 140 fail: 141 iso7816_free(&apdu); 142 143 return ok; 144 } 145 146 static int 147 rx_init(fido_dev_t *d, unsigned char *buf, size_t count, int ms) 148 { 149 fido_ctap_info_t *attr = (fido_ctap_info_t *)buf; 150 uint8_t f[64]; 151 int n; 152 153 if (count != sizeof(*attr)) { 154 fido_log_debug("%s: count=%zu", __func__, count); 155 return -1; 156 } 157 158 memset(attr, 0, sizeof(*attr)); 159 160 if ((n = d->io.read(d->io_handle, f, sizeof(f), ms)) < 2 || 161 (f[n - 2] << 8 | f[n - 1]) != SW_NO_ERROR) { 162 fido_log_debug("%s: read", __func__); 163 return -1; 164 } 165 166 n -= 2; 167 168 if (n == sizeof(v_u2f) && memcmp(f, v_u2f, sizeof(v_u2f)) == 0) 169 attr->flags = FIDO_CAP_CBOR; 170 else if (n == sizeof(v_fido) && memcmp(f, v_fido, sizeof(v_fido)) == 0) 171 attr->flags = FIDO_CAP_CBOR | FIDO_CAP_NMSG; 172 else { 173 fido_log_debug("%s: unknown version string", __func__); 174 #ifdef FIDO_FUZZ 175 attr->flags = FIDO_CAP_CBOR | FIDO_CAP_NMSG; 176 #else 177 return -1; 178 #endif 179 } 180 181 memcpy(&attr->nonce, &d->nonce, sizeof(attr->nonce)); /* XXX */ 182 183 return (int)count; 184 } 185 186 static int 187 tx_get_response(fido_dev_t *d, uint8_t count) 188 { 189 uint8_t apdu[5]; 190 191 memset(apdu, 0, sizeof(apdu)); 192 apdu[1] = 0xc0; /* GET_RESPONSE */ 193 apdu[4] = count; 194 195 if (d->io.write(d->io_handle, apdu, sizeof(apdu)) < 0) { 196 fido_log_debug("%s: write", __func__); 197 return -1; 198 } 199 200 return 0; 201 } 202 203 static int 204 rx_apdu(fido_dev_t *d, uint8_t sw[2], unsigned char **buf, size_t *count, int *ms) 205 { 206 uint8_t f[256 + 2]; 207 struct timespec ts; 208 int n, ok = -1; 209 210 if (fido_time_now(&ts) != 0) 211 goto fail; 212 213 if ((n = d->io.read(d->io_handle, f, sizeof(f), *ms)) < 2) { 214 fido_log_debug("%s: read", __func__); 215 goto fail; 216 } 217 218 if (fido_time_delta(&ts, ms) != 0) 219 goto fail; 220 221 if (fido_buf_write(buf, count, f, (size_t)(n - 2)) < 0) { 222 fido_log_debug("%s: fido_buf_write", __func__); 223 goto fail; 224 } 225 226 memcpy(sw, f + n - 2, 2); 227 228 ok = 0; 229 fail: 230 explicit_bzero(f, sizeof(f)); 231 232 return ok; 233 } 234 235 static int 236 rx_msg(fido_dev_t *d, unsigned char *buf, size_t count, int ms) 237 { 238 uint8_t sw[2]; 239 const size_t bufsiz = count; 240 241 if (rx_apdu(d, sw, &buf, &count, &ms) < 0) { 242 fido_log_debug("%s: preamble", __func__); 243 return -1; 244 } 245 246 while (sw[0] == SW1_MORE_DATA) 247 if (tx_get_response(d, sw[1]) < 0 || 248 rx_apdu(d, sw, &buf, &count, &ms) < 0) { 249 fido_log_debug("%s: chain", __func__); 250 return -1; 251 } 252 253 if (fido_buf_write(&buf, &count, sw, sizeof(sw)) < 0) { 254 fido_log_debug("%s: sw", __func__); 255 return -1; 256 } 257 258 if (bufsiz - count > INT_MAX) { 259 fido_log_debug("%s: bufsiz", __func__); 260 return -1; 261 } 262 263 return (int)(bufsiz - count); 264 } 265 266 static int 267 rx_cbor(fido_dev_t *d, unsigned char *buf, size_t count, int ms) 268 { 269 int r; 270 271 if ((r = rx_msg(d, buf, count, ms)) < 2) 272 return -1; 273 274 return r - 2; 275 } 276 277 int 278 fido_nfc_rx(fido_dev_t *d, uint8_t cmd, unsigned char *buf, size_t count, int ms) 279 { 280 switch (cmd) { 281 case CTAP_CMD_INIT: 282 return rx_init(d, buf, count, ms); 283 case CTAP_CMD_CBOR: 284 return rx_cbor(d, buf, count, ms); 285 case CTAP_CMD_MSG: 286 return rx_msg(d, buf, count, ms); 287 default: 288 fido_log_debug("%s: cmd=%02x", __func__, cmd); 289 return -1; 290 } 291 } 292 293 bool 294 nfc_is_fido(const char *path) 295 { 296 bool fido = false; 297 fido_dev_t *d; 298 int r; 299 300 if ((d = fido_dev_new()) == NULL) { 301 fido_log_debug("%s: fido_dev_new", __func__); 302 goto fail; 303 } 304 /* fido_dev_open selects the fido applet */ 305 if ((r = fido_dev_open(d, path)) != FIDO_OK) { 306 fido_log_debug("%s: fido_dev_open: 0x%x", __func__, r); 307 goto fail; 308 } 309 if ((r = fido_dev_close(d)) != FIDO_OK) { 310 fido_log_debug("%s: fido_dev_close: 0x%x", __func__, r); 311 goto fail; 312 313 } 314 315 fido = true; 316 fail: 317 fido_dev_free(&d); 318 319 return fido; 320 } 321 322 #ifdef USE_NFC 323 bool 324 fido_is_nfc(const char *path) 325 { 326 return strncmp(path, FIDO_NFC_PREFIX, strlen(FIDO_NFC_PREFIX)) == 0; 327 } 328 329 int 330 fido_dev_set_nfc(fido_dev_t *d) 331 { 332 if (d->io_handle != NULL) { 333 fido_log_debug("%s: device open", __func__); 334 return -1; 335 } 336 d->io_own = true; 337 d->io = (fido_dev_io_t) { 338 fido_nfc_open, 339 fido_nfc_close, 340 fido_nfc_read, 341 fido_nfc_write, 342 }; 343 d->transport = (fido_dev_transport_t) { 344 fido_nfc_rx, 345 fido_nfc_tx, 346 }; 347 348 return 0; 349 } 350 #endif /* USE_NFC */ 351