1*a6c5e0d9Schristos /*	$NetBSD: sk-usbhid.c,v 1.8 2022/10/05 22:39:36 christos Exp $	*/
2*a6c5e0d9Schristos /* $OpenBSD: sk-usbhid.c,v 1.45 2022/09/14 00:14:37 djm Exp $ */
3*a6c5e0d9Schristos 
4b5c5924fSchristos /*
5b5c5924fSchristos  * Copyright (c) 2019 Markus Friedl
67d35d687Schristos  * Copyright (c) 2020 Pedro Martelletto
7b5c5924fSchristos  *
8b5c5924fSchristos  * Permission to use, copy, modify, and distribute this software for any
9b5c5924fSchristos  * purpose with or without fee is hereby granted, provided that the above
10b5c5924fSchristos  * copyright notice and this permission notice appear in all copies.
11b5c5924fSchristos  *
12b5c5924fSchristos  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13b5c5924fSchristos  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14b5c5924fSchristos  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15b5c5924fSchristos  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16b5c5924fSchristos  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17b5c5924fSchristos  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18b5c5924fSchristos  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19b5c5924fSchristos  */
20c8864e3fSchristos #include "includes.h"
21*a6c5e0d9Schristos __RCSID("$NetBSD: sk-usbhid.c,v 1.8 2022/10/05 22:39:36 christos Exp $");
22b5c5924fSchristos 
23b5c5924fSchristos #include <stdint.h>
24b5c5924fSchristos #include <stdlib.h>
25b5c5924fSchristos #include <string.h>
26b5c5924fSchristos #include <stdio.h>
27b5c5924fSchristos #include <stddef.h>
28b5c5924fSchristos #include <stdarg.h>
29c305e046Schristos #include <time.h>
30b5c5924fSchristos 
31b5c5924fSchristos #ifdef WITH_OPENSSL
32b5c5924fSchristos #include <openssl/opensslv.h>
33b5c5924fSchristos #include <openssl/crypto.h>
34b5c5924fSchristos #include <openssl/bn.h>
35b5c5924fSchristos #include <openssl/ec.h>
36b5c5924fSchristos #include <openssl/ecdsa.h>
37e29d081dSchristos #include <openssl/evp.h>
38b5c5924fSchristos #endif /* WITH_OPENSSL */
39b5c5924fSchristos 
40b5c5924fSchristos #include <fido.h>
41b5c5924fSchristos #include <fido/credman.h>
42b5c5924fSchristos 
43b5c5924fSchristos #ifndef SK_STANDALONE
44b5c5924fSchristos # include "log.h"
45b5c5924fSchristos # include "xmalloc.h"
467d35d687Schristos # include "misc.h"
47b5c5924fSchristos /*
48b5c5924fSchristos  * If building as part of OpenSSH, then rename exported functions.
49b5c5924fSchristos  * This must be done before including sk-api.h.
50b5c5924fSchristos  */
51b5c5924fSchristos # define sk_api_version		ssh_sk_api_version
52b5c5924fSchristos # define sk_enroll		ssh_sk_enroll
53b5c5924fSchristos # define sk_sign		ssh_sk_sign
54b5c5924fSchristos # define sk_load_resident_keys	ssh_sk_load_resident_keys
55b5c5924fSchristos #endif /* !SK_STANDALONE */
56b5c5924fSchristos 
57b5c5924fSchristos #include "sk-api.h"
58b5c5924fSchristos 
59b5c5924fSchristos /* #define SK_DEBUG 1 */
60b5c5924fSchristos 
617d35d687Schristos #ifdef SK_DEBUG
627d35d687Schristos #define SSH_FIDO_INIT_ARG	FIDO_DEBUG
637d35d687Schristos #else
647d35d687Schristos #define SSH_FIDO_INIT_ARG	0
657d35d687Schristos #endif
667d35d687Schristos 
677d35d687Schristos #define MAX_FIDO_DEVICES	8
687d35d687Schristos #define FIDO_POLL_MS		50
697d35d687Schristos #define SELECT_MS		15000
707d35d687Schristos #define POLL_SLEEP_NS		200000000
71b5c5924fSchristos 
72b5c5924fSchristos /* Compatibility with OpenSSH 1.0.x */
73b5c5924fSchristos #if (OPENSSL_VERSION_NUMBER < 0x10100000L)
74b5c5924fSchristos #define ECDSA_SIG_get0(sig, pr, ps) \
75b5c5924fSchristos 	do { \
76b5c5924fSchristos 		(*pr) = sig->r; \
77b5c5924fSchristos 		(*ps) = sig->s; \
78b5c5924fSchristos 	} while (0)
79b5c5924fSchristos #endif
8084f89c72Schristos #ifndef FIDO_ERR_OPERATION_DENIED
8184f89c72Schristos #define FIDO_ERR_OPERATION_DENIED 0x27
8284f89c72Schristos #endif
83b5c5924fSchristos 
847d35d687Schristos struct sk_usbhid {
857d35d687Schristos 	fido_dev_t *dev;
867d35d687Schristos 	char *path;
877d35d687Schristos };
887d35d687Schristos 
89b5c5924fSchristos /* Return the version of the middleware API */
90b5c5924fSchristos uint32_t sk_api_version(void);
91b5c5924fSchristos 
92b5c5924fSchristos /* Enroll a U2F key (private key generation) */
93b5c5924fSchristos int sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len,
94b5c5924fSchristos     const char *application, uint8_t flags, const char *pin,
95b5c5924fSchristos     struct sk_option **options, struct sk_enroll_response **enroll_response);
96b5c5924fSchristos 
97b5c5924fSchristos /* Sign a challenge */
98c305e046Schristos int sk_sign(uint32_t alg, const uint8_t *data, size_t data_len,
99b5c5924fSchristos     const char *application, const uint8_t *key_handle, size_t key_handle_len,
100b5c5924fSchristos     uint8_t flags, const char *pin, struct sk_option **options,
101b5c5924fSchristos     struct sk_sign_response **sign_response);
102b5c5924fSchristos 
103b5c5924fSchristos /* Load resident keys */
104b5c5924fSchristos int sk_load_resident_keys(const char *pin, struct sk_option **options,
105b5c5924fSchristos     struct sk_resident_key ***rks, size_t *nrks);
106b5c5924fSchristos 
107b5c5924fSchristos static void skdebug(const char *func, const char *fmt, ...)
108b5c5924fSchristos     __attribute__((__format__ (printf, 2, 3)));
109b5c5924fSchristos 
110b5c5924fSchristos static void
skdebug(const char * func,const char * fmt,...)111b5c5924fSchristos skdebug(const char *func, const char *fmt, ...)
112b5c5924fSchristos {
113b5c5924fSchristos #if !defined(SK_STANDALONE)
114b5c5924fSchristos 	char *msg;
115b5c5924fSchristos 	va_list ap;
116b5c5924fSchristos 
117b5c5924fSchristos 	va_start(ap, fmt);
118b5c5924fSchristos 	xvasprintf(&msg, fmt, ap);
119b5c5924fSchristos 	va_end(ap);
120b5c5924fSchristos 	debug("%s: %s", func, msg);
121b5c5924fSchristos 	free(msg);
122b5c5924fSchristos #elif defined(SK_DEBUG)
123b5c5924fSchristos 	va_list ap;
124b5c5924fSchristos 
125b5c5924fSchristos 	va_start(ap, fmt);
126b5c5924fSchristos 	fprintf(stderr, "%s: ", func);
127b5c5924fSchristos 	vfprintf(stderr, fmt, ap);
128b5c5924fSchristos 	fputc('\n', stderr);
129b5c5924fSchristos 	va_end(ap);
130b5c5924fSchristos #else
131b5c5924fSchristos 	(void)func; /* XXX */
132b5c5924fSchristos 	(void)fmt; /* XXX */
133b5c5924fSchristos #endif
134b5c5924fSchristos }
135b5c5924fSchristos 
136b5c5924fSchristos uint32_t
sk_api_version(void)137b5c5924fSchristos sk_api_version(void)
138b5c5924fSchristos {
139b5c5924fSchristos 	return SSH_SK_VERSION_MAJOR;
140b5c5924fSchristos }
141b5c5924fSchristos 
1427d35d687Schristos static struct sk_usbhid *
sk_open(const char * path)1437d35d687Schristos sk_open(const char *path)
144b5c5924fSchristos {
1457d35d687Schristos 	struct sk_usbhid *sk;
146b5c5924fSchristos 	int r;
147b5c5924fSchristos 
1487d35d687Schristos 	if (path == NULL) {
1497d35d687Schristos 		skdebug(__func__, "path == NULL");
1507d35d687Schristos 		return NULL;
151b5c5924fSchristos 	}
1527d35d687Schristos 	if ((sk = calloc(1, sizeof(*sk))) == NULL) {
1537d35d687Schristos 		skdebug(__func__, "calloc sk failed");
1547d35d687Schristos 		return NULL;
1557d35d687Schristos 	}
1567d35d687Schristos 	if ((sk->path = strdup(path)) == NULL) {
1577d35d687Schristos 		skdebug(__func__, "strdup path failed");
1587d35d687Schristos 		free(sk);
1597d35d687Schristos 		return NULL;
1607d35d687Schristos 	}
1617d35d687Schristos 	if ((sk->dev = fido_dev_new()) == NULL) {
1627d35d687Schristos 		skdebug(__func__, "fido_dev_new failed");
1637d35d687Schristos 		free(sk->path);
1647d35d687Schristos 		free(sk);
1657d35d687Schristos 		return NULL;
1667d35d687Schristos 	}
1677d35d687Schristos 	if ((r = fido_dev_open(sk->dev, sk->path)) != FIDO_OK) {
1687d35d687Schristos 		skdebug(__func__, "fido_dev_open %s failed: %s", sk->path,
169b5c5924fSchristos 		    fido_strerr(r));
1707d35d687Schristos 		fido_dev_free(&sk->dev);
1717d35d687Schristos 		free(sk->path);
1727d35d687Schristos 		free(sk);
1737d35d687Schristos 		return NULL;
174b5c5924fSchristos 	}
1757d35d687Schristos 	return sk;
176b5c5924fSchristos }
177b5c5924fSchristos 
1787d35d687Schristos static void
sk_close(struct sk_usbhid * sk)1797d35d687Schristos sk_close(struct sk_usbhid *sk)
1807d35d687Schristos {
1817d35d687Schristos 	if (sk == NULL)
1827d35d687Schristos 		return;
1837d35d687Schristos 	fido_dev_cancel(sk->dev); /* cancel any pending operation */
1847d35d687Schristos 	fido_dev_close(sk->dev);
1857d35d687Schristos 	fido_dev_free(&sk->dev);
1867d35d687Schristos 	free(sk->path);
1877d35d687Schristos 	free(sk);
1887d35d687Schristos }
1897d35d687Schristos 
1907d35d687Schristos static struct sk_usbhid **
sk_openv(const fido_dev_info_t * devlist,size_t ndevs,size_t * nopen)1917d35d687Schristos sk_openv(const fido_dev_info_t *devlist, size_t ndevs, size_t *nopen)
1927d35d687Schristos {
1937d35d687Schristos 	const fido_dev_info_t *di;
1947d35d687Schristos 	struct sk_usbhid **skv;
1957d35d687Schristos 	size_t i;
1967d35d687Schristos 
1977d35d687Schristos 	*nopen = 0;
1987d35d687Schristos 	if ((skv = calloc(ndevs, sizeof(*skv))) == NULL) {
1997d35d687Schristos 		skdebug(__func__, "calloc skv failed");
2007d35d687Schristos 		return NULL;
2017d35d687Schristos 	}
2027d35d687Schristos 	for (i = 0; i < ndevs; i++) {
2037d35d687Schristos 		if ((di = fido_dev_info_ptr(devlist, i)) == NULL)
2047d35d687Schristos 			skdebug(__func__, "fido_dev_info_ptr failed");
2057d35d687Schristos 		else if ((skv[*nopen] = sk_open(fido_dev_info_path(di))) == NULL)
2067d35d687Schristos 			skdebug(__func__, "sk_open failed");
2077d35d687Schristos 		else
2087d35d687Schristos 			(*nopen)++;
2097d35d687Schristos 	}
2107d35d687Schristos 	if (*nopen == 0) {
2117d35d687Schristos 		for (i = 0; i < ndevs; i++)
2127d35d687Schristos 			sk_close(skv[i]);
2137d35d687Schristos 		free(skv);
2147d35d687Schristos 		skv = NULL;
2157d35d687Schristos 	}
2167d35d687Schristos 
2177d35d687Schristos 	return skv;
2187d35d687Schristos }
2197d35d687Schristos 
2207d35d687Schristos static void
sk_closev(struct sk_usbhid ** skv,size_t nsk)2217d35d687Schristos sk_closev(struct sk_usbhid **skv, size_t nsk)
2227d35d687Schristos {
2237d35d687Schristos 	size_t i;
2247d35d687Schristos 
2257d35d687Schristos 	for (i = 0; i < nsk; i++)
2267d35d687Schristos 		sk_close(skv[i]);
2277d35d687Schristos 	free(skv);
2287d35d687Schristos }
2297d35d687Schristos 
230b5c5924fSchristos static int
sk_touch_begin(struct sk_usbhid ** skv,size_t nsk)2317d35d687Schristos sk_touch_begin(struct sk_usbhid **skv, size_t nsk)
2327d35d687Schristos {
2337d35d687Schristos 	size_t i, ok = 0;
2347d35d687Schristos 	int r;
2357d35d687Schristos 
2367d35d687Schristos 	for (i = 0; i < nsk; i++)
2377d35d687Schristos 		if ((r = fido_dev_get_touch_begin(skv[i]->dev)) != FIDO_OK)
2387d35d687Schristos 			skdebug(__func__, "fido_dev_get_touch_begin %s failed:"
2397d35d687Schristos 			    " %s", skv[i]->path, fido_strerr(r));
2407d35d687Schristos 		else
2417d35d687Schristos 			ok++;
2427d35d687Schristos 
2437d35d687Schristos 	return ok ? 0 : -1;
2447d35d687Schristos }
2457d35d687Schristos 
2467d35d687Schristos static int
sk_touch_poll(struct sk_usbhid ** skv,size_t nsk,int * touch,size_t * idx)2477d35d687Schristos sk_touch_poll(struct sk_usbhid **skv, size_t nsk, int *touch, size_t *idx)
2487d35d687Schristos {
2497d35d687Schristos 	struct timespec ts_pause;
2507d35d687Schristos 	size_t npoll, i;
2517d35d687Schristos 	int r;
2527d35d687Schristos 
2537d35d687Schristos 	ts_pause.tv_sec = 0;
2547d35d687Schristos 	ts_pause.tv_nsec = POLL_SLEEP_NS;
2557d35d687Schristos 	nanosleep(&ts_pause, NULL);
2567d35d687Schristos 	npoll = nsk;
2577d35d687Schristos 	for (i = 0; i < nsk; i++) {
2587d35d687Schristos 		if (skv[i] == NULL)
2597d35d687Schristos 			continue; /* device discarded */
2607d35d687Schristos 		skdebug(__func__, "polling %s", skv[i]->path);
2617d35d687Schristos 		if ((r = fido_dev_get_touch_status(skv[i]->dev, touch,
2627d35d687Schristos 		    FIDO_POLL_MS)) != FIDO_OK) {
2637d35d687Schristos 			skdebug(__func__, "fido_dev_get_touch_status %s: %s",
2647d35d687Schristos 			    skv[i]->path, fido_strerr(r));
2657d35d687Schristos 			sk_close(skv[i]); /* discard device */
2667d35d687Schristos 			skv[i] = NULL;
2677d35d687Schristos 			if (--npoll == 0) {
2687d35d687Schristos 				skdebug(__func__, "no device left to poll");
2697d35d687Schristos 				return -1;
2707d35d687Schristos 			}
2717d35d687Schristos 		} else if (*touch) {
2727d35d687Schristos 			*idx = i;
2737d35d687Schristos 			return 0;
2747d35d687Schristos 		}
2757d35d687Schristos 	}
2767d35d687Schristos 	*touch = 0;
2777d35d687Schristos 	return 0;
2787d35d687Schristos }
2797d35d687Schristos 
2807d35d687Schristos /* Check if the specified key handle exists on a given sk. */
2817d35d687Schristos static int
sk_try(const struct sk_usbhid * sk,const char * application,const uint8_t * key_handle,size_t key_handle_len)2827d35d687Schristos sk_try(const struct sk_usbhid *sk, const char *application,
2837d35d687Schristos     const uint8_t *key_handle, size_t key_handle_len)
284b5c5924fSchristos {
285b5c5924fSchristos 	fido_assert_t *assert = NULL;
286b5c5924fSchristos 	int r = FIDO_ERR_INTERNAL;
28784f89c72Schristos 	uint8_t message[32];
288b5c5924fSchristos 
28984f89c72Schristos 	memset(message, '\0', sizeof(message));
290b5c5924fSchristos 	if ((assert = fido_assert_new()) == NULL) {
291b5c5924fSchristos 		skdebug(__func__, "fido_assert_new failed");
292b5c5924fSchristos 		goto out;
293b5c5924fSchristos 	}
29484f89c72Schristos 	/* generate an invalid signature on FIDO2 tokens */
29584f89c72Schristos 	if ((r = fido_assert_set_clientdata(assert, message,
2967d35d687Schristos 	    sizeof(message))) != FIDO_OK) {
297*a6c5e0d9Schristos 		skdebug(__func__, "fido_assert_set_clientdata: %s",
298b5c5924fSchristos 		    fido_strerr(r));
299b5c5924fSchristos 		goto out;
300b5c5924fSchristos 	}
301b5c5924fSchristos 	if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) {
302b5c5924fSchristos 		skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r));
303b5c5924fSchristos 		goto out;
304b5c5924fSchristos 	}
305b5c5924fSchristos 	if ((r = fido_assert_allow_cred(assert, key_handle,
306b5c5924fSchristos 	    key_handle_len)) != FIDO_OK) {
307b5c5924fSchristos 		skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r));
308b5c5924fSchristos 		goto out;
309b5c5924fSchristos 	}
310b5c5924fSchristos 	if ((r = fido_assert_set_up(assert, FIDO_OPT_FALSE)) != FIDO_OK) {
311b5c5924fSchristos 		skdebug(__func__, "fido_assert_up: %s", fido_strerr(r));
312b5c5924fSchristos 		goto out;
313b5c5924fSchristos 	}
3147d35d687Schristos 	r = fido_dev_get_assert(sk->dev, assert, NULL);
315b5c5924fSchristos 	skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r));
316b5c5924fSchristos 	if (r == FIDO_ERR_USER_PRESENCE_REQUIRED) {
317b5c5924fSchristos 		/* U2F tokens may return this */
318b5c5924fSchristos 		r = FIDO_OK;
319b5c5924fSchristos 	}
320b5c5924fSchristos  out:
321b5c5924fSchristos 	fido_assert_free(&assert);
322b5c5924fSchristos 
323b5c5924fSchristos 	return r != FIDO_OK ? -1 : 0;
324b5c5924fSchristos }
325b5c5924fSchristos 
32684f89c72Schristos static int
check_sk_options(fido_dev_t * dev,const char * opt,int * ret)32784f89c72Schristos check_sk_options(fido_dev_t *dev, const char *opt, int *ret)
32884f89c72Schristos {
32984f89c72Schristos 	fido_cbor_info_t *info;
33084f89c72Schristos 	char * const *name;
33184f89c72Schristos 	const bool *value;
33284f89c72Schristos 	size_t len, i;
33384f89c72Schristos 	int r;
33484f89c72Schristos 
33584f89c72Schristos 	*ret = -1;
33684f89c72Schristos 
33784f89c72Schristos 	if (!fido_dev_is_fido2(dev)) {
33884f89c72Schristos 		skdebug(__func__, "device is not fido2");
33984f89c72Schristos 		return 0;
34084f89c72Schristos 	}
34184f89c72Schristos 	if ((info = fido_cbor_info_new()) == NULL) {
34284f89c72Schristos 		skdebug(__func__, "fido_cbor_info_new failed");
34384f89c72Schristos 		return -1;
34484f89c72Schristos 	}
34584f89c72Schristos 	if ((r = fido_dev_get_cbor_info(dev, info)) != FIDO_OK) {
34684f89c72Schristos 		skdebug(__func__, "fido_dev_get_cbor_info: %s", fido_strerr(r));
34784f89c72Schristos 		fido_cbor_info_free(&info);
34884f89c72Schristos 		return -1;
34984f89c72Schristos 	}
35084f89c72Schristos 	name = fido_cbor_info_options_name_ptr(info);
35184f89c72Schristos 	value = fido_cbor_info_options_value_ptr(info);
35284f89c72Schristos 	len = fido_cbor_info_options_len(info);
35384f89c72Schristos 	for (i = 0; i < len; i++) {
35484f89c72Schristos 		if (!strcmp(name[i], opt)) {
35584f89c72Schristos 			*ret = value[i];
35684f89c72Schristos 			break;
35784f89c72Schristos 		}
35884f89c72Schristos 	}
35984f89c72Schristos 	fido_cbor_info_free(&info);
36084f89c72Schristos 	if (*ret == -1)
36184f89c72Schristos 		skdebug(__func__, "option %s is unknown", opt);
36284f89c72Schristos 	else
36384f89c72Schristos 		skdebug(__func__, "option %s is %s", opt, *ret ? "on" : "off");
36484f89c72Schristos 
36584f89c72Schristos 	return 0;
36684f89c72Schristos }
36784f89c72Schristos 
3687d35d687Schristos static struct sk_usbhid *
sk_select_by_cred(const fido_dev_info_t * devlist,size_t ndevs,const char * application,const uint8_t * key_handle,size_t key_handle_len)3697d35d687Schristos sk_select_by_cred(const fido_dev_info_t *devlist, size_t ndevs,
370b5c5924fSchristos     const char *application, const uint8_t *key_handle, size_t key_handle_len)
371b5c5924fSchristos {
3727d35d687Schristos 	struct sk_usbhid **skv, *sk;
3737d35d687Schristos 	size_t skvcnt, i;
37484f89c72Schristos 	int internal_uv;
375b5c5924fSchristos 
3767d35d687Schristos 	if ((skv = sk_openv(devlist, ndevs, &skvcnt)) == NULL) {
3777d35d687Schristos 		skdebug(__func__, "sk_openv failed");
378b5c5924fSchristos 		return NULL;
379b5c5924fSchristos 	}
38084f89c72Schristos 	if (skvcnt == 1 && check_sk_options(skv[0]->dev, "uv",
38184f89c72Schristos 	    &internal_uv) == 0 && internal_uv != -1) {
3827d35d687Schristos 		sk = skv[0];
3837d35d687Schristos 		skv[0] = NULL;
3847d35d687Schristos 		goto out;
3857d35d687Schristos 	}
3867d35d687Schristos 	sk = NULL;
3877d35d687Schristos 	for (i = 0; i < skvcnt; i++) {
3887d35d687Schristos 		if (sk_try(skv[i], application, key_handle,
3897d35d687Schristos 		    key_handle_len) == 0) {
3907d35d687Schristos 			sk = skv[i];
3917d35d687Schristos 			skv[i] = NULL;
3927d35d687Schristos 			skdebug(__func__, "found key in %s", sk->path);
3937d35d687Schristos 			break;
3947d35d687Schristos 		}
3957d35d687Schristos 	}
3967d35d687Schristos  out:
3977d35d687Schristos 	sk_closev(skv, skvcnt);
3987d35d687Schristos 	return sk;
3997d35d687Schristos }
4007d35d687Schristos 
4017d35d687Schristos static struct sk_usbhid *
sk_select_by_touch(const fido_dev_info_t * devlist,size_t ndevs)4027d35d687Schristos sk_select_by_touch(const fido_dev_info_t *devlist, size_t ndevs)
4037d35d687Schristos {
4047d35d687Schristos 	struct sk_usbhid **skv, *sk;
4057d35d687Schristos 	struct timeval tv_start, tv_now, tv_delta;
4067d35d687Schristos 	size_t skvcnt, idx;
4077d35d687Schristos 	int touch, ms_remain;
4087d35d687Schristos 
4097d35d687Schristos 	if ((skv = sk_openv(devlist, ndevs, &skvcnt)) == NULL) {
4107d35d687Schristos 		skdebug(__func__, "sk_openv failed");
411b5c5924fSchristos 		return NULL;
412b5c5924fSchristos 	}
4137d35d687Schristos 	sk = NULL;
4147d35d687Schristos 	if (skvcnt < 2) {
4157d35d687Schristos 		if (skvcnt == 1) {
4167d35d687Schristos 			/* single candidate */
4177d35d687Schristos 			sk = skv[0];
4187d35d687Schristos 			skv[0] = NULL;
419b5c5924fSchristos 		}
4207d35d687Schristos 		goto out;
4217d35d687Schristos 	}
4227d35d687Schristos 	if (sk_touch_begin(skv, skvcnt) == -1) {
4237d35d687Schristos 		skdebug(__func__, "sk_touch_begin failed");
4247d35d687Schristos 		goto out;
4257d35d687Schristos 	}
4267d35d687Schristos 	monotime_tv(&tv_start);
4277d35d687Schristos 	do {
4287d35d687Schristos 		if (sk_touch_poll(skv, skvcnt, &touch, &idx) == -1) {
4297d35d687Schristos 			skdebug(__func__, "sk_touch_poll failed");
4307d35d687Schristos 			goto out;
4317d35d687Schristos 		}
4327d35d687Schristos 		if (touch) {
4337d35d687Schristos 			sk = skv[idx];
4347d35d687Schristos 			skv[idx] = NULL;
4357d35d687Schristos 			goto out;
4367d35d687Schristos 		}
4377d35d687Schristos 		monotime_tv(&tv_now);
4387d35d687Schristos 		timersub(&tv_now, &tv_start, &tv_delta);
4397d35d687Schristos 		ms_remain = SELECT_MS - tv_delta.tv_sec * 1000 -
4407d35d687Schristos 		    tv_delta.tv_usec / 1000;
4417d35d687Schristos 	} while (ms_remain >= FIDO_POLL_MS);
4427d35d687Schristos 	skdebug(__func__, "timeout");
4437d35d687Schristos out:
4447d35d687Schristos 	sk_closev(skv, skvcnt);
4457d35d687Schristos 	return sk;
4467d35d687Schristos }
4477d35d687Schristos 
4487d35d687Schristos static struct sk_usbhid *
sk_probe(const char * application,const uint8_t * key_handle,size_t key_handle_len,int probe_resident)4497d35d687Schristos sk_probe(const char *application, const uint8_t *key_handle,
450*a6c5e0d9Schristos     size_t key_handle_len, int probe_resident)
4517d35d687Schristos {
4527d35d687Schristos 	struct sk_usbhid *sk;
4537d35d687Schristos 	fido_dev_info_t *devlist;
4547d35d687Schristos 	size_t ndevs;
4557d35d687Schristos 	int r;
456b5c5924fSchristos 
457b5c5924fSchristos 	if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) {
458b5c5924fSchristos 		skdebug(__func__, "fido_dev_info_new failed");
4597d35d687Schristos 		return NULL;
460b5c5924fSchristos 	}
461b5c5924fSchristos 	if ((r = fido_dev_info_manifest(devlist, MAX_FIDO_DEVICES,
4627d35d687Schristos 	    &ndevs)) != FIDO_OK) {
4637d35d687Schristos 		skdebug(__func__, "fido_dev_info_manifest failed: %s",
4647d35d687Schristos 		    fido_strerr(r));
465b5c5924fSchristos 		fido_dev_info_free(&devlist, MAX_FIDO_DEVICES);
4667d35d687Schristos 		return NULL;
4677d35d687Schristos 	}
4687d35d687Schristos 	skdebug(__func__, "%zu device(s) detected", ndevs);
4697d35d687Schristos 	if (ndevs == 0) {
4707d35d687Schristos 		sk = NULL;
4717d35d687Schristos 	} else if (application != NULL && key_handle != NULL) {
4727d35d687Schristos 		skdebug(__func__, "selecting sk by cred");
4737d35d687Schristos 		sk = sk_select_by_cred(devlist, ndevs, application, key_handle,
4747d35d687Schristos 		    key_handle_len);
4757d35d687Schristos 	} else {
4767d35d687Schristos 		skdebug(__func__, "selecting sk by touch");
4777d35d687Schristos 		sk = sk_select_by_touch(devlist, ndevs);
4787d35d687Schristos 	}
4797d35d687Schristos 	fido_dev_info_free(&devlist, MAX_FIDO_DEVICES);
4807d35d687Schristos 	return sk;
481b5c5924fSchristos }
482b5c5924fSchristos 
483b5c5924fSchristos #ifdef WITH_OPENSSL
484b5c5924fSchristos /*
485b5c5924fSchristos  * The key returned via fido_cred_pubkey_ptr() is in affine coordinates,
486b5c5924fSchristos  * but the API expects a SEC1 octet string.
487b5c5924fSchristos  */
488b5c5924fSchristos static int
pack_public_key_ecdsa(const fido_cred_t * cred,struct sk_enroll_response * response)489b5c5924fSchristos pack_public_key_ecdsa(const fido_cred_t *cred,
490b5c5924fSchristos     struct sk_enroll_response *response)
491b5c5924fSchristos {
492b5c5924fSchristos 	const uint8_t *ptr;
493b5c5924fSchristos 	BIGNUM *x = NULL, *y = NULL;
494b5c5924fSchristos 	EC_POINT *q = NULL;
495b5c5924fSchristos 	EC_GROUP *g = NULL;
496b5c5924fSchristos 	int ret = -1;
497b5c5924fSchristos 
498b5c5924fSchristos 	response->public_key = NULL;
499b5c5924fSchristos 	response->public_key_len = 0;
500b5c5924fSchristos 
501b5c5924fSchristos 	if ((x = BN_new()) == NULL ||
502b5c5924fSchristos 	    (y = BN_new()) == NULL ||
503b5c5924fSchristos 	    (g = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)) == NULL ||
504b5c5924fSchristos 	    (q = EC_POINT_new(g)) == NULL) {
505b5c5924fSchristos 		skdebug(__func__, "libcrypto setup failed");
506b5c5924fSchristos 		goto out;
507b5c5924fSchristos 	}
508b5c5924fSchristos 	if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) {
509b5c5924fSchristos 		skdebug(__func__, "fido_cred_pubkey_ptr failed");
510b5c5924fSchristos 		goto out;
511b5c5924fSchristos 	}
512b5c5924fSchristos 	if (fido_cred_pubkey_len(cred) != 64) {
513b5c5924fSchristos 		skdebug(__func__, "bad fido_cred_pubkey_len %zu",
514b5c5924fSchristos 		    fido_cred_pubkey_len(cred));
515b5c5924fSchristos 		goto out;
516b5c5924fSchristos 	}
517b5c5924fSchristos 
518b5c5924fSchristos 	if (BN_bin2bn(ptr, 32, x) == NULL ||
519b5c5924fSchristos 	    BN_bin2bn(ptr + 32, 32, y) == NULL) {
520b5c5924fSchristos 		skdebug(__func__, "BN_bin2bn failed");
521b5c5924fSchristos 		goto out;
522b5c5924fSchristos 	}
523b5c5924fSchristos 	if (EC_POINT_set_affine_coordinates_GFp(g, q, x, y, NULL) != 1) {
524b5c5924fSchristos 		skdebug(__func__, "EC_POINT_set_affine_coordinates_GFp failed");
525b5c5924fSchristos 		goto out;
526b5c5924fSchristos 	}
527b5c5924fSchristos 	response->public_key_len = EC_POINT_point2oct(g, q,
528b5c5924fSchristos 	    POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL);
529b5c5924fSchristos 	if (response->public_key_len == 0 || response->public_key_len > 2048) {
530b5c5924fSchristos 		skdebug(__func__, "bad pubkey length %zu",
531b5c5924fSchristos 		    response->public_key_len);
532b5c5924fSchristos 		goto out;
533b5c5924fSchristos 	}
534b5c5924fSchristos 	if ((response->public_key = malloc(response->public_key_len)) == NULL) {
535b5c5924fSchristos 		skdebug(__func__, "malloc pubkey failed");
536b5c5924fSchristos 		goto out;
537b5c5924fSchristos 	}
538b5c5924fSchristos 	if (EC_POINT_point2oct(g, q, POINT_CONVERSION_UNCOMPRESSED,
539b5c5924fSchristos 	    response->public_key, response->public_key_len, NULL) == 0) {
540b5c5924fSchristos 		skdebug(__func__, "EC_POINT_point2oct failed");
541b5c5924fSchristos 		goto out;
542b5c5924fSchristos 	}
543b5c5924fSchristos 	/* success */
544b5c5924fSchristos 	ret = 0;
545b5c5924fSchristos  out:
546b5c5924fSchristos 	if (ret != 0 && response->public_key != NULL) {
547b5c5924fSchristos 		memset(response->public_key, 0, response->public_key_len);
548b5c5924fSchristos 		free(response->public_key);
549b5c5924fSchristos 		response->public_key = NULL;
550b5c5924fSchristos 	}
551b5c5924fSchristos 	EC_POINT_free(q);
552b5c5924fSchristos 	EC_GROUP_free(g);
553b5c5924fSchristos 	BN_clear_free(x);
554b5c5924fSchristos 	BN_clear_free(y);
555b5c5924fSchristos 	return ret;
556b5c5924fSchristos }
557b5c5924fSchristos #endif /* WITH_OPENSSL */
558b5c5924fSchristos 
559b5c5924fSchristos static int
pack_public_key_ed25519(const fido_cred_t * cred,struct sk_enroll_response * response)560b5c5924fSchristos pack_public_key_ed25519(const fido_cred_t *cred,
561b5c5924fSchristos     struct sk_enroll_response *response)
562b5c5924fSchristos {
563b5c5924fSchristos 	const uint8_t *ptr;
564b5c5924fSchristos 	size_t len;
565b5c5924fSchristos 	int ret = -1;
566b5c5924fSchristos 
567b5c5924fSchristos 	response->public_key = NULL;
568b5c5924fSchristos 	response->public_key_len = 0;
569b5c5924fSchristos 
570b5c5924fSchristos 	if ((len = fido_cred_pubkey_len(cred)) != 32) {
571b5c5924fSchristos 		skdebug(__func__, "bad fido_cred_pubkey_len len %zu", len);
572b5c5924fSchristos 		goto out;
573b5c5924fSchristos 	}
574b5c5924fSchristos 	if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) {
575b5c5924fSchristos 		skdebug(__func__, "fido_cred_pubkey_ptr failed");
576b5c5924fSchristos 		goto out;
577b5c5924fSchristos 	}
578b5c5924fSchristos 	response->public_key_len = len;
579b5c5924fSchristos 	if ((response->public_key = malloc(response->public_key_len)) == NULL) {
580b5c5924fSchristos 		skdebug(__func__, "malloc pubkey failed");
581b5c5924fSchristos 		goto out;
582b5c5924fSchristos 	}
583b5c5924fSchristos 	memcpy(response->public_key, ptr, len);
584b5c5924fSchristos 	ret = 0;
585b5c5924fSchristos  out:
586b5c5924fSchristos 	if (ret != 0)
587b5c5924fSchristos 		free(response->public_key);
588b5c5924fSchristos 	return ret;
589b5c5924fSchristos }
590b5c5924fSchristos 
591b5c5924fSchristos static int
pack_public_key(uint32_t alg,const fido_cred_t * cred,struct sk_enroll_response * response)592b5c5924fSchristos pack_public_key(uint32_t alg, const fido_cred_t *cred,
593b5c5924fSchristos     struct sk_enroll_response *response)
594b5c5924fSchristos {
595b5c5924fSchristos 	switch(alg) {
596b5c5924fSchristos #ifdef WITH_OPENSSL
597b5c5924fSchristos 	case SSH_SK_ECDSA:
598b5c5924fSchristos 		return pack_public_key_ecdsa(cred, response);
599b5c5924fSchristos #endif /* WITH_OPENSSL */
600b5c5924fSchristos 	case SSH_SK_ED25519:
601b5c5924fSchristos 		return pack_public_key_ed25519(cred, response);
602b5c5924fSchristos 	default:
603b5c5924fSchristos 		return -1;
604b5c5924fSchristos 	}
605b5c5924fSchristos }
606b5c5924fSchristos 
607b5c5924fSchristos static int
fidoerr_to_skerr(int fidoerr)608b5c5924fSchristos fidoerr_to_skerr(int fidoerr)
609b5c5924fSchristos {
610b5c5924fSchristos 	switch (fidoerr) {
611b5c5924fSchristos 	case FIDO_ERR_UNSUPPORTED_OPTION:
612b5c5924fSchristos 	case FIDO_ERR_UNSUPPORTED_ALGORITHM:
613b5c5924fSchristos 		return SSH_SK_ERR_UNSUPPORTED;
614b5c5924fSchristos 	case FIDO_ERR_PIN_REQUIRED:
615b5c5924fSchristos 	case FIDO_ERR_PIN_INVALID:
61684f89c72Schristos 	case FIDO_ERR_OPERATION_DENIED:
617b5c5924fSchristos 		return SSH_SK_ERR_PIN_REQUIRED;
618b5c5924fSchristos 	default:
619b5c5924fSchristos 		return -1;
620b5c5924fSchristos 	}
621b5c5924fSchristos }
622b5c5924fSchristos 
623b5c5924fSchristos static int
check_enroll_options(struct sk_option ** options,char ** devicep,uint8_t * user_id,size_t user_id_len)624b5c5924fSchristos check_enroll_options(struct sk_option **options, char **devicep,
625b5c5924fSchristos     uint8_t *user_id, size_t user_id_len)
626b5c5924fSchristos {
627b5c5924fSchristos 	size_t i;
628b5c5924fSchristos 
629b5c5924fSchristos 	if (options == NULL)
630b5c5924fSchristos 		return 0;
631b5c5924fSchristos 	for (i = 0; options[i] != NULL; i++) {
632b5c5924fSchristos 		if (strcmp(options[i]->name, "device") == 0) {
633b5c5924fSchristos 			if ((*devicep = strdup(options[i]->value)) == NULL) {
634b5c5924fSchristos 				skdebug(__func__, "strdup device failed");
635b5c5924fSchristos 				return -1;
636b5c5924fSchristos 			}
637b5c5924fSchristos 			skdebug(__func__, "requested device %s", *devicep);
638b5c5924fSchristos 		} else if (strcmp(options[i]->name, "user") == 0) {
639c8864e3fSchristos 			if (strlcpy((char *)user_id, options[i]->value, user_id_len) >=
640b5c5924fSchristos 			    user_id_len) {
641b5c5924fSchristos 				skdebug(__func__, "user too long");
642b5c5924fSchristos 				return -1;
643b5c5924fSchristos 			}
644b5c5924fSchristos 			skdebug(__func__, "requested user %s",
645b5c5924fSchristos 			    (char *)user_id);
646b5c5924fSchristos 		} else {
647b5c5924fSchristos 			skdebug(__func__, "requested unsupported option %s",
648b5c5924fSchristos 			    options[i]->name);
649b5c5924fSchristos 			if (options[i]->required) {
650b5c5924fSchristos 				skdebug(__func__, "unknown required option");
651b5c5924fSchristos 				return -1;
652b5c5924fSchristos 			}
653b5c5924fSchristos 		}
654b5c5924fSchristos 	}
655b5c5924fSchristos 	return 0;
656b5c5924fSchristos }
657b5c5924fSchristos 
658*a6c5e0d9Schristos static int
key_lookup(fido_dev_t * dev,const char * application,const uint8_t * user_id,size_t user_id_len,const char * pin)659*a6c5e0d9Schristos key_lookup(fido_dev_t *dev, const char *application, const uint8_t *user_id,
660*a6c5e0d9Schristos     size_t user_id_len, const char *pin)
661*a6c5e0d9Schristos {
662*a6c5e0d9Schristos 	fido_assert_t *assert = NULL;
663*a6c5e0d9Schristos 	uint8_t message[32];
664*a6c5e0d9Schristos 	int r = FIDO_ERR_INTERNAL;
665*a6c5e0d9Schristos 	int sk_supports_uv, uv;
666*a6c5e0d9Schristos 	size_t i;
667*a6c5e0d9Schristos 
668*a6c5e0d9Schristos 	memset(message, '\0', sizeof(message));
669*a6c5e0d9Schristos 	if ((assert = fido_assert_new()) == NULL) {
670*a6c5e0d9Schristos 		skdebug(__func__, "fido_assert_new failed");
671*a6c5e0d9Schristos 		goto out;
672*a6c5e0d9Schristos 	}
673*a6c5e0d9Schristos 	/* generate an invalid signature on FIDO2 tokens */
674*a6c5e0d9Schristos 	if ((r = fido_assert_set_clientdata(assert, message,
675*a6c5e0d9Schristos 	    sizeof(message))) != FIDO_OK) {
676*a6c5e0d9Schristos 		skdebug(__func__, "fido_assert_set_clientdata: %s",
677*a6c5e0d9Schristos 		    fido_strerr(r));
678*a6c5e0d9Schristos 		goto out;
679*a6c5e0d9Schristos 	}
680*a6c5e0d9Schristos 	if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) {
681*a6c5e0d9Schristos 		skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r));
682*a6c5e0d9Schristos 		goto out;
683*a6c5e0d9Schristos 	}
684*a6c5e0d9Schristos 	if ((r = fido_assert_set_up(assert, FIDO_OPT_FALSE)) != FIDO_OK) {
685*a6c5e0d9Schristos 		skdebug(__func__, "fido_assert_set_up: %s", fido_strerr(r));
686*a6c5e0d9Schristos 		goto out;
687*a6c5e0d9Schristos 	}
688*a6c5e0d9Schristos 	uv = FIDO_OPT_OMIT;
689*a6c5e0d9Schristos 	if (pin == NULL && check_sk_options(dev, "uv", &sk_supports_uv) == 0 &&
690*a6c5e0d9Schristos 	    sk_supports_uv != -1)
691*a6c5e0d9Schristos 		uv = FIDO_OPT_TRUE;
692*a6c5e0d9Schristos 	if ((r = fido_assert_set_uv(assert, uv)) != FIDO_OK) {
693*a6c5e0d9Schristos 		skdebug(__func__, "fido_assert_set_uv: %s", fido_strerr(r));
694*a6c5e0d9Schristos 		goto out;
695*a6c5e0d9Schristos 	}
696*a6c5e0d9Schristos 	if ((r = fido_dev_get_assert(dev, assert, pin)) != FIDO_OK) {
697*a6c5e0d9Schristos 		skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r));
698*a6c5e0d9Schristos 		goto out;
699*a6c5e0d9Schristos 	}
700*a6c5e0d9Schristos 	r = FIDO_ERR_NO_CREDENTIALS;
701*a6c5e0d9Schristos 	skdebug(__func__, "%zu signatures returned", fido_assert_count(assert));
702*a6c5e0d9Schristos 	for (i = 0; i < fido_assert_count(assert); i++) {
703*a6c5e0d9Schristos 		if (fido_assert_user_id_len(assert, i) == user_id_len &&
704*a6c5e0d9Schristos 		    memcmp(fido_assert_user_id_ptr(assert, i), user_id,
705*a6c5e0d9Schristos 		    user_id_len) == 0) {
706*a6c5e0d9Schristos 			skdebug(__func__, "credential exists");
707*a6c5e0d9Schristos 			r = FIDO_OK;
708*a6c5e0d9Schristos 			goto out;
709*a6c5e0d9Schristos 		}
710*a6c5e0d9Schristos 	}
711*a6c5e0d9Schristos  out:
712*a6c5e0d9Schristos 	fido_assert_free(&assert);
713*a6c5e0d9Schristos 
714*a6c5e0d9Schristos 	return r;
715*a6c5e0d9Schristos }
716*a6c5e0d9Schristos 
717b5c5924fSchristos int
sk_enroll(uint32_t alg,const uint8_t * challenge,size_t challenge_len,const char * application,uint8_t flags,const char * pin,struct sk_option ** options,struct sk_enroll_response ** enroll_response)718b5c5924fSchristos sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len,
719b5c5924fSchristos     const char *application, uint8_t flags, const char *pin,
720b5c5924fSchristos     struct sk_option **options, struct sk_enroll_response **enroll_response)
721b5c5924fSchristos {
722b5c5924fSchristos 	fido_cred_t *cred = NULL;
723b5c5924fSchristos 	const uint8_t *ptr;
72484f89c72Schristos 	uint8_t user_id[32];
7257d35d687Schristos 	struct sk_usbhid *sk = NULL;
726b5c5924fSchristos 	struct sk_enroll_response *response = NULL;
727b5c5924fSchristos 	size_t len;
7287d35d687Schristos 	int credprot;
729b5c5924fSchristos 	int cose_alg;
730b5c5924fSchristos 	int ret = SSH_SK_ERR_GENERAL;
731b5c5924fSchristos 	int r;
732b5c5924fSchristos 	char *device = NULL;
733b5c5924fSchristos 
7347d35d687Schristos 	fido_init(SSH_FIDO_INIT_ARG);
7357d35d687Schristos 
736b5c5924fSchristos 	if (enroll_response == NULL) {
737b5c5924fSchristos 		skdebug(__func__, "enroll_response == NULL");
738b5c5924fSchristos 		goto out;
739b5c5924fSchristos 	}
7407d35d687Schristos 	*enroll_response = NULL;
741b5c5924fSchristos 	memset(user_id, 0, sizeof(user_id));
7427d35d687Schristos 	if (check_enroll_options(options, &device, user_id,
7437d35d687Schristos 	    sizeof(user_id)) != 0)
744b5c5924fSchristos 		goto out; /* error already logged */
745b5c5924fSchristos 
746b5c5924fSchristos 	switch(alg) {
747b5c5924fSchristos #ifdef WITH_OPENSSL
748b5c5924fSchristos 	case SSH_SK_ECDSA:
749b5c5924fSchristos 		cose_alg = COSE_ES256;
750b5c5924fSchristos 		break;
751b5c5924fSchristos #endif /* WITH_OPENSSL */
752b5c5924fSchristos 	case SSH_SK_ED25519:
753b5c5924fSchristos 		cose_alg = COSE_EDDSA;
754b5c5924fSchristos 		break;
755b5c5924fSchristos 	default:
756b5c5924fSchristos 		skdebug(__func__, "unsupported key type %d", alg);
757b5c5924fSchristos 		goto out;
758b5c5924fSchristos 	}
7597d35d687Schristos 	if (device != NULL)
7607d35d687Schristos 		sk = sk_open(device);
7617d35d687Schristos 	else
762*a6c5e0d9Schristos 		sk = sk_probe(NULL, NULL, 0, 0);
7637d35d687Schristos 	if (sk == NULL) {
76484f89c72Schristos 		ret = SSH_SK_ERR_DEVICE_NOT_FOUND;
7657d35d687Schristos 		skdebug(__func__, "failed to find sk");
766b5c5924fSchristos 		goto out;
767b5c5924fSchristos 	}
7687d35d687Schristos 	skdebug(__func__, "using device %s", sk->path);
769*a6c5e0d9Schristos 	if ((flags & SSH_SK_RESIDENT_KEY) != 0 &&
770*a6c5e0d9Schristos 	    (flags & SSH_SK_FORCE_OPERATION) == 0 &&
771*a6c5e0d9Schristos 	    (r = key_lookup(sk->dev, application, user_id, sizeof(user_id),
772*a6c5e0d9Schristos 	    pin)) != FIDO_ERR_NO_CREDENTIALS) {
773*a6c5e0d9Schristos 		if (r != FIDO_OK) {
774*a6c5e0d9Schristos 			ret = fidoerr_to_skerr(r);
775*a6c5e0d9Schristos 			skdebug(__func__, "key_lookup failed");
776*a6c5e0d9Schristos 		} else {
777*a6c5e0d9Schristos 			ret = SSH_SK_ERR_CREDENTIAL_EXISTS;
778*a6c5e0d9Schristos 			skdebug(__func__, "key exists");
779*a6c5e0d9Schristos 		}
780*a6c5e0d9Schristos 		goto out;
781*a6c5e0d9Schristos 	}
782b5c5924fSchristos 	if ((cred = fido_cred_new()) == NULL) {
783b5c5924fSchristos 		skdebug(__func__, "fido_cred_new failed");
784b5c5924fSchristos 		goto out;
785b5c5924fSchristos 	}
786b5c5924fSchristos 	if ((r = fido_cred_set_type(cred, cose_alg)) != FIDO_OK) {
787b5c5924fSchristos 		skdebug(__func__, "fido_cred_set_type: %s", fido_strerr(r));
788b5c5924fSchristos 		goto out;
789b5c5924fSchristos 	}
79084f89c72Schristos 	if ((r = fido_cred_set_clientdata(cred,
79184f89c72Schristos 	    challenge, challenge_len)) != FIDO_OK) {
79284f89c72Schristos 		skdebug(__func__, "fido_cred_set_clientdata: %s",
793b5c5924fSchristos 		    fido_strerr(r));
794b5c5924fSchristos 		goto out;
795b5c5924fSchristos 	}
796b5c5924fSchristos 	if ((r = fido_cred_set_rk(cred, (flags & SSH_SK_RESIDENT_KEY) != 0 ?
797b5c5924fSchristos 	    FIDO_OPT_TRUE : FIDO_OPT_OMIT)) != FIDO_OK) {
798b5c5924fSchristos 		skdebug(__func__, "fido_cred_set_rk: %s", fido_strerr(r));
799b5c5924fSchristos 		goto out;
800b5c5924fSchristos 	}
801b5c5924fSchristos 	if ((r = fido_cred_set_user(cred, user_id, sizeof(user_id),
802b5c5924fSchristos 	    "openssh", "openssh", NULL)) != FIDO_OK) {
803b5c5924fSchristos 		skdebug(__func__, "fido_cred_set_user: %s", fido_strerr(r));
804b5c5924fSchristos 		goto out;
805b5c5924fSchristos 	}
806b5c5924fSchristos 	if ((r = fido_cred_set_rp(cred, application, NULL)) != FIDO_OK) {
807b5c5924fSchristos 		skdebug(__func__, "fido_cred_set_rp: %s", fido_strerr(r));
808b5c5924fSchristos 		goto out;
809b5c5924fSchristos 	}
8107d35d687Schristos 	if ((flags & (SSH_SK_RESIDENT_KEY|SSH_SK_USER_VERIFICATION_REQD)) != 0) {
8117d35d687Schristos 		if (!fido_dev_supports_cred_prot(sk->dev)) {
8127d35d687Schristos 			skdebug(__func__, "%s does not support credprot, "
8137d35d687Schristos 			    "refusing to create unprotected "
8147d35d687Schristos 			    "resident/verify-required key", sk->path);
8157d35d687Schristos 			ret = SSH_SK_ERR_UNSUPPORTED;
816b5c5924fSchristos 			goto out;
817b5c5924fSchristos 		}
8187d35d687Schristos 		if ((flags & SSH_SK_USER_VERIFICATION_REQD))
8197d35d687Schristos 			credprot = FIDO_CRED_PROT_UV_REQUIRED;
8207d35d687Schristos 		else
8217d35d687Schristos 			credprot = FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID;
8227d35d687Schristos 
8237d35d687Schristos 		if ((r = fido_cred_set_prot(cred, credprot)) != FIDO_OK) {
8247d35d687Schristos 			skdebug(__func__, "fido_cred_set_prot: %s",
8257d35d687Schristos 			    fido_strerr(r));
8267d35d687Schristos 			ret = fidoerr_to_skerr(r);
827b5c5924fSchristos 			goto out;
828b5c5924fSchristos 		}
8297d35d687Schristos 	}
8307d35d687Schristos 	if ((r = fido_dev_make_cred(sk->dev, cred, pin)) != FIDO_OK) {
831b5c5924fSchristos 		skdebug(__func__, "fido_dev_make_cred: %s", fido_strerr(r));
832b5c5924fSchristos 		ret = fidoerr_to_skerr(r);
833b5c5924fSchristos 		goto out;
834b5c5924fSchristos 	}
835b5c5924fSchristos 	if (fido_cred_x5c_ptr(cred) != NULL) {
836b5c5924fSchristos 		if ((r = fido_cred_verify(cred)) != FIDO_OK) {
837b5c5924fSchristos 			skdebug(__func__, "fido_cred_verify: %s",
838b5c5924fSchristos 			    fido_strerr(r));
839b5c5924fSchristos 			goto out;
840b5c5924fSchristos 		}
841b5c5924fSchristos 	} else {
842b5c5924fSchristos 		skdebug(__func__, "self-attested credential");
843b5c5924fSchristos 		if ((r = fido_cred_verify_self(cred)) != FIDO_OK) {
844b5c5924fSchristos 			skdebug(__func__, "fido_cred_verify_self: %s",
845b5c5924fSchristos 			    fido_strerr(r));
846b5c5924fSchristos 			goto out;
847b5c5924fSchristos 		}
848b5c5924fSchristos 	}
849b5c5924fSchristos 	if ((response = calloc(1, sizeof(*response))) == NULL) {
850b5c5924fSchristos 		skdebug(__func__, "calloc response failed");
851b5c5924fSchristos 		goto out;
852b5c5924fSchristos 	}
85384f89c72Schristos 	response->flags = flags;
854b5c5924fSchristos 	if (pack_public_key(alg, cred, response) != 0) {
855b5c5924fSchristos 		skdebug(__func__, "pack_public_key failed");
856b5c5924fSchristos 		goto out;
857b5c5924fSchristos 	}
858b5c5924fSchristos 	if ((ptr = fido_cred_id_ptr(cred)) != NULL) {
859b5c5924fSchristos 		len = fido_cred_id_len(cred);
860b5c5924fSchristos 		if ((response->key_handle = calloc(1, len)) == NULL) {
861b5c5924fSchristos 			skdebug(__func__, "calloc key handle failed");
862b5c5924fSchristos 			goto out;
863b5c5924fSchristos 		}
864b5c5924fSchristos 		memcpy(response->key_handle, ptr, len);
865b5c5924fSchristos 		response->key_handle_len = len;
866b5c5924fSchristos 	}
867b5c5924fSchristos 	if ((ptr = fido_cred_sig_ptr(cred)) != NULL) {
868b5c5924fSchristos 		len = fido_cred_sig_len(cred);
869b5c5924fSchristos 		if ((response->signature = calloc(1, len)) == NULL) {
870b5c5924fSchristos 			skdebug(__func__, "calloc signature failed");
871b5c5924fSchristos 			goto out;
872b5c5924fSchristos 		}
873b5c5924fSchristos 		memcpy(response->signature, ptr, len);
874b5c5924fSchristos 		response->signature_len = len;
875b5c5924fSchristos 	}
876b5c5924fSchristos 	if ((ptr = fido_cred_x5c_ptr(cred)) != NULL) {
877b5c5924fSchristos 		len = fido_cred_x5c_len(cred);
878c305e046Schristos 		skdebug(__func__, "attestation cert len=%zu", len);
879b5c5924fSchristos 		if ((response->attestation_cert = calloc(1, len)) == NULL) {
880b5c5924fSchristos 			skdebug(__func__, "calloc attestation cert failed");
881b5c5924fSchristos 			goto out;
882b5c5924fSchristos 		}
883b5c5924fSchristos 		memcpy(response->attestation_cert, ptr, len);
884b5c5924fSchristos 		response->attestation_cert_len = len;
885b5c5924fSchristos 	}
8867d35d687Schristos 	if ((ptr = fido_cred_authdata_ptr(cred)) != NULL) {
8877d35d687Schristos 		len = fido_cred_authdata_len(cred);
888c305e046Schristos 		skdebug(__func__, "authdata len=%zu", len);
8897d35d687Schristos 		if ((response->authdata = calloc(1, len)) == NULL) {
8907d35d687Schristos 			skdebug(__func__, "calloc authdata failed");
8917d35d687Schristos 			goto out;
8927d35d687Schristos 		}
8937d35d687Schristos 		memcpy(response->authdata, ptr, len);
8947d35d687Schristos 		response->authdata_len = len;
8957d35d687Schristos 	}
896b5c5924fSchristos 	*enroll_response = response;
897b5c5924fSchristos 	response = NULL;
898b5c5924fSchristos 	ret = 0;
899b5c5924fSchristos  out:
900b5c5924fSchristos 	free(device);
901b5c5924fSchristos 	if (response != NULL) {
902b5c5924fSchristos 		free(response->public_key);
903b5c5924fSchristos 		free(response->key_handle);
904b5c5924fSchristos 		free(response->signature);
905b5c5924fSchristos 		free(response->attestation_cert);
9067d35d687Schristos 		free(response->authdata);
907b5c5924fSchristos 		free(response);
908b5c5924fSchristos 	}
9097d35d687Schristos 	sk_close(sk);
910b5c5924fSchristos 	fido_cred_free(&cred);
911b5c5924fSchristos 	return ret;
912b5c5924fSchristos }
913b5c5924fSchristos 
914b5c5924fSchristos #ifdef WITH_OPENSSL
915b5c5924fSchristos static int
pack_sig_ecdsa(fido_assert_t * assert,struct sk_sign_response * response)916b5c5924fSchristos pack_sig_ecdsa(fido_assert_t *assert, struct sk_sign_response *response)
917b5c5924fSchristos {
918b5c5924fSchristos 	ECDSA_SIG *sig = NULL;
919b5c5924fSchristos 	const BIGNUM *sig_r, *sig_s;
920b5c5924fSchristos 	const unsigned char *cp;
921b5c5924fSchristos 	size_t sig_len;
922b5c5924fSchristos 	int ret = -1;
923b5c5924fSchristos 
924b5c5924fSchristos 	cp = fido_assert_sig_ptr(assert, 0);
925b5c5924fSchristos 	sig_len = fido_assert_sig_len(assert, 0);
926b5c5924fSchristos 	if ((sig = d2i_ECDSA_SIG(NULL, &cp, sig_len)) == NULL) {
927b5c5924fSchristos 		skdebug(__func__, "d2i_ECDSA_SIG failed");
928b5c5924fSchristos 		goto out;
929b5c5924fSchristos 	}
930b5c5924fSchristos 	ECDSA_SIG_get0(sig, &sig_r, &sig_s);
931b5c5924fSchristos 	response->sig_r_len = BN_num_bytes(sig_r);
932b5c5924fSchristos 	response->sig_s_len = BN_num_bytes(sig_s);
933b5c5924fSchristos 	if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL ||
934b5c5924fSchristos 	    (response->sig_s = calloc(1, response->sig_s_len)) == NULL) {
935b5c5924fSchristos 		skdebug(__func__, "calloc signature failed");
936b5c5924fSchristos 		goto out;
937b5c5924fSchristos 	}
938b5c5924fSchristos 	BN_bn2bin(sig_r, response->sig_r);
939b5c5924fSchristos 	BN_bn2bin(sig_s, response->sig_s);
940b5c5924fSchristos 	ret = 0;
941b5c5924fSchristos  out:
942b5c5924fSchristos 	ECDSA_SIG_free(sig);
943b5c5924fSchristos 	if (ret != 0) {
944b5c5924fSchristos 		free(response->sig_r);
945b5c5924fSchristos 		free(response->sig_s);
946b5c5924fSchristos 		response->sig_r = NULL;
947b5c5924fSchristos 		response->sig_s = NULL;
948b5c5924fSchristos 	}
949b5c5924fSchristos 	return ret;
950b5c5924fSchristos }
951b5c5924fSchristos #endif /* WITH_OPENSSL */
952b5c5924fSchristos 
953b5c5924fSchristos static int
pack_sig_ed25519(fido_assert_t * assert,struct sk_sign_response * response)954b5c5924fSchristos pack_sig_ed25519(fido_assert_t *assert, struct sk_sign_response *response)
955b5c5924fSchristos {
956b5c5924fSchristos 	const unsigned char *ptr;
957b5c5924fSchristos 	size_t len;
958b5c5924fSchristos 	int ret = -1;
959b5c5924fSchristos 
960b5c5924fSchristos 	ptr = fido_assert_sig_ptr(assert, 0);
961b5c5924fSchristos 	len = fido_assert_sig_len(assert, 0);
962b5c5924fSchristos 	if (len != 64) {
963b5c5924fSchristos 		skdebug(__func__, "bad length %zu", len);
964b5c5924fSchristos 		goto out;
965b5c5924fSchristos 	}
966b5c5924fSchristos 	response->sig_r_len = len;
967b5c5924fSchristos 	if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL) {
968b5c5924fSchristos 		skdebug(__func__, "calloc signature failed");
969b5c5924fSchristos 		goto out;
970b5c5924fSchristos 	}
971b5c5924fSchristos 	memcpy(response->sig_r, ptr, len);
972b5c5924fSchristos 	ret = 0;
973b5c5924fSchristos  out:
974b5c5924fSchristos 	if (ret != 0) {
975b5c5924fSchristos 		free(response->sig_r);
976b5c5924fSchristos 		response->sig_r = NULL;
977b5c5924fSchristos 	}
978b5c5924fSchristos 	return ret;
979b5c5924fSchristos }
980b5c5924fSchristos 
981b5c5924fSchristos static int
pack_sig(uint32_t alg,fido_assert_t * assert,struct sk_sign_response * response)982b5c5924fSchristos pack_sig(uint32_t  alg, fido_assert_t *assert,
983b5c5924fSchristos     struct sk_sign_response *response)
984b5c5924fSchristos {
985b5c5924fSchristos 	switch(alg) {
986b5c5924fSchristos #ifdef WITH_OPENSSL
987b5c5924fSchristos 	case SSH_SK_ECDSA:
988b5c5924fSchristos 		return pack_sig_ecdsa(assert, response);
989b5c5924fSchristos #endif /* WITH_OPENSSL */
990b5c5924fSchristos 	case SSH_SK_ED25519:
991b5c5924fSchristos 		return pack_sig_ed25519(assert, response);
992b5c5924fSchristos 	default:
993b5c5924fSchristos 		return -1;
994b5c5924fSchristos 	}
995b5c5924fSchristos }
996b5c5924fSchristos 
997b5c5924fSchristos /* Checks sk_options for sk_sign() and sk_load_resident_keys() */
998b5c5924fSchristos static int
check_sign_load_resident_options(struct sk_option ** options,char ** devicep)999b5c5924fSchristos check_sign_load_resident_options(struct sk_option **options, char **devicep)
1000b5c5924fSchristos {
1001b5c5924fSchristos 	size_t i;
1002b5c5924fSchristos 
1003b5c5924fSchristos 	if (options == NULL)
1004b5c5924fSchristos 		return 0;
1005b5c5924fSchristos 	for (i = 0; options[i] != NULL; i++) {
1006b5c5924fSchristos 		if (strcmp(options[i]->name, "device") == 0) {
1007b5c5924fSchristos 			if ((*devicep = strdup(options[i]->value)) == NULL) {
1008b5c5924fSchristos 				skdebug(__func__, "strdup device failed");
1009b5c5924fSchristos 				return -1;
1010b5c5924fSchristos 			}
1011b5c5924fSchristos 			skdebug(__func__, "requested device %s", *devicep);
1012b5c5924fSchristos 		} else {
1013b5c5924fSchristos 			skdebug(__func__, "requested unsupported option %s",
1014b5c5924fSchristos 			    options[i]->name);
1015b5c5924fSchristos 			if (options[i]->required) {
1016b5c5924fSchristos 				skdebug(__func__, "unknown required option");
1017b5c5924fSchristos 				return -1;
1018b5c5924fSchristos 			}
1019b5c5924fSchristos 		}
1020b5c5924fSchristos 	}
1021b5c5924fSchristos 	return 0;
1022b5c5924fSchristos }
1023b5c5924fSchristos 
1024b5c5924fSchristos int
sk_sign(uint32_t alg,const uint8_t * data,size_t datalen,const char * application,const uint8_t * key_handle,size_t key_handle_len,uint8_t flags,const char * pin,struct sk_option ** options,struct sk_sign_response ** sign_response)1025e29d081dSchristos sk_sign(uint32_t alg, const uint8_t *data, size_t datalen,
1026b5c5924fSchristos     const char *application,
1027b5c5924fSchristos     const uint8_t *key_handle, size_t key_handle_len,
1028b5c5924fSchristos     uint8_t flags, const char *pin, struct sk_option **options,
1029b5c5924fSchristos     struct sk_sign_response **sign_response)
1030b5c5924fSchristos {
1031b5c5924fSchristos 	fido_assert_t *assert = NULL;
1032b5c5924fSchristos 	char *device = NULL;
10337d35d687Schristos 	struct sk_usbhid *sk = NULL;
1034b5c5924fSchristos 	struct sk_sign_response *response = NULL;
103584f89c72Schristos 	int ret = SSH_SK_ERR_GENERAL, internal_uv;
1036b5c5924fSchristos 	int r;
1037b5c5924fSchristos 
10387d35d687Schristos 	fido_init(SSH_FIDO_INIT_ARG);
1039b5c5924fSchristos 
1040b5c5924fSchristos 	if (sign_response == NULL) {
1041b5c5924fSchristos 		skdebug(__func__, "sign_response == NULL");
1042b5c5924fSchristos 		goto out;
1043b5c5924fSchristos 	}
1044b5c5924fSchristos 	*sign_response = NULL;
1045b5c5924fSchristos 	if (check_sign_load_resident_options(options, &device) != 0)
1046b5c5924fSchristos 		goto out; /* error already logged */
10477d35d687Schristos 	if (device != NULL)
10487d35d687Schristos 		sk = sk_open(device);
10497d35d687Schristos 	else if (pin != NULL || (flags & SSH_SK_USER_VERIFICATION_REQD))
1050*a6c5e0d9Schristos 		sk = sk_probe(NULL, NULL, 0, 0);
10517d35d687Schristos 	else
1052*a6c5e0d9Schristos 		sk = sk_probe(application, key_handle, key_handle_len, 0);
10537d35d687Schristos 	if (sk == NULL) {
105484f89c72Schristos 		ret = SSH_SK_ERR_DEVICE_NOT_FOUND;
10557d35d687Schristos 		skdebug(__func__, "failed to find sk");
1056b5c5924fSchristos 		goto out;
1057b5c5924fSchristos 	}
1058b5c5924fSchristos 	if ((assert = fido_assert_new()) == NULL) {
1059b5c5924fSchristos 		skdebug(__func__, "fido_assert_new failed");
1060b5c5924fSchristos 		goto out;
1061b5c5924fSchristos 	}
106284f89c72Schristos 	if ((r = fido_assert_set_clientdata(assert,
106384f89c72Schristos 	    data, datalen)) != FIDO_OK)  {
106484f89c72Schristos 		skdebug(__func__, "fido_assert_set_clientdata: %s",
1065b5c5924fSchristos 		    fido_strerr(r));
1066b5c5924fSchristos 		goto out;
1067b5c5924fSchristos 	}
1068b5c5924fSchristos 	if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) {
1069b5c5924fSchristos 		skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r));
1070b5c5924fSchristos 		goto out;
1071b5c5924fSchristos 	}
1072b5c5924fSchristos 	if ((r = fido_assert_allow_cred(assert, key_handle,
1073b5c5924fSchristos 	    key_handle_len)) != FIDO_OK) {
1074b5c5924fSchristos 		skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r));
1075b5c5924fSchristos 		goto out;
1076b5c5924fSchristos 	}
1077b5c5924fSchristos 	if ((r = fido_assert_set_up(assert,
1078b5c5924fSchristos 	    (flags & SSH_SK_USER_PRESENCE_REQD) ?
1079b5c5924fSchristos 	    FIDO_OPT_TRUE : FIDO_OPT_FALSE)) != FIDO_OK) {
1080b5c5924fSchristos 		skdebug(__func__, "fido_assert_set_up: %s", fido_strerr(r));
1081b5c5924fSchristos 		goto out;
1082b5c5924fSchristos 	}
108384f89c72Schristos 	if (pin == NULL && (flags & SSH_SK_USER_VERIFICATION_REQD)) {
108484f89c72Schristos 		if (check_sk_options(sk->dev, "uv", &internal_uv) < 0 ||
108584f89c72Schristos 		    internal_uv != 1) {
108684f89c72Schristos 			skdebug(__func__, "check_sk_options uv");
108784f89c72Schristos 			ret = SSH_SK_ERR_PIN_REQUIRED;
10887d35d687Schristos 			goto out;
10897d35d687Schristos 		}
109084f89c72Schristos 		if ((r = fido_assert_set_uv(assert,
109184f89c72Schristos 		    FIDO_OPT_TRUE)) != FIDO_OK) {
109284f89c72Schristos 			skdebug(__func__, "fido_assert_set_uv: %s",
109384f89c72Schristos 			    fido_strerr(r));
109484f89c72Schristos 			ret = fidoerr_to_skerr(r);
109584f89c72Schristos 			goto out;
109684f89c72Schristos 		}
109784f89c72Schristos 	}
10987d35d687Schristos 	if ((r = fido_dev_get_assert(sk->dev, assert, pin)) != FIDO_OK) {
1099b5c5924fSchristos 		skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r));
11007d35d687Schristos 		ret = fidoerr_to_skerr(r);
1101b5c5924fSchristos 		goto out;
1102b5c5924fSchristos 	}
1103b5c5924fSchristos 	if ((response = calloc(1, sizeof(*response))) == NULL) {
1104b5c5924fSchristos 		skdebug(__func__, "calloc response failed");
1105b5c5924fSchristos 		goto out;
1106b5c5924fSchristos 	}
1107b5c5924fSchristos 	response->flags = fido_assert_flags(assert, 0);
1108b5c5924fSchristos 	response->counter = fido_assert_sigcount(assert, 0);
1109b5c5924fSchristos 	if (pack_sig(alg, assert, response) != 0) {
1110b5c5924fSchristos 		skdebug(__func__, "pack_sig failed");
1111b5c5924fSchristos 		goto out;
1112b5c5924fSchristos 	}
1113b5c5924fSchristos 	*sign_response = response;
1114b5c5924fSchristos 	response = NULL;
1115b5c5924fSchristos 	ret = 0;
1116b5c5924fSchristos  out:
1117b5c5924fSchristos 	free(device);
1118b5c5924fSchristos 	if (response != NULL) {
1119b5c5924fSchristos 		free(response->sig_r);
1120b5c5924fSchristos 		free(response->sig_s);
1121b5c5924fSchristos 		free(response);
1122b5c5924fSchristos 	}
11237d35d687Schristos 	sk_close(sk);
1124b5c5924fSchristos 	fido_assert_free(&assert);
1125b5c5924fSchristos 	return ret;
1126b5c5924fSchristos }
1127b5c5924fSchristos 
1128b5c5924fSchristos static int
read_rks(struct sk_usbhid * sk,const char * pin,struct sk_resident_key *** rksp,size_t * nrksp)11297d35d687Schristos read_rks(struct sk_usbhid *sk, const char *pin,
1130b5c5924fSchristos     struct sk_resident_key ***rksp, size_t *nrksp)
1131b5c5924fSchristos {
113284f89c72Schristos 	int ret = SSH_SK_ERR_GENERAL, r = -1, internal_uv;
1133b5c5924fSchristos 	fido_credman_metadata_t *metadata = NULL;
1134b5c5924fSchristos 	fido_credman_rp_t *rp = NULL;
1135b5c5924fSchristos 	fido_credman_rk_t *rk = NULL;
113684f89c72Schristos 	size_t i, j, nrp, nrk, user_id_len;
1137b5c5924fSchristos 	const fido_cred_t *cred;
113884f89c72Schristos 	const char *rp_id, *rp_name, *user_name;
1139b5c5924fSchristos 	struct sk_resident_key *srk = NULL, **tmp;
114084f89c72Schristos 	const u_char *user_id;
1141b5c5924fSchristos 
11427d35d687Schristos 	if (pin == NULL) {
11437d35d687Schristos 		skdebug(__func__, "no PIN specified");
11447d35d687Schristos 		ret = SSH_SK_ERR_PIN_REQUIRED;
11457d35d687Schristos 		goto out;
1146b5c5924fSchristos 	}
1147b5c5924fSchristos 	if ((metadata = fido_credman_metadata_new()) == NULL) {
1148b5c5924fSchristos 		skdebug(__func__, "alloc failed");
1149b5c5924fSchristos 		goto out;
1150b5c5924fSchristos 	}
115184f89c72Schristos 	if (check_sk_options(sk->dev, "uv", &internal_uv) != 0) {
115284f89c72Schristos 		skdebug(__func__, "check_sk_options failed");
115384f89c72Schristos 		goto out;
115484f89c72Schristos 	}
1155b5c5924fSchristos 
11567d35d687Schristos 	if ((r = fido_credman_get_dev_metadata(sk->dev, metadata, pin)) != 0) {
1157b5c5924fSchristos 		if (r == FIDO_ERR_INVALID_COMMAND) {
1158b5c5924fSchristos 			skdebug(__func__, "device %s does not support "
11597d35d687Schristos 			    "resident keys", sk->path);
1160b5c5924fSchristos 			ret = 0;
1161b5c5924fSchristos 			goto out;
1162b5c5924fSchristos 		}
1163b5c5924fSchristos 		skdebug(__func__, "get metadata for %s failed: %s",
11647d35d687Schristos 		    sk->path, fido_strerr(r));
1165b5c5924fSchristos 		ret = fidoerr_to_skerr(r);
1166b5c5924fSchristos 		goto out;
1167b5c5924fSchristos 	}
1168b5c5924fSchristos 	skdebug(__func__, "existing %llu, remaining %llu",
1169b5c5924fSchristos 	    (unsigned long long)fido_credman_rk_existing(metadata),
1170b5c5924fSchristos 	    (unsigned long long)fido_credman_rk_remaining(metadata));
1171b5c5924fSchristos 	if ((rp = fido_credman_rp_new()) == NULL) {
1172b5c5924fSchristos 		skdebug(__func__, "alloc rp failed");
1173b5c5924fSchristos 		goto out;
1174b5c5924fSchristos 	}
11757d35d687Schristos 	if ((r = fido_credman_get_dev_rp(sk->dev, rp, pin)) != 0) {
1176b5c5924fSchristos 		skdebug(__func__, "get RPs for %s failed: %s",
11777d35d687Schristos 		    sk->path, fido_strerr(r));
1178b5c5924fSchristos 		goto out;
1179b5c5924fSchristos 	}
1180b5c5924fSchristos 	nrp = fido_credman_rp_count(rp);
1181b5c5924fSchristos 	skdebug(__func__, "Device %s has resident keys for %zu RPs",
11827d35d687Schristos 	    sk->path, nrp);
1183b5c5924fSchristos 
1184b5c5924fSchristos 	/* Iterate over RP IDs that have resident keys */
1185b5c5924fSchristos 	for (i = 0; i < nrp; i++) {
118684f89c72Schristos 		rp_id = fido_credman_rp_id(rp, i);
118784f89c72Schristos 		rp_name = fido_credman_rp_name(rp, i);
1188b5c5924fSchristos 		skdebug(__func__, "rp %zu: name=\"%s\" id=\"%s\" hashlen=%zu",
118984f89c72Schristos 		    i, rp_name == NULL ? "(none)" : rp_name,
119084f89c72Schristos 		    rp_id == NULL ? "(none)" : rp_id,
1191b5c5924fSchristos 		    fido_credman_rp_id_hash_len(rp, i));
1192b5c5924fSchristos 
1193b5c5924fSchristos 		/* Skip non-SSH RP IDs */
119484f89c72Schristos 		if (rp_id == NULL ||
119584f89c72Schristos 		    strncasecmp(fido_credman_rp_id(rp, i), "ssh:", 4) != 0)
1196b5c5924fSchristos 			continue;
1197b5c5924fSchristos 
1198b5c5924fSchristos 		fido_credman_rk_free(&rk);
1199b5c5924fSchristos 		if ((rk = fido_credman_rk_new()) == NULL) {
1200b5c5924fSchristos 			skdebug(__func__, "alloc rk failed");
1201b5c5924fSchristos 			goto out;
1202b5c5924fSchristos 		}
12037d35d687Schristos 		if ((r = fido_credman_get_dev_rk(sk->dev,
12047d35d687Schristos 		    fido_credman_rp_id(rp, i), rk, pin)) != 0) {
1205b5c5924fSchristos 			skdebug(__func__, "get RKs for %s slot %zu failed: %s",
12067d35d687Schristos 			    sk->path, i, fido_strerr(r));
1207b5c5924fSchristos 			goto out;
1208b5c5924fSchristos 		}
1209b5c5924fSchristos 		nrk = fido_credman_rk_count(rk);
1210b5c5924fSchristos 		skdebug(__func__, "RP \"%s\" has %zu resident keys",
1211b5c5924fSchristos 		    fido_credman_rp_id(rp, i), nrk);
1212b5c5924fSchristos 
1213b5c5924fSchristos 		/* Iterate over resident keys for this RP ID */
1214b5c5924fSchristos 		for (j = 0; j < nrk; j++) {
1215b5c5924fSchristos 			if ((cred = fido_credman_rk(rk, j)) == NULL) {
1216b5c5924fSchristos 				skdebug(__func__, "no RK in slot %zu", j);
1217b5c5924fSchristos 				continue;
1218b5c5924fSchristos 			}
121984f89c72Schristos 			if ((user_name = fido_cred_user_name(cred)) == NULL)
122084f89c72Schristos 				user_name = "";
122184f89c72Schristos 			user_id = fido_cred_user_id_ptr(cred);
122284f89c72Schristos 			user_id_len = fido_cred_user_id_len(cred);
122384f89c72Schristos 			skdebug(__func__, "Device %s RP \"%s\" user \"%s\" "
122484f89c72Schristos 			    "uidlen %zu slot %zu: type %d flags 0x%02x "
122584f89c72Schristos 			    "prot 0x%02x", sk->path, rp_id, user_name,
122684f89c72Schristos 			    user_id_len, j, fido_cred_type(cred),
12277d35d687Schristos 			    fido_cred_flags(cred), fido_cred_prot(cred));
1228b5c5924fSchristos 
1229b5c5924fSchristos 			/* build response entry */
1230b5c5924fSchristos 			if ((srk = calloc(1, sizeof(*srk))) == NULL ||
1231b5c5924fSchristos 			    (srk->key.key_handle = calloc(1,
1232b5c5924fSchristos 			    fido_cred_id_len(cred))) == NULL ||
123384f89c72Schristos 			    (srk->application = strdup(rp_id)) == NULL ||
123484f89c72Schristos 			    (user_id_len > 0 &&
123584f89c72Schristos 			     (srk->user_id = calloc(1, user_id_len)) == NULL)) {
1236b5c5924fSchristos 				skdebug(__func__, "alloc sk_resident_key");
1237b5c5924fSchristos 				goto out;
1238b5c5924fSchristos 			}
1239b5c5924fSchristos 
1240b5c5924fSchristos 			srk->key.key_handle_len = fido_cred_id_len(cred);
12417d35d687Schristos 			memcpy(srk->key.key_handle, fido_cred_id_ptr(cred),
1242b5c5924fSchristos 			    srk->key.key_handle_len);
124384f89c72Schristos 			srk->user_id_len = user_id_len;
124484f89c72Schristos 			if (srk->user_id_len != 0)
124584f89c72Schristos 				memcpy(srk->user_id, user_id, srk->user_id_len);
1246b5c5924fSchristos 
1247b5c5924fSchristos 			switch (fido_cred_type(cred)) {
1248b5c5924fSchristos 			case COSE_ES256:
1249b5c5924fSchristos 				srk->alg = SSH_SK_ECDSA;
1250b5c5924fSchristos 				break;
1251b5c5924fSchristos 			case COSE_EDDSA:
1252b5c5924fSchristos 				srk->alg = SSH_SK_ED25519;
1253b5c5924fSchristos 				break;
1254b5c5924fSchristos 			default:
1255b5c5924fSchristos 				skdebug(__func__, "unsupported key type %d",
1256b5c5924fSchristos 				    fido_cred_type(cred));
1257b5c5924fSchristos 				goto out; /* XXX free rk and continue */
1258b5c5924fSchristos 			}
1259b5c5924fSchristos 
126084f89c72Schristos 			if (fido_cred_prot(cred) == FIDO_CRED_PROT_UV_REQUIRED
126184f89c72Schristos 			    && internal_uv == -1)
12627d35d687Schristos 				srk->flags |=  SSH_SK_USER_VERIFICATION_REQD;
12637d35d687Schristos 
1264b5c5924fSchristos 			if ((r = pack_public_key(srk->alg, cred,
1265b5c5924fSchristos 			    &srk->key)) != 0) {
1266b5c5924fSchristos 				skdebug(__func__, "pack public key failed");
1267b5c5924fSchristos 				goto out;
1268b5c5924fSchristos 			}
1269b5c5924fSchristos 			/* append */
1270b5c5924fSchristos 			if ((tmp = recallocarray(*rksp, *nrksp, (*nrksp) + 1,
1271b5c5924fSchristos 			    sizeof(**rksp))) == NULL) {
1272b5c5924fSchristos 				skdebug(__func__, "alloc rksp");
1273b5c5924fSchristos 				goto out;
1274b5c5924fSchristos 			}
1275b5c5924fSchristos 			*rksp = tmp;
1276b5c5924fSchristos 			(*rksp)[(*nrksp)++] = srk;
1277b5c5924fSchristos 			srk = NULL;
1278b5c5924fSchristos 		}
1279b5c5924fSchristos 	}
1280b5c5924fSchristos 	/* Success */
1281b5c5924fSchristos 	ret = 0;
1282b5c5924fSchristos  out:
1283b5c5924fSchristos 	if (srk != NULL) {
1284b5c5924fSchristos 		free(srk->application);
1285b5c5924fSchristos 		freezero(srk->key.public_key, srk->key.public_key_len);
1286b5c5924fSchristos 		freezero(srk->key.key_handle, srk->key.key_handle_len);
128784f89c72Schristos 		freezero(srk->user_id, srk->user_id_len);
1288b5c5924fSchristos 		freezero(srk, sizeof(*srk));
1289b5c5924fSchristos 	}
1290b5c5924fSchristos 	fido_credman_rp_free(&rp);
1291b5c5924fSchristos 	fido_credman_rk_free(&rk);
1292b5c5924fSchristos 	fido_credman_metadata_free(&metadata);
1293b5c5924fSchristos 	return ret;
1294b5c5924fSchristos }
1295b5c5924fSchristos 
1296b5c5924fSchristos int
sk_load_resident_keys(const char * pin,struct sk_option ** options,struct sk_resident_key *** rksp,size_t * nrksp)1297b5c5924fSchristos sk_load_resident_keys(const char *pin, struct sk_option **options,
1298b5c5924fSchristos     struct sk_resident_key ***rksp, size_t *nrksp)
1299b5c5924fSchristos {
1300b5c5924fSchristos 	int ret = SSH_SK_ERR_GENERAL, r = -1;
13017d35d687Schristos 	size_t i, nrks = 0;
1302b5c5924fSchristos 	struct sk_resident_key **rks = NULL;
13037d35d687Schristos 	struct sk_usbhid *sk = NULL;
1304b5c5924fSchristos 	char *device = NULL;
13057d35d687Schristos 
1306b5c5924fSchristos 	*rksp = NULL;
1307b5c5924fSchristos 	*nrksp = 0;
1308b5c5924fSchristos 
13097d35d687Schristos 	fido_init(SSH_FIDO_INIT_ARG);
13107d35d687Schristos 
1311b5c5924fSchristos 	if (check_sign_load_resident_options(options, &device) != 0)
1312b5c5924fSchristos 		goto out; /* error already logged */
13137d35d687Schristos 	if (device != NULL)
13147d35d687Schristos 		sk = sk_open(device);
13157d35d687Schristos 	else
1316*a6c5e0d9Schristos 		sk = sk_probe(NULL, NULL, 0, 1);
13177d35d687Schristos 	if (sk == NULL) {
131884f89c72Schristos 		ret = SSH_SK_ERR_DEVICE_NOT_FOUND;
13197d35d687Schristos 		skdebug(__func__, "failed to find sk");
13207d35d687Schristos 		goto out;
13217d35d687Schristos 	}
13227d35d687Schristos 	skdebug(__func__, "trying %s", sk->path);
13237d35d687Schristos 	if ((r = read_rks(sk, pin, &rks, &nrks)) != 0) {
13247d35d687Schristos 		skdebug(__func__, "read_rks failed for %s", sk->path);
1325b5c5924fSchristos 		ret = r;
1326b5c5924fSchristos 		goto out;
1327b5c5924fSchristos 	}
1328b5c5924fSchristos 	/* success, unless we have no keys but a specific error */
1329b5c5924fSchristos 	if (nrks > 0 || ret == SSH_SK_ERR_GENERAL)
1330b5c5924fSchristos 		ret = 0;
1331b5c5924fSchristos 	*rksp = rks;
1332b5c5924fSchristos 	*nrksp = nrks;
1333b5c5924fSchristos 	rks = NULL;
1334b5c5924fSchristos 	nrks = 0;
1335b5c5924fSchristos  out:
13367d35d687Schristos 	sk_close(sk);
1337b5c5924fSchristos 	for (i = 0; i < nrks; i++) {
1338b5c5924fSchristos 		free(rks[i]->application);
1339b5c5924fSchristos 		freezero(rks[i]->key.public_key, rks[i]->key.public_key_len);
1340b5c5924fSchristos 		freezero(rks[i]->key.key_handle, rks[i]->key.key_handle_len);
134184f89c72Schristos 		freezero(rks[i]->user_id, rks[i]->user_id_len);
1342b5c5924fSchristos 		freezero(rks[i], sizeof(*rks[i]));
1343b5c5924fSchristos 	}
1344*a6c5e0d9Schristos 	free(device);
1345b5c5924fSchristos 	free(rks);
1346b5c5924fSchristos 	return ret;
1347b5c5924fSchristos }
1348b5c5924fSchristos 
1349