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