xref: /freebsd/contrib/libfido2/src/cred.c (revision 9c77fb6aaa366cbabc80ee1b834bcfe4df135491)
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