xref: /openbsd-src/lib/libfido2/src/cred.c (revision 1a8dbaac879b9f3335ad7fb25429ce63ac1d6bac)
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_u2f(fido_blob_t *dgst, const unsigned char *rp_id,
207     size_t rp_id_len, const fido_blob_t *clientdata, const fido_blob_t *id,
208     const es256_pk_t *pk)
209 {
210 	const uint8_t		zero = 0;
211 	const uint8_t		four = 4; /* uncompressed point */
212 	SHA256_CTX		ctx;
213 
214 	if (dgst->len != SHA256_DIGEST_LENGTH || SHA256_Init(&ctx) == 0 ||
215 	    SHA256_Update(&ctx, &zero, sizeof(zero)) == 0 ||
216 	    SHA256_Update(&ctx, rp_id, rp_id_len) == 0 ||
217 	    SHA256_Update(&ctx, clientdata->ptr, clientdata->len) == 0 ||
218 	    SHA256_Update(&ctx, id->ptr, id->len) == 0 ||
219 	    SHA256_Update(&ctx, &four, sizeof(four)) == 0 ||
220 	    SHA256_Update(&ctx, pk->x, sizeof(pk->x)) == 0 ||
221 	    SHA256_Update(&ctx, pk->y, sizeof(pk->y)) == 0 ||
222 	    SHA256_Final(dgst->ptr, &ctx) == 0) {
223 		fido_log_debug("%s: sha256", __func__);
224 		return (-1);
225 	}
226 
227 	return (0);
228 }
229 
230 static int
231 verify_sig(const fido_blob_t *dgst, const fido_blob_t *x5c,
232     const fido_blob_t *sig)
233 {
234 	BIO		*rawcert = NULL;
235 	X509		*cert = NULL;
236 	EVP_PKEY	*pkey = NULL;
237 	EC_KEY		*ec;
238 	int		 ok = -1;
239 
240 	/* openssl needs ints */
241 	if (dgst->len > INT_MAX || x5c->len > INT_MAX || sig->len > INT_MAX) {
242 		fido_log_debug("%s: dgst->len=%zu, x5c->len=%zu, sig->len=%zu",
243 		    __func__, dgst->len, x5c->len, sig->len);
244 		return (-1);
245 	}
246 
247 	/* fetch key from x509 */
248 	if ((rawcert = BIO_new_mem_buf(x5c->ptr, (int)x5c->len)) == NULL ||
249 	    (cert = d2i_X509_bio(rawcert, NULL)) == NULL ||
250 	    (pkey = X509_get_pubkey(cert)) == NULL ||
251 	    (ec = EVP_PKEY_get0_EC_KEY(pkey)) == NULL) {
252 		fido_log_debug("%s: x509 key", __func__);
253 		goto fail;
254 	}
255 
256 	if (ECDSA_verify(0, dgst->ptr, (int)dgst->len, sig->ptr,
257 	    (int)sig->len, ec) != 1) {
258 		fido_log_debug("%s: ECDSA_verify", __func__);
259 		goto fail;
260 	}
261 
262 	ok = 0;
263 fail:
264 	if (rawcert != NULL)
265 		BIO_free(rawcert);
266 	if (cert != NULL)
267 		X509_free(cert);
268 	if (pkey != NULL)
269 		EVP_PKEY_free(pkey);
270 
271 	return (ok);
272 }
273 
274 int
275 fido_cred_verify(const fido_cred_t *cred)
276 {
277 	unsigned char	buf[SHA256_DIGEST_LENGTH];
278 	fido_blob_t	dgst;
279 	int		r;
280 
281 	dgst.ptr = buf;
282 	dgst.len = sizeof(buf);
283 
284 	/* do we have everything we need? */
285 	if (cred->cdh.ptr == NULL || cred->authdata_cbor.ptr == NULL ||
286 	    cred->attstmt.x5c.ptr == NULL || cred->attstmt.sig.ptr == NULL ||
287 	    cred->fmt == NULL || cred->attcred.id.ptr == NULL ||
288 	    cred->rp.id == NULL) {
289 		fido_log_debug("%s: cdh=%p, authdata=%p, x5c=%p, sig=%p, "
290 		    "fmt=%p id=%p, rp.id=%s", __func__, (void *)cred->cdh.ptr,
291 		    (void *)cred->authdata_cbor.ptr,
292 		    (void *)cred->attstmt.x5c.ptr,
293 		    (void *)cred->attstmt.sig.ptr, (void *)cred->fmt,
294 		    (void *)cred->attcred.id.ptr, cred->rp.id);
295 		r = FIDO_ERR_INVALID_ARGUMENT;
296 		goto out;
297 	}
298 
299 	if (fido_check_rp_id(cred->rp.id, cred->authdata.rp_id_hash) != 0) {
300 		fido_log_debug("%s: fido_check_rp_id", __func__);
301 		r = FIDO_ERR_INVALID_PARAM;
302 		goto out;
303 	}
304 
305 	if (fido_check_flags(cred->authdata.flags, FIDO_OPT_TRUE,
306 	    cred->uv) < 0) {
307 		fido_log_debug("%s: fido_check_flags", __func__);
308 		r = FIDO_ERR_INVALID_PARAM;
309 		goto out;
310 	}
311 
312 	if (check_extensions(&cred->authdata_ext, &cred->ext) != 0) {
313 		fido_log_debug("%s: check_extensions", __func__);
314 		r = FIDO_ERR_INVALID_PARAM;
315 		goto out;
316 	}
317 
318 	if (!strcmp(cred->fmt, "packed")) {
319 		if (fido_get_signed_hash(COSE_ES256, &dgst, &cred->cdh,
320 		    &cred->authdata_cbor) < 0) {
321 			fido_log_debug("%s: fido_get_signed_hash", __func__);
322 			r = FIDO_ERR_INTERNAL;
323 			goto out;
324 		}
325 	} else {
326 		if (get_signed_hash_u2f(&dgst, cred->authdata.rp_id_hash,
327 		    sizeof(cred->authdata.rp_id_hash), &cred->cdh,
328 		    &cred->attcred.id, &cred->attcred.pubkey.es256) < 0) {
329 			fido_log_debug("%s: get_signed_hash_u2f", __func__);
330 			r = FIDO_ERR_INTERNAL;
331 			goto out;
332 		}
333 	}
334 
335 	if (verify_sig(&dgst, &cred->attstmt.x5c, &cred->attstmt.sig) < 0) {
336 		fido_log_debug("%s: verify_sig", __func__);
337 		r = FIDO_ERR_INVALID_SIG;
338 		goto out;
339 	}
340 
341 	r = FIDO_OK;
342 out:
343 	explicit_bzero(buf, sizeof(buf));
344 
345 	return (r);
346 }
347 
348 int
349 fido_cred_verify_self(const fido_cred_t *cred)
350 {
351 	unsigned char	buf[1024]; /* XXX */
352 	fido_blob_t	dgst;
353 	int		ok = -1;
354 	int		r;
355 
356 	dgst.ptr = buf;
357 	dgst.len = sizeof(buf);
358 
359 	/* do we have everything we need? */
360 	if (cred->cdh.ptr == NULL || cred->authdata_cbor.ptr == NULL ||
361 	    cred->attstmt.x5c.ptr != NULL || cred->attstmt.sig.ptr == NULL ||
362 	    cred->fmt == NULL || cred->attcred.id.ptr == NULL ||
363 	    cred->rp.id == NULL) {
364 		fido_log_debug("%s: cdh=%p, authdata=%p, x5c=%p, sig=%p, "
365 		    "fmt=%p id=%p, rp.id=%s", __func__, (void *)cred->cdh.ptr,
366 		    (void *)cred->authdata_cbor.ptr,
367 		    (void *)cred->attstmt.x5c.ptr,
368 		    (void *)cred->attstmt.sig.ptr, (void *)cred->fmt,
369 		    (void *)cred->attcred.id.ptr, cred->rp.id);
370 		r = FIDO_ERR_INVALID_ARGUMENT;
371 		goto out;
372 	}
373 
374 	if (fido_check_rp_id(cred->rp.id, cred->authdata.rp_id_hash) != 0) {
375 		fido_log_debug("%s: fido_check_rp_id", __func__);
376 		r = FIDO_ERR_INVALID_PARAM;
377 		goto out;
378 	}
379 
380 	if (fido_check_flags(cred->authdata.flags, FIDO_OPT_TRUE,
381 	    cred->uv) < 0) {
382 		fido_log_debug("%s: fido_check_flags", __func__);
383 		r = FIDO_ERR_INVALID_PARAM;
384 		goto out;
385 	}
386 
387 	if (check_extensions(&cred->authdata_ext, &cred->ext) != 0) {
388 		fido_log_debug("%s: check_extensions", __func__);
389 		r = FIDO_ERR_INVALID_PARAM;
390 		goto out;
391 	}
392 
393 	if (!strcmp(cred->fmt, "packed")) {
394 		if (fido_get_signed_hash(cred->attcred.type, &dgst, &cred->cdh,
395 		    &cred->authdata_cbor) < 0) {
396 			fido_log_debug("%s: fido_get_signed_hash", __func__);
397 			r = FIDO_ERR_INTERNAL;
398 			goto out;
399 		}
400 	} else {
401 		if (get_signed_hash_u2f(&dgst, cred->authdata.rp_id_hash,
402 		    sizeof(cred->authdata.rp_id_hash), &cred->cdh,
403 		    &cred->attcred.id, &cred->attcred.pubkey.es256) < 0) {
404 			fido_log_debug("%s: get_signed_hash_u2f", __func__);
405 			r = FIDO_ERR_INTERNAL;
406 			goto out;
407 		}
408 	}
409 
410 	switch (cred->attcred.type) {
411 	case COSE_ES256:
412 		ok = fido_verify_sig_es256(&dgst, &cred->attcred.pubkey.es256,
413 		    &cred->attstmt.sig);
414 		break;
415 	case COSE_RS256:
416 		ok = fido_verify_sig_rs256(&dgst, &cred->attcred.pubkey.rs256,
417 		    &cred->attstmt.sig);
418 		break;
419 	case COSE_EDDSA:
420 		ok = fido_verify_sig_eddsa(&dgst, &cred->attcred.pubkey.eddsa,
421 		    &cred->attstmt.sig);
422 		break;
423 	default:
424 		fido_log_debug("%s: unsupported cose_alg %d", __func__,
425 		    cred->attcred.type);
426 		r = FIDO_ERR_UNSUPPORTED_OPTION;
427 		goto out;
428 	}
429 
430 	if (ok < 0)
431 		r = FIDO_ERR_INVALID_SIG;
432 	else
433 		r = FIDO_OK;
434 
435 out:
436 	explicit_bzero(buf, sizeof(buf));
437 
438 	return (r);
439 }
440 
441 fido_cred_t *
442 fido_cred_new(void)
443 {
444 	return (calloc(1, sizeof(fido_cred_t)));
445 }
446 
447 static void
448 fido_cred_clean_authdata(fido_cred_t *cred)
449 {
450 	free(cred->authdata_cbor.ptr);
451 	free(cred->attcred.id.ptr);
452 
453 	memset(&cred->authdata_ext, 0, sizeof(cred->authdata_ext));
454 	memset(&cred->authdata_cbor, 0, sizeof(cred->authdata_cbor));
455 	memset(&cred->authdata, 0, sizeof(cred->authdata));
456 	memset(&cred->attcred, 0, sizeof(cred->attcred));
457 }
458 
459 void
460 fido_cred_reset_tx(fido_cred_t *cred)
461 {
462 	free(cred->cdh.ptr);
463 	free(cred->rp.id);
464 	free(cred->rp.name);
465 	free(cred->user.id.ptr);
466 	free(cred->user.icon);
467 	free(cred->user.name);
468 	free(cred->user.display_name);
469 	fido_free_blob_array(&cred->excl);
470 
471 	memset(&cred->cdh, 0, sizeof(cred->cdh));
472 	memset(&cred->rp, 0, sizeof(cred->rp));
473 	memset(&cred->user, 0, sizeof(cred->user));
474 	memset(&cred->excl, 0, sizeof(cred->excl));
475 	memset(&cred->ext, 0, sizeof(cred->ext));
476 
477 	cred->type = 0;
478 	cred->rk = FIDO_OPT_OMIT;
479 	cred->uv = FIDO_OPT_OMIT;
480 }
481 
482 static void
483 fido_cred_clean_x509(fido_cred_t *cred)
484 {
485 	free(cred->attstmt.x5c.ptr);
486 	cred->attstmt.x5c.ptr = NULL;
487 	cred->attstmt.x5c.len = 0;
488 }
489 
490 static void
491 fido_cred_clean_sig(fido_cred_t *cred)
492 {
493 	free(cred->attstmt.sig.ptr);
494 	cred->attstmt.sig.ptr = NULL;
495 	cred->attstmt.sig.len = 0;
496 }
497 
498 void
499 fido_cred_reset_rx(fido_cred_t *cred)
500 {
501 	free(cred->fmt);
502 	cred->fmt = NULL;
503 
504 	fido_cred_clean_authdata(cred);
505 	fido_cred_clean_x509(cred);
506 	fido_cred_clean_sig(cred);
507 }
508 
509 void
510 fido_cred_free(fido_cred_t **cred_p)
511 {
512 	fido_cred_t *cred;
513 
514 	if (cred_p == NULL || (cred = *cred_p) == NULL)
515 		return;
516 
517 	fido_cred_reset_tx(cred);
518 	fido_cred_reset_rx(cred);
519 
520 	free(cred);
521 
522 	*cred_p = NULL;
523 }
524 
525 int
526 fido_cred_set_authdata(fido_cred_t *cred, const unsigned char *ptr, size_t len)
527 {
528 	cbor_item_t		*item = NULL;
529 	struct cbor_load_result	 cbor;
530 	int			 r;
531 
532 	fido_cred_clean_authdata(cred);
533 
534 	if (ptr == NULL || len == 0) {
535 		r = FIDO_ERR_INVALID_ARGUMENT;
536 		goto fail;
537 	}
538 
539 	if ((item = cbor_load(ptr, len, &cbor)) == NULL) {
540 		fido_log_debug("%s: cbor_load", __func__);
541 		r = FIDO_ERR_INVALID_ARGUMENT;
542 		goto fail;
543 	}
544 
545 	if (cbor_decode_cred_authdata(item, cred->type, &cred->authdata_cbor,
546 	    &cred->authdata, &cred->attcred, &cred->authdata_ext) < 0) {
547 		fido_log_debug("%s: cbor_decode_cred_authdata", __func__);
548 		r = FIDO_ERR_INVALID_ARGUMENT;
549 		goto fail;
550 	}
551 
552 	r = FIDO_OK;
553 fail:
554 	if (item != NULL)
555 		cbor_decref(&item);
556 
557 	if (r != FIDO_OK)
558 		fido_cred_clean_authdata(cred);
559 
560 	return (r);
561 
562 }
563 
564 int
565 fido_cred_set_authdata_raw(fido_cred_t *cred, const unsigned char *ptr,
566     size_t len)
567 {
568 	cbor_item_t		*item = NULL;
569 	int			 r;
570 
571 	fido_cred_clean_authdata(cred);
572 
573 	if (ptr == NULL || len == 0) {
574 		r = FIDO_ERR_INVALID_ARGUMENT;
575 		goto fail;
576 	}
577 
578 	if ((item = cbor_build_bytestring(ptr, len)) == NULL) {
579 		fido_log_debug("%s: cbor_build_bytestring", __func__);
580 		r = FIDO_ERR_INTERNAL;
581 		goto fail;
582 	}
583 
584 	if (cbor_decode_cred_authdata(item, cred->type, &cred->authdata_cbor,
585 	    &cred->authdata, &cred->attcred, &cred->authdata_ext) < 0) {
586 		fido_log_debug("%s: cbor_decode_cred_authdata", __func__);
587 		r = FIDO_ERR_INVALID_ARGUMENT;
588 		goto fail;
589 	}
590 
591 	r = FIDO_OK;
592 fail:
593 	if (item != NULL)
594 		cbor_decref(&item);
595 
596 	if (r != FIDO_OK)
597 		fido_cred_clean_authdata(cred);
598 
599 	return (r);
600 
601 }
602 
603 int
604 fido_cred_set_x509(fido_cred_t *cred, const unsigned char *ptr, size_t len)
605 {
606 	unsigned char *x509;
607 
608 	fido_cred_clean_x509(cred);
609 
610 	if (ptr == NULL || len == 0)
611 		return (FIDO_ERR_INVALID_ARGUMENT);
612 	if ((x509 = malloc(len)) == NULL)
613 		return (FIDO_ERR_INTERNAL);
614 
615 	memcpy(x509, ptr, len);
616 	cred->attstmt.x5c.ptr = x509;
617 	cred->attstmt.x5c.len = len;
618 
619 	return (FIDO_OK);
620 }
621 
622 int
623 fido_cred_set_sig(fido_cred_t *cred, const unsigned char *ptr, size_t len)
624 {
625 	unsigned char *sig;
626 
627 	fido_cred_clean_sig(cred);
628 
629 	if (ptr == NULL || len == 0)
630 		return (FIDO_ERR_INVALID_ARGUMENT);
631 	if ((sig = malloc(len)) == NULL)
632 		return (FIDO_ERR_INTERNAL);
633 
634 	memcpy(sig, ptr, len);
635 	cred->attstmt.sig.ptr = sig;
636 	cred->attstmt.sig.len = len;
637 
638 	return (FIDO_OK);
639 }
640 
641 int
642 fido_cred_exclude(fido_cred_t *cred, const unsigned char *id_ptr, size_t id_len)
643 {
644 	fido_blob_t id_blob;
645 	fido_blob_t *list_ptr;
646 
647 	memset(&id_blob, 0, sizeof(id_blob));
648 
649 	if (fido_blob_set(&id_blob, id_ptr, id_len) < 0)
650 		return (FIDO_ERR_INVALID_ARGUMENT);
651 
652 	if (cred->excl.len == SIZE_MAX) {
653 		free(id_blob.ptr);
654 		return (FIDO_ERR_INVALID_ARGUMENT);
655 	}
656 
657 	if ((list_ptr = recallocarray(cred->excl.ptr, cred->excl.len,
658 	    cred->excl.len + 1, sizeof(fido_blob_t))) == NULL) {
659 		free(id_blob.ptr);
660 		return (FIDO_ERR_INTERNAL);
661 	}
662 
663 	list_ptr[cred->excl.len++] = id_blob;
664 	cred->excl.ptr = list_ptr;
665 
666 	return (FIDO_OK);
667 }
668 
669 int
670 fido_cred_set_clientdata_hash(fido_cred_t *cred, const unsigned char *hash,
671     size_t hash_len)
672 {
673 	if (fido_blob_set(&cred->cdh, hash, hash_len) < 0)
674 		return (FIDO_ERR_INVALID_ARGUMENT);
675 
676 	return (FIDO_OK);
677 }
678 
679 int
680 fido_cred_set_rp(fido_cred_t *cred, const char *id, const char *name)
681 {
682 	fido_rp_t *rp = &cred->rp;
683 
684 	if (rp->id != NULL) {
685 		free(rp->id);
686 		rp->id = NULL;
687 	}
688 	if (rp->name != NULL) {
689 		free(rp->name);
690 		rp->name = NULL;
691 	}
692 
693 	if (id != NULL && (rp->id = strdup(id)) == NULL)
694 		goto fail;
695 	if (name != NULL && (rp->name = strdup(name)) == NULL)
696 		goto fail;
697 
698 	return (FIDO_OK);
699 fail:
700 	free(rp->id);
701 	free(rp->name);
702 	rp->id = NULL;
703 	rp->name = NULL;
704 
705 	return (FIDO_ERR_INTERNAL);
706 }
707 
708 int
709 fido_cred_set_user(fido_cred_t *cred, const unsigned char *user_id,
710     size_t user_id_len, const char *name, const char *display_name,
711     const char *icon)
712 {
713 	fido_user_t *up = &cred->user;
714 
715 	if (up->id.ptr != NULL) {
716 		free(up->id.ptr);
717 		up->id.ptr = NULL;
718 		up->id.len = 0;
719 	}
720 	if (up->name != NULL) {
721 		free(up->name);
722 		up->name = NULL;
723 	}
724 	if (up->display_name != NULL) {
725 		free(up->display_name);
726 		up->display_name = NULL;
727 	}
728 	if (up->icon != NULL) {
729 		free(up->icon);
730 		up->icon = NULL;
731 	}
732 
733 	if (user_id != NULL) {
734 		if ((up->id.ptr = malloc(user_id_len)) == NULL)
735 			goto fail;
736 		memcpy(up->id.ptr, user_id, user_id_len);
737 		up->id.len = user_id_len;
738 	}
739 	if (name != NULL && (up->name = strdup(name)) == NULL)
740 		goto fail;
741 	if (display_name != NULL &&
742 	    (up->display_name = strdup(display_name)) == NULL)
743 		goto fail;
744 	if (icon != NULL && (up->icon = strdup(icon)) == NULL)
745 		goto fail;
746 
747 	return (FIDO_OK);
748 fail:
749 	free(up->id.ptr);
750 	free(up->name);
751 	free(up->display_name);
752 	free(up->icon);
753 
754 	up->id.ptr = NULL;
755 	up->id.len = 0;
756 	up->name = NULL;
757 	up->display_name = NULL;
758 	up->icon = NULL;
759 
760 	return (FIDO_ERR_INTERNAL);
761 }
762 
763 int
764 fido_cred_set_extensions(fido_cred_t *cred, int ext)
765 {
766 	if (ext == 0)
767 		cred->ext.mask = 0;
768 	else {
769 		if (ext != FIDO_EXT_HMAC_SECRET &&
770 		    ext != FIDO_EXT_CRED_PROTECT)
771 			return (FIDO_ERR_INVALID_ARGUMENT);
772 		cred->ext.mask |= ext;
773 	}
774 
775 	return (FIDO_OK);
776 }
777 
778 int
779 fido_cred_set_options(fido_cred_t *cred, bool rk, bool uv)
780 {
781 	cred->rk = rk ? FIDO_OPT_TRUE : FIDO_OPT_FALSE;
782 	cred->uv = uv ? FIDO_OPT_TRUE : FIDO_OPT_FALSE;
783 
784 	return (FIDO_OK);
785 }
786 
787 int
788 fido_cred_set_rk(fido_cred_t *cred, fido_opt_t rk)
789 {
790 	cred->rk = rk;
791 
792 	return (FIDO_OK);
793 }
794 
795 int
796 fido_cred_set_uv(fido_cred_t *cred, fido_opt_t uv)
797 {
798 	cred->uv = uv;
799 
800 	return (FIDO_OK);
801 }
802 
803 int
804 fido_cred_set_prot(fido_cred_t *cred, int prot)
805 {
806 	if (prot == 0) {
807 		cred->ext.mask &= ~FIDO_EXT_CRED_PROTECT;
808 		cred->ext.prot = 0;
809 	} else {
810 		if (prot != FIDO_CRED_PROT_UV_OPTIONAL &&
811 		    prot != FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID &&
812 		    prot != FIDO_CRED_PROT_UV_REQUIRED)
813 			return (FIDO_ERR_INVALID_ARGUMENT);
814 
815 		cred->ext.mask |= FIDO_EXT_CRED_PROTECT;
816 		cred->ext.prot = prot;
817 	}
818 
819 	return (FIDO_OK);
820 }
821 
822 int
823 fido_cred_set_fmt(fido_cred_t *cred, const char *fmt)
824 {
825 	free(cred->fmt);
826 	cred->fmt = NULL;
827 
828 	if (fmt == NULL)
829 		return (FIDO_ERR_INVALID_ARGUMENT);
830 
831 	if (strcmp(fmt, "packed") && strcmp(fmt, "fido-u2f"))
832 		return (FIDO_ERR_INVALID_ARGUMENT);
833 
834 	if ((cred->fmt = strdup(fmt)) == NULL)
835 		return (FIDO_ERR_INTERNAL);
836 
837 	return (FIDO_OK);
838 }
839 
840 int
841 fido_cred_set_type(fido_cred_t *cred, int cose_alg)
842 {
843 	if ((cose_alg != COSE_ES256 && cose_alg != COSE_RS256 &&
844 	    cose_alg != COSE_EDDSA) || cred->type != 0)
845 		return (FIDO_ERR_INVALID_ARGUMENT);
846 
847 	cred->type = cose_alg;
848 
849 	return (FIDO_OK);
850 }
851 
852 int
853 fido_cred_type(const fido_cred_t *cred)
854 {
855 	return (cred->type);
856 }
857 
858 uint8_t
859 fido_cred_flags(const fido_cred_t *cred)
860 {
861 	return (cred->authdata.flags);
862 }
863 
864 const unsigned char *
865 fido_cred_clientdata_hash_ptr(const fido_cred_t *cred)
866 {
867 	return (cred->cdh.ptr);
868 }
869 
870 size_t
871 fido_cred_clientdata_hash_len(const fido_cred_t *cred)
872 {
873 	return (cred->cdh.len);
874 }
875 
876 const unsigned char *
877 fido_cred_x5c_ptr(const fido_cred_t *cred)
878 {
879 	return (cred->attstmt.x5c.ptr);
880 }
881 
882 size_t
883 fido_cred_x5c_len(const fido_cred_t *cred)
884 {
885 	return (cred->attstmt.x5c.len);
886 }
887 
888 const unsigned char *
889 fido_cred_sig_ptr(const fido_cred_t *cred)
890 {
891 	return (cred->attstmt.sig.ptr);
892 }
893 
894 size_t
895 fido_cred_sig_len(const fido_cred_t *cred)
896 {
897 	return (cred->attstmt.sig.len);
898 }
899 
900 const unsigned char *
901 fido_cred_authdata_ptr(const fido_cred_t *cred)
902 {
903 	return (cred->authdata_cbor.ptr);
904 }
905 
906 size_t
907 fido_cred_authdata_len(const fido_cred_t *cred)
908 {
909 	return (cred->authdata_cbor.len);
910 }
911 
912 const unsigned char *
913 fido_cred_pubkey_ptr(const fido_cred_t *cred)
914 {
915 	const void *ptr;
916 
917 	switch (cred->attcred.type) {
918 	case COSE_ES256:
919 		ptr = &cred->attcred.pubkey.es256;
920 		break;
921 	case COSE_RS256:
922 		ptr = &cred->attcred.pubkey.rs256;
923 		break;
924 	case COSE_EDDSA:
925 		ptr = &cred->attcred.pubkey.eddsa;
926 		break;
927 	default:
928 		ptr = NULL;
929 		break;
930 	}
931 
932 	return (ptr);
933 }
934 
935 size_t
936 fido_cred_pubkey_len(const fido_cred_t *cred)
937 {
938 	size_t len;
939 
940 	switch (cred->attcred.type) {
941 	case COSE_ES256:
942 		len = sizeof(cred->attcred.pubkey.es256);
943 		break;
944 	case COSE_RS256:
945 		len = sizeof(cred->attcred.pubkey.rs256);
946 		break;
947 	case COSE_EDDSA:
948 		len = sizeof(cred->attcred.pubkey.eddsa);
949 		break;
950 	default:
951 		len = 0;
952 		break;
953 	}
954 
955 	return (len);
956 }
957 
958 const unsigned char *
959 fido_cred_id_ptr(const fido_cred_t *cred)
960 {
961 	return (cred->attcred.id.ptr);
962 }
963 
964 size_t
965 fido_cred_id_len(const fido_cred_t *cred)
966 {
967 	return (cred->attcred.id.len);
968 }
969 
970 const unsigned char *
971 fido_cred_aaguid_ptr(const fido_cred_t *cred)
972 {
973 	return (cred->attcred.aaguid);
974 }
975 
976 size_t
977 fido_cred_aaguid_len(const fido_cred_t *cred)
978 {
979 	return (sizeof(cred->attcred.aaguid));
980 }
981 
982 int
983 fido_cred_prot(const fido_cred_t *cred)
984 {
985 	return (cred->ext.prot);
986 }
987 
988 const char *
989 fido_cred_fmt(const fido_cred_t *cred)
990 {
991 	return (cred->fmt);
992 }
993 
994 const char *
995 fido_cred_rp_id(const fido_cred_t *cred)
996 {
997 	return (cred->rp.id);
998 }
999 
1000 const char *
1001 fido_cred_rp_name(const fido_cred_t *cred)
1002 {
1003 	return (cred->rp.name);
1004 }
1005 
1006 const char *
1007 fido_cred_user_name(const fido_cred_t *cred)
1008 {
1009 	return (cred->user.name);
1010 }
1011 
1012 const char *
1013 fido_cred_display_name(const fido_cred_t *cred)
1014 {
1015 	return (cred->user.display_name);
1016 }
1017 
1018 const unsigned char *
1019 fido_cred_user_id_ptr(const fido_cred_t *cred)
1020 {
1021 	return (cred->user.id.ptr);
1022 }
1023 
1024 size_t
1025 fido_cred_user_id_len(const fido_cred_t *cred)
1026 {
1027 	return (cred->user.id.len);
1028 }
1029