Home | History | Annotate | Line # | Download | only in src
      1 /*
      2  * Copyright (c) 2018-2023 Yubico AB. All rights reserved.
      3  * Use of this source code is governed by a BSD-style
      4  * license that can be found in the LICENSE file.
      5  * SPDX-License-Identifier: BSD-2-Clause
      6  */
      7 
      8 #include <openssl/sha.h>
      9 
     10 #include "fido.h"
     11 #include "fido/es256.h"
     12 #include "fido/rs256.h"
     13 #include "fido/eddsa.h"
     14 
     15 static int
     16 adjust_assert_count(const cbor_item_t *key, const cbor_item_t *val, void *arg)
     17 {
     18 	fido_assert_t	*assert = arg;
     19 	uint64_t	 n;
     20 
     21 	/* numberOfCredentials; see section 6.2 */
     22 	if (cbor_isa_uint(key) == false ||
     23 	    cbor_int_get_width(key) != CBOR_INT_8 ||
     24 	    cbor_get_uint8(key) != 5) {
     25 		fido_log_debug("%s: cbor_type", __func__);
     26 		return (0); /* ignore */
     27 	}
     28 
     29 	if (cbor_decode_uint64(val, &n) < 0 || n > SIZE_MAX) {
     30 		fido_log_debug("%s: cbor_decode_uint64", __func__);
     31 		return (-1);
     32 	}
     33 
     34 	if (assert->stmt_len != 0 || assert->stmt_cnt != 1 ||
     35 	    (size_t)n < assert->stmt_cnt) {
     36 		fido_log_debug("%s: stmt_len=%zu, stmt_cnt=%zu, n=%zu",
     37 		    __func__, assert->stmt_len, assert->stmt_cnt, (size_t)n);
     38 		return (-1);
     39 	}
     40 
     41 	if (fido_assert_set_count(assert, (size_t)n) != FIDO_OK) {
     42 		fido_log_debug("%s: fido_assert_set_count", __func__);
     43 		return (-1);
     44 	}
     45 
     46 	assert->stmt_len = 0; /* XXX */
     47 
     48 	return (0);
     49 }
     50 
     51 static int
     52 parse_assert_reply(const cbor_item_t *key, const cbor_item_t *val, void *arg)
     53 {
     54 	fido_assert_stmt *stmt = arg;
     55 
     56 	if (cbor_isa_uint(key) == false ||
     57 	    cbor_int_get_width(key) != CBOR_INT_8) {
     58 		fido_log_debug("%s: cbor type", __func__);
     59 		return (0); /* ignore */
     60 	}
     61 
     62 	switch (cbor_get_uint8(key)) {
     63 	case 1: /* credential id */
     64 		return (cbor_decode_cred_id(val, &stmt->id));
     65 	case 2: /* authdata */
     66 		if (fido_blob_decode(val, &stmt->authdata_raw) < 0) {
     67 			fido_log_debug("%s: fido_blob_decode", __func__);
     68 			return (-1);
     69 		}
     70 		return (cbor_decode_assert_authdata(val, &stmt->authdata_cbor,
     71 		    &stmt->authdata, &stmt->authdata_ext));
     72 	case 3: /* signature */
     73 		return (fido_blob_decode(val, &stmt->sig));
     74 	case 4: /* user attributes */
     75 		return (cbor_decode_user(val, &stmt->user));
     76 	case 7: /* large blob key */
     77 		return (fido_blob_decode(val, &stmt->largeblob_key));
     78 	default: /* ignore */
     79 		fido_log_debug("%s: cbor type", __func__);
     80 		return (0);
     81 	}
     82 }
     83 
     84 static int
     85 fido_dev_get_assert_tx(fido_dev_t *dev, fido_assert_t *assert,
     86     const es256_pk_t *pk, const fido_blob_t *ecdh, const char *pin, int *ms)
     87 {
     88 	fido_blob_t	 f;
     89 	fido_opt_t	 uv = assert->uv;
     90 	cbor_item_t	*argv[7];
     91 	const uint8_t	 cmd = CTAP_CBOR_ASSERT;
     92 	int		 r;
     93 
     94 	memset(argv, 0, sizeof(argv));
     95 	memset(&f, 0, sizeof(f));
     96 
     97 	/* do we have everything we need? */
     98 	if (assert->rp_id == NULL || assert->cdh.ptr == NULL) {
     99 		fido_log_debug("%s: rp_id=%p, cdh.ptr=%p", __func__,
    100 		    (void *)assert->rp_id, (void *)assert->cdh.ptr);
    101 		r = FIDO_ERR_INVALID_ARGUMENT;
    102 		goto fail;
    103 	}
    104 
    105 	if ((argv[0] = cbor_build_string(assert->rp_id)) == NULL ||
    106 	    (argv[1] = fido_blob_encode(&assert->cdh)) == NULL) {
    107 		fido_log_debug("%s: cbor encode", __func__);
    108 		r = FIDO_ERR_INTERNAL;
    109 		goto fail;
    110 	}
    111 
    112 	/* allowed credentials */
    113 	if (assert->allow_list.len) {
    114 		const fido_blob_array_t *cl = &assert->allow_list;
    115 		if ((argv[2] = cbor_encode_pubkey_list(cl)) == NULL) {
    116 			fido_log_debug("%s: cbor_encode_pubkey_list", __func__);
    117 			r = FIDO_ERR_INTERNAL;
    118 			goto fail;
    119 		}
    120 	}
    121 
    122 	if (assert->ext.mask)
    123 		if ((argv[3] = cbor_encode_assert_ext(dev, &assert->ext, ecdh,
    124 		    pk)) == NULL) {
    125 			fido_log_debug("%s: cbor_encode_assert_ext", __func__);
    126 			r = FIDO_ERR_INTERNAL;
    127 			goto fail;
    128 		}
    129 
    130 	/* user verification */
    131 	if (pin != NULL || (uv == FIDO_OPT_TRUE &&
    132 	    fido_dev_supports_permissions(dev))) {
    133 		if ((r = cbor_add_uv_params(dev, cmd, &assert->cdh, pk, ecdh,
    134 		    pin, assert->rp_id, &argv[5], &argv[6], ms)) != FIDO_OK) {
    135 			fido_log_debug("%s: cbor_add_uv_params", __func__);
    136 			goto fail;
    137 		}
    138 		uv = FIDO_OPT_OMIT;
    139 	}
    140 
    141 	/* options */
    142 	if (assert->up != FIDO_OPT_OMIT || uv != FIDO_OPT_OMIT)
    143 		if ((argv[4] = cbor_encode_assert_opt(assert->up, uv)) == NULL) {
    144 			fido_log_debug("%s: cbor_encode_assert_opt", __func__);
    145 			r = FIDO_ERR_INTERNAL;
    146 			goto fail;
    147 		}
    148 
    149 	/* frame and transmit */
    150 	if (cbor_build_frame(cmd, argv, nitems(argv), &f) < 0 ||
    151 	    fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
    152 		fido_log_debug("%s: fido_tx", __func__);
    153 		r = FIDO_ERR_TX;
    154 		goto fail;
    155 	}
    156 
    157 	r = FIDO_OK;
    158 fail:
    159 	cbor_vector_free(argv, nitems(argv));
    160 	free(f.ptr);
    161 
    162 	return (r);
    163 }
    164 
    165 static int
    166 fido_dev_get_assert_rx(fido_dev_t *dev, fido_assert_t *assert, int *ms)
    167 {
    168 	unsigned char	*msg;
    169 	int		 msglen;
    170 	int		 r;
    171 
    172 	fido_assert_reset_rx(assert);
    173 
    174 	if ((msg = malloc(FIDO_MAXMSG)) == NULL) {
    175 		r = FIDO_ERR_INTERNAL;
    176 		goto out;
    177 	}
    178 
    179 	if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) {
    180 		fido_log_debug("%s: fido_rx", __func__);
    181 		r = FIDO_ERR_RX;
    182 		goto out;
    183 	}
    184 
    185 	/* start with room for a single assertion */
    186 	if ((assert->stmt = calloc(1, sizeof(fido_assert_stmt))) == NULL) {
    187 		r = FIDO_ERR_INTERNAL;
    188 		goto out;
    189 	}
    190 	assert->stmt_len = 0;
    191 	assert->stmt_cnt = 1;
    192 
    193 	/* adjust as needed */
    194 	if ((r = cbor_parse_reply(msg, (size_t)msglen, assert,
    195 	    adjust_assert_count)) != FIDO_OK) {
    196 		fido_log_debug("%s: adjust_assert_count", __func__);
    197 		goto out;
    198 	}
    199 
    200 	/* parse the first assertion */
    201 	if ((r = cbor_parse_reply(msg, (size_t)msglen, &assert->stmt[0],
    202 	    parse_assert_reply)) != FIDO_OK) {
    203 		fido_log_debug("%s: parse_assert_reply", __func__);
    204 		goto out;
    205 	}
    206 	assert->stmt_len = 1;
    207 
    208 	r = FIDO_OK;
    209 out:
    210 	freezero(msg, FIDO_MAXMSG);
    211 
    212 	return (r);
    213 }
    214 
    215 static int
    216 fido_get_next_assert_tx(fido_dev_t *dev, int *ms)
    217 {
    218 	const unsigned char cbor[] = { CTAP_CBOR_NEXT_ASSERT };
    219 
    220 	if (fido_tx(dev, CTAP_CMD_CBOR, cbor, sizeof(cbor), ms) < 0) {
    221 		fido_log_debug("%s: fido_tx", __func__);
    222 		return (FIDO_ERR_TX);
    223 	}
    224 
    225 	return (FIDO_OK);
    226 }
    227 
    228 static int
    229 fido_get_next_assert_rx(fido_dev_t *dev, fido_assert_t *assert, int *ms)
    230 {
    231 	unsigned char	*msg;
    232 	int		 msglen;
    233 	int		 r;
    234 
    235 	if ((msg = malloc(FIDO_MAXMSG)) == NULL) {
    236 		r = FIDO_ERR_INTERNAL;
    237 		goto out;
    238 	}
    239 
    240 	if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) {
    241 		fido_log_debug("%s: fido_rx", __func__);
    242 		r = FIDO_ERR_RX;
    243 		goto out;
    244 	}
    245 
    246 	/* sanity check */
    247 	if (assert->stmt_len >= assert->stmt_cnt) {
    248 		fido_log_debug("%s: stmt_len=%zu, stmt_cnt=%zu", __func__,
    249 		    assert->stmt_len, assert->stmt_cnt);
    250 		r = FIDO_ERR_INTERNAL;
    251 		goto out;
    252 	}
    253 
    254 	if ((r = cbor_parse_reply(msg, (size_t)msglen,
    255 	    &assert->stmt[assert->stmt_len], parse_assert_reply)) != FIDO_OK) {
    256 		fido_log_debug("%s: parse_assert_reply", __func__);
    257 		goto out;
    258 	}
    259 
    260 	r = FIDO_OK;
    261 out:
    262 	freezero(msg, FIDO_MAXMSG);
    263 
    264 	return (r);
    265 }
    266 
    267 static int
    268 fido_dev_get_assert_wait(fido_dev_t *dev, fido_assert_t *assert,
    269     const es256_pk_t *pk, const fido_blob_t *ecdh, const char *pin, int *ms)
    270 {
    271 	int r;
    272 
    273 	if ((r = fido_dev_get_assert_tx(dev, assert, pk, ecdh, pin,
    274 	    ms)) != FIDO_OK ||
    275 	    (r = fido_dev_get_assert_rx(dev, assert, ms)) != FIDO_OK)
    276 		return (r);
    277 
    278 	while (assert->stmt_len < assert->stmt_cnt) {
    279 		if ((r = fido_get_next_assert_tx(dev, ms)) != FIDO_OK ||
    280 		    (r = fido_get_next_assert_rx(dev, assert, ms)) != FIDO_OK)
    281 			return (r);
    282 		assert->stmt_len++;
    283 	}
    284 
    285 	return (FIDO_OK);
    286 }
    287 
    288 static int
    289 decrypt_hmac_secrets(const fido_dev_t *dev, fido_assert_t *assert,
    290     const fido_blob_t *key)
    291 {
    292 	for (size_t i = 0; i < assert->stmt_cnt; i++) {
    293 		fido_assert_stmt *stmt = &assert->stmt[i];
    294 		if (stmt->authdata_ext.hmac_secret_enc.ptr != NULL) {
    295 			if (aes256_cbc_dec(dev, key,
    296 			    &stmt->authdata_ext.hmac_secret_enc,
    297 			    &stmt->hmac_secret) < 0) {
    298 				fido_log_debug("%s: aes256_cbc_dec %zu",
    299 				    __func__, i);
    300 				return (-1);
    301 			}
    302 		}
    303 	}
    304 
    305 	return (0);
    306 }
    307 
    308 int
    309 fido_dev_get_assert(fido_dev_t *dev, fido_assert_t *assert, const char *pin)
    310 {
    311 	fido_blob_t	*ecdh = NULL;
    312 	es256_pk_t	*pk = NULL;
    313 	int		 ms = dev->timeout_ms;
    314 	int		 r;
    315 
    316 #ifdef USE_WINHELLO
    317 	if (dev->flags & FIDO_DEV_WINHELLO)
    318 		return (fido_winhello_get_assert(dev, assert, pin, ms));
    319 #endif
    320 
    321 	if (assert->rp_id == NULL || assert->cdh.ptr == NULL) {
    322 		fido_log_debug("%s: rp_id=%p, cdh.ptr=%p", __func__,
    323 		    (void *)assert->rp_id, (void *)assert->cdh.ptr);
    324 		return (FIDO_ERR_INVALID_ARGUMENT);
    325 	}
    326 
    327 	if (fido_dev_is_fido2(dev) == false) {
    328 		if (pin != NULL || assert->ext.mask != 0)
    329 			return (FIDO_ERR_UNSUPPORTED_OPTION);
    330 		return (u2f_authenticate(dev, assert, &ms));
    331 	}
    332 
    333 	if (pin != NULL || (assert->uv == FIDO_OPT_TRUE &&
    334 	    fido_dev_supports_permissions(dev)) ||
    335 	    (assert->ext.mask & FIDO_EXT_HMAC_SECRET)) {
    336 		if ((r = fido_do_ecdh(dev, &pk, &ecdh, &ms)) != FIDO_OK) {
    337 			fido_log_debug("%s: fido_do_ecdh", __func__);
    338 			goto fail;
    339 		}
    340 	}
    341 
    342 	r = fido_dev_get_assert_wait(dev, assert, pk, ecdh, pin, &ms);
    343 	if (r == FIDO_OK && (assert->ext.mask & FIDO_EXT_HMAC_SECRET))
    344 		if (decrypt_hmac_secrets(dev, assert, ecdh) < 0) {
    345 			fido_log_debug("%s: decrypt_hmac_secrets", __func__);
    346 			r = FIDO_ERR_INTERNAL;
    347 			goto fail;
    348 		}
    349 
    350 fail:
    351 	es256_pk_free(&pk);
    352 	fido_blob_free(&ecdh);
    353 
    354 	return (r);
    355 }
    356 
    357 int
    358 fido_check_flags(uint8_t flags, fido_opt_t up, fido_opt_t uv)
    359 {
    360 	fido_log_debug("%s: flags=%02x", __func__, flags);
    361 	fido_log_debug("%s: up=%d, uv=%d", __func__, up, uv);
    362 
    363 	if (up == FIDO_OPT_TRUE &&
    364 	    (flags & CTAP_AUTHDATA_USER_PRESENT) == 0) {
    365 		fido_log_debug("%s: CTAP_AUTHDATA_USER_PRESENT", __func__);
    366 		return (-1); /* user not present */
    367 	}
    368 
    369 	if (uv == FIDO_OPT_TRUE &&
    370 	    (flags & CTAP_AUTHDATA_USER_VERIFIED) == 0) {
    371 		fido_log_debug("%s: CTAP_AUTHDATA_USER_VERIFIED", __func__);
    372 		return (-1); /* user not verified */
    373 	}
    374 
    375 	return (0);
    376 }
    377 
    378 static int
    379 check_extensions(int authdata_ext, int ext)
    380 {
    381 	/* XXX: largeBlobKey is not part of extensions map */
    382 	ext &= ~FIDO_EXT_LARGEBLOB_KEY;
    383 	if (authdata_ext != ext) {
    384 		fido_log_debug("%s: authdata_ext=0x%x != ext=0x%x", __func__,
    385 		    authdata_ext, ext);
    386 		return (-1);
    387 	}
    388 
    389 	return (0);
    390 }
    391 
    392 static int
    393 get_es256_hash(fido_blob_t *dgst, const fido_blob_t *clientdata,
    394     const fido_blob_t *authdata)
    395 {
    396 	const EVP_MD	*md;
    397 	EVP_MD_CTX	*ctx = NULL;
    398 
    399 	if (dgst->len < SHA256_DIGEST_LENGTH ||
    400 	    (md = EVP_sha256()) == NULL ||
    401 	    (ctx = EVP_MD_CTX_new()) == NULL ||
    402 	    EVP_DigestInit_ex(ctx, md, NULL) != 1 ||
    403 	    EVP_DigestUpdate(ctx, authdata->ptr, authdata->len) != 1 ||
    404 	    EVP_DigestUpdate(ctx, clientdata->ptr, clientdata->len) != 1 ||
    405 	    EVP_DigestFinal_ex(ctx, dgst->ptr, NULL) != 1) {
    406 		EVP_MD_CTX_free(ctx);
    407 		return (-1);
    408 	}
    409 	dgst->len = SHA256_DIGEST_LENGTH;
    410 
    411 	EVP_MD_CTX_free(ctx);
    412 
    413 	return (0);
    414 }
    415 
    416 static int
    417 get_es384_hash(fido_blob_t *dgst, const fido_blob_t *clientdata,
    418     const fido_blob_t *authdata)
    419 {
    420 	const EVP_MD	*md;
    421 	EVP_MD_CTX	*ctx = NULL;
    422 
    423 	if (dgst->len < SHA384_DIGEST_LENGTH ||
    424 	    (md = EVP_sha384()) == NULL ||
    425 	    (ctx = EVP_MD_CTX_new()) == NULL ||
    426 	    EVP_DigestInit_ex(ctx, md, NULL) != 1 ||
    427 	    EVP_DigestUpdate(ctx, authdata->ptr, authdata->len) != 1 ||
    428 	    EVP_DigestUpdate(ctx, clientdata->ptr, clientdata->len) != 1 ||
    429 	    EVP_DigestFinal_ex(ctx, dgst->ptr, NULL) != 1) {
    430 		EVP_MD_CTX_free(ctx);
    431 		return (-1);
    432 	}
    433 	dgst->len = SHA384_DIGEST_LENGTH;
    434 
    435 	EVP_MD_CTX_free(ctx);
    436 
    437 	return (0);
    438 }
    439 
    440 static int
    441 get_eddsa_hash(fido_blob_t *dgst, const fido_blob_t *clientdata,
    442     const fido_blob_t *authdata)
    443 {
    444 	if (SIZE_MAX - authdata->len < clientdata->len ||
    445 	    dgst->len < authdata->len + clientdata->len)
    446 		return (-1);
    447 
    448 	memcpy(dgst->ptr, authdata->ptr, authdata->len);
    449 	memcpy(dgst->ptr + authdata->len, clientdata->ptr, clientdata->len);
    450 	dgst->len = authdata->len + clientdata->len;
    451 
    452 	return (0);
    453 }
    454 
    455 int
    456 fido_get_signed_hash(int cose_alg, fido_blob_t *dgst,
    457     const fido_blob_t *clientdata, const fido_blob_t *authdata_cbor)
    458 {
    459 	cbor_item_t		*item = NULL;
    460 	fido_blob_t		 authdata;
    461 	struct cbor_load_result	 cbor;
    462 	int			 ok = -1;
    463 
    464 	fido_log_debug("%s: cose_alg=%d", __func__, cose_alg);
    465 
    466 	if ((item = cbor_load(authdata_cbor->ptr, authdata_cbor->len,
    467 	    &cbor)) == NULL || cbor_isa_bytestring(item) == false ||
    468 	    cbor_bytestring_is_definite(item) == false) {
    469 		fido_log_debug("%s: authdata", __func__);
    470 		goto fail;
    471 	}
    472 	authdata.ptr = cbor_bytestring_handle(item);
    473 	authdata.len = cbor_bytestring_length(item);
    474 
    475 	switch (cose_alg) {
    476 	case COSE_ES256:
    477 	case COSE_RS256:
    478 		ok = get_es256_hash(dgst, clientdata, &authdata);
    479 		break;
    480 	case COSE_ES384:
    481 		ok = get_es384_hash(dgst, clientdata, &authdata);
    482 		break;
    483 	case COSE_EDDSA:
    484 		ok = get_eddsa_hash(dgst, clientdata, &authdata);
    485 		break;
    486 	default:
    487 		fido_log_debug("%s: unknown cose_alg", __func__);
    488 		break;
    489 	}
    490 fail:
    491 	if (item != NULL)
    492 		cbor_decref(&item);
    493 
    494 	return (ok);
    495 }
    496 
    497 int
    498 fido_assert_verify(const fido_assert_t *assert, size_t idx, int cose_alg,
    499     const void *pk)
    500 {
    501 	unsigned char		 buf[1024]; /* XXX */
    502 	fido_blob_t		 dgst;
    503 	const fido_assert_stmt	*stmt = NULL;
    504 	int			 ok = -1;
    505 	int			 r;
    506 
    507 	dgst.ptr = buf;
    508 	dgst.len = sizeof(buf);
    509 
    510 	if (idx >= assert->stmt_len || pk == NULL) {
    511 		r = FIDO_ERR_INVALID_ARGUMENT;
    512 		goto out;
    513 	}
    514 
    515 	stmt = &assert->stmt[idx];
    516 
    517 	/* do we have everything we need? */
    518 	if (assert->cdh.ptr == NULL || assert->rp_id == NULL ||
    519 	    stmt->authdata_cbor.ptr == NULL || stmt->sig.ptr == NULL) {
    520 		fido_log_debug("%s: cdh=%p, rp_id=%s, authdata=%p, sig=%p",
    521 		    __func__, (void *)assert->cdh.ptr, assert->rp_id,
    522 		    (void *)stmt->authdata_cbor.ptr, (void *)stmt->sig.ptr);
    523 		r = FIDO_ERR_INVALID_ARGUMENT;
    524 		goto out;
    525 	}
    526 
    527 	if (fido_check_flags(stmt->authdata.flags, assert->up,
    528 	    assert->uv) < 0) {
    529 		fido_log_debug("%s: fido_check_flags", __func__);
    530 		r = FIDO_ERR_INVALID_PARAM;
    531 		goto out;
    532 	}
    533 
    534 	if (check_extensions(stmt->authdata_ext.mask, assert->ext.mask) < 0) {
    535 		fido_log_debug("%s: check_extensions", __func__);
    536 		r = FIDO_ERR_INVALID_PARAM;
    537 		goto out;
    538 	}
    539 
    540 	if (fido_check_rp_id(assert->rp_id, stmt->authdata.rp_id_hash) != 0) {
    541 		fido_log_debug("%s: fido_check_rp_id", __func__);
    542 		r = FIDO_ERR_INVALID_PARAM;
    543 		goto out;
    544 	}
    545 
    546 	if (fido_get_signed_hash(cose_alg, &dgst, &assert->cdh,
    547 	    &stmt->authdata_cbor) < 0) {
    548 		fido_log_debug("%s: fido_get_signed_hash", __func__);
    549 		r = FIDO_ERR_INTERNAL;
    550 		goto out;
    551 	}
    552 
    553 	switch (cose_alg) {
    554 	case COSE_ES256:
    555 		ok = es256_pk_verify_sig(&dgst, pk, &stmt->sig);
    556 		break;
    557 	case COSE_ES384:
    558 		ok = es384_pk_verify_sig(&dgst, pk, &stmt->sig);
    559 		break;
    560 	case COSE_RS256:
    561 		ok = rs256_pk_verify_sig(&dgst, pk, &stmt->sig);
    562 		break;
    563 	case COSE_EDDSA:
    564 		ok = eddsa_pk_verify_sig(&dgst, pk, &stmt->sig);
    565 		break;
    566 	default:
    567 		fido_log_debug("%s: unsupported cose_alg %d", __func__,
    568 		    cose_alg);
    569 		r = FIDO_ERR_UNSUPPORTED_OPTION;
    570 		goto out;
    571 	}
    572 
    573 	if (ok < 0)
    574 		r = FIDO_ERR_INVALID_SIG;
    575 	else
    576 		r = FIDO_OK;
    577 out:
    578 	explicit_bzero(buf, sizeof(buf));
    579 
    580 	return (r);
    581 }
    582 
    583 int
    584 fido_assert_set_clientdata(fido_assert_t *assert, const unsigned char *data,
    585     size_t data_len)
    586 {
    587 	if (!fido_blob_is_empty(&assert->cdh) ||
    588 	    fido_blob_set(&assert->cd, data, data_len) < 0) {
    589 		return (FIDO_ERR_INVALID_ARGUMENT);
    590 	}
    591 	if (fido_sha256(&assert->cdh, data, data_len) < 0) {
    592 		fido_blob_reset(&assert->cd);
    593 		return (FIDO_ERR_INTERNAL);
    594 	}
    595 
    596 	return (FIDO_OK);
    597 }
    598 
    599 int
    600 fido_assert_set_clientdata_hash(fido_assert_t *assert,
    601     const unsigned char *hash, size_t hash_len)
    602 {
    603 	if (!fido_blob_is_empty(&assert->cd) ||
    604 	    fido_blob_set(&assert->cdh, hash, hash_len) < 0)
    605 		return (FIDO_ERR_INVALID_ARGUMENT);
    606 
    607 	return (FIDO_OK);
    608 }
    609 
    610 int
    611 fido_assert_set_hmac_salt(fido_assert_t *assert, const unsigned char *salt,
    612     size_t salt_len)
    613 {
    614 	if ((salt_len != 32 && salt_len != 64) ||
    615 	    fido_blob_set(&assert->ext.hmac_salt, salt, salt_len) < 0)
    616 		return (FIDO_ERR_INVALID_ARGUMENT);
    617 
    618 	return (FIDO_OK);
    619 }
    620 
    621 int
    622 fido_assert_set_hmac_secret(fido_assert_t *assert, size_t idx,
    623     const unsigned char *secret, size_t secret_len)
    624 {
    625 	if (idx >= assert->stmt_len || (secret_len != 32 && secret_len != 64) ||
    626 	    fido_blob_set(&assert->stmt[idx].hmac_secret, secret,
    627 	    secret_len) < 0)
    628 		return (FIDO_ERR_INVALID_ARGUMENT);
    629 
    630 	return (FIDO_OK);
    631 }
    632 
    633 int
    634 fido_assert_set_rp(fido_assert_t *assert, const char *id)
    635 {
    636 	if (assert->rp_id != NULL) {
    637 		free(assert->rp_id);
    638 		assert->rp_id = NULL;
    639 	}
    640 
    641 	if (id == NULL)
    642 		return (FIDO_ERR_INVALID_ARGUMENT);
    643 
    644 	if ((assert->rp_id = strdup(id)) == NULL)
    645 		return (FIDO_ERR_INTERNAL);
    646 
    647 	return (FIDO_OK);
    648 }
    649 
    650 #ifdef USE_WINHELLO
    651 int
    652 fido_assert_set_winhello_appid(fido_assert_t *assert, const char *id)
    653 {
    654 	if (assert->appid != NULL) {
    655 		free(assert->appid);
    656 		assert->appid = NULL;
    657 	}
    658 
    659 	if (id == NULL)
    660 		return (FIDO_ERR_INVALID_ARGUMENT);
    661 
    662 	if ((assert->appid = strdup(id)) == NULL)
    663 		return (FIDO_ERR_INTERNAL);
    664 
    665 	return (FIDO_OK);
    666 }
    667 #else
    668 int
    669 fido_assert_set_winhello_appid(fido_assert_t *assert, const char *id)
    670 {
    671 	(void)assert;
    672 	(void)id;
    673 
    674 	return (FIDO_ERR_UNSUPPORTED_EXTENSION);
    675 }
    676 #endif /* USE_WINHELLO */
    677 
    678 int
    679 fido_assert_allow_cred(fido_assert_t *assert, const unsigned char *ptr,
    680     size_t len)
    681 {
    682 	fido_blob_t	 id;
    683 	fido_blob_t	*list_ptr;
    684 	int		 r;
    685 
    686 	memset(&id, 0, sizeof(id));
    687 
    688 	if (assert->allow_list.len == SIZE_MAX) {
    689 		r = FIDO_ERR_INVALID_ARGUMENT;
    690 		goto fail;
    691 	}
    692 
    693 	if (fido_blob_set(&id, ptr, len) < 0 || (list_ptr =
    694 	    recallocarray(assert->allow_list.ptr, assert->allow_list.len,
    695 	    assert->allow_list.len + 1, sizeof(fido_blob_t))) == NULL) {
    696 		r = FIDO_ERR_INVALID_ARGUMENT;
    697 		goto fail;
    698 	}
    699 
    700 	list_ptr[assert->allow_list.len++] = id;
    701 	assert->allow_list.ptr = list_ptr;
    702 
    703 	return (FIDO_OK);
    704 fail:
    705 	free(id.ptr);
    706 
    707 	return (r);
    708 }
    709 
    710 int
    711 fido_assert_empty_allow_list(fido_assert_t *assert)
    712 {
    713 	fido_free_blob_array(&assert->allow_list);
    714 	memset(&assert->allow_list, 0, sizeof(assert->allow_list));
    715 
    716 	return (FIDO_OK);
    717 }
    718 
    719 int
    720 fido_assert_set_extensions(fido_assert_t *assert, int ext)
    721 {
    722 	if (ext == 0)
    723 		assert->ext.mask = 0;
    724 	else {
    725 		if ((ext & FIDO_EXT_ASSERT_MASK) != ext)
    726 			return (FIDO_ERR_INVALID_ARGUMENT);
    727 		assert->ext.mask |= ext;
    728 	}
    729 
    730 	return (FIDO_OK);
    731 }
    732 
    733 int
    734 fido_assert_set_options(fido_assert_t *assert, bool up, bool uv)
    735 {
    736 	assert->up = up ? FIDO_OPT_TRUE : FIDO_OPT_FALSE;
    737 	assert->uv = uv ? FIDO_OPT_TRUE : FIDO_OPT_FALSE;
    738 
    739 	return (FIDO_OK);
    740 }
    741 
    742 int
    743 fido_assert_set_up(fido_assert_t *assert, fido_opt_t up)
    744 {
    745 	assert->up = up;
    746 
    747 	return (FIDO_OK);
    748 }
    749 
    750 int
    751 fido_assert_set_uv(fido_assert_t *assert, fido_opt_t uv)
    752 {
    753 	assert->uv = uv;
    754 
    755 	return (FIDO_OK);
    756 }
    757 
    758 const unsigned char *
    759 fido_assert_clientdata_hash_ptr(const fido_assert_t *assert)
    760 {
    761 	return (assert->cdh.ptr);
    762 }
    763 
    764 size_t
    765 fido_assert_clientdata_hash_len(const fido_assert_t *assert)
    766 {
    767 	return (assert->cdh.len);
    768 }
    769 
    770 fido_assert_t *
    771 fido_assert_new(void)
    772 {
    773 	return (calloc(1, sizeof(fido_assert_t)));
    774 }
    775 
    776 void
    777 fido_assert_reset_tx(fido_assert_t *assert)
    778 {
    779 	free(assert->rp_id);
    780 	free(assert->appid);
    781 	fido_blob_reset(&assert->cd);
    782 	fido_blob_reset(&assert->cdh);
    783 	fido_blob_reset(&assert->ext.hmac_salt);
    784 	fido_assert_empty_allow_list(assert);
    785 	memset(&assert->ext, 0, sizeof(assert->ext));
    786 	assert->rp_id = NULL;
    787 	assert->appid = NULL;
    788 	assert->up = FIDO_OPT_OMIT;
    789 	assert->uv = FIDO_OPT_OMIT;
    790 }
    791 
    792 static void
    793 fido_assert_reset_extattr(fido_assert_extattr_t *ext)
    794 {
    795 	fido_blob_reset(&ext->hmac_secret_enc);
    796 	fido_blob_reset(&ext->blob);
    797 	memset(ext, 0, sizeof(*ext));
    798 }
    799 
    800 void
    801 fido_assert_reset_rx(fido_assert_t *assert)
    802 {
    803 	for (size_t i = 0; i < assert->stmt_cnt; i++) {
    804 		free(assert->stmt[i].user.icon);
    805 		free(assert->stmt[i].user.name);
    806 		free(assert->stmt[i].user.display_name);
    807 		fido_blob_reset(&assert->stmt[i].user.id);
    808 		fido_blob_reset(&assert->stmt[i].id);
    809 		fido_blob_reset(&assert->stmt[i].hmac_secret);
    810 		fido_blob_reset(&assert->stmt[i].authdata_cbor);
    811 		fido_blob_reset(&assert->stmt[i].authdata_raw);
    812 		fido_blob_reset(&assert->stmt[i].largeblob_key);
    813 		fido_blob_reset(&assert->stmt[i].sig);
    814 		fido_assert_reset_extattr(&assert->stmt[i].authdata_ext);
    815 		memset(&assert->stmt[i], 0, sizeof(assert->stmt[i]));
    816 	}
    817 	free(assert->stmt);
    818 	assert->stmt = NULL;
    819 	assert->stmt_len = 0;
    820 	assert->stmt_cnt = 0;
    821 }
    822 
    823 void
    824 fido_assert_free(fido_assert_t **assert_p)
    825 {
    826 	fido_assert_t *assert;
    827 
    828 	if (assert_p == NULL || (assert = *assert_p) == NULL)
    829 		return;
    830 	fido_assert_reset_tx(assert);
    831 	fido_assert_reset_rx(assert);
    832 	free(assert);
    833 	*assert_p = NULL;
    834 }
    835 
    836 size_t
    837 fido_assert_count(const fido_assert_t *assert)
    838 {
    839 	return (assert->stmt_len);
    840 }
    841 
    842 const char *
    843 fido_assert_rp_id(const fido_assert_t *assert)
    844 {
    845 	return (assert->rp_id);
    846 }
    847 
    848 uint8_t
    849 fido_assert_flags(const fido_assert_t *assert, size_t idx)
    850 {
    851 	if (idx >= assert->stmt_len)
    852 		return (0);
    853 
    854 	return (assert->stmt[idx].authdata.flags);
    855 }
    856 
    857 uint32_t
    858 fido_assert_sigcount(const fido_assert_t *assert, size_t idx)
    859 {
    860 	if (idx >= assert->stmt_len)
    861 		return (0);
    862 
    863 	return (assert->stmt[idx].authdata.sigcount);
    864 }
    865 
    866 const unsigned char *
    867 fido_assert_authdata_ptr(const fido_assert_t *assert, size_t idx)
    868 {
    869 	if (idx >= assert->stmt_len)
    870 		return (NULL);
    871 
    872 	return (assert->stmt[idx].authdata_cbor.ptr);
    873 }
    874 
    875 size_t
    876 fido_assert_authdata_len(const fido_assert_t *assert, size_t idx)
    877 {
    878 	if (idx >= assert->stmt_len)
    879 		return (0);
    880 
    881 	return (assert->stmt[idx].authdata_cbor.len);
    882 }
    883 
    884 const unsigned char *
    885 fido_assert_authdata_raw_ptr(const fido_assert_t *assert, size_t idx)
    886 {
    887 	if (idx >= assert->stmt_len)
    888 		return (NULL);
    889 
    890 	return (assert->stmt[idx].authdata_raw.ptr);
    891 }
    892 
    893 size_t
    894 fido_assert_authdata_raw_len(const fido_assert_t *assert, size_t idx)
    895 {
    896 	if (idx >= assert->stmt_len)
    897 		return (0);
    898 
    899 	return (assert->stmt[idx].authdata_raw.len);
    900 }
    901 
    902 const unsigned char *
    903 fido_assert_sig_ptr(const fido_assert_t *assert, size_t idx)
    904 {
    905 	if (idx >= assert->stmt_len)
    906 		return (NULL);
    907 
    908 	return (assert->stmt[idx].sig.ptr);
    909 }
    910 
    911 size_t
    912 fido_assert_sig_len(const fido_assert_t *assert, size_t idx)
    913 {
    914 	if (idx >= assert->stmt_len)
    915 		return (0);
    916 
    917 	return (assert->stmt[idx].sig.len);
    918 }
    919 
    920 const unsigned char *
    921 fido_assert_id_ptr(const fido_assert_t *assert, size_t idx)
    922 {
    923 	if (idx >= assert->stmt_len)
    924 		return (NULL);
    925 
    926 	return (assert->stmt[idx].id.ptr);
    927 }
    928 
    929 size_t
    930 fido_assert_id_len(const fido_assert_t *assert, size_t idx)
    931 {
    932 	if (idx >= assert->stmt_len)
    933 		return (0);
    934 
    935 	return (assert->stmt[idx].id.len);
    936 }
    937 
    938 const unsigned char *
    939 fido_assert_user_id_ptr(const fido_assert_t *assert, size_t idx)
    940 {
    941 	if (idx >= assert->stmt_len)
    942 		return (NULL);
    943 
    944 	return (assert->stmt[idx].user.id.ptr);
    945 }
    946 
    947 size_t
    948 fido_assert_user_id_len(const fido_assert_t *assert, size_t idx)
    949 {
    950 	if (idx >= assert->stmt_len)
    951 		return (0);
    952 
    953 	return (assert->stmt[idx].user.id.len);
    954 }
    955 
    956 const char *
    957 fido_assert_user_icon(const fido_assert_t *assert, size_t idx)
    958 {
    959 	if (idx >= assert->stmt_len)
    960 		return (NULL);
    961 
    962 	return (assert->stmt[idx].user.icon);
    963 }
    964 
    965 const char *
    966 fido_assert_user_name(const fido_assert_t *assert, size_t idx)
    967 {
    968 	if (idx >= assert->stmt_len)
    969 		return (NULL);
    970 
    971 	return (assert->stmt[idx].user.name);
    972 }
    973 
    974 const char *
    975 fido_assert_user_display_name(const fido_assert_t *assert, size_t idx)
    976 {
    977 	if (idx >= assert->stmt_len)
    978 		return (NULL);
    979 
    980 	return (assert->stmt[idx].user.display_name);
    981 }
    982 
    983 const unsigned char *
    984 fido_assert_hmac_secret_ptr(const fido_assert_t *assert, size_t idx)
    985 {
    986 	if (idx >= assert->stmt_len)
    987 		return (NULL);
    988 
    989 	return (assert->stmt[idx].hmac_secret.ptr);
    990 }
    991 
    992 size_t
    993 fido_assert_hmac_secret_len(const fido_assert_t *assert, size_t idx)
    994 {
    995 	if (idx >= assert->stmt_len)
    996 		return (0);
    997 
    998 	return (assert->stmt[idx].hmac_secret.len);
    999 }
   1000 
   1001 const unsigned char *
   1002 fido_assert_largeblob_key_ptr(const fido_assert_t *assert, size_t idx)
   1003 {
   1004 	if (idx >= assert->stmt_len)
   1005 		return (NULL);
   1006 
   1007 	return (assert->stmt[idx].largeblob_key.ptr);
   1008 }
   1009 
   1010 size_t
   1011 fido_assert_largeblob_key_len(const fido_assert_t *assert, size_t idx)
   1012 {
   1013 	if (idx >= assert->stmt_len)
   1014 		return (0);
   1015 
   1016 	return (assert->stmt[idx].largeblob_key.len);
   1017 }
   1018 
   1019 const unsigned char *
   1020 fido_assert_blob_ptr(const fido_assert_t *assert, size_t idx)
   1021 {
   1022 	if (idx >= assert->stmt_len)
   1023 		return (NULL);
   1024 
   1025 	return (assert->stmt[idx].authdata_ext.blob.ptr);
   1026 }
   1027 
   1028 size_t
   1029 fido_assert_blob_len(const fido_assert_t *assert, size_t idx)
   1030 {
   1031 	if (idx >= assert->stmt_len)
   1032 		return (0);
   1033 
   1034 	return (assert->stmt[idx].authdata_ext.blob.len);
   1035 }
   1036 
   1037 static void
   1038 fido_assert_clean_authdata(fido_assert_stmt *stmt)
   1039 {
   1040 	fido_blob_reset(&stmt->authdata_cbor);
   1041 	fido_blob_reset(&stmt->authdata_raw);
   1042 	fido_assert_reset_extattr(&stmt->authdata_ext);
   1043 	memset(&stmt->authdata, 0, sizeof(stmt->authdata));
   1044 }
   1045 
   1046 int
   1047 fido_assert_set_authdata(fido_assert_t *assert, size_t idx,
   1048     const unsigned char *ptr, size_t len)
   1049 {
   1050 	cbor_item_t		*item = NULL;
   1051 	fido_assert_stmt	*stmt = NULL;
   1052 	struct cbor_load_result	 cbor;
   1053 	int			 r;
   1054 
   1055 	if (idx >= assert->stmt_len || ptr == NULL || len == 0)
   1056 		return (FIDO_ERR_INVALID_ARGUMENT);
   1057 
   1058 	stmt = &assert->stmt[idx];
   1059 	fido_assert_clean_authdata(stmt);
   1060 
   1061 	if ((item = cbor_load(ptr, len, &cbor)) == NULL) {
   1062 		fido_log_debug("%s: cbor_load", __func__);
   1063 		r = FIDO_ERR_INVALID_ARGUMENT;
   1064 		goto fail;
   1065 	}
   1066 
   1067 	if (fido_blob_decode(item, &stmt->authdata_raw) < 0) {
   1068 	    fido_log_debug("%s: fido_blob_decode", __func__);
   1069 	    r = FIDO_ERR_INTERNAL;
   1070 	    goto fail;
   1071 	}
   1072 
   1073 	if (cbor_decode_assert_authdata(item, &stmt->authdata_cbor,
   1074 	    &stmt->authdata, &stmt->authdata_ext) < 0) {
   1075 		fido_log_debug("%s: cbor_decode_assert_authdata", __func__);
   1076 		r = FIDO_ERR_INVALID_ARGUMENT;
   1077 		goto fail;
   1078 	}
   1079 
   1080 	r = FIDO_OK;
   1081 fail:
   1082 	if (item != NULL)
   1083 		cbor_decref(&item);
   1084 
   1085 	if (r != FIDO_OK)
   1086 		fido_assert_clean_authdata(stmt);
   1087 
   1088 	return (r);
   1089 }
   1090 
   1091 int
   1092 fido_assert_set_authdata_raw(fido_assert_t *assert, size_t idx,
   1093     const unsigned char *ptr, size_t len)
   1094 {
   1095 	cbor_item_t		*item = NULL;
   1096 	fido_assert_stmt	*stmt = NULL;
   1097 	int			 r;
   1098 
   1099 	if (idx >= assert->stmt_len || ptr == NULL || len == 0)
   1100 		return (FIDO_ERR_INVALID_ARGUMENT);
   1101 
   1102 	stmt = &assert->stmt[idx];
   1103 	fido_assert_clean_authdata(stmt);
   1104 
   1105 	if (fido_blob_set(&stmt->authdata_raw, ptr, len) < 0) {
   1106 		fido_log_debug("%s: fido_blob_set", __func__);
   1107 		r = FIDO_ERR_INTERNAL;
   1108 		goto fail;
   1109 	}
   1110 
   1111 	if ((item = cbor_build_bytestring(ptr, len)) == NULL) {
   1112 		fido_log_debug("%s: cbor_build_bytestring", __func__);
   1113 		r = FIDO_ERR_INTERNAL;
   1114 		goto fail;
   1115 	}
   1116 
   1117 	if (cbor_decode_assert_authdata(item, &stmt->authdata_cbor,
   1118 	    &stmt->authdata, &stmt->authdata_ext) < 0) {
   1119 		fido_log_debug("%s: cbor_decode_assert_authdata", __func__);
   1120 		r = FIDO_ERR_INVALID_ARGUMENT;
   1121 		goto fail;
   1122 	}
   1123 
   1124 	r = FIDO_OK;
   1125 fail:
   1126 	if (item != NULL)
   1127 		cbor_decref(&item);
   1128 
   1129 	if (r != FIDO_OK)
   1130 		fido_assert_clean_authdata(stmt);
   1131 
   1132 	return (r);
   1133 }
   1134 
   1135 int
   1136 fido_assert_set_sig(fido_assert_t *a, size_t idx, const unsigned char *ptr,
   1137     size_t len)
   1138 {
   1139 	if (idx >= a->stmt_len || ptr == NULL || len == 0)
   1140 		return (FIDO_ERR_INVALID_ARGUMENT);
   1141 	if (fido_blob_set(&a->stmt[idx].sig, ptr, len) < 0)
   1142 		return (FIDO_ERR_INTERNAL);
   1143 
   1144 	return (FIDO_OK);
   1145 }
   1146 
   1147 /* XXX shrinking leaks memory; fortunately that shouldn't happen */
   1148 int
   1149 fido_assert_set_count(fido_assert_t *assert, size_t n)
   1150 {
   1151 	void *new_stmt;
   1152 
   1153 #ifdef FIDO_FUZZ
   1154 	if (n > UINT8_MAX) {
   1155 		fido_log_debug("%s: n > UINT8_MAX", __func__);
   1156 		return (FIDO_ERR_INTERNAL);
   1157 	}
   1158 #endif
   1159 
   1160 	new_stmt = recallocarray(assert->stmt, assert->stmt_cnt, n,
   1161 	    sizeof(fido_assert_stmt));
   1162 	if (new_stmt == NULL)
   1163 		return (FIDO_ERR_INTERNAL);
   1164 
   1165 	assert->stmt = new_stmt;
   1166 	assert->stmt_cnt = n;
   1167 	assert->stmt_len = n;
   1168 
   1169 	return (FIDO_OK);
   1170 }
   1171