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