xref: /freebsd/contrib/libfido2/src/dev.c (revision f7c32ed617858bcd22f8d1b03199099d50125721)
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/sha.h>
8 #include "fido.h"
9 
10 #ifndef TLS
11 #define TLS
12 #endif
13 
14 typedef struct dev_manifest_func_node {
15 	dev_manifest_func_t manifest_func;
16 	struct dev_manifest_func_node *next;
17 } dev_manifest_func_node_t;
18 
19 static TLS dev_manifest_func_node_t *manifest_funcs = NULL;
20 static TLS bool disable_u2f_fallback;
21 
22 static void
23 find_manifest_func_node(dev_manifest_func_t f, dev_manifest_func_node_t **curr,
24     dev_manifest_func_node_t **prev)
25 {
26 	*prev = NULL;
27 	*curr = manifest_funcs;
28 
29 	while (*curr != NULL && (*curr)->manifest_func != f) {
30 		*prev = *curr;
31 		*curr = (*curr)->next;
32 	}
33 }
34 
35 #ifdef FIDO_FUZZ
36 static void
37 set_random_report_len(fido_dev_t *dev)
38 {
39 	dev->rx_len = CTAP_MIN_REPORT_LEN +
40 	    uniform_random(CTAP_MAX_REPORT_LEN - CTAP_MIN_REPORT_LEN + 1);
41 	dev->tx_len = CTAP_MIN_REPORT_LEN +
42 	    uniform_random(CTAP_MAX_REPORT_LEN - CTAP_MIN_REPORT_LEN + 1);
43 }
44 #endif
45 
46 static void
47 fido_dev_set_extension_flags(fido_dev_t *dev, const fido_cbor_info_t *info)
48 {
49 	char * const	*ptr = fido_cbor_info_extensions_ptr(info);
50 	size_t		 len = fido_cbor_info_extensions_len(info);
51 
52 	for (size_t i = 0; i < len; i++)
53 		if (strcmp(ptr[i], "credProtect") == 0)
54 			dev->flags |= FIDO_DEV_CRED_PROT;
55 }
56 
57 static void
58 fido_dev_set_option_flags(fido_dev_t *dev, const fido_cbor_info_t *info)
59 {
60 	char * const	*ptr = fido_cbor_info_options_name_ptr(info);
61 	const bool	*val = fido_cbor_info_options_value_ptr(info);
62 	size_t		 len = fido_cbor_info_options_len(info);
63 
64 	for (size_t i = 0; i < len; i++)
65 		if (strcmp(ptr[i], "clientPin") == 0) {
66 			dev->flags |= val[i] ? FIDO_DEV_PIN_SET : FIDO_DEV_PIN_UNSET;
67 		} else if (strcmp(ptr[i], "credMgmt") == 0 ||
68 			   strcmp(ptr[i], "credentialMgmtPreview") == 0) {
69 			if (val[i])
70 				dev->flags |= FIDO_DEV_CREDMAN;
71 		} else if (strcmp(ptr[i], "uv") == 0) {
72 			dev->flags |= val[i] ? FIDO_DEV_UV_SET : FIDO_DEV_UV_UNSET;
73 		} else if (strcmp(ptr[i], "pinUvAuthToken") == 0) {
74 			if (val[i])
75 				dev->flags |= FIDO_DEV_TOKEN_PERMS;
76 		}
77 }
78 
79 static void
80 fido_dev_set_protocol_flags(fido_dev_t *dev, const fido_cbor_info_t *info)
81 {
82 	const uint8_t	*ptr = fido_cbor_info_protocols_ptr(info);
83 	size_t		 len = fido_cbor_info_protocols_len(info);
84 
85 	for (size_t i = 0; i < len; i++)
86 		switch (ptr[i]) {
87 		case CTAP_PIN_PROTOCOL1:
88 			dev->flags |= FIDO_DEV_PIN_PROTOCOL1;
89 			break;
90 		case CTAP_PIN_PROTOCOL2:
91 			dev->flags |= FIDO_DEV_PIN_PROTOCOL2;
92 			break;
93 		default:
94 			fido_log_debug("%s: unknown protocol %u", __func__,
95 			    ptr[i]);
96 			break;
97 		}
98 }
99 
100 static void
101 fido_dev_set_flags(fido_dev_t *dev, const fido_cbor_info_t *info)
102 {
103 	fido_dev_set_extension_flags(dev, info);
104 	fido_dev_set_option_flags(dev, info);
105 	fido_dev_set_protocol_flags(dev, info);
106 }
107 
108 static int
109 fido_dev_open_tx(fido_dev_t *dev, const char *path)
110 {
111 	int r;
112 
113 	if (dev->io_handle != NULL) {
114 		fido_log_debug("%s: handle=%p", __func__, dev->io_handle);
115 		return (FIDO_ERR_INVALID_ARGUMENT);
116 	}
117 
118 	if (dev->io.open == NULL || dev->io.close == NULL) {
119 		fido_log_debug("%s: NULL open/close", __func__);
120 		return (FIDO_ERR_INVALID_ARGUMENT);
121 	}
122 
123 	if (dev->cid != CTAP_CID_BROADCAST) {
124 		fido_log_debug("%s: cid=0x%x", __func__, dev->cid);
125 		return (FIDO_ERR_INVALID_ARGUMENT);
126 	}
127 
128 	if (fido_get_random(&dev->nonce, sizeof(dev->nonce)) < 0) {
129 		fido_log_debug("%s: fido_get_random", __func__);
130 		return (FIDO_ERR_INTERNAL);
131 	}
132 
133 	if ((dev->io_handle = dev->io.open(path)) == NULL) {
134 		fido_log_debug("%s: dev->io.open", __func__);
135 		return (FIDO_ERR_INTERNAL);
136 	}
137 
138 	if (dev->io_own) {
139 		dev->rx_len = CTAP_MAX_REPORT_LEN;
140 		dev->tx_len = CTAP_MAX_REPORT_LEN;
141 	} else {
142 		dev->rx_len = fido_hid_report_in_len(dev->io_handle);
143 		dev->tx_len = fido_hid_report_out_len(dev->io_handle);
144 	}
145 
146 #ifdef FIDO_FUZZ
147 	set_random_report_len(dev);
148 #endif
149 
150 	if (dev->rx_len < CTAP_MIN_REPORT_LEN ||
151 	    dev->rx_len > CTAP_MAX_REPORT_LEN) {
152 		fido_log_debug("%s: invalid rx_len %zu", __func__, dev->rx_len);
153 		r = FIDO_ERR_RX;
154 		goto fail;
155 	}
156 
157 	if (dev->tx_len < CTAP_MIN_REPORT_LEN ||
158 	    dev->tx_len > CTAP_MAX_REPORT_LEN) {
159 		fido_log_debug("%s: invalid tx_len %zu", __func__, dev->tx_len);
160 		r = FIDO_ERR_TX;
161 		goto fail;
162 	}
163 
164 	if (fido_tx(dev, CTAP_CMD_INIT, &dev->nonce, sizeof(dev->nonce)) < 0) {
165 		fido_log_debug("%s: fido_tx", __func__);
166 		r = FIDO_ERR_TX;
167 		goto fail;
168 	}
169 
170 	return (FIDO_OK);
171 fail:
172 	dev->io.close(dev->io_handle);
173 	dev->io_handle = NULL;
174 
175 	return (r);
176 }
177 
178 static int
179 fido_dev_open_rx(fido_dev_t *dev, int ms)
180 {
181 	fido_cbor_info_t	*info = NULL;
182 	int			 reply_len;
183 	int			 r;
184 
185 	if ((reply_len = fido_rx(dev, CTAP_CMD_INIT, &dev->attr,
186 	    sizeof(dev->attr), ms)) < 0) {
187 		fido_log_debug("%s: fido_rx", __func__);
188 		r = FIDO_ERR_RX;
189 		goto fail;
190 	}
191 
192 #ifdef FIDO_FUZZ
193 	dev->attr.nonce = dev->nonce;
194 #endif
195 
196 	if ((size_t)reply_len != sizeof(dev->attr) ||
197 	    dev->attr.nonce != dev->nonce) {
198 		fido_log_debug("%s: invalid nonce", __func__);
199 		r = FIDO_ERR_RX;
200 		goto fail;
201 	}
202 
203 	dev->flags = 0;
204 	dev->cid = dev->attr.cid;
205 
206 	if (fido_dev_is_fido2(dev)) {
207 		if ((info = fido_cbor_info_new()) == NULL) {
208 			fido_log_debug("%s: fido_cbor_info_new", __func__);
209 			r = FIDO_ERR_INTERNAL;
210 			goto fail;
211 		}
212 		if ((r = fido_dev_get_cbor_info_wait(dev, info,
213 		    ms)) != FIDO_OK) {
214 			fido_log_debug("%s: fido_dev_cbor_info_wait: %d",
215 			    __func__, r);
216 			if (disable_u2f_fallback)
217 				goto fail;
218 			fido_log_debug("%s: falling back to u2f", __func__);
219 			fido_dev_force_u2f(dev);
220 		} else {
221 			fido_dev_set_flags(dev, info);
222 		}
223 	}
224 
225 	if (fido_dev_is_fido2(dev) && info != NULL) {
226 		dev->maxmsgsize = fido_cbor_info_maxmsgsiz(info);
227 		fido_log_debug("%s: FIDO_MAXMSG=%d, maxmsgsiz=%lu", __func__,
228 		    FIDO_MAXMSG, (unsigned long)dev->maxmsgsize);
229 	}
230 
231 	r = FIDO_OK;
232 fail:
233 	fido_cbor_info_free(&info);
234 
235 	if (r != FIDO_OK) {
236 		dev->io.close(dev->io_handle);
237 		dev->io_handle = NULL;
238 	}
239 
240 	return (r);
241 }
242 
243 static int
244 fido_dev_open_wait(fido_dev_t *dev, const char *path, int ms)
245 {
246 	int r;
247 
248 #ifdef USE_WINHELLO
249 	if (strcmp(path, FIDO_WINHELLO_PATH) == 0)
250 		return (fido_winhello_open(dev));
251 #endif
252 	if ((r = fido_dev_open_tx(dev, path)) != FIDO_OK ||
253 	    (r = fido_dev_open_rx(dev, ms)) != FIDO_OK)
254 		return (r);
255 
256 	return (FIDO_OK);
257 }
258 
259 int
260 fido_dev_register_manifest_func(const dev_manifest_func_t f)
261 {
262 	dev_manifest_func_node_t *prev, *curr, *n;
263 
264 	find_manifest_func_node(f, &curr, &prev);
265 	if (curr != NULL)
266 		return (FIDO_OK);
267 
268 	if ((n = calloc(1, sizeof(*n))) == NULL) {
269 		fido_log_debug("%s: calloc", __func__);
270 		return (FIDO_ERR_INTERNAL);
271 	}
272 
273 	n->manifest_func = f;
274 	n->next = manifest_funcs;
275 	manifest_funcs = n;
276 
277 	return (FIDO_OK);
278 }
279 
280 void
281 fido_dev_unregister_manifest_func(const dev_manifest_func_t f)
282 {
283 	dev_manifest_func_node_t *prev, *curr;
284 
285 	find_manifest_func_node(f, &curr, &prev);
286 	if (curr == NULL)
287 		return;
288 	if (prev != NULL)
289 		prev->next = curr->next;
290 	else
291 		manifest_funcs = curr->next;
292 
293 	free(curr);
294 }
295 
296 int
297 fido_dev_info_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
298 {
299 	dev_manifest_func_node_t	*curr = NULL;
300 	dev_manifest_func_t		 m_func;
301 	size_t				 curr_olen;
302 	int				 r;
303 
304 	*olen = 0;
305 
306 	if (fido_dev_register_manifest_func(fido_hid_manifest) != FIDO_OK)
307 		return (FIDO_ERR_INTERNAL);
308 #ifdef NFC_LINUX
309 	if (fido_dev_register_manifest_func(fido_nfc_manifest) != FIDO_OK)
310 		return (FIDO_ERR_INTERNAL);
311 #endif
312 #ifdef USE_WINHELLO
313 	if (fido_dev_register_manifest_func(fido_winhello_manifest) != FIDO_OK)
314 		return (FIDO_ERR_INTERNAL);
315 #endif
316 
317 	for (curr = manifest_funcs; curr != NULL; curr = curr->next) {
318 		curr_olen = 0;
319 		m_func = curr->manifest_func;
320 		r = m_func(devlist + *olen, ilen - *olen, &curr_olen);
321 		if (r != FIDO_OK)
322 			return (r);
323 		*olen += curr_olen;
324 		if (*olen == ilen)
325 			break;
326 	}
327 
328 	return (FIDO_OK);
329 }
330 
331 int
332 fido_dev_open_with_info(fido_dev_t *dev)
333 {
334 	if (dev->path == NULL)
335 		return (FIDO_ERR_INVALID_ARGUMENT);
336 
337 	return (fido_dev_open_wait(dev, dev->path, -1));
338 }
339 
340 int
341 fido_dev_open(fido_dev_t *dev, const char *path)
342 {
343 #ifdef NFC_LINUX
344 	/*
345 	 * this is a hack to get existing applications up and running with nfc;
346 	 * it will *NOT* be part of a libfido2 release. to support nfc in your
347 	 * application, please change it to use fido_dev_open_with_info().
348 	 */
349 	if (strncmp(path, "/sys", strlen("/sys")) == 0 && strlen(path) > 4 &&
350 	    path[strlen(path) - 4] == 'n' && path[strlen(path) - 3] == 'f' &&
351 	    path[strlen(path) - 2] == 'c') {
352 		dev->io_own = true;
353 		dev->io = (fido_dev_io_t) {
354 			fido_nfc_open,
355 			fido_nfc_close,
356 			fido_nfc_read,
357 			fido_nfc_write,
358 		};
359 		dev->transport = (fido_dev_transport_t) {
360 			fido_nfc_rx,
361 			fido_nfc_tx,
362 		};
363 	}
364 #endif
365 
366 	return (fido_dev_open_wait(dev, path, -1));
367 }
368 
369 int
370 fido_dev_close(fido_dev_t *dev)
371 {
372 #ifdef USE_WINHELLO
373 	if (dev->flags & FIDO_DEV_WINHELLO)
374 		return (fido_winhello_close(dev));
375 #endif
376 	if (dev->io_handle == NULL || dev->io.close == NULL)
377 		return (FIDO_ERR_INVALID_ARGUMENT);
378 
379 	dev->io.close(dev->io_handle);
380 	dev->io_handle = NULL;
381 	dev->cid = CTAP_CID_BROADCAST;
382 
383 	return (FIDO_OK);
384 }
385 
386 int
387 fido_dev_set_sigmask(fido_dev_t *dev, const fido_sigset_t *sigmask)
388 {
389 	if (dev->io_own || dev->io_handle == NULL || sigmask == NULL)
390 		return (FIDO_ERR_INVALID_ARGUMENT);
391 
392 #ifdef NFC_LINUX
393 	if (dev->transport.rx == fido_nfc_rx)
394 		return (fido_nfc_set_sigmask(dev->io_handle, sigmask));
395 #endif
396 	return (fido_hid_set_sigmask(dev->io_handle, sigmask));
397 }
398 
399 int
400 fido_dev_cancel(fido_dev_t *dev)
401 {
402 #ifdef USE_WINHELLO
403 	if (dev->flags & FIDO_DEV_WINHELLO)
404 		return (fido_winhello_cancel(dev));
405 #endif
406 	if (fido_dev_is_fido2(dev) == false)
407 		return (FIDO_ERR_INVALID_ARGUMENT);
408 	if (fido_tx(dev, CTAP_CMD_CANCEL, NULL, 0) < 0)
409 		return (FIDO_ERR_TX);
410 
411 	return (FIDO_OK);
412 }
413 
414 int
415 fido_dev_get_touch_begin(fido_dev_t *dev)
416 {
417 	fido_blob_t	 f;
418 	cbor_item_t	*argv[9];
419 	const char	*clientdata = FIDO_DUMMY_CLIENTDATA;
420 	const uint8_t	 user_id = FIDO_DUMMY_USER_ID;
421 	unsigned char	 cdh[SHA256_DIGEST_LENGTH];
422 	fido_rp_t	 rp;
423 	fido_user_t	 user;
424 	int		 r = FIDO_ERR_INTERNAL;
425 
426 	memset(&f, 0, sizeof(f));
427 	memset(argv, 0, sizeof(argv));
428 	memset(cdh, 0, sizeof(cdh));
429 	memset(&rp, 0, sizeof(rp));
430 	memset(&user, 0, sizeof(user));
431 
432 	if (fido_dev_is_fido2(dev) == false)
433 		return (u2f_get_touch_begin(dev));
434 
435 	if (SHA256((const void *)clientdata, strlen(clientdata), cdh) != cdh) {
436 		fido_log_debug("%s: sha256", __func__);
437 		return (FIDO_ERR_INTERNAL);
438 	}
439 
440 	if ((rp.id = strdup(FIDO_DUMMY_RP_ID)) == NULL ||
441 	    (user.name = strdup(FIDO_DUMMY_USER_NAME)) == NULL) {
442 		fido_log_debug("%s: strdup", __func__);
443 		goto fail;
444 	}
445 
446 	if (fido_blob_set(&user.id, &user_id, sizeof(user_id)) < 0) {
447 		fido_log_debug("%s: fido_blob_set", __func__);
448 		goto fail;
449 	}
450 
451 	if ((argv[0] = cbor_build_bytestring(cdh, sizeof(cdh))) == NULL ||
452 	    (argv[1] = cbor_encode_rp_entity(&rp)) == NULL ||
453 	    (argv[2] = cbor_encode_user_entity(&user)) == NULL ||
454 	    (argv[3] = cbor_encode_pubkey_param(COSE_ES256)) == NULL) {
455 		fido_log_debug("%s: cbor encode", __func__);
456 		goto fail;
457 	}
458 
459 	if (fido_dev_supports_pin(dev)) {
460 		if ((argv[7] = cbor_new_definite_bytestring()) == NULL ||
461 		    (argv[8] = cbor_encode_pin_opt(dev)) == NULL) {
462 			fido_log_debug("%s: cbor encode", __func__);
463 			goto fail;
464 		}
465 	}
466 
467 	if (cbor_build_frame(CTAP_CBOR_MAKECRED, argv, nitems(argv), &f) < 0 ||
468 	    fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
469 		fido_log_debug("%s: fido_tx", __func__);
470 		r = FIDO_ERR_TX;
471 		goto fail;
472 	}
473 
474 	r = FIDO_OK;
475 fail:
476 	cbor_vector_free(argv, nitems(argv));
477 	free(f.ptr);
478 	free(rp.id);
479 	free(user.name);
480 	free(user.id.ptr);
481 
482 	return (r);
483 }
484 
485 int
486 fido_dev_get_touch_status(fido_dev_t *dev, int *touched, int ms)
487 {
488 	int r;
489 
490 	*touched = 0;
491 
492 	if (fido_dev_is_fido2(dev) == false)
493 		return (u2f_get_touch_status(dev, touched, ms));
494 
495 	switch ((r = fido_rx_cbor_status(dev, ms))) {
496 	case FIDO_ERR_PIN_AUTH_INVALID:
497 	case FIDO_ERR_PIN_INVALID:
498 	case FIDO_ERR_PIN_NOT_SET:
499 	case FIDO_ERR_SUCCESS:
500 		*touched = 1;
501 		break;
502 	case FIDO_ERR_RX:
503 		/* ignore */
504 		break;
505 	default:
506 		fido_log_debug("%s: fido_rx_cbor_status", __func__);
507 		return (r);
508 	}
509 
510 	return (FIDO_OK);
511 }
512 
513 int
514 fido_dev_set_io_functions(fido_dev_t *dev, const fido_dev_io_t *io)
515 {
516 	if (dev->io_handle != NULL) {
517 		fido_log_debug("%s: non-NULL handle", __func__);
518 		return (FIDO_ERR_INVALID_ARGUMENT);
519 	}
520 
521 	if (io == NULL || io->open == NULL || io->close == NULL ||
522 	    io->read == NULL || io->write == NULL) {
523 		fido_log_debug("%s: NULL function", __func__);
524 		return (FIDO_ERR_INVALID_ARGUMENT);
525 	}
526 
527 	dev->io = *io;
528 	dev->io_own = true;
529 
530 	return (FIDO_OK);
531 }
532 
533 int
534 fido_dev_set_transport_functions(fido_dev_t *dev, const fido_dev_transport_t *t)
535 {
536 	if (dev->io_handle != NULL) {
537 		fido_log_debug("%s: non-NULL handle", __func__);
538 		return (FIDO_ERR_INVALID_ARGUMENT);
539 	}
540 
541 	dev->transport = *t;
542 	dev->io_own = true;
543 
544 	return (FIDO_OK);
545 }
546 
547 void
548 fido_init(int flags)
549 {
550 	if (flags & FIDO_DEBUG || getenv("FIDO_DEBUG") != NULL)
551 		fido_log_init();
552 
553 	disable_u2f_fallback = (flags & FIDO_DISABLE_U2F_FALLBACK);
554 }
555 
556 fido_dev_t *
557 fido_dev_new(void)
558 {
559 	fido_dev_t *dev;
560 
561 	if ((dev = calloc(1, sizeof(*dev))) == NULL)
562 		return (NULL);
563 
564 	dev->cid = CTAP_CID_BROADCAST;
565 	dev->io = (fido_dev_io_t) {
566 		&fido_hid_open,
567 		&fido_hid_close,
568 		&fido_hid_read,
569 		&fido_hid_write,
570 	};
571 
572 	return (dev);
573 }
574 
575 fido_dev_t *
576 fido_dev_new_with_info(const fido_dev_info_t *di)
577 {
578 	fido_dev_t *dev;
579 
580 	if ((dev = calloc(1, sizeof(*dev))) == NULL)
581 		return (NULL);
582 
583 #if 0
584 	if (di->io.open == NULL || di->io.close == NULL ||
585 	    di->io.read == NULL || di->io.write == NULL) {
586 		fido_log_debug("%s: NULL function", __func__);
587 		fido_dev_free(&dev);
588 		return (NULL);
589 	}
590 #endif
591 
592 	dev->io = di->io;
593 	dev->io_own = di->transport.tx != NULL || di->transport.rx != NULL;
594 	dev->transport = di->transport;
595 	dev->cid = CTAP_CID_BROADCAST;
596 
597 	if ((dev->path = strdup(di->path)) == NULL) {
598 		fido_log_debug("%s: strdup", __func__);
599 		fido_dev_free(&dev);
600 		return (NULL);
601 	}
602 
603 	return (dev);
604 }
605 
606 void
607 fido_dev_free(fido_dev_t **dev_p)
608 {
609 	fido_dev_t *dev;
610 
611 	if (dev_p == NULL || (dev = *dev_p) == NULL)
612 		return;
613 
614 	free(dev->path);
615 	free(dev);
616 
617 	*dev_p = NULL;
618 }
619 
620 uint8_t
621 fido_dev_protocol(const fido_dev_t *dev)
622 {
623 	return (dev->attr.protocol);
624 }
625 
626 uint8_t
627 fido_dev_major(const fido_dev_t *dev)
628 {
629 	return (dev->attr.major);
630 }
631 
632 uint8_t
633 fido_dev_minor(const fido_dev_t *dev)
634 {
635 	return (dev->attr.minor);
636 }
637 
638 uint8_t
639 fido_dev_build(const fido_dev_t *dev)
640 {
641 	return (dev->attr.build);
642 }
643 
644 uint8_t
645 fido_dev_flags(const fido_dev_t *dev)
646 {
647 	return (dev->attr.flags);
648 }
649 
650 bool
651 fido_dev_is_fido2(const fido_dev_t *dev)
652 {
653 	return (dev->attr.flags & FIDO_CAP_CBOR);
654 }
655 
656 bool
657 fido_dev_is_winhello(const fido_dev_t *dev)
658 {
659 	return (dev->flags & FIDO_DEV_WINHELLO);
660 }
661 
662 bool
663 fido_dev_supports_pin(const fido_dev_t *dev)
664 {
665 	return (dev->flags & (FIDO_DEV_PIN_SET|FIDO_DEV_PIN_UNSET));
666 }
667 
668 bool
669 fido_dev_has_pin(const fido_dev_t *dev)
670 {
671 	return (dev->flags & FIDO_DEV_PIN_SET);
672 }
673 
674 bool
675 fido_dev_supports_cred_prot(const fido_dev_t *dev)
676 {
677 	return (dev->flags & FIDO_DEV_CRED_PROT);
678 }
679 
680 bool
681 fido_dev_supports_credman(const fido_dev_t *dev)
682 {
683 	return (dev->flags & FIDO_DEV_CREDMAN);
684 }
685 
686 bool
687 fido_dev_supports_uv(const fido_dev_t *dev)
688 {
689 	return (dev->flags & (FIDO_DEV_UV_SET|FIDO_DEV_UV_UNSET));
690 }
691 
692 bool
693 fido_dev_has_uv(const fido_dev_t *dev)
694 {
695 	return (dev->flags & FIDO_DEV_UV_SET);
696 }
697 
698 bool
699 fido_dev_supports_permissions(const fido_dev_t *dev)
700 {
701 	return (dev->flags & FIDO_DEV_TOKEN_PERMS);
702 }
703 
704 void
705 fido_dev_force_u2f(fido_dev_t *dev)
706 {
707 	dev->attr.flags &= (uint8_t)~FIDO_CAP_CBOR;
708 	dev->flags = 0;
709 }
710 
711 void
712 fido_dev_force_fido2(fido_dev_t *dev)
713 {
714 	dev->attr.flags |= FIDO_CAP_CBOR;
715 }
716 
717 uint8_t
718 fido_dev_get_pin_protocol(const fido_dev_t *dev)
719 {
720 	if (dev->flags & FIDO_DEV_PIN_PROTOCOL2)
721 		return (CTAP_PIN_PROTOCOL2);
722 	else if (dev->flags & FIDO_DEV_PIN_PROTOCOL1)
723 		return (CTAP_PIN_PROTOCOL1);
724 
725 	return (0);
726 }
727 
728 uint64_t
729 fido_dev_maxmsgsize(const fido_dev_t *dev)
730 {
731 	return (dev->maxmsgsize);
732 }
733