Coverage Report

Created: 2025-03-01 02:43

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