Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2018-2026 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 <openssl/sha.h> |
9 | | #include <openssl/x509.h> |
10 | | |
11 | | #ifdef HAVE_UNISTD_H |
12 | | #include <unistd.h> |
13 | | #endif |
14 | | #include <errno.h> |
15 | | |
16 | | #include "fido.h" |
17 | | #include "fido/es256.h" |
18 | | #include "fallthrough.h" |
19 | | |
20 | 3.88k | #define U2F_PACE_MS (100) |
21 | 12.3k | #define U2F_IO_MS (20) |
22 | | |
23 | | #if defined(_MSC_VER) |
24 | | static int |
25 | | usleep(unsigned int usec) |
26 | | { |
27 | | Sleep(usec / 1000); |
28 | | |
29 | | return (0); |
30 | | } |
31 | | #endif |
32 | | |
33 | | static int |
34 | | delay_ms(unsigned int ms, int *ms_remain) |
35 | 3.88k | { |
36 | 3.88k | if (*ms_remain > -1 && (unsigned int)*ms_remain < ms) |
37 | 629 | ms = (unsigned int)*ms_remain; |
38 | | |
39 | 3.88k | if (ms > UINT_MAX / 1000) { |
40 | 0 | fido_log_debug("%s: ms=%u", __func__, ms); |
41 | 0 | return (-1); |
42 | 0 | } |
43 | | |
44 | 3.88k | if (usleep(ms * 1000) < 0) { |
45 | 24 | fido_log_error(errno, "%s: usleep", __func__); |
46 | 24 | return (-1); |
47 | 24 | } |
48 | | |
49 | 3.86k | if (*ms_remain > -1) |
50 | 3.86k | *ms_remain -= (int)ms; |
51 | | |
52 | 3.86k | return (0); |
53 | 3.88k | } |
54 | | |
55 | | static int |
56 | | sig_get(fido_blob_t *sig, const unsigned char **buf, size_t *len) |
57 | 621 | { |
58 | 621 | sig->len = *len; /* consume the whole buffer */ |
59 | 621 | if ((sig->ptr = calloc(1, sig->len)) == NULL || |
60 | 621 | fido_buf_read(buf, len, sig->ptr, sig->len) < 0) { |
61 | 2 | fido_log_debug("%s: fido_buf_read", __func__); |
62 | 2 | fido_blob_reset(sig); |
63 | 2 | return (-1); |
64 | 2 | } |
65 | | |
66 | 619 | return (0); |
67 | 621 | } |
68 | | |
69 | | static int |
70 | | x5c_get(fido_blob_t *x5c, const unsigned char **buf, size_t *len) |
71 | 1.70k | { |
72 | 1.70k | X509 *cert = NULL; |
73 | 1.70k | int ok = -1; |
74 | | |
75 | 1.70k | if (*len > LONG_MAX) { |
76 | 0 | fido_log_debug("%s: invalid len %zu", __func__, *len); |
77 | 0 | goto fail; |
78 | 0 | } |
79 | | |
80 | | /* find out the certificate's length */ |
81 | 1.70k | const unsigned char *end = *buf; |
82 | 1.70k | if ((cert = d2i_X509(NULL, &end, (long)*len)) == NULL || end <= *buf || |
83 | 1.70k | (x5c->len = (size_t)(end - *buf)) >= *len) { |
84 | 1.08k | fido_log_debug("%s: d2i_X509", __func__); |
85 | 1.08k | goto fail; |
86 | 1.08k | } |
87 | | |
88 | | /* read accordingly */ |
89 | 623 | if ((x5c->ptr = calloc(1, x5c->len)) == NULL || |
90 | 623 | fido_buf_read(buf, len, x5c->ptr, x5c->len) < 0) { |
91 | 2 | fido_log_debug("%s: fido_buf_read", __func__); |
92 | 2 | goto fail; |
93 | 2 | } |
94 | | |
95 | 621 | ok = 0; |
96 | 1.70k | fail: |
97 | 1.70k | if (cert != NULL) |
98 | 623 | X509_free(cert); |
99 | | |
100 | 1.70k | if (ok < 0) |
101 | 1.08k | fido_blob_reset(x5c); |
102 | | |
103 | 1.70k | return (ok); |
104 | 621 | } |
105 | | |
106 | | static int |
107 | | authdata_fake(const char *rp_id, uint8_t flags, uint32_t sigcount, |
108 | | fido_blob_t *fake_cbor_ad) |
109 | 0 | { |
110 | 0 | fido_authdata_t ad; |
111 | 0 | cbor_item_t *item = NULL; |
112 | 0 | size_t alloc_len; |
113 | |
|
114 | 0 | memset(&ad, 0, sizeof(ad)); |
115 | |
|
116 | 0 | if (SHA256((const void *)rp_id, strlen(rp_id), |
117 | 0 | ad.rp_id_hash) != ad.rp_id_hash) { |
118 | 0 | fido_log_debug("%s: sha256", __func__); |
119 | 0 | return (-1); |
120 | 0 | } |
121 | | |
122 | 0 | ad.flags = flags; /* XXX translate? */ |
123 | 0 | ad.sigcount = sigcount; |
124 | |
|
125 | 0 | if ((item = cbor_build_bytestring((const unsigned char *)&ad, |
126 | 0 | sizeof(ad))) == NULL) { |
127 | 0 | fido_log_debug("%s: cbor_build_bytestring", __func__); |
128 | 0 | return (-1); |
129 | 0 | } |
130 | | |
131 | 0 | if (fake_cbor_ad->ptr != NULL || |
132 | 0 | (fake_cbor_ad->len = cbor_serialize_alloc(item, &fake_cbor_ad->ptr, |
133 | 0 | &alloc_len)) == 0) { |
134 | 0 | fido_log_debug("%s: cbor_serialize_alloc", __func__); |
135 | 0 | cbor_decref(&item); |
136 | 0 | return (-1); |
137 | 0 | } |
138 | | |
139 | 0 | cbor_decref(&item); |
140 | |
|
141 | 0 | return (0); |
142 | 0 | } |
143 | | |
144 | | static int |
145 | | check_io_timeout(int ms) |
146 | 12.3k | { |
147 | 12.3k | if (ms < 0 || ms >= U2F_IO_MS) |
148 | 10.7k | return FIDO_OK; |
149 | | |
150 | 1.58k | return FIDO_ERR_TIMEOUT; |
151 | 12.3k | } |
152 | | |
153 | | /* TODO: use u2f_get_touch_begin & u2f_get_touch_status instead */ |
154 | | static int |
155 | | send_dummy_register(fido_dev_t *dev, int *ms) |
156 | 385 | { |
157 | 385 | iso7816_apdu_t *apdu = NULL; |
158 | 385 | unsigned char *reply = NULL; |
159 | 385 | unsigned char challenge[SHA256_DIGEST_LENGTH]; |
160 | 385 | unsigned char application[SHA256_DIGEST_LENGTH]; |
161 | 385 | int r; |
162 | | |
163 | | /* dummy challenge & application */ |
164 | 385 | memset(&challenge, 0xff, sizeof(challenge)); |
165 | 385 | memset(&application, 0xff, sizeof(application)); |
166 | | |
167 | 385 | if ((apdu = iso7816_new(0, U2F_CMD_REGISTER, 0, 2 * |
168 | 385 | SHA256_DIGEST_LENGTH)) == NULL || |
169 | 385 | iso7816_add(apdu, &challenge, sizeof(challenge)) < 0 || |
170 | 385 | iso7816_add(apdu, &application, sizeof(application)) < 0) { |
171 | 6 | fido_log_debug("%s: iso7816", __func__); |
172 | 6 | r = FIDO_ERR_INTERNAL; |
173 | 6 | goto fail; |
174 | 6 | } |
175 | | |
176 | 379 | if ((reply = malloc(FIDO_MAXMSG)) == NULL) { |
177 | 7 | fido_log_debug("%s: malloc", __func__); |
178 | 7 | r = FIDO_ERR_INTERNAL; |
179 | 7 | goto fail; |
180 | 7 | } |
181 | | |
182 | 945 | do { |
183 | 945 | if ((r = check_io_timeout(*ms)) != FIDO_OK) { |
184 | 212 | fido_log_debug("%s: check_io_timeout", __func__); |
185 | 212 | goto fail; |
186 | 212 | } |
187 | 733 | if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu), |
188 | 733 | iso7816_len(apdu), ms) < 0) { |
189 | 25 | fido_log_debug("%s: fido_tx", __func__); |
190 | 25 | r = FIDO_ERR_TX; |
191 | 25 | goto fail; |
192 | 25 | } |
193 | 708 | if (fido_rx(dev, CTAP_CMD_MSG, reply, FIDO_MAXMSG, ms) < 2) { |
194 | 65 | fido_log_debug("%s: fido_rx", __func__); |
195 | 65 | r = FIDO_ERR_RX; |
196 | 65 | goto fail; |
197 | 65 | } |
198 | 643 | if (delay_ms(U2F_PACE_MS, ms) != 0) { |
199 | 20 | fido_log_debug("%s: delay_ms", __func__); |
200 | 20 | r = FIDO_ERR_RX; |
201 | 20 | goto fail; |
202 | 20 | } |
203 | 643 | } while (((reply[0] << 8) | reply[1]) == SW_CONDITIONS_NOT_SATISFIED); |
204 | | |
205 | 50 | r = FIDO_OK; |
206 | 385 | fail: |
207 | 385 | iso7816_free(&apdu); |
208 | 385 | freezero(reply, FIDO_MAXMSG); |
209 | | |
210 | 385 | return (r); |
211 | 50 | } |
212 | | |
213 | | static int |
214 | | key_lookup(fido_dev_t *dev, const char *rp_id, const fido_blob_t *key_id, |
215 | | int *found, int *ms) |
216 | 6.82k | { |
217 | 6.82k | iso7816_apdu_t *apdu = NULL; |
218 | 6.82k | unsigned char *reply = NULL; |
219 | 6.82k | unsigned char challenge[SHA256_DIGEST_LENGTH]; |
220 | 6.82k | unsigned char rp_id_hash[SHA256_DIGEST_LENGTH]; |
221 | 6.82k | uint8_t key_id_len; |
222 | 6.82k | int r; |
223 | | |
224 | 6.82k | if (key_id->len > UINT8_MAX || rp_id == NULL) { |
225 | 62 | fido_log_debug("%s: key_id->len=%zu, rp_id=%p", __func__, |
226 | 62 | key_id->len, (const void *)rp_id); |
227 | 62 | r = FIDO_ERR_INVALID_ARGUMENT; |
228 | 62 | goto fail; |
229 | 62 | } |
230 | | |
231 | 6.76k | memset(&challenge, 0xff, sizeof(challenge)); |
232 | 6.76k | memset(&rp_id_hash, 0, sizeof(rp_id_hash)); |
233 | | |
234 | 6.76k | if (SHA256((const void *)rp_id, strlen(rp_id), |
235 | 6.76k | rp_id_hash) != rp_id_hash) { |
236 | 15 | fido_log_debug("%s: sha256", __func__); |
237 | 15 | r = FIDO_ERR_INTERNAL; |
238 | 15 | goto fail; |
239 | 15 | } |
240 | | |
241 | 6.74k | key_id_len = (uint8_t)key_id->len; |
242 | | |
243 | 6.74k | if ((apdu = iso7816_new(0, U2F_CMD_AUTH, U2F_AUTH_CHECK, (uint16_t)(2 * |
244 | 6.74k | SHA256_DIGEST_LENGTH + sizeof(key_id_len) + key_id_len))) == NULL || |
245 | 6.74k | iso7816_add(apdu, &challenge, sizeof(challenge)) < 0 || |
246 | 6.74k | iso7816_add(apdu, &rp_id_hash, sizeof(rp_id_hash)) < 0 || |
247 | 6.74k | iso7816_add(apdu, &key_id_len, sizeof(key_id_len)) < 0 || |
248 | 6.74k | iso7816_add(apdu, key_id->ptr, key_id_len) < 0) { |
249 | 12 | fido_log_debug("%s: iso7816", __func__); |
250 | 12 | r = FIDO_ERR_INTERNAL; |
251 | 12 | goto fail; |
252 | 12 | } |
253 | | |
254 | 6.73k | if ((reply = malloc(FIDO_MAXMSG)) == NULL) { |
255 | 13 | fido_log_debug("%s: malloc", __func__); |
256 | 13 | r = FIDO_ERR_INTERNAL; |
257 | 13 | goto fail; |
258 | 13 | } |
259 | | |
260 | 6.72k | if ((r = check_io_timeout(*ms)) != FIDO_OK) { |
261 | 60 | fido_log_debug("%s: check_io_timeout", __func__); |
262 | 60 | goto fail; |
263 | 60 | } |
264 | 6.66k | if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu), |
265 | 6.66k | iso7816_len(apdu), ms) < 0) { |
266 | 331 | fido_log_debug("%s: fido_tx", __func__); |
267 | 331 | r = FIDO_ERR_TX; |
268 | 331 | goto fail; |
269 | 331 | } |
270 | 6.33k | if (fido_rx(dev, CTAP_CMD_MSG, reply, FIDO_MAXMSG, ms) != 2) { |
271 | 3.13k | fido_log_debug("%s: fido_rx", __func__); |
272 | 3.13k | r = FIDO_ERR_RX; |
273 | 3.13k | goto fail; |
274 | 3.13k | } |
275 | | |
276 | 3.19k | switch ((reply[0] << 8) | reply[1]) { |
277 | 2.69k | case SW_CONDITIONS_NOT_SATISFIED: |
278 | 2.69k | *found = 1; /* key exists */ |
279 | 2.69k | break; |
280 | 79 | case SW_WRONG_DATA: |
281 | 85 | case SW_WRONG_LENGTH: |
282 | 85 | *found = 0; /* key does not exist */ |
283 | 85 | break; |
284 | 415 | default: |
285 | | /* unexpected sw */ |
286 | 415 | r = FIDO_ERR_INTERNAL; |
287 | 415 | goto fail; |
288 | 3.19k | } |
289 | | |
290 | 2.77k | r = FIDO_OK; |
291 | 6.82k | fail: |
292 | 6.82k | iso7816_free(&apdu); |
293 | 6.82k | freezero(reply, FIDO_MAXMSG); |
294 | | |
295 | 6.82k | return (r); |
296 | 2.77k | } |
297 | | |
298 | | static int |
299 | | parse_auth_reply(fido_blob_t *sig, fido_blob_t *ad, const char *rp_id, |
300 | | const unsigned char *reply, size_t len) |
301 | 0 | { |
302 | 0 | uint8_t flags; |
303 | 0 | uint32_t sigcount; |
304 | |
|
305 | 0 | if (len < 2 || ((reply[len - 2] << 8) | reply[len - 1]) != SW_NO_ERROR) { |
306 | 0 | fido_log_debug("%s: unexpected sw", __func__); |
307 | 0 | return (FIDO_ERR_RX); |
308 | 0 | } |
309 | | |
310 | 0 | len -= 2; |
311 | |
|
312 | 0 | if (fido_buf_read(&reply, &len, &flags, sizeof(flags)) < 0 || |
313 | 0 | fido_buf_read(&reply, &len, &sigcount, sizeof(sigcount)) < 0) { |
314 | 0 | fido_log_debug("%s: fido_buf_read", __func__); |
315 | 0 | return (FIDO_ERR_RX); |
316 | 0 | } |
317 | | |
318 | 0 | if (sig_get(sig, &reply, &len) < 0) { |
319 | 0 | fido_log_debug("%s: sig_get", __func__); |
320 | 0 | return (FIDO_ERR_RX); |
321 | 0 | } |
322 | | |
323 | 0 | if (authdata_fake(rp_id, flags, sigcount, ad) < 0) { |
324 | 0 | fido_log_debug("%s; authdata_fake", __func__); |
325 | 0 | return (FIDO_ERR_RX); |
326 | 0 | } |
327 | | |
328 | 0 | return (FIDO_OK); |
329 | 0 | } |
330 | | |
331 | | static int |
332 | | do_auth(fido_dev_t *dev, const fido_blob_t *cdh, const char *rp_id, |
333 | | const fido_blob_t *key_id, fido_blob_t *sig, fido_blob_t *ad, int *ms) |
334 | 990 | { |
335 | 990 | iso7816_apdu_t *apdu = NULL; |
336 | 990 | unsigned char *reply = NULL; |
337 | 990 | unsigned char rp_id_hash[SHA256_DIGEST_LENGTH]; |
338 | 990 | int reply_len; |
339 | 990 | uint8_t key_id_len; |
340 | 990 | int r; |
341 | | |
342 | 990 | #ifdef FIDO_FUZZ |
343 | 990 | *ms = 0; /* XXX */ |
344 | 990 | #endif |
345 | | |
346 | 990 | if (cdh->len != SHA256_DIGEST_LENGTH || key_id->len > UINT8_MAX || |
347 | 990 | rp_id == NULL) { |
348 | 89 | r = FIDO_ERR_INVALID_ARGUMENT; |
349 | 89 | goto fail; |
350 | 89 | } |
351 | | |
352 | 901 | memset(&rp_id_hash, 0, sizeof(rp_id_hash)); |
353 | | |
354 | 901 | if (SHA256((const void *)rp_id, strlen(rp_id), |
355 | 901 | rp_id_hash) != rp_id_hash) { |
356 | 3 | fido_log_debug("%s: sha256", __func__); |
357 | 3 | r = FIDO_ERR_INTERNAL; |
358 | 3 | goto fail; |
359 | 3 | } |
360 | | |
361 | 898 | key_id_len = (uint8_t)key_id->len; |
362 | | |
363 | 898 | if ((apdu = iso7816_new(0, U2F_CMD_AUTH, U2F_AUTH_SIGN, (uint16_t)(2 * |
364 | 898 | SHA256_DIGEST_LENGTH + sizeof(key_id_len) + key_id_len))) == NULL || |
365 | 898 | iso7816_add(apdu, cdh->ptr, cdh->len) < 0 || |
366 | 898 | iso7816_add(apdu, &rp_id_hash, sizeof(rp_id_hash)) < 0 || |
367 | 898 | iso7816_add(apdu, &key_id_len, sizeof(key_id_len)) < 0 || |
368 | 898 | iso7816_add(apdu, key_id->ptr, key_id_len) < 0) { |
369 | 3 | fido_log_debug("%s: iso7816", __func__); |
370 | 3 | r = FIDO_ERR_INTERNAL; |
371 | 3 | goto fail; |
372 | 3 | } |
373 | | |
374 | 895 | if ((reply = malloc(FIDO_MAXMSG)) == NULL) { |
375 | 3 | fido_log_debug("%s: malloc", __func__); |
376 | 3 | r = FIDO_ERR_INTERNAL; |
377 | 3 | goto fail; |
378 | 3 | } |
379 | | |
380 | 892 | do { |
381 | 892 | if ((r = check_io_timeout(*ms)) != FIDO_OK) { |
382 | 892 | fido_log_debug("%s: check_io_timeout", __func__); |
383 | 892 | goto fail; |
384 | 892 | } |
385 | 0 | if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu), |
386 | 0 | iso7816_len(apdu), ms) < 0) { |
387 | 0 | fido_log_debug("%s: fido_tx", __func__); |
388 | 0 | r = FIDO_ERR_TX; |
389 | 0 | goto fail; |
390 | 0 | } |
391 | 0 | if ((reply_len = fido_rx(dev, CTAP_CMD_MSG, reply, |
392 | 0 | FIDO_MAXMSG, ms)) < 2) { |
393 | 0 | fido_log_debug("%s: fido_rx", __func__); |
394 | 0 | r = FIDO_ERR_RX; |
395 | 0 | goto fail; |
396 | 0 | } |
397 | 0 | if (delay_ms(U2F_PACE_MS, ms) != 0) { |
398 | 0 | fido_log_debug("%s: delay_ms", __func__); |
399 | 0 | r = FIDO_ERR_RX; |
400 | 0 | goto fail; |
401 | 0 | } |
402 | 0 | } while (((reply[0] << 8) | reply[1]) == SW_CONDITIONS_NOT_SATISFIED); |
403 | | |
404 | 0 | if ((r = parse_auth_reply(sig, ad, rp_id, reply, |
405 | 0 | (size_t)reply_len)) != FIDO_OK) { |
406 | 0 | fido_log_debug("%s: parse_auth_reply", __func__); |
407 | 0 | goto fail; |
408 | 0 | } |
409 | | |
410 | 990 | fail: |
411 | 990 | iso7816_free(&apdu); |
412 | 990 | freezero(reply, FIDO_MAXMSG); |
413 | | |
414 | 990 | return (r); |
415 | 0 | } |
416 | | |
417 | | static int |
418 | | cbor_blob_from_ec_point(const uint8_t *ec_point, size_t ec_point_len, |
419 | | fido_blob_t *cbor_blob) |
420 | 573 | { |
421 | 573 | es256_pk_t *pk = NULL; |
422 | 573 | cbor_item_t *pk_cbor = NULL; |
423 | 573 | size_t alloc_len; |
424 | 573 | int ok = -1; |
425 | | |
426 | | /* only handle uncompressed points */ |
427 | 573 | if (ec_point_len != 65 || ec_point[0] != 0x04) { |
428 | 13 | fido_log_debug("%s: unexpected format", __func__); |
429 | 13 | goto fail; |
430 | 13 | } |
431 | | |
432 | 560 | if ((pk = es256_pk_new()) == NULL || |
433 | 560 | es256_pk_set_x(pk, &ec_point[1]) < 0 || |
434 | 560 | es256_pk_set_y(pk, &ec_point[33]) < 0) { |
435 | 1 | fido_log_debug("%s: es256_pk_set", __func__); |
436 | 1 | goto fail; |
437 | 1 | } |
438 | | |
439 | 559 | if ((pk_cbor = es256_pk_encode(pk, 0)) == NULL) { |
440 | 8 | fido_log_debug("%s: es256_pk_encode", __func__); |
441 | 8 | goto fail; |
442 | 8 | } |
443 | | |
444 | 551 | if ((cbor_blob->len = cbor_serialize_alloc(pk_cbor, &cbor_blob->ptr, |
445 | 551 | &alloc_len)) != 77) { |
446 | 5 | fido_log_debug("%s: cbor_serialize_alloc", __func__); |
447 | 5 | goto fail; |
448 | 5 | } |
449 | | |
450 | 546 | ok = 0; |
451 | 573 | fail: |
452 | 573 | es256_pk_free(&pk); |
453 | | |
454 | 573 | if (pk_cbor) |
455 | 551 | cbor_decref(&pk_cbor); |
456 | | |
457 | 573 | return (ok); |
458 | 546 | } |
459 | | |
460 | | static int |
461 | | encode_cred_attstmt(int cose_alg, const fido_blob_t *x5c, |
462 | | const fido_blob_t *sig, fido_blob_t *out) |
463 | 619 | { |
464 | 619 | cbor_item_t *item = NULL; |
465 | 619 | cbor_item_t *x5c_cbor = NULL; |
466 | 619 | const uint8_t alg_cbor = (uint8_t)(-cose_alg - 1); |
467 | 619 | struct cbor_pair kv[3]; |
468 | 619 | size_t alloc_len; |
469 | 619 | int ok = -1; |
470 | | |
471 | 619 | memset(&kv, 0, sizeof(kv)); |
472 | 619 | memset(out, 0, sizeof(*out)); |
473 | | |
474 | 619 | if ((item = cbor_new_definite_map(3)) == NULL) { |
475 | 1 | fido_log_debug("%s: cbor_new_definite_map", __func__); |
476 | 1 | goto fail; |
477 | 1 | } |
478 | | |
479 | 618 | if ((kv[0].key = cbor_build_string("alg")) == NULL || |
480 | 618 | (kv[0].value = cbor_build_negint8(alg_cbor)) == NULL || |
481 | 618 | !cbor_map_add(item, kv[0])) { |
482 | 10 | fido_log_debug("%s: alg", __func__); |
483 | 10 | goto fail; |
484 | 10 | } |
485 | | |
486 | 608 | if ((kv[1].key = cbor_build_string("sig")) == NULL || |
487 | 608 | (kv[1].value = fido_blob_encode(sig)) == NULL || |
488 | 608 | !cbor_map_add(item, kv[1])) { |
489 | 11 | fido_log_debug("%s: sig", __func__); |
490 | 11 | goto fail; |
491 | 11 | } |
492 | | |
493 | 597 | if ((kv[2].key = cbor_build_string("x5c")) == NULL || |
494 | 597 | (kv[2].value = cbor_new_definite_array(1)) == NULL || |
495 | 597 | (x5c_cbor = fido_blob_encode(x5c)) == NULL || |
496 | 597 | !cbor_array_push(kv[2].value, x5c_cbor) || |
497 | 597 | !cbor_map_add(item, kv[2])) { |
498 | 19 | fido_log_debug("%s: x5c", __func__); |
499 | 19 | goto fail; |
500 | 19 | } |
501 | | |
502 | 578 | if ((out->len = cbor_serialize_alloc(item, &out->ptr, |
503 | 578 | &alloc_len)) == 0) { |
504 | 5 | fido_log_debug("%s: cbor_serialize_alloc", __func__); |
505 | 5 | goto fail; |
506 | 5 | } |
507 | | |
508 | 573 | ok = 0; |
509 | 619 | fail: |
510 | 619 | if (item != NULL) |
511 | 618 | cbor_decref(&item); |
512 | 619 | if (x5c_cbor != NULL) |
513 | 585 | cbor_decref(&x5c_cbor); |
514 | | |
515 | 2.47k | for (size_t i = 0; i < nitems(kv); i++) { |
516 | 1.85k | if (kv[i].key) |
517 | 1.81k | cbor_decref(&kv[i].key); |
518 | 1.85k | if (kv[i].value) |
519 | 1.80k | cbor_decref(&kv[i].value); |
520 | 1.85k | } |
521 | | |
522 | 619 | return (ok); |
523 | 573 | } |
524 | | |
525 | | static int |
526 | | encode_cred_authdata(const char *rp_id, const uint8_t *kh, uint8_t kh_len, |
527 | | const uint8_t *pubkey, size_t pubkey_len, fido_blob_t *out) |
528 | 573 | { |
529 | 573 | fido_authdata_t authdata; |
530 | 573 | fido_attcred_raw_t attcred_raw; |
531 | 573 | fido_blob_t pk_blob; |
532 | 573 | fido_blob_t authdata_blob; |
533 | 573 | cbor_item_t *authdata_cbor = NULL; |
534 | 573 | unsigned char *ptr; |
535 | 573 | size_t len; |
536 | 573 | size_t alloc_len; |
537 | 573 | int ok = -1; |
538 | | |
539 | 573 | memset(&pk_blob, 0, sizeof(pk_blob)); |
540 | 573 | memset(&authdata, 0, sizeof(authdata)); |
541 | 573 | memset(&authdata_blob, 0, sizeof(authdata_blob)); |
542 | 573 | memset(out, 0, sizeof(*out)); |
543 | | |
544 | 573 | if (rp_id == NULL) { |
545 | 0 | fido_log_debug("%s: NULL rp_id", __func__); |
546 | 0 | goto fail; |
547 | 0 | } |
548 | | |
549 | 573 | if (cbor_blob_from_ec_point(pubkey, pubkey_len, &pk_blob) < 0) { |
550 | 27 | fido_log_debug("%s: cbor_blob_from_ec_point", __func__); |
551 | 27 | goto fail; |
552 | 27 | } |
553 | | |
554 | 546 | if (SHA256((const void *)rp_id, strlen(rp_id), |
555 | 546 | authdata.rp_id_hash) != authdata.rp_id_hash) { |
556 | 6 | fido_log_debug("%s: sha256", __func__); |
557 | 6 | goto fail; |
558 | 6 | } |
559 | | |
560 | 540 | authdata.flags = (CTAP_AUTHDATA_ATT_CRED | CTAP_AUTHDATA_USER_PRESENT); |
561 | 540 | authdata.sigcount = 0; |
562 | | |
563 | 540 | memset(&attcred_raw.aaguid, 0, sizeof(attcred_raw.aaguid)); |
564 | 540 | attcred_raw.id_len = htobe16(kh_len); |
565 | | |
566 | 540 | len = authdata_blob.len = sizeof(authdata) + sizeof(attcred_raw) + |
567 | 540 | kh_len + pk_blob.len; |
568 | 540 | ptr = authdata_blob.ptr = calloc(1, authdata_blob.len); |
569 | | |
570 | 540 | fido_log_debug("%s: ptr=%p, len=%zu", __func__, (void *)ptr, len); |
571 | | |
572 | 540 | if (authdata_blob.ptr == NULL) |
573 | 4 | goto fail; |
574 | | |
575 | 536 | if (fido_buf_write(&ptr, &len, &authdata, sizeof(authdata)) < 0 || |
576 | 536 | fido_buf_write(&ptr, &len, &attcred_raw, sizeof(attcred_raw)) < 0 || |
577 | 536 | fido_buf_write(&ptr, &len, kh, kh_len) < 0 || |
578 | 536 | fido_buf_write(&ptr, &len, pk_blob.ptr, pk_blob.len) < 0) { |
579 | 0 | fido_log_debug("%s: fido_buf_write", __func__); |
580 | 0 | goto fail; |
581 | 0 | } |
582 | | |
583 | 536 | if ((authdata_cbor = fido_blob_encode(&authdata_blob)) == NULL) { |
584 | 4 | fido_log_debug("%s: fido_blob_encode", __func__); |
585 | 4 | goto fail; |
586 | 4 | } |
587 | | |
588 | 532 | if ((out->len = cbor_serialize_alloc(authdata_cbor, &out->ptr, |
589 | 532 | &alloc_len)) == 0) { |
590 | 5 | fido_log_debug("%s: cbor_serialize_alloc", __func__); |
591 | 5 | goto fail; |
592 | 5 | } |
593 | | |
594 | 527 | ok = 0; |
595 | 573 | fail: |
596 | 573 | if (authdata_cbor) |
597 | 532 | cbor_decref(&authdata_cbor); |
598 | | |
599 | 573 | fido_blob_reset(&pk_blob); |
600 | 573 | fido_blob_reset(&authdata_blob); |
601 | | |
602 | 573 | return (ok); |
603 | 527 | } |
604 | | |
605 | | static int |
606 | | parse_register_reply(fido_cred_t *cred, const unsigned char *reply, size_t len) |
607 | 1.98k | { |
608 | 1.98k | fido_blob_t x5c; |
609 | 1.98k | fido_blob_t sig; |
610 | 1.98k | fido_blob_t ad; |
611 | 1.98k | fido_blob_t stmt; |
612 | 1.98k | uint8_t dummy; |
613 | 1.98k | uint8_t pubkey[65]; |
614 | 1.98k | uint8_t kh_len = 0; |
615 | 1.98k | uint8_t *kh = NULL; |
616 | 1.98k | int r; |
617 | | |
618 | 1.98k | memset(&x5c, 0, sizeof(x5c)); |
619 | 1.98k | memset(&sig, 0, sizeof(sig)); |
620 | 1.98k | memset(&ad, 0, sizeof(ad)); |
621 | 1.98k | memset(&stmt, 0, sizeof(stmt)); |
622 | 1.98k | r = FIDO_ERR_RX; |
623 | | |
624 | | /* status word */ |
625 | 1.98k | if (len < 2 || ((reply[len - 2] << 8) | reply[len - 1]) != SW_NO_ERROR) { |
626 | 250 | fido_log_debug("%s: unexpected sw", __func__); |
627 | 250 | goto fail; |
628 | 250 | } |
629 | | |
630 | 1.73k | len -= 2; |
631 | | |
632 | | /* reserved byte */ |
633 | 1.73k | if (fido_buf_read(&reply, &len, &dummy, sizeof(dummy)) < 0 || |
634 | 1.73k | dummy != 0x05) { |
635 | 7 | fido_log_debug("%s: reserved byte", __func__); |
636 | 7 | goto fail; |
637 | 7 | } |
638 | | |
639 | | /* pubkey + key handle */ |
640 | 1.72k | if (fido_buf_read(&reply, &len, &pubkey, sizeof(pubkey)) < 0 || |
641 | 1.72k | fido_buf_read(&reply, &len, &kh_len, sizeof(kh_len)) < 0 || |
642 | 1.72k | (kh = calloc(1, kh_len)) == NULL || |
643 | 1.72k | fido_buf_read(&reply, &len, kh, kh_len) < 0) { |
644 | 15 | fido_log_debug("%s: fido_buf_read", __func__); |
645 | 15 | goto fail; |
646 | 15 | } |
647 | | |
648 | | /* x5c + sig */ |
649 | 1.70k | if (x5c_get(&x5c, &reply, &len) < 0 || |
650 | 1.70k | sig_get(&sig, &reply, &len) < 0) { |
651 | 1.09k | fido_log_debug("%s: x5c || sig", __func__); |
652 | 1.09k | goto fail; |
653 | 1.09k | } |
654 | | |
655 | | /* attstmt */ |
656 | 619 | if (encode_cred_attstmt(COSE_ES256, &x5c, &sig, &stmt) < 0) { |
657 | 46 | fido_log_debug("%s: encode_cred_attstmt", __func__); |
658 | 46 | goto fail; |
659 | 46 | } |
660 | | |
661 | | /* authdata */ |
662 | 573 | if (encode_cred_authdata(cred->rp.id, kh, kh_len, pubkey, |
663 | 573 | sizeof(pubkey), &ad) < 0) { |
664 | 46 | fido_log_debug("%s: encode_cred_authdata", __func__); |
665 | 46 | goto fail; |
666 | 46 | } |
667 | | |
668 | 527 | if (fido_cred_set_fmt(cred, "fido-u2f") != FIDO_OK || |
669 | 527 | fido_cred_set_authdata(cred, ad.ptr, ad.len) != FIDO_OK || |
670 | 527 | fido_cred_set_attstmt(cred, stmt.ptr, stmt.len) != FIDO_OK) { |
671 | 31 | fido_log_debug("%s: fido_cred_set", __func__); |
672 | 31 | r = FIDO_ERR_INTERNAL; |
673 | 31 | goto fail; |
674 | 31 | } |
675 | | |
676 | 496 | r = FIDO_OK; |
677 | 1.98k | fail: |
678 | 1.98k | freezero(kh, kh_len); |
679 | 1.98k | fido_blob_reset(&x5c); |
680 | 1.98k | fido_blob_reset(&sig); |
681 | 1.98k | fido_blob_reset(&ad); |
682 | 1.98k | fido_blob_reset(&stmt); |
683 | | |
684 | 1.98k | return (r); |
685 | 496 | } |
686 | | |
687 | | int |
688 | | u2f_register(fido_dev_t *dev, fido_cred_t *cred, int *ms) |
689 | 4.17k | { |
690 | 4.17k | iso7816_apdu_t *apdu = NULL; |
691 | 4.17k | unsigned char rp_id_hash[SHA256_DIGEST_LENGTH]; |
692 | 4.17k | unsigned char *reply = NULL; |
693 | 4.17k | int reply_len; |
694 | 4.17k | int found; |
695 | 4.17k | int r; |
696 | | |
697 | 4.17k | if (cred->rk == FIDO_OPT_TRUE || cred->uv == FIDO_OPT_TRUE) { |
698 | 119 | fido_log_debug("%s: rk=%d, uv=%d", __func__, cred->rk, |
699 | 119 | cred->uv); |
700 | 119 | return (FIDO_ERR_UNSUPPORTED_OPTION); |
701 | 119 | } |
702 | | |
703 | 4.05k | if (cred->type != COSE_ES256 || cred->cdh.ptr == NULL || |
704 | 4.05k | cred->rp.id == NULL || cred->cdh.len != SHA256_DIGEST_LENGTH) { |
705 | 250 | fido_log_debug("%s: type=%d, cdh=(%p,%zu)" , __func__, |
706 | 250 | cred->type, (void *)cred->cdh.ptr, cred->cdh.len); |
707 | 250 | return (FIDO_ERR_INVALID_ARGUMENT); |
708 | 250 | } |
709 | | |
710 | 3.82k | for (size_t i = 0; i < cred->excl.len; i++) { |
711 | 1.27k | if ((r = key_lookup(dev, cred->rp.id, &cred->excl.ptr[i], |
712 | 1.27k | &found, ms)) != FIDO_OK) { |
713 | 864 | fido_log_debug("%s: key_lookup", __func__); |
714 | 864 | return (r); |
715 | 864 | } |
716 | 408 | if (found) { |
717 | 385 | if ((r = send_dummy_register(dev, ms)) != FIDO_OK) { |
718 | 335 | fido_log_debug("%s: send_dummy_register", |
719 | 335 | __func__); |
720 | 335 | return (r); |
721 | 335 | } |
722 | 50 | return (FIDO_ERR_CREDENTIAL_EXCLUDED); |
723 | 385 | } |
724 | 408 | } |
725 | | |
726 | 2.55k | memset(&rp_id_hash, 0, sizeof(rp_id_hash)); |
727 | | |
728 | 2.55k | if (SHA256((const void *)cred->rp.id, strlen(cred->rp.id), |
729 | 2.55k | rp_id_hash) != rp_id_hash) { |
730 | 1 | fido_log_debug("%s: sha256", __func__); |
731 | 1 | return (FIDO_ERR_INTERNAL); |
732 | 1 | } |
733 | | |
734 | 2.55k | if ((apdu = iso7816_new(0, U2F_CMD_REGISTER, 0, 2 * |
735 | 2.55k | SHA256_DIGEST_LENGTH)) == NULL || |
736 | 2.55k | iso7816_add(apdu, cred->cdh.ptr, cred->cdh.len) < 0 || |
737 | 2.55k | iso7816_add(apdu, rp_id_hash, sizeof(rp_id_hash)) < 0) { |
738 | 3 | fido_log_debug("%s: iso7816", __func__); |
739 | 3 | r = FIDO_ERR_INTERNAL; |
740 | 3 | goto fail; |
741 | 3 | } |
742 | | |
743 | 2.55k | if ((reply = malloc(FIDO_MAXMSG)) == NULL) { |
744 | 4 | fido_log_debug("%s: malloc", __func__); |
745 | 4 | r = FIDO_ERR_INTERNAL; |
746 | 4 | goto fail; |
747 | 4 | } |
748 | | |
749 | 3.80k | do { |
750 | 3.80k | if ((r = check_io_timeout(*ms)) != FIDO_OK) { |
751 | 420 | fido_log_debug("%s: check_io_timeout", __func__); |
752 | 420 | goto fail; |
753 | 420 | } |
754 | 3.38k | if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu), |
755 | 3.38k | iso7816_len(apdu), ms) < 0) { |
756 | 16 | fido_log_debug("%s: fido_tx", __func__); |
757 | 16 | r = FIDO_ERR_TX; |
758 | 16 | goto fail; |
759 | 16 | } |
760 | 3.37k | if ((reply_len = fido_rx(dev, CTAP_CMD_MSG, reply, |
761 | 3.37k | FIDO_MAXMSG, ms)) < 2) { |
762 | 125 | fido_log_debug("%s: fido_rx", __func__); |
763 | 125 | r = FIDO_ERR_RX; |
764 | 125 | goto fail; |
765 | 125 | } |
766 | 3.24k | if (delay_ms(U2F_PACE_MS, ms) != 0) { |
767 | 4 | fido_log_debug("%s: delay_ms", __func__); |
768 | 4 | r = FIDO_ERR_RX; |
769 | 4 | goto fail; |
770 | 4 | } |
771 | 3.24k | } while (((reply[0] << 8) | reply[1]) == SW_CONDITIONS_NOT_SATISFIED); |
772 | | |
773 | 1.98k | if ((r = parse_register_reply(cred, reply, |
774 | 1.98k | (size_t)reply_len)) != FIDO_OK) { |
775 | 1.48k | fido_log_debug("%s: parse_register_reply", __func__); |
776 | 1.48k | goto fail; |
777 | 1.48k | } |
778 | 2.55k | fail: |
779 | 2.55k | iso7816_free(&apdu); |
780 | 2.55k | freezero(reply, FIDO_MAXMSG); |
781 | | |
782 | 2.55k | return (r); |
783 | 1.98k | } |
784 | | |
785 | | static int |
786 | | u2f_authenticate_single(fido_dev_t *dev, const fido_blob_t *key_id, |
787 | | fido_assert_t *fa, size_t idx, int *ms) |
788 | 5.55k | { |
789 | 5.55k | fido_blob_t sig; |
790 | 5.55k | fido_blob_t ad; |
791 | 5.55k | int found; |
792 | 5.55k | int r; |
793 | | |
794 | 5.55k | memset(&sig, 0, sizeof(sig)); |
795 | 5.55k | memset(&ad, 0, sizeof(ad)); |
796 | | |
797 | 5.55k | if ((r = key_lookup(dev, fa->rp_id, key_id, &found, ms)) != FIDO_OK) { |
798 | 3.18k | fido_log_debug("%s: key_lookup", __func__); |
799 | 3.18k | goto fail; |
800 | 3.18k | } |
801 | | |
802 | 2.37k | if (!found) { |
803 | 62 | fido_log_debug("%s: not found", __func__); |
804 | 62 | r = FIDO_ERR_CREDENTIAL_EXCLUDED; |
805 | 62 | goto fail; |
806 | 62 | } |
807 | | |
808 | 2.30k | if (fido_blob_set(&fa->stmt[idx].id, key_id->ptr, key_id->len) < 0) { |
809 | 7 | fido_log_debug("%s: fido_blob_set", __func__); |
810 | 7 | r = FIDO_ERR_INTERNAL; |
811 | 7 | goto fail; |
812 | 7 | } |
813 | | |
814 | 2.30k | if (fa->up == FIDO_OPT_FALSE) { |
815 | 1.31k | fido_log_debug("%s: checking for key existence only", __func__); |
816 | 1.31k | r = FIDO_ERR_USER_PRESENCE_REQUIRED; |
817 | 1.31k | goto fail; |
818 | 1.31k | } |
819 | | |
820 | 990 | if ((r = do_auth(dev, &fa->cdh, fa->rp_id, key_id, &sig, &ad, |
821 | 990 | ms)) != FIDO_OK) { |
822 | 990 | fido_log_debug("%s: do_auth", __func__); |
823 | 990 | goto fail; |
824 | 990 | } |
825 | | |
826 | 0 | if (fido_assert_set_authdata(fa, idx, ad.ptr, ad.len) != FIDO_OK || |
827 | 0 | fido_assert_set_sig(fa, idx, sig.ptr, sig.len) != FIDO_OK) { |
828 | 0 | fido_log_debug("%s: fido_assert_set", __func__); |
829 | 0 | r = FIDO_ERR_INTERNAL; |
830 | 0 | goto fail; |
831 | 0 | } |
832 | | |
833 | 0 | r = FIDO_OK; |
834 | 5.55k | fail: |
835 | 5.55k | fido_blob_reset(&sig); |
836 | 5.55k | fido_blob_reset(&ad); |
837 | | |
838 | 5.55k | return (r); |
839 | 0 | } |
840 | | |
841 | | int |
842 | | u2f_authenticate(fido_dev_t *dev, fido_assert_t *fa, int *ms) |
843 | 4.87k | { |
844 | 4.87k | size_t nfound = 0; |
845 | 4.87k | size_t nauth_ok = 0; |
846 | 4.87k | int r; |
847 | | |
848 | 4.87k | if (fa->uv == FIDO_OPT_TRUE || fa->allow_list.ptr == NULL) { |
849 | 667 | fido_log_debug("%s: uv=%d, allow_list=%p", __func__, fa->uv, |
850 | 667 | (void *)fa->allow_list.ptr); |
851 | 667 | return (FIDO_ERR_UNSUPPORTED_OPTION); |
852 | 667 | } |
853 | | |
854 | 4.20k | if ((r = fido_assert_set_count(fa, fa->allow_list.len)) != FIDO_OK) { |
855 | 6 | fido_log_debug("%s: fido_assert_set_count", __func__); |
856 | 6 | return (r); |
857 | 6 | } |
858 | | |
859 | 5.57k | for (size_t i = 0; i < fa->allow_list.len; i++) { |
860 | 5.55k | switch ((r = u2f_authenticate_single(dev, |
861 | 5.55k | &fa->allow_list.ptr[i], fa, nfound, ms))) { |
862 | 0 | case FIDO_OK: |
863 | 0 | nauth_ok++; |
864 | 0 | FALLTHROUGH |
865 | 1.31k | case FIDO_ERR_USER_PRESENCE_REQUIRED: |
866 | 1.31k | nfound++; |
867 | 1.31k | break; |
868 | 4.24k | default: |
869 | 4.24k | if (r != FIDO_ERR_CREDENTIAL_EXCLUDED) { |
870 | 4.18k | fido_log_debug("%s: u2f_authenticate_single", |
871 | 4.18k | __func__); |
872 | 4.18k | return (r); |
873 | 4.18k | } |
874 | | /* ignore credentials that don't exist */ |
875 | 5.55k | } |
876 | 5.55k | } |
877 | | |
878 | 19 | fa->stmt_len = nfound; |
879 | | |
880 | 19 | if (nfound == 0) |
881 | 9 | return (FIDO_ERR_NO_CREDENTIALS); |
882 | 10 | if (nauth_ok == 0) |
883 | 10 | return (FIDO_ERR_USER_PRESENCE_REQUIRED); |
884 | | |
885 | 0 | return (FIDO_OK); |
886 | 10 | } |
887 | | |
888 | | int |
889 | | u2f_get_touch_begin(fido_dev_t *dev, int *ms) |
890 | 21.1k | { |
891 | 21.1k | iso7816_apdu_t *apdu = NULL; |
892 | 21.1k | const char *clientdata = FIDO_DUMMY_CLIENTDATA; |
893 | 21.1k | const char *rp_id = FIDO_DUMMY_RP_ID; |
894 | 21.1k | unsigned char *reply = NULL; |
895 | 21.1k | unsigned char clientdata_hash[SHA256_DIGEST_LENGTH]; |
896 | 21.1k | unsigned char rp_id_hash[SHA256_DIGEST_LENGTH]; |
897 | 21.1k | int r; |
898 | | |
899 | 21.1k | memset(&clientdata_hash, 0, sizeof(clientdata_hash)); |
900 | 21.1k | memset(&rp_id_hash, 0, sizeof(rp_id_hash)); |
901 | | |
902 | 21.1k | if (SHA256((const void *)clientdata, strlen(clientdata), |
903 | 21.1k | clientdata_hash) != clientdata_hash || SHA256((const void *)rp_id, |
904 | 21.0k | strlen(rp_id), rp_id_hash) != rp_id_hash) { |
905 | 121 | fido_log_debug("%s: sha256", __func__); |
906 | 121 | return (FIDO_ERR_INTERNAL); |
907 | 121 | } |
908 | | |
909 | 20.9k | if ((apdu = iso7816_new(0, U2F_CMD_REGISTER, 0, 2 * |
910 | 20.9k | SHA256_DIGEST_LENGTH)) == NULL || |
911 | 20.9k | iso7816_add(apdu, clientdata_hash, sizeof(clientdata_hash)) < 0 || |
912 | 20.9k | iso7816_add(apdu, rp_id_hash, sizeof(rp_id_hash)) < 0) { |
913 | 87 | fido_log_debug("%s: iso7816", __func__); |
914 | 87 | r = FIDO_ERR_INTERNAL; |
915 | 87 | goto fail; |
916 | 87 | } |
917 | | |
918 | 20.8k | if ((reply = malloc(FIDO_MAXMSG)) == NULL) { |
919 | 81 | fido_log_debug("%s: malloc", __func__); |
920 | 81 | r = FIDO_ERR_INTERNAL; |
921 | 81 | goto fail; |
922 | 81 | } |
923 | | |
924 | 20.8k | if (dev->attr.flags & FIDO_CAP_WINK) { |
925 | 13.2k | fido_tx(dev, CTAP_CMD_WINK, NULL, 0, ms); |
926 | 13.2k | fido_rx(dev, CTAP_CMD_WINK, reply, FIDO_MAXMSG, ms); |
927 | 13.2k | } |
928 | | |
929 | 20.8k | if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu), |
930 | 20.8k | iso7816_len(apdu), ms) < 0) { |
931 | 1.37k | fido_log_debug("%s: fido_tx", __func__); |
932 | 1.37k | r = FIDO_ERR_TX; |
933 | 1.37k | goto fail; |
934 | 1.37k | } |
935 | | |
936 | 19.4k | r = FIDO_OK; |
937 | 20.9k | fail: |
938 | 20.9k | iso7816_free(&apdu); |
939 | 20.9k | freezero(reply, FIDO_MAXMSG); |
940 | | |
941 | 20.9k | return (r); |
942 | 19.4k | } |
943 | | |
944 | | int |
945 | | u2f_get_touch_status(fido_dev_t *dev, int *touched, int *ms) |
946 | 20.9k | { |
947 | 20.9k | unsigned char *reply; |
948 | 20.9k | int reply_len; |
949 | 20.9k | int r; |
950 | | |
951 | 20.9k | if ((reply = malloc(FIDO_MAXMSG)) == NULL) { |
952 | 64 | fido_log_debug("%s: malloc", __func__); |
953 | 64 | r = FIDO_ERR_INTERNAL; |
954 | 64 | goto out; |
955 | 64 | } |
956 | | |
957 | 20.8k | if ((reply_len = fido_rx(dev, CTAP_CMD_MSG, reply, FIDO_MAXMSG, |
958 | 20.8k | ms)) < 2) { |
959 | 18.8k | fido_log_debug("%s: fido_rx", __func__); |
960 | 18.8k | r = FIDO_OK; /* ignore */ |
961 | 18.8k | goto out; |
962 | 18.8k | } |
963 | | |
964 | 2.00k | switch ((reply[reply_len - 2] << 8) | reply[reply_len - 1]) { |
965 | 156 | case SW_CONDITIONS_NOT_SATISFIED: |
966 | 156 | if ((r = u2f_get_touch_begin(dev, ms)) != FIDO_OK) { |
967 | 50 | fido_log_debug("%s: u2f_get_touch_begin", __func__); |
968 | 50 | goto out; |
969 | 50 | } |
970 | 106 | *touched = 0; |
971 | 106 | break; |
972 | 65 | case SW_NO_ERROR: |
973 | 65 | *touched = 1; |
974 | 65 | break; |
975 | 1.78k | default: |
976 | 1.78k | fido_log_debug("%s: unexpected sw", __func__); |
977 | 1.78k | r = FIDO_ERR_RX; |
978 | 1.78k | goto out; |
979 | 2.00k | } |
980 | | |
981 | 171 | r = FIDO_OK; |
982 | 20.9k | out: |
983 | 20.9k | freezero(reply, FIDO_MAXMSG); |
984 | | |
985 | 20.9k | return (r); |
986 | 171 | } |