xref: /freebsd/contrib/libfido2/src/dev.c (revision 1d386b48)
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, int *ms)
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),
165 	    ms) < 0) {
166 		fido_log_debug("%s: fido_tx", __func__);
167 		r = FIDO_ERR_TX;
168 		goto fail;
169 	}
170 
171 	return (FIDO_OK);
172 fail:
173 	dev->io.close(dev->io_handle);
174 	dev->io_handle = NULL;
175 
176 	return (r);
177 }
178 
179 static int
180 fido_dev_open_rx(fido_dev_t *dev, int *ms)
181 {
182 	fido_cbor_info_t	*info = NULL;
183 	int			 reply_len;
184 	int			 r;
185 
186 	if ((reply_len = fido_rx(dev, CTAP_CMD_INIT, &dev->attr,
187 	    sizeof(dev->attr), ms)) < 0) {
188 		fido_log_debug("%s: fido_rx", __func__);
189 		r = FIDO_ERR_RX;
190 		goto fail;
191 	}
192 
193 #ifdef FIDO_FUZZ
194 	dev->attr.nonce = dev->nonce;
195 #endif
196 
197 	if ((size_t)reply_len != sizeof(dev->attr) ||
198 	    dev->attr.nonce != dev->nonce) {
199 		fido_log_debug("%s: invalid nonce", __func__);
200 		r = FIDO_ERR_RX;
201 		goto fail;
202 	}
203 
204 	dev->flags = 0;
205 	dev->cid = dev->attr.cid;
206 
207 	if (fido_dev_is_fido2(dev)) {
208 		if ((info = fido_cbor_info_new()) == NULL) {
209 			fido_log_debug("%s: fido_cbor_info_new", __func__);
210 			r = FIDO_ERR_INTERNAL;
211 			goto fail;
212 		}
213 		if ((r = fido_dev_get_cbor_info_wait(dev, info,
214 		    ms)) != FIDO_OK) {
215 			fido_log_debug("%s: fido_dev_cbor_info_wait: %d",
216 			    __func__, r);
217 			if (disable_u2f_fallback)
218 				goto fail;
219 			fido_log_debug("%s: falling back to u2f", __func__);
220 			fido_dev_force_u2f(dev);
221 		} else {
222 			fido_dev_set_flags(dev, info);
223 		}
224 	}
225 
226 	if (fido_dev_is_fido2(dev) && info != NULL) {
227 		dev->maxmsgsize = fido_cbor_info_maxmsgsiz(info);
228 		fido_log_debug("%s: FIDO_MAXMSG=%d, maxmsgsiz=%lu", __func__,
229 		    FIDO_MAXMSG, (unsigned long)dev->maxmsgsize);
230 	}
231 
232 	r = FIDO_OK;
233 fail:
234 	fido_cbor_info_free(&info);
235 
236 	if (r != FIDO_OK) {
237 		dev->io.close(dev->io_handle);
238 		dev->io_handle = NULL;
239 	}
240 
241 	return (r);
242 }
243 
244 static int
245 fido_dev_open_wait(fido_dev_t *dev, const char *path, int *ms)
246 {
247 	int r;
248 
249 #ifdef USE_WINHELLO
250 	if (strcmp(path, FIDO_WINHELLO_PATH) == 0)
251 		return (fido_winhello_open(dev));
252 #endif
253 	if ((r = fido_dev_open_tx(dev, path, ms)) != FIDO_OK ||
254 	    (r = fido_dev_open_rx(dev, ms)) != FIDO_OK)
255 		return (r);
256 
257 	return (FIDO_OK);
258 }
259 
260 int
261 fido_dev_register_manifest_func(const dev_manifest_func_t f)
262 {
263 	dev_manifest_func_node_t *prev, *curr, *n;
264 
265 	find_manifest_func_node(f, &curr, &prev);
266 	if (curr != NULL)
267 		return (FIDO_OK);
268 
269 	if ((n = calloc(1, sizeof(*n))) == NULL) {
270 		fido_log_debug("%s: calloc", __func__);
271 		return (FIDO_ERR_INTERNAL);
272 	}
273 
274 	n->manifest_func = f;
275 	n->next = manifest_funcs;
276 	manifest_funcs = n;
277 
278 	return (FIDO_OK);
279 }
280 
281 void
282 fido_dev_unregister_manifest_func(const dev_manifest_func_t f)
283 {
284 	dev_manifest_func_node_t *prev, *curr;
285 
286 	find_manifest_func_node(f, &curr, &prev);
287 	if (curr == NULL)
288 		return;
289 	if (prev != NULL)
290 		prev->next = curr->next;
291 	else
292 		manifest_funcs = curr->next;
293 
294 	free(curr);
295 }
296 
297 int
298 fido_dev_info_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
299 {
300 	dev_manifest_func_node_t	*curr = NULL;
301 	dev_manifest_func_t		 m_func;
302 	size_t				 curr_olen;
303 	int				 r;
304 
305 	*olen = 0;
306 
307 	if (fido_dev_register_manifest_func(fido_hid_manifest) != FIDO_OK)
308 		return (FIDO_ERR_INTERNAL);
309 #ifdef NFC_LINUX
310 	if (fido_dev_register_manifest_func(fido_nfc_manifest) != FIDO_OK)
311 		return (FIDO_ERR_INTERNAL);
312 #endif
313 #ifdef USE_WINHELLO
314 	if (fido_dev_register_manifest_func(fido_winhello_manifest) != FIDO_OK)
315 		return (FIDO_ERR_INTERNAL);
316 #endif
317 
318 	for (curr = manifest_funcs; curr != NULL; curr = curr->next) {
319 		curr_olen = 0;
320 		m_func = curr->manifest_func;
321 		r = m_func(devlist + *olen, ilen - *olen, &curr_olen);
322 		if (r != FIDO_OK)
323 			return (r);
324 		*olen += curr_olen;
325 		if (*olen == ilen)
326 			break;
327 	}
328 
329 	return (FIDO_OK);
330 }
331 
332 int
333 fido_dev_open_with_info(fido_dev_t *dev)
334 {
335 	int ms = dev->timeout_ms;
336 
337 	if (dev->path == NULL)
338 		return (FIDO_ERR_INVALID_ARGUMENT);
339 
340 	return (fido_dev_open_wait(dev, dev->path, &ms));
341 }
342 
343 int
344 fido_dev_open(fido_dev_t *dev, const char *path)
345 {
346 	int ms = dev->timeout_ms;
347 
348 #ifdef NFC_LINUX
349 	if (strncmp(path, FIDO_NFC_PREFIX, strlen(FIDO_NFC_PREFIX)) == 0) {
350 		dev->io_own = true;
351 		dev->io = (fido_dev_io_t) {
352 			fido_nfc_open,
353 			fido_nfc_close,
354 			fido_nfc_read,
355 			fido_nfc_write,
356 		};
357 		dev->transport = (fido_dev_transport_t) {
358 			fido_nfc_rx,
359 			fido_nfc_tx,
360 		};
361 	}
362 #endif
363 
364 	return (fido_dev_open_wait(dev, path, &ms));
365 }
366 
367 int
368 fido_dev_close(fido_dev_t *dev)
369 {
370 #ifdef USE_WINHELLO
371 	if (dev->flags & FIDO_DEV_WINHELLO)
372 		return (fido_winhello_close(dev));
373 #endif
374 	if (dev->io_handle == NULL || dev->io.close == NULL)
375 		return (FIDO_ERR_INVALID_ARGUMENT);
376 
377 	dev->io.close(dev->io_handle);
378 	dev->io_handle = NULL;
379 	dev->cid = CTAP_CID_BROADCAST;
380 
381 	return (FIDO_OK);
382 }
383 
384 int
385 fido_dev_set_sigmask(fido_dev_t *dev, const fido_sigset_t *sigmask)
386 {
387 	if (dev->io_handle == NULL || sigmask == NULL)
388 		return (FIDO_ERR_INVALID_ARGUMENT);
389 
390 #ifdef NFC_LINUX
391 	if (dev->transport.rx == fido_nfc_rx && dev->io.read == fido_nfc_read)
392 		return (fido_nfc_set_sigmask(dev->io_handle, sigmask));
393 #endif
394 	if (dev->transport.rx == NULL && dev->io.read == fido_hid_read)
395 		return (fido_hid_set_sigmask(dev->io_handle, sigmask));
396 
397 	return (FIDO_ERR_INVALID_ARGUMENT);
398 }
399 
400 int
401 fido_dev_cancel(fido_dev_t *dev)
402 {
403 	int ms = dev->timeout_ms;
404 
405 #ifdef USE_WINHELLO
406 	if (dev->flags & FIDO_DEV_WINHELLO)
407 		return (fido_winhello_cancel(dev));
408 #endif
409 	if (fido_dev_is_fido2(dev) == false)
410 		return (FIDO_ERR_INVALID_ARGUMENT);
411 	if (fido_tx(dev, CTAP_CMD_CANCEL, NULL, 0, &ms) < 0)
412 		return (FIDO_ERR_TX);
413 
414 	return (FIDO_OK);
415 }
416 
417 int
418 fido_dev_get_touch_begin(fido_dev_t *dev)
419 {
420 	fido_blob_t	 f;
421 	cbor_item_t	*argv[9];
422 	const char	*clientdata = FIDO_DUMMY_CLIENTDATA;
423 	const uint8_t	 user_id = FIDO_DUMMY_USER_ID;
424 	unsigned char	 cdh[SHA256_DIGEST_LENGTH];
425 	fido_rp_t	 rp;
426 	fido_user_t	 user;
427 	int		 ms = dev->timeout_ms;
428 	int		 r = FIDO_ERR_INTERNAL;
429 
430 	memset(&f, 0, sizeof(f));
431 	memset(argv, 0, sizeof(argv));
432 	memset(cdh, 0, sizeof(cdh));
433 	memset(&rp, 0, sizeof(rp));
434 	memset(&user, 0, sizeof(user));
435 
436 	if (fido_dev_is_fido2(dev) == false)
437 		return (u2f_get_touch_begin(dev, &ms));
438 
439 	if (SHA256((const void *)clientdata, strlen(clientdata), cdh) != cdh) {
440 		fido_log_debug("%s: sha256", __func__);
441 		return (FIDO_ERR_INTERNAL);
442 	}
443 
444 	if ((rp.id = strdup(FIDO_DUMMY_RP_ID)) == NULL ||
445 	    (user.name = strdup(FIDO_DUMMY_USER_NAME)) == NULL) {
446 		fido_log_debug("%s: strdup", __func__);
447 		goto fail;
448 	}
449 
450 	if (fido_blob_set(&user.id, &user_id, sizeof(user_id)) < 0) {
451 		fido_log_debug("%s: fido_blob_set", __func__);
452 		goto fail;
453 	}
454 
455 	if ((argv[0] = cbor_build_bytestring(cdh, sizeof(cdh))) == NULL ||
456 	    (argv[1] = cbor_encode_rp_entity(&rp)) == NULL ||
457 	    (argv[2] = cbor_encode_user_entity(&user)) == NULL ||
458 	    (argv[3] = cbor_encode_pubkey_param(COSE_ES256)) == NULL) {
459 		fido_log_debug("%s: cbor encode", __func__);
460 		goto fail;
461 	}
462 
463 	if (fido_dev_supports_pin(dev)) {
464 		if ((argv[7] = cbor_new_definite_bytestring()) == NULL ||
465 		    (argv[8] = cbor_encode_pin_opt(dev)) == NULL) {
466 			fido_log_debug("%s: cbor encode", __func__);
467 			goto fail;
468 		}
469 	}
470 
471 	if (cbor_build_frame(CTAP_CBOR_MAKECRED, argv, nitems(argv), &f) < 0 ||
472 	    fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, &ms) < 0) {
473 		fido_log_debug("%s: fido_tx", __func__);
474 		r = FIDO_ERR_TX;
475 		goto fail;
476 	}
477 
478 	r = FIDO_OK;
479 fail:
480 	cbor_vector_free(argv, nitems(argv));
481 	free(f.ptr);
482 	free(rp.id);
483 	free(user.name);
484 	free(user.id.ptr);
485 
486 	return (r);
487 }
488 
489 int
490 fido_dev_get_touch_status(fido_dev_t *dev, int *touched, int ms)
491 {
492 	int r;
493 
494 	*touched = 0;
495 
496 	if (fido_dev_is_fido2(dev) == false)
497 		return (u2f_get_touch_status(dev, touched, &ms));
498 
499 	switch ((r = fido_rx_cbor_status(dev, &ms))) {
500 	case FIDO_ERR_PIN_AUTH_INVALID:
501 	case FIDO_ERR_PIN_INVALID:
502 	case FIDO_ERR_PIN_NOT_SET:
503 	case FIDO_ERR_SUCCESS:
504 		*touched = 1;
505 		break;
506 	case FIDO_ERR_RX:
507 		/* ignore */
508 		break;
509 	default:
510 		fido_log_debug("%s: fido_rx_cbor_status", __func__);
511 		return (r);
512 	}
513 
514 	return (FIDO_OK);
515 }
516 
517 int
518 fido_dev_set_io_functions(fido_dev_t *dev, const fido_dev_io_t *io)
519 {
520 	if (dev->io_handle != NULL) {
521 		fido_log_debug("%s: non-NULL handle", __func__);
522 		return (FIDO_ERR_INVALID_ARGUMENT);
523 	}
524 
525 	if (io == NULL || io->open == NULL || io->close == NULL ||
526 	    io->read == NULL || io->write == NULL) {
527 		fido_log_debug("%s: NULL function", __func__);
528 		return (FIDO_ERR_INVALID_ARGUMENT);
529 	}
530 
531 	dev->io = *io;
532 	dev->io_own = true;
533 
534 	return (FIDO_OK);
535 }
536 
537 int
538 fido_dev_set_transport_functions(fido_dev_t *dev, const fido_dev_transport_t *t)
539 {
540 	if (dev->io_handle != NULL) {
541 		fido_log_debug("%s: non-NULL handle", __func__);
542 		return (FIDO_ERR_INVALID_ARGUMENT);
543 	}
544 
545 	dev->transport = *t;
546 	dev->io_own = true;
547 
548 	return (FIDO_OK);
549 }
550 
551 void *
552 fido_dev_io_handle(const fido_dev_t *dev)
553 {
554 
555 	return (dev->io_handle);
556 }
557 
558 void
559 fido_init(int flags)
560 {
561 	if (flags & FIDO_DEBUG || getenv("FIDO_DEBUG") != NULL)
562 		fido_log_init();
563 
564 	disable_u2f_fallback = (flags & FIDO_DISABLE_U2F_FALLBACK);
565 }
566 
567 fido_dev_t *
568 fido_dev_new(void)
569 {
570 	fido_dev_t *dev;
571 
572 	if ((dev = calloc(1, sizeof(*dev))) == NULL)
573 		return (NULL);
574 
575 	dev->cid = CTAP_CID_BROADCAST;
576 	dev->timeout_ms = -1;
577 	dev->io = (fido_dev_io_t) {
578 		&fido_hid_open,
579 		&fido_hid_close,
580 		&fido_hid_read,
581 		&fido_hid_write,
582 	};
583 
584 	return (dev);
585 }
586 
587 fido_dev_t *
588 fido_dev_new_with_info(const fido_dev_info_t *di)
589 {
590 	fido_dev_t *dev;
591 
592 	if ((dev = calloc(1, sizeof(*dev))) == NULL)
593 		return (NULL);
594 
595 #if 0
596 	if (di->io.open == NULL || di->io.close == NULL ||
597 	    di->io.read == NULL || di->io.write == NULL) {
598 		fido_log_debug("%s: NULL function", __func__);
599 		fido_dev_free(&dev);
600 		return (NULL);
601 	}
602 #endif
603 
604 	dev->io = di->io;
605 	dev->io_own = di->transport.tx != NULL || di->transport.rx != NULL;
606 	dev->transport = di->transport;
607 	dev->cid = CTAP_CID_BROADCAST;
608 	dev->timeout_ms = -1;
609 
610 	if ((dev->path = strdup(di->path)) == NULL) {
611 		fido_log_debug("%s: strdup", __func__);
612 		fido_dev_free(&dev);
613 		return (NULL);
614 	}
615 
616 	return (dev);
617 }
618 
619 void
620 fido_dev_free(fido_dev_t **dev_p)
621 {
622 	fido_dev_t *dev;
623 
624 	if (dev_p == NULL || (dev = *dev_p) == NULL)
625 		return;
626 
627 	free(dev->path);
628 	free(dev);
629 
630 	*dev_p = NULL;
631 }
632 
633 uint8_t
634 fido_dev_protocol(const fido_dev_t *dev)
635 {
636 	return (dev->attr.protocol);
637 }
638 
639 uint8_t
640 fido_dev_major(const fido_dev_t *dev)
641 {
642 	return (dev->attr.major);
643 }
644 
645 uint8_t
646 fido_dev_minor(const fido_dev_t *dev)
647 {
648 	return (dev->attr.minor);
649 }
650 
651 uint8_t
652 fido_dev_build(const fido_dev_t *dev)
653 {
654 	return (dev->attr.build);
655 }
656 
657 uint8_t
658 fido_dev_flags(const fido_dev_t *dev)
659 {
660 	return (dev->attr.flags);
661 }
662 
663 bool
664 fido_dev_is_fido2(const fido_dev_t *dev)
665 {
666 	return (dev->attr.flags & FIDO_CAP_CBOR);
667 }
668 
669 bool
670 fido_dev_is_winhello(const fido_dev_t *dev)
671 {
672 	return (dev->flags & FIDO_DEV_WINHELLO);
673 }
674 
675 bool
676 fido_dev_supports_pin(const fido_dev_t *dev)
677 {
678 	return (dev->flags & (FIDO_DEV_PIN_SET|FIDO_DEV_PIN_UNSET));
679 }
680 
681 bool
682 fido_dev_has_pin(const fido_dev_t *dev)
683 {
684 	return (dev->flags & FIDO_DEV_PIN_SET);
685 }
686 
687 bool
688 fido_dev_supports_cred_prot(const fido_dev_t *dev)
689 {
690 	return (dev->flags & FIDO_DEV_CRED_PROT);
691 }
692 
693 bool
694 fido_dev_supports_credman(const fido_dev_t *dev)
695 {
696 	return (dev->flags & FIDO_DEV_CREDMAN);
697 }
698 
699 bool
700 fido_dev_supports_uv(const fido_dev_t *dev)
701 {
702 	return (dev->flags & (FIDO_DEV_UV_SET|FIDO_DEV_UV_UNSET));
703 }
704 
705 bool
706 fido_dev_has_uv(const fido_dev_t *dev)
707 {
708 	return (dev->flags & FIDO_DEV_UV_SET);
709 }
710 
711 bool
712 fido_dev_supports_permissions(const fido_dev_t *dev)
713 {
714 	return (dev->flags & FIDO_DEV_TOKEN_PERMS);
715 }
716 
717 void
718 fido_dev_force_u2f(fido_dev_t *dev)
719 {
720 	dev->attr.flags &= (uint8_t)~FIDO_CAP_CBOR;
721 	dev->flags = 0;
722 }
723 
724 void
725 fido_dev_force_fido2(fido_dev_t *dev)
726 {
727 	dev->attr.flags |= FIDO_CAP_CBOR;
728 }
729 
730 uint8_t
731 fido_dev_get_pin_protocol(const fido_dev_t *dev)
732 {
733 	if (dev->flags & FIDO_DEV_PIN_PROTOCOL2)
734 		return (CTAP_PIN_PROTOCOL2);
735 	else if (dev->flags & FIDO_DEV_PIN_PROTOCOL1)
736 		return (CTAP_PIN_PROTOCOL1);
737 
738 	return (0);
739 }
740 
741 uint64_t
742 fido_dev_maxmsgsize(const fido_dev_t *dev)
743 {
744 	return (dev->maxmsgsize);
745 }
746 
747 int
748 fido_dev_set_timeout(fido_dev_t *dev, int ms)
749 {
750 	if (ms < -1)
751 		return (FIDO_ERR_INVALID_ARGUMENT);
752 
753 	dev->timeout_ms = ms;
754 
755 	return (FIDO_OK);
756 }
757