xref: /openbsd/lib/libfido2/src/cred.c (revision ab19a69e)
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 
17 static int
parse_makecred_reply(const cbor_item_t * key,const cbor_item_t * val,void * arg)18 parse_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 
49 static int
fido_dev_make_cred_tx(fido_dev_t * dev,fido_cred_t * cred,const char * pin,int * ms)50 fido_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;
129 fail:
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 
138 static int
fido_dev_make_cred_rx(fido_dev_t * dev,fido_cred_t * cred,int * ms)139 fido_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;
172 fail:
173 	free(reply);
174 
175 	if (r != FIDO_OK)
176 		fido_cred_reset_rx(cred);
177 
178 	return (r);
179 }
180 
181 static int
fido_dev_make_cred_wait(fido_dev_t * dev,fido_cred_t * cred,const char * pin,int * ms)182 fido_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 
194 int
fido_dev_make_cred(fido_dev_t * dev,fido_cred_t * cred,const char * pin)195 fido_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 
213 static int
check_extensions(const fido_cred_ext_t * authdata_ext,const fido_cred_ext_t * ext)214 check_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 
226 int
fido_check_rp_id(const char * id,const unsigned char * obtained_hash)227 fido_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 
243 static int
get_signed_hash_u2f(fido_blob_t * dgst,const unsigned char * rp_id,size_t rp_id_len,const fido_blob_t * clientdata,const fido_blob_t * id,const es256_pk_t * pk)244 get_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;
271 fail:
272 	EVP_MD_CTX_free(ctx);
273 
274 	return (ok);
275 }
276 
277 static int
verify_attstmt(const fido_blob_t * dgst,const fido_attstmt_t * attstmt)278 verify_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 
319 fail:
320 	BIO_free(rawcert);
321 	X509_free(cert);
322 	EVP_PKEY_free(pkey);
323 
324 	return (ok);
325 }
326 
327 int
fido_cred_verify(const fido_cred_t * cred)328 fido_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;
406 out:
407 	explicit_bzero(buf, sizeof(buf));
408 
409 	return (r);
410 }
411 
412 int
fido_cred_verify_self(const fido_cred_t * cred)413 fido_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 
503 out:
504 	explicit_bzero(buf, sizeof(buf));
505 
506 	return (r);
507 }
508 
509 fido_cred_t *
fido_cred_new(void)510 fido_cred_new(void)
511 {
512 	return (calloc(1, sizeof(fido_cred_t)));
513 }
514 
515 static void
fido_cred_clean_authdata(fido_cred_t * cred)516 fido_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 
527 static void
fido_cred_clean_attstmt(fido_attstmt_t * attstmt)528 fido_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 
539 void
fido_cred_reset_tx(fido_cred_t * cred)540 fido_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 
564 void
fido_cred_reset_rx(fido_cred_t * cred)565 fido_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 
574 void
fido_cred_free(fido_cred_t ** cred_p)575 fido_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 
587 int
fido_cred_set_authdata(fido_cred_t * cred,const unsigned char * ptr,size_t len)588 fido_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;
616 fail:
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 
626 int
fido_cred_set_authdata_raw(fido_cred_t * cred,const unsigned char * ptr,size_t len)627 fido_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;
657 fail:
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 
667 int
fido_cred_set_id(fido_cred_t * cred,const unsigned char * ptr,size_t len)668 fido_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 
676 int
fido_cred_set_x509(fido_cred_t * cred,const unsigned char * ptr,size_t len)677 fido_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 
685 int
fido_cred_set_sig(fido_cred_t * cred,const unsigned char * ptr,size_t len)686 fido_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 
694 int
fido_cred_set_attstmt(fido_cred_t * cred,const unsigned char * ptr,size_t len)695 fido_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;
717 fail:
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 
727 int
fido_cred_exclude(fido_cred_t * cred,const unsigned char * id_ptr,size_t id_len)728 fido_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 
755 int
fido_cred_set_clientdata(fido_cred_t * cred,const unsigned char * data,size_t data_len)756 fido_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 
771 int
fido_cred_set_clientdata_hash(fido_cred_t * cred,const unsigned char * hash,size_t hash_len)772 fido_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 
782 int
fido_cred_set_rp(fido_cred_t * cred,const char * id,const char * name)783 fido_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);
802 fail:
803 	free(rp->id);
804 	free(rp->name);
805 	rp->id = NULL;
806 	rp->name = NULL;
807 
808 	return (FIDO_ERR_INTERNAL);
809 }
810 
811 int
fido_cred_set_user(fido_cred_t * cred,const unsigned char * user_id,size_t user_id_len,const char * name,const char * display_name,const char * icon)812 fido_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);
847 fail:
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 
862 int
fido_cred_set_extensions(fido_cred_t * cred,int ext)863 fido_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 
876 int
fido_cred_set_options(fido_cred_t * cred,bool rk,bool uv)877 fido_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 
885 int
fido_cred_set_rk(fido_cred_t * cred,fido_opt_t rk)886 fido_cred_set_rk(fido_cred_t *cred, fido_opt_t rk)
887 {
888 	cred->rk = rk;
889 
890 	return (FIDO_OK);
891 }
892 
893 int
fido_cred_set_uv(fido_cred_t * cred,fido_opt_t uv)894 fido_cred_set_uv(fido_cred_t *cred, fido_opt_t uv)
895 {
896 	cred->uv = uv;
897 
898 	return (FIDO_OK);
899 }
900 
901 int
fido_cred_set_prot(fido_cred_t * cred,int prot)902 fido_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 
920 int
fido_cred_set_pin_minlen(fido_cred_t * cred,size_t len)921 fido_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 
933 int
fido_cred_set_blob(fido_cred_t * cred,const unsigned char * ptr,size_t len)934 fido_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 
946 int
fido_cred_set_fmt(fido_cred_t * cred,const char * fmt)947 fido_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 
965 int
fido_cred_set_type(fido_cred_t * cred,int cose_alg)966 fido_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 
977 int
fido_cred_type(const fido_cred_t * cred)978 fido_cred_type(const fido_cred_t *cred)
979 {
980 	return (cred->type);
981 }
982 
983 uint8_t
fido_cred_flags(const fido_cred_t * cred)984 fido_cred_flags(const fido_cred_t *cred)
985 {
986 	return (cred->authdata.flags);
987 }
988 
989 uint32_t
fido_cred_sigcount(const fido_cred_t * cred)990 fido_cred_sigcount(const fido_cred_t *cred)
991 {
992 	return (cred->authdata.sigcount);
993 }
994 
995 const unsigned char *
fido_cred_clientdata_hash_ptr(const fido_cred_t * cred)996 fido_cred_clientdata_hash_ptr(const fido_cred_t *cred)
997 {
998 	return (cred->cdh.ptr);
999 }
1000 
1001 size_t
fido_cred_clientdata_hash_len(const fido_cred_t * cred)1002 fido_cred_clientdata_hash_len(const fido_cred_t *cred)
1003 {
1004 	return (cred->cdh.len);
1005 }
1006 
1007 const unsigned char *
fido_cred_x5c_ptr(const fido_cred_t * cred)1008 fido_cred_x5c_ptr(const fido_cred_t *cred)
1009 {
1010 	return (cred->attstmt.x5c.ptr);
1011 }
1012 
1013 size_t
fido_cred_x5c_len(const fido_cred_t * cred)1014 fido_cred_x5c_len(const fido_cred_t *cred)
1015 {
1016 	return (cred->attstmt.x5c.len);
1017 }
1018 
1019 const unsigned char *
fido_cred_sig_ptr(const fido_cred_t * cred)1020 fido_cred_sig_ptr(const fido_cred_t *cred)
1021 {
1022 	return (cred->attstmt.sig.ptr);
1023 }
1024 
1025 size_t
fido_cred_sig_len(const fido_cred_t * cred)1026 fido_cred_sig_len(const fido_cred_t *cred)
1027 {
1028 	return (cred->attstmt.sig.len);
1029 }
1030 
1031 const unsigned char *
fido_cred_authdata_ptr(const fido_cred_t * cred)1032 fido_cred_authdata_ptr(const fido_cred_t *cred)
1033 {
1034 	return (cred->authdata_cbor.ptr);
1035 }
1036 
1037 size_t
fido_cred_authdata_len(const fido_cred_t * cred)1038 fido_cred_authdata_len(const fido_cred_t *cred)
1039 {
1040 	return (cred->authdata_cbor.len);
1041 }
1042 
1043 const unsigned char *
fido_cred_authdata_raw_ptr(const fido_cred_t * cred)1044 fido_cred_authdata_raw_ptr(const fido_cred_t *cred)
1045 {
1046 	return (cred->authdata_raw.ptr);
1047 }
1048 
1049 size_t
fido_cred_authdata_raw_len(const fido_cred_t * cred)1050 fido_cred_authdata_raw_len(const fido_cred_t *cred)
1051 {
1052 	return (cred->authdata_raw.len);
1053 }
1054 
1055 const unsigned char *
fido_cred_attstmt_ptr(const fido_cred_t * cred)1056 fido_cred_attstmt_ptr(const fido_cred_t *cred)
1057 {
1058 	return (cred->attstmt.cbor.ptr);
1059 }
1060 
1061 size_t
fido_cred_attstmt_len(const fido_cred_t * cred)1062 fido_cred_attstmt_len(const fido_cred_t *cred)
1063 {
1064 	return (cred->attstmt.cbor.len);
1065 }
1066 
1067 const unsigned char *
fido_cred_pubkey_ptr(const fido_cred_t * cred)1068 fido_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 
1090 size_t
fido_cred_pubkey_len(const fido_cred_t * cred)1091 fido_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 
1113 const unsigned char *
fido_cred_id_ptr(const fido_cred_t * cred)1114 fido_cred_id_ptr(const fido_cred_t *cred)
1115 {
1116 	return (cred->attcred.id.ptr);
1117 }
1118 
1119 size_t
fido_cred_id_len(const fido_cred_t * cred)1120 fido_cred_id_len(const fido_cred_t *cred)
1121 {
1122 	return (cred->attcred.id.len);
1123 }
1124 
1125 const unsigned char *
fido_cred_aaguid_ptr(const fido_cred_t * cred)1126 fido_cred_aaguid_ptr(const fido_cred_t *cred)
1127 {
1128 	return (cred->attcred.aaguid);
1129 }
1130 
1131 size_t
fido_cred_aaguid_len(const fido_cred_t * cred)1132 fido_cred_aaguid_len(const fido_cred_t *cred)
1133 {
1134 	return (sizeof(cred->attcred.aaguid));
1135 }
1136 
1137 int
fido_cred_prot(const fido_cred_t * cred)1138 fido_cred_prot(const fido_cred_t *cred)
1139 {
1140 	return (cred->ext.prot);
1141 }
1142 
1143 size_t
fido_cred_pin_minlen(const fido_cred_t * cred)1144 fido_cred_pin_minlen(const fido_cred_t *cred)
1145 {
1146 	return (cred->ext.minpinlen);
1147 }
1148 
1149 const char *
fido_cred_fmt(const fido_cred_t * cred)1150 fido_cred_fmt(const fido_cred_t *cred)
1151 {
1152 	return (cred->fmt);
1153 }
1154 
1155 const char *
fido_cred_rp_id(const fido_cred_t * cred)1156 fido_cred_rp_id(const fido_cred_t *cred)
1157 {
1158 	return (cred->rp.id);
1159 }
1160 
1161 const char *
fido_cred_rp_name(const fido_cred_t * cred)1162 fido_cred_rp_name(const fido_cred_t *cred)
1163 {
1164 	return (cred->rp.name);
1165 }
1166 
1167 const char *
fido_cred_user_name(const fido_cred_t * cred)1168 fido_cred_user_name(const fido_cred_t *cred)
1169 {
1170 	return (cred->user.name);
1171 }
1172 
1173 const char *
fido_cred_display_name(const fido_cred_t * cred)1174 fido_cred_display_name(const fido_cred_t *cred)
1175 {
1176 	return (cred->user.display_name);
1177 }
1178 
1179 const unsigned char *
fido_cred_user_id_ptr(const fido_cred_t * cred)1180 fido_cred_user_id_ptr(const fido_cred_t *cred)
1181 {
1182 	return (cred->user.id.ptr);
1183 }
1184 
1185 size_t
fido_cred_user_id_len(const fido_cred_t * cred)1186 fido_cred_user_id_len(const fido_cred_t *cred)
1187 {
1188 	return (cred->user.id.len);
1189 }
1190 
1191 const unsigned char *
fido_cred_largeblob_key_ptr(const fido_cred_t * cred)1192 fido_cred_largeblob_key_ptr(const fido_cred_t *cred)
1193 {
1194 	return (cred->largeblob_key.ptr);
1195 }
1196 
1197 size_t
fido_cred_largeblob_key_len(const fido_cred_t * cred)1198 fido_cred_largeblob_key_len(const fido_cred_t *cred)
1199 {
1200 	return (cred->largeblob_key.len);
1201 }
1202