2 * Copyright (c) 2018 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.
7 #include <openssl/sha.h>
8 #include <openssl/x509.h>
15 #include "fido/es256.h"
19 usleep(unsigned int usec)
28 sig_get(fido_blob_t *sig, const unsigned char **buf, size_t *len)
30 sig->len = *len; /* consume the whole buffer */
31 if ((sig->ptr = calloc(1, sig->len)) == NULL ||
32 fido_buf_read(buf, len, sig->ptr, sig->len) < 0) {
33 fido_log_debug("%s: fido_buf_read", __func__);
42 x5c_get(fido_blob_t *x5c, const unsigned char **buf, size_t *len)
47 if (*len > LONG_MAX) {
48 fido_log_debug("%s: invalid len %zu", __func__, *len);
52 /* find out the certificate's length */
53 const unsigned char *end = *buf;
54 if ((cert = d2i_X509(NULL, &end, (long)*len)) == NULL || end <= *buf ||
55 (x5c->len = (size_t)(end - *buf)) >= *len) {
56 fido_log_debug("%s: d2i_X509", __func__);
60 /* read accordingly */
61 if ((x5c->ptr = calloc(1, x5c->len)) == NULL ||
62 fido_buf_read(buf, len, x5c->ptr, x5c->len) < 0) {
63 fido_log_debug("%s: fido_buf_read", __func__);
79 authdata_fake(const char *rp_id, uint8_t flags, uint32_t sigcount,
80 fido_blob_t *fake_cbor_ad)
83 cbor_item_t *item = NULL;
86 memset(&ad, 0, sizeof(ad));
88 if (SHA256((const void *)rp_id, strlen(rp_id),
89 ad.rp_id_hash) != ad.rp_id_hash) {
90 fido_log_debug("%s: sha256", __func__);
94 ad.flags = flags; /* XXX translate? */
95 ad.sigcount = sigcount;
97 if ((item = cbor_build_bytestring((const unsigned char *)&ad,
98 sizeof(ad))) == NULL) {
99 fido_log_debug("%s: cbor_build_bytestring", __func__);
103 if (fake_cbor_ad->ptr != NULL ||
104 (fake_cbor_ad->len = cbor_serialize_alloc(item, &fake_cbor_ad->ptr,
106 fido_log_debug("%s: cbor_serialize_alloc", __func__);
116 /* TODO: use u2f_get_touch_begin & u2f_get_touch_status instead */
118 send_dummy_register(fido_dev_t *dev, int ms)
120 iso7816_apdu_t *apdu = NULL;
121 unsigned char challenge[SHA256_DIGEST_LENGTH];
122 unsigned char application[SHA256_DIGEST_LENGTH];
123 unsigned char reply[FIDO_MAXMSG];
130 /* dummy challenge & application */
131 memset(&challenge, 0xff, sizeof(challenge));
132 memset(&application, 0xff, sizeof(application));
134 if ((apdu = iso7816_new(0, U2F_CMD_REGISTER, 0, 2 *
135 SHA256_DIGEST_LENGTH)) == NULL ||
136 iso7816_add(apdu, &challenge, sizeof(challenge)) < 0 ||
137 iso7816_add(apdu, &application, sizeof(application)) < 0) {
138 fido_log_debug("%s: iso7816", __func__);
139 r = FIDO_ERR_INTERNAL;
144 if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu),
145 iso7816_len(apdu)) < 0) {
146 fido_log_debug("%s: fido_tx", __func__);
150 if (fido_rx(dev, CTAP_CMD_MSG, &reply, sizeof(reply), ms) < 2) {
151 fido_log_debug("%s: fido_rx", __func__);
155 if (usleep((unsigned)(ms == -1 ? 100 : ms) * 1000) < 0) {
156 fido_log_debug("%s: usleep", __func__);
160 } while (((reply[0] << 8) | reply[1]) == SW_CONDITIONS_NOT_SATISFIED);
170 key_lookup(fido_dev_t *dev, const char *rp_id, const fido_blob_t *key_id,
173 iso7816_apdu_t *apdu = NULL;
174 unsigned char challenge[SHA256_DIGEST_LENGTH];
175 unsigned char rp_id_hash[SHA256_DIGEST_LENGTH];
176 unsigned char reply[FIDO_MAXMSG];
180 if (key_id->len > UINT8_MAX || rp_id == NULL) {
181 fido_log_debug("%s: key_id->len=%zu, rp_id=%p", __func__,
182 key_id->len, (const void *)rp_id);
183 r = FIDO_ERR_INVALID_ARGUMENT;
187 memset(&challenge, 0xff, sizeof(challenge));
188 memset(&rp_id_hash, 0, sizeof(rp_id_hash));
190 if (SHA256((const void *)rp_id, strlen(rp_id),
191 rp_id_hash) != rp_id_hash) {
192 fido_log_debug("%s: sha256", __func__);
193 r = FIDO_ERR_INTERNAL;
197 key_id_len = (uint8_t)key_id->len;
199 if ((apdu = iso7816_new(0, U2F_CMD_AUTH, U2F_AUTH_CHECK, (uint16_t)(2 *
200 SHA256_DIGEST_LENGTH + sizeof(key_id_len) + key_id_len))) == NULL ||
201 iso7816_add(apdu, &challenge, sizeof(challenge)) < 0 ||
202 iso7816_add(apdu, &rp_id_hash, sizeof(rp_id_hash)) < 0 ||
203 iso7816_add(apdu, &key_id_len, sizeof(key_id_len)) < 0 ||
204 iso7816_add(apdu, key_id->ptr, key_id_len) < 0) {
205 fido_log_debug("%s: iso7816", __func__);
206 r = FIDO_ERR_INTERNAL;
210 if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu),
211 iso7816_len(apdu)) < 0) {
212 fido_log_debug("%s: fido_tx", __func__);
216 if (fido_rx(dev, CTAP_CMD_MSG, &reply, sizeof(reply), ms) != 2) {
217 fido_log_debug("%s: fido_rx", __func__);
222 switch ((reply[0] << 8) | reply[1]) {
223 case SW_CONDITIONS_NOT_SATISFIED:
224 *found = 1; /* key exists */
227 *found = 0; /* key does not exist */
231 r = FIDO_ERR_INTERNAL;
243 parse_auth_reply(fido_blob_t *sig, fido_blob_t *ad, const char *rp_id,
244 const unsigned char *reply, size_t len)
249 if (len < 2 || ((reply[len - 2] << 8) | reply[len - 1]) != SW_NO_ERROR) {
250 fido_log_debug("%s: unexpected sw", __func__);
251 return (FIDO_ERR_RX);
256 if (fido_buf_read(&reply, &len, &flags, sizeof(flags)) < 0 ||
257 fido_buf_read(&reply, &len, &sigcount, sizeof(sigcount)) < 0) {
258 fido_log_debug("%s: fido_buf_read", __func__);
259 return (FIDO_ERR_RX);
262 if (sig_get(sig, &reply, &len) < 0) {
263 fido_log_debug("%s: sig_get", __func__);
264 return (FIDO_ERR_RX);
267 if (authdata_fake(rp_id, flags, sigcount, ad) < 0) {
268 fido_log_debug("%s; authdata_fake", __func__);
269 return (FIDO_ERR_RX);
276 do_auth(fido_dev_t *dev, const fido_blob_t *cdh, const char *rp_id,
277 const fido_blob_t *key_id, fido_blob_t *sig, fido_blob_t *ad, int ms)
279 iso7816_apdu_t *apdu = NULL;
280 unsigned char rp_id_hash[SHA256_DIGEST_LENGTH];
281 unsigned char reply[FIDO_MAXMSG];
290 if (cdh->len != SHA256_DIGEST_LENGTH || key_id->len > UINT8_MAX ||
292 r = FIDO_ERR_INVALID_ARGUMENT;
296 memset(&rp_id_hash, 0, sizeof(rp_id_hash));
298 if (SHA256((const void *)rp_id, strlen(rp_id),
299 rp_id_hash) != rp_id_hash) {
300 fido_log_debug("%s: sha256", __func__);
301 r = FIDO_ERR_INTERNAL;
305 key_id_len = (uint8_t)key_id->len;
307 if ((apdu = iso7816_new(0, U2F_CMD_AUTH, U2F_AUTH_SIGN, (uint16_t)(2 *
308 SHA256_DIGEST_LENGTH + sizeof(key_id_len) + key_id_len))) == NULL ||
309 iso7816_add(apdu, cdh->ptr, cdh->len) < 0 ||
310 iso7816_add(apdu, &rp_id_hash, sizeof(rp_id_hash)) < 0 ||
311 iso7816_add(apdu, &key_id_len, sizeof(key_id_len)) < 0 ||
312 iso7816_add(apdu, key_id->ptr, key_id_len) < 0) {
313 fido_log_debug("%s: iso7816", __func__);
314 r = FIDO_ERR_INTERNAL;
319 if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu),
320 iso7816_len(apdu)) < 0) {
321 fido_log_debug("%s: fido_tx", __func__);
325 if ((reply_len = fido_rx(dev, CTAP_CMD_MSG, &reply,
326 sizeof(reply), ms)) < 2) {
327 fido_log_debug("%s: fido_rx", __func__);
331 if (usleep((unsigned)(ms == -1 ? 100 : ms) * 1000) < 0) {
332 fido_log_debug("%s: usleep", __func__);
336 } while (((reply[0] << 8) | reply[1]) == SW_CONDITIONS_NOT_SATISFIED);
338 if ((r = parse_auth_reply(sig, ad, rp_id, reply,
339 (size_t)reply_len)) != FIDO_OK) {
340 fido_log_debug("%s: parse_auth_reply", __func__);
351 cbor_blob_from_ec_point(const uint8_t *ec_point, size_t ec_point_len,
352 fido_blob_t *cbor_blob)
354 es256_pk_t *pk = NULL;
355 cbor_item_t *pk_cbor = NULL;
359 /* only handle uncompressed points */
360 if (ec_point_len != 65 || ec_point[0] != 0x04) {
361 fido_log_debug("%s: unexpected format", __func__);
365 if ((pk = es256_pk_new()) == NULL ||
366 es256_pk_set_x(pk, &ec_point[1]) < 0 ||
367 es256_pk_set_y(pk, &ec_point[33]) < 0) {
368 fido_log_debug("%s: es256_pk_set", __func__);
372 if ((pk_cbor = es256_pk_encode(pk, 0)) == NULL) {
373 fido_log_debug("%s: es256_pk_encode", __func__);
377 if ((cbor_blob->len = cbor_serialize_alloc(pk_cbor, &cbor_blob->ptr,
378 &alloc_len)) != 77) {
379 fido_log_debug("%s: cbor_serialize_alloc", __func__);
388 cbor_decref(&pk_cbor);
394 encode_cred_authdata(const char *rp_id, const uint8_t *kh, uint8_t kh_len,
395 const uint8_t *pubkey, size_t pubkey_len, fido_blob_t *out)
397 fido_authdata_t authdata;
398 fido_attcred_raw_t attcred_raw;
400 fido_blob_t authdata_blob;
401 cbor_item_t *authdata_cbor = NULL;
407 memset(&pk_blob, 0, sizeof(pk_blob));
408 memset(&authdata, 0, sizeof(authdata));
409 memset(&authdata_blob, 0, sizeof(authdata_blob));
410 memset(out, 0, sizeof(*out));
413 fido_log_debug("%s: NULL rp_id", __func__);
417 if (cbor_blob_from_ec_point(pubkey, pubkey_len, &pk_blob) < 0) {
418 fido_log_debug("%s: cbor_blob_from_ec_point", __func__);
422 if (SHA256((const void *)rp_id, strlen(rp_id),
423 authdata.rp_id_hash) != authdata.rp_id_hash) {
424 fido_log_debug("%s: sha256", __func__);
428 authdata.flags = (CTAP_AUTHDATA_ATT_CRED | CTAP_AUTHDATA_USER_PRESENT);
429 authdata.sigcount = 0;
431 memset(&attcred_raw.aaguid, 0, sizeof(attcred_raw.aaguid));
432 attcred_raw.id_len = htobe16(kh_len);
434 len = authdata_blob.len = sizeof(authdata) + sizeof(attcred_raw) +
435 kh_len + pk_blob.len;
436 ptr = authdata_blob.ptr = calloc(1, authdata_blob.len);
438 fido_log_debug("%s: ptr=%p, len=%zu", __func__, (void *)ptr, len);
440 if (authdata_blob.ptr == NULL)
443 if (fido_buf_write(&ptr, &len, &authdata, sizeof(authdata)) < 0 ||
444 fido_buf_write(&ptr, &len, &attcred_raw, sizeof(attcred_raw)) < 0 ||
445 fido_buf_write(&ptr, &len, kh, kh_len) < 0 ||
446 fido_buf_write(&ptr, &len, pk_blob.ptr, pk_blob.len) < 0) {
447 fido_log_debug("%s: fido_buf_write", __func__);
451 if ((authdata_cbor = fido_blob_encode(&authdata_blob)) == NULL) {
452 fido_log_debug("%s: fido_blob_encode", __func__);
456 if ((out->len = cbor_serialize_alloc(authdata_cbor, &out->ptr,
458 fido_log_debug("%s: cbor_serialize_alloc", __func__);
465 cbor_decref(&authdata_cbor);
467 fido_blob_reset(&pk_blob);
468 fido_blob_reset(&authdata_blob);
474 parse_register_reply(fido_cred_t *cred, const unsigned char *reply, size_t len)
485 memset(&x5c, 0, sizeof(x5c));
486 memset(&sig, 0, sizeof(sig));
487 memset(&ad, 0, sizeof(ad));
491 if (len < 2 || ((reply[len - 2] << 8) | reply[len - 1]) != SW_NO_ERROR) {
492 fido_log_debug("%s: unexpected sw", __func__);
499 if (fido_buf_read(&reply, &len, &dummy, sizeof(dummy)) < 0 ||
501 fido_log_debug("%s: reserved byte", __func__);
505 /* pubkey + key handle */
506 if (fido_buf_read(&reply, &len, &pubkey, sizeof(pubkey)) < 0 ||
507 fido_buf_read(&reply, &len, &kh_len, sizeof(kh_len)) < 0 ||
508 (kh = calloc(1, kh_len)) == NULL ||
509 fido_buf_read(&reply, &len, kh, kh_len) < 0) {
510 fido_log_debug("%s: fido_buf_read", __func__);
515 if (x5c_get(&x5c, &reply, &len) < 0 ||
516 sig_get(&sig, &reply, &len) < 0) {
517 fido_log_debug("%s: x5c || sig", __func__);
522 if (encode_cred_authdata(cred->rp.id, kh, kh_len, pubkey,
523 sizeof(pubkey), &ad) < 0) {
524 fido_log_debug("%s: encode_cred_authdata", __func__);
528 if (fido_cred_set_fmt(cred, "fido-u2f") != FIDO_OK ||
529 fido_cred_set_authdata(cred, ad.ptr, ad.len) != FIDO_OK ||
530 fido_cred_set_x509(cred, x5c.ptr, x5c.len) != FIDO_OK ||
531 fido_cred_set_sig(cred, sig.ptr, sig.len) != FIDO_OK) {
532 fido_log_debug("%s: fido_cred_set", __func__);
533 r = FIDO_ERR_INTERNAL;
539 freezero(kh, kh_len);
540 fido_blob_reset(&x5c);
541 fido_blob_reset(&sig);
542 fido_blob_reset(&ad);
548 u2f_register(fido_dev_t *dev, fido_cred_t *cred, int ms)
550 iso7816_apdu_t *apdu = NULL;
551 unsigned char rp_id_hash[SHA256_DIGEST_LENGTH];
552 unsigned char reply[FIDO_MAXMSG];
561 if (cred->rk == FIDO_OPT_TRUE || cred->uv == FIDO_OPT_TRUE) {
562 fido_log_debug("%s: rk=%d, uv=%d", __func__, cred->rk,
564 return (FIDO_ERR_UNSUPPORTED_OPTION);
567 if (cred->type != COSE_ES256 || cred->cdh.ptr == NULL ||
568 cred->rp.id == NULL || cred->cdh.len != SHA256_DIGEST_LENGTH) {
569 fido_log_debug("%s: type=%d, cdh=(%p,%zu)" , __func__,
570 cred->type, (void *)cred->cdh.ptr, cred->cdh.len);
571 return (FIDO_ERR_INVALID_ARGUMENT);
574 for (size_t i = 0; i < cred->excl.len; i++) {
575 if ((r = key_lookup(dev, cred->rp.id, &cred->excl.ptr[i],
576 &found, ms)) != FIDO_OK) {
577 fido_log_debug("%s: key_lookup", __func__);
581 if ((r = send_dummy_register(dev, ms)) != FIDO_OK) {
582 fido_log_debug("%s: send_dummy_register",
586 return (FIDO_ERR_CREDENTIAL_EXCLUDED);
590 memset(&rp_id_hash, 0, sizeof(rp_id_hash));
592 if (SHA256((const void *)cred->rp.id, strlen(cred->rp.id),
593 rp_id_hash) != rp_id_hash) {
594 fido_log_debug("%s: sha256", __func__);
595 return (FIDO_ERR_INTERNAL);
598 if ((apdu = iso7816_new(0, U2F_CMD_REGISTER, 0, 2 *
599 SHA256_DIGEST_LENGTH)) == NULL ||
600 iso7816_add(apdu, cred->cdh.ptr, cred->cdh.len) < 0 ||
601 iso7816_add(apdu, rp_id_hash, sizeof(rp_id_hash)) < 0) {
602 fido_log_debug("%s: iso7816", __func__);
603 r = FIDO_ERR_INTERNAL;
608 if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu),
609 iso7816_len(apdu)) < 0) {
610 fido_log_debug("%s: fido_tx", __func__);
614 if ((reply_len = fido_rx(dev, CTAP_CMD_MSG, &reply,
615 sizeof(reply), ms)) < 2) {
616 fido_log_debug("%s: fido_rx", __func__);
620 if (usleep((unsigned)(ms == -1 ? 100 : ms) * 1000) < 0) {
621 fido_log_debug("%s: usleep", __func__);
625 } while (((reply[0] << 8) | reply[1]) == SW_CONDITIONS_NOT_SATISFIED);
627 if ((r = parse_register_reply(cred, reply,
628 (size_t)reply_len)) != FIDO_OK) {
629 fido_log_debug("%s: parse_register_reply", __func__);
639 u2f_authenticate_single(fido_dev_t *dev, const fido_blob_t *key_id,
640 fido_assert_t *fa, size_t idx, int ms)
647 memset(&sig, 0, sizeof(sig));
648 memset(&ad, 0, sizeof(ad));
650 if ((r = key_lookup(dev, fa->rp_id, key_id, &found, ms)) != FIDO_OK) {
651 fido_log_debug("%s: key_lookup", __func__);
656 fido_log_debug("%s: not found", __func__);
657 r = FIDO_ERR_CREDENTIAL_EXCLUDED;
661 if (fido_blob_set(&fa->stmt[idx].id, key_id->ptr, key_id->len) < 0) {
662 fido_log_debug("%s: fido_blob_set", __func__);
663 r = FIDO_ERR_INTERNAL;
667 if (fa->up == FIDO_OPT_FALSE) {
668 fido_log_debug("%s: checking for key existence only", __func__);
669 r = FIDO_ERR_USER_PRESENCE_REQUIRED;
673 if ((r = do_auth(dev, &fa->cdh, fa->rp_id, key_id, &sig, &ad,
675 fido_log_debug("%s: do_auth", __func__);
679 if (fido_assert_set_authdata(fa, idx, ad.ptr, ad.len) != FIDO_OK ||
680 fido_assert_set_sig(fa, idx, sig.ptr, sig.len) != FIDO_OK) {
681 fido_log_debug("%s: fido_assert_set", __func__);
682 r = FIDO_ERR_INTERNAL;
688 fido_blob_reset(&sig);
689 fido_blob_reset(&ad);
695 u2f_authenticate(fido_dev_t *dev, fido_assert_t *fa, int ms)
701 if (fa->uv == FIDO_OPT_TRUE || fa->allow_list.ptr == NULL) {
702 fido_log_debug("%s: uv=%d, allow_list=%p", __func__, fa->uv,
703 (void *)fa->allow_list.ptr);
704 return (FIDO_ERR_UNSUPPORTED_OPTION);
707 if ((r = fido_assert_set_count(fa, fa->allow_list.len)) != FIDO_OK) {
708 fido_log_debug("%s: fido_assert_set_count", __func__);
712 for (size_t i = 0; i < fa->allow_list.len; i++) {
713 switch ((r = u2f_authenticate_single(dev,
714 &fa->allow_list.ptr[i], fa, nfound, ms))) {
718 case FIDO_ERR_USER_PRESENCE_REQUIRED:
722 if (r != FIDO_ERR_CREDENTIAL_EXCLUDED) {
723 fido_log_debug("%s: u2f_authenticate_single",
727 /* ignore credentials that don't exist */
731 fa->stmt_len = nfound;
734 return (FIDO_ERR_NO_CREDENTIALS);
736 return (FIDO_ERR_USER_PRESENCE_REQUIRED);
742 u2f_get_touch_begin(fido_dev_t *dev)
744 iso7816_apdu_t *apdu = NULL;
745 const char *clientdata = FIDO_DUMMY_CLIENTDATA;
746 const char *rp_id = FIDO_DUMMY_RP_ID;
747 unsigned char clientdata_hash[SHA256_DIGEST_LENGTH];
748 unsigned char rp_id_hash[SHA256_DIGEST_LENGTH];
749 unsigned char reply[FIDO_MAXMSG];
752 memset(&clientdata_hash, 0, sizeof(clientdata_hash));
753 memset(&rp_id_hash, 0, sizeof(rp_id_hash));
755 if (SHA256((const void *)clientdata, strlen(clientdata),
756 clientdata_hash) != clientdata_hash || SHA256((const void *)rp_id,
757 strlen(rp_id), rp_id_hash) != rp_id_hash) {
758 fido_log_debug("%s: sha256", __func__);
759 return (FIDO_ERR_INTERNAL);
762 if ((apdu = iso7816_new(0, U2F_CMD_REGISTER, 0, 2 *
763 SHA256_DIGEST_LENGTH)) == NULL ||
764 iso7816_add(apdu, clientdata_hash, sizeof(clientdata_hash)) < 0 ||
765 iso7816_add(apdu, rp_id_hash, sizeof(rp_id_hash)) < 0) {
766 fido_log_debug("%s: iso7816", __func__);
767 r = FIDO_ERR_INTERNAL;
771 if (dev->attr.flags & FIDO_CAP_WINK) {
772 fido_tx(dev, CTAP_CMD_WINK, NULL, 0);
773 fido_rx(dev, CTAP_CMD_WINK, &reply, sizeof(reply), 200);
776 if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu),
777 iso7816_len(apdu)) < 0) {
778 fido_log_debug("%s: fido_tx", __func__);
791 u2f_get_touch_status(fido_dev_t *dev, int *touched, int ms)
793 unsigned char reply[FIDO_MAXMSG];
797 if ((reply_len = fido_rx(dev, CTAP_CMD_MSG, &reply, sizeof(reply),
799 fido_log_debug("%s: fido_rx", __func__);
800 return (FIDO_OK); /* ignore */
803 switch ((reply[reply_len - 2] << 8) | reply[reply_len - 1]) {
804 case SW_CONDITIONS_NOT_SATISFIED:
805 if ((r = u2f_get_touch_begin(dev)) != FIDO_OK) {
806 fido_log_debug("%s: u2f_get_touch_begin", __func__);
815 fido_log_debug("%s: unexpected sw", __func__);
816 return (FIDO_ERR_RX);