xref: /openbsd/usr.bin/ssh/sk-usbhid.c (revision 991d5a20)
1*991d5a20Sdjm /* $OpenBSD: sk-usbhid.c,v 1.32 2021/10/28 02:54:18 djm Exp $ */
2094c80e0Sdjm /*
3094c80e0Sdjm  * Copyright (c) 2019 Markus Friedl
401bb7af0Sdjm  * Copyright (c) 2020 Pedro Martelletto
5094c80e0Sdjm  *
6094c80e0Sdjm  * Permission to use, copy, modify, and distribute this software for any
7094c80e0Sdjm  * purpose with or without fee is hereby granted, provided that the above
8094c80e0Sdjm  * copyright notice and this permission notice appear in all copies.
9094c80e0Sdjm  *
10094c80e0Sdjm  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11094c80e0Sdjm  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12094c80e0Sdjm  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13094c80e0Sdjm  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14094c80e0Sdjm  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15094c80e0Sdjm  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16094c80e0Sdjm  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17094c80e0Sdjm  */
18094c80e0Sdjm 
19094c80e0Sdjm #include <stdint.h>
20094c80e0Sdjm #include <stdlib.h>
21094c80e0Sdjm #include <string.h>
22094c80e0Sdjm #include <stdio.h>
23094c80e0Sdjm #include <stddef.h>
24094c80e0Sdjm #include <stdarg.h>
253ce2af41Sdjm #include <sha2.h>
26719d3f82Sdjm #include <time.h>
27094c80e0Sdjm 
28f8cd6cb1Snaddy #ifdef WITH_OPENSSL
29094c80e0Sdjm #include <openssl/opensslv.h>
30094c80e0Sdjm #include <openssl/crypto.h>
31094c80e0Sdjm #include <openssl/bn.h>
32094c80e0Sdjm #include <openssl/ec.h>
33094c80e0Sdjm #include <openssl/ecdsa.h>
343ce2af41Sdjm #include <openssl/evp.h>
35f8cd6cb1Snaddy #endif /* WITH_OPENSSL */
36094c80e0Sdjm 
37094c80e0Sdjm #include <fido.h>
381ac4a90aSdjm #include <fido/credman.h>
39094c80e0Sdjm 
40094c80e0Sdjm #ifndef SK_STANDALONE
41094c80e0Sdjm # include "log.h"
42094c80e0Sdjm # include "xmalloc.h"
4301bb7af0Sdjm # include "misc.h"
44b0297854Sdjm /*
45b0297854Sdjm  * If building as part of OpenSSH, then rename exported functions.
46b0297854Sdjm  * This must be done before including sk-api.h.
47b0297854Sdjm  */
48b0297854Sdjm # define sk_api_version		ssh_sk_api_version
49b0297854Sdjm # define sk_enroll		ssh_sk_enroll
50b0297854Sdjm # define sk_sign		ssh_sk_sign
51b0297854Sdjm # define sk_load_resident_keys	ssh_sk_load_resident_keys
52b0297854Sdjm #endif /* !SK_STANDALONE */
53b0297854Sdjm 
54b0297854Sdjm #include "sk-api.h"
55094c80e0Sdjm 
56094c80e0Sdjm /* #define SK_DEBUG 1 */
57094c80e0Sdjm 
58fb0a6bcaSdjm #ifdef SK_DEBUG
59fb0a6bcaSdjm #define SSH_FIDO_INIT_ARG	FIDO_DEBUG
60fb0a6bcaSdjm #else
61fb0a6bcaSdjm #define SSH_FIDO_INIT_ARG	0
62fb0a6bcaSdjm #endif
63fb0a6bcaSdjm 
6401bb7af0Sdjm #define MAX_FIDO_DEVICES	8
6501bb7af0Sdjm #define FIDO_POLL_MS		50
6601bb7af0Sdjm #define SELECT_MS		15000
6701bb7af0Sdjm #define POLL_SLEEP_NS		200000000
68094c80e0Sdjm 
69094c80e0Sdjm /* Compatibility with OpenSSH 1.0.x */
70094c80e0Sdjm #if (OPENSSL_VERSION_NUMBER < 0x10100000L)
71094c80e0Sdjm #define ECDSA_SIG_get0(sig, pr, ps) \
72094c80e0Sdjm 	do { \
73094c80e0Sdjm 		(*pr) = sig->r; \
74094c80e0Sdjm 		(*ps) = sig->s; \
75094c80e0Sdjm 	} while (0)
76094c80e0Sdjm #endif
77094c80e0Sdjm 
7801bb7af0Sdjm struct sk_usbhid {
7901bb7af0Sdjm 	fido_dev_t *dev;
8001bb7af0Sdjm 	char *path;
8101bb7af0Sdjm };
8201bb7af0Sdjm 
83094c80e0Sdjm /* Return the version of the middleware API */
84094c80e0Sdjm uint32_t sk_api_version(void);
85094c80e0Sdjm 
86094c80e0Sdjm /* Enroll a U2F key (private key generation) */
87a0caf565Sdjm int sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len,
882db06755Sdjm     const char *application, uint8_t flags, const char *pin,
89a0caf565Sdjm     struct sk_option **options, struct sk_enroll_response **enroll_response);
90094c80e0Sdjm 
91094c80e0Sdjm /* Sign a challenge */
9274d7b7bdSdjm int sk_sign(uint32_t alg, const uint8_t *data, size_t data_len,
93094c80e0Sdjm     const char *application, const uint8_t *key_handle, size_t key_handle_len,
94a0caf565Sdjm     uint8_t flags, const char *pin, struct sk_option **options,
95a0caf565Sdjm     struct sk_sign_response **sign_response);
96094c80e0Sdjm 
971ac4a90aSdjm /* Load resident keys */
98a0caf565Sdjm int sk_load_resident_keys(const char *pin, struct sk_option **options,
991ac4a90aSdjm     struct sk_resident_key ***rks, size_t *nrks);
1001ac4a90aSdjm 
101094c80e0Sdjm static void skdebug(const char *func, const char *fmt, ...)
102094c80e0Sdjm     __attribute__((__format__ (printf, 2, 3)));
103094c80e0Sdjm 
104094c80e0Sdjm static void
105094c80e0Sdjm skdebug(const char *func, const char *fmt, ...)
106094c80e0Sdjm {
107094c80e0Sdjm #if !defined(SK_STANDALONE)
108094c80e0Sdjm 	char *msg;
109094c80e0Sdjm 	va_list ap;
110094c80e0Sdjm 
111094c80e0Sdjm 	va_start(ap, fmt);
112094c80e0Sdjm 	xvasprintf(&msg, fmt, ap);
113094c80e0Sdjm 	va_end(ap);
11459959935Sdjm 	debug("%s: %s", func, msg);
115094c80e0Sdjm 	free(msg);
116094c80e0Sdjm #elif defined(SK_DEBUG)
117094c80e0Sdjm 	va_list ap;
118094c80e0Sdjm 
119094c80e0Sdjm 	va_start(ap, fmt);
120094c80e0Sdjm 	fprintf(stderr, "%s: ", func);
121094c80e0Sdjm 	vfprintf(stderr, fmt, ap);
122094c80e0Sdjm 	fputc('\n', stderr);
123094c80e0Sdjm 	va_end(ap);
124094c80e0Sdjm #else
125094c80e0Sdjm 	(void)func; /* XXX */
126094c80e0Sdjm 	(void)fmt; /* XXX */
127094c80e0Sdjm #endif
128094c80e0Sdjm }
129094c80e0Sdjm 
130094c80e0Sdjm uint32_t
131094c80e0Sdjm sk_api_version(void)
132094c80e0Sdjm {
133b0297854Sdjm 	return SSH_SK_VERSION_MAJOR;
134094c80e0Sdjm }
135094c80e0Sdjm 
13601bb7af0Sdjm static struct sk_usbhid *
13701bb7af0Sdjm sk_open(const char *path)
138094c80e0Sdjm {
13901bb7af0Sdjm 	struct sk_usbhid *sk;
140094c80e0Sdjm 	int r;
141094c80e0Sdjm 
14201bb7af0Sdjm 	if (path == NULL) {
14301bb7af0Sdjm 		skdebug(__func__, "path == NULL");
14401bb7af0Sdjm 		return NULL;
145094c80e0Sdjm 	}
14601bb7af0Sdjm 	if ((sk = calloc(1, sizeof(*sk))) == NULL) {
14701bb7af0Sdjm 		skdebug(__func__, "calloc sk failed");
14801bb7af0Sdjm 		return NULL;
14901bb7af0Sdjm 	}
15001bb7af0Sdjm 	if ((sk->path = strdup(path)) == NULL) {
15101bb7af0Sdjm 		skdebug(__func__, "strdup path failed");
15201bb7af0Sdjm 		free(sk);
15301bb7af0Sdjm 		return NULL;
15401bb7af0Sdjm 	}
15501bb7af0Sdjm 	if ((sk->dev = fido_dev_new()) == NULL) {
15601bb7af0Sdjm 		skdebug(__func__, "fido_dev_new failed");
15701bb7af0Sdjm 		free(sk->path);
15801bb7af0Sdjm 		free(sk);
15901bb7af0Sdjm 		return NULL;
16001bb7af0Sdjm 	}
16101bb7af0Sdjm 	if ((r = fido_dev_open(sk->dev, sk->path)) != FIDO_OK) {
16201bb7af0Sdjm 		skdebug(__func__, "fido_dev_open %s failed: %s", sk->path,
163094c80e0Sdjm 		    fido_strerr(r));
16401bb7af0Sdjm 		fido_dev_free(&sk->dev);
16501bb7af0Sdjm 		free(sk->path);
16601bb7af0Sdjm 		free(sk);
16701bb7af0Sdjm 		return NULL;
168094c80e0Sdjm 	}
16901bb7af0Sdjm 	return sk;
170094c80e0Sdjm }
171094c80e0Sdjm 
17201bb7af0Sdjm static void
17301bb7af0Sdjm sk_close(struct sk_usbhid *sk)
17401bb7af0Sdjm {
17501bb7af0Sdjm 	if (sk == NULL)
17601bb7af0Sdjm 		return;
17701bb7af0Sdjm 	fido_dev_cancel(sk->dev); /* cancel any pending operation */
17801bb7af0Sdjm 	fido_dev_close(sk->dev);
17901bb7af0Sdjm 	fido_dev_free(&sk->dev);
18001bb7af0Sdjm 	free(sk->path);
18101bb7af0Sdjm 	free(sk);
18201bb7af0Sdjm }
18301bb7af0Sdjm 
18401bb7af0Sdjm static struct sk_usbhid **
18501bb7af0Sdjm sk_openv(const fido_dev_info_t *devlist, size_t ndevs, size_t *nopen)
18601bb7af0Sdjm {
18701bb7af0Sdjm 	const fido_dev_info_t *di;
18801bb7af0Sdjm 	struct sk_usbhid **skv;
18901bb7af0Sdjm 	size_t i;
19001bb7af0Sdjm 
19101bb7af0Sdjm 	*nopen = 0;
19201bb7af0Sdjm 	if ((skv = calloc(ndevs, sizeof(*skv))) == NULL) {
19301bb7af0Sdjm 		skdebug(__func__, "calloc skv failed");
19401bb7af0Sdjm 		return NULL;
19501bb7af0Sdjm 	}
19601bb7af0Sdjm 	for (i = 0; i < ndevs; i++) {
19701bb7af0Sdjm 		if ((di = fido_dev_info_ptr(devlist, i)) == NULL)
19801bb7af0Sdjm 			skdebug(__func__, "fido_dev_info_ptr failed");
19901bb7af0Sdjm 		else if ((skv[*nopen] = sk_open(fido_dev_info_path(di))) == NULL)
20001bb7af0Sdjm 			skdebug(__func__, "sk_open failed");
20101bb7af0Sdjm 		else
20201bb7af0Sdjm 			(*nopen)++;
20301bb7af0Sdjm 	}
20401bb7af0Sdjm 	if (*nopen == 0) {
20501bb7af0Sdjm 		for (i = 0; i < ndevs; i++)
20601bb7af0Sdjm 			sk_close(skv[i]);
20701bb7af0Sdjm 		free(skv);
20801bb7af0Sdjm 		skv = NULL;
20901bb7af0Sdjm 	}
21001bb7af0Sdjm 
21101bb7af0Sdjm 	return skv;
21201bb7af0Sdjm }
21301bb7af0Sdjm 
21401bb7af0Sdjm static void
21501bb7af0Sdjm sk_closev(struct sk_usbhid **skv, size_t nsk)
21601bb7af0Sdjm {
21701bb7af0Sdjm 	size_t i;
21801bb7af0Sdjm 
21901bb7af0Sdjm 	for (i = 0; i < nsk; i++)
22001bb7af0Sdjm 		sk_close(skv[i]);
22101bb7af0Sdjm 	free(skv);
22201bb7af0Sdjm }
22301bb7af0Sdjm 
224094c80e0Sdjm static int
22501bb7af0Sdjm sk_touch_begin(struct sk_usbhid **skv, size_t nsk)
22601bb7af0Sdjm {
22701bb7af0Sdjm 	size_t i, ok = 0;
22801bb7af0Sdjm 	int r;
22901bb7af0Sdjm 
23001bb7af0Sdjm 	for (i = 0; i < nsk; i++)
23101bb7af0Sdjm 		if ((r = fido_dev_get_touch_begin(skv[i]->dev)) != FIDO_OK)
23201bb7af0Sdjm 			skdebug(__func__, "fido_dev_get_touch_begin %s failed:"
23301bb7af0Sdjm 			    " %s", skv[i]->path, fido_strerr(r));
23401bb7af0Sdjm 		else
23501bb7af0Sdjm 			ok++;
23601bb7af0Sdjm 
23701bb7af0Sdjm 	return ok ? 0 : -1;
23801bb7af0Sdjm }
23901bb7af0Sdjm 
24001bb7af0Sdjm static int
24101bb7af0Sdjm sk_touch_poll(struct sk_usbhid **skv, size_t nsk, int *touch, size_t *idx)
24201bb7af0Sdjm {
24301bb7af0Sdjm 	struct timespec ts_pause;
24401bb7af0Sdjm 	size_t npoll, i;
24501bb7af0Sdjm 	int r;
24601bb7af0Sdjm 
24701bb7af0Sdjm 	ts_pause.tv_sec = 0;
24801bb7af0Sdjm 	ts_pause.tv_nsec = POLL_SLEEP_NS;
24901bb7af0Sdjm 	nanosleep(&ts_pause, NULL);
25001bb7af0Sdjm 	npoll = nsk;
25101bb7af0Sdjm 	for (i = 0; i < nsk; i++) {
25201bb7af0Sdjm 		if (skv[i] == NULL)
25301bb7af0Sdjm 			continue; /* device discarded */
25401bb7af0Sdjm 		skdebug(__func__, "polling %s", skv[i]->path);
25501bb7af0Sdjm 		if ((r = fido_dev_get_touch_status(skv[i]->dev, touch,
25601bb7af0Sdjm 		    FIDO_POLL_MS)) != FIDO_OK) {
25701bb7af0Sdjm 			skdebug(__func__, "fido_dev_get_touch_status %s: %s",
25801bb7af0Sdjm 			    skv[i]->path, fido_strerr(r));
25901bb7af0Sdjm 			sk_close(skv[i]); /* discard device */
26001bb7af0Sdjm 			skv[i] = NULL;
26101bb7af0Sdjm 			if (--npoll == 0) {
26201bb7af0Sdjm 				skdebug(__func__, "no device left to poll");
26301bb7af0Sdjm 				return -1;
26401bb7af0Sdjm 			}
26501bb7af0Sdjm 		} else if (*touch) {
26601bb7af0Sdjm 			*idx = i;
26701bb7af0Sdjm 			return 0;
26801bb7af0Sdjm 		}
26901bb7af0Sdjm 	}
27001bb7af0Sdjm 	*touch = 0;
27101bb7af0Sdjm 	return 0;
27201bb7af0Sdjm }
27301bb7af0Sdjm 
27401bb7af0Sdjm /* Calculate SHA256(m) */
27501bb7af0Sdjm static int
27601bb7af0Sdjm sha256_mem(const void *m, size_t mlen, u_char *d, size_t dlen)
27701bb7af0Sdjm {
27801bb7af0Sdjm #ifdef WITH_OPENSSL
27901bb7af0Sdjm 	u_int mdlen;
2809424a71bSdjm #else
2819424a71bSdjm 	SHA2_CTX ctx;
28201bb7af0Sdjm #endif
28301bb7af0Sdjm 
28401bb7af0Sdjm 	if (dlen != 32)
28501bb7af0Sdjm 		return -1;
28601bb7af0Sdjm #ifdef WITH_OPENSSL
28701bb7af0Sdjm 	mdlen = dlen;
28801bb7af0Sdjm 	if (!EVP_Digest(m, mlen, d, &mdlen, EVP_sha256(), NULL))
28901bb7af0Sdjm 		return -1;
29001bb7af0Sdjm #else
2919424a71bSdjm 	SHA256Init(&ctx);
2929424a71bSdjm 	SHA256Update(&ctx, (const uint8_t *)m, mlen);
2939424a71bSdjm 	SHA256Final(d, &ctx);
29401bb7af0Sdjm #endif
29501bb7af0Sdjm 	return 0;
29601bb7af0Sdjm }
29701bb7af0Sdjm 
29801bb7af0Sdjm /* Check if the specified key handle exists on a given sk. */
29901bb7af0Sdjm static int
30001bb7af0Sdjm sk_try(const struct sk_usbhid *sk, const char *application,
30101bb7af0Sdjm     const uint8_t *key_handle, size_t key_handle_len)
302094c80e0Sdjm {
303094c80e0Sdjm 	fido_assert_t *assert = NULL;
30401bb7af0Sdjm 	/* generate an invalid signature on FIDO2 tokens */
30501bb7af0Sdjm 	const char *data = "";
30601bb7af0Sdjm 	uint8_t message[32];
307094c80e0Sdjm 	int r = FIDO_ERR_INTERNAL;
308094c80e0Sdjm 
30901bb7af0Sdjm 	if (sha256_mem(data, strlen(data), message, sizeof(message)) != 0) {
31001bb7af0Sdjm 		skdebug(__func__, "hash message failed");
31101bb7af0Sdjm 		goto out;
31201bb7af0Sdjm 	}
313094c80e0Sdjm 	if ((assert = fido_assert_new()) == NULL) {
314094c80e0Sdjm 		skdebug(__func__, "fido_assert_new failed");
315094c80e0Sdjm 		goto out;
316094c80e0Sdjm 	}
317094c80e0Sdjm 	if ((r = fido_assert_set_clientdata_hash(assert, message,
31801bb7af0Sdjm 	    sizeof(message))) != FIDO_OK) {
319094c80e0Sdjm 		skdebug(__func__, "fido_assert_set_clientdata_hash: %s",
320094c80e0Sdjm 		    fido_strerr(r));
321094c80e0Sdjm 		goto out;
322094c80e0Sdjm 	}
323094c80e0Sdjm 	if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) {
324094c80e0Sdjm 		skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r));
325094c80e0Sdjm 		goto out;
326094c80e0Sdjm 	}
327094c80e0Sdjm 	if ((r = fido_assert_allow_cred(assert, key_handle,
328094c80e0Sdjm 	    key_handle_len)) != FIDO_OK) {
329094c80e0Sdjm 		skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r));
330094c80e0Sdjm 		goto out;
331094c80e0Sdjm 	}
332094c80e0Sdjm 	if ((r = fido_assert_set_up(assert, FIDO_OPT_FALSE)) != FIDO_OK) {
333094c80e0Sdjm 		skdebug(__func__, "fido_assert_up: %s", fido_strerr(r));
334094c80e0Sdjm 		goto out;
335094c80e0Sdjm 	}
33601bb7af0Sdjm 	r = fido_dev_get_assert(sk->dev, assert, NULL);
337094c80e0Sdjm 	skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r));
3387cf8e58dSdjm 	if (r == FIDO_ERR_USER_PRESENCE_REQUIRED) {
3397cf8e58dSdjm 		/* U2F tokens may return this */
3407cf8e58dSdjm 		r = FIDO_OK;
3417cf8e58dSdjm 	}
342094c80e0Sdjm  out:
343094c80e0Sdjm 	fido_assert_free(&assert);
344094c80e0Sdjm 
345094c80e0Sdjm 	return r != FIDO_OK ? -1 : 0;
346094c80e0Sdjm }
347094c80e0Sdjm 
34801bb7af0Sdjm static struct sk_usbhid *
34901bb7af0Sdjm sk_select_by_cred(const fido_dev_info_t *devlist, size_t ndevs,
35001bb7af0Sdjm     const char *application, const uint8_t *key_handle, size_t key_handle_len)
351094c80e0Sdjm {
35201bb7af0Sdjm 	struct sk_usbhid **skv, *sk;
35301bb7af0Sdjm 	size_t skvcnt, i;
354094c80e0Sdjm 
35501bb7af0Sdjm 	if ((skv = sk_openv(devlist, ndevs, &skvcnt)) == NULL) {
35601bb7af0Sdjm 		skdebug(__func__, "sk_openv failed");
357a0caf565Sdjm 		return NULL;
358a0caf565Sdjm 	}
3598c966c57Sdjm 	if (skvcnt == 1) {
3608c966c57Sdjm 		sk = skv[0];
3618c966c57Sdjm 		skv[0] = NULL;
3628c966c57Sdjm 		goto out;
3638c966c57Sdjm 	}
36401bb7af0Sdjm 	sk = NULL;
3658c966c57Sdjm 	for (i = 0; i < skvcnt; i++) {
36601bb7af0Sdjm 		if (sk_try(skv[i], application, key_handle,
36701bb7af0Sdjm 		    key_handle_len) == 0) {
36801bb7af0Sdjm 			sk = skv[i];
36901bb7af0Sdjm 			skv[i] = NULL;
37001bb7af0Sdjm 			skdebug(__func__, "found key in %s", sk->path);
37101bb7af0Sdjm 			break;
37201bb7af0Sdjm 		}
3738c966c57Sdjm 	}
3748c966c57Sdjm  out:
37501bb7af0Sdjm 	sk_closev(skv, skvcnt);
37601bb7af0Sdjm 	return sk;
37701bb7af0Sdjm }
37801bb7af0Sdjm 
37901bb7af0Sdjm static struct sk_usbhid *
38001bb7af0Sdjm sk_select_by_touch(const fido_dev_info_t *devlist, size_t ndevs)
38101bb7af0Sdjm {
38201bb7af0Sdjm 	struct sk_usbhid **skv, *sk;
38301bb7af0Sdjm 	struct timeval tv_start, tv_now, tv_delta;
38401bb7af0Sdjm 	size_t skvcnt, idx;
38501bb7af0Sdjm 	int touch, ms_remain;
38601bb7af0Sdjm 
38701bb7af0Sdjm 	if ((skv = sk_openv(devlist, ndevs, &skvcnt)) == NULL) {
38801bb7af0Sdjm 		skdebug(__func__, "sk_openv failed");
389a0caf565Sdjm 		return NULL;
390a0caf565Sdjm 	}
39101bb7af0Sdjm 	sk = NULL;
39201bb7af0Sdjm 	if (skvcnt < 2) {
39301bb7af0Sdjm 		if (skvcnt == 1) {
39401bb7af0Sdjm 			/* single candidate */
39501bb7af0Sdjm 			sk = skv[0];
39601bb7af0Sdjm 			skv[0] = NULL;
397a0caf565Sdjm 		}
39801bb7af0Sdjm 		goto out;
39901bb7af0Sdjm 	}
40001bb7af0Sdjm 	if (sk_touch_begin(skv, skvcnt) == -1) {
40101bb7af0Sdjm 		skdebug(__func__, "sk_touch_begin failed");
40201bb7af0Sdjm 		goto out;
40301bb7af0Sdjm 	}
40401bb7af0Sdjm 	monotime_tv(&tv_start);
40501bb7af0Sdjm 	do {
40601bb7af0Sdjm 		if (sk_touch_poll(skv, skvcnt, &touch, &idx) == -1) {
40701bb7af0Sdjm 			skdebug(__func__, "sk_touch_poll failed");
40801bb7af0Sdjm 			goto out;
40901bb7af0Sdjm 		}
41001bb7af0Sdjm 		if (touch) {
41101bb7af0Sdjm 			sk = skv[idx];
41201bb7af0Sdjm 			skv[idx] = NULL;
41301bb7af0Sdjm 			goto out;
41401bb7af0Sdjm 		}
41501bb7af0Sdjm 		monotime_tv(&tv_now);
41601bb7af0Sdjm 		timersub(&tv_now, &tv_start, &tv_delta);
41701bb7af0Sdjm 		ms_remain = SELECT_MS - tv_delta.tv_sec * 1000 -
41801bb7af0Sdjm 		    tv_delta.tv_usec / 1000;
41901bb7af0Sdjm 	} while (ms_remain >= FIDO_POLL_MS);
42001bb7af0Sdjm 	skdebug(__func__, "timeout");
42101bb7af0Sdjm out:
42201bb7af0Sdjm 	sk_closev(skv, skvcnt);
42301bb7af0Sdjm 	return sk;
42401bb7af0Sdjm }
42501bb7af0Sdjm 
42601bb7af0Sdjm static struct sk_usbhid *
42701bb7af0Sdjm sk_probe(const char *application, const uint8_t *key_handle,
42801bb7af0Sdjm     size_t key_handle_len)
42901bb7af0Sdjm {
43001bb7af0Sdjm 	struct sk_usbhid *sk;
43101bb7af0Sdjm 	fido_dev_info_t *devlist;
43201bb7af0Sdjm 	size_t ndevs;
43301bb7af0Sdjm 	int r;
434a0caf565Sdjm 
435094c80e0Sdjm 	if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) {
436094c80e0Sdjm 		skdebug(__func__, "fido_dev_info_new failed");
43701bb7af0Sdjm 		return NULL;
438094c80e0Sdjm 	}
439094c80e0Sdjm 	if ((r = fido_dev_info_manifest(devlist, MAX_FIDO_DEVICES,
44001bb7af0Sdjm 	    &ndevs)) != FIDO_OK) {
44101bb7af0Sdjm 		skdebug(__func__, "fido_dev_info_manifest failed: %s",
44201bb7af0Sdjm 		    fido_strerr(r));
443094c80e0Sdjm 		fido_dev_info_free(&devlist, MAX_FIDO_DEVICES);
44401bb7af0Sdjm 		return NULL;
44501bb7af0Sdjm 	}
44601bb7af0Sdjm 	skdebug(__func__, "%zu device(s) detected", ndevs);
44701bb7af0Sdjm 	if (ndevs == 0) {
44801bb7af0Sdjm 		sk = NULL;
44901bb7af0Sdjm 	} else if (application != NULL && key_handle != NULL) {
45001bb7af0Sdjm 		skdebug(__func__, "selecting sk by cred");
45101bb7af0Sdjm 		sk = sk_select_by_cred(devlist, ndevs, application, key_handle,
45201bb7af0Sdjm 		    key_handle_len);
45301bb7af0Sdjm 	} else {
45401bb7af0Sdjm 		skdebug(__func__, "selecting sk by touch");
45501bb7af0Sdjm 		sk = sk_select_by_touch(devlist, ndevs);
45601bb7af0Sdjm 	}
45701bb7af0Sdjm 	fido_dev_info_free(&devlist, MAX_FIDO_DEVICES);
45801bb7af0Sdjm 	return sk;
459094c80e0Sdjm }
460094c80e0Sdjm 
461f8cd6cb1Snaddy #ifdef WITH_OPENSSL
462094c80e0Sdjm /*
463094c80e0Sdjm  * The key returned via fido_cred_pubkey_ptr() is in affine coordinates,
464094c80e0Sdjm  * but the API expects a SEC1 octet string.
465094c80e0Sdjm  */
466094c80e0Sdjm static int
4671ac4a90aSdjm pack_public_key_ecdsa(const fido_cred_t *cred,
4681ac4a90aSdjm     struct sk_enroll_response *response)
469094c80e0Sdjm {
470094c80e0Sdjm 	const uint8_t *ptr;
471094c80e0Sdjm 	BIGNUM *x = NULL, *y = NULL;
472094c80e0Sdjm 	EC_POINT *q = NULL;
473094c80e0Sdjm 	EC_GROUP *g = NULL;
474094c80e0Sdjm 	int ret = -1;
475094c80e0Sdjm 
476094c80e0Sdjm 	response->public_key = NULL;
477094c80e0Sdjm 	response->public_key_len = 0;
478094c80e0Sdjm 
47907b718edSdjm 	if ((x = BN_new()) == NULL ||
48007b718edSdjm 	    (y = BN_new()) == NULL ||
481094c80e0Sdjm 	    (g = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)) == NULL ||
482094c80e0Sdjm 	    (q = EC_POINT_new(g)) == NULL) {
483094c80e0Sdjm 		skdebug(__func__, "libcrypto setup failed");
484094c80e0Sdjm 		goto out;
485094c80e0Sdjm 	}
486094c80e0Sdjm 	if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) {
487094c80e0Sdjm 		skdebug(__func__, "fido_cred_pubkey_ptr failed");
488094c80e0Sdjm 		goto out;
489094c80e0Sdjm 	}
490094c80e0Sdjm 	if (fido_cred_pubkey_len(cred) != 64) {
491094c80e0Sdjm 		skdebug(__func__, "bad fido_cred_pubkey_len %zu",
492094c80e0Sdjm 		    fido_cred_pubkey_len(cred));
493094c80e0Sdjm 		goto out;
494094c80e0Sdjm 	}
495094c80e0Sdjm 
496094c80e0Sdjm 	if (BN_bin2bn(ptr, 32, x) == NULL ||
497094c80e0Sdjm 	    BN_bin2bn(ptr + 32, 32, y) == NULL) {
498094c80e0Sdjm 		skdebug(__func__, "BN_bin2bn failed");
499094c80e0Sdjm 		goto out;
500094c80e0Sdjm 	}
50107b718edSdjm 	if (EC_POINT_set_affine_coordinates_GFp(g, q, x, y, NULL) != 1) {
502094c80e0Sdjm 		skdebug(__func__, "EC_POINT_set_affine_coordinates_GFp failed");
503094c80e0Sdjm 		goto out;
504094c80e0Sdjm 	}
505094c80e0Sdjm 	response->public_key_len = EC_POINT_point2oct(g, q,
50607b718edSdjm 	    POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL);
507094c80e0Sdjm 	if (response->public_key_len == 0 || response->public_key_len > 2048) {
508094c80e0Sdjm 		skdebug(__func__, "bad pubkey length %zu",
509094c80e0Sdjm 		    response->public_key_len);
510094c80e0Sdjm 		goto out;
511094c80e0Sdjm 	}
512094c80e0Sdjm 	if ((response->public_key = malloc(response->public_key_len)) == NULL) {
513094c80e0Sdjm 		skdebug(__func__, "malloc pubkey failed");
514094c80e0Sdjm 		goto out;
515094c80e0Sdjm 	}
516094c80e0Sdjm 	if (EC_POINT_point2oct(g, q, POINT_CONVERSION_UNCOMPRESSED,
51707b718edSdjm 	    response->public_key, response->public_key_len, NULL) == 0) {
518094c80e0Sdjm 		skdebug(__func__, "EC_POINT_point2oct failed");
519094c80e0Sdjm 		goto out;
520094c80e0Sdjm 	}
521094c80e0Sdjm 	/* success */
522094c80e0Sdjm 	ret = 0;
523094c80e0Sdjm  out:
524094c80e0Sdjm 	if (ret != 0 && response->public_key != NULL) {
525094c80e0Sdjm 		memset(response->public_key, 0, response->public_key_len);
526094c80e0Sdjm 		free(response->public_key);
527094c80e0Sdjm 		response->public_key = NULL;
528094c80e0Sdjm 	}
529094c80e0Sdjm 	EC_POINT_free(q);
530094c80e0Sdjm 	EC_GROUP_free(g);
53107b718edSdjm 	BN_clear_free(x);
53207b718edSdjm 	BN_clear_free(y);
533094c80e0Sdjm 	return ret;
534094c80e0Sdjm }
535f8cd6cb1Snaddy #endif /* WITH_OPENSSL */
536094c80e0Sdjm 
537094c80e0Sdjm static int
5381ac4a90aSdjm pack_public_key_ed25519(const fido_cred_t *cred,
5391ac4a90aSdjm     struct sk_enroll_response *response)
540094c80e0Sdjm {
541094c80e0Sdjm 	const uint8_t *ptr;
542094c80e0Sdjm 	size_t len;
543094c80e0Sdjm 	int ret = -1;
544094c80e0Sdjm 
545094c80e0Sdjm 	response->public_key = NULL;
546094c80e0Sdjm 	response->public_key_len = 0;
547094c80e0Sdjm 
548094c80e0Sdjm 	if ((len = fido_cred_pubkey_len(cred)) != 32) {
549094c80e0Sdjm 		skdebug(__func__, "bad fido_cred_pubkey_len len %zu", len);
550094c80e0Sdjm 		goto out;
551094c80e0Sdjm 	}
552094c80e0Sdjm 	if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) {
553094c80e0Sdjm 		skdebug(__func__, "fido_cred_pubkey_ptr failed");
554094c80e0Sdjm 		goto out;
555094c80e0Sdjm 	}
556094c80e0Sdjm 	response->public_key_len = len;
557094c80e0Sdjm 	if ((response->public_key = malloc(response->public_key_len)) == NULL) {
558094c80e0Sdjm 		skdebug(__func__, "malloc pubkey failed");
559094c80e0Sdjm 		goto out;
560094c80e0Sdjm 	}
561094c80e0Sdjm 	memcpy(response->public_key, ptr, len);
562094c80e0Sdjm 	ret = 0;
563094c80e0Sdjm  out:
564094c80e0Sdjm 	if (ret != 0)
565094c80e0Sdjm 		free(response->public_key);
566094c80e0Sdjm 	return ret;
567094c80e0Sdjm }
568094c80e0Sdjm 
569094c80e0Sdjm static int
570a0caf565Sdjm pack_public_key(uint32_t alg, const fido_cred_t *cred,
5711ac4a90aSdjm     struct sk_enroll_response *response)
572094c80e0Sdjm {
573094c80e0Sdjm 	switch(alg) {
574f8cd6cb1Snaddy #ifdef WITH_OPENSSL
575b0297854Sdjm 	case SSH_SK_ECDSA:
576094c80e0Sdjm 		return pack_public_key_ecdsa(cred, response);
577f8cd6cb1Snaddy #endif /* WITH_OPENSSL */
578b0297854Sdjm 	case SSH_SK_ED25519:
579094c80e0Sdjm 		return pack_public_key_ed25519(cred, response);
580094c80e0Sdjm 	default:
581094c80e0Sdjm 		return -1;
582094c80e0Sdjm 	}
583094c80e0Sdjm }
584094c80e0Sdjm 
585480af03fSdjm static int
586480af03fSdjm fidoerr_to_skerr(int fidoerr)
587480af03fSdjm {
588480af03fSdjm 	switch (fidoerr) {
589480af03fSdjm 	case FIDO_ERR_UNSUPPORTED_OPTION:
590b0297854Sdjm 	case FIDO_ERR_UNSUPPORTED_ALGORITHM:
591480af03fSdjm 		return SSH_SK_ERR_UNSUPPORTED;
592480af03fSdjm 	case FIDO_ERR_PIN_REQUIRED:
593480af03fSdjm 	case FIDO_ERR_PIN_INVALID:
594480af03fSdjm 		return SSH_SK_ERR_PIN_REQUIRED;
595480af03fSdjm 	default:
596480af03fSdjm 		return -1;
597480af03fSdjm 	}
598480af03fSdjm }
599480af03fSdjm 
600a0caf565Sdjm static int
601a0caf565Sdjm check_enroll_options(struct sk_option **options, char **devicep,
602a0caf565Sdjm     uint8_t *user_id, size_t user_id_len)
603a0caf565Sdjm {
604a0caf565Sdjm 	size_t i;
605a0caf565Sdjm 
606a0caf565Sdjm 	if (options == NULL)
607a0caf565Sdjm 		return 0;
608a0caf565Sdjm 	for (i = 0; options[i] != NULL; i++) {
609a0caf565Sdjm 		if (strcmp(options[i]->name, "device") == 0) {
610a0caf565Sdjm 			if ((*devicep = strdup(options[i]->value)) == NULL) {
611a0caf565Sdjm 				skdebug(__func__, "strdup device failed");
612a0caf565Sdjm 				return -1;
613a0caf565Sdjm 			}
614a0caf565Sdjm 			skdebug(__func__, "requested device %s", *devicep);
6151a9bfc1cSdjm 		} else if (strcmp(options[i]->name, "user") == 0) {
616a0caf565Sdjm 			if (strlcpy(user_id, options[i]->value, user_id_len) >=
617a0caf565Sdjm 			    user_id_len) {
618a0caf565Sdjm 				skdebug(__func__, "user too long");
619a0caf565Sdjm 				return -1;
620a0caf565Sdjm 			}
621a0caf565Sdjm 			skdebug(__func__, "requested user %s",
622a0caf565Sdjm 			    (char *)user_id);
623a0caf565Sdjm 		} else {
624a0caf565Sdjm 			skdebug(__func__, "requested unsupported option %s",
625a0caf565Sdjm 			    options[i]->name);
626a0caf565Sdjm 			if (options[i]->required) {
627a0caf565Sdjm 				skdebug(__func__, "unknown required option");
628a0caf565Sdjm 				return -1;
629a0caf565Sdjm 			}
630a0caf565Sdjm 		}
631a0caf565Sdjm 	}
632a0caf565Sdjm 	return 0;
633a0caf565Sdjm }
634a0caf565Sdjm 
635094c80e0Sdjm int
636a0caf565Sdjm sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len,
6372db06755Sdjm     const char *application, uint8_t flags, const char *pin,
638a0caf565Sdjm     struct sk_option **options, struct sk_enroll_response **enroll_response)
639094c80e0Sdjm {
640094c80e0Sdjm 	fido_cred_t *cred = NULL;
641094c80e0Sdjm 	const uint8_t *ptr;
6425e70647bSdjm 	uint8_t user_id[32], chall_hash[32];
64301bb7af0Sdjm 	struct sk_usbhid *sk = NULL;
644094c80e0Sdjm 	struct sk_enroll_response *response = NULL;
645094c80e0Sdjm 	size_t len;
6464a0fa473Sdjm 	int credprot;
647094c80e0Sdjm 	int cose_alg;
648480af03fSdjm 	int ret = SSH_SK_ERR_GENERAL;
649094c80e0Sdjm 	int r;
650094c80e0Sdjm 	char *device = NULL;
651094c80e0Sdjm 
652fb0a6bcaSdjm 	fido_init(SSH_FIDO_INIT_ARG);
653fb0a6bcaSdjm 
65435f0234cSmarkus 	if (enroll_response == NULL) {
65535f0234cSmarkus 		skdebug(__func__, "enroll_response == NULL");
656094c80e0Sdjm 		goto out;
657094c80e0Sdjm 	}
65801bb7af0Sdjm 	*enroll_response = NULL;
659a0caf565Sdjm 	memset(user_id, 0, sizeof(user_id));
66001bb7af0Sdjm 	if (check_enroll_options(options, &device, user_id,
66101bb7af0Sdjm 	    sizeof(user_id)) != 0)
662a0caf565Sdjm 		goto out; /* error already logged */
663a0caf565Sdjm 
664094c80e0Sdjm 	switch(alg) {
665f8cd6cb1Snaddy #ifdef WITH_OPENSSL
666b0297854Sdjm 	case SSH_SK_ECDSA:
667094c80e0Sdjm 		cose_alg = COSE_ES256;
668094c80e0Sdjm 		break;
669f8cd6cb1Snaddy #endif /* WITH_OPENSSL */
670b0297854Sdjm 	case SSH_SK_ED25519:
671094c80e0Sdjm 		cose_alg = COSE_EDDSA;
672094c80e0Sdjm 		break;
673094c80e0Sdjm 	default:
674094c80e0Sdjm 		skdebug(__func__, "unsupported key type %d", alg);
675094c80e0Sdjm 		goto out;
676094c80e0Sdjm 	}
67701bb7af0Sdjm 	if (device != NULL)
67801bb7af0Sdjm 		sk = sk_open(device);
67901bb7af0Sdjm 	else
68001bb7af0Sdjm 		sk = sk_probe(NULL, NULL, 0);
68101bb7af0Sdjm 	if (sk == NULL) {
68201bb7af0Sdjm 		skdebug(__func__, "failed to find sk");
683094c80e0Sdjm 		goto out;
684094c80e0Sdjm 	}
68501bb7af0Sdjm 	skdebug(__func__, "using device %s", sk->path);
686094c80e0Sdjm 	if ((cred = fido_cred_new()) == NULL) {
687094c80e0Sdjm 		skdebug(__func__, "fido_cred_new failed");
688094c80e0Sdjm 		goto out;
689094c80e0Sdjm 	}
690094c80e0Sdjm 	if ((r = fido_cred_set_type(cred, cose_alg)) != FIDO_OK) {
691094c80e0Sdjm 		skdebug(__func__, "fido_cred_set_type: %s", fido_strerr(r));
692094c80e0Sdjm 		goto out;
693094c80e0Sdjm 	}
6945e70647bSdjm 	if (sha256_mem(challenge, challenge_len,
6955e70647bSdjm 	    chall_hash, sizeof(chall_hash)) != 0) {
6965e70647bSdjm 		skdebug(__func__, "hash challenge failed");
6975e70647bSdjm 		goto out;
6985e70647bSdjm 	}
6995e70647bSdjm 	if ((r = fido_cred_set_clientdata_hash(cred, chall_hash,
7005e70647bSdjm 	    sizeof(chall_hash))) != FIDO_OK) {
701094c80e0Sdjm 		skdebug(__func__, "fido_cred_set_clientdata_hash: %s",
702094c80e0Sdjm 		    fido_strerr(r));
703094c80e0Sdjm 		goto out;
704094c80e0Sdjm 	}
705b0297854Sdjm 	if ((r = fido_cred_set_rk(cred, (flags & SSH_SK_RESIDENT_KEY) != 0 ?
7068908bc36Sdjm 	    FIDO_OPT_TRUE : FIDO_OPT_OMIT)) != FIDO_OK) {
7078908bc36Sdjm 		skdebug(__func__, "fido_cred_set_rk: %s", fido_strerr(r));
7088908bc36Sdjm 		goto out;
7098908bc36Sdjm 	}
710094c80e0Sdjm 	if ((r = fido_cred_set_user(cred, user_id, sizeof(user_id),
711094c80e0Sdjm 	    "openssh", "openssh", NULL)) != FIDO_OK) {
712094c80e0Sdjm 		skdebug(__func__, "fido_cred_set_user: %s", fido_strerr(r));
713094c80e0Sdjm 		goto out;
714094c80e0Sdjm 	}
715094c80e0Sdjm 	if ((r = fido_cred_set_rp(cred, application, NULL)) != FIDO_OK) {
716094c80e0Sdjm 		skdebug(__func__, "fido_cred_set_rp: %s", fido_strerr(r));
717094c80e0Sdjm 		goto out;
718094c80e0Sdjm 	}
7191f63d3c4Sdjm 	if ((flags & (SSH_SK_RESIDENT_KEY|SSH_SK_USER_VERIFICATION_REQD)) != 0) {
72001bb7af0Sdjm 		if (!fido_dev_supports_cred_prot(sk->dev)) {
72101bb7af0Sdjm 			skdebug(__func__, "%s does not support credprot, "
72201bb7af0Sdjm 			    "refusing to create unprotected "
72301bb7af0Sdjm 			    "resident/verify-required key", sk->path);
7244a0fa473Sdjm 			ret = SSH_SK_ERR_UNSUPPORTED;
7254a0fa473Sdjm 			goto out;
7264a0fa473Sdjm 		}
7271f63d3c4Sdjm 		if ((flags & SSH_SK_USER_VERIFICATION_REQD))
7281f63d3c4Sdjm 			credprot = FIDO_CRED_PROT_UV_REQUIRED;
7291f63d3c4Sdjm 		else
7301f63d3c4Sdjm 			credprot = FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID;
7311f63d3c4Sdjm 
7321f63d3c4Sdjm 		if ((r = fido_cred_set_prot(cred, credprot)) != FIDO_OK) {
7334a0fa473Sdjm 			skdebug(__func__, "fido_cred_set_prot: %s",
7344a0fa473Sdjm 			    fido_strerr(r));
7354a0fa473Sdjm 			ret = fidoerr_to_skerr(r);
7364a0fa473Sdjm 			goto out;
7374a0fa473Sdjm 		}
7384a0fa473Sdjm 	}
73901bb7af0Sdjm 	if ((r = fido_dev_make_cred(sk->dev, cred, pin)) != FIDO_OK) {
740094c80e0Sdjm 		skdebug(__func__, "fido_dev_make_cred: %s", fido_strerr(r));
741480af03fSdjm 		ret = fidoerr_to_skerr(r);
742094c80e0Sdjm 		goto out;
743094c80e0Sdjm 	}
744094c80e0Sdjm 	if (fido_cred_x5c_ptr(cred) != NULL) {
745094c80e0Sdjm 		if ((r = fido_cred_verify(cred)) != FIDO_OK) {
746094c80e0Sdjm 			skdebug(__func__, "fido_cred_verify: %s",
747094c80e0Sdjm 			    fido_strerr(r));
748094c80e0Sdjm 			goto out;
749094c80e0Sdjm 		}
750094c80e0Sdjm 	} else {
751094c80e0Sdjm 		skdebug(__func__, "self-attested credential");
752094c80e0Sdjm 		if ((r = fido_cred_verify_self(cred)) != FIDO_OK) {
753094c80e0Sdjm 			skdebug(__func__, "fido_cred_verify_self: %s",
754094c80e0Sdjm 			    fido_strerr(r));
755094c80e0Sdjm 			goto out;
756094c80e0Sdjm 		}
757094c80e0Sdjm 	}
758094c80e0Sdjm 	if ((response = calloc(1, sizeof(*response))) == NULL) {
759094c80e0Sdjm 		skdebug(__func__, "calloc response failed");
760094c80e0Sdjm 		goto out;
761094c80e0Sdjm 	}
762094c80e0Sdjm 	if (pack_public_key(alg, cred, response) != 0) {
763094c80e0Sdjm 		skdebug(__func__, "pack_public_key failed");
764094c80e0Sdjm 		goto out;
765094c80e0Sdjm 	}
766094c80e0Sdjm 	if ((ptr = fido_cred_id_ptr(cred)) != NULL) {
767094c80e0Sdjm 		len = fido_cred_id_len(cred);
768094c80e0Sdjm 		if ((response->key_handle = calloc(1, len)) == NULL) {
769094c80e0Sdjm 			skdebug(__func__, "calloc key handle failed");
770094c80e0Sdjm 			goto out;
771094c80e0Sdjm 		}
772094c80e0Sdjm 		memcpy(response->key_handle, ptr, len);
773094c80e0Sdjm 		response->key_handle_len = len;
774094c80e0Sdjm 	}
775094c80e0Sdjm 	if ((ptr = fido_cred_sig_ptr(cred)) != NULL) {
776094c80e0Sdjm 		len = fido_cred_sig_len(cred);
777094c80e0Sdjm 		if ((response->signature = calloc(1, len)) == NULL) {
778094c80e0Sdjm 			skdebug(__func__, "calloc signature failed");
779094c80e0Sdjm 			goto out;
780094c80e0Sdjm 		}
781094c80e0Sdjm 		memcpy(response->signature, ptr, len);
782094c80e0Sdjm 		response->signature_len = len;
783094c80e0Sdjm 	}
784094c80e0Sdjm 	if ((ptr = fido_cred_x5c_ptr(cred)) != NULL) {
785094c80e0Sdjm 		len = fido_cred_x5c_len(cred);
78648e6b99dSdjm 		skdebug(__func__, "attestation cert len=%zu", len);
787094c80e0Sdjm 		if ((response->attestation_cert = calloc(1, len)) == NULL) {
788094c80e0Sdjm 			skdebug(__func__, "calloc attestation cert failed");
789094c80e0Sdjm 			goto out;
790094c80e0Sdjm 		}
791094c80e0Sdjm 		memcpy(response->attestation_cert, ptr, len);
792094c80e0Sdjm 		response->attestation_cert_len = len;
793094c80e0Sdjm 	}
794ee0a8761Sdjm 	if ((ptr = fido_cred_authdata_ptr(cred)) != NULL) {
795ee0a8761Sdjm 		len = fido_cred_authdata_len(cred);
79648e6b99dSdjm 		skdebug(__func__, "authdata len=%zu", len);
797ee0a8761Sdjm 		if ((response->authdata = calloc(1, len)) == NULL) {
798ee0a8761Sdjm 			skdebug(__func__, "calloc authdata failed");
799ee0a8761Sdjm 			goto out;
800ee0a8761Sdjm 		}
801ee0a8761Sdjm 		memcpy(response->authdata, ptr, len);
802ee0a8761Sdjm 		response->authdata_len = len;
803ee0a8761Sdjm 	}
80435f0234cSmarkus 	*enroll_response = response;
805094c80e0Sdjm 	response = NULL;
806094c80e0Sdjm 	ret = 0;
807094c80e0Sdjm  out:
808094c80e0Sdjm 	free(device);
809094c80e0Sdjm 	if (response != NULL) {
810094c80e0Sdjm 		free(response->public_key);
811094c80e0Sdjm 		free(response->key_handle);
812094c80e0Sdjm 		free(response->signature);
813094c80e0Sdjm 		free(response->attestation_cert);
814ee0a8761Sdjm 		free(response->authdata);
815094c80e0Sdjm 		free(response);
816094c80e0Sdjm 	}
81701bb7af0Sdjm 	sk_close(sk);
818094c80e0Sdjm 	fido_cred_free(&cred);
819094c80e0Sdjm 	return ret;
820094c80e0Sdjm }
821094c80e0Sdjm 
822f8cd6cb1Snaddy #ifdef WITH_OPENSSL
823094c80e0Sdjm static int
824094c80e0Sdjm pack_sig_ecdsa(fido_assert_t *assert, struct sk_sign_response *response)
825094c80e0Sdjm {
826094c80e0Sdjm 	ECDSA_SIG *sig = NULL;
827094c80e0Sdjm 	const BIGNUM *sig_r, *sig_s;
828094c80e0Sdjm 	const unsigned char *cp;
829094c80e0Sdjm 	size_t sig_len;
830094c80e0Sdjm 	int ret = -1;
831094c80e0Sdjm 
832094c80e0Sdjm 	cp = fido_assert_sig_ptr(assert, 0);
833094c80e0Sdjm 	sig_len = fido_assert_sig_len(assert, 0);
834094c80e0Sdjm 	if ((sig = d2i_ECDSA_SIG(NULL, &cp, sig_len)) == NULL) {
835094c80e0Sdjm 		skdebug(__func__, "d2i_ECDSA_SIG failed");
836094c80e0Sdjm 		goto out;
837094c80e0Sdjm 	}
838094c80e0Sdjm 	ECDSA_SIG_get0(sig, &sig_r, &sig_s);
839094c80e0Sdjm 	response->sig_r_len = BN_num_bytes(sig_r);
840094c80e0Sdjm 	response->sig_s_len = BN_num_bytes(sig_s);
841094c80e0Sdjm 	if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL ||
842094c80e0Sdjm 	    (response->sig_s = calloc(1, response->sig_s_len)) == NULL) {
843094c80e0Sdjm 		skdebug(__func__, "calloc signature failed");
844094c80e0Sdjm 		goto out;
845094c80e0Sdjm 	}
846094c80e0Sdjm 	BN_bn2bin(sig_r, response->sig_r);
847094c80e0Sdjm 	BN_bn2bin(sig_s, response->sig_s);
848094c80e0Sdjm 	ret = 0;
849094c80e0Sdjm  out:
850094c80e0Sdjm 	ECDSA_SIG_free(sig);
851094c80e0Sdjm 	if (ret != 0) {
852094c80e0Sdjm 		free(response->sig_r);
853094c80e0Sdjm 		free(response->sig_s);
854094c80e0Sdjm 		response->sig_r = NULL;
855094c80e0Sdjm 		response->sig_s = NULL;
856094c80e0Sdjm 	}
857094c80e0Sdjm 	return ret;
858094c80e0Sdjm }
859f8cd6cb1Snaddy #endif /* WITH_OPENSSL */
860094c80e0Sdjm 
861094c80e0Sdjm static int
862094c80e0Sdjm pack_sig_ed25519(fido_assert_t *assert, struct sk_sign_response *response)
863094c80e0Sdjm {
864094c80e0Sdjm 	const unsigned char *ptr;
865094c80e0Sdjm 	size_t len;
866094c80e0Sdjm 	int ret = -1;
867094c80e0Sdjm 
868094c80e0Sdjm 	ptr = fido_assert_sig_ptr(assert, 0);
869094c80e0Sdjm 	len = fido_assert_sig_len(assert, 0);
870094c80e0Sdjm 	if (len != 64) {
871094c80e0Sdjm 		skdebug(__func__, "bad length %zu", len);
872094c80e0Sdjm 		goto out;
873094c80e0Sdjm 	}
874094c80e0Sdjm 	response->sig_r_len = len;
875094c80e0Sdjm 	if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL) {
876094c80e0Sdjm 		skdebug(__func__, "calloc signature failed");
877094c80e0Sdjm 		goto out;
878094c80e0Sdjm 	}
879094c80e0Sdjm 	memcpy(response->sig_r, ptr, len);
880094c80e0Sdjm 	ret = 0;
881094c80e0Sdjm  out:
882094c80e0Sdjm 	if (ret != 0) {
883094c80e0Sdjm 		free(response->sig_r);
884094c80e0Sdjm 		response->sig_r = NULL;
885094c80e0Sdjm 	}
886094c80e0Sdjm 	return ret;
887094c80e0Sdjm }
888094c80e0Sdjm 
889094c80e0Sdjm static int
890a0caf565Sdjm pack_sig(uint32_t  alg, fido_assert_t *assert,
891a0caf565Sdjm     struct sk_sign_response *response)
892094c80e0Sdjm {
893094c80e0Sdjm 	switch(alg) {
894f8cd6cb1Snaddy #ifdef WITH_OPENSSL
895b0297854Sdjm 	case SSH_SK_ECDSA:
896094c80e0Sdjm 		return pack_sig_ecdsa(assert, response);
897f8cd6cb1Snaddy #endif /* WITH_OPENSSL */
898b0297854Sdjm 	case SSH_SK_ED25519:
899094c80e0Sdjm 		return pack_sig_ed25519(assert, response);
900094c80e0Sdjm 	default:
901094c80e0Sdjm 		return -1;
902094c80e0Sdjm 	}
903094c80e0Sdjm }
904094c80e0Sdjm 
905a0caf565Sdjm /* Checks sk_options for sk_sign() and sk_load_resident_keys() */
906a0caf565Sdjm static int
907a0caf565Sdjm check_sign_load_resident_options(struct sk_option **options, char **devicep)
908a0caf565Sdjm {
909a0caf565Sdjm 	size_t i;
910a0caf565Sdjm 
911a0caf565Sdjm 	if (options == NULL)
912a0caf565Sdjm 		return 0;
913a0caf565Sdjm 	for (i = 0; options[i] != NULL; i++) {
914a0caf565Sdjm 		if (strcmp(options[i]->name, "device") == 0) {
915a0caf565Sdjm 			if ((*devicep = strdup(options[i]->value)) == NULL) {
916a0caf565Sdjm 				skdebug(__func__, "strdup device failed");
917a0caf565Sdjm 				return -1;
918a0caf565Sdjm 			}
919a0caf565Sdjm 			skdebug(__func__, "requested device %s", *devicep);
920a0caf565Sdjm 		} else {
921a0caf565Sdjm 			skdebug(__func__, "requested unsupported option %s",
922a0caf565Sdjm 			    options[i]->name);
923a0caf565Sdjm 			if (options[i]->required) {
924a0caf565Sdjm 				skdebug(__func__, "unknown required option");
925a0caf565Sdjm 				return -1;
926a0caf565Sdjm 			}
927a0caf565Sdjm 		}
928a0caf565Sdjm 	}
929a0caf565Sdjm 	return 0;
930a0caf565Sdjm }
931a0caf565Sdjm 
932094c80e0Sdjm int
9333ce2af41Sdjm sk_sign(uint32_t alg, const uint8_t *data, size_t datalen,
934094c80e0Sdjm     const char *application,
935094c80e0Sdjm     const uint8_t *key_handle, size_t key_handle_len,
936a0caf565Sdjm     uint8_t flags, const char *pin, struct sk_option **options,
937a0caf565Sdjm     struct sk_sign_response **sign_response)
938094c80e0Sdjm {
939094c80e0Sdjm 	fido_assert_t *assert = NULL;
940a0caf565Sdjm 	char *device = NULL;
94101bb7af0Sdjm 	struct sk_usbhid *sk = NULL;
942094c80e0Sdjm 	struct sk_sign_response *response = NULL;
9433ce2af41Sdjm 	uint8_t message[32];
944480af03fSdjm 	int ret = SSH_SK_ERR_GENERAL;
945094c80e0Sdjm 	int r;
946094c80e0Sdjm 
947fb0a6bcaSdjm 	fido_init(SSH_FIDO_INIT_ARG);
948094c80e0Sdjm 
949094c80e0Sdjm 	if (sign_response == NULL) {
950094c80e0Sdjm 		skdebug(__func__, "sign_response == NULL");
951094c80e0Sdjm 		goto out;
952094c80e0Sdjm 	}
953094c80e0Sdjm 	*sign_response = NULL;
954a0caf565Sdjm 	if (check_sign_load_resident_options(options, &device) != 0)
955a0caf565Sdjm 		goto out; /* error already logged */
9563ce2af41Sdjm 	/* hash data to be signed before it goes to the security key */
9573ce2af41Sdjm 	if ((r = sha256_mem(data, datalen, message, sizeof(message))) != 0) {
9583ce2af41Sdjm 		skdebug(__func__, "hash message failed");
9593ce2af41Sdjm 		goto out;
9603ce2af41Sdjm 	}
96101bb7af0Sdjm 	if (device != NULL)
96201bb7af0Sdjm 		sk = sk_open(device);
96301bb7af0Sdjm 	else if (pin != NULL || (flags & SSH_SK_USER_VERIFICATION_REQD))
96401bb7af0Sdjm 		sk = sk_probe(NULL, NULL, 0);
96501bb7af0Sdjm 	else
96601bb7af0Sdjm 		sk = sk_probe(application, key_handle, key_handle_len);
96701bb7af0Sdjm 	if (sk == NULL) {
96801bb7af0Sdjm 		skdebug(__func__, "failed to find sk");
969094c80e0Sdjm 		goto out;
970094c80e0Sdjm 	}
971094c80e0Sdjm 	if ((assert = fido_assert_new()) == NULL) {
972094c80e0Sdjm 		skdebug(__func__, "fido_assert_new failed");
973094c80e0Sdjm 		goto out;
974094c80e0Sdjm 	}
975094c80e0Sdjm 	if ((r = fido_assert_set_clientdata_hash(assert, message,
9763ce2af41Sdjm 	    sizeof(message))) != FIDO_OK) {
977094c80e0Sdjm 		skdebug(__func__, "fido_assert_set_clientdata_hash: %s",
978094c80e0Sdjm 		    fido_strerr(r));
979094c80e0Sdjm 		goto out;
980094c80e0Sdjm 	}
981094c80e0Sdjm 	if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) {
982094c80e0Sdjm 		skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r));
983094c80e0Sdjm 		goto out;
984094c80e0Sdjm 	}
985094c80e0Sdjm 	if ((r = fido_assert_allow_cred(assert, key_handle,
986094c80e0Sdjm 	    key_handle_len)) != FIDO_OK) {
987094c80e0Sdjm 		skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r));
988094c80e0Sdjm 		goto out;
989094c80e0Sdjm 	}
990094c80e0Sdjm 	if ((r = fido_assert_set_up(assert,
991b0297854Sdjm 	    (flags & SSH_SK_USER_PRESENCE_REQD) ?
992094c80e0Sdjm 	    FIDO_OPT_TRUE : FIDO_OPT_FALSE)) != FIDO_OK) {
993094c80e0Sdjm 		skdebug(__func__, "fido_assert_set_up: %s", fido_strerr(r));
994094c80e0Sdjm 		goto out;
995094c80e0Sdjm 	}
9961f63d3c4Sdjm 	if (pin == NULL && (flags & SSH_SK_USER_VERIFICATION_REQD) &&
9971f63d3c4Sdjm 	    (r = fido_assert_set_uv(assert, FIDO_OPT_TRUE)) != FIDO_OK) {
9981f63d3c4Sdjm 		skdebug(__func__, "fido_assert_set_uv: %s", fido_strerr(r));
9991f63d3c4Sdjm 		ret = FIDO_ERR_PIN_REQUIRED;
10001f63d3c4Sdjm 		goto out;
10011f63d3c4Sdjm 	}
100201bb7af0Sdjm 	if ((r = fido_dev_get_assert(sk->dev, assert, pin)) != FIDO_OK) {
1003094c80e0Sdjm 		skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r));
10041f63d3c4Sdjm 		ret = fidoerr_to_skerr(r);
1005094c80e0Sdjm 		goto out;
1006094c80e0Sdjm 	}
1007094c80e0Sdjm 	if ((response = calloc(1, sizeof(*response))) == NULL) {
1008094c80e0Sdjm 		skdebug(__func__, "calloc response failed");
1009094c80e0Sdjm 		goto out;
1010094c80e0Sdjm 	}
1011094c80e0Sdjm 	response->flags = fido_assert_flags(assert, 0);
1012094c80e0Sdjm 	response->counter = fido_assert_sigcount(assert, 0);
1013094c80e0Sdjm 	if (pack_sig(alg, assert, response) != 0) {
1014094c80e0Sdjm 		skdebug(__func__, "pack_sig failed");
1015094c80e0Sdjm 		goto out;
1016094c80e0Sdjm 	}
1017094c80e0Sdjm 	*sign_response = response;
1018094c80e0Sdjm 	response = NULL;
1019094c80e0Sdjm 	ret = 0;
1020094c80e0Sdjm  out:
10213ce2af41Sdjm 	explicit_bzero(message, sizeof(message));
1022a0caf565Sdjm 	free(device);
1023094c80e0Sdjm 	if (response != NULL) {
1024094c80e0Sdjm 		free(response->sig_r);
1025094c80e0Sdjm 		free(response->sig_s);
1026094c80e0Sdjm 		free(response);
1027094c80e0Sdjm 	}
102801bb7af0Sdjm 	sk_close(sk);
1029094c80e0Sdjm 	fido_assert_free(&assert);
1030094c80e0Sdjm 	return ret;
1031094c80e0Sdjm }
10321ac4a90aSdjm 
10331ac4a90aSdjm static int
103401bb7af0Sdjm read_rks(struct sk_usbhid *sk, const char *pin,
10351ac4a90aSdjm     struct sk_resident_key ***rksp, size_t *nrksp)
10361ac4a90aSdjm {
1037480af03fSdjm 	int ret = SSH_SK_ERR_GENERAL, r = -1;
10381ac4a90aSdjm 	fido_credman_metadata_t *metadata = NULL;
10391ac4a90aSdjm 	fido_credman_rp_t *rp = NULL;
10401ac4a90aSdjm 	fido_credman_rk_t *rk = NULL;
1041*991d5a20Sdjm 	size_t i, j, nrp, nrk, user_id_len;
10421ac4a90aSdjm 	const fido_cred_t *cred;
1043*991d5a20Sdjm 	const char *rp_id, *rp_name, *user_name;
10441ac4a90aSdjm 	struct sk_resident_key *srk = NULL, **tmp;
1045*991d5a20Sdjm 	const u_char *user_id;
10461ac4a90aSdjm 
104701bb7af0Sdjm 	if (pin == NULL) {
104801bb7af0Sdjm 		skdebug(__func__, "no PIN specified");
104901bb7af0Sdjm 		ret = SSH_SK_ERR_PIN_REQUIRED;
105001bb7af0Sdjm 		goto out;
10511ac4a90aSdjm 	}
10521ac4a90aSdjm 	if ((metadata = fido_credman_metadata_new()) == NULL) {
10531ac4a90aSdjm 		skdebug(__func__, "alloc failed");
10541ac4a90aSdjm 		goto out;
10551ac4a90aSdjm 	}
10561ac4a90aSdjm 
105701bb7af0Sdjm 	if ((r = fido_credman_get_dev_metadata(sk->dev, metadata, pin)) != 0) {
10581ac4a90aSdjm 		if (r == FIDO_ERR_INVALID_COMMAND) {
10591ac4a90aSdjm 			skdebug(__func__, "device %s does not support "
106001bb7af0Sdjm 			    "resident keys", sk->path);
1061480af03fSdjm 			ret = 0;
10621ac4a90aSdjm 			goto out;
10631ac4a90aSdjm 		}
10641ac4a90aSdjm 		skdebug(__func__, "get metadata for %s failed: %s",
106501bb7af0Sdjm 		    sk->path, fido_strerr(r));
1066a0caf565Sdjm 		ret = fidoerr_to_skerr(r);
10671ac4a90aSdjm 		goto out;
10681ac4a90aSdjm 	}
10691ac4a90aSdjm 	skdebug(__func__, "existing %llu, remaining %llu",
10701ac4a90aSdjm 	    (unsigned long long)fido_credman_rk_existing(metadata),
10711ac4a90aSdjm 	    (unsigned long long)fido_credman_rk_remaining(metadata));
10721ac4a90aSdjm 	if ((rp = fido_credman_rp_new()) == NULL) {
10731ac4a90aSdjm 		skdebug(__func__, "alloc rp failed");
10741ac4a90aSdjm 		goto out;
10751ac4a90aSdjm 	}
107601bb7af0Sdjm 	if ((r = fido_credman_get_dev_rp(sk->dev, rp, pin)) != 0) {
10771ac4a90aSdjm 		skdebug(__func__, "get RPs for %s failed: %s",
107801bb7af0Sdjm 		    sk->path, fido_strerr(r));
10791ac4a90aSdjm 		goto out;
10801ac4a90aSdjm 	}
10811ac4a90aSdjm 	nrp = fido_credman_rp_count(rp);
10821ac4a90aSdjm 	skdebug(__func__, "Device %s has resident keys for %zu RPs",
108301bb7af0Sdjm 	    sk->path, nrp);
10841ac4a90aSdjm 
10851ac4a90aSdjm 	/* Iterate over RP IDs that have resident keys */
10861ac4a90aSdjm 	for (i = 0; i < nrp; i++) {
1087*991d5a20Sdjm 		rp_id = fido_credman_rp_id(rp, i);
1088*991d5a20Sdjm 		rp_name = fido_credman_rp_name(rp, i);
10891ac4a90aSdjm 		skdebug(__func__, "rp %zu: name=\"%s\" id=\"%s\" hashlen=%zu",
1090*991d5a20Sdjm 		    i, rp_name == NULL ? "(none)" : rp_name,
1091*991d5a20Sdjm 		    rp_id == NULL ? "(none)" : rp_id,
10921ac4a90aSdjm 		    fido_credman_rp_id_hash_len(rp, i));
10931ac4a90aSdjm 
10941ac4a90aSdjm 		/* Skip non-SSH RP IDs */
1095*991d5a20Sdjm 		if (rp_id == NULL ||
1096*991d5a20Sdjm 		    strncasecmp(fido_credman_rp_id(rp, i), "ssh:", 4) != 0)
10971ac4a90aSdjm 			continue;
10981ac4a90aSdjm 
10991ac4a90aSdjm 		fido_credman_rk_free(&rk);
11001ac4a90aSdjm 		if ((rk = fido_credman_rk_new()) == NULL) {
11011ac4a90aSdjm 			skdebug(__func__, "alloc rk failed");
11021ac4a90aSdjm 			goto out;
11031ac4a90aSdjm 		}
110401bb7af0Sdjm 		if ((r = fido_credman_get_dev_rk(sk->dev,
110501bb7af0Sdjm 		    fido_credman_rp_id(rp, i), rk, pin)) != 0) {
11061ac4a90aSdjm 			skdebug(__func__, "get RKs for %s slot %zu failed: %s",
110701bb7af0Sdjm 			    sk->path, i, fido_strerr(r));
11081ac4a90aSdjm 			goto out;
11091ac4a90aSdjm 		}
11101ac4a90aSdjm 		nrk = fido_credman_rk_count(rk);
11111ac4a90aSdjm 		skdebug(__func__, "RP \"%s\" has %zu resident keys",
11121ac4a90aSdjm 		    fido_credman_rp_id(rp, i), nrk);
11131ac4a90aSdjm 
11141ac4a90aSdjm 		/* Iterate over resident keys for this RP ID */
11151ac4a90aSdjm 		for (j = 0; j < nrk; j++) {
11161ac4a90aSdjm 			if ((cred = fido_credman_rk(rk, j)) == NULL) {
11171ac4a90aSdjm 				skdebug(__func__, "no RK in slot %zu", j);
11181ac4a90aSdjm 				continue;
11191ac4a90aSdjm 			}
1120*991d5a20Sdjm 			if ((user_name = fido_cred_user_name(cred)) == NULL)
1121*991d5a20Sdjm 				user_name = "";
1122*991d5a20Sdjm 			user_id = fido_cred_user_id_ptr(cred);
1123*991d5a20Sdjm 			user_id_len = fido_cred_user_id_len(cred);
1124*991d5a20Sdjm 			skdebug(__func__, "Device %s RP \"%s\" user \"%s\" "
1125*991d5a20Sdjm 			    "uidlen %zu slot %zu: type %d flags 0x%02x "
1126*991d5a20Sdjm 			    "prot 0x%02x", sk->path, rp_id, user_name,
1127*991d5a20Sdjm 			    user_id_len, j, fido_cred_type(cred),
11281f63d3c4Sdjm 			    fido_cred_flags(cred), fido_cred_prot(cred));
11291ac4a90aSdjm 
11301ac4a90aSdjm 			/* build response entry */
11311ac4a90aSdjm 			if ((srk = calloc(1, sizeof(*srk))) == NULL ||
11321ac4a90aSdjm 			    (srk->key.key_handle = calloc(1,
11331ac4a90aSdjm 			    fido_cred_id_len(cred))) == NULL ||
1134*991d5a20Sdjm 			    (srk->application = strdup(rp_id)) == NULL ||
1135*991d5a20Sdjm 			    (user_id_len > 0 &&
1136*991d5a20Sdjm 			     (srk->user_id = calloc(1, user_id_len)) == NULL)) {
11371ac4a90aSdjm 				skdebug(__func__, "alloc sk_resident_key");
11381ac4a90aSdjm 				goto out;
11391ac4a90aSdjm 			}
11401ac4a90aSdjm 
11411ac4a90aSdjm 			srk->key.key_handle_len = fido_cred_id_len(cred);
114215a2cdb6Sdjm 			memcpy(srk->key.key_handle, fido_cred_id_ptr(cred),
11431ac4a90aSdjm 			    srk->key.key_handle_len);
1144*991d5a20Sdjm 			srk->user_id_len = user_id_len;
1145*991d5a20Sdjm 			if (srk->user_id_len != 0)
1146*991d5a20Sdjm 				memcpy(srk->user_id, user_id, srk->user_id_len);
11471ac4a90aSdjm 
11481ac4a90aSdjm 			switch (fido_cred_type(cred)) {
11491ac4a90aSdjm 			case COSE_ES256:
1150b0297854Sdjm 				srk->alg = SSH_SK_ECDSA;
11511ac4a90aSdjm 				break;
11521ac4a90aSdjm 			case COSE_EDDSA:
1153b0297854Sdjm 				srk->alg = SSH_SK_ED25519;
11541ac4a90aSdjm 				break;
11551ac4a90aSdjm 			default:
11561ac4a90aSdjm 				skdebug(__func__, "unsupported key type %d",
11571ac4a90aSdjm 				    fido_cred_type(cred));
1158b0297854Sdjm 				goto out; /* XXX free rk and continue */
11591ac4a90aSdjm 			}
11601ac4a90aSdjm 
116115a2cdb6Sdjm 			if (fido_cred_prot(cred) == FIDO_CRED_PROT_UV_REQUIRED)
116215a2cdb6Sdjm 				srk->flags |=  SSH_SK_USER_VERIFICATION_REQD;
116315a2cdb6Sdjm 
11641ac4a90aSdjm 			if ((r = pack_public_key(srk->alg, cred,
11651ac4a90aSdjm 			    &srk->key)) != 0) {
11661ac4a90aSdjm 				skdebug(__func__, "pack public key failed");
11671ac4a90aSdjm 				goto out;
11681ac4a90aSdjm 			}
11691ac4a90aSdjm 			/* append */
11701ac4a90aSdjm 			if ((tmp = recallocarray(*rksp, *nrksp, (*nrksp) + 1,
11711ac4a90aSdjm 			    sizeof(**rksp))) == NULL) {
11721ac4a90aSdjm 				skdebug(__func__, "alloc rksp");
11731ac4a90aSdjm 				goto out;
11741ac4a90aSdjm 			}
11751ac4a90aSdjm 			*rksp = tmp;
11761ac4a90aSdjm 			(*rksp)[(*nrksp)++] = srk;
11771ac4a90aSdjm 			srk = NULL;
11781ac4a90aSdjm 		}
11791ac4a90aSdjm 	}
11801ac4a90aSdjm 	/* Success */
1181480af03fSdjm 	ret = 0;
11821ac4a90aSdjm  out:
11831ac4a90aSdjm 	if (srk != NULL) {
11841ac4a90aSdjm 		free(srk->application);
11851ac4a90aSdjm 		freezero(srk->key.public_key, srk->key.public_key_len);
11861ac4a90aSdjm 		freezero(srk->key.key_handle, srk->key.key_handle_len);
1187*991d5a20Sdjm 		freezero(srk->user_id, srk->user_id_len);
11881ac4a90aSdjm 		freezero(srk, sizeof(*srk));
11891ac4a90aSdjm 	}
11901ac4a90aSdjm 	fido_credman_rp_free(&rp);
11911ac4a90aSdjm 	fido_credman_rk_free(&rk);
11921ac4a90aSdjm 	fido_credman_metadata_free(&metadata);
1193480af03fSdjm 	return ret;
11941ac4a90aSdjm }
11951ac4a90aSdjm 
11961ac4a90aSdjm int
1197a0caf565Sdjm sk_load_resident_keys(const char *pin, struct sk_option **options,
11981ac4a90aSdjm     struct sk_resident_key ***rksp, size_t *nrksp)
11991ac4a90aSdjm {
1200480af03fSdjm 	int ret = SSH_SK_ERR_GENERAL, r = -1;
120101bb7af0Sdjm 	size_t i, nrks = 0;
12021ac4a90aSdjm 	struct sk_resident_key **rks = NULL;
120301bb7af0Sdjm 	struct sk_usbhid *sk = NULL;
1204a0caf565Sdjm 	char *device = NULL;
120501bb7af0Sdjm 
12061ac4a90aSdjm 	*rksp = NULL;
12071ac4a90aSdjm 	*nrksp = 0;
12081ac4a90aSdjm 
1209fb0a6bcaSdjm 	fido_init(SSH_FIDO_INIT_ARG);
1210fb0a6bcaSdjm 
1211a0caf565Sdjm 	if (check_sign_load_resident_options(options, &device) != 0)
1212a0caf565Sdjm 		goto out; /* error already logged */
121301bb7af0Sdjm 	if (device != NULL)
121401bb7af0Sdjm 		sk = sk_open(device);
121501bb7af0Sdjm 	else
121601bb7af0Sdjm 		sk = sk_probe(NULL, NULL, 0);
121701bb7af0Sdjm 	if (sk == NULL) {
121801bb7af0Sdjm 		skdebug(__func__, "failed to find sk");
121901bb7af0Sdjm 		goto out;
122001bb7af0Sdjm 	}
122101bb7af0Sdjm 	skdebug(__func__, "trying %s", sk->path);
122201bb7af0Sdjm 	if ((r = read_rks(sk, pin, &rks, &nrks)) != 0) {
122301bb7af0Sdjm 		skdebug(__func__, "read_rks failed for %s", sk->path);
1224a0caf565Sdjm 		ret = r;
1225a0caf565Sdjm 		goto out;
1226a0caf565Sdjm 	}
1227a0caf565Sdjm 	/* success, unless we have no keys but a specific error */
1228a0caf565Sdjm 	if (nrks > 0 || ret == SSH_SK_ERR_GENERAL)
1229480af03fSdjm 		ret = 0;
12301ac4a90aSdjm 	*rksp = rks;
12311ac4a90aSdjm 	*nrksp = nrks;
12321ac4a90aSdjm 	rks = NULL;
12331ac4a90aSdjm 	nrks = 0;
12341ac4a90aSdjm  out:
123501bb7af0Sdjm 	sk_close(sk);
12361ac4a90aSdjm 	for (i = 0; i < nrks; i++) {
12371ac4a90aSdjm 		free(rks[i]->application);
12381ac4a90aSdjm 		freezero(rks[i]->key.public_key, rks[i]->key.public_key_len);
12391ac4a90aSdjm 		freezero(rks[i]->key.key_handle, rks[i]->key.key_handle_len);
1240*991d5a20Sdjm 		freezero(rks[i]->user_id, rks[i]->user_id_len);
12411ac4a90aSdjm 		freezero(rks[i], sizeof(*rks[i]));
12421ac4a90aSdjm 	}
12431ac4a90aSdjm 	free(rks);
1244480af03fSdjm 	return ret;
12451ac4a90aSdjm }
12461ac4a90aSdjm 
1247