Home | History | Annotate | Line # | Download | only in src
      1 /*
      2  * Copyright (c) 2018-2024 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 #include <openssl/x509.h>
     10 
     11 #include "fido.h"
     12 #include "fido/es256.h"
     13 
     14 #ifndef FIDO_MAXMSG_CRED
     15 #define FIDO_MAXMSG_CRED	4096
     16 #endif
     17 
     18 static int
     19 parse_makecred_reply(const cbor_item_t *key, const cbor_item_t *val, void *arg)
     20 {
     21 	fido_cred_t *cred = arg;
     22 
     23 	if (cbor_isa_uint(key) == false ||
     24 	    cbor_int_get_width(key) != CBOR_INT_8) {
     25 		fido_log_debug("%s: cbor type", __func__);
     26 		return (0); /* ignore */
     27 	}
     28 
     29 	switch (cbor_get_uint8(key)) {
     30 	case 1: /* fmt */
     31 		return (cbor_decode_fmt(val, &cred->fmt));
     32 	case 2: /* authdata */
     33 		if (fido_blob_decode(val, &cred->authdata_raw) < 0) {
     34 			fido_log_debug("%s: fido_blob_decode", __func__);
     35 			return (-1);
     36 		}
     37 		return (cbor_decode_cred_authdata(val, cred->type,
     38 		    &cred->authdata_cbor, &cred->authdata, &cred->attcred,
     39 		    &cred->authdata_ext));
     40 	case 3: /* attestation statement */
     41 		return (cbor_decode_attstmt(val, &cred->attstmt));
     42 	case 4: /* enterprise attestation */
     43 		return (cbor_decode_bool(val, &cred->ea.att));
     44 	case 5: /* large blob key */
     45 		return (fido_blob_decode(val, &cred->largeblob_key));
     46 	default: /* ignore */
     47 		fido_log_debug("%s: cbor type", __func__);
     48 		return (0);
     49 	}
     50 }
     51 
     52 static int
     53 fido_dev_make_cred_tx(fido_dev_t *dev, fido_cred_t *cred, const char *pin,
     54     int *ms)
     55 {
     56 	fido_blob_t	 f;
     57 	fido_blob_t	*ecdh = NULL;
     58 	fido_opt_t	 uv = cred->uv;
     59 	es256_pk_t	*pk = NULL;
     60 	cbor_item_t	*argv[10];
     61 	const uint8_t	 cmd = CTAP_CBOR_MAKECRED;
     62 	int		 r;
     63 
     64 	memset(&f, 0, sizeof(f));
     65 	memset(argv, 0, sizeof(argv));
     66 
     67 	if (cred->cdh.ptr == NULL || cred->type == 0) {
     68 		fido_log_debug("%s: cdh=%p, type=%d", __func__,
     69 		    (void *)cred->cdh.ptr, cred->type);
     70 		r = FIDO_ERR_INVALID_ARGUMENT;
     71 		goto fail;
     72 	}
     73 
     74 	if ((argv[0] = fido_blob_encode(&cred->cdh)) == NULL ||
     75 	    (argv[1] = cbor_encode_rp_entity(&cred->rp)) == NULL ||
     76 	    (argv[2] = cbor_encode_user_entity(&cred->user)) == NULL ||
     77 	    (argv[3] = cbor_encode_pubkey_param(cred->type)) == NULL) {
     78 		fido_log_debug("%s: cbor encode", __func__);
     79 		r = FIDO_ERR_INTERNAL;
     80 		goto fail;
     81 	}
     82 
     83 	/* excluded credentials */
     84 	if (cred->excl.len)
     85 		if ((argv[4] = cbor_encode_pubkey_list(&cred->excl)) == NULL) {
     86 			fido_log_debug("%s: cbor_encode_pubkey_list", __func__);
     87 			r = FIDO_ERR_INTERNAL;
     88 			goto fail;
     89 		}
     90 
     91 	/* extensions */
     92 	if (cred->ext.mask)
     93 		if ((argv[5] = cbor_encode_cred_ext(&cred->ext,
     94 		    &cred->blob)) == NULL) {
     95 			fido_log_debug("%s: cbor_encode_cred_ext", __func__);
     96 			r = FIDO_ERR_INTERNAL;
     97 			goto fail;
     98 		}
     99 
    100 	/* user verification */
    101 	if (pin != NULL || (uv == FIDO_OPT_TRUE &&
    102 	    fido_dev_supports_permissions(dev))) {
    103 		if ((r = fido_do_ecdh(dev, &pk, &ecdh, ms)) != FIDO_OK) {
    104 			fido_log_debug("%s: fido_do_ecdh", __func__);
    105 			goto fail;
    106 		}
    107 		if ((r = cbor_add_uv_params(dev, cmd, &cred->cdh, pk, ecdh,
    108 		    pin, cred->rp.id, &argv[7], &argv[8], ms)) != FIDO_OK) {
    109 			fido_log_debug("%s: cbor_add_uv_params", __func__);
    110 			goto fail;
    111 		}
    112 		uv = FIDO_OPT_OMIT;
    113 	}
    114 
    115 	/* options */
    116 	if (cred->rk != FIDO_OPT_OMIT || uv != FIDO_OPT_OMIT)
    117 		if ((argv[6] = cbor_encode_cred_opt(cred->rk, uv)) == NULL) {
    118 			fido_log_debug("%s: cbor_encode_cred_opt", __func__);
    119 			r = FIDO_ERR_INTERNAL;
    120 			goto fail;
    121 		}
    122 
    123 	/* enterprise attestation */
    124 	if (cred->ea.mode != 0)
    125 		if ((argv[9] = cbor_build_uint8((uint8_t)cred->ea.mode)) ==
    126 		    NULL) {
    127 			fido_log_debug("%s: cbor_build_uint8", __func__);
    128 			r = FIDO_ERR_INTERNAL;
    129 			goto fail;
    130 		}
    131 
    132 	/* framing and transmission */
    133 	if (cbor_build_frame(cmd, argv, nitems(argv), &f) < 0 ||
    134 	    fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
    135 		fido_log_debug("%s: fido_tx", __func__);
    136 		r = FIDO_ERR_TX;
    137 		goto fail;
    138 	}
    139 
    140 	r = FIDO_OK;
    141 fail:
    142 	es256_pk_free(&pk);
    143 	fido_blob_free(&ecdh);
    144 	cbor_vector_free(argv, nitems(argv));
    145 	free(f.ptr);
    146 
    147 	return (r);
    148 }
    149 
    150 static int
    151 fido_dev_make_cred_rx(fido_dev_t *dev, fido_cred_t *cred, int *ms)
    152 {
    153 	unsigned char	*reply;
    154 	int		 reply_len;
    155 	int		 r;
    156 
    157 	fido_cred_reset_rx(cred);
    158 
    159 	if ((reply = malloc(FIDO_MAXMSG_CRED)) == NULL) {
    160 		r = FIDO_ERR_INTERNAL;
    161 		goto fail;
    162 	}
    163 
    164 	if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, reply, FIDO_MAXMSG_CRED,
    165 	    ms)) < 0) {
    166 		fido_log_debug("%s: fido_rx", __func__);
    167 		r = FIDO_ERR_RX;
    168 		goto fail;
    169 	}
    170 
    171 	if ((r = cbor_parse_reply(reply, (size_t)reply_len, cred,
    172 	    parse_makecred_reply)) != FIDO_OK) {
    173 		fido_log_debug("%s: parse_makecred_reply", __func__);
    174 		goto fail;
    175 	}
    176 
    177 	if (cred->fmt == NULL || fido_blob_is_empty(&cred->authdata_cbor) ||
    178 	    fido_blob_is_empty(&cred->attcred.id)) {
    179 		r = FIDO_ERR_INVALID_CBOR;
    180 		goto fail;
    181 	}
    182 
    183 	r = FIDO_OK;
    184 fail:
    185 	free(reply);
    186 
    187 	if (r != FIDO_OK)
    188 		fido_cred_reset_rx(cred);
    189 
    190 	return (r);
    191 }
    192 
    193 static int
    194 fido_dev_make_cred_wait(fido_dev_t *dev, fido_cred_t *cred, const char *pin,
    195     int *ms)
    196 {
    197 	int  r;
    198 
    199 	if ((r = fido_dev_make_cred_tx(dev, cred, pin, ms)) != FIDO_OK ||
    200 	    (r = fido_dev_make_cred_rx(dev, cred, ms)) != FIDO_OK)
    201 		return (r);
    202 
    203 	return (FIDO_OK);
    204 }
    205 
    206 int
    207 fido_dev_make_cred(fido_dev_t *dev, fido_cred_t *cred, const char *pin)
    208 {
    209 	int ms = dev->timeout_ms;
    210 
    211 #ifdef USE_WINHELLO
    212 	if (dev->flags & FIDO_DEV_WINHELLO)
    213 		return (fido_winhello_make_cred(dev, cred, pin, ms));
    214 #endif
    215 	if (fido_dev_is_fido2(dev) == false) {
    216 		if (pin != NULL || cred->rk == FIDO_OPT_TRUE ||
    217 		    cred->ext.mask != 0)
    218 			return (FIDO_ERR_UNSUPPORTED_OPTION);
    219 		return (u2f_register(dev, cred, &ms));
    220 	}
    221 
    222 	return (fido_dev_make_cred_wait(dev, cred, pin, &ms));
    223 }
    224 
    225 static int
    226 check_extensions(const fido_cred_ext_t *authdata_ext,
    227     const fido_cred_ext_t *ext)
    228 {
    229 	fido_cred_ext_t	 tmp;
    230 
    231 	/* XXX: largeBlobKey is not part of the extensions map */
    232 	memcpy(&tmp, ext, sizeof(tmp));
    233 	tmp.mask &= ~FIDO_EXT_LARGEBLOB_KEY;
    234 
    235 	return (timingsafe_bcmp(authdata_ext, &tmp, sizeof(*authdata_ext)));
    236 }
    237 
    238 int
    239 fido_check_rp_id(const char *id, const unsigned char *obtained_hash)
    240 {
    241 	unsigned char expected_hash[SHA256_DIGEST_LENGTH];
    242 
    243 	explicit_bzero(expected_hash, sizeof(expected_hash));
    244 
    245 	if (SHA256((const unsigned char *)id, strlen(id),
    246 	    expected_hash) != expected_hash) {
    247 		fido_log_debug("%s: sha256", __func__);
    248 		return (-1);
    249 	}
    250 
    251 	return (timingsafe_bcmp(expected_hash, obtained_hash,
    252 	    SHA256_DIGEST_LENGTH));
    253 }
    254 
    255 static int
    256 get_signed_hash_u2f(fido_blob_t *dgst, const unsigned char *rp_id,
    257     size_t rp_id_len, const fido_blob_t *clientdata, const fido_blob_t *id,
    258     const es256_pk_t *pk)
    259 {
    260 	const uint8_t	 zero = 0;
    261 	const uint8_t	 four = 4; /* uncompressed point */
    262 	const EVP_MD	*md = NULL;
    263 	EVP_MD_CTX	*ctx = NULL;
    264 	int		 ok = -1;
    265 
    266 	if (dgst->len < SHA256_DIGEST_LENGTH ||
    267 	    (md = EVP_sha256()) == NULL ||
    268 	    (ctx = EVP_MD_CTX_new()) == NULL ||
    269 	    EVP_DigestInit_ex(ctx, md, NULL) != 1 ||
    270 	    EVP_DigestUpdate(ctx, &zero, sizeof(zero)) != 1 ||
    271 	    EVP_DigestUpdate(ctx, rp_id, rp_id_len) != 1 ||
    272 	    EVP_DigestUpdate(ctx, clientdata->ptr, clientdata->len) != 1 ||
    273 	    EVP_DigestUpdate(ctx, id->ptr, id->len) != 1 ||
    274 	    EVP_DigestUpdate(ctx, &four, sizeof(four)) != 1 ||
    275 	    EVP_DigestUpdate(ctx, pk->x, sizeof(pk->x)) != 1 ||
    276 	    EVP_DigestUpdate(ctx, pk->y, sizeof(pk->y)) != 1 ||
    277 	    EVP_DigestFinal_ex(ctx, dgst->ptr, NULL) != 1) {
    278 		fido_log_debug("%s: sha256", __func__);
    279 		goto fail;
    280 	}
    281 	dgst->len = SHA256_DIGEST_LENGTH;
    282 
    283 	ok = 0;
    284 fail:
    285 	EVP_MD_CTX_free(ctx);
    286 
    287 	return (ok);
    288 }
    289 
    290 static int
    291 verify_attstmt(const fido_blob_t *dgst, const fido_attstmt_t *attstmt)
    292 {
    293 	BIO		*rawcert = NULL;
    294 	X509		*cert = NULL;
    295 	EVP_PKEY	*pkey = NULL;
    296 	int		 ok = -1;
    297 
    298 	if (!attstmt->x5c.len) {
    299 		fido_log_debug("%s: x5c.len=%zu", __func__, attstmt->x5c.len);
    300 		return (-1);
    301 	}
    302 
    303 	/* openssl needs ints */
    304 	if (attstmt->x5c.ptr[0].len > INT_MAX) {
    305 		fido_log_debug("%s: x5c[0].len=%zu", __func__,
    306 		    attstmt->x5c.ptr[0].len);
    307 		return (-1);
    308 	}
    309 
    310 	/* fetch key from x509 */
    311 	if ((rawcert = BIO_new_mem_buf(attstmt->x5c.ptr[0].ptr,
    312 	    (int)attstmt->x5c.ptr[0].len)) == NULL ||
    313 	    (cert = d2i_X509_bio(rawcert, NULL)) == NULL ||
    314 	    (pkey = X509_get_pubkey(cert)) == NULL) {
    315 		fido_log_debug("%s: x509 key", __func__);
    316 		goto fail;
    317 	}
    318 
    319 	switch (attstmt->alg) {
    320 	case COSE_UNSPEC:
    321 	case COSE_ES256:
    322 		ok = es256_verify_sig(dgst, pkey, &attstmt->sig);
    323 		break;
    324 	case COSE_ES384:
    325 		ok = es384_verify_sig(dgst, pkey, &attstmt->sig);
    326 		break;
    327 	case COSE_RS256:
    328 		ok = rs256_verify_sig(dgst, pkey, &attstmt->sig);
    329 		break;
    330 	case COSE_RS1:
    331 		ok = rs1_verify_sig(dgst, pkey, &attstmt->sig);
    332 		break;
    333 	case COSE_EDDSA:
    334 		ok = eddsa_verify_sig(dgst, pkey, &attstmt->sig);
    335 		break;
    336 	default:
    337 		fido_log_debug("%s: unknown alg %d", __func__, attstmt->alg);
    338 		break;
    339 	}
    340 
    341 fail:
    342 	BIO_free(rawcert);
    343 	X509_free(cert);
    344 	EVP_PKEY_free(pkey);
    345 
    346 	return (ok);
    347 }
    348 
    349 int
    350 fido_cred_verify(const fido_cred_t *cred)
    351 {
    352 	unsigned char	buf[1024]; /* XXX */
    353 	fido_blob_t	dgst;
    354 	int		cose_alg;
    355 	int		r;
    356 
    357 	dgst.ptr = buf;
    358 	dgst.len = sizeof(buf);
    359 
    360 	/* do we have everything we need? */
    361 	if (cred->cdh.ptr == NULL || cred->authdata_cbor.ptr == NULL ||
    362 	    cred->attstmt.x5c.ptr == NULL || cred->attstmt.sig.ptr == NULL ||
    363 	    cred->fmt == NULL || cred->attcred.id.ptr == NULL ||
    364 	    cred->rp.id == NULL) {
    365 		fido_log_debug("%s: cdh=%p, authdata=%p, x5c=%p, sig=%p, "
    366 		    "fmt=%p id=%p, rp.id=%s", __func__, (void *)cred->cdh.ptr,
    367 		    (void *)cred->authdata_cbor.ptr,
    368 		    (void *)cred->attstmt.x5c.ptr,
    369 		    (void *)cred->attstmt.sig.ptr, (void *)cred->fmt,
    370 		    (void *)cred->attcred.id.ptr, cred->rp.id);
    371 		r = FIDO_ERR_INVALID_ARGUMENT;
    372 		goto out;
    373 	}
    374 
    375 	if (fido_check_rp_id(cred->rp.id, cred->authdata.rp_id_hash) != 0) {
    376 		fido_log_debug("%s: fido_check_rp_id", __func__);
    377 		r = FIDO_ERR_INVALID_PARAM;
    378 		goto out;
    379 	}
    380 
    381 	if (fido_check_flags(cred->authdata.flags, FIDO_OPT_TRUE,
    382 	    cred->uv) < 0) {
    383 		fido_log_debug("%s: fido_check_flags", __func__);
    384 		r = FIDO_ERR_INVALID_PARAM;
    385 		goto out;
    386 	}
    387 
    388 	if (check_extensions(&cred->authdata_ext, &cred->ext) != 0) {
    389 		fido_log_debug("%s: check_extensions", __func__);
    390 		r = FIDO_ERR_INVALID_PARAM;
    391 		goto out;
    392 	}
    393 
    394 	if ((cose_alg = cred->attstmt.alg) == COSE_UNSPEC)
    395 		cose_alg = COSE_ES256; /* backwards compat */
    396 
    397 	if (!strcmp(cred->fmt, "packed")) {
    398 		if (fido_get_signed_hash(cose_alg, &dgst, &cred->cdh,
    399 		    &cred->authdata_cbor) < 0) {
    400 			fido_log_debug("%s: fido_get_signed_hash", __func__);
    401 			r = FIDO_ERR_INTERNAL;
    402 			goto out;
    403 		}
    404 	} else if (!strcmp(cred->fmt, "fido-u2f")) {
    405 		if (get_signed_hash_u2f(&dgst, cred->authdata.rp_id_hash,
    406 		    sizeof(cred->authdata.rp_id_hash), &cred->cdh,
    407 		    &cred->attcred.id, &cred->attcred.pubkey.es256) < 0) {
    408 			fido_log_debug("%s: get_signed_hash_u2f", __func__);
    409 			r = FIDO_ERR_INTERNAL;
    410 			goto out;
    411 		}
    412 	} else if (!strcmp(cred->fmt, "tpm")) {
    413 		if (fido_get_signed_hash_tpm(&dgst, &cred->cdh,
    414 		    &cred->authdata_raw, &cred->attstmt, &cred->attcred) < 0) {
    415 			fido_log_debug("%s: fido_get_signed_hash_tpm", __func__);
    416 			r = FIDO_ERR_INTERNAL;
    417 			goto out;
    418 		}
    419 	} else {
    420 		fido_log_debug("%s: unknown fmt %s", __func__, cred->fmt);
    421 		r = FIDO_ERR_INVALID_ARGUMENT;
    422 		goto out;
    423 	}
    424 
    425 	if (verify_attstmt(&dgst, &cred->attstmt) < 0) {
    426 		fido_log_debug("%s: verify_attstmt", __func__);
    427 		r = FIDO_ERR_INVALID_SIG;
    428 		goto out;
    429 	}
    430 
    431 	r = FIDO_OK;
    432 out:
    433 	explicit_bzero(buf, sizeof(buf));
    434 
    435 	return (r);
    436 }
    437 
    438 int
    439 fido_cred_verify_self(const fido_cred_t *cred)
    440 {
    441 	unsigned char	buf[1024]; /* XXX */
    442 	fido_blob_t	dgst;
    443 	int		ok = -1;
    444 	int		r;
    445 
    446 	dgst.ptr = buf;
    447 	dgst.len = sizeof(buf);
    448 
    449 	/* do we have everything we need? */
    450 	if (cred->cdh.ptr == NULL || cred->authdata_cbor.ptr == NULL ||
    451 	    cred->attstmt.x5c.ptr != NULL || cred->attstmt.sig.ptr == NULL ||
    452 	    cred->fmt == NULL || cred->attcred.id.ptr == NULL ||
    453 	    cred->rp.id == NULL) {
    454 		fido_log_debug("%s: cdh=%p, authdata=%p, x5c=%p, sig=%p, "
    455 		    "fmt=%p id=%p, rp.id=%s", __func__, (void *)cred->cdh.ptr,
    456 		    (void *)cred->authdata_cbor.ptr,
    457 		    (void *)cred->attstmt.x5c.ptr,
    458 		    (void *)cred->attstmt.sig.ptr, (void *)cred->fmt,
    459 		    (void *)cred->attcred.id.ptr, cred->rp.id);
    460 		r = FIDO_ERR_INVALID_ARGUMENT;
    461 		goto out;
    462 	}
    463 
    464 	if (fido_check_rp_id(cred->rp.id, cred->authdata.rp_id_hash) != 0) {
    465 		fido_log_debug("%s: fido_check_rp_id", __func__);
    466 		r = FIDO_ERR_INVALID_PARAM;
    467 		goto out;
    468 	}
    469 
    470 	if (fido_check_flags(cred->authdata.flags, FIDO_OPT_TRUE,
    471 	    cred->uv) < 0) {
    472 		fido_log_debug("%s: fido_check_flags", __func__);
    473 		r = FIDO_ERR_INVALID_PARAM;
    474 		goto out;
    475 	}
    476 
    477 	if (check_extensions(&cred->authdata_ext, &cred->ext) != 0) {
    478 		fido_log_debug("%s: check_extensions", __func__);
    479 		r = FIDO_ERR_INVALID_PARAM;
    480 		goto out;
    481 	}
    482 
    483 	if (!strcmp(cred->fmt, "packed")) {
    484 		if (fido_get_signed_hash(cred->attcred.type, &dgst, &cred->cdh,
    485 		    &cred->authdata_cbor) < 0) {
    486 			fido_log_debug("%s: fido_get_signed_hash", __func__);
    487 			r = FIDO_ERR_INTERNAL;
    488 			goto out;
    489 		}
    490 	} else if (!strcmp(cred->fmt, "fido-u2f")) {
    491 		if (get_signed_hash_u2f(&dgst, cred->authdata.rp_id_hash,
    492 		    sizeof(cred->authdata.rp_id_hash), &cred->cdh,
    493 		    &cred->attcred.id, &cred->attcred.pubkey.es256) < 0) {
    494 			fido_log_debug("%s: get_signed_hash_u2f", __func__);
    495 			r = FIDO_ERR_INTERNAL;
    496 			goto out;
    497 		}
    498 	} else {
    499 		fido_log_debug("%s: unknown fmt %s", __func__, cred->fmt);
    500 		r = FIDO_ERR_INVALID_ARGUMENT;
    501 		goto out;
    502 	}
    503 
    504 	switch (cred->attcred.type) {
    505 	case COSE_ES256:
    506 		ok = es256_pk_verify_sig(&dgst, &cred->attcred.pubkey.es256,
    507 		    &cred->attstmt.sig);
    508 		break;
    509 	case COSE_ES384:
    510 		ok = es384_pk_verify_sig(&dgst, &cred->attcred.pubkey.es384,
    511 		    &cred->attstmt.sig);
    512 		break;
    513 	case COSE_RS256:
    514 		ok = rs256_pk_verify_sig(&dgst, &cred->attcred.pubkey.rs256,
    515 		    &cred->attstmt.sig);
    516 		break;
    517 	case COSE_EDDSA:
    518 		ok = eddsa_pk_verify_sig(&dgst, &cred->attcred.pubkey.eddsa,
    519 		    &cred->attstmt.sig);
    520 		break;
    521 	default:
    522 		fido_log_debug("%s: unsupported cose_alg %d", __func__,
    523 		    cred->attcred.type);
    524 		r = FIDO_ERR_UNSUPPORTED_OPTION;
    525 		goto out;
    526 	}
    527 
    528 	if (ok < 0)
    529 		r = FIDO_ERR_INVALID_SIG;
    530 	else
    531 		r = FIDO_OK;
    532 
    533 out:
    534 	explicit_bzero(buf, sizeof(buf));
    535 
    536 	return (r);
    537 }
    538 
    539 fido_cred_t *
    540 fido_cred_new(void)
    541 {
    542 	return (calloc(1, sizeof(fido_cred_t)));
    543 }
    544 
    545 static void
    546 fido_cred_clean_authdata(fido_cred_t *cred)
    547 {
    548 	fido_blob_reset(&cred->authdata_cbor);
    549 	fido_blob_reset(&cred->authdata_raw);
    550 	fido_blob_reset(&cred->attcred.id);
    551 
    552 	memset(&cred->authdata_ext, 0, sizeof(cred->authdata_ext));
    553 	memset(&cred->authdata, 0, sizeof(cred->authdata));
    554 	memset(&cred->attcred, 0, sizeof(cred->attcred));
    555 }
    556 
    557 static void
    558 fido_cred_clean_attstmt(fido_attstmt_t *attstmt)
    559 {
    560 	fido_blob_reset(&attstmt->certinfo);
    561 	fido_blob_reset(&attstmt->pubarea);
    562 	fido_blob_reset(&attstmt->cbor);
    563 	fido_free_blob_array(&attstmt->x5c);
    564 	fido_blob_reset(&attstmt->sig);
    565 
    566 	memset(attstmt, 0, sizeof(*attstmt));
    567 }
    568 
    569 static void
    570 fido_cred_clean_attobj(fido_cred_t *cred)
    571 {
    572 	free(cred->fmt);
    573 	cred->fmt = NULL;
    574 	fido_cred_clean_authdata(cred);
    575 	fido_cred_clean_attstmt(&cred->attstmt);
    576 }
    577 
    578 void
    579 fido_cred_reset_tx(fido_cred_t *cred)
    580 {
    581 	fido_blob_reset(&cred->cd);
    582 	fido_blob_reset(&cred->cdh);
    583 	fido_blob_reset(&cred->user.id);
    584 	fido_blob_reset(&cred->blob);
    585 
    586 	free(cred->rp.id);
    587 	free(cred->rp.name);
    588 	free(cred->user.icon);
    589 	free(cred->user.name);
    590 	free(cred->user.display_name);
    591 	fido_cred_empty_exclude_list(cred);
    592 
    593 	memset(&cred->rp, 0, sizeof(cred->rp));
    594 	memset(&cred->user, 0, sizeof(cred->user));
    595 	memset(&cred->ext, 0, sizeof(cred->ext));
    596 
    597 	cred->type = 0;
    598 	cred->rk = FIDO_OPT_OMIT;
    599 	cred->uv = FIDO_OPT_OMIT;
    600 	cred->ea.mode = 0;
    601 }
    602 
    603 void
    604 fido_cred_reset_rx(fido_cred_t *cred)
    605 {
    606 	fido_cred_clean_attobj(cred);
    607 	fido_blob_reset(&cred->largeblob_key);
    608 	cred->ea.att = false;
    609 }
    610 
    611 void
    612 fido_cred_free(fido_cred_t **cred_p)
    613 {
    614 	fido_cred_t *cred;
    615 
    616 	if (cred_p == NULL || (cred = *cred_p) == NULL)
    617 		return;
    618 	fido_cred_reset_tx(cred);
    619 	fido_cred_reset_rx(cred);
    620 	free(cred);
    621 	*cred_p = NULL;
    622 }
    623 
    624 int
    625 fido_cred_set_authdata(fido_cred_t *cred, const unsigned char *ptr, size_t len)
    626 {
    627 	cbor_item_t		*item = NULL;
    628 	struct cbor_load_result	 cbor;
    629 	int			 r = FIDO_ERR_INVALID_ARGUMENT;
    630 
    631 	fido_cred_clean_authdata(cred);
    632 
    633 	if (ptr == NULL || len == 0)
    634 		goto fail;
    635 
    636 	if ((item = cbor_load(ptr, len, &cbor)) == NULL) {
    637 		fido_log_debug("%s: cbor_load", __func__);
    638 		goto fail;
    639 	}
    640 
    641 	if (fido_blob_decode(item, &cred->authdata_raw) < 0) {
    642 		fido_log_debug("%s: fido_blob_decode", __func__);
    643 		goto fail;
    644 	}
    645 
    646 	if (cbor_decode_cred_authdata(item, cred->type, &cred->authdata_cbor,
    647 	    &cred->authdata, &cred->attcred, &cred->authdata_ext) < 0) {
    648 		fido_log_debug("%s: cbor_decode_cred_authdata", __func__);
    649 		goto fail;
    650 	}
    651 
    652 	r = FIDO_OK;
    653 fail:
    654 	if (item != NULL)
    655 		cbor_decref(&item);
    656 
    657 	if (r != FIDO_OK)
    658 		fido_cred_clean_authdata(cred);
    659 
    660 	return (r);
    661 }
    662 
    663 int
    664 fido_cred_set_authdata_raw(fido_cred_t *cred, const unsigned char *ptr,
    665     size_t len)
    666 {
    667 	cbor_item_t	*item = NULL;
    668 	int		 r = FIDO_ERR_INVALID_ARGUMENT;
    669 
    670 	fido_cred_clean_authdata(cred);
    671 
    672 	if (ptr == NULL || len == 0)
    673 		goto fail;
    674 
    675 	if (fido_blob_set(&cred->authdata_raw, ptr, len) < 0) {
    676 		fido_log_debug("%s: fido_blob_set", __func__);
    677 		r = FIDO_ERR_INTERNAL;
    678 		goto fail;
    679 	}
    680 
    681 	if ((item = cbor_build_bytestring(ptr, len)) == NULL) {
    682 		fido_log_debug("%s: cbor_build_bytestring", __func__);
    683 		r = FIDO_ERR_INTERNAL;
    684 		goto fail;
    685 	}
    686 
    687 	if (cbor_decode_cred_authdata(item, cred->type, &cred->authdata_cbor,
    688 	    &cred->authdata, &cred->attcred, &cred->authdata_ext) < 0) {
    689 		fido_log_debug("%s: cbor_decode_cred_authdata", __func__);
    690 		goto fail;
    691 	}
    692 
    693 	r = FIDO_OK;
    694 fail:
    695 	if (item != NULL)
    696 		cbor_decref(&item);
    697 
    698 	if (r != FIDO_OK)
    699 		fido_cred_clean_authdata(cred);
    700 
    701 	return (r);
    702 }
    703 
    704 int
    705 fido_cred_set_id(fido_cred_t *cred, const unsigned char *ptr, size_t len)
    706 {
    707 	if (fido_blob_set(&cred->attcred.id, ptr, len) < 0)
    708 		return (FIDO_ERR_INVALID_ARGUMENT);
    709 
    710 	return (FIDO_OK);
    711 }
    712 
    713 int
    714 fido_cred_set_x509(fido_cred_t *cred, const unsigned char *ptr, size_t len)
    715 {
    716 	fido_blob_t x5c_blob;
    717 	fido_blob_t *list_ptr = NULL;
    718 
    719 	memset(&x5c_blob, 0, sizeof(x5c_blob));
    720 	fido_free_blob_array(&cred->attstmt.x5c);
    721 
    722 	if (fido_blob_set(&x5c_blob, ptr, len) < 0)
    723 		return (FIDO_ERR_INVALID_ARGUMENT);
    724 
    725 	if (cred->attstmt.x5c.len == SIZE_MAX) {
    726 		fido_blob_reset(&x5c_blob);
    727 		return (FIDO_ERR_INVALID_ARGUMENT);
    728 	}
    729 
    730 	if ((list_ptr = recallocarray(cred->attstmt.x5c.ptr,
    731 	    cred->attstmt.x5c.len, cred->attstmt.x5c.len + 1,
    732 	    sizeof(x5c_blob))) == NULL) {
    733 		fido_blob_reset(&x5c_blob);
    734 		return (FIDO_ERR_INTERNAL);
    735 	}
    736 
    737 	list_ptr[cred->attstmt.x5c.len++] = x5c_blob;
    738 	cred->attstmt.x5c.ptr = list_ptr;
    739 
    740 	return (FIDO_OK);
    741 }
    742 
    743 int
    744 fido_cred_set_sig(fido_cred_t *cred, const unsigned char *ptr, size_t len)
    745 {
    746 	if (fido_blob_set(&cred->attstmt.sig, ptr, len) < 0)
    747 		return (FIDO_ERR_INVALID_ARGUMENT);
    748 
    749 	return (FIDO_OK);
    750 }
    751 
    752 int
    753 fido_cred_set_attstmt(fido_cred_t *cred, const unsigned char *ptr, size_t len)
    754 {
    755 	cbor_item_t		*item = NULL;
    756 	struct cbor_load_result	 cbor;
    757 	int			 r = FIDO_ERR_INVALID_ARGUMENT;
    758 
    759 	fido_cred_clean_attstmt(&cred->attstmt);
    760 
    761 	if (ptr == NULL || len == 0)
    762 		goto fail;
    763 
    764 	if ((item = cbor_load(ptr, len, &cbor)) == NULL) {
    765 		fido_log_debug("%s: cbor_load", __func__);
    766 		goto fail;
    767 	}
    768 
    769 	if (cbor_decode_attstmt(item, &cred->attstmt) < 0) {
    770 		fido_log_debug("%s: cbor_decode_attstmt", __func__);
    771 		goto fail;
    772 	}
    773 
    774 	r = FIDO_OK;
    775 fail:
    776 	if (item != NULL)
    777 		cbor_decref(&item);
    778 
    779 	if (r != FIDO_OK)
    780 		fido_cred_clean_attstmt(&cred->attstmt);
    781 
    782 	return (r);
    783 }
    784 
    785 int
    786 fido_cred_set_attobj(fido_cred_t *cred, const unsigned char *ptr, size_t len)
    787 {
    788 	cbor_item_t		*item = NULL;
    789 	struct cbor_load_result	 cbor;
    790 	int			 r = FIDO_ERR_INVALID_ARGUMENT;
    791 
    792 	fido_cred_clean_attobj(cred);
    793 
    794 	if (ptr == NULL || len == 0)
    795 		goto fail;
    796 
    797 	if ((item = cbor_load(ptr, len, &cbor)) == NULL) {
    798 		fido_log_debug("%s: cbor_load", __func__);
    799 		goto fail;
    800 	}
    801 	if (cbor_decode_attobj(item, cred) != 0) {
    802 		fido_log_debug("%s: cbor_decode_attobj", __func__);
    803 		goto fail;
    804 	}
    805 
    806 	r = FIDO_OK;
    807 fail:
    808 	if (item != NULL)
    809 		cbor_decref(&item);
    810 
    811 	return (r);
    812 }
    813 
    814 int
    815 fido_cred_exclude(fido_cred_t *cred, const unsigned char *id_ptr, size_t id_len)
    816 {
    817 	fido_blob_t id_blob;
    818 	fido_blob_t *list_ptr;
    819 
    820 	memset(&id_blob, 0, sizeof(id_blob));
    821 
    822 	if (fido_blob_set(&id_blob, id_ptr, id_len) < 0)
    823 		return (FIDO_ERR_INVALID_ARGUMENT);
    824 
    825 	if (cred->excl.len == SIZE_MAX) {
    826 		free(id_blob.ptr);
    827 		return (FIDO_ERR_INVALID_ARGUMENT);
    828 	}
    829 
    830 	if ((list_ptr = recallocarray(cred->excl.ptr, cred->excl.len,
    831 	    cred->excl.len + 1, sizeof(fido_blob_t))) == NULL) {
    832 		free(id_blob.ptr);
    833 		return (FIDO_ERR_INTERNAL);
    834 	}
    835 
    836 	list_ptr[cred->excl.len++] = id_blob;
    837 	cred->excl.ptr = list_ptr;
    838 
    839 	return (FIDO_OK);
    840 }
    841 
    842 int
    843 fido_cred_empty_exclude_list(fido_cred_t *cred)
    844 {
    845 	fido_free_blob_array(&cred->excl);
    846 	memset(&cred->excl, 0, sizeof(cred->excl));
    847 
    848 	return (FIDO_OK);
    849 }
    850 
    851 int
    852 fido_cred_set_clientdata(fido_cred_t *cred, const unsigned char *data,
    853     size_t data_len)
    854 {
    855 	if (!fido_blob_is_empty(&cred->cdh) ||
    856 	    fido_blob_set(&cred->cd, data, data_len) < 0) {
    857 		return (FIDO_ERR_INVALID_ARGUMENT);
    858 	}
    859 	if (fido_sha256(&cred->cdh, data, data_len) < 0) {
    860 		fido_blob_reset(&cred->cd);
    861 		return (FIDO_ERR_INTERNAL);
    862 	}
    863 
    864 	return (FIDO_OK);
    865 }
    866 
    867 int
    868 fido_cred_set_clientdata_hash(fido_cred_t *cred, const unsigned char *hash,
    869     size_t hash_len)
    870 {
    871 	if (!fido_blob_is_empty(&cred->cd) ||
    872 	    fido_blob_set(&cred->cdh, hash, hash_len) < 0)
    873 		return (FIDO_ERR_INVALID_ARGUMENT);
    874 
    875 	return (FIDO_OK);
    876 }
    877 
    878 int
    879 fido_cred_set_rp(fido_cred_t *cred, const char *id, const char *name)
    880 {
    881 	fido_rp_t *rp = &cred->rp;
    882 
    883 	if (rp->id != NULL) {
    884 		free(rp->id);
    885 		rp->id = NULL;
    886 	}
    887 	if (rp->name != NULL) {
    888 		free(rp->name);
    889 		rp->name = NULL;
    890 	}
    891 
    892 	if (id != NULL && (rp->id = strdup(id)) == NULL)
    893 		goto fail;
    894 	if (name != NULL && (rp->name = strdup(name)) == NULL)
    895 		goto fail;
    896 
    897 	return (FIDO_OK);
    898 fail:
    899 	free(rp->id);
    900 	free(rp->name);
    901 	rp->id = NULL;
    902 	rp->name = NULL;
    903 
    904 	return (FIDO_ERR_INTERNAL);
    905 }
    906 
    907 int
    908 fido_cred_set_user(fido_cred_t *cred, const unsigned char *user_id,
    909     size_t user_id_len, const char *name, const char *display_name,
    910     const char *icon)
    911 {
    912 	fido_user_t *up = &cred->user;
    913 
    914 	if (up->id.ptr != NULL) {
    915 		free(up->id.ptr);
    916 		up->id.ptr = NULL;
    917 		up->id.len = 0;
    918 	}
    919 	if (up->name != NULL) {
    920 		free(up->name);
    921 		up->name = NULL;
    922 	}
    923 	if (up->display_name != NULL) {
    924 		free(up->display_name);
    925 		up->display_name = NULL;
    926 	}
    927 	if (up->icon != NULL) {
    928 		free(up->icon);
    929 		up->icon = NULL;
    930 	}
    931 
    932 	if (user_id != NULL && fido_blob_set(&up->id, user_id, user_id_len) < 0)
    933 		goto fail;
    934 	if (name != NULL && (up->name = strdup(name)) == NULL)
    935 		goto fail;
    936 	if (display_name != NULL &&
    937 	    (up->display_name = strdup(display_name)) == NULL)
    938 		goto fail;
    939 	if (icon != NULL && (up->icon = strdup(icon)) == NULL)
    940 		goto fail;
    941 
    942 	return (FIDO_OK);
    943 fail:
    944 	free(up->id.ptr);
    945 	free(up->name);
    946 	free(up->display_name);
    947 	free(up->icon);
    948 
    949 	up->id.ptr = NULL;
    950 	up->id.len = 0;
    951 	up->name = NULL;
    952 	up->display_name = NULL;
    953 	up->icon = NULL;
    954 
    955 	return (FIDO_ERR_INTERNAL);
    956 }
    957 
    958 int
    959 fido_cred_set_extensions(fido_cred_t *cred, int ext)
    960 {
    961 	if (ext == 0)
    962 		cred->ext.mask = 0;
    963 	else {
    964 		if ((ext & FIDO_EXT_CRED_MASK) != ext)
    965 			return (FIDO_ERR_INVALID_ARGUMENT);
    966 		cred->ext.mask |= ext;
    967 	}
    968 
    969 	return (FIDO_OK);
    970 }
    971 
    972 int
    973 fido_cred_set_options(fido_cred_t *cred, bool rk, bool uv)
    974 {
    975 	cred->rk = rk ? FIDO_OPT_TRUE : FIDO_OPT_FALSE;
    976 	cred->uv = uv ? FIDO_OPT_TRUE : FIDO_OPT_FALSE;
    977 
    978 	return (FIDO_OK);
    979 }
    980 
    981 int
    982 fido_cred_set_rk(fido_cred_t *cred, fido_opt_t rk)
    983 {
    984 	cred->rk = rk;
    985 
    986 	return (FIDO_OK);
    987 }
    988 
    989 int
    990 fido_cred_set_uv(fido_cred_t *cred, fido_opt_t uv)
    991 {
    992 	cred->uv = uv;
    993 
    994 	return (FIDO_OK);
    995 }
    996 
    997 int
    998 fido_cred_set_entattest(fido_cred_t *cred, int ea)
    999 {
   1000 	if (ea != 0 && ea != FIDO_ENTATTEST_VENDOR &&
   1001 	    ea != FIDO_ENTATTEST_PLATFORM)
   1002 		return (FIDO_ERR_INVALID_ARGUMENT);
   1003 
   1004 	cred->ea.mode = ea;
   1005 
   1006 	return (FIDO_OK);
   1007 }
   1008 
   1009 int
   1010 fido_cred_set_prot(fido_cred_t *cred, int prot)
   1011 {
   1012 	if (prot == 0) {
   1013 		cred->ext.mask &= ~FIDO_EXT_CRED_PROTECT;
   1014 		cred->ext.prot = 0;
   1015 	} else {
   1016 		if (prot != FIDO_CRED_PROT_UV_OPTIONAL &&
   1017 		    prot != FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID &&
   1018 		    prot != FIDO_CRED_PROT_UV_REQUIRED)
   1019 			return (FIDO_ERR_INVALID_ARGUMENT);
   1020 
   1021 		cred->ext.mask |= FIDO_EXT_CRED_PROTECT;
   1022 		cred->ext.prot = prot;
   1023 	}
   1024 
   1025 	return (FIDO_OK);
   1026 }
   1027 
   1028 int
   1029 fido_cred_set_pin_minlen(fido_cred_t *cred, size_t len)
   1030 {
   1031 	if (len == 0)
   1032 		cred->ext.mask &= ~FIDO_EXT_MINPINLEN;
   1033 	else
   1034 		cred->ext.mask |= FIDO_EXT_MINPINLEN;
   1035 
   1036 	cred->ext.minpinlen = len;
   1037 
   1038 	return (FIDO_OK);
   1039 }
   1040 
   1041 int
   1042 fido_cred_set_blob(fido_cred_t *cred, const unsigned char *ptr, size_t len)
   1043 {
   1044 	if (ptr == NULL || len == 0)
   1045 		return (FIDO_ERR_INVALID_ARGUMENT);
   1046 	if (fido_blob_set(&cred->blob, ptr, len) < 0)
   1047 		return (FIDO_ERR_INTERNAL);
   1048 
   1049 	cred->ext.mask |= FIDO_EXT_CRED_BLOB;
   1050 
   1051 	return (FIDO_OK);
   1052 }
   1053 
   1054 int
   1055 fido_cred_set_fmt(fido_cred_t *cred, const char *fmt)
   1056 {
   1057 	free(cred->fmt);
   1058 	cred->fmt = NULL;
   1059 
   1060 	if (fmt == NULL)
   1061 		return (FIDO_ERR_INVALID_ARGUMENT);
   1062 
   1063 	if (strcmp(fmt, "packed") && strcmp(fmt, "fido-u2f") &&
   1064 	    strcmp(fmt, "none") && strcmp(fmt, "tpm"))
   1065 		return (FIDO_ERR_INVALID_ARGUMENT);
   1066 
   1067 	if ((cred->fmt = strdup(fmt)) == NULL)
   1068 		return (FIDO_ERR_INTERNAL);
   1069 
   1070 	return (FIDO_OK);
   1071 }
   1072 
   1073 int
   1074 fido_cred_set_type(fido_cred_t *cred, int cose_alg)
   1075 {
   1076 	if (cred->type != 0)
   1077 		return (FIDO_ERR_INVALID_ARGUMENT);
   1078 	if (cose_alg != COSE_ES256 && cose_alg != COSE_ES384 &&
   1079 	    cose_alg != COSE_RS256 && cose_alg != COSE_EDDSA)
   1080 		return (FIDO_ERR_INVALID_ARGUMENT);
   1081 
   1082 	cred->type = cose_alg;
   1083 
   1084 	return (FIDO_OK);
   1085 }
   1086 
   1087 int
   1088 fido_cred_type(const fido_cred_t *cred)
   1089 {
   1090 	return (cred->type);
   1091 }
   1092 
   1093 uint8_t
   1094 fido_cred_flags(const fido_cred_t *cred)
   1095 {
   1096 	return (cred->authdata.flags);
   1097 }
   1098 
   1099 uint32_t
   1100 fido_cred_sigcount(const fido_cred_t *cred)
   1101 {
   1102 	return (cred->authdata.sigcount);
   1103 }
   1104 
   1105 const unsigned char *
   1106 fido_cred_clientdata_hash_ptr(const fido_cred_t *cred)
   1107 {
   1108 	return (cred->cdh.ptr);
   1109 }
   1110 
   1111 size_t
   1112 fido_cred_clientdata_hash_len(const fido_cred_t *cred)
   1113 {
   1114 	return (cred->cdh.len);
   1115 }
   1116 
   1117 const unsigned char *
   1118 fido_cred_x5c_ptr(const fido_cred_t *cred)
   1119 {
   1120 	return (fido_cred_x5c_list_ptr(cred, 0));
   1121 }
   1122 
   1123 size_t
   1124 fido_cred_x5c_len(const fido_cred_t *cred)
   1125 {
   1126 	return (fido_cred_x5c_list_len(cred, 0));
   1127 }
   1128 
   1129 size_t
   1130 fido_cred_x5c_list_count(const fido_cred_t *cred)
   1131 {
   1132 	return (cred->attstmt.x5c.len);
   1133 }
   1134 
   1135 const unsigned char *
   1136 fido_cred_x5c_list_ptr(const fido_cred_t *cred, size_t i)
   1137 {
   1138 	if (i >= cred->attstmt.x5c.len)
   1139 		return (NULL);
   1140 
   1141 	return (cred->attstmt.x5c.ptr[i].ptr);
   1142 }
   1143 
   1144 size_t
   1145 fido_cred_x5c_list_len(const fido_cred_t *cred, size_t i)
   1146 {
   1147 	if (i >= cred->attstmt.x5c.len)
   1148 		return (0);
   1149 
   1150 	return (cred->attstmt.x5c.ptr[i].len);
   1151 }
   1152 
   1153 const unsigned char *
   1154 fido_cred_sig_ptr(const fido_cred_t *cred)
   1155 {
   1156 	return (cred->attstmt.sig.ptr);
   1157 }
   1158 
   1159 size_t
   1160 fido_cred_sig_len(const fido_cred_t *cred)
   1161 {
   1162 	return (cred->attstmt.sig.len);
   1163 }
   1164 
   1165 const unsigned char *
   1166 fido_cred_authdata_ptr(const fido_cred_t *cred)
   1167 {
   1168 	return (cred->authdata_cbor.ptr);
   1169 }
   1170 
   1171 size_t
   1172 fido_cred_authdata_len(const fido_cred_t *cred)
   1173 {
   1174 	return (cred->authdata_cbor.len);
   1175 }
   1176 
   1177 const unsigned char *
   1178 fido_cred_authdata_raw_ptr(const fido_cred_t *cred)
   1179 {
   1180 	return (cred->authdata_raw.ptr);
   1181 }
   1182 
   1183 size_t
   1184 fido_cred_authdata_raw_len(const fido_cred_t *cred)
   1185 {
   1186 	return (cred->authdata_raw.len);
   1187 }
   1188 
   1189 const unsigned char *
   1190 fido_cred_attstmt_ptr(const fido_cred_t *cred)
   1191 {
   1192 	return (cred->attstmt.cbor.ptr);
   1193 }
   1194 
   1195 size_t
   1196 fido_cred_attstmt_len(const fido_cred_t *cred)
   1197 {
   1198 	return (cred->attstmt.cbor.len);
   1199 }
   1200 
   1201 const unsigned char *
   1202 fido_cred_pubkey_ptr(const fido_cred_t *cred)
   1203 {
   1204 	const void *ptr;
   1205 
   1206 	switch (cred->attcred.type) {
   1207 	case COSE_ES256:
   1208 		ptr = &cred->attcred.pubkey.es256;
   1209 		break;
   1210 	case COSE_ES384:
   1211 		ptr = &cred->attcred.pubkey.es384;
   1212 		break;
   1213 	case COSE_RS256:
   1214 		ptr = &cred->attcred.pubkey.rs256;
   1215 		break;
   1216 	case COSE_EDDSA:
   1217 		ptr = &cred->attcred.pubkey.eddsa;
   1218 		break;
   1219 	default:
   1220 		ptr = NULL;
   1221 		break;
   1222 	}
   1223 
   1224 	return (ptr);
   1225 }
   1226 
   1227 size_t
   1228 fido_cred_pubkey_len(const fido_cred_t *cred)
   1229 {
   1230 	size_t len;
   1231 
   1232 	switch (cred->attcred.type) {
   1233 	case COSE_ES256:
   1234 		len = sizeof(cred->attcred.pubkey.es256);
   1235 		break;
   1236 	case COSE_ES384:
   1237 		len = sizeof(cred->attcred.pubkey.es384);
   1238 		break;
   1239 	case COSE_RS256:
   1240 		len = sizeof(cred->attcred.pubkey.rs256);
   1241 		break;
   1242 	case COSE_EDDSA:
   1243 		len = sizeof(cred->attcred.pubkey.eddsa);
   1244 		break;
   1245 	default:
   1246 		len = 0;
   1247 		break;
   1248 	}
   1249 
   1250 	return (len);
   1251 }
   1252 
   1253 const unsigned char *
   1254 fido_cred_id_ptr(const fido_cred_t *cred)
   1255 {
   1256 	return (cred->attcred.id.ptr);
   1257 }
   1258 
   1259 size_t
   1260 fido_cred_id_len(const fido_cred_t *cred)
   1261 {
   1262 	return (cred->attcred.id.len);
   1263 }
   1264 
   1265 const unsigned char *
   1266 fido_cred_aaguid_ptr(const fido_cred_t *cred)
   1267 {
   1268 	return (cred->attcred.aaguid);
   1269 }
   1270 
   1271 size_t
   1272 fido_cred_aaguid_len(const fido_cred_t *cred)
   1273 {
   1274 	return (sizeof(cred->attcred.aaguid));
   1275 }
   1276 
   1277 int
   1278 fido_cred_prot(const fido_cred_t *cred)
   1279 {
   1280 	return (cred->ext.prot);
   1281 }
   1282 
   1283 size_t
   1284 fido_cred_pin_minlen(const fido_cred_t *cred)
   1285 {
   1286 	return (cred->ext.minpinlen);
   1287 }
   1288 
   1289 const char *
   1290 fido_cred_fmt(const fido_cred_t *cred)
   1291 {
   1292 	return (cred->fmt);
   1293 }
   1294 
   1295 const char *
   1296 fido_cred_rp_id(const fido_cred_t *cred)
   1297 {
   1298 	return (cred->rp.id);
   1299 }
   1300 
   1301 const char *
   1302 fido_cred_rp_name(const fido_cred_t *cred)
   1303 {
   1304 	return (cred->rp.name);
   1305 }
   1306 
   1307 const char *
   1308 fido_cred_user_name(const fido_cred_t *cred)
   1309 {
   1310 	return (cred->user.name);
   1311 }
   1312 
   1313 const char *
   1314 fido_cred_display_name(const fido_cred_t *cred)
   1315 {
   1316 	return (cred->user.display_name);
   1317 }
   1318 
   1319 const unsigned char *
   1320 fido_cred_user_id_ptr(const fido_cred_t *cred)
   1321 {
   1322 	return (cred->user.id.ptr);
   1323 }
   1324 
   1325 size_t
   1326 fido_cred_user_id_len(const fido_cred_t *cred)
   1327 {
   1328 	return (cred->user.id.len);
   1329 }
   1330 
   1331 const unsigned char *
   1332 fido_cred_largeblob_key_ptr(const fido_cred_t *cred)
   1333 {
   1334 	return (cred->largeblob_key.ptr);
   1335 }
   1336 
   1337 size_t
   1338 fido_cred_largeblob_key_len(const fido_cred_t *cred)
   1339 {
   1340 	return (cred->largeblob_key.len);
   1341 }
   1342 
   1343 bool
   1344 fido_cred_entattest(const fido_cred_t *cred)
   1345 {
   1346 	return (cred->ea.att);
   1347 }
   1348