Home | History | Annotate | Line # | Download | only in src
      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