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