10afa8e06SEd Maste /*
2*2ccfa855SEd Maste * Copyright (c) 2018-2022 Yubico AB. All rights reserved.
30afa8e06SEd Maste * Use of this source code is governed by a BSD-style
40afa8e06SEd Maste * license that can be found in the LICENSE file.
5*2ccfa855SEd Maste * SPDX-License-Identifier: BSD-2-Clause
60afa8e06SEd Maste */
70afa8e06SEd Maste
80afa8e06SEd Maste #include <openssl/sha.h>
90afa8e06SEd Maste #include <openssl/x509.h>
100afa8e06SEd Maste
110afa8e06SEd Maste #ifdef HAVE_UNISTD_H
120afa8e06SEd Maste #include <unistd.h>
130afa8e06SEd Maste #endif
14f540a430SEd Maste #include <errno.h>
150afa8e06SEd Maste
160afa8e06SEd Maste #include "fido.h"
170afa8e06SEd Maste #include "fido/es256.h"
18*2ccfa855SEd Maste #include "fallthrough.h"
190afa8e06SEd Maste
20f540a430SEd Maste #define U2F_PACE_MS (100)
21f540a430SEd Maste
220afa8e06SEd Maste #if defined(_MSC_VER)
230afa8e06SEd Maste static int
usleep(unsigned int usec)240afa8e06SEd Maste usleep(unsigned int usec)
250afa8e06SEd Maste {
260afa8e06SEd Maste Sleep(usec / 1000);
270afa8e06SEd Maste
280afa8e06SEd Maste return (0);
290afa8e06SEd Maste }
300afa8e06SEd Maste #endif
310afa8e06SEd Maste
320afa8e06SEd Maste static int
delay_ms(unsigned int ms,int * ms_remain)33f540a430SEd Maste delay_ms(unsigned int ms, int *ms_remain)
34f540a430SEd Maste {
35f540a430SEd Maste if (*ms_remain > -1 && (unsigned int)*ms_remain < ms)
36f540a430SEd Maste ms = (unsigned int)*ms_remain;
37f540a430SEd Maste
38f540a430SEd Maste if (ms > UINT_MAX / 1000) {
39f540a430SEd Maste fido_log_debug("%s: ms=%u", __func__, ms);
40f540a430SEd Maste return (-1);
41f540a430SEd Maste }
42f540a430SEd Maste
43f540a430SEd Maste if (usleep(ms * 1000) < 0) {
44f540a430SEd Maste fido_log_error(errno, "%s: usleep", __func__);
45f540a430SEd Maste return (-1);
46f540a430SEd Maste }
47f540a430SEd Maste
48f540a430SEd Maste if (*ms_remain > -1)
49f540a430SEd Maste *ms_remain -= (int)ms;
50f540a430SEd Maste
51f540a430SEd Maste return (0);
52f540a430SEd Maste }
53f540a430SEd Maste
54f540a430SEd Maste static int
sig_get(fido_blob_t * sig,const unsigned char ** buf,size_t * len)550afa8e06SEd Maste sig_get(fido_blob_t *sig, const unsigned char **buf, size_t *len)
560afa8e06SEd Maste {
570afa8e06SEd Maste sig->len = *len; /* consume the whole buffer */
580afa8e06SEd Maste if ((sig->ptr = calloc(1, sig->len)) == NULL ||
590afa8e06SEd Maste fido_buf_read(buf, len, sig->ptr, sig->len) < 0) {
600afa8e06SEd Maste fido_log_debug("%s: fido_buf_read", __func__);
610afa8e06SEd Maste fido_blob_reset(sig);
620afa8e06SEd Maste return (-1);
630afa8e06SEd Maste }
640afa8e06SEd Maste
650afa8e06SEd Maste return (0);
660afa8e06SEd Maste }
670afa8e06SEd Maste
680afa8e06SEd Maste static int
x5c_get(fido_blob_t * x5c,const unsigned char ** buf,size_t * len)690afa8e06SEd Maste x5c_get(fido_blob_t *x5c, const unsigned char **buf, size_t *len)
700afa8e06SEd Maste {
710afa8e06SEd Maste X509 *cert = NULL;
720afa8e06SEd Maste int ok = -1;
730afa8e06SEd Maste
740afa8e06SEd Maste if (*len > LONG_MAX) {
750afa8e06SEd Maste fido_log_debug("%s: invalid len %zu", __func__, *len);
760afa8e06SEd Maste goto fail;
770afa8e06SEd Maste }
780afa8e06SEd Maste
790afa8e06SEd Maste /* find out the certificate's length */
800afa8e06SEd Maste const unsigned char *end = *buf;
810afa8e06SEd Maste if ((cert = d2i_X509(NULL, &end, (long)*len)) == NULL || end <= *buf ||
820afa8e06SEd Maste (x5c->len = (size_t)(end - *buf)) >= *len) {
830afa8e06SEd Maste fido_log_debug("%s: d2i_X509", __func__);
840afa8e06SEd Maste goto fail;
850afa8e06SEd Maste }
860afa8e06SEd Maste
870afa8e06SEd Maste /* read accordingly */
880afa8e06SEd Maste if ((x5c->ptr = calloc(1, x5c->len)) == NULL ||
890afa8e06SEd Maste fido_buf_read(buf, len, x5c->ptr, x5c->len) < 0) {
900afa8e06SEd Maste fido_log_debug("%s: fido_buf_read", __func__);
910afa8e06SEd Maste goto fail;
920afa8e06SEd Maste }
930afa8e06SEd Maste
940afa8e06SEd Maste ok = 0;
950afa8e06SEd Maste fail:
960afa8e06SEd Maste if (cert != NULL)
970afa8e06SEd Maste X509_free(cert);
980afa8e06SEd Maste
990afa8e06SEd Maste if (ok < 0)
1000afa8e06SEd Maste fido_blob_reset(x5c);
1010afa8e06SEd Maste
1020afa8e06SEd Maste return (ok);
1030afa8e06SEd Maste }
1040afa8e06SEd Maste
1050afa8e06SEd Maste static int
authdata_fake(const char * rp_id,uint8_t flags,uint32_t sigcount,fido_blob_t * fake_cbor_ad)1060afa8e06SEd Maste authdata_fake(const char *rp_id, uint8_t flags, uint32_t sigcount,
1070afa8e06SEd Maste fido_blob_t *fake_cbor_ad)
1080afa8e06SEd Maste {
1090afa8e06SEd Maste fido_authdata_t ad;
1100afa8e06SEd Maste cbor_item_t *item = NULL;
1110afa8e06SEd Maste size_t alloc_len;
1120afa8e06SEd Maste
1130afa8e06SEd Maste memset(&ad, 0, sizeof(ad));
1140afa8e06SEd Maste
1150afa8e06SEd Maste if (SHA256((const void *)rp_id, strlen(rp_id),
1160afa8e06SEd Maste ad.rp_id_hash) != ad.rp_id_hash) {
1170afa8e06SEd Maste fido_log_debug("%s: sha256", __func__);
1180afa8e06SEd Maste return (-1);
1190afa8e06SEd Maste }
1200afa8e06SEd Maste
1210afa8e06SEd Maste ad.flags = flags; /* XXX translate? */
1220afa8e06SEd Maste ad.sigcount = sigcount;
1230afa8e06SEd Maste
1240afa8e06SEd Maste if ((item = cbor_build_bytestring((const unsigned char *)&ad,
1250afa8e06SEd Maste sizeof(ad))) == NULL) {
1260afa8e06SEd Maste fido_log_debug("%s: cbor_build_bytestring", __func__);
1270afa8e06SEd Maste return (-1);
1280afa8e06SEd Maste }
1290afa8e06SEd Maste
1300afa8e06SEd Maste if (fake_cbor_ad->ptr != NULL ||
1310afa8e06SEd Maste (fake_cbor_ad->len = cbor_serialize_alloc(item, &fake_cbor_ad->ptr,
1320afa8e06SEd Maste &alloc_len)) == 0) {
1330afa8e06SEd Maste fido_log_debug("%s: cbor_serialize_alloc", __func__);
1340afa8e06SEd Maste cbor_decref(&item);
1350afa8e06SEd Maste return (-1);
1360afa8e06SEd Maste }
1370afa8e06SEd Maste
1380afa8e06SEd Maste cbor_decref(&item);
1390afa8e06SEd Maste
1400afa8e06SEd Maste return (0);
1410afa8e06SEd Maste }
1420afa8e06SEd Maste
1430afa8e06SEd Maste /* TODO: use u2f_get_touch_begin & u2f_get_touch_status instead */
1440afa8e06SEd Maste static int
send_dummy_register(fido_dev_t * dev,int * ms)145f540a430SEd Maste send_dummy_register(fido_dev_t *dev, int *ms)
1460afa8e06SEd Maste {
1470afa8e06SEd Maste iso7816_apdu_t *apdu = NULL;
148*2ccfa855SEd Maste unsigned char *reply = NULL;
1490afa8e06SEd Maste unsigned char challenge[SHA256_DIGEST_LENGTH];
1500afa8e06SEd Maste unsigned char application[SHA256_DIGEST_LENGTH];
1510afa8e06SEd Maste int r;
1520afa8e06SEd Maste
1530afa8e06SEd Maste /* dummy challenge & application */
1540afa8e06SEd Maste memset(&challenge, 0xff, sizeof(challenge));
1550afa8e06SEd Maste memset(&application, 0xff, sizeof(application));
1560afa8e06SEd Maste
1570afa8e06SEd Maste if ((apdu = iso7816_new(0, U2F_CMD_REGISTER, 0, 2 *
1580afa8e06SEd Maste SHA256_DIGEST_LENGTH)) == NULL ||
1590afa8e06SEd Maste iso7816_add(apdu, &challenge, sizeof(challenge)) < 0 ||
1600afa8e06SEd Maste iso7816_add(apdu, &application, sizeof(application)) < 0) {
1610afa8e06SEd Maste fido_log_debug("%s: iso7816", __func__);
1620afa8e06SEd Maste r = FIDO_ERR_INTERNAL;
1630afa8e06SEd Maste goto fail;
1640afa8e06SEd Maste }
1650afa8e06SEd Maste
166*2ccfa855SEd Maste if ((reply = malloc(FIDO_MAXMSG)) == NULL) {
167*2ccfa855SEd Maste fido_log_debug("%s: malloc", __func__);
168*2ccfa855SEd Maste r = FIDO_ERR_INTERNAL;
169*2ccfa855SEd Maste goto fail;
170*2ccfa855SEd Maste }
171*2ccfa855SEd Maste
1720afa8e06SEd Maste do {
1730afa8e06SEd Maste if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu),
174f540a430SEd Maste iso7816_len(apdu), ms) < 0) {
1750afa8e06SEd Maste fido_log_debug("%s: fido_tx", __func__);
1760afa8e06SEd Maste r = FIDO_ERR_TX;
1770afa8e06SEd Maste goto fail;
1780afa8e06SEd Maste }
179*2ccfa855SEd Maste if (fido_rx(dev, CTAP_CMD_MSG, reply, FIDO_MAXMSG, ms) < 2) {
1800afa8e06SEd Maste fido_log_debug("%s: fido_rx", __func__);
1810afa8e06SEd Maste r = FIDO_ERR_RX;
1820afa8e06SEd Maste goto fail;
1830afa8e06SEd Maste }
184f540a430SEd Maste if (delay_ms(U2F_PACE_MS, ms) != 0) {
185f540a430SEd Maste fido_log_debug("%s: delay_ms", __func__);
1860afa8e06SEd Maste r = FIDO_ERR_RX;
1870afa8e06SEd Maste goto fail;
1880afa8e06SEd Maste }
1890afa8e06SEd Maste } while (((reply[0] << 8) | reply[1]) == SW_CONDITIONS_NOT_SATISFIED);
1900afa8e06SEd Maste
1910afa8e06SEd Maste r = FIDO_OK;
1920afa8e06SEd Maste fail:
1930afa8e06SEd Maste iso7816_free(&apdu);
194*2ccfa855SEd Maste freezero(reply, FIDO_MAXMSG);
1950afa8e06SEd Maste
1960afa8e06SEd Maste return (r);
1970afa8e06SEd Maste }
1980afa8e06SEd Maste
1990afa8e06SEd Maste static int
key_lookup(fido_dev_t * dev,const char * rp_id,const fido_blob_t * key_id,int * found,int * ms)2000afa8e06SEd Maste key_lookup(fido_dev_t *dev, const char *rp_id, const fido_blob_t *key_id,
201f540a430SEd Maste int *found, int *ms)
2020afa8e06SEd Maste {
2030afa8e06SEd Maste iso7816_apdu_t *apdu = NULL;
204*2ccfa855SEd Maste unsigned char *reply = NULL;
2050afa8e06SEd Maste unsigned char challenge[SHA256_DIGEST_LENGTH];
2060afa8e06SEd Maste unsigned char rp_id_hash[SHA256_DIGEST_LENGTH];
2070afa8e06SEd Maste uint8_t key_id_len;
2080afa8e06SEd Maste int r;
2090afa8e06SEd Maste
2100afa8e06SEd Maste if (key_id->len > UINT8_MAX || rp_id == NULL) {
2110afa8e06SEd Maste fido_log_debug("%s: key_id->len=%zu, rp_id=%p", __func__,
2120afa8e06SEd Maste key_id->len, (const void *)rp_id);
2130afa8e06SEd Maste r = FIDO_ERR_INVALID_ARGUMENT;
2140afa8e06SEd Maste goto fail;
2150afa8e06SEd Maste }
2160afa8e06SEd Maste
2170afa8e06SEd Maste memset(&challenge, 0xff, sizeof(challenge));
2180afa8e06SEd Maste memset(&rp_id_hash, 0, sizeof(rp_id_hash));
2190afa8e06SEd Maste
2200afa8e06SEd Maste if (SHA256((const void *)rp_id, strlen(rp_id),
2210afa8e06SEd Maste rp_id_hash) != rp_id_hash) {
2220afa8e06SEd Maste fido_log_debug("%s: sha256", __func__);
2230afa8e06SEd Maste r = FIDO_ERR_INTERNAL;
2240afa8e06SEd Maste goto fail;
2250afa8e06SEd Maste }
2260afa8e06SEd Maste
2270afa8e06SEd Maste key_id_len = (uint8_t)key_id->len;
2280afa8e06SEd Maste
2290afa8e06SEd Maste if ((apdu = iso7816_new(0, U2F_CMD_AUTH, U2F_AUTH_CHECK, (uint16_t)(2 *
2300afa8e06SEd Maste SHA256_DIGEST_LENGTH + sizeof(key_id_len) + key_id_len))) == NULL ||
2310afa8e06SEd Maste iso7816_add(apdu, &challenge, sizeof(challenge)) < 0 ||
2320afa8e06SEd Maste iso7816_add(apdu, &rp_id_hash, sizeof(rp_id_hash)) < 0 ||
2330afa8e06SEd Maste iso7816_add(apdu, &key_id_len, sizeof(key_id_len)) < 0 ||
2340afa8e06SEd Maste iso7816_add(apdu, key_id->ptr, key_id_len) < 0) {
2350afa8e06SEd Maste fido_log_debug("%s: iso7816", __func__);
2360afa8e06SEd Maste r = FIDO_ERR_INTERNAL;
2370afa8e06SEd Maste goto fail;
2380afa8e06SEd Maste }
2390afa8e06SEd Maste
240*2ccfa855SEd Maste if ((reply = malloc(FIDO_MAXMSG)) == NULL) {
241*2ccfa855SEd Maste fido_log_debug("%s: malloc", __func__);
242*2ccfa855SEd Maste r = FIDO_ERR_INTERNAL;
243*2ccfa855SEd Maste goto fail;
244*2ccfa855SEd Maste }
245*2ccfa855SEd Maste
2460afa8e06SEd Maste if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu),
247f540a430SEd Maste iso7816_len(apdu), ms) < 0) {
2480afa8e06SEd Maste fido_log_debug("%s: fido_tx", __func__);
2490afa8e06SEd Maste r = FIDO_ERR_TX;
2500afa8e06SEd Maste goto fail;
2510afa8e06SEd Maste }
252*2ccfa855SEd Maste if (fido_rx(dev, CTAP_CMD_MSG, reply, FIDO_MAXMSG, ms) != 2) {
2530afa8e06SEd Maste fido_log_debug("%s: fido_rx", __func__);
2540afa8e06SEd Maste r = FIDO_ERR_RX;
2550afa8e06SEd Maste goto fail;
2560afa8e06SEd Maste }
2570afa8e06SEd Maste
2580afa8e06SEd Maste switch ((reply[0] << 8) | reply[1]) {
2590afa8e06SEd Maste case SW_CONDITIONS_NOT_SATISFIED:
2600afa8e06SEd Maste *found = 1; /* key exists */
2610afa8e06SEd Maste break;
2620afa8e06SEd Maste case SW_WRONG_DATA:
2630afa8e06SEd Maste *found = 0; /* key does not exist */
2640afa8e06SEd Maste break;
2650afa8e06SEd Maste default:
2660afa8e06SEd Maste /* unexpected sw */
2670afa8e06SEd Maste r = FIDO_ERR_INTERNAL;
2680afa8e06SEd Maste goto fail;
2690afa8e06SEd Maste }
2700afa8e06SEd Maste
2710afa8e06SEd Maste r = FIDO_OK;
2720afa8e06SEd Maste fail:
2730afa8e06SEd Maste iso7816_free(&apdu);
274*2ccfa855SEd Maste freezero(reply, FIDO_MAXMSG);
2750afa8e06SEd Maste
2760afa8e06SEd Maste return (r);
2770afa8e06SEd Maste }
2780afa8e06SEd Maste
2790afa8e06SEd Maste static int
parse_auth_reply(fido_blob_t * sig,fido_blob_t * ad,const char * rp_id,const unsigned char * reply,size_t len)2800afa8e06SEd Maste parse_auth_reply(fido_blob_t *sig, fido_blob_t *ad, const char *rp_id,
2810afa8e06SEd Maste const unsigned char *reply, size_t len)
2820afa8e06SEd Maste {
2830afa8e06SEd Maste uint8_t flags;
2840afa8e06SEd Maste uint32_t sigcount;
2850afa8e06SEd Maste
2860afa8e06SEd Maste if (len < 2 || ((reply[len - 2] << 8) | reply[len - 1]) != SW_NO_ERROR) {
2870afa8e06SEd Maste fido_log_debug("%s: unexpected sw", __func__);
2880afa8e06SEd Maste return (FIDO_ERR_RX);
2890afa8e06SEd Maste }
2900afa8e06SEd Maste
2910afa8e06SEd Maste len -= 2;
2920afa8e06SEd Maste
2930afa8e06SEd Maste if (fido_buf_read(&reply, &len, &flags, sizeof(flags)) < 0 ||
2940afa8e06SEd Maste fido_buf_read(&reply, &len, &sigcount, sizeof(sigcount)) < 0) {
2950afa8e06SEd Maste fido_log_debug("%s: fido_buf_read", __func__);
2960afa8e06SEd Maste return (FIDO_ERR_RX);
2970afa8e06SEd Maste }
2980afa8e06SEd Maste
2990afa8e06SEd Maste if (sig_get(sig, &reply, &len) < 0) {
3000afa8e06SEd Maste fido_log_debug("%s: sig_get", __func__);
3010afa8e06SEd Maste return (FIDO_ERR_RX);
3020afa8e06SEd Maste }
3030afa8e06SEd Maste
3040afa8e06SEd Maste if (authdata_fake(rp_id, flags, sigcount, ad) < 0) {
3050afa8e06SEd Maste fido_log_debug("%s; authdata_fake", __func__);
3060afa8e06SEd Maste return (FIDO_ERR_RX);
3070afa8e06SEd Maste }
3080afa8e06SEd Maste
3090afa8e06SEd Maste return (FIDO_OK);
3100afa8e06SEd Maste }
3110afa8e06SEd Maste
3120afa8e06SEd Maste 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)3130afa8e06SEd Maste do_auth(fido_dev_t *dev, const fido_blob_t *cdh, const char *rp_id,
314f540a430SEd Maste const fido_blob_t *key_id, fido_blob_t *sig, fido_blob_t *ad, int *ms)
3150afa8e06SEd Maste {
3160afa8e06SEd Maste iso7816_apdu_t *apdu = NULL;
317*2ccfa855SEd Maste unsigned char *reply = NULL;
3180afa8e06SEd Maste unsigned char rp_id_hash[SHA256_DIGEST_LENGTH];
3190afa8e06SEd Maste int reply_len;
3200afa8e06SEd Maste uint8_t key_id_len;
3210afa8e06SEd Maste int r;
3220afa8e06SEd Maste
3230afa8e06SEd Maste #ifdef FIDO_FUZZ
324f540a430SEd Maste *ms = 0; /* XXX */
3250afa8e06SEd Maste #endif
3260afa8e06SEd Maste
3270afa8e06SEd Maste if (cdh->len != SHA256_DIGEST_LENGTH || key_id->len > UINT8_MAX ||
3280afa8e06SEd Maste rp_id == NULL) {
3290afa8e06SEd Maste r = FIDO_ERR_INVALID_ARGUMENT;
3300afa8e06SEd Maste goto fail;
3310afa8e06SEd Maste }
3320afa8e06SEd Maste
3330afa8e06SEd Maste memset(&rp_id_hash, 0, sizeof(rp_id_hash));
3340afa8e06SEd Maste
3350afa8e06SEd Maste if (SHA256((const void *)rp_id, strlen(rp_id),
3360afa8e06SEd Maste rp_id_hash) != rp_id_hash) {
3370afa8e06SEd Maste fido_log_debug("%s: sha256", __func__);
3380afa8e06SEd Maste r = FIDO_ERR_INTERNAL;
3390afa8e06SEd Maste goto fail;
3400afa8e06SEd Maste }
3410afa8e06SEd Maste
3420afa8e06SEd Maste key_id_len = (uint8_t)key_id->len;
3430afa8e06SEd Maste
3440afa8e06SEd Maste if ((apdu = iso7816_new(0, U2F_CMD_AUTH, U2F_AUTH_SIGN, (uint16_t)(2 *
3450afa8e06SEd Maste SHA256_DIGEST_LENGTH + sizeof(key_id_len) + key_id_len))) == NULL ||
3460afa8e06SEd Maste iso7816_add(apdu, cdh->ptr, cdh->len) < 0 ||
3470afa8e06SEd Maste iso7816_add(apdu, &rp_id_hash, sizeof(rp_id_hash)) < 0 ||
3480afa8e06SEd Maste iso7816_add(apdu, &key_id_len, sizeof(key_id_len)) < 0 ||
3490afa8e06SEd Maste iso7816_add(apdu, key_id->ptr, key_id_len) < 0) {
3500afa8e06SEd Maste fido_log_debug("%s: iso7816", __func__);
3510afa8e06SEd Maste r = FIDO_ERR_INTERNAL;
3520afa8e06SEd Maste goto fail;
3530afa8e06SEd Maste }
3540afa8e06SEd Maste
355*2ccfa855SEd Maste if ((reply = malloc(FIDO_MAXMSG)) == NULL) {
356*2ccfa855SEd Maste fido_log_debug("%s: malloc", __func__);
357*2ccfa855SEd Maste r = FIDO_ERR_INTERNAL;
358*2ccfa855SEd Maste goto fail;
359*2ccfa855SEd Maste }
360*2ccfa855SEd Maste
3610afa8e06SEd Maste do {
3620afa8e06SEd Maste if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu),
363f540a430SEd Maste iso7816_len(apdu), ms) < 0) {
3640afa8e06SEd Maste fido_log_debug("%s: fido_tx", __func__);
3650afa8e06SEd Maste r = FIDO_ERR_TX;
3660afa8e06SEd Maste goto fail;
3670afa8e06SEd Maste }
368*2ccfa855SEd Maste if ((reply_len = fido_rx(dev, CTAP_CMD_MSG, reply,
369*2ccfa855SEd Maste FIDO_MAXMSG, ms)) < 2) {
3700afa8e06SEd Maste fido_log_debug("%s: fido_rx", __func__);
3710afa8e06SEd Maste r = FIDO_ERR_RX;
3720afa8e06SEd Maste goto fail;
3730afa8e06SEd Maste }
374f540a430SEd Maste if (delay_ms(U2F_PACE_MS, ms) != 0) {
375f540a430SEd Maste fido_log_debug("%s: delay_ms", __func__);
3760afa8e06SEd Maste r = FIDO_ERR_RX;
3770afa8e06SEd Maste goto fail;
3780afa8e06SEd Maste }
3790afa8e06SEd Maste } while (((reply[0] << 8) | reply[1]) == SW_CONDITIONS_NOT_SATISFIED);
3800afa8e06SEd Maste
3810afa8e06SEd Maste if ((r = parse_auth_reply(sig, ad, rp_id, reply,
3820afa8e06SEd Maste (size_t)reply_len)) != FIDO_OK) {
3830afa8e06SEd Maste fido_log_debug("%s: parse_auth_reply", __func__);
3840afa8e06SEd Maste goto fail;
3850afa8e06SEd Maste }
3860afa8e06SEd Maste
3870afa8e06SEd Maste fail:
3880afa8e06SEd Maste iso7816_free(&apdu);
389*2ccfa855SEd Maste freezero(reply, FIDO_MAXMSG);
3900afa8e06SEd Maste
3910afa8e06SEd Maste return (r);
3920afa8e06SEd Maste }
3930afa8e06SEd Maste
3940afa8e06SEd Maste static int
cbor_blob_from_ec_point(const uint8_t * ec_point,size_t ec_point_len,fido_blob_t * cbor_blob)3950afa8e06SEd Maste cbor_blob_from_ec_point(const uint8_t *ec_point, size_t ec_point_len,
3960afa8e06SEd Maste fido_blob_t *cbor_blob)
3970afa8e06SEd Maste {
3980afa8e06SEd Maste es256_pk_t *pk = NULL;
3990afa8e06SEd Maste cbor_item_t *pk_cbor = NULL;
4000afa8e06SEd Maste size_t alloc_len;
4010afa8e06SEd Maste int ok = -1;
4020afa8e06SEd Maste
4030afa8e06SEd Maste /* only handle uncompressed points */
4040afa8e06SEd Maste if (ec_point_len != 65 || ec_point[0] != 0x04) {
4050afa8e06SEd Maste fido_log_debug("%s: unexpected format", __func__);
4060afa8e06SEd Maste goto fail;
4070afa8e06SEd Maste }
4080afa8e06SEd Maste
4090afa8e06SEd Maste if ((pk = es256_pk_new()) == NULL ||
4100afa8e06SEd Maste es256_pk_set_x(pk, &ec_point[1]) < 0 ||
4110afa8e06SEd Maste es256_pk_set_y(pk, &ec_point[33]) < 0) {
4120afa8e06SEd Maste fido_log_debug("%s: es256_pk_set", __func__);
4130afa8e06SEd Maste goto fail;
4140afa8e06SEd Maste }
4150afa8e06SEd Maste
4160afa8e06SEd Maste if ((pk_cbor = es256_pk_encode(pk, 0)) == NULL) {
4170afa8e06SEd Maste fido_log_debug("%s: es256_pk_encode", __func__);
4180afa8e06SEd Maste goto fail;
4190afa8e06SEd Maste }
4200afa8e06SEd Maste
4210afa8e06SEd Maste if ((cbor_blob->len = cbor_serialize_alloc(pk_cbor, &cbor_blob->ptr,
4220afa8e06SEd Maste &alloc_len)) != 77) {
4230afa8e06SEd Maste fido_log_debug("%s: cbor_serialize_alloc", __func__);
4240afa8e06SEd Maste goto fail;
4250afa8e06SEd Maste }
4260afa8e06SEd Maste
4270afa8e06SEd Maste ok = 0;
4280afa8e06SEd Maste fail:
4290afa8e06SEd Maste es256_pk_free(&pk);
4300afa8e06SEd Maste
4310afa8e06SEd Maste if (pk_cbor)
4320afa8e06SEd Maste cbor_decref(&pk_cbor);
4330afa8e06SEd Maste
4340afa8e06SEd Maste return (ok);
4350afa8e06SEd Maste }
4360afa8e06SEd Maste
4370afa8e06SEd Maste static int
encode_cred_attstmt(int cose_alg,const fido_blob_t * x5c,const fido_blob_t * sig,fido_blob_t * out)438f540a430SEd Maste encode_cred_attstmt(int cose_alg, const fido_blob_t *x5c,
439f540a430SEd Maste const fido_blob_t *sig, fido_blob_t *out)
440f540a430SEd Maste {
441f540a430SEd Maste cbor_item_t *item = NULL;
442f540a430SEd Maste cbor_item_t *x5c_cbor = NULL;
443f540a430SEd Maste const uint8_t alg_cbor = (uint8_t)(-cose_alg - 1);
444f540a430SEd Maste struct cbor_pair kv[3];
445f540a430SEd Maste size_t alloc_len;
446f540a430SEd Maste int ok = -1;
447f540a430SEd Maste
448f540a430SEd Maste memset(&kv, 0, sizeof(kv));
449f540a430SEd Maste memset(out, 0, sizeof(*out));
450f540a430SEd Maste
451f540a430SEd Maste if ((item = cbor_new_definite_map(3)) == NULL) {
452f540a430SEd Maste fido_log_debug("%s: cbor_new_definite_map", __func__);
453f540a430SEd Maste goto fail;
454f540a430SEd Maste }
455f540a430SEd Maste
456f540a430SEd Maste if ((kv[0].key = cbor_build_string("alg")) == NULL ||
457f540a430SEd Maste (kv[0].value = cbor_build_negint8(alg_cbor)) == NULL ||
458f540a430SEd Maste !cbor_map_add(item, kv[0])) {
459f540a430SEd Maste fido_log_debug("%s: alg", __func__);
460f540a430SEd Maste goto fail;
461f540a430SEd Maste }
462f540a430SEd Maste
463f540a430SEd Maste if ((kv[1].key = cbor_build_string("sig")) == NULL ||
464f540a430SEd Maste (kv[1].value = fido_blob_encode(sig)) == NULL ||
465f540a430SEd Maste !cbor_map_add(item, kv[1])) {
466f540a430SEd Maste fido_log_debug("%s: sig", __func__);
467f540a430SEd Maste goto fail;
468f540a430SEd Maste }
469f540a430SEd Maste
470f540a430SEd Maste if ((kv[2].key = cbor_build_string("x5c")) == NULL ||
471f540a430SEd Maste (kv[2].value = cbor_new_definite_array(1)) == NULL ||
472f540a430SEd Maste (x5c_cbor = fido_blob_encode(x5c)) == NULL ||
473f540a430SEd Maste !cbor_array_push(kv[2].value, x5c_cbor) ||
474f540a430SEd Maste !cbor_map_add(item, kv[2])) {
475f540a430SEd Maste fido_log_debug("%s: x5c", __func__);
476f540a430SEd Maste goto fail;
477f540a430SEd Maste }
478f540a430SEd Maste
479f540a430SEd Maste if ((out->len = cbor_serialize_alloc(item, &out->ptr,
480f540a430SEd Maste &alloc_len)) == 0) {
481f540a430SEd Maste fido_log_debug("%s: cbor_serialize_alloc", __func__);
482f540a430SEd Maste goto fail;
483f540a430SEd Maste }
484f540a430SEd Maste
485f540a430SEd Maste ok = 0;
486f540a430SEd Maste fail:
487f540a430SEd Maste if (item != NULL)
488f540a430SEd Maste cbor_decref(&item);
489f540a430SEd Maste if (x5c_cbor != NULL)
490f540a430SEd Maste cbor_decref(&x5c_cbor);
491f540a430SEd Maste
492f540a430SEd Maste for (size_t i = 0; i < nitems(kv); i++) {
493f540a430SEd Maste if (kv[i].key)
494f540a430SEd Maste cbor_decref(&kv[i].key);
495f540a430SEd Maste if (kv[i].value)
496f540a430SEd Maste cbor_decref(&kv[i].value);
497f540a430SEd Maste }
498f540a430SEd Maste
499f540a430SEd Maste return (ok);
500f540a430SEd Maste }
501f540a430SEd Maste
502f540a430SEd Maste 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)5030afa8e06SEd Maste encode_cred_authdata(const char *rp_id, const uint8_t *kh, uint8_t kh_len,
5040afa8e06SEd Maste const uint8_t *pubkey, size_t pubkey_len, fido_blob_t *out)
5050afa8e06SEd Maste {
5060afa8e06SEd Maste fido_authdata_t authdata;
5070afa8e06SEd Maste fido_attcred_raw_t attcred_raw;
5080afa8e06SEd Maste fido_blob_t pk_blob;
5090afa8e06SEd Maste fido_blob_t authdata_blob;
5100afa8e06SEd Maste cbor_item_t *authdata_cbor = NULL;
5110afa8e06SEd Maste unsigned char *ptr;
5120afa8e06SEd Maste size_t len;
5130afa8e06SEd Maste size_t alloc_len;
5140afa8e06SEd Maste int ok = -1;
5150afa8e06SEd Maste
5160afa8e06SEd Maste memset(&pk_blob, 0, sizeof(pk_blob));
5170afa8e06SEd Maste memset(&authdata, 0, sizeof(authdata));
5180afa8e06SEd Maste memset(&authdata_blob, 0, sizeof(authdata_blob));
5190afa8e06SEd Maste memset(out, 0, sizeof(*out));
5200afa8e06SEd Maste
5210afa8e06SEd Maste if (rp_id == NULL) {
5220afa8e06SEd Maste fido_log_debug("%s: NULL rp_id", __func__);
5230afa8e06SEd Maste goto fail;
5240afa8e06SEd Maste }
5250afa8e06SEd Maste
5260afa8e06SEd Maste if (cbor_blob_from_ec_point(pubkey, pubkey_len, &pk_blob) < 0) {
5270afa8e06SEd Maste fido_log_debug("%s: cbor_blob_from_ec_point", __func__);
5280afa8e06SEd Maste goto fail;
5290afa8e06SEd Maste }
5300afa8e06SEd Maste
5310afa8e06SEd Maste if (SHA256((const void *)rp_id, strlen(rp_id),
5320afa8e06SEd Maste authdata.rp_id_hash) != authdata.rp_id_hash) {
5330afa8e06SEd Maste fido_log_debug("%s: sha256", __func__);
5340afa8e06SEd Maste goto fail;
5350afa8e06SEd Maste }
5360afa8e06SEd Maste
5370afa8e06SEd Maste authdata.flags = (CTAP_AUTHDATA_ATT_CRED | CTAP_AUTHDATA_USER_PRESENT);
5380afa8e06SEd Maste authdata.sigcount = 0;
5390afa8e06SEd Maste
5400afa8e06SEd Maste memset(&attcred_raw.aaguid, 0, sizeof(attcred_raw.aaguid));
5410afa8e06SEd Maste attcred_raw.id_len = htobe16(kh_len);
5420afa8e06SEd Maste
5430afa8e06SEd Maste len = authdata_blob.len = sizeof(authdata) + sizeof(attcred_raw) +
5440afa8e06SEd Maste kh_len + pk_blob.len;
5450afa8e06SEd Maste ptr = authdata_blob.ptr = calloc(1, authdata_blob.len);
5460afa8e06SEd Maste
5470afa8e06SEd Maste fido_log_debug("%s: ptr=%p, len=%zu", __func__, (void *)ptr, len);
5480afa8e06SEd Maste
5490afa8e06SEd Maste if (authdata_blob.ptr == NULL)
5500afa8e06SEd Maste goto fail;
5510afa8e06SEd Maste
5520afa8e06SEd Maste if (fido_buf_write(&ptr, &len, &authdata, sizeof(authdata)) < 0 ||
5530afa8e06SEd Maste fido_buf_write(&ptr, &len, &attcred_raw, sizeof(attcred_raw)) < 0 ||
5540afa8e06SEd Maste fido_buf_write(&ptr, &len, kh, kh_len) < 0 ||
5550afa8e06SEd Maste fido_buf_write(&ptr, &len, pk_blob.ptr, pk_blob.len) < 0) {
5560afa8e06SEd Maste fido_log_debug("%s: fido_buf_write", __func__);
5570afa8e06SEd Maste goto fail;
5580afa8e06SEd Maste }
5590afa8e06SEd Maste
5600afa8e06SEd Maste if ((authdata_cbor = fido_blob_encode(&authdata_blob)) == NULL) {
5610afa8e06SEd Maste fido_log_debug("%s: fido_blob_encode", __func__);
5620afa8e06SEd Maste goto fail;
5630afa8e06SEd Maste }
5640afa8e06SEd Maste
5650afa8e06SEd Maste if ((out->len = cbor_serialize_alloc(authdata_cbor, &out->ptr,
5660afa8e06SEd Maste &alloc_len)) == 0) {
5670afa8e06SEd Maste fido_log_debug("%s: cbor_serialize_alloc", __func__);
5680afa8e06SEd Maste goto fail;
5690afa8e06SEd Maste }
5700afa8e06SEd Maste
5710afa8e06SEd Maste ok = 0;
5720afa8e06SEd Maste fail:
5730afa8e06SEd Maste if (authdata_cbor)
5740afa8e06SEd Maste cbor_decref(&authdata_cbor);
5750afa8e06SEd Maste
5760afa8e06SEd Maste fido_blob_reset(&pk_blob);
5770afa8e06SEd Maste fido_blob_reset(&authdata_blob);
5780afa8e06SEd Maste
5790afa8e06SEd Maste return (ok);
5800afa8e06SEd Maste }
5810afa8e06SEd Maste
5820afa8e06SEd Maste static int
parse_register_reply(fido_cred_t * cred,const unsigned char * reply,size_t len)5830afa8e06SEd Maste parse_register_reply(fido_cred_t *cred, const unsigned char *reply, size_t len)
5840afa8e06SEd Maste {
5850afa8e06SEd Maste fido_blob_t x5c;
5860afa8e06SEd Maste fido_blob_t sig;
5870afa8e06SEd Maste fido_blob_t ad;
588f540a430SEd Maste fido_blob_t stmt;
5890afa8e06SEd Maste uint8_t dummy;
5900afa8e06SEd Maste uint8_t pubkey[65];
5910afa8e06SEd Maste uint8_t kh_len = 0;
5920afa8e06SEd Maste uint8_t *kh = NULL;
5930afa8e06SEd Maste int r;
5940afa8e06SEd Maste
5950afa8e06SEd Maste memset(&x5c, 0, sizeof(x5c));
5960afa8e06SEd Maste memset(&sig, 0, sizeof(sig));
5970afa8e06SEd Maste memset(&ad, 0, sizeof(ad));
598f540a430SEd Maste memset(&stmt, 0, sizeof(stmt));
5990afa8e06SEd Maste r = FIDO_ERR_RX;
6000afa8e06SEd Maste
6010afa8e06SEd Maste /* status word */
6020afa8e06SEd Maste if (len < 2 || ((reply[len - 2] << 8) | reply[len - 1]) != SW_NO_ERROR) {
6030afa8e06SEd Maste fido_log_debug("%s: unexpected sw", __func__);
6040afa8e06SEd Maste goto fail;
6050afa8e06SEd Maste }
6060afa8e06SEd Maste
6070afa8e06SEd Maste len -= 2;
6080afa8e06SEd Maste
6090afa8e06SEd Maste /* reserved byte */
6100afa8e06SEd Maste if (fido_buf_read(&reply, &len, &dummy, sizeof(dummy)) < 0 ||
6110afa8e06SEd Maste dummy != 0x05) {
6120afa8e06SEd Maste fido_log_debug("%s: reserved byte", __func__);
6130afa8e06SEd Maste goto fail;
6140afa8e06SEd Maste }
6150afa8e06SEd Maste
6160afa8e06SEd Maste /* pubkey + key handle */
6170afa8e06SEd Maste if (fido_buf_read(&reply, &len, &pubkey, sizeof(pubkey)) < 0 ||
6180afa8e06SEd Maste fido_buf_read(&reply, &len, &kh_len, sizeof(kh_len)) < 0 ||
6190afa8e06SEd Maste (kh = calloc(1, kh_len)) == NULL ||
6200afa8e06SEd Maste fido_buf_read(&reply, &len, kh, kh_len) < 0) {
6210afa8e06SEd Maste fido_log_debug("%s: fido_buf_read", __func__);
6220afa8e06SEd Maste goto fail;
6230afa8e06SEd Maste }
6240afa8e06SEd Maste
6250afa8e06SEd Maste /* x5c + sig */
6260afa8e06SEd Maste if (x5c_get(&x5c, &reply, &len) < 0 ||
6270afa8e06SEd Maste sig_get(&sig, &reply, &len) < 0) {
6280afa8e06SEd Maste fido_log_debug("%s: x5c || sig", __func__);
6290afa8e06SEd Maste goto fail;
6300afa8e06SEd Maste }
6310afa8e06SEd Maste
632f540a430SEd Maste /* attstmt */
633f540a430SEd Maste if (encode_cred_attstmt(COSE_ES256, &x5c, &sig, &stmt) < 0) {
634f540a430SEd Maste fido_log_debug("%s: encode_cred_attstmt", __func__);
635f540a430SEd Maste goto fail;
636f540a430SEd Maste }
637f540a430SEd Maste
6380afa8e06SEd Maste /* authdata */
6390afa8e06SEd Maste if (encode_cred_authdata(cred->rp.id, kh, kh_len, pubkey,
6400afa8e06SEd Maste sizeof(pubkey), &ad) < 0) {
6410afa8e06SEd Maste fido_log_debug("%s: encode_cred_authdata", __func__);
6420afa8e06SEd Maste goto fail;
6430afa8e06SEd Maste }
6440afa8e06SEd Maste
6450afa8e06SEd Maste if (fido_cred_set_fmt(cred, "fido-u2f") != FIDO_OK ||
6460afa8e06SEd Maste fido_cred_set_authdata(cred, ad.ptr, ad.len) != FIDO_OK ||
647f540a430SEd Maste fido_cred_set_attstmt(cred, stmt.ptr, stmt.len) != FIDO_OK) {
6480afa8e06SEd Maste fido_log_debug("%s: fido_cred_set", __func__);
6490afa8e06SEd Maste r = FIDO_ERR_INTERNAL;
6500afa8e06SEd Maste goto fail;
6510afa8e06SEd Maste }
6520afa8e06SEd Maste
6530afa8e06SEd Maste r = FIDO_OK;
6540afa8e06SEd Maste fail:
6550afa8e06SEd Maste freezero(kh, kh_len);
6560afa8e06SEd Maste fido_blob_reset(&x5c);
6570afa8e06SEd Maste fido_blob_reset(&sig);
6580afa8e06SEd Maste fido_blob_reset(&ad);
659f540a430SEd Maste fido_blob_reset(&stmt);
6600afa8e06SEd Maste
6610afa8e06SEd Maste return (r);
6620afa8e06SEd Maste }
6630afa8e06SEd Maste
6640afa8e06SEd Maste int
u2f_register(fido_dev_t * dev,fido_cred_t * cred,int * ms)665f540a430SEd Maste u2f_register(fido_dev_t *dev, fido_cred_t *cred, int *ms)
6660afa8e06SEd Maste {
6670afa8e06SEd Maste iso7816_apdu_t *apdu = NULL;
6680afa8e06SEd Maste unsigned char rp_id_hash[SHA256_DIGEST_LENGTH];
669*2ccfa855SEd Maste unsigned char *reply = NULL;
6700afa8e06SEd Maste int reply_len;
6710afa8e06SEd Maste int found;
6720afa8e06SEd Maste int r;
6730afa8e06SEd Maste
6740afa8e06SEd Maste if (cred->rk == FIDO_OPT_TRUE || cred->uv == FIDO_OPT_TRUE) {
6750afa8e06SEd Maste fido_log_debug("%s: rk=%d, uv=%d", __func__, cred->rk,
6760afa8e06SEd Maste cred->uv);
6770afa8e06SEd Maste return (FIDO_ERR_UNSUPPORTED_OPTION);
6780afa8e06SEd Maste }
6790afa8e06SEd Maste
6800afa8e06SEd Maste if (cred->type != COSE_ES256 || cred->cdh.ptr == NULL ||
6810afa8e06SEd Maste cred->rp.id == NULL || cred->cdh.len != SHA256_DIGEST_LENGTH) {
6820afa8e06SEd Maste fido_log_debug("%s: type=%d, cdh=(%p,%zu)" , __func__,
6830afa8e06SEd Maste cred->type, (void *)cred->cdh.ptr, cred->cdh.len);
6840afa8e06SEd Maste return (FIDO_ERR_INVALID_ARGUMENT);
6850afa8e06SEd Maste }
6860afa8e06SEd Maste
6870afa8e06SEd Maste for (size_t i = 0; i < cred->excl.len; i++) {
6880afa8e06SEd Maste if ((r = key_lookup(dev, cred->rp.id, &cred->excl.ptr[i],
6890afa8e06SEd Maste &found, ms)) != FIDO_OK) {
6900afa8e06SEd Maste fido_log_debug("%s: key_lookup", __func__);
6910afa8e06SEd Maste return (r);
6920afa8e06SEd Maste }
6930afa8e06SEd Maste if (found) {
6940afa8e06SEd Maste if ((r = send_dummy_register(dev, ms)) != FIDO_OK) {
6950afa8e06SEd Maste fido_log_debug("%s: send_dummy_register",
6960afa8e06SEd Maste __func__);
6970afa8e06SEd Maste return (r);
6980afa8e06SEd Maste }
6990afa8e06SEd Maste return (FIDO_ERR_CREDENTIAL_EXCLUDED);
7000afa8e06SEd Maste }
7010afa8e06SEd Maste }
7020afa8e06SEd Maste
7030afa8e06SEd Maste memset(&rp_id_hash, 0, sizeof(rp_id_hash));
7040afa8e06SEd Maste
7050afa8e06SEd Maste if (SHA256((const void *)cred->rp.id, strlen(cred->rp.id),
7060afa8e06SEd Maste rp_id_hash) != rp_id_hash) {
7070afa8e06SEd Maste fido_log_debug("%s: sha256", __func__);
7080afa8e06SEd Maste return (FIDO_ERR_INTERNAL);
7090afa8e06SEd Maste }
7100afa8e06SEd Maste
7110afa8e06SEd Maste if ((apdu = iso7816_new(0, U2F_CMD_REGISTER, 0, 2 *
7120afa8e06SEd Maste SHA256_DIGEST_LENGTH)) == NULL ||
7130afa8e06SEd Maste iso7816_add(apdu, cred->cdh.ptr, cred->cdh.len) < 0 ||
7140afa8e06SEd Maste iso7816_add(apdu, rp_id_hash, sizeof(rp_id_hash)) < 0) {
7150afa8e06SEd Maste fido_log_debug("%s: iso7816", __func__);
7160afa8e06SEd Maste r = FIDO_ERR_INTERNAL;
7170afa8e06SEd Maste goto fail;
7180afa8e06SEd Maste }
7190afa8e06SEd Maste
720*2ccfa855SEd Maste if ((reply = malloc(FIDO_MAXMSG)) == NULL) {
721*2ccfa855SEd Maste fido_log_debug("%s: malloc", __func__);
722*2ccfa855SEd Maste r = FIDO_ERR_INTERNAL;
723*2ccfa855SEd Maste goto fail;
724*2ccfa855SEd Maste }
725*2ccfa855SEd Maste
7260afa8e06SEd Maste do {
7270afa8e06SEd Maste if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu),
728f540a430SEd Maste iso7816_len(apdu), ms) < 0) {
7290afa8e06SEd Maste fido_log_debug("%s: fido_tx", __func__);
7300afa8e06SEd Maste r = FIDO_ERR_TX;
7310afa8e06SEd Maste goto fail;
7320afa8e06SEd Maste }
733*2ccfa855SEd Maste if ((reply_len = fido_rx(dev, CTAP_CMD_MSG, reply,
734*2ccfa855SEd Maste FIDO_MAXMSG, ms)) < 2) {
7350afa8e06SEd Maste fido_log_debug("%s: fido_rx", __func__);
7360afa8e06SEd Maste r = FIDO_ERR_RX;
7370afa8e06SEd Maste goto fail;
7380afa8e06SEd Maste }
739f540a430SEd Maste if (delay_ms(U2F_PACE_MS, ms) != 0) {
740f540a430SEd Maste fido_log_debug("%s: delay_ms", __func__);
7410afa8e06SEd Maste r = FIDO_ERR_RX;
7420afa8e06SEd Maste goto fail;
7430afa8e06SEd Maste }
7440afa8e06SEd Maste } while (((reply[0] << 8) | reply[1]) == SW_CONDITIONS_NOT_SATISFIED);
7450afa8e06SEd Maste
7460afa8e06SEd Maste if ((r = parse_register_reply(cred, reply,
7470afa8e06SEd Maste (size_t)reply_len)) != FIDO_OK) {
7480afa8e06SEd Maste fido_log_debug("%s: parse_register_reply", __func__);
7490afa8e06SEd Maste goto fail;
7500afa8e06SEd Maste }
7510afa8e06SEd Maste fail:
7520afa8e06SEd Maste iso7816_free(&apdu);
753*2ccfa855SEd Maste freezero(reply, FIDO_MAXMSG);
7540afa8e06SEd Maste
7550afa8e06SEd Maste return (r);
7560afa8e06SEd Maste }
7570afa8e06SEd Maste
7580afa8e06SEd Maste static int
u2f_authenticate_single(fido_dev_t * dev,const fido_blob_t * key_id,fido_assert_t * fa,size_t idx,int * ms)7590afa8e06SEd Maste u2f_authenticate_single(fido_dev_t *dev, const fido_blob_t *key_id,
760f540a430SEd Maste fido_assert_t *fa, size_t idx, int *ms)
7610afa8e06SEd Maste {
7620afa8e06SEd Maste fido_blob_t sig;
7630afa8e06SEd Maste fido_blob_t ad;
7640afa8e06SEd Maste int found;
7650afa8e06SEd Maste int r;
7660afa8e06SEd Maste
7670afa8e06SEd Maste memset(&sig, 0, sizeof(sig));
7680afa8e06SEd Maste memset(&ad, 0, sizeof(ad));
7690afa8e06SEd Maste
7700afa8e06SEd Maste if ((r = key_lookup(dev, fa->rp_id, key_id, &found, ms)) != FIDO_OK) {
7710afa8e06SEd Maste fido_log_debug("%s: key_lookup", __func__);
7720afa8e06SEd Maste goto fail;
7730afa8e06SEd Maste }
7740afa8e06SEd Maste
7750afa8e06SEd Maste if (!found) {
7760afa8e06SEd Maste fido_log_debug("%s: not found", __func__);
7770afa8e06SEd Maste r = FIDO_ERR_CREDENTIAL_EXCLUDED;
7780afa8e06SEd Maste goto fail;
7790afa8e06SEd Maste }
7800afa8e06SEd Maste
7810afa8e06SEd Maste if (fido_blob_set(&fa->stmt[idx].id, key_id->ptr, key_id->len) < 0) {
7820afa8e06SEd Maste fido_log_debug("%s: fido_blob_set", __func__);
7830afa8e06SEd Maste r = FIDO_ERR_INTERNAL;
7840afa8e06SEd Maste goto fail;
7850afa8e06SEd Maste }
7860afa8e06SEd Maste
7870afa8e06SEd Maste if (fa->up == FIDO_OPT_FALSE) {
7880afa8e06SEd Maste fido_log_debug("%s: checking for key existence only", __func__);
7890afa8e06SEd Maste r = FIDO_ERR_USER_PRESENCE_REQUIRED;
7900afa8e06SEd Maste goto fail;
7910afa8e06SEd Maste }
7920afa8e06SEd Maste
7930afa8e06SEd Maste if ((r = do_auth(dev, &fa->cdh, fa->rp_id, key_id, &sig, &ad,
7940afa8e06SEd Maste ms)) != FIDO_OK) {
7950afa8e06SEd Maste fido_log_debug("%s: do_auth", __func__);
7960afa8e06SEd Maste goto fail;
7970afa8e06SEd Maste }
7980afa8e06SEd Maste
7990afa8e06SEd Maste if (fido_assert_set_authdata(fa, idx, ad.ptr, ad.len) != FIDO_OK ||
8000afa8e06SEd Maste fido_assert_set_sig(fa, idx, sig.ptr, sig.len) != FIDO_OK) {
8010afa8e06SEd Maste fido_log_debug("%s: fido_assert_set", __func__);
8020afa8e06SEd Maste r = FIDO_ERR_INTERNAL;
8030afa8e06SEd Maste goto fail;
8040afa8e06SEd Maste }
8050afa8e06SEd Maste
8060afa8e06SEd Maste r = FIDO_OK;
8070afa8e06SEd Maste fail:
8080afa8e06SEd Maste fido_blob_reset(&sig);
8090afa8e06SEd Maste fido_blob_reset(&ad);
8100afa8e06SEd Maste
8110afa8e06SEd Maste return (r);
8120afa8e06SEd Maste }
8130afa8e06SEd Maste
8140afa8e06SEd Maste int
u2f_authenticate(fido_dev_t * dev,fido_assert_t * fa,int * ms)815f540a430SEd Maste u2f_authenticate(fido_dev_t *dev, fido_assert_t *fa, int *ms)
8160afa8e06SEd Maste {
8170afa8e06SEd Maste size_t nfound = 0;
8180afa8e06SEd Maste size_t nauth_ok = 0;
8190afa8e06SEd Maste int r;
8200afa8e06SEd Maste
8210afa8e06SEd Maste if (fa->uv == FIDO_OPT_TRUE || fa->allow_list.ptr == NULL) {
8220afa8e06SEd Maste fido_log_debug("%s: uv=%d, allow_list=%p", __func__, fa->uv,
8230afa8e06SEd Maste (void *)fa->allow_list.ptr);
8240afa8e06SEd Maste return (FIDO_ERR_UNSUPPORTED_OPTION);
8250afa8e06SEd Maste }
8260afa8e06SEd Maste
8270afa8e06SEd Maste if ((r = fido_assert_set_count(fa, fa->allow_list.len)) != FIDO_OK) {
8280afa8e06SEd Maste fido_log_debug("%s: fido_assert_set_count", __func__);
8290afa8e06SEd Maste return (r);
8300afa8e06SEd Maste }
8310afa8e06SEd Maste
8320afa8e06SEd Maste for (size_t i = 0; i < fa->allow_list.len; i++) {
8330afa8e06SEd Maste switch ((r = u2f_authenticate_single(dev,
8340afa8e06SEd Maste &fa->allow_list.ptr[i], fa, nfound, ms))) {
8350afa8e06SEd Maste case FIDO_OK:
8360afa8e06SEd Maste nauth_ok++;
837*2ccfa855SEd Maste FALLTHROUGH
8380afa8e06SEd Maste case FIDO_ERR_USER_PRESENCE_REQUIRED:
8390afa8e06SEd Maste nfound++;
8400afa8e06SEd Maste break;
8410afa8e06SEd Maste default:
8420afa8e06SEd Maste if (r != FIDO_ERR_CREDENTIAL_EXCLUDED) {
8430afa8e06SEd Maste fido_log_debug("%s: u2f_authenticate_single",
8440afa8e06SEd Maste __func__);
8450afa8e06SEd Maste return (r);
8460afa8e06SEd Maste }
8470afa8e06SEd Maste /* ignore credentials that don't exist */
8480afa8e06SEd Maste }
8490afa8e06SEd Maste }
8500afa8e06SEd Maste
8510afa8e06SEd Maste fa->stmt_len = nfound;
8520afa8e06SEd Maste
8530afa8e06SEd Maste if (nfound == 0)
8540afa8e06SEd Maste return (FIDO_ERR_NO_CREDENTIALS);
8550afa8e06SEd Maste if (nauth_ok == 0)
8560afa8e06SEd Maste return (FIDO_ERR_USER_PRESENCE_REQUIRED);
8570afa8e06SEd Maste
8580afa8e06SEd Maste return (FIDO_OK);
8590afa8e06SEd Maste }
8600afa8e06SEd Maste
8610afa8e06SEd Maste int
u2f_get_touch_begin(fido_dev_t * dev,int * ms)862f540a430SEd Maste u2f_get_touch_begin(fido_dev_t *dev, int *ms)
8630afa8e06SEd Maste {
8640afa8e06SEd Maste iso7816_apdu_t *apdu = NULL;
8650afa8e06SEd Maste const char *clientdata = FIDO_DUMMY_CLIENTDATA;
8660afa8e06SEd Maste const char *rp_id = FIDO_DUMMY_RP_ID;
867*2ccfa855SEd Maste unsigned char *reply = NULL;
8680afa8e06SEd Maste unsigned char clientdata_hash[SHA256_DIGEST_LENGTH];
8690afa8e06SEd Maste unsigned char rp_id_hash[SHA256_DIGEST_LENGTH];
8700afa8e06SEd Maste int r;
8710afa8e06SEd Maste
8720afa8e06SEd Maste memset(&clientdata_hash, 0, sizeof(clientdata_hash));
8730afa8e06SEd Maste memset(&rp_id_hash, 0, sizeof(rp_id_hash));
8740afa8e06SEd Maste
8750afa8e06SEd Maste if (SHA256((const void *)clientdata, strlen(clientdata),
8760afa8e06SEd Maste clientdata_hash) != clientdata_hash || SHA256((const void *)rp_id,
8770afa8e06SEd Maste strlen(rp_id), rp_id_hash) != rp_id_hash) {
8780afa8e06SEd Maste fido_log_debug("%s: sha256", __func__);
8790afa8e06SEd Maste return (FIDO_ERR_INTERNAL);
8800afa8e06SEd Maste }
8810afa8e06SEd Maste
8820afa8e06SEd Maste if ((apdu = iso7816_new(0, U2F_CMD_REGISTER, 0, 2 *
8830afa8e06SEd Maste SHA256_DIGEST_LENGTH)) == NULL ||
8840afa8e06SEd Maste iso7816_add(apdu, clientdata_hash, sizeof(clientdata_hash)) < 0 ||
8850afa8e06SEd Maste iso7816_add(apdu, rp_id_hash, sizeof(rp_id_hash)) < 0) {
8860afa8e06SEd Maste fido_log_debug("%s: iso7816", __func__);
8870afa8e06SEd Maste r = FIDO_ERR_INTERNAL;
8880afa8e06SEd Maste goto fail;
8890afa8e06SEd Maste }
8900afa8e06SEd Maste
891*2ccfa855SEd Maste if ((reply = malloc(FIDO_MAXMSG)) == NULL) {
892*2ccfa855SEd Maste fido_log_debug("%s: malloc", __func__);
893*2ccfa855SEd Maste r = FIDO_ERR_INTERNAL;
894*2ccfa855SEd Maste goto fail;
895*2ccfa855SEd Maste }
896*2ccfa855SEd Maste
8970afa8e06SEd Maste if (dev->attr.flags & FIDO_CAP_WINK) {
898f540a430SEd Maste fido_tx(dev, CTAP_CMD_WINK, NULL, 0, ms);
899*2ccfa855SEd Maste fido_rx(dev, CTAP_CMD_WINK, reply, FIDO_MAXMSG, ms);
9000afa8e06SEd Maste }
9010afa8e06SEd Maste
9020afa8e06SEd Maste if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu),
903f540a430SEd Maste iso7816_len(apdu), ms) < 0) {
9040afa8e06SEd Maste fido_log_debug("%s: fido_tx", __func__);
9050afa8e06SEd Maste r = FIDO_ERR_TX;
9060afa8e06SEd Maste goto fail;
9070afa8e06SEd Maste }
9080afa8e06SEd Maste
9090afa8e06SEd Maste r = FIDO_OK;
9100afa8e06SEd Maste fail:
9110afa8e06SEd Maste iso7816_free(&apdu);
912*2ccfa855SEd Maste freezero(reply, FIDO_MAXMSG);
9130afa8e06SEd Maste
9140afa8e06SEd Maste return (r);
9150afa8e06SEd Maste }
9160afa8e06SEd Maste
9170afa8e06SEd Maste int
u2f_get_touch_status(fido_dev_t * dev,int * touched,int * ms)918f540a430SEd Maste u2f_get_touch_status(fido_dev_t *dev, int *touched, int *ms)
9190afa8e06SEd Maste {
920*2ccfa855SEd Maste unsigned char *reply;
9210afa8e06SEd Maste int reply_len;
9220afa8e06SEd Maste int r;
9230afa8e06SEd Maste
924*2ccfa855SEd Maste if ((reply = malloc(FIDO_MAXMSG)) == NULL) {
925*2ccfa855SEd Maste fido_log_debug("%s: malloc", __func__);
926*2ccfa855SEd Maste r = FIDO_ERR_INTERNAL;
927*2ccfa855SEd Maste goto out;
928*2ccfa855SEd Maste }
929*2ccfa855SEd Maste
930*2ccfa855SEd Maste if ((reply_len = fido_rx(dev, CTAP_CMD_MSG, reply, FIDO_MAXMSG,
9310afa8e06SEd Maste ms)) < 2) {
9320afa8e06SEd Maste fido_log_debug("%s: fido_rx", __func__);
933*2ccfa855SEd Maste r = FIDO_OK; /* ignore */
934*2ccfa855SEd Maste goto out;
9350afa8e06SEd Maste }
9360afa8e06SEd Maste
9370afa8e06SEd Maste switch ((reply[reply_len - 2] << 8) | reply[reply_len - 1]) {
9380afa8e06SEd Maste case SW_CONDITIONS_NOT_SATISFIED:
939f540a430SEd Maste if ((r = u2f_get_touch_begin(dev, ms)) != FIDO_OK) {
9400afa8e06SEd Maste fido_log_debug("%s: u2f_get_touch_begin", __func__);
941*2ccfa855SEd Maste goto out;
9420afa8e06SEd Maste }
9430afa8e06SEd Maste *touched = 0;
9440afa8e06SEd Maste break;
9450afa8e06SEd Maste case SW_NO_ERROR:
9460afa8e06SEd Maste *touched = 1;
9470afa8e06SEd Maste break;
9480afa8e06SEd Maste default:
9490afa8e06SEd Maste fido_log_debug("%s: unexpected sw", __func__);
950*2ccfa855SEd Maste r = FIDO_ERR_RX;
951*2ccfa855SEd Maste goto out;
9520afa8e06SEd Maste }
9530afa8e06SEd Maste
954*2ccfa855SEd Maste r = FIDO_OK;
955*2ccfa855SEd Maste out:
956*2ccfa855SEd Maste freezero(reply, FIDO_MAXMSG);
957*2ccfa855SEd Maste
958*2ccfa855SEd Maste return (r);
9590afa8e06SEd Maste }
960