10afa8e06SEd Maste /* 2*60a517b6SEd Maste * Copyright (c) 2018-2023 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. 52ccfa855SEd Maste * SPDX-License-Identifier: BSD-2-Clause 60afa8e06SEd Maste */ 70afa8e06SEd Maste 80afa8e06SEd Maste #include <openssl/sha.h> 90afa8e06SEd Maste 100afa8e06SEd Maste #include "fido.h" 110afa8e06SEd Maste #include "fido/es256.h" 120afa8e06SEd Maste #include "fido/rs256.h" 130afa8e06SEd Maste #include "fido/eddsa.h" 140afa8e06SEd Maste 150afa8e06SEd Maste static int 160afa8e06SEd Maste adjust_assert_count(const cbor_item_t *key, const cbor_item_t *val, void *arg) 170afa8e06SEd Maste { 180afa8e06SEd Maste fido_assert_t *assert = arg; 190afa8e06SEd Maste uint64_t n; 200afa8e06SEd Maste 210afa8e06SEd Maste /* numberOfCredentials; see section 6.2 */ 220afa8e06SEd Maste if (cbor_isa_uint(key) == false || 230afa8e06SEd Maste cbor_int_get_width(key) != CBOR_INT_8 || 240afa8e06SEd Maste cbor_get_uint8(key) != 5) { 250afa8e06SEd Maste fido_log_debug("%s: cbor_type", __func__); 260afa8e06SEd Maste return (0); /* ignore */ 270afa8e06SEd Maste } 280afa8e06SEd Maste 290afa8e06SEd Maste if (cbor_decode_uint64(val, &n) < 0 || n > SIZE_MAX) { 300afa8e06SEd Maste fido_log_debug("%s: cbor_decode_uint64", __func__); 310afa8e06SEd Maste return (-1); 320afa8e06SEd Maste } 330afa8e06SEd Maste 340afa8e06SEd Maste if (assert->stmt_len != 0 || assert->stmt_cnt != 1 || 350afa8e06SEd Maste (size_t)n < assert->stmt_cnt) { 360afa8e06SEd Maste fido_log_debug("%s: stmt_len=%zu, stmt_cnt=%zu, n=%zu", 370afa8e06SEd Maste __func__, assert->stmt_len, assert->stmt_cnt, (size_t)n); 380afa8e06SEd Maste return (-1); 390afa8e06SEd Maste } 400afa8e06SEd Maste 410afa8e06SEd Maste if (fido_assert_set_count(assert, (size_t)n) != FIDO_OK) { 420afa8e06SEd Maste fido_log_debug("%s: fido_assert_set_count", __func__); 430afa8e06SEd Maste return (-1); 440afa8e06SEd Maste } 450afa8e06SEd Maste 460afa8e06SEd Maste assert->stmt_len = 0; /* XXX */ 470afa8e06SEd Maste 480afa8e06SEd Maste return (0); 490afa8e06SEd Maste } 500afa8e06SEd Maste 510afa8e06SEd Maste static int 520afa8e06SEd Maste parse_assert_reply(const cbor_item_t *key, const cbor_item_t *val, void *arg) 530afa8e06SEd Maste { 540afa8e06SEd Maste fido_assert_stmt *stmt = arg; 550afa8e06SEd Maste 560afa8e06SEd Maste if (cbor_isa_uint(key) == false || 570afa8e06SEd Maste cbor_int_get_width(key) != CBOR_INT_8) { 580afa8e06SEd Maste fido_log_debug("%s: cbor type", __func__); 590afa8e06SEd Maste return (0); /* ignore */ 600afa8e06SEd Maste } 610afa8e06SEd Maste 620afa8e06SEd Maste switch (cbor_get_uint8(key)) { 630afa8e06SEd Maste case 1: /* credential id */ 640afa8e06SEd Maste return (cbor_decode_cred_id(val, &stmt->id)); 650afa8e06SEd Maste case 2: /* authdata */ 66*60a517b6SEd Maste if (fido_blob_decode(val, &stmt->authdata_raw) < 0) { 67*60a517b6SEd Maste fido_log_debug("%s: fido_blob_decode", __func__); 68*60a517b6SEd Maste return (-1); 69*60a517b6SEd Maste } 700afa8e06SEd Maste return (cbor_decode_assert_authdata(val, &stmt->authdata_cbor, 710afa8e06SEd Maste &stmt->authdata, &stmt->authdata_ext)); 720afa8e06SEd Maste case 3: /* signature */ 730afa8e06SEd Maste return (fido_blob_decode(val, &stmt->sig)); 740afa8e06SEd Maste case 4: /* user attributes */ 750afa8e06SEd Maste return (cbor_decode_user(val, &stmt->user)); 760afa8e06SEd Maste case 7: /* large blob key */ 770afa8e06SEd Maste return (fido_blob_decode(val, &stmt->largeblob_key)); 780afa8e06SEd Maste default: /* ignore */ 790afa8e06SEd Maste fido_log_debug("%s: cbor type", __func__); 800afa8e06SEd Maste return (0); 810afa8e06SEd Maste } 820afa8e06SEd Maste } 830afa8e06SEd Maste 840afa8e06SEd Maste static int 850afa8e06SEd Maste fido_dev_get_assert_tx(fido_dev_t *dev, fido_assert_t *assert, 86f540a430SEd Maste const es256_pk_t *pk, const fido_blob_t *ecdh, const char *pin, int *ms) 870afa8e06SEd Maste { 880afa8e06SEd Maste fido_blob_t f; 890afa8e06SEd Maste fido_opt_t uv = assert->uv; 900afa8e06SEd Maste cbor_item_t *argv[7]; 910afa8e06SEd Maste const uint8_t cmd = CTAP_CBOR_ASSERT; 920afa8e06SEd Maste int r; 930afa8e06SEd Maste 940afa8e06SEd Maste memset(argv, 0, sizeof(argv)); 950afa8e06SEd Maste memset(&f, 0, sizeof(f)); 960afa8e06SEd Maste 970afa8e06SEd Maste /* do we have everything we need? */ 980afa8e06SEd Maste if (assert->rp_id == NULL || assert->cdh.ptr == NULL) { 990afa8e06SEd Maste fido_log_debug("%s: rp_id=%p, cdh.ptr=%p", __func__, 1000afa8e06SEd Maste (void *)assert->rp_id, (void *)assert->cdh.ptr); 1010afa8e06SEd Maste r = FIDO_ERR_INVALID_ARGUMENT; 1020afa8e06SEd Maste goto fail; 1030afa8e06SEd Maste } 1040afa8e06SEd Maste 1050afa8e06SEd Maste if ((argv[0] = cbor_build_string(assert->rp_id)) == NULL || 1060afa8e06SEd Maste (argv[1] = fido_blob_encode(&assert->cdh)) == NULL) { 1070afa8e06SEd Maste fido_log_debug("%s: cbor encode", __func__); 1080afa8e06SEd Maste r = FIDO_ERR_INTERNAL; 1090afa8e06SEd Maste goto fail; 1100afa8e06SEd Maste } 1110afa8e06SEd Maste 1120afa8e06SEd Maste /* allowed credentials */ 1130afa8e06SEd Maste if (assert->allow_list.len) { 1140afa8e06SEd Maste const fido_blob_array_t *cl = &assert->allow_list; 1150afa8e06SEd Maste if ((argv[2] = cbor_encode_pubkey_list(cl)) == NULL) { 1160afa8e06SEd Maste fido_log_debug("%s: cbor_encode_pubkey_list", __func__); 1170afa8e06SEd Maste r = FIDO_ERR_INTERNAL; 1180afa8e06SEd Maste goto fail; 1190afa8e06SEd Maste } 1200afa8e06SEd Maste } 1210afa8e06SEd Maste 1220afa8e06SEd Maste if (assert->ext.mask) 1230afa8e06SEd Maste if ((argv[3] = cbor_encode_assert_ext(dev, &assert->ext, ecdh, 1240afa8e06SEd Maste pk)) == NULL) { 1250afa8e06SEd Maste fido_log_debug("%s: cbor_encode_assert_ext", __func__); 1260afa8e06SEd Maste r = FIDO_ERR_INTERNAL; 1270afa8e06SEd Maste goto fail; 1280afa8e06SEd Maste } 1290afa8e06SEd Maste 1300afa8e06SEd Maste /* user verification */ 1310afa8e06SEd Maste if (pin != NULL || (uv == FIDO_OPT_TRUE && 1320afa8e06SEd Maste fido_dev_supports_permissions(dev))) { 1330afa8e06SEd Maste if ((r = cbor_add_uv_params(dev, cmd, &assert->cdh, pk, ecdh, 134f540a430SEd Maste pin, assert->rp_id, &argv[5], &argv[6], ms)) != FIDO_OK) { 1350afa8e06SEd Maste fido_log_debug("%s: cbor_add_uv_params", __func__); 1360afa8e06SEd Maste goto fail; 1370afa8e06SEd Maste } 1380afa8e06SEd Maste uv = FIDO_OPT_OMIT; 1390afa8e06SEd Maste } 1400afa8e06SEd Maste 1410afa8e06SEd Maste /* options */ 1420afa8e06SEd Maste if (assert->up != FIDO_OPT_OMIT || uv != FIDO_OPT_OMIT) 1430afa8e06SEd Maste if ((argv[4] = cbor_encode_assert_opt(assert->up, uv)) == NULL) { 1440afa8e06SEd Maste fido_log_debug("%s: cbor_encode_assert_opt", __func__); 1450afa8e06SEd Maste r = FIDO_ERR_INTERNAL; 1460afa8e06SEd Maste goto fail; 1470afa8e06SEd Maste } 1480afa8e06SEd Maste 1490afa8e06SEd Maste /* frame and transmit */ 1500afa8e06SEd Maste if (cbor_build_frame(cmd, argv, nitems(argv), &f) < 0 || 151f540a430SEd Maste fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) { 1520afa8e06SEd Maste fido_log_debug("%s: fido_tx", __func__); 1530afa8e06SEd Maste r = FIDO_ERR_TX; 1540afa8e06SEd Maste goto fail; 1550afa8e06SEd Maste } 1560afa8e06SEd Maste 1570afa8e06SEd Maste r = FIDO_OK; 1580afa8e06SEd Maste fail: 1590afa8e06SEd Maste cbor_vector_free(argv, nitems(argv)); 1600afa8e06SEd Maste free(f.ptr); 1610afa8e06SEd Maste 1620afa8e06SEd Maste return (r); 1630afa8e06SEd Maste } 1640afa8e06SEd Maste 1650afa8e06SEd Maste static int 166f540a430SEd Maste fido_dev_get_assert_rx(fido_dev_t *dev, fido_assert_t *assert, int *ms) 1670afa8e06SEd Maste { 1682ccfa855SEd Maste unsigned char *msg; 1692ccfa855SEd Maste int msglen; 1700afa8e06SEd Maste int r; 1710afa8e06SEd Maste 1720afa8e06SEd Maste fido_assert_reset_rx(assert); 1730afa8e06SEd Maste 1742ccfa855SEd Maste if ((msg = malloc(FIDO_MAXMSG)) == NULL) { 1752ccfa855SEd Maste r = FIDO_ERR_INTERNAL; 1762ccfa855SEd Maste goto out; 1772ccfa855SEd Maste } 1782ccfa855SEd Maste 1792ccfa855SEd Maste if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) { 1800afa8e06SEd Maste fido_log_debug("%s: fido_rx", __func__); 1812ccfa855SEd Maste r = FIDO_ERR_RX; 1822ccfa855SEd Maste goto out; 1830afa8e06SEd Maste } 1840afa8e06SEd Maste 1850afa8e06SEd Maste /* start with room for a single assertion */ 1862ccfa855SEd Maste if ((assert->stmt = calloc(1, sizeof(fido_assert_stmt))) == NULL) { 1872ccfa855SEd Maste r = FIDO_ERR_INTERNAL; 1882ccfa855SEd Maste goto out; 1892ccfa855SEd Maste } 1900afa8e06SEd Maste assert->stmt_len = 0; 1910afa8e06SEd Maste assert->stmt_cnt = 1; 1920afa8e06SEd Maste 1930afa8e06SEd Maste /* adjust as needed */ 1942ccfa855SEd Maste if ((r = cbor_parse_reply(msg, (size_t)msglen, assert, 1950afa8e06SEd Maste adjust_assert_count)) != FIDO_OK) { 1960afa8e06SEd Maste fido_log_debug("%s: adjust_assert_count", __func__); 1972ccfa855SEd Maste goto out; 1980afa8e06SEd Maste } 1990afa8e06SEd Maste 2000afa8e06SEd Maste /* parse the first assertion */ 2012ccfa855SEd Maste if ((r = cbor_parse_reply(msg, (size_t)msglen, &assert->stmt[0], 2022ccfa855SEd Maste parse_assert_reply)) != FIDO_OK) { 2030afa8e06SEd Maste fido_log_debug("%s: parse_assert_reply", __func__); 2042ccfa855SEd Maste goto out; 2050afa8e06SEd Maste } 2062ccfa855SEd Maste assert->stmt_len = 1; 2070afa8e06SEd Maste 2082ccfa855SEd Maste r = FIDO_OK; 2092ccfa855SEd Maste out: 2102ccfa855SEd Maste freezero(msg, FIDO_MAXMSG); 2110afa8e06SEd Maste 2122ccfa855SEd Maste return (r); 2130afa8e06SEd Maste } 2140afa8e06SEd Maste 2150afa8e06SEd Maste static int 216f540a430SEd Maste fido_get_next_assert_tx(fido_dev_t *dev, int *ms) 2170afa8e06SEd Maste { 2180afa8e06SEd Maste const unsigned char cbor[] = { CTAP_CBOR_NEXT_ASSERT }; 2190afa8e06SEd Maste 220f540a430SEd Maste if (fido_tx(dev, CTAP_CMD_CBOR, cbor, sizeof(cbor), ms) < 0) { 2210afa8e06SEd Maste fido_log_debug("%s: fido_tx", __func__); 2220afa8e06SEd Maste return (FIDO_ERR_TX); 2230afa8e06SEd Maste } 2240afa8e06SEd Maste 2250afa8e06SEd Maste return (FIDO_OK); 2260afa8e06SEd Maste } 2270afa8e06SEd Maste 2280afa8e06SEd Maste static int 229f540a430SEd Maste fido_get_next_assert_rx(fido_dev_t *dev, fido_assert_t *assert, int *ms) 2300afa8e06SEd Maste { 2312ccfa855SEd Maste unsigned char *msg; 2322ccfa855SEd Maste int msglen; 2330afa8e06SEd Maste int r; 2340afa8e06SEd Maste 2352ccfa855SEd Maste if ((msg = malloc(FIDO_MAXMSG)) == NULL) { 2362ccfa855SEd Maste r = FIDO_ERR_INTERNAL; 2372ccfa855SEd Maste goto out; 2382ccfa855SEd Maste } 2392ccfa855SEd Maste 2402ccfa855SEd Maste if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) { 2410afa8e06SEd Maste fido_log_debug("%s: fido_rx", __func__); 2422ccfa855SEd Maste r = FIDO_ERR_RX; 2432ccfa855SEd Maste goto out; 2440afa8e06SEd Maste } 2450afa8e06SEd Maste 2460afa8e06SEd Maste /* sanity check */ 2470afa8e06SEd Maste if (assert->stmt_len >= assert->stmt_cnt) { 2480afa8e06SEd Maste fido_log_debug("%s: stmt_len=%zu, stmt_cnt=%zu", __func__, 2490afa8e06SEd Maste assert->stmt_len, assert->stmt_cnt); 2502ccfa855SEd Maste r = FIDO_ERR_INTERNAL; 2512ccfa855SEd Maste goto out; 2520afa8e06SEd Maste } 2530afa8e06SEd Maste 2542ccfa855SEd Maste if ((r = cbor_parse_reply(msg, (size_t)msglen, 2550afa8e06SEd Maste &assert->stmt[assert->stmt_len], parse_assert_reply)) != FIDO_OK) { 2560afa8e06SEd Maste fido_log_debug("%s: parse_assert_reply", __func__); 2572ccfa855SEd Maste goto out; 2580afa8e06SEd Maste } 2590afa8e06SEd Maste 2602ccfa855SEd Maste r = FIDO_OK; 2612ccfa855SEd Maste out: 2622ccfa855SEd Maste freezero(msg, FIDO_MAXMSG); 2632ccfa855SEd Maste 2642ccfa855SEd Maste return (r); 2650afa8e06SEd Maste } 2660afa8e06SEd Maste 2670afa8e06SEd Maste static int 2680afa8e06SEd Maste fido_dev_get_assert_wait(fido_dev_t *dev, fido_assert_t *assert, 269f540a430SEd Maste const es256_pk_t *pk, const fido_blob_t *ecdh, const char *pin, int *ms) 2700afa8e06SEd Maste { 2710afa8e06SEd Maste int r; 2720afa8e06SEd Maste 273f540a430SEd Maste if ((r = fido_dev_get_assert_tx(dev, assert, pk, ecdh, pin, 274f540a430SEd Maste ms)) != FIDO_OK || 2750afa8e06SEd Maste (r = fido_dev_get_assert_rx(dev, assert, ms)) != FIDO_OK) 2760afa8e06SEd Maste return (r); 2770afa8e06SEd Maste 2780afa8e06SEd Maste while (assert->stmt_len < assert->stmt_cnt) { 279f540a430SEd Maste if ((r = fido_get_next_assert_tx(dev, ms)) != FIDO_OK || 2800afa8e06SEd Maste (r = fido_get_next_assert_rx(dev, assert, ms)) != FIDO_OK) 2810afa8e06SEd Maste return (r); 2820afa8e06SEd Maste assert->stmt_len++; 2830afa8e06SEd Maste } 2840afa8e06SEd Maste 2850afa8e06SEd Maste return (FIDO_OK); 2860afa8e06SEd Maste } 2870afa8e06SEd Maste 2880afa8e06SEd Maste static int 2890afa8e06SEd Maste decrypt_hmac_secrets(const fido_dev_t *dev, fido_assert_t *assert, 2900afa8e06SEd Maste const fido_blob_t *key) 2910afa8e06SEd Maste { 2920afa8e06SEd Maste for (size_t i = 0; i < assert->stmt_cnt; i++) { 2930afa8e06SEd Maste fido_assert_stmt *stmt = &assert->stmt[i]; 2940afa8e06SEd Maste if (stmt->authdata_ext.hmac_secret_enc.ptr != NULL) { 2950afa8e06SEd Maste if (aes256_cbc_dec(dev, key, 2960afa8e06SEd Maste &stmt->authdata_ext.hmac_secret_enc, 2970afa8e06SEd Maste &stmt->hmac_secret) < 0) { 2980afa8e06SEd Maste fido_log_debug("%s: aes256_cbc_dec %zu", 2990afa8e06SEd Maste __func__, i); 3000afa8e06SEd Maste return (-1); 3010afa8e06SEd Maste } 3020afa8e06SEd Maste } 3030afa8e06SEd Maste } 3040afa8e06SEd Maste 3050afa8e06SEd Maste return (0); 3060afa8e06SEd Maste } 3070afa8e06SEd Maste 3080afa8e06SEd Maste int 3090afa8e06SEd Maste fido_dev_get_assert(fido_dev_t *dev, fido_assert_t *assert, const char *pin) 3100afa8e06SEd Maste { 3110afa8e06SEd Maste fido_blob_t *ecdh = NULL; 3120afa8e06SEd Maste es256_pk_t *pk = NULL; 313f540a430SEd Maste int ms = dev->timeout_ms; 3140afa8e06SEd Maste int r; 3150afa8e06SEd Maste 3160afa8e06SEd Maste #ifdef USE_WINHELLO 3170afa8e06SEd Maste if (dev->flags & FIDO_DEV_WINHELLO) 318f540a430SEd Maste return (fido_winhello_get_assert(dev, assert, pin, ms)); 3190afa8e06SEd Maste #endif 3200afa8e06SEd Maste 3210afa8e06SEd Maste if (assert->rp_id == NULL || assert->cdh.ptr == NULL) { 3220afa8e06SEd Maste fido_log_debug("%s: rp_id=%p, cdh.ptr=%p", __func__, 3230afa8e06SEd Maste (void *)assert->rp_id, (void *)assert->cdh.ptr); 3240afa8e06SEd Maste return (FIDO_ERR_INVALID_ARGUMENT); 3250afa8e06SEd Maste } 3260afa8e06SEd Maste 3270afa8e06SEd Maste if (fido_dev_is_fido2(dev) == false) { 3280afa8e06SEd Maste if (pin != NULL || assert->ext.mask != 0) 3290afa8e06SEd Maste return (FIDO_ERR_UNSUPPORTED_OPTION); 330f540a430SEd Maste return (u2f_authenticate(dev, assert, &ms)); 3310afa8e06SEd Maste } 3320afa8e06SEd Maste 3330afa8e06SEd Maste if (pin != NULL || (assert->uv == FIDO_OPT_TRUE && 3340afa8e06SEd Maste fido_dev_supports_permissions(dev)) || 3350afa8e06SEd Maste (assert->ext.mask & FIDO_EXT_HMAC_SECRET)) { 336f540a430SEd Maste if ((r = fido_do_ecdh(dev, &pk, &ecdh, &ms)) != FIDO_OK) { 3370afa8e06SEd Maste fido_log_debug("%s: fido_do_ecdh", __func__); 3380afa8e06SEd Maste goto fail; 3390afa8e06SEd Maste } 3400afa8e06SEd Maste } 3410afa8e06SEd Maste 342f540a430SEd Maste r = fido_dev_get_assert_wait(dev, assert, pk, ecdh, pin, &ms); 3430afa8e06SEd Maste if (r == FIDO_OK && (assert->ext.mask & FIDO_EXT_HMAC_SECRET)) 3440afa8e06SEd Maste if (decrypt_hmac_secrets(dev, assert, ecdh) < 0) { 3450afa8e06SEd Maste fido_log_debug("%s: decrypt_hmac_secrets", __func__); 3460afa8e06SEd Maste r = FIDO_ERR_INTERNAL; 3470afa8e06SEd Maste goto fail; 3480afa8e06SEd Maste } 3490afa8e06SEd Maste 3500afa8e06SEd Maste fail: 3510afa8e06SEd Maste es256_pk_free(&pk); 3520afa8e06SEd Maste fido_blob_free(&ecdh); 3530afa8e06SEd Maste 3540afa8e06SEd Maste return (r); 3550afa8e06SEd Maste } 3560afa8e06SEd Maste 3570afa8e06SEd Maste int 3580afa8e06SEd Maste fido_check_flags(uint8_t flags, fido_opt_t up, fido_opt_t uv) 3590afa8e06SEd Maste { 3600afa8e06SEd Maste fido_log_debug("%s: flags=%02x", __func__, flags); 3610afa8e06SEd Maste fido_log_debug("%s: up=%d, uv=%d", __func__, up, uv); 3620afa8e06SEd Maste 3630afa8e06SEd Maste if (up == FIDO_OPT_TRUE && 3640afa8e06SEd Maste (flags & CTAP_AUTHDATA_USER_PRESENT) == 0) { 3650afa8e06SEd Maste fido_log_debug("%s: CTAP_AUTHDATA_USER_PRESENT", __func__); 3660afa8e06SEd Maste return (-1); /* user not present */ 3670afa8e06SEd Maste } 3680afa8e06SEd Maste 3690afa8e06SEd Maste if (uv == FIDO_OPT_TRUE && 3700afa8e06SEd Maste (flags & CTAP_AUTHDATA_USER_VERIFIED) == 0) { 3710afa8e06SEd Maste fido_log_debug("%s: CTAP_AUTHDATA_USER_VERIFIED", __func__); 3720afa8e06SEd Maste return (-1); /* user not verified */ 3730afa8e06SEd Maste } 3740afa8e06SEd Maste 3750afa8e06SEd Maste return (0); 3760afa8e06SEd Maste } 3770afa8e06SEd Maste 3780afa8e06SEd Maste static int 3790afa8e06SEd Maste check_extensions(int authdata_ext, int ext) 3800afa8e06SEd Maste { 3810afa8e06SEd Maste /* XXX: largeBlobKey is not part of extensions map */ 3820afa8e06SEd Maste ext &= ~FIDO_EXT_LARGEBLOB_KEY; 3830afa8e06SEd Maste if (authdata_ext != ext) { 3840afa8e06SEd Maste fido_log_debug("%s: authdata_ext=0x%x != ext=0x%x", __func__, 3850afa8e06SEd Maste authdata_ext, ext); 3860afa8e06SEd Maste return (-1); 3870afa8e06SEd Maste } 3880afa8e06SEd Maste 3890afa8e06SEd Maste return (0); 3900afa8e06SEd Maste } 3910afa8e06SEd Maste 3922ccfa855SEd Maste static int 3932ccfa855SEd Maste get_es256_hash(fido_blob_t *dgst, const fido_blob_t *clientdata, 3942ccfa855SEd Maste const fido_blob_t *authdata) 3952ccfa855SEd Maste { 3962ccfa855SEd Maste const EVP_MD *md; 3972ccfa855SEd Maste EVP_MD_CTX *ctx = NULL; 3982ccfa855SEd Maste 3992ccfa855SEd Maste if (dgst->len < SHA256_DIGEST_LENGTH || 4002ccfa855SEd Maste (md = EVP_sha256()) == NULL || 4012ccfa855SEd Maste (ctx = EVP_MD_CTX_new()) == NULL || 4022ccfa855SEd Maste EVP_DigestInit_ex(ctx, md, NULL) != 1 || 4032ccfa855SEd Maste EVP_DigestUpdate(ctx, authdata->ptr, authdata->len) != 1 || 4042ccfa855SEd Maste EVP_DigestUpdate(ctx, clientdata->ptr, clientdata->len) != 1 || 4052ccfa855SEd Maste EVP_DigestFinal_ex(ctx, dgst->ptr, NULL) != 1) { 4062ccfa855SEd Maste EVP_MD_CTX_free(ctx); 4072ccfa855SEd Maste return (-1); 4082ccfa855SEd Maste } 4092ccfa855SEd Maste dgst->len = SHA256_DIGEST_LENGTH; 4102ccfa855SEd Maste 4112ccfa855SEd Maste EVP_MD_CTX_free(ctx); 4122ccfa855SEd Maste 4132ccfa855SEd Maste return (0); 4142ccfa855SEd Maste } 4152ccfa855SEd Maste 4162ccfa855SEd Maste static int 4172ccfa855SEd Maste get_es384_hash(fido_blob_t *dgst, const fido_blob_t *clientdata, 4182ccfa855SEd Maste const fido_blob_t *authdata) 4192ccfa855SEd Maste { 4202ccfa855SEd Maste const EVP_MD *md; 4212ccfa855SEd Maste EVP_MD_CTX *ctx = NULL; 4222ccfa855SEd Maste 4232ccfa855SEd Maste if (dgst->len < SHA384_DIGEST_LENGTH || 4242ccfa855SEd Maste (md = EVP_sha384()) == NULL || 4252ccfa855SEd Maste (ctx = EVP_MD_CTX_new()) == NULL || 4262ccfa855SEd Maste EVP_DigestInit_ex(ctx, md, NULL) != 1 || 4272ccfa855SEd Maste EVP_DigestUpdate(ctx, authdata->ptr, authdata->len) != 1 || 4282ccfa855SEd Maste EVP_DigestUpdate(ctx, clientdata->ptr, clientdata->len) != 1 || 4292ccfa855SEd Maste EVP_DigestFinal_ex(ctx, dgst->ptr, NULL) != 1) { 4302ccfa855SEd Maste EVP_MD_CTX_free(ctx); 4312ccfa855SEd Maste return (-1); 4322ccfa855SEd Maste } 4332ccfa855SEd Maste dgst->len = SHA384_DIGEST_LENGTH; 4342ccfa855SEd Maste 4352ccfa855SEd Maste EVP_MD_CTX_free(ctx); 4362ccfa855SEd Maste 4372ccfa855SEd Maste return (0); 4382ccfa855SEd Maste } 4392ccfa855SEd Maste 4402ccfa855SEd Maste static int 4412ccfa855SEd Maste get_eddsa_hash(fido_blob_t *dgst, const fido_blob_t *clientdata, 4422ccfa855SEd Maste const fido_blob_t *authdata) 4432ccfa855SEd Maste { 4442ccfa855SEd Maste if (SIZE_MAX - authdata->len < clientdata->len || 4452ccfa855SEd Maste dgst->len < authdata->len + clientdata->len) 4462ccfa855SEd Maste return (-1); 4472ccfa855SEd Maste 4482ccfa855SEd Maste memcpy(dgst->ptr, authdata->ptr, authdata->len); 4492ccfa855SEd Maste memcpy(dgst->ptr + authdata->len, clientdata->ptr, clientdata->len); 4502ccfa855SEd Maste dgst->len = authdata->len + clientdata->len; 4512ccfa855SEd Maste 4522ccfa855SEd Maste return (0); 4532ccfa855SEd Maste } 4542ccfa855SEd Maste 4550afa8e06SEd Maste int 4560afa8e06SEd Maste fido_get_signed_hash(int cose_alg, fido_blob_t *dgst, 4570afa8e06SEd Maste const fido_blob_t *clientdata, const fido_blob_t *authdata_cbor) 4580afa8e06SEd Maste { 4590afa8e06SEd Maste cbor_item_t *item = NULL; 4602ccfa855SEd Maste fido_blob_t authdata; 4610afa8e06SEd Maste struct cbor_load_result cbor; 4620afa8e06SEd Maste int ok = -1; 4630afa8e06SEd Maste 4642ccfa855SEd Maste fido_log_debug("%s: cose_alg=%d", __func__, cose_alg); 4652ccfa855SEd Maste 4660afa8e06SEd Maste if ((item = cbor_load(authdata_cbor->ptr, authdata_cbor->len, 4670afa8e06SEd Maste &cbor)) == NULL || cbor_isa_bytestring(item) == false || 4680afa8e06SEd Maste cbor_bytestring_is_definite(item) == false) { 4690afa8e06SEd Maste fido_log_debug("%s: authdata", __func__); 4700afa8e06SEd Maste goto fail; 4710afa8e06SEd Maste } 4722ccfa855SEd Maste authdata.ptr = cbor_bytestring_handle(item); 4732ccfa855SEd Maste authdata.len = cbor_bytestring_length(item); 4740afa8e06SEd Maste 4752ccfa855SEd Maste switch (cose_alg) { 4762ccfa855SEd Maste case COSE_ES256: 4772ccfa855SEd Maste case COSE_RS256: 4782ccfa855SEd Maste ok = get_es256_hash(dgst, clientdata, &authdata); 4792ccfa855SEd Maste break; 4802ccfa855SEd Maste case COSE_ES384: 4812ccfa855SEd Maste ok = get_es384_hash(dgst, clientdata, &authdata); 4822ccfa855SEd Maste break; 4832ccfa855SEd Maste case COSE_EDDSA: 4842ccfa855SEd Maste ok = get_eddsa_hash(dgst, clientdata, &authdata); 4852ccfa855SEd Maste break; 4862ccfa855SEd Maste default: 4872ccfa855SEd Maste fido_log_debug("%s: unknown cose_alg", __func__); 4882ccfa855SEd Maste break; 4890afa8e06SEd Maste } 4900afa8e06SEd Maste fail: 4910afa8e06SEd Maste if (item != NULL) 4920afa8e06SEd Maste cbor_decref(&item); 4930afa8e06SEd Maste 4940afa8e06SEd Maste return (ok); 4950afa8e06SEd Maste } 4960afa8e06SEd Maste 4970afa8e06SEd Maste int 4980afa8e06SEd Maste fido_assert_verify(const fido_assert_t *assert, size_t idx, int cose_alg, 4990afa8e06SEd Maste const void *pk) 5000afa8e06SEd Maste { 5010afa8e06SEd Maste unsigned char buf[1024]; /* XXX */ 5020afa8e06SEd Maste fido_blob_t dgst; 5030afa8e06SEd Maste const fido_assert_stmt *stmt = NULL; 5040afa8e06SEd Maste int ok = -1; 5050afa8e06SEd Maste int r; 5060afa8e06SEd Maste 5070afa8e06SEd Maste dgst.ptr = buf; 5080afa8e06SEd Maste dgst.len = sizeof(buf); 5090afa8e06SEd Maste 5100afa8e06SEd Maste if (idx >= assert->stmt_len || pk == NULL) { 5110afa8e06SEd Maste r = FIDO_ERR_INVALID_ARGUMENT; 5120afa8e06SEd Maste goto out; 5130afa8e06SEd Maste } 5140afa8e06SEd Maste 5150afa8e06SEd Maste stmt = &assert->stmt[idx]; 5160afa8e06SEd Maste 5170afa8e06SEd Maste /* do we have everything we need? */ 5180afa8e06SEd Maste if (assert->cdh.ptr == NULL || assert->rp_id == NULL || 5190afa8e06SEd Maste stmt->authdata_cbor.ptr == NULL || stmt->sig.ptr == NULL) { 5200afa8e06SEd Maste fido_log_debug("%s: cdh=%p, rp_id=%s, authdata=%p, sig=%p", 5210afa8e06SEd Maste __func__, (void *)assert->cdh.ptr, assert->rp_id, 5220afa8e06SEd Maste (void *)stmt->authdata_cbor.ptr, (void *)stmt->sig.ptr); 5230afa8e06SEd Maste r = FIDO_ERR_INVALID_ARGUMENT; 5240afa8e06SEd Maste goto out; 5250afa8e06SEd Maste } 5260afa8e06SEd Maste 5270afa8e06SEd Maste if (fido_check_flags(stmt->authdata.flags, assert->up, 5280afa8e06SEd Maste assert->uv) < 0) { 5290afa8e06SEd Maste fido_log_debug("%s: fido_check_flags", __func__); 5300afa8e06SEd Maste r = FIDO_ERR_INVALID_PARAM; 5310afa8e06SEd Maste goto out; 5320afa8e06SEd Maste } 5330afa8e06SEd Maste 5340afa8e06SEd Maste if (check_extensions(stmt->authdata_ext.mask, assert->ext.mask) < 0) { 5350afa8e06SEd Maste fido_log_debug("%s: check_extensions", __func__); 5360afa8e06SEd Maste r = FIDO_ERR_INVALID_PARAM; 5370afa8e06SEd Maste goto out; 5380afa8e06SEd Maste } 5390afa8e06SEd Maste 5400afa8e06SEd Maste if (fido_check_rp_id(assert->rp_id, stmt->authdata.rp_id_hash) != 0) { 5410afa8e06SEd Maste fido_log_debug("%s: fido_check_rp_id", __func__); 5420afa8e06SEd Maste r = FIDO_ERR_INVALID_PARAM; 5430afa8e06SEd Maste goto out; 5440afa8e06SEd Maste } 5450afa8e06SEd Maste 5460afa8e06SEd Maste if (fido_get_signed_hash(cose_alg, &dgst, &assert->cdh, 5470afa8e06SEd Maste &stmt->authdata_cbor) < 0) { 5480afa8e06SEd Maste fido_log_debug("%s: fido_get_signed_hash", __func__); 5490afa8e06SEd Maste r = FIDO_ERR_INTERNAL; 5500afa8e06SEd Maste goto out; 5510afa8e06SEd Maste } 5520afa8e06SEd Maste 5530afa8e06SEd Maste switch (cose_alg) { 5540afa8e06SEd Maste case COSE_ES256: 555f540a430SEd Maste ok = es256_pk_verify_sig(&dgst, pk, &stmt->sig); 5560afa8e06SEd Maste break; 5572ccfa855SEd Maste case COSE_ES384: 5582ccfa855SEd Maste ok = es384_pk_verify_sig(&dgst, pk, &stmt->sig); 5592ccfa855SEd Maste break; 5600afa8e06SEd Maste case COSE_RS256: 561f540a430SEd Maste ok = rs256_pk_verify_sig(&dgst, pk, &stmt->sig); 5620afa8e06SEd Maste break; 5630afa8e06SEd Maste case COSE_EDDSA: 564f540a430SEd Maste ok = eddsa_pk_verify_sig(&dgst, pk, &stmt->sig); 5650afa8e06SEd Maste break; 5660afa8e06SEd Maste default: 5670afa8e06SEd Maste fido_log_debug("%s: unsupported cose_alg %d", __func__, 5680afa8e06SEd Maste cose_alg); 5690afa8e06SEd Maste r = FIDO_ERR_UNSUPPORTED_OPTION; 5700afa8e06SEd Maste goto out; 5710afa8e06SEd Maste } 5720afa8e06SEd Maste 5730afa8e06SEd Maste if (ok < 0) 5740afa8e06SEd Maste r = FIDO_ERR_INVALID_SIG; 5750afa8e06SEd Maste else 5760afa8e06SEd Maste r = FIDO_OK; 5770afa8e06SEd Maste out: 5780afa8e06SEd Maste explicit_bzero(buf, sizeof(buf)); 5790afa8e06SEd Maste 5800afa8e06SEd Maste return (r); 5810afa8e06SEd Maste } 5820afa8e06SEd Maste 5830afa8e06SEd Maste int 5840afa8e06SEd Maste fido_assert_set_clientdata(fido_assert_t *assert, const unsigned char *data, 5850afa8e06SEd Maste size_t data_len) 5860afa8e06SEd Maste { 5870afa8e06SEd Maste if (!fido_blob_is_empty(&assert->cdh) || 5880afa8e06SEd Maste fido_blob_set(&assert->cd, data, data_len) < 0) { 5890afa8e06SEd Maste return (FIDO_ERR_INVALID_ARGUMENT); 5900afa8e06SEd Maste } 5910afa8e06SEd Maste if (fido_sha256(&assert->cdh, data, data_len) < 0) { 5920afa8e06SEd Maste fido_blob_reset(&assert->cd); 5930afa8e06SEd Maste return (FIDO_ERR_INTERNAL); 5940afa8e06SEd Maste } 5950afa8e06SEd Maste 5960afa8e06SEd Maste return (FIDO_OK); 5970afa8e06SEd Maste } 5980afa8e06SEd Maste 5990afa8e06SEd Maste int 6000afa8e06SEd Maste fido_assert_set_clientdata_hash(fido_assert_t *assert, 6010afa8e06SEd Maste const unsigned char *hash, size_t hash_len) 6020afa8e06SEd Maste { 6030afa8e06SEd Maste if (!fido_blob_is_empty(&assert->cd) || 6040afa8e06SEd Maste fido_blob_set(&assert->cdh, hash, hash_len) < 0) 6050afa8e06SEd Maste return (FIDO_ERR_INVALID_ARGUMENT); 6060afa8e06SEd Maste 6070afa8e06SEd Maste return (FIDO_OK); 6080afa8e06SEd Maste } 6090afa8e06SEd Maste 6100afa8e06SEd Maste int 6110afa8e06SEd Maste fido_assert_set_hmac_salt(fido_assert_t *assert, const unsigned char *salt, 6120afa8e06SEd Maste size_t salt_len) 6130afa8e06SEd Maste { 6140afa8e06SEd Maste if ((salt_len != 32 && salt_len != 64) || 6150afa8e06SEd Maste fido_blob_set(&assert->ext.hmac_salt, salt, salt_len) < 0) 6160afa8e06SEd Maste return (FIDO_ERR_INVALID_ARGUMENT); 6170afa8e06SEd Maste 6180afa8e06SEd Maste return (FIDO_OK); 6190afa8e06SEd Maste } 6200afa8e06SEd Maste 6210afa8e06SEd Maste int 6220afa8e06SEd Maste fido_assert_set_hmac_secret(fido_assert_t *assert, size_t idx, 6230afa8e06SEd Maste const unsigned char *secret, size_t secret_len) 6240afa8e06SEd Maste { 6250afa8e06SEd Maste if (idx >= assert->stmt_len || (secret_len != 32 && secret_len != 64) || 6260afa8e06SEd Maste fido_blob_set(&assert->stmt[idx].hmac_secret, secret, 6270afa8e06SEd Maste secret_len) < 0) 6280afa8e06SEd Maste return (FIDO_ERR_INVALID_ARGUMENT); 6290afa8e06SEd Maste 6300afa8e06SEd Maste return (FIDO_OK); 6310afa8e06SEd Maste } 6320afa8e06SEd Maste 6330afa8e06SEd Maste int 6340afa8e06SEd Maste fido_assert_set_rp(fido_assert_t *assert, const char *id) 6350afa8e06SEd Maste { 6360afa8e06SEd Maste if (assert->rp_id != NULL) { 6370afa8e06SEd Maste free(assert->rp_id); 6380afa8e06SEd Maste assert->rp_id = NULL; 6390afa8e06SEd Maste } 6400afa8e06SEd Maste 6410afa8e06SEd Maste if (id == NULL) 6420afa8e06SEd Maste return (FIDO_ERR_INVALID_ARGUMENT); 6430afa8e06SEd Maste 6440afa8e06SEd Maste if ((assert->rp_id = strdup(id)) == NULL) 6450afa8e06SEd Maste return (FIDO_ERR_INTERNAL); 6460afa8e06SEd Maste 6470afa8e06SEd Maste return (FIDO_OK); 6480afa8e06SEd Maste } 6490afa8e06SEd Maste 650*60a517b6SEd Maste #ifdef USE_WINHELLO 651*60a517b6SEd Maste int 652*60a517b6SEd Maste fido_assert_set_winhello_appid(fido_assert_t *assert, const char *id) 653*60a517b6SEd Maste { 654*60a517b6SEd Maste if (assert->appid != NULL) { 655*60a517b6SEd Maste free(assert->appid); 656*60a517b6SEd Maste assert->appid = NULL; 657*60a517b6SEd Maste } 658*60a517b6SEd Maste 659*60a517b6SEd Maste if (id == NULL) 660*60a517b6SEd Maste return (FIDO_ERR_INVALID_ARGUMENT); 661*60a517b6SEd Maste 662*60a517b6SEd Maste if ((assert->appid = strdup(id)) == NULL) 663*60a517b6SEd Maste return (FIDO_ERR_INTERNAL); 664*60a517b6SEd Maste 665*60a517b6SEd Maste return (FIDO_OK); 666*60a517b6SEd Maste } 667*60a517b6SEd Maste #else 668*60a517b6SEd Maste int 669*60a517b6SEd Maste fido_assert_set_winhello_appid(fido_assert_t *assert, const char *id) 670*60a517b6SEd Maste { 671*60a517b6SEd Maste (void)assert; 672*60a517b6SEd Maste (void)id; 673*60a517b6SEd Maste 674*60a517b6SEd Maste return (FIDO_ERR_UNSUPPORTED_EXTENSION); 675*60a517b6SEd Maste } 676*60a517b6SEd Maste #endif /* USE_WINHELLO */ 677*60a517b6SEd Maste 6780afa8e06SEd Maste int 6790afa8e06SEd Maste fido_assert_allow_cred(fido_assert_t *assert, const unsigned char *ptr, 6800afa8e06SEd Maste size_t len) 6810afa8e06SEd Maste { 6820afa8e06SEd Maste fido_blob_t id; 6830afa8e06SEd Maste fido_blob_t *list_ptr; 6840afa8e06SEd Maste int r; 6850afa8e06SEd Maste 6860afa8e06SEd Maste memset(&id, 0, sizeof(id)); 6870afa8e06SEd Maste 6880afa8e06SEd Maste if (assert->allow_list.len == SIZE_MAX) { 6890afa8e06SEd Maste r = FIDO_ERR_INVALID_ARGUMENT; 6900afa8e06SEd Maste goto fail; 6910afa8e06SEd Maste } 6920afa8e06SEd Maste 6930afa8e06SEd Maste if (fido_blob_set(&id, ptr, len) < 0 || (list_ptr = 6940afa8e06SEd Maste recallocarray(assert->allow_list.ptr, assert->allow_list.len, 6950afa8e06SEd Maste assert->allow_list.len + 1, sizeof(fido_blob_t))) == NULL) { 6960afa8e06SEd Maste r = FIDO_ERR_INVALID_ARGUMENT; 6970afa8e06SEd Maste goto fail; 6980afa8e06SEd Maste } 6990afa8e06SEd Maste 7000afa8e06SEd Maste list_ptr[assert->allow_list.len++] = id; 7010afa8e06SEd Maste assert->allow_list.ptr = list_ptr; 7020afa8e06SEd Maste 7030afa8e06SEd Maste return (FIDO_OK); 7040afa8e06SEd Maste fail: 7050afa8e06SEd Maste free(id.ptr); 7060afa8e06SEd Maste 7070afa8e06SEd Maste return (r); 7082ccfa855SEd Maste } 7090afa8e06SEd Maste 7102ccfa855SEd Maste int 7112ccfa855SEd Maste fido_assert_empty_allow_list(fido_assert_t *assert) 7122ccfa855SEd Maste { 7132ccfa855SEd Maste fido_free_blob_array(&assert->allow_list); 7142ccfa855SEd Maste memset(&assert->allow_list, 0, sizeof(assert->allow_list)); 7152ccfa855SEd Maste 7162ccfa855SEd Maste return (FIDO_OK); 7170afa8e06SEd Maste } 7180afa8e06SEd Maste 7190afa8e06SEd Maste int 7200afa8e06SEd Maste fido_assert_set_extensions(fido_assert_t *assert, int ext) 7210afa8e06SEd Maste { 7220afa8e06SEd Maste if (ext == 0) 7230afa8e06SEd Maste assert->ext.mask = 0; 7240afa8e06SEd Maste else { 7250afa8e06SEd Maste if ((ext & FIDO_EXT_ASSERT_MASK) != ext) 7260afa8e06SEd Maste return (FIDO_ERR_INVALID_ARGUMENT); 7270afa8e06SEd Maste assert->ext.mask |= ext; 7280afa8e06SEd Maste } 7290afa8e06SEd Maste 7300afa8e06SEd Maste return (FIDO_OK); 7310afa8e06SEd Maste } 7320afa8e06SEd Maste 7330afa8e06SEd Maste int 7340afa8e06SEd Maste fido_assert_set_options(fido_assert_t *assert, bool up, bool uv) 7350afa8e06SEd Maste { 7360afa8e06SEd Maste assert->up = up ? FIDO_OPT_TRUE : FIDO_OPT_FALSE; 7370afa8e06SEd Maste assert->uv = uv ? FIDO_OPT_TRUE : FIDO_OPT_FALSE; 7380afa8e06SEd Maste 7390afa8e06SEd Maste return (FIDO_OK); 7400afa8e06SEd Maste } 7410afa8e06SEd Maste 7420afa8e06SEd Maste int 7430afa8e06SEd Maste fido_assert_set_up(fido_assert_t *assert, fido_opt_t up) 7440afa8e06SEd Maste { 7450afa8e06SEd Maste assert->up = up; 7460afa8e06SEd Maste 7470afa8e06SEd Maste return (FIDO_OK); 7480afa8e06SEd Maste } 7490afa8e06SEd Maste 7500afa8e06SEd Maste int 7510afa8e06SEd Maste fido_assert_set_uv(fido_assert_t *assert, fido_opt_t uv) 7520afa8e06SEd Maste { 7530afa8e06SEd Maste assert->uv = uv; 7540afa8e06SEd Maste 7550afa8e06SEd Maste return (FIDO_OK); 7560afa8e06SEd Maste } 7570afa8e06SEd Maste 7580afa8e06SEd Maste const unsigned char * 7590afa8e06SEd Maste fido_assert_clientdata_hash_ptr(const fido_assert_t *assert) 7600afa8e06SEd Maste { 7610afa8e06SEd Maste return (assert->cdh.ptr); 7620afa8e06SEd Maste } 7630afa8e06SEd Maste 7640afa8e06SEd Maste size_t 7650afa8e06SEd Maste fido_assert_clientdata_hash_len(const fido_assert_t *assert) 7660afa8e06SEd Maste { 7670afa8e06SEd Maste return (assert->cdh.len); 7680afa8e06SEd Maste } 7690afa8e06SEd Maste 7700afa8e06SEd Maste fido_assert_t * 7710afa8e06SEd Maste fido_assert_new(void) 7720afa8e06SEd Maste { 7730afa8e06SEd Maste return (calloc(1, sizeof(fido_assert_t))); 7740afa8e06SEd Maste } 7750afa8e06SEd Maste 7760afa8e06SEd Maste void 7770afa8e06SEd Maste fido_assert_reset_tx(fido_assert_t *assert) 7780afa8e06SEd Maste { 7790afa8e06SEd Maste free(assert->rp_id); 780*60a517b6SEd Maste free(assert->appid); 7810afa8e06SEd Maste fido_blob_reset(&assert->cd); 7820afa8e06SEd Maste fido_blob_reset(&assert->cdh); 7830afa8e06SEd Maste fido_blob_reset(&assert->ext.hmac_salt); 7842ccfa855SEd Maste fido_assert_empty_allow_list(assert); 7850afa8e06SEd Maste memset(&assert->ext, 0, sizeof(assert->ext)); 7860afa8e06SEd Maste assert->rp_id = NULL; 787*60a517b6SEd Maste assert->appid = NULL; 7880afa8e06SEd Maste assert->up = FIDO_OPT_OMIT; 7890afa8e06SEd Maste assert->uv = FIDO_OPT_OMIT; 7900afa8e06SEd Maste } 7910afa8e06SEd Maste 7922ccfa855SEd Maste static void 7932ccfa855SEd Maste fido_assert_reset_extattr(fido_assert_extattr_t *ext) 7940afa8e06SEd Maste { 7950afa8e06SEd Maste fido_blob_reset(&ext->hmac_secret_enc); 7960afa8e06SEd Maste fido_blob_reset(&ext->blob); 7970afa8e06SEd Maste memset(ext, 0, sizeof(*ext)); 7980afa8e06SEd Maste } 7990afa8e06SEd Maste 8000afa8e06SEd Maste void 8010afa8e06SEd Maste fido_assert_reset_rx(fido_assert_t *assert) 8020afa8e06SEd Maste { 8030afa8e06SEd Maste for (size_t i = 0; i < assert->stmt_cnt; i++) { 8040afa8e06SEd Maste free(assert->stmt[i].user.icon); 8050afa8e06SEd Maste free(assert->stmt[i].user.name); 8060afa8e06SEd Maste free(assert->stmt[i].user.display_name); 8070afa8e06SEd Maste fido_blob_reset(&assert->stmt[i].user.id); 8080afa8e06SEd Maste fido_blob_reset(&assert->stmt[i].id); 8090afa8e06SEd Maste fido_blob_reset(&assert->stmt[i].hmac_secret); 8100afa8e06SEd Maste fido_blob_reset(&assert->stmt[i].authdata_cbor); 811*60a517b6SEd Maste fido_blob_reset(&assert->stmt[i].authdata_raw); 8120afa8e06SEd Maste fido_blob_reset(&assert->stmt[i].largeblob_key); 8130afa8e06SEd Maste fido_blob_reset(&assert->stmt[i].sig); 8140afa8e06SEd Maste fido_assert_reset_extattr(&assert->stmt[i].authdata_ext); 8150afa8e06SEd Maste memset(&assert->stmt[i], 0, sizeof(assert->stmt[i])); 8160afa8e06SEd Maste } 8170afa8e06SEd Maste free(assert->stmt); 8180afa8e06SEd Maste assert->stmt = NULL; 8190afa8e06SEd Maste assert->stmt_len = 0; 8200afa8e06SEd Maste assert->stmt_cnt = 0; 8210afa8e06SEd Maste } 8220afa8e06SEd Maste 8230afa8e06SEd Maste void 8240afa8e06SEd Maste fido_assert_free(fido_assert_t **assert_p) 8250afa8e06SEd Maste { 8260afa8e06SEd Maste fido_assert_t *assert; 8270afa8e06SEd Maste 8280afa8e06SEd Maste if (assert_p == NULL || (assert = *assert_p) == NULL) 8290afa8e06SEd Maste return; 8300afa8e06SEd Maste fido_assert_reset_tx(assert); 8310afa8e06SEd Maste fido_assert_reset_rx(assert); 8320afa8e06SEd Maste free(assert); 8330afa8e06SEd Maste *assert_p = NULL; 8340afa8e06SEd Maste } 8350afa8e06SEd Maste 8360afa8e06SEd Maste size_t 8370afa8e06SEd Maste fido_assert_count(const fido_assert_t *assert) 8380afa8e06SEd Maste { 8390afa8e06SEd Maste return (assert->stmt_len); 8400afa8e06SEd Maste } 8410afa8e06SEd Maste 8420afa8e06SEd Maste const char * 8430afa8e06SEd Maste fido_assert_rp_id(const fido_assert_t *assert) 8440afa8e06SEd Maste { 8450afa8e06SEd Maste return (assert->rp_id); 8460afa8e06SEd Maste } 8470afa8e06SEd Maste 8480afa8e06SEd Maste uint8_t 8490afa8e06SEd Maste fido_assert_flags(const fido_assert_t *assert, size_t idx) 8500afa8e06SEd Maste { 8510afa8e06SEd Maste if (idx >= assert->stmt_len) 8520afa8e06SEd Maste return (0); 8530afa8e06SEd Maste 8540afa8e06SEd Maste return (assert->stmt[idx].authdata.flags); 8550afa8e06SEd Maste } 8560afa8e06SEd Maste 8570afa8e06SEd Maste uint32_t 8580afa8e06SEd Maste fido_assert_sigcount(const fido_assert_t *assert, size_t idx) 8590afa8e06SEd Maste { 8600afa8e06SEd Maste if (idx >= assert->stmt_len) 8610afa8e06SEd Maste return (0); 8620afa8e06SEd Maste 8630afa8e06SEd Maste return (assert->stmt[idx].authdata.sigcount); 8640afa8e06SEd Maste } 8650afa8e06SEd Maste 8660afa8e06SEd Maste const unsigned char * 8670afa8e06SEd Maste fido_assert_authdata_ptr(const fido_assert_t *assert, size_t idx) 8680afa8e06SEd Maste { 8690afa8e06SEd Maste if (idx >= assert->stmt_len) 8700afa8e06SEd Maste return (NULL); 8710afa8e06SEd Maste 8720afa8e06SEd Maste return (assert->stmt[idx].authdata_cbor.ptr); 8730afa8e06SEd Maste } 8740afa8e06SEd Maste 8750afa8e06SEd Maste size_t 8760afa8e06SEd Maste fido_assert_authdata_len(const fido_assert_t *assert, size_t idx) 8770afa8e06SEd Maste { 8780afa8e06SEd Maste if (idx >= assert->stmt_len) 8790afa8e06SEd Maste return (0); 8800afa8e06SEd Maste 8810afa8e06SEd Maste return (assert->stmt[idx].authdata_cbor.len); 8820afa8e06SEd Maste } 8830afa8e06SEd Maste 8840afa8e06SEd Maste const unsigned char * 885*60a517b6SEd Maste fido_assert_authdata_raw_ptr(const fido_assert_t *assert, size_t idx) 886*60a517b6SEd Maste { 887*60a517b6SEd Maste if (idx >= assert->stmt_len) 888*60a517b6SEd Maste return (NULL); 889*60a517b6SEd Maste 890*60a517b6SEd Maste return (assert->stmt[idx].authdata_raw.ptr); 891*60a517b6SEd Maste } 892*60a517b6SEd Maste 893*60a517b6SEd Maste size_t 894*60a517b6SEd Maste fido_assert_authdata_raw_len(const fido_assert_t *assert, size_t idx) 895*60a517b6SEd Maste { 896*60a517b6SEd Maste if (idx >= assert->stmt_len) 897*60a517b6SEd Maste return (0); 898*60a517b6SEd Maste 899*60a517b6SEd Maste return (assert->stmt[idx].authdata_raw.len); 900*60a517b6SEd Maste } 901*60a517b6SEd Maste 902*60a517b6SEd Maste const unsigned char * 9030afa8e06SEd Maste fido_assert_sig_ptr(const fido_assert_t *assert, size_t idx) 9040afa8e06SEd Maste { 9050afa8e06SEd Maste if (idx >= assert->stmt_len) 9060afa8e06SEd Maste return (NULL); 9070afa8e06SEd Maste 9080afa8e06SEd Maste return (assert->stmt[idx].sig.ptr); 9090afa8e06SEd Maste } 9100afa8e06SEd Maste 9110afa8e06SEd Maste size_t 9120afa8e06SEd Maste fido_assert_sig_len(const fido_assert_t *assert, size_t idx) 9130afa8e06SEd Maste { 9140afa8e06SEd Maste if (idx >= assert->stmt_len) 9150afa8e06SEd Maste return (0); 9160afa8e06SEd Maste 9170afa8e06SEd Maste return (assert->stmt[idx].sig.len); 9180afa8e06SEd Maste } 9190afa8e06SEd Maste 9200afa8e06SEd Maste const unsigned char * 9210afa8e06SEd Maste fido_assert_id_ptr(const fido_assert_t *assert, size_t idx) 9220afa8e06SEd Maste { 9230afa8e06SEd Maste if (idx >= assert->stmt_len) 9240afa8e06SEd Maste return (NULL); 9250afa8e06SEd Maste 9260afa8e06SEd Maste return (assert->stmt[idx].id.ptr); 9270afa8e06SEd Maste } 9280afa8e06SEd Maste 9290afa8e06SEd Maste size_t 9300afa8e06SEd Maste fido_assert_id_len(const fido_assert_t *assert, size_t idx) 9310afa8e06SEd Maste { 9320afa8e06SEd Maste if (idx >= assert->stmt_len) 9330afa8e06SEd Maste return (0); 9340afa8e06SEd Maste 9350afa8e06SEd Maste return (assert->stmt[idx].id.len); 9360afa8e06SEd Maste } 9370afa8e06SEd Maste 9380afa8e06SEd Maste const unsigned char * 9390afa8e06SEd Maste fido_assert_user_id_ptr(const fido_assert_t *assert, size_t idx) 9400afa8e06SEd Maste { 9410afa8e06SEd Maste if (idx >= assert->stmt_len) 9420afa8e06SEd Maste return (NULL); 9430afa8e06SEd Maste 9440afa8e06SEd Maste return (assert->stmt[idx].user.id.ptr); 9450afa8e06SEd Maste } 9460afa8e06SEd Maste 9470afa8e06SEd Maste size_t 9480afa8e06SEd Maste fido_assert_user_id_len(const fido_assert_t *assert, size_t idx) 9490afa8e06SEd Maste { 9500afa8e06SEd Maste if (idx >= assert->stmt_len) 9510afa8e06SEd Maste return (0); 9520afa8e06SEd Maste 9530afa8e06SEd Maste return (assert->stmt[idx].user.id.len); 9540afa8e06SEd Maste } 9550afa8e06SEd Maste 9560afa8e06SEd Maste const char * 9570afa8e06SEd Maste fido_assert_user_icon(const fido_assert_t *assert, size_t idx) 9580afa8e06SEd Maste { 9590afa8e06SEd Maste if (idx >= assert->stmt_len) 9600afa8e06SEd Maste return (NULL); 9610afa8e06SEd Maste 9620afa8e06SEd Maste return (assert->stmt[idx].user.icon); 9630afa8e06SEd Maste } 9640afa8e06SEd Maste 9650afa8e06SEd Maste const char * 9660afa8e06SEd Maste fido_assert_user_name(const fido_assert_t *assert, size_t idx) 9670afa8e06SEd Maste { 9680afa8e06SEd Maste if (idx >= assert->stmt_len) 9690afa8e06SEd Maste return (NULL); 9700afa8e06SEd Maste 9710afa8e06SEd Maste return (assert->stmt[idx].user.name); 9720afa8e06SEd Maste } 9730afa8e06SEd Maste 9740afa8e06SEd Maste const char * 9750afa8e06SEd Maste fido_assert_user_display_name(const fido_assert_t *assert, size_t idx) 9760afa8e06SEd Maste { 9770afa8e06SEd Maste if (idx >= assert->stmt_len) 9780afa8e06SEd Maste return (NULL); 9790afa8e06SEd Maste 9800afa8e06SEd Maste return (assert->stmt[idx].user.display_name); 9810afa8e06SEd Maste } 9820afa8e06SEd Maste 9830afa8e06SEd Maste const unsigned char * 9840afa8e06SEd Maste fido_assert_hmac_secret_ptr(const fido_assert_t *assert, size_t idx) 9850afa8e06SEd Maste { 9860afa8e06SEd Maste if (idx >= assert->stmt_len) 9870afa8e06SEd Maste return (NULL); 9880afa8e06SEd Maste 9890afa8e06SEd Maste return (assert->stmt[idx].hmac_secret.ptr); 9900afa8e06SEd Maste } 9910afa8e06SEd Maste 9920afa8e06SEd Maste size_t 9930afa8e06SEd Maste fido_assert_hmac_secret_len(const fido_assert_t *assert, size_t idx) 9940afa8e06SEd Maste { 9950afa8e06SEd Maste if (idx >= assert->stmt_len) 9960afa8e06SEd Maste return (0); 9970afa8e06SEd Maste 9980afa8e06SEd Maste return (assert->stmt[idx].hmac_secret.len); 9990afa8e06SEd Maste } 10000afa8e06SEd Maste 10010afa8e06SEd Maste const unsigned char * 10020afa8e06SEd Maste fido_assert_largeblob_key_ptr(const fido_assert_t *assert, size_t idx) 10030afa8e06SEd Maste { 10040afa8e06SEd Maste if (idx >= assert->stmt_len) 10050afa8e06SEd Maste return (NULL); 10060afa8e06SEd Maste 10070afa8e06SEd Maste return (assert->stmt[idx].largeblob_key.ptr); 10080afa8e06SEd Maste } 10090afa8e06SEd Maste 10100afa8e06SEd Maste size_t 10110afa8e06SEd Maste fido_assert_largeblob_key_len(const fido_assert_t *assert, size_t idx) 10120afa8e06SEd Maste { 10130afa8e06SEd Maste if (idx >= assert->stmt_len) 10140afa8e06SEd Maste return (0); 10150afa8e06SEd Maste 10160afa8e06SEd Maste return (assert->stmt[idx].largeblob_key.len); 10170afa8e06SEd Maste } 10180afa8e06SEd Maste 10190afa8e06SEd Maste const unsigned char * 10200afa8e06SEd Maste fido_assert_blob_ptr(const fido_assert_t *assert, size_t idx) 10210afa8e06SEd Maste { 10220afa8e06SEd Maste if (idx >= assert->stmt_len) 10230afa8e06SEd Maste return (NULL); 10240afa8e06SEd Maste 10250afa8e06SEd Maste return (assert->stmt[idx].authdata_ext.blob.ptr); 10260afa8e06SEd Maste } 10270afa8e06SEd Maste 10280afa8e06SEd Maste size_t 10290afa8e06SEd Maste fido_assert_blob_len(const fido_assert_t *assert, size_t idx) 10300afa8e06SEd Maste { 10310afa8e06SEd Maste if (idx >= assert->stmt_len) 10320afa8e06SEd Maste return (0); 10330afa8e06SEd Maste 10340afa8e06SEd Maste return (assert->stmt[idx].authdata_ext.blob.len); 10350afa8e06SEd Maste } 10360afa8e06SEd Maste 10370afa8e06SEd Maste static void 10380afa8e06SEd Maste fido_assert_clean_authdata(fido_assert_stmt *stmt) 10390afa8e06SEd Maste { 10400afa8e06SEd Maste fido_blob_reset(&stmt->authdata_cbor); 1041*60a517b6SEd Maste fido_blob_reset(&stmt->authdata_raw); 10420afa8e06SEd Maste fido_assert_reset_extattr(&stmt->authdata_ext); 10430afa8e06SEd Maste memset(&stmt->authdata, 0, sizeof(stmt->authdata)); 10440afa8e06SEd Maste } 10450afa8e06SEd Maste 10460afa8e06SEd Maste int 10470afa8e06SEd Maste fido_assert_set_authdata(fido_assert_t *assert, size_t idx, 10480afa8e06SEd Maste const unsigned char *ptr, size_t len) 10490afa8e06SEd Maste { 10500afa8e06SEd Maste cbor_item_t *item = NULL; 10510afa8e06SEd Maste fido_assert_stmt *stmt = NULL; 10520afa8e06SEd Maste struct cbor_load_result cbor; 10530afa8e06SEd Maste int r; 10540afa8e06SEd Maste 10550afa8e06SEd Maste if (idx >= assert->stmt_len || ptr == NULL || len == 0) 10560afa8e06SEd Maste return (FIDO_ERR_INVALID_ARGUMENT); 10570afa8e06SEd Maste 10580afa8e06SEd Maste stmt = &assert->stmt[idx]; 10590afa8e06SEd Maste fido_assert_clean_authdata(stmt); 10600afa8e06SEd Maste 10610afa8e06SEd Maste if ((item = cbor_load(ptr, len, &cbor)) == NULL) { 10620afa8e06SEd Maste fido_log_debug("%s: cbor_load", __func__); 10630afa8e06SEd Maste r = FIDO_ERR_INVALID_ARGUMENT; 10640afa8e06SEd Maste goto fail; 10650afa8e06SEd Maste } 10660afa8e06SEd Maste 1067*60a517b6SEd Maste if (fido_blob_decode(item, &stmt->authdata_raw) < 0) { 1068*60a517b6SEd Maste fido_log_debug("%s: fido_blob_decode", __func__); 1069*60a517b6SEd Maste r = FIDO_ERR_INTERNAL; 1070*60a517b6SEd Maste goto fail; 1071*60a517b6SEd Maste } 1072*60a517b6SEd Maste 10730afa8e06SEd Maste if (cbor_decode_assert_authdata(item, &stmt->authdata_cbor, 10740afa8e06SEd Maste &stmt->authdata, &stmt->authdata_ext) < 0) { 10750afa8e06SEd Maste fido_log_debug("%s: cbor_decode_assert_authdata", __func__); 10760afa8e06SEd Maste r = FIDO_ERR_INVALID_ARGUMENT; 10770afa8e06SEd Maste goto fail; 10780afa8e06SEd Maste } 10790afa8e06SEd Maste 10800afa8e06SEd Maste r = FIDO_OK; 10810afa8e06SEd Maste fail: 10820afa8e06SEd Maste if (item != NULL) 10830afa8e06SEd Maste cbor_decref(&item); 10840afa8e06SEd Maste 10850afa8e06SEd Maste if (r != FIDO_OK) 10860afa8e06SEd Maste fido_assert_clean_authdata(stmt); 10870afa8e06SEd Maste 10880afa8e06SEd Maste return (r); 10890afa8e06SEd Maste } 10900afa8e06SEd Maste 10910afa8e06SEd Maste int 10920afa8e06SEd Maste fido_assert_set_authdata_raw(fido_assert_t *assert, size_t idx, 10930afa8e06SEd Maste const unsigned char *ptr, size_t len) 10940afa8e06SEd Maste { 10950afa8e06SEd Maste cbor_item_t *item = NULL; 10960afa8e06SEd Maste fido_assert_stmt *stmt = NULL; 10970afa8e06SEd Maste int r; 10980afa8e06SEd Maste 10990afa8e06SEd Maste if (idx >= assert->stmt_len || ptr == NULL || len == 0) 11000afa8e06SEd Maste return (FIDO_ERR_INVALID_ARGUMENT); 11010afa8e06SEd Maste 11020afa8e06SEd Maste stmt = &assert->stmt[idx]; 11030afa8e06SEd Maste fido_assert_clean_authdata(stmt); 11040afa8e06SEd Maste 1105*60a517b6SEd Maste if (fido_blob_set(&stmt->authdata_raw, ptr, len) < 0) { 1106*60a517b6SEd Maste fido_log_debug("%s: fido_blob_set", __func__); 1107*60a517b6SEd Maste r = FIDO_ERR_INTERNAL; 1108*60a517b6SEd Maste goto fail; 1109*60a517b6SEd Maste } 1110*60a517b6SEd Maste 11110afa8e06SEd Maste if ((item = cbor_build_bytestring(ptr, len)) == NULL) { 11120afa8e06SEd Maste fido_log_debug("%s: cbor_build_bytestring", __func__); 11130afa8e06SEd Maste r = FIDO_ERR_INTERNAL; 11140afa8e06SEd Maste goto fail; 11150afa8e06SEd Maste } 11160afa8e06SEd Maste 11170afa8e06SEd Maste if (cbor_decode_assert_authdata(item, &stmt->authdata_cbor, 11180afa8e06SEd Maste &stmt->authdata, &stmt->authdata_ext) < 0) { 11190afa8e06SEd Maste fido_log_debug("%s: cbor_decode_assert_authdata", __func__); 11200afa8e06SEd Maste r = FIDO_ERR_INVALID_ARGUMENT; 11210afa8e06SEd Maste goto fail; 11220afa8e06SEd Maste } 11230afa8e06SEd Maste 11240afa8e06SEd Maste r = FIDO_OK; 11250afa8e06SEd Maste fail: 11260afa8e06SEd Maste if (item != NULL) 11270afa8e06SEd Maste cbor_decref(&item); 11280afa8e06SEd Maste 11290afa8e06SEd Maste if (r != FIDO_OK) 11300afa8e06SEd Maste fido_assert_clean_authdata(stmt); 11310afa8e06SEd Maste 11320afa8e06SEd Maste return (r); 11330afa8e06SEd Maste } 11340afa8e06SEd Maste 11350afa8e06SEd Maste int 11360afa8e06SEd Maste fido_assert_set_sig(fido_assert_t *a, size_t idx, const unsigned char *ptr, 11370afa8e06SEd Maste size_t len) 11380afa8e06SEd Maste { 11390afa8e06SEd Maste if (idx >= a->stmt_len || ptr == NULL || len == 0) 11400afa8e06SEd Maste return (FIDO_ERR_INVALID_ARGUMENT); 11410afa8e06SEd Maste if (fido_blob_set(&a->stmt[idx].sig, ptr, len) < 0) 11420afa8e06SEd Maste return (FIDO_ERR_INTERNAL); 11430afa8e06SEd Maste 11440afa8e06SEd Maste return (FIDO_OK); 11450afa8e06SEd Maste } 11460afa8e06SEd Maste 11470afa8e06SEd Maste /* XXX shrinking leaks memory; fortunately that shouldn't happen */ 11480afa8e06SEd Maste int 11490afa8e06SEd Maste fido_assert_set_count(fido_assert_t *assert, size_t n) 11500afa8e06SEd Maste { 11510afa8e06SEd Maste void *new_stmt; 11520afa8e06SEd Maste 11530afa8e06SEd Maste #ifdef FIDO_FUZZ 11540afa8e06SEd Maste if (n > UINT8_MAX) { 11550afa8e06SEd Maste fido_log_debug("%s: n > UINT8_MAX", __func__); 11560afa8e06SEd Maste return (FIDO_ERR_INTERNAL); 11570afa8e06SEd Maste } 11580afa8e06SEd Maste #endif 11590afa8e06SEd Maste 11600afa8e06SEd Maste new_stmt = recallocarray(assert->stmt, assert->stmt_cnt, n, 11610afa8e06SEd Maste sizeof(fido_assert_stmt)); 11620afa8e06SEd Maste if (new_stmt == NULL) 11630afa8e06SEd Maste return (FIDO_ERR_INTERNAL); 11640afa8e06SEd Maste 11650afa8e06SEd Maste assert->stmt = new_stmt; 11660afa8e06SEd Maste assert->stmt_cnt = n; 11670afa8e06SEd Maste assert->stmt_len = n; 11680afa8e06SEd Maste 11690afa8e06SEd Maste return (FIDO_OK); 11700afa8e06SEd Maste } 1171