xref: /freebsd/contrib/libfido2/src/assert.c (revision 8ddb146abcdf061be9f2c0db7e391697dafad85c)
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/ecdsa.h>
8 #include <openssl/sha.h>
9 
10 #include "fido.h"
11 #include "fido/es256.h"
12 #include "fido/rs256.h"
13 #include "fido/eddsa.h"
14 
15 static int
16 adjust_assert_count(const cbor_item_t *key, const cbor_item_t *val, void *arg)
17 {
18 	fido_assert_t	*assert = arg;
19 	uint64_t	 n;
20 
21 	/* numberOfCredentials; see section 6.2 */
22 	if (cbor_isa_uint(key) == false ||
23 	    cbor_int_get_width(key) != CBOR_INT_8 ||
24 	    cbor_get_uint8(key) != 5) {
25 		fido_log_debug("%s: cbor_type", __func__);
26 		return (0); /* ignore */
27 	}
28 
29 	if (cbor_decode_uint64(val, &n) < 0 || n > SIZE_MAX) {
30 		fido_log_debug("%s: cbor_decode_uint64", __func__);
31 		return (-1);
32 	}
33 
34 	if (assert->stmt_len != 0 || assert->stmt_cnt != 1 ||
35 	    (size_t)n < assert->stmt_cnt) {
36 		fido_log_debug("%s: stmt_len=%zu, stmt_cnt=%zu, n=%zu",
37 		    __func__, assert->stmt_len, assert->stmt_cnt, (size_t)n);
38 		return (-1);
39 	}
40 
41 	if (fido_assert_set_count(assert, (size_t)n) != FIDO_OK) {
42 		fido_log_debug("%s: fido_assert_set_count", __func__);
43 		return (-1);
44 	}
45 
46 	assert->stmt_len = 0; /* XXX */
47 
48 	return (0);
49 }
50 
51 static int
52 parse_assert_reply(const cbor_item_t *key, const cbor_item_t *val, void *arg)
53 {
54 	fido_assert_stmt *stmt = arg;
55 
56 	if (cbor_isa_uint(key) == false ||
57 	    cbor_int_get_width(key) != CBOR_INT_8) {
58 		fido_log_debug("%s: cbor type", __func__);
59 		return (0); /* ignore */
60 	}
61 
62 	switch (cbor_get_uint8(key)) {
63 	case 1: /* credential id */
64 		return (cbor_decode_cred_id(val, &stmt->id));
65 	case 2: /* authdata */
66 		return (cbor_decode_assert_authdata(val, &stmt->authdata_cbor,
67 		    &stmt->authdata, &stmt->authdata_ext));
68 	case 3: /* signature */
69 		return (fido_blob_decode(val, &stmt->sig));
70 	case 4: /* user attributes */
71 		return (cbor_decode_user(val, &stmt->user));
72 	case 7: /* large blob key */
73 		return (fido_blob_decode(val, &stmt->largeblob_key));
74 	default: /* ignore */
75 		fido_log_debug("%s: cbor type", __func__);
76 		return (0);
77 	}
78 }
79 
80 static int
81 fido_dev_get_assert_tx(fido_dev_t *dev, fido_assert_t *assert,
82     const es256_pk_t *pk, const fido_blob_t *ecdh, const char *pin)
83 {
84 	fido_blob_t	 f;
85 	fido_opt_t	 uv = assert->uv;
86 	cbor_item_t	*argv[7];
87 	const uint8_t	 cmd = CTAP_CBOR_ASSERT;
88 	int		 r;
89 
90 	memset(argv, 0, sizeof(argv));
91 	memset(&f, 0, sizeof(f));
92 
93 	/* do we have everything we need? */
94 	if (assert->rp_id == NULL || assert->cdh.ptr == NULL) {
95 		fido_log_debug("%s: rp_id=%p, cdh.ptr=%p", __func__,
96 		    (void *)assert->rp_id, (void *)assert->cdh.ptr);
97 		r = FIDO_ERR_INVALID_ARGUMENT;
98 		goto fail;
99 	}
100 
101 	if ((argv[0] = cbor_build_string(assert->rp_id)) == NULL ||
102 	    (argv[1] = fido_blob_encode(&assert->cdh)) == NULL) {
103 		fido_log_debug("%s: cbor encode", __func__);
104 		r = FIDO_ERR_INTERNAL;
105 		goto fail;
106 	}
107 
108 	/* allowed credentials */
109 	if (assert->allow_list.len) {
110 		const fido_blob_array_t *cl = &assert->allow_list;
111 		if ((argv[2] = cbor_encode_pubkey_list(cl)) == NULL) {
112 			fido_log_debug("%s: cbor_encode_pubkey_list", __func__);
113 			r = FIDO_ERR_INTERNAL;
114 			goto fail;
115 		}
116 	}
117 
118 	if (assert->ext.mask)
119 		if ((argv[3] = cbor_encode_assert_ext(dev, &assert->ext, ecdh,
120 		    pk)) == NULL) {
121 			fido_log_debug("%s: cbor_encode_assert_ext", __func__);
122 			r = FIDO_ERR_INTERNAL;
123 			goto fail;
124 		}
125 
126 	/* user verification */
127 	if (pin != NULL || (uv == FIDO_OPT_TRUE &&
128 	    fido_dev_supports_permissions(dev))) {
129 		if ((r = cbor_add_uv_params(dev, cmd, &assert->cdh, pk, ecdh,
130 		    pin, assert->rp_id, &argv[5], &argv[6])) != FIDO_OK) {
131 			fido_log_debug("%s: cbor_add_uv_params", __func__);
132 			goto fail;
133 		}
134 		uv = FIDO_OPT_OMIT;
135 	}
136 
137 	/* options */
138 	if (assert->up != FIDO_OPT_OMIT || uv != FIDO_OPT_OMIT)
139 		if ((argv[4] = cbor_encode_assert_opt(assert->up, uv)) == NULL) {
140 			fido_log_debug("%s: cbor_encode_assert_opt", __func__);
141 			r = FIDO_ERR_INTERNAL;
142 			goto fail;
143 		}
144 
145 	/* frame and transmit */
146 	if (cbor_build_frame(cmd, argv, nitems(argv), &f) < 0 ||
147 	    fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
148 		fido_log_debug("%s: fido_tx", __func__);
149 		r = FIDO_ERR_TX;
150 		goto fail;
151 	}
152 
153 	r = FIDO_OK;
154 fail:
155 	cbor_vector_free(argv, nitems(argv));
156 	free(f.ptr);
157 
158 	return (r);
159 }
160 
161 static int
162 fido_dev_get_assert_rx(fido_dev_t *dev, fido_assert_t *assert, int ms)
163 {
164 	unsigned char	reply[FIDO_MAXMSG];
165 	int		reply_len;
166 	int		r;
167 
168 	fido_assert_reset_rx(assert);
169 
170 	if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply),
171 	    ms)) < 0) {
172 		fido_log_debug("%s: fido_rx", __func__);
173 		return (FIDO_ERR_RX);
174 	}
175 
176 	/* start with room for a single assertion */
177 	if ((assert->stmt = calloc(1, sizeof(fido_assert_stmt))) == NULL)
178 		return (FIDO_ERR_INTERNAL);
179 
180 	assert->stmt_len = 0;
181 	assert->stmt_cnt = 1;
182 
183 	/* adjust as needed */
184 	if ((r = cbor_parse_reply(reply, (size_t)reply_len, assert,
185 	    adjust_assert_count)) != FIDO_OK) {
186 		fido_log_debug("%s: adjust_assert_count", __func__);
187 		return (r);
188 	}
189 
190 	/* parse the first assertion */
191 	if ((r = cbor_parse_reply(reply, (size_t)reply_len,
192 	    &assert->stmt[assert->stmt_len], parse_assert_reply)) != FIDO_OK) {
193 		fido_log_debug("%s: parse_assert_reply", __func__);
194 		return (r);
195 	}
196 
197 	assert->stmt_len++;
198 
199 	return (FIDO_OK);
200 }
201 
202 static int
203 fido_get_next_assert_tx(fido_dev_t *dev)
204 {
205 	const unsigned char cbor[] = { CTAP_CBOR_NEXT_ASSERT };
206 
207 	if (fido_tx(dev, CTAP_CMD_CBOR, cbor, sizeof(cbor)) < 0) {
208 		fido_log_debug("%s: fido_tx", __func__);
209 		return (FIDO_ERR_TX);
210 	}
211 
212 	return (FIDO_OK);
213 }
214 
215 static int
216 fido_get_next_assert_rx(fido_dev_t *dev, fido_assert_t *assert, int ms)
217 {
218 	unsigned char	reply[FIDO_MAXMSG];
219 	int		reply_len;
220 	int		r;
221 
222 	if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply),
223 	    ms)) < 0) {
224 		fido_log_debug("%s: fido_rx", __func__);
225 		return (FIDO_ERR_RX);
226 	}
227 
228 	/* sanity check */
229 	if (assert->stmt_len >= assert->stmt_cnt) {
230 		fido_log_debug("%s: stmt_len=%zu, stmt_cnt=%zu", __func__,
231 		    assert->stmt_len, assert->stmt_cnt);
232 		return (FIDO_ERR_INTERNAL);
233 	}
234 
235 	if ((r = cbor_parse_reply(reply, (size_t)reply_len,
236 	    &assert->stmt[assert->stmt_len], parse_assert_reply)) != FIDO_OK) {
237 		fido_log_debug("%s: parse_assert_reply", __func__);
238 		return (r);
239 	}
240 
241 	return (FIDO_OK);
242 }
243 
244 static int
245 fido_dev_get_assert_wait(fido_dev_t *dev, fido_assert_t *assert,
246     const es256_pk_t *pk, const fido_blob_t *ecdh, const char *pin, int ms)
247 {
248 	int r;
249 
250 	if ((r = fido_dev_get_assert_tx(dev, assert, pk, ecdh, pin)) != FIDO_OK ||
251 	    (r = fido_dev_get_assert_rx(dev, assert, ms)) != FIDO_OK)
252 		return (r);
253 
254 	while (assert->stmt_len < assert->stmt_cnt) {
255 		if ((r = fido_get_next_assert_tx(dev)) != FIDO_OK ||
256 		    (r = fido_get_next_assert_rx(dev, assert, ms)) != FIDO_OK)
257 			return (r);
258 		assert->stmt_len++;
259 	}
260 
261 	return (FIDO_OK);
262 }
263 
264 static int
265 decrypt_hmac_secrets(const fido_dev_t *dev, fido_assert_t *assert,
266     const fido_blob_t *key)
267 {
268 	for (size_t i = 0; i < assert->stmt_cnt; i++) {
269 		fido_assert_stmt *stmt = &assert->stmt[i];
270 		if (stmt->authdata_ext.hmac_secret_enc.ptr != NULL) {
271 			if (aes256_cbc_dec(dev, key,
272 			    &stmt->authdata_ext.hmac_secret_enc,
273 			    &stmt->hmac_secret) < 0) {
274 				fido_log_debug("%s: aes256_cbc_dec %zu",
275 				    __func__, i);
276 				return (-1);
277 			}
278 		}
279 	}
280 
281 	return (0);
282 }
283 
284 int
285 fido_dev_get_assert(fido_dev_t *dev, fido_assert_t *assert, const char *pin)
286 {
287 	fido_blob_t	*ecdh = NULL;
288 	es256_pk_t	*pk = NULL;
289 	int		 r;
290 
291 #ifdef USE_WINHELLO
292 	if (dev->flags & FIDO_DEV_WINHELLO)
293 		return (fido_winhello_get_assert(dev, assert, pin));
294 #endif
295 
296 	if (assert->rp_id == NULL || assert->cdh.ptr == NULL) {
297 		fido_log_debug("%s: rp_id=%p, cdh.ptr=%p", __func__,
298 		    (void *)assert->rp_id, (void *)assert->cdh.ptr);
299 		return (FIDO_ERR_INVALID_ARGUMENT);
300 	}
301 
302 	if (fido_dev_is_fido2(dev) == false) {
303 		if (pin != NULL || assert->ext.mask != 0)
304 			return (FIDO_ERR_UNSUPPORTED_OPTION);
305 		return (u2f_authenticate(dev, assert, -1));
306 	}
307 
308 	if (pin != NULL || (assert->uv == FIDO_OPT_TRUE &&
309 	    fido_dev_supports_permissions(dev)) ||
310 	    (assert->ext.mask & FIDO_EXT_HMAC_SECRET)) {
311 		if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
312 			fido_log_debug("%s: fido_do_ecdh", __func__);
313 			goto fail;
314 		}
315 	}
316 
317 	r = fido_dev_get_assert_wait(dev, assert, pk, ecdh, pin, -1);
318 	if (r == FIDO_OK && (assert->ext.mask & FIDO_EXT_HMAC_SECRET))
319 		if (decrypt_hmac_secrets(dev, assert, ecdh) < 0) {
320 			fido_log_debug("%s: decrypt_hmac_secrets", __func__);
321 			r = FIDO_ERR_INTERNAL;
322 			goto fail;
323 		}
324 
325 fail:
326 	es256_pk_free(&pk);
327 	fido_blob_free(&ecdh);
328 
329 	return (r);
330 }
331 
332 int
333 fido_check_flags(uint8_t flags, fido_opt_t up, fido_opt_t uv)
334 {
335 	fido_log_debug("%s: flags=%02x", __func__, flags);
336 	fido_log_debug("%s: up=%d, uv=%d", __func__, up, uv);
337 
338 	if (up == FIDO_OPT_TRUE &&
339 	    (flags & CTAP_AUTHDATA_USER_PRESENT) == 0) {
340 		fido_log_debug("%s: CTAP_AUTHDATA_USER_PRESENT", __func__);
341 		return (-1); /* user not present */
342 	}
343 
344 	if (uv == FIDO_OPT_TRUE &&
345 	    (flags & CTAP_AUTHDATA_USER_VERIFIED) == 0) {
346 		fido_log_debug("%s: CTAP_AUTHDATA_USER_VERIFIED", __func__);
347 		return (-1); /* user not verified */
348 	}
349 
350 	return (0);
351 }
352 
353 static int
354 check_extensions(int authdata_ext, int ext)
355 {
356 	/* XXX: largeBlobKey is not part of extensions map */
357 	ext &= ~FIDO_EXT_LARGEBLOB_KEY;
358 	if (authdata_ext != ext) {
359 		fido_log_debug("%s: authdata_ext=0x%x != ext=0x%x", __func__,
360 		    authdata_ext, ext);
361 		return (-1);
362 	}
363 
364 	return (0);
365 }
366 
367 int
368 fido_get_signed_hash(int cose_alg, fido_blob_t *dgst,
369     const fido_blob_t *clientdata, const fido_blob_t *authdata_cbor)
370 {
371 	cbor_item_t		*item = NULL;
372 	unsigned char		*authdata_ptr = NULL;
373 	size_t			 authdata_len;
374 	struct cbor_load_result	 cbor;
375 	SHA256_CTX		 ctx;
376 	int			 ok = -1;
377 
378 	if ((item = cbor_load(authdata_cbor->ptr, authdata_cbor->len,
379 	    &cbor)) == NULL || cbor_isa_bytestring(item) == false ||
380 	    cbor_bytestring_is_definite(item) == false) {
381 		fido_log_debug("%s: authdata", __func__);
382 		goto fail;
383 	}
384 
385 	authdata_ptr = cbor_bytestring_handle(item);
386 	authdata_len = cbor_bytestring_length(item);
387 
388 	if (cose_alg != COSE_EDDSA) {
389 		if (dgst->len < SHA256_DIGEST_LENGTH || SHA256_Init(&ctx) == 0 ||
390 		    SHA256_Update(&ctx, authdata_ptr, authdata_len) == 0 ||
391 		    SHA256_Update(&ctx, clientdata->ptr, clientdata->len) == 0 ||
392 		    SHA256_Final(dgst->ptr, &ctx) == 0) {
393 			fido_log_debug("%s: sha256", __func__);
394 			goto fail;
395 		}
396 		dgst->len = SHA256_DIGEST_LENGTH;
397 	} else {
398 		if (SIZE_MAX - authdata_len < clientdata->len ||
399 		    dgst->len < authdata_len + clientdata->len) {
400 			fido_log_debug("%s: memcpy", __func__);
401 			goto fail;
402 		}
403 		memcpy(dgst->ptr, authdata_ptr, authdata_len);
404 		memcpy(dgst->ptr + authdata_len, clientdata->ptr,
405 		    clientdata->len);
406 		dgst->len = authdata_len + clientdata->len;
407 	}
408 
409 	ok = 0;
410 fail:
411 	if (item != NULL)
412 		cbor_decref(&item);
413 
414 	return (ok);
415 }
416 
417 int
418 fido_verify_sig_es256(const fido_blob_t *dgst, const es256_pk_t *pk,
419     const fido_blob_t *sig)
420 {
421 	EVP_PKEY	*pkey = NULL;
422 	EC_KEY		*ec = NULL;
423 	int		 ok = -1;
424 
425 	/* ECDSA_verify needs ints */
426 	if (dgst->len > INT_MAX || sig->len > INT_MAX) {
427 		fido_log_debug("%s: dgst->len=%zu, sig->len=%zu", __func__,
428 		    dgst->len, sig->len);
429 		return (-1);
430 	}
431 
432 	if ((pkey = es256_pk_to_EVP_PKEY(pk)) == NULL ||
433 	    (ec = EVP_PKEY_get0_EC_KEY(pkey)) == NULL) {
434 		fido_log_debug("%s: pk -> ec", __func__);
435 		goto fail;
436 	}
437 
438 	if (ECDSA_verify(0, dgst->ptr, (int)dgst->len, sig->ptr,
439 	    (int)sig->len, ec) != 1) {
440 		fido_log_debug("%s: ECDSA_verify", __func__);
441 		goto fail;
442 	}
443 
444 	ok = 0;
445 fail:
446 	if (pkey != NULL)
447 		EVP_PKEY_free(pkey);
448 
449 	return (ok);
450 }
451 
452 int
453 fido_verify_sig_rs256(const fido_blob_t *dgst, const rs256_pk_t *pk,
454     const fido_blob_t *sig)
455 {
456 	EVP_PKEY	*pkey = NULL;
457 	RSA		*rsa = NULL;
458 	int		 ok = -1;
459 
460 	/* RSA_verify needs unsigned ints */
461 	if (dgst->len > UINT_MAX || sig->len > UINT_MAX) {
462 		fido_log_debug("%s: dgst->len=%zu, sig->len=%zu", __func__,
463 		    dgst->len, sig->len);
464 		return (-1);
465 	}
466 
467 	if ((pkey = rs256_pk_to_EVP_PKEY(pk)) == NULL ||
468 	    (rsa = EVP_PKEY_get0_RSA(pkey)) == NULL) {
469 		fido_log_debug("%s: pk -> ec", __func__);
470 		goto fail;
471 	}
472 
473 	if (RSA_verify(NID_sha256, dgst->ptr, (unsigned int)dgst->len, sig->ptr,
474 	    (unsigned int)sig->len, rsa) != 1) {
475 		fido_log_debug("%s: RSA_verify", __func__);
476 		goto fail;
477 	}
478 
479 	ok = 0;
480 fail:
481 	if (pkey != NULL)
482 		EVP_PKEY_free(pkey);
483 
484 	return (ok);
485 }
486 
487 int
488 fido_verify_sig_eddsa(const fido_blob_t *dgst, const eddsa_pk_t *pk,
489     const fido_blob_t *sig)
490 {
491 	EVP_PKEY	*pkey = NULL;
492 	EVP_MD_CTX	*mdctx = NULL;
493 	int		 ok = -1;
494 
495 	/* EVP_DigestVerify needs ints */
496 	if (dgst->len > INT_MAX || sig->len > INT_MAX) {
497 		fido_log_debug("%s: dgst->len=%zu, sig->len=%zu", __func__,
498 		    dgst->len, sig->len);
499 		return (-1);
500 	}
501 
502 	if ((pkey = eddsa_pk_to_EVP_PKEY(pk)) == NULL) {
503 		fido_log_debug("%s: pk -> pkey", __func__);
504 		goto fail;
505 	}
506 
507 	if ((mdctx = EVP_MD_CTX_new()) == NULL) {
508 		fido_log_debug("%s: EVP_MD_CTX_new", __func__);
509 		goto fail;
510 	}
511 
512 	if (EVP_DigestVerifyInit(mdctx, NULL, NULL, NULL, pkey) != 1) {
513 		fido_log_debug("%s: EVP_DigestVerifyInit", __func__);
514 		goto fail;
515 	}
516 
517 	if (EVP_DigestVerify(mdctx, sig->ptr, sig->len, dgst->ptr,
518 	    dgst->len) != 1) {
519 		fido_log_debug("%s: EVP_DigestVerify", __func__);
520 		goto fail;
521 	}
522 
523 	ok = 0;
524 fail:
525 	if (mdctx != NULL)
526 		EVP_MD_CTX_free(mdctx);
527 
528 	if (pkey != NULL)
529 		EVP_PKEY_free(pkey);
530 
531 	return (ok);
532 }
533 
534 int
535 fido_assert_verify(const fido_assert_t *assert, size_t idx, int cose_alg,
536     const void *pk)
537 {
538 	unsigned char		 buf[1024]; /* XXX */
539 	fido_blob_t		 dgst;
540 	const fido_assert_stmt	*stmt = NULL;
541 	int			 ok = -1;
542 	int			 r;
543 
544 	dgst.ptr = buf;
545 	dgst.len = sizeof(buf);
546 
547 	if (idx >= assert->stmt_len || pk == NULL) {
548 		r = FIDO_ERR_INVALID_ARGUMENT;
549 		goto out;
550 	}
551 
552 	stmt = &assert->stmt[idx];
553 
554 	/* do we have everything we need? */
555 	if (assert->cdh.ptr == NULL || assert->rp_id == NULL ||
556 	    stmt->authdata_cbor.ptr == NULL || stmt->sig.ptr == NULL) {
557 		fido_log_debug("%s: cdh=%p, rp_id=%s, authdata=%p, sig=%p",
558 		    __func__, (void *)assert->cdh.ptr, assert->rp_id,
559 		    (void *)stmt->authdata_cbor.ptr, (void *)stmt->sig.ptr);
560 		r = FIDO_ERR_INVALID_ARGUMENT;
561 		goto out;
562 	}
563 
564 	if (fido_check_flags(stmt->authdata.flags, assert->up,
565 	    assert->uv) < 0) {
566 		fido_log_debug("%s: fido_check_flags", __func__);
567 		r = FIDO_ERR_INVALID_PARAM;
568 		goto out;
569 	}
570 
571 	if (check_extensions(stmt->authdata_ext.mask, assert->ext.mask) < 0) {
572 		fido_log_debug("%s: check_extensions", __func__);
573 		r = FIDO_ERR_INVALID_PARAM;
574 		goto out;
575 	}
576 
577 	if (fido_check_rp_id(assert->rp_id, stmt->authdata.rp_id_hash) != 0) {
578 		fido_log_debug("%s: fido_check_rp_id", __func__);
579 		r = FIDO_ERR_INVALID_PARAM;
580 		goto out;
581 	}
582 
583 	if (fido_get_signed_hash(cose_alg, &dgst, &assert->cdh,
584 	    &stmt->authdata_cbor) < 0) {
585 		fido_log_debug("%s: fido_get_signed_hash", __func__);
586 		r = FIDO_ERR_INTERNAL;
587 		goto out;
588 	}
589 
590 	switch (cose_alg) {
591 	case COSE_ES256:
592 		ok = fido_verify_sig_es256(&dgst, pk, &stmt->sig);
593 		break;
594 	case COSE_RS256:
595 		ok = fido_verify_sig_rs256(&dgst, pk, &stmt->sig);
596 		break;
597 	case COSE_EDDSA:
598 		ok = fido_verify_sig_eddsa(&dgst, pk, &stmt->sig);
599 		break;
600 	default:
601 		fido_log_debug("%s: unsupported cose_alg %d", __func__,
602 		    cose_alg);
603 		r = FIDO_ERR_UNSUPPORTED_OPTION;
604 		goto out;
605 	}
606 
607 	if (ok < 0)
608 		r = FIDO_ERR_INVALID_SIG;
609 	else
610 		r = FIDO_OK;
611 out:
612 	explicit_bzero(buf, sizeof(buf));
613 
614 	return (r);
615 }
616 
617 int
618 fido_assert_set_clientdata(fido_assert_t *assert, const unsigned char *data,
619     size_t data_len)
620 {
621 	if (!fido_blob_is_empty(&assert->cdh) ||
622 	    fido_blob_set(&assert->cd, data, data_len) < 0) {
623 		return (FIDO_ERR_INVALID_ARGUMENT);
624 	}
625 	if (fido_sha256(&assert->cdh, data, data_len) < 0) {
626 		fido_blob_reset(&assert->cd);
627 		return (FIDO_ERR_INTERNAL);
628 	}
629 
630 	return (FIDO_OK);
631 }
632 
633 int
634 fido_assert_set_clientdata_hash(fido_assert_t *assert,
635     const unsigned char *hash, size_t hash_len)
636 {
637 	if (!fido_blob_is_empty(&assert->cd) ||
638 	    fido_blob_set(&assert->cdh, hash, hash_len) < 0)
639 		return (FIDO_ERR_INVALID_ARGUMENT);
640 
641 	return (FIDO_OK);
642 }
643 
644 int
645 fido_assert_set_hmac_salt(fido_assert_t *assert, const unsigned char *salt,
646     size_t salt_len)
647 {
648 	if ((salt_len != 32 && salt_len != 64) ||
649 	    fido_blob_set(&assert->ext.hmac_salt, salt, salt_len) < 0)
650 		return (FIDO_ERR_INVALID_ARGUMENT);
651 
652 	return (FIDO_OK);
653 }
654 
655 int
656 fido_assert_set_hmac_secret(fido_assert_t *assert, size_t idx,
657     const unsigned char *secret, size_t secret_len)
658 {
659 	if (idx >= assert->stmt_len || (secret_len != 32 && secret_len != 64) ||
660 	    fido_blob_set(&assert->stmt[idx].hmac_secret, secret,
661 	    secret_len) < 0)
662 		return (FIDO_ERR_INVALID_ARGUMENT);
663 
664 	return (FIDO_OK);
665 }
666 
667 int
668 fido_assert_set_rp(fido_assert_t *assert, const char *id)
669 {
670 	if (assert->rp_id != NULL) {
671 		free(assert->rp_id);
672 		assert->rp_id = NULL;
673 	}
674 
675 	if (id == NULL)
676 		return (FIDO_ERR_INVALID_ARGUMENT);
677 
678 	if ((assert->rp_id = strdup(id)) == NULL)
679 		return (FIDO_ERR_INTERNAL);
680 
681 	return (FIDO_OK);
682 }
683 
684 int
685 fido_assert_allow_cred(fido_assert_t *assert, const unsigned char *ptr,
686     size_t len)
687 {
688 	fido_blob_t	 id;
689 	fido_blob_t	*list_ptr;
690 	int		 r;
691 
692 	memset(&id, 0, sizeof(id));
693 
694 	if (assert->allow_list.len == SIZE_MAX) {
695 		r = FIDO_ERR_INVALID_ARGUMENT;
696 		goto fail;
697 	}
698 
699 	if (fido_blob_set(&id, ptr, len) < 0 || (list_ptr =
700 	    recallocarray(assert->allow_list.ptr, assert->allow_list.len,
701 	    assert->allow_list.len + 1, sizeof(fido_blob_t))) == NULL) {
702 		r = FIDO_ERR_INVALID_ARGUMENT;
703 		goto fail;
704 	}
705 
706 	list_ptr[assert->allow_list.len++] = id;
707 	assert->allow_list.ptr = list_ptr;
708 
709 	return (FIDO_OK);
710 fail:
711 	free(id.ptr);
712 
713 	return (r);
714 
715 }
716 
717 int
718 fido_assert_set_extensions(fido_assert_t *assert, int ext)
719 {
720 	if (ext == 0)
721 		assert->ext.mask = 0;
722 	else {
723 		if ((ext & FIDO_EXT_ASSERT_MASK) != ext)
724 			return (FIDO_ERR_INVALID_ARGUMENT);
725 		assert->ext.mask |= ext;
726 	}
727 
728 	return (FIDO_OK);
729 }
730 
731 int
732 fido_assert_set_options(fido_assert_t *assert, bool up, bool uv)
733 {
734 	assert->up = up ? FIDO_OPT_TRUE : FIDO_OPT_FALSE;
735 	assert->uv = uv ? FIDO_OPT_TRUE : FIDO_OPT_FALSE;
736 
737 	return (FIDO_OK);
738 }
739 
740 int
741 fido_assert_set_up(fido_assert_t *assert, fido_opt_t up)
742 {
743 	assert->up = up;
744 
745 	return (FIDO_OK);
746 }
747 
748 int
749 fido_assert_set_uv(fido_assert_t *assert, fido_opt_t uv)
750 {
751 	assert->uv = uv;
752 
753 	return (FIDO_OK);
754 }
755 
756 const unsigned char *
757 fido_assert_clientdata_hash_ptr(const fido_assert_t *assert)
758 {
759 	return (assert->cdh.ptr);
760 }
761 
762 size_t
763 fido_assert_clientdata_hash_len(const fido_assert_t *assert)
764 {
765 	return (assert->cdh.len);
766 }
767 
768 fido_assert_t *
769 fido_assert_new(void)
770 {
771 	return (calloc(1, sizeof(fido_assert_t)));
772 }
773 
774 void
775 fido_assert_reset_tx(fido_assert_t *assert)
776 {
777 	free(assert->rp_id);
778 	fido_blob_reset(&assert->cd);
779 	fido_blob_reset(&assert->cdh);
780 	fido_blob_reset(&assert->ext.hmac_salt);
781 	fido_free_blob_array(&assert->allow_list);
782 	memset(&assert->ext, 0, sizeof(assert->ext));
783 	memset(&assert->allow_list, 0, sizeof(assert->allow_list));
784 	assert->rp_id = NULL;
785 	assert->up = FIDO_OPT_OMIT;
786 	assert->uv = FIDO_OPT_OMIT;
787 }
788 
789 static void fido_assert_reset_extattr(fido_assert_extattr_t *ext)
790 {
791 	fido_blob_reset(&ext->hmac_secret_enc);
792 	fido_blob_reset(&ext->blob);
793 	memset(ext, 0, sizeof(*ext));
794 }
795 
796 void
797 fido_assert_reset_rx(fido_assert_t *assert)
798 {
799 	for (size_t i = 0; i < assert->stmt_cnt; i++) {
800 		free(assert->stmt[i].user.icon);
801 		free(assert->stmt[i].user.name);
802 		free(assert->stmt[i].user.display_name);
803 		fido_blob_reset(&assert->stmt[i].user.id);
804 		fido_blob_reset(&assert->stmt[i].id);
805 		fido_blob_reset(&assert->stmt[i].hmac_secret);
806 		fido_blob_reset(&assert->stmt[i].authdata_cbor);
807 		fido_blob_reset(&assert->stmt[i].largeblob_key);
808 		fido_blob_reset(&assert->stmt[i].sig);
809 		fido_assert_reset_extattr(&assert->stmt[i].authdata_ext);
810 		memset(&assert->stmt[i], 0, sizeof(assert->stmt[i]));
811 	}
812 	free(assert->stmt);
813 	assert->stmt = NULL;
814 	assert->stmt_len = 0;
815 	assert->stmt_cnt = 0;
816 }
817 
818 void
819 fido_assert_free(fido_assert_t **assert_p)
820 {
821 	fido_assert_t *assert;
822 
823 	if (assert_p == NULL || (assert = *assert_p) == NULL)
824 		return;
825 	fido_assert_reset_tx(assert);
826 	fido_assert_reset_rx(assert);
827 	free(assert);
828 	*assert_p = NULL;
829 }
830 
831 size_t
832 fido_assert_count(const fido_assert_t *assert)
833 {
834 	return (assert->stmt_len);
835 }
836 
837 const char *
838 fido_assert_rp_id(const fido_assert_t *assert)
839 {
840 	return (assert->rp_id);
841 }
842 
843 uint8_t
844 fido_assert_flags(const fido_assert_t *assert, size_t idx)
845 {
846 	if (idx >= assert->stmt_len)
847 		return (0);
848 
849 	return (assert->stmt[idx].authdata.flags);
850 }
851 
852 uint32_t
853 fido_assert_sigcount(const fido_assert_t *assert, size_t idx)
854 {
855 	if (idx >= assert->stmt_len)
856 		return (0);
857 
858 	return (assert->stmt[idx].authdata.sigcount);
859 }
860 
861 const unsigned char *
862 fido_assert_authdata_ptr(const fido_assert_t *assert, size_t idx)
863 {
864 	if (idx >= assert->stmt_len)
865 		return (NULL);
866 
867 	return (assert->stmt[idx].authdata_cbor.ptr);
868 }
869 
870 size_t
871 fido_assert_authdata_len(const fido_assert_t *assert, size_t idx)
872 {
873 	if (idx >= assert->stmt_len)
874 		return (0);
875 
876 	return (assert->stmt[idx].authdata_cbor.len);
877 }
878 
879 const unsigned char *
880 fido_assert_sig_ptr(const fido_assert_t *assert, size_t idx)
881 {
882 	if (idx >= assert->stmt_len)
883 		return (NULL);
884 
885 	return (assert->stmt[idx].sig.ptr);
886 }
887 
888 size_t
889 fido_assert_sig_len(const fido_assert_t *assert, size_t idx)
890 {
891 	if (idx >= assert->stmt_len)
892 		return (0);
893 
894 	return (assert->stmt[idx].sig.len);
895 }
896 
897 const unsigned char *
898 fido_assert_id_ptr(const fido_assert_t *assert, size_t idx)
899 {
900 	if (idx >= assert->stmt_len)
901 		return (NULL);
902 
903 	return (assert->stmt[idx].id.ptr);
904 }
905 
906 size_t
907 fido_assert_id_len(const fido_assert_t *assert, size_t idx)
908 {
909 	if (idx >= assert->stmt_len)
910 		return (0);
911 
912 	return (assert->stmt[idx].id.len);
913 }
914 
915 const unsigned char *
916 fido_assert_user_id_ptr(const fido_assert_t *assert, size_t idx)
917 {
918 	if (idx >= assert->stmt_len)
919 		return (NULL);
920 
921 	return (assert->stmt[idx].user.id.ptr);
922 }
923 
924 size_t
925 fido_assert_user_id_len(const fido_assert_t *assert, size_t idx)
926 {
927 	if (idx >= assert->stmt_len)
928 		return (0);
929 
930 	return (assert->stmt[idx].user.id.len);
931 }
932 
933 const char *
934 fido_assert_user_icon(const fido_assert_t *assert, size_t idx)
935 {
936 	if (idx >= assert->stmt_len)
937 		return (NULL);
938 
939 	return (assert->stmt[idx].user.icon);
940 }
941 
942 const char *
943 fido_assert_user_name(const fido_assert_t *assert, size_t idx)
944 {
945 	if (idx >= assert->stmt_len)
946 		return (NULL);
947 
948 	return (assert->stmt[idx].user.name);
949 }
950 
951 const char *
952 fido_assert_user_display_name(const fido_assert_t *assert, size_t idx)
953 {
954 	if (idx >= assert->stmt_len)
955 		return (NULL);
956 
957 	return (assert->stmt[idx].user.display_name);
958 }
959 
960 const unsigned char *
961 fido_assert_hmac_secret_ptr(const fido_assert_t *assert, size_t idx)
962 {
963 	if (idx >= assert->stmt_len)
964 		return (NULL);
965 
966 	return (assert->stmt[idx].hmac_secret.ptr);
967 }
968 
969 size_t
970 fido_assert_hmac_secret_len(const fido_assert_t *assert, size_t idx)
971 {
972 	if (idx >= assert->stmt_len)
973 		return (0);
974 
975 	return (assert->stmt[idx].hmac_secret.len);
976 }
977 
978 const unsigned char *
979 fido_assert_largeblob_key_ptr(const fido_assert_t *assert, size_t idx)
980 {
981 	if (idx >= assert->stmt_len)
982 		return (NULL);
983 
984 	return (assert->stmt[idx].largeblob_key.ptr);
985 }
986 
987 size_t
988 fido_assert_largeblob_key_len(const fido_assert_t *assert, size_t idx)
989 {
990 	if (idx >= assert->stmt_len)
991 		return (0);
992 
993 	return (assert->stmt[idx].largeblob_key.len);
994 }
995 
996 const unsigned char *
997 fido_assert_blob_ptr(const fido_assert_t *assert, size_t idx)
998 {
999 	if (idx >= assert->stmt_len)
1000 		return (NULL);
1001 
1002 	return (assert->stmt[idx].authdata_ext.blob.ptr);
1003 }
1004 
1005 size_t
1006 fido_assert_blob_len(const fido_assert_t *assert, size_t idx)
1007 {
1008 	if (idx >= assert->stmt_len)
1009 		return (0);
1010 
1011 	return (assert->stmt[idx].authdata_ext.blob.len);
1012 }
1013 
1014 static void
1015 fido_assert_clean_authdata(fido_assert_stmt *stmt)
1016 {
1017 	fido_blob_reset(&stmt->authdata_cbor);
1018 	fido_assert_reset_extattr(&stmt->authdata_ext);
1019 	memset(&stmt->authdata, 0, sizeof(stmt->authdata));
1020 }
1021 
1022 int
1023 fido_assert_set_authdata(fido_assert_t *assert, size_t idx,
1024     const unsigned char *ptr, size_t len)
1025 {
1026 	cbor_item_t		*item = NULL;
1027 	fido_assert_stmt	*stmt = NULL;
1028 	struct cbor_load_result	 cbor;
1029 	int			 r;
1030 
1031 	if (idx >= assert->stmt_len || ptr == NULL || len == 0)
1032 		return (FIDO_ERR_INVALID_ARGUMENT);
1033 
1034 	stmt = &assert->stmt[idx];
1035 	fido_assert_clean_authdata(stmt);
1036 
1037 	if ((item = cbor_load(ptr, len, &cbor)) == NULL) {
1038 		fido_log_debug("%s: cbor_load", __func__);
1039 		r = FIDO_ERR_INVALID_ARGUMENT;
1040 		goto fail;
1041 	}
1042 
1043 	if (cbor_decode_assert_authdata(item, &stmt->authdata_cbor,
1044 	    &stmt->authdata, &stmt->authdata_ext) < 0) {
1045 		fido_log_debug("%s: cbor_decode_assert_authdata", __func__);
1046 		r = FIDO_ERR_INVALID_ARGUMENT;
1047 		goto fail;
1048 	}
1049 
1050 	r = FIDO_OK;
1051 fail:
1052 	if (item != NULL)
1053 		cbor_decref(&item);
1054 
1055 	if (r != FIDO_OK)
1056 		fido_assert_clean_authdata(stmt);
1057 
1058 	return (r);
1059 }
1060 
1061 int
1062 fido_assert_set_authdata_raw(fido_assert_t *assert, size_t idx,
1063     const unsigned char *ptr, size_t len)
1064 {
1065 	cbor_item_t		*item = NULL;
1066 	fido_assert_stmt	*stmt = NULL;
1067 	int			 r;
1068 
1069 	if (idx >= assert->stmt_len || ptr == NULL || len == 0)
1070 		return (FIDO_ERR_INVALID_ARGUMENT);
1071 
1072 	stmt = &assert->stmt[idx];
1073 	fido_assert_clean_authdata(stmt);
1074 
1075 	if ((item = cbor_build_bytestring(ptr, len)) == NULL) {
1076 		fido_log_debug("%s: cbor_build_bytestring", __func__);
1077 		r = FIDO_ERR_INTERNAL;
1078 		goto fail;
1079 	}
1080 
1081 	if (cbor_decode_assert_authdata(item, &stmt->authdata_cbor,
1082 	    &stmt->authdata, &stmt->authdata_ext) < 0) {
1083 		fido_log_debug("%s: cbor_decode_assert_authdata", __func__);
1084 		r = FIDO_ERR_INVALID_ARGUMENT;
1085 		goto fail;
1086 	}
1087 
1088 	r = FIDO_OK;
1089 fail:
1090 	if (item != NULL)
1091 		cbor_decref(&item);
1092 
1093 	if (r != FIDO_OK)
1094 		fido_assert_clean_authdata(stmt);
1095 
1096 	return (r);
1097 }
1098 
1099 int
1100 fido_assert_set_sig(fido_assert_t *a, size_t idx, const unsigned char *ptr,
1101     size_t len)
1102 {
1103 	if (idx >= a->stmt_len || ptr == NULL || len == 0)
1104 		return (FIDO_ERR_INVALID_ARGUMENT);
1105 	if (fido_blob_set(&a->stmt[idx].sig, ptr, len) < 0)
1106 		return (FIDO_ERR_INTERNAL);
1107 
1108 	return (FIDO_OK);
1109 }
1110 
1111 /* XXX shrinking leaks memory; fortunately that shouldn't happen */
1112 int
1113 fido_assert_set_count(fido_assert_t *assert, size_t n)
1114 {
1115 	void *new_stmt;
1116 
1117 #ifdef FIDO_FUZZ
1118 	if (n > UINT8_MAX) {
1119 		fido_log_debug("%s: n > UINT8_MAX", __func__);
1120 		return (FIDO_ERR_INTERNAL);
1121 	}
1122 #endif
1123 
1124 	new_stmt = recallocarray(assert->stmt, assert->stmt_cnt, n,
1125 	    sizeof(fido_assert_stmt));
1126 	if (new_stmt == NULL)
1127 		return (FIDO_ERR_INTERNAL);
1128 
1129 	assert->stmt = new_stmt;
1130 	assert->stmt_cnt = n;
1131 	assert->stmt_len = n;
1132 
1133 	return (FIDO_OK);
1134 }
1135