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