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