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