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