xref: /openssh-portable/sk-usbhid.c (revision 34c5ef6e)
1*34c5ef6eSdjm@openbsd.org /* $OpenBSD: sk-usbhid.c,v 1.29 2021/02/18 02:15:07 djm Exp $ */
26bff9521Sdjm@openbsd.org /*
36bff9521Sdjm@openbsd.org  * Copyright (c) 2019 Markus Friedl
4642e06d0Sdjm@openbsd.org  * Copyright (c) 2020 Pedro Martelletto
56bff9521Sdjm@openbsd.org  *
66bff9521Sdjm@openbsd.org  * Permission to use, copy, modify, and distribute this software for any
76bff9521Sdjm@openbsd.org  * purpose with or without fee is hereby granted, provided that the above
86bff9521Sdjm@openbsd.org  * copyright notice and this permission notice appear in all copies.
96bff9521Sdjm@openbsd.org  *
106bff9521Sdjm@openbsd.org  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
116bff9521Sdjm@openbsd.org  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
126bff9521Sdjm@openbsd.org  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
136bff9521Sdjm@openbsd.org  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
146bff9521Sdjm@openbsd.org  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
156bff9521Sdjm@openbsd.org  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
166bff9521Sdjm@openbsd.org  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
176bff9521Sdjm@openbsd.org  */
186bff9521Sdjm@openbsd.org 
196bff9521Sdjm@openbsd.org #include "includes.h"
206bff9521Sdjm@openbsd.org 
216bff9521Sdjm@openbsd.org #ifdef ENABLE_SK_INTERNAL
226bff9521Sdjm@openbsd.org 
236bff9521Sdjm@openbsd.org #include <stdint.h>
246bff9521Sdjm@openbsd.org #include <stdlib.h>
256bff9521Sdjm@openbsd.org #include <string.h>
266bff9521Sdjm@openbsd.org #include <stdio.h>
276bff9521Sdjm@openbsd.org #include <stddef.h>
286bff9521Sdjm@openbsd.org #include <stdarg.h>
29e5ed753aSdjm@openbsd.org #include <time.h>
30c0dfd18dSDamien Miller #ifdef HAVE_SHA2_H
3159d2de95Sdjm@openbsd.org #include <sha2.h>
32c0dfd18dSDamien Miller #endif
336bff9521Sdjm@openbsd.org 
34723a5369Snaddy@openbsd.org #ifdef WITH_OPENSSL
356bff9521Sdjm@openbsd.org #include <openssl/opensslv.h>
366bff9521Sdjm@openbsd.org #include <openssl/crypto.h>
376bff9521Sdjm@openbsd.org #include <openssl/bn.h>
386bff9521Sdjm@openbsd.org #include <openssl/ec.h>
396bff9521Sdjm@openbsd.org #include <openssl/ecdsa.h>
4059d2de95Sdjm@openbsd.org #include <openssl/evp.h>
41723a5369Snaddy@openbsd.org #endif /* WITH_OPENSSL */
426bff9521Sdjm@openbsd.org 
436bff9521Sdjm@openbsd.org #include <fido.h>
4414cea36dSdjm@openbsd.org #include <fido/credman.h>
456bff9521Sdjm@openbsd.org 
46bbcc858dSDamien Miller /* backwards compat for libfido2 */
47c1c44eeeSpedro martelletto #ifndef HAVE_FIDO_CRED_PROT
48ce178be0SDamien Miller #define fido_cred_prot(x) (0)
49ce178be0SDamien Miller #endif
50ce178be0SDamien Miller #ifndef HAVE_FIDO_CRED_SET_PROT
51ce178be0SDamien Miller #define fido_cred_set_prot(x, y) (FIDO_ERR_UNSUPPORTED_OPTION)
52ce178be0SDamien Miller #endif
53bbcc858dSDamien Miller #ifndef HAVE_FIDO_DEV_SUPPORTS_CRED_PROT
54bbcc858dSDamien Miller #define fido_dev_supports_cred_prot(x) (0)
55bbcc858dSDamien Miller #endif
56bbcc858dSDamien Miller #ifndef HAVE_FIDO_DEV_GET_TOUCH_BEGIN
57bbcc858dSDamien Miller #define fido_dev_get_touch_begin(x) (FIDO_ERR_UNSUPPORTED_OPTION)
58bbcc858dSDamien Miller #endif
59bbcc858dSDamien Miller #ifndef HAVE_FIDO_DEV_GET_TOUCH_STATUS
60bbcc858dSDamien Miller #define fido_dev_get_touch_status(x, y, z) (FIDO_ERR_UNSUPPORTED_OPTION)
61bbcc858dSDamien Miller #endif
62ce178be0SDamien Miller #ifndef FIDO_CRED_PROT_UV_REQUIRED
63ce178be0SDamien Miller #define FIDO_CRED_PROT_UV_REQUIRED 0
64ce178be0SDamien Miller #endif
65ce178be0SDamien Miller #ifndef FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID
66ce178be0SDamien Miller #define FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID 0
67ce178be0SDamien Miller #endif
68bbcc858dSDamien Miller 
696bff9521Sdjm@openbsd.org #ifndef SK_STANDALONE
706bff9521Sdjm@openbsd.org # include "log.h"
716bff9521Sdjm@openbsd.org # include "xmalloc.h"
72642e06d0Sdjm@openbsd.org # include "misc.h"
7359d01f1dSdjm@openbsd.org /*
7459d01f1dSdjm@openbsd.org  * If building as part of OpenSSH, then rename exported functions.
7559d01f1dSdjm@openbsd.org  * This must be done before including sk-api.h.
7659d01f1dSdjm@openbsd.org  */
7759d01f1dSdjm@openbsd.org # define sk_api_version		ssh_sk_api_version
7859d01f1dSdjm@openbsd.org # define sk_enroll		ssh_sk_enroll
7959d01f1dSdjm@openbsd.org # define sk_sign		ssh_sk_sign
8059d01f1dSdjm@openbsd.org # define sk_load_resident_keys	ssh_sk_load_resident_keys
8159d01f1dSdjm@openbsd.org #endif /* !SK_STANDALONE */
8259d01f1dSdjm@openbsd.org 
8359d01f1dSdjm@openbsd.org #include "sk-api.h"
846bff9521Sdjm@openbsd.org 
856bff9521Sdjm@openbsd.org /* #define SK_DEBUG 1 */
866bff9521Sdjm@openbsd.org 
871e70dc32Sdjm@openbsd.org #ifdef SK_DEBUG
881e70dc32Sdjm@openbsd.org #define SSH_FIDO_INIT_ARG	FIDO_DEBUG
891e70dc32Sdjm@openbsd.org #else
901e70dc32Sdjm@openbsd.org #define SSH_FIDO_INIT_ARG	0
911e70dc32Sdjm@openbsd.org #endif
921e70dc32Sdjm@openbsd.org 
93642e06d0Sdjm@openbsd.org #define MAX_FIDO_DEVICES	8
94642e06d0Sdjm@openbsd.org #define FIDO_POLL_MS		50
95642e06d0Sdjm@openbsd.org #define SELECT_MS		15000
96642e06d0Sdjm@openbsd.org #define POLL_SLEEP_NS		200000000
976bff9521Sdjm@openbsd.org 
986bff9521Sdjm@openbsd.org /* Compatibility with OpenSSH 1.0.x */
996bff9521Sdjm@openbsd.org #if (OPENSSL_VERSION_NUMBER < 0x10100000L)
1006bff9521Sdjm@openbsd.org #define ECDSA_SIG_get0(sig, pr, ps) \
1016bff9521Sdjm@openbsd.org 	do { \
1026bff9521Sdjm@openbsd.org 		(*pr) = sig->r; \
1036bff9521Sdjm@openbsd.org 		(*ps) = sig->s; \
1046bff9521Sdjm@openbsd.org 	} while (0)
1056bff9521Sdjm@openbsd.org #endif
1066bff9521Sdjm@openbsd.org 
107642e06d0Sdjm@openbsd.org struct sk_usbhid {
108642e06d0Sdjm@openbsd.org 	fido_dev_t *dev;
109642e06d0Sdjm@openbsd.org 	char *path;
110642e06d0Sdjm@openbsd.org };
111642e06d0Sdjm@openbsd.org 
1126bff9521Sdjm@openbsd.org /* Return the version of the middleware API */
1136bff9521Sdjm@openbsd.org uint32_t sk_api_version(void);
1146bff9521Sdjm@openbsd.org 
1156bff9521Sdjm@openbsd.org /* Enroll a U2F key (private key generation) */
116c312ca07Sdjm@openbsd.org int sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len,
117c54cd189Sdjm@openbsd.org     const char *application, uint8_t flags, const char *pin,
118c312ca07Sdjm@openbsd.org     struct sk_option **options, struct sk_enroll_response **enroll_response);
1196bff9521Sdjm@openbsd.org 
1206bff9521Sdjm@openbsd.org /* Sign a challenge */
121*34c5ef6eSdjm@openbsd.org int sk_sign(uint32_t alg, const uint8_t *data, size_t data_len,
1226bff9521Sdjm@openbsd.org     const char *application, const uint8_t *key_handle, size_t key_handle_len,
123c312ca07Sdjm@openbsd.org     uint8_t flags, const char *pin, struct sk_option **options,
124c312ca07Sdjm@openbsd.org     struct sk_sign_response **sign_response);
1256bff9521Sdjm@openbsd.org 
12614cea36dSdjm@openbsd.org /* Load resident keys */
127c312ca07Sdjm@openbsd.org int sk_load_resident_keys(const char *pin, struct sk_option **options,
12814cea36dSdjm@openbsd.org     struct sk_resident_key ***rks, size_t *nrks);
12914cea36dSdjm@openbsd.org 
1306bff9521Sdjm@openbsd.org static void skdebug(const char *func, const char *fmt, ...)
1316bff9521Sdjm@openbsd.org     __attribute__((__format__ (printf, 2, 3)));
1326bff9521Sdjm@openbsd.org 
1336bff9521Sdjm@openbsd.org static void
skdebug(const char * func,const char * fmt,...)1346bff9521Sdjm@openbsd.org skdebug(const char *func, const char *fmt, ...)
1356bff9521Sdjm@openbsd.org {
1366bff9521Sdjm@openbsd.org #if !defined(SK_STANDALONE)
1376bff9521Sdjm@openbsd.org 	char *msg;
1386bff9521Sdjm@openbsd.org 	va_list ap;
1396bff9521Sdjm@openbsd.org 
1406bff9521Sdjm@openbsd.org 	va_start(ap, fmt);
1416bff9521Sdjm@openbsd.org 	xvasprintf(&msg, fmt, ap);
1426bff9521Sdjm@openbsd.org 	va_end(ap);
14322a82712Sdjm@openbsd.org 	debug("%s: %s", func, msg);
1446bff9521Sdjm@openbsd.org 	free(msg);
1456bff9521Sdjm@openbsd.org #elif defined(SK_DEBUG)
1466bff9521Sdjm@openbsd.org 	va_list ap;
1476bff9521Sdjm@openbsd.org 
1486bff9521Sdjm@openbsd.org 	va_start(ap, fmt);
1496bff9521Sdjm@openbsd.org 	fprintf(stderr, "%s: ", func);
1506bff9521Sdjm@openbsd.org 	vfprintf(stderr, fmt, ap);
1516bff9521Sdjm@openbsd.org 	fputc('\n', stderr);
1526bff9521Sdjm@openbsd.org 	va_end(ap);
1536bff9521Sdjm@openbsd.org #else
1546bff9521Sdjm@openbsd.org 	(void)func; /* XXX */
1556bff9521Sdjm@openbsd.org 	(void)fmt; /* XXX */
1566bff9521Sdjm@openbsd.org #endif
1576bff9521Sdjm@openbsd.org }
1586bff9521Sdjm@openbsd.org 
1596bff9521Sdjm@openbsd.org uint32_t
sk_api_version(void)1606bff9521Sdjm@openbsd.org sk_api_version(void)
1616bff9521Sdjm@openbsd.org {
16259d01f1dSdjm@openbsd.org 	return SSH_SK_VERSION_MAJOR;
1636bff9521Sdjm@openbsd.org }
1646bff9521Sdjm@openbsd.org 
165642e06d0Sdjm@openbsd.org static struct sk_usbhid *
sk_open(const char * path)166642e06d0Sdjm@openbsd.org sk_open(const char *path)
1676bff9521Sdjm@openbsd.org {
168642e06d0Sdjm@openbsd.org 	struct sk_usbhid *sk;
1696bff9521Sdjm@openbsd.org 	int r;
1706bff9521Sdjm@openbsd.org 
171642e06d0Sdjm@openbsd.org 	if (path == NULL) {
172642e06d0Sdjm@openbsd.org 		skdebug(__func__, "path == NULL");
173642e06d0Sdjm@openbsd.org 		return NULL;
1746bff9521Sdjm@openbsd.org 	}
175642e06d0Sdjm@openbsd.org 	if ((sk = calloc(1, sizeof(*sk))) == NULL) {
176642e06d0Sdjm@openbsd.org 		skdebug(__func__, "calloc sk failed");
177642e06d0Sdjm@openbsd.org 		return NULL;
178642e06d0Sdjm@openbsd.org 	}
179642e06d0Sdjm@openbsd.org 	if ((sk->path = strdup(path)) == NULL) {
180642e06d0Sdjm@openbsd.org 		skdebug(__func__, "strdup path failed");
181642e06d0Sdjm@openbsd.org 		free(sk);
182642e06d0Sdjm@openbsd.org 		return NULL;
183642e06d0Sdjm@openbsd.org 	}
184642e06d0Sdjm@openbsd.org 	if ((sk->dev = fido_dev_new()) == NULL) {
185642e06d0Sdjm@openbsd.org 		skdebug(__func__, "fido_dev_new failed");
186642e06d0Sdjm@openbsd.org 		free(sk->path);
187642e06d0Sdjm@openbsd.org 		free(sk);
188642e06d0Sdjm@openbsd.org 		return NULL;
189642e06d0Sdjm@openbsd.org 	}
190642e06d0Sdjm@openbsd.org 	if ((r = fido_dev_open(sk->dev, sk->path)) != FIDO_OK) {
191642e06d0Sdjm@openbsd.org 		skdebug(__func__, "fido_dev_open %s failed: %s", sk->path,
1926bff9521Sdjm@openbsd.org 		    fido_strerr(r));
193642e06d0Sdjm@openbsd.org 		fido_dev_free(&sk->dev);
194642e06d0Sdjm@openbsd.org 		free(sk->path);
195642e06d0Sdjm@openbsd.org 		free(sk);
196642e06d0Sdjm@openbsd.org 		return NULL;
1976bff9521Sdjm@openbsd.org 	}
198642e06d0Sdjm@openbsd.org 	return sk;
1996bff9521Sdjm@openbsd.org }
2006bff9521Sdjm@openbsd.org 
201642e06d0Sdjm@openbsd.org static void
sk_close(struct sk_usbhid * sk)202642e06d0Sdjm@openbsd.org sk_close(struct sk_usbhid *sk)
203642e06d0Sdjm@openbsd.org {
204642e06d0Sdjm@openbsd.org 	if (sk == NULL)
205642e06d0Sdjm@openbsd.org 		return;
206642e06d0Sdjm@openbsd.org 	fido_dev_cancel(sk->dev); /* cancel any pending operation */
207642e06d0Sdjm@openbsd.org 	fido_dev_close(sk->dev);
208642e06d0Sdjm@openbsd.org 	fido_dev_free(&sk->dev);
209642e06d0Sdjm@openbsd.org 	free(sk->path);
210642e06d0Sdjm@openbsd.org 	free(sk);
211642e06d0Sdjm@openbsd.org }
212642e06d0Sdjm@openbsd.org 
213642e06d0Sdjm@openbsd.org static struct sk_usbhid **
sk_openv(const fido_dev_info_t * devlist,size_t ndevs,size_t * nopen)214642e06d0Sdjm@openbsd.org sk_openv(const fido_dev_info_t *devlist, size_t ndevs, size_t *nopen)
215642e06d0Sdjm@openbsd.org {
216642e06d0Sdjm@openbsd.org 	const fido_dev_info_t *di;
217642e06d0Sdjm@openbsd.org 	struct sk_usbhid **skv;
218642e06d0Sdjm@openbsd.org 	size_t i;
219642e06d0Sdjm@openbsd.org 
220642e06d0Sdjm@openbsd.org 	*nopen = 0;
221642e06d0Sdjm@openbsd.org 	if ((skv = calloc(ndevs, sizeof(*skv))) == NULL) {
222642e06d0Sdjm@openbsd.org 		skdebug(__func__, "calloc skv failed");
223642e06d0Sdjm@openbsd.org 		return NULL;
224642e06d0Sdjm@openbsd.org 	}
225642e06d0Sdjm@openbsd.org 	for (i = 0; i < ndevs; i++) {
226642e06d0Sdjm@openbsd.org 		if ((di = fido_dev_info_ptr(devlist, i)) == NULL)
227642e06d0Sdjm@openbsd.org 			skdebug(__func__, "fido_dev_info_ptr failed");
228642e06d0Sdjm@openbsd.org 		else if ((skv[*nopen] = sk_open(fido_dev_info_path(di))) == NULL)
229642e06d0Sdjm@openbsd.org 			skdebug(__func__, "sk_open failed");
230642e06d0Sdjm@openbsd.org 		else
231642e06d0Sdjm@openbsd.org 			(*nopen)++;
232642e06d0Sdjm@openbsd.org 	}
233642e06d0Sdjm@openbsd.org 	if (*nopen == 0) {
234642e06d0Sdjm@openbsd.org 		for (i = 0; i < ndevs; i++)
235642e06d0Sdjm@openbsd.org 			sk_close(skv[i]);
236642e06d0Sdjm@openbsd.org 		free(skv);
237642e06d0Sdjm@openbsd.org 		skv = NULL;
238642e06d0Sdjm@openbsd.org 	}
239642e06d0Sdjm@openbsd.org 
240642e06d0Sdjm@openbsd.org 	return skv;
241642e06d0Sdjm@openbsd.org }
242642e06d0Sdjm@openbsd.org 
243642e06d0Sdjm@openbsd.org static void
sk_closev(struct sk_usbhid ** skv,size_t nsk)244642e06d0Sdjm@openbsd.org sk_closev(struct sk_usbhid **skv, size_t nsk)
245642e06d0Sdjm@openbsd.org {
246642e06d0Sdjm@openbsd.org 	size_t i;
247642e06d0Sdjm@openbsd.org 
248642e06d0Sdjm@openbsd.org 	for (i = 0; i < nsk; i++)
249642e06d0Sdjm@openbsd.org 		sk_close(skv[i]);
250642e06d0Sdjm@openbsd.org 	free(skv);
251642e06d0Sdjm@openbsd.org }
252642e06d0Sdjm@openbsd.org 
2536bff9521Sdjm@openbsd.org static int
sk_touch_begin(struct sk_usbhid ** skv,size_t nsk)254642e06d0Sdjm@openbsd.org sk_touch_begin(struct sk_usbhid **skv, size_t nsk)
255642e06d0Sdjm@openbsd.org {
256642e06d0Sdjm@openbsd.org 	size_t i, ok = 0;
257642e06d0Sdjm@openbsd.org 	int r;
258642e06d0Sdjm@openbsd.org 
259642e06d0Sdjm@openbsd.org 	for (i = 0; i < nsk; i++)
260642e06d0Sdjm@openbsd.org 		if ((r = fido_dev_get_touch_begin(skv[i]->dev)) != FIDO_OK)
261642e06d0Sdjm@openbsd.org 			skdebug(__func__, "fido_dev_get_touch_begin %s failed:"
262642e06d0Sdjm@openbsd.org 			    " %s", skv[i]->path, fido_strerr(r));
263642e06d0Sdjm@openbsd.org 		else
264642e06d0Sdjm@openbsd.org 			ok++;
265642e06d0Sdjm@openbsd.org 
266642e06d0Sdjm@openbsd.org 	return ok ? 0 : -1;
267642e06d0Sdjm@openbsd.org }
268642e06d0Sdjm@openbsd.org 
269642e06d0Sdjm@openbsd.org static int
sk_touch_poll(struct sk_usbhid ** skv,size_t nsk,int * touch,size_t * idx)270642e06d0Sdjm@openbsd.org sk_touch_poll(struct sk_usbhid **skv, size_t nsk, int *touch, size_t *idx)
271642e06d0Sdjm@openbsd.org {
272642e06d0Sdjm@openbsd.org 	struct timespec ts_pause;
273642e06d0Sdjm@openbsd.org 	size_t npoll, i;
274642e06d0Sdjm@openbsd.org 	int r;
275642e06d0Sdjm@openbsd.org 
276642e06d0Sdjm@openbsd.org 	ts_pause.tv_sec = 0;
277642e06d0Sdjm@openbsd.org 	ts_pause.tv_nsec = POLL_SLEEP_NS;
278642e06d0Sdjm@openbsd.org 	nanosleep(&ts_pause, NULL);
279642e06d0Sdjm@openbsd.org 	npoll = nsk;
280642e06d0Sdjm@openbsd.org 	for (i = 0; i < nsk; i++) {
281642e06d0Sdjm@openbsd.org 		if (skv[i] == NULL)
282642e06d0Sdjm@openbsd.org 			continue; /* device discarded */
283642e06d0Sdjm@openbsd.org 		skdebug(__func__, "polling %s", skv[i]->path);
284642e06d0Sdjm@openbsd.org 		if ((r = fido_dev_get_touch_status(skv[i]->dev, touch,
285642e06d0Sdjm@openbsd.org 		    FIDO_POLL_MS)) != FIDO_OK) {
286642e06d0Sdjm@openbsd.org 			skdebug(__func__, "fido_dev_get_touch_status %s: %s",
287642e06d0Sdjm@openbsd.org 			    skv[i]->path, fido_strerr(r));
288642e06d0Sdjm@openbsd.org 			sk_close(skv[i]); /* discard device */
289642e06d0Sdjm@openbsd.org 			skv[i] = NULL;
290642e06d0Sdjm@openbsd.org 			if (--npoll == 0) {
291642e06d0Sdjm@openbsd.org 				skdebug(__func__, "no device left to poll");
292642e06d0Sdjm@openbsd.org 				return -1;
293642e06d0Sdjm@openbsd.org 			}
294642e06d0Sdjm@openbsd.org 		} else if (*touch) {
295642e06d0Sdjm@openbsd.org 			*idx = i;
296642e06d0Sdjm@openbsd.org 			return 0;
297642e06d0Sdjm@openbsd.org 		}
298642e06d0Sdjm@openbsd.org 	}
299642e06d0Sdjm@openbsd.org 	*touch = 0;
300642e06d0Sdjm@openbsd.org 	return 0;
301642e06d0Sdjm@openbsd.org }
302642e06d0Sdjm@openbsd.org 
303642e06d0Sdjm@openbsd.org /* Calculate SHA256(m) */
304642e06d0Sdjm@openbsd.org static int
sha256_mem(const void * m,size_t mlen,u_char * d,size_t dlen)305642e06d0Sdjm@openbsd.org sha256_mem(const void *m, size_t mlen, u_char *d, size_t dlen)
306642e06d0Sdjm@openbsd.org {
307642e06d0Sdjm@openbsd.org #ifdef WITH_OPENSSL
308642e06d0Sdjm@openbsd.org 	u_int mdlen;
309642e06d0Sdjm@openbsd.org #endif
310642e06d0Sdjm@openbsd.org 
311642e06d0Sdjm@openbsd.org 	if (dlen != 32)
312642e06d0Sdjm@openbsd.org 		return -1;
313642e06d0Sdjm@openbsd.org #ifdef WITH_OPENSSL
314642e06d0Sdjm@openbsd.org 	mdlen = dlen;
315642e06d0Sdjm@openbsd.org 	if (!EVP_Digest(m, mlen, d, &mdlen, EVP_sha256(), NULL))
316642e06d0Sdjm@openbsd.org 		return -1;
317642e06d0Sdjm@openbsd.org #else
318642e06d0Sdjm@openbsd.org 	SHA256Data(m, mlen, d);
319642e06d0Sdjm@openbsd.org #endif
320642e06d0Sdjm@openbsd.org 	return 0;
321642e06d0Sdjm@openbsd.org }
322642e06d0Sdjm@openbsd.org 
323642e06d0Sdjm@openbsd.org /* Check if the specified key handle exists on a given sk. */
324642e06d0Sdjm@openbsd.org static int
sk_try(const struct sk_usbhid * sk,const char * application,const uint8_t * key_handle,size_t key_handle_len)325642e06d0Sdjm@openbsd.org sk_try(const struct sk_usbhid *sk, const char *application,
326642e06d0Sdjm@openbsd.org     const uint8_t *key_handle, size_t key_handle_len)
3276bff9521Sdjm@openbsd.org {
3286bff9521Sdjm@openbsd.org 	fido_assert_t *assert = NULL;
329642e06d0Sdjm@openbsd.org 	/* generate an invalid signature on FIDO2 tokens */
330642e06d0Sdjm@openbsd.org 	const char *data = "";
331642e06d0Sdjm@openbsd.org 	uint8_t message[32];
3326bff9521Sdjm@openbsd.org 	int r = FIDO_ERR_INTERNAL;
3336bff9521Sdjm@openbsd.org 
334642e06d0Sdjm@openbsd.org 	if (sha256_mem(data, strlen(data), message, sizeof(message)) != 0) {
335642e06d0Sdjm@openbsd.org 		skdebug(__func__, "hash message failed");
336642e06d0Sdjm@openbsd.org 		goto out;
337642e06d0Sdjm@openbsd.org 	}
3386bff9521Sdjm@openbsd.org 	if ((assert = fido_assert_new()) == NULL) {
3396bff9521Sdjm@openbsd.org 		skdebug(__func__, "fido_assert_new failed");
3406bff9521Sdjm@openbsd.org 		goto out;
3416bff9521Sdjm@openbsd.org 	}
3426bff9521Sdjm@openbsd.org 	if ((r = fido_assert_set_clientdata_hash(assert, message,
343642e06d0Sdjm@openbsd.org 	    sizeof(message))) != FIDO_OK) {
3446bff9521Sdjm@openbsd.org 		skdebug(__func__, "fido_assert_set_clientdata_hash: %s",
3456bff9521Sdjm@openbsd.org 		    fido_strerr(r));
3466bff9521Sdjm@openbsd.org 		goto out;
3476bff9521Sdjm@openbsd.org 	}
3486bff9521Sdjm@openbsd.org 	if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) {
3496bff9521Sdjm@openbsd.org 		skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r));
3506bff9521Sdjm@openbsd.org 		goto out;
3516bff9521Sdjm@openbsd.org 	}
3526bff9521Sdjm@openbsd.org 	if ((r = fido_assert_allow_cred(assert, key_handle,
3536bff9521Sdjm@openbsd.org 	    key_handle_len)) != FIDO_OK) {
3546bff9521Sdjm@openbsd.org 		skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r));
3556bff9521Sdjm@openbsd.org 		goto out;
3566bff9521Sdjm@openbsd.org 	}
3576bff9521Sdjm@openbsd.org 	if ((r = fido_assert_set_up(assert, FIDO_OPT_FALSE)) != FIDO_OK) {
3586bff9521Sdjm@openbsd.org 		skdebug(__func__, "fido_assert_up: %s", fido_strerr(r));
3596bff9521Sdjm@openbsd.org 		goto out;
3606bff9521Sdjm@openbsd.org 	}
361642e06d0Sdjm@openbsd.org 	r = fido_dev_get_assert(sk->dev, assert, NULL);
3626bff9521Sdjm@openbsd.org 	skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r));
36301362cf7Sdjm@openbsd.org 	if (r == FIDO_ERR_USER_PRESENCE_REQUIRED) {
36401362cf7Sdjm@openbsd.org 		/* U2F tokens may return this */
36501362cf7Sdjm@openbsd.org 		r = FIDO_OK;
36601362cf7Sdjm@openbsd.org 	}
3676bff9521Sdjm@openbsd.org  out:
3686bff9521Sdjm@openbsd.org 	fido_assert_free(&assert);
3696bff9521Sdjm@openbsd.org 
3706bff9521Sdjm@openbsd.org 	return r != FIDO_OK ? -1 : 0;
3716bff9521Sdjm@openbsd.org }
3726bff9521Sdjm@openbsd.org 
373642e06d0Sdjm@openbsd.org 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)374642e06d0Sdjm@openbsd.org sk_select_by_cred(const fido_dev_info_t *devlist, size_t ndevs,
375642e06d0Sdjm@openbsd.org     const char *application, const uint8_t *key_handle, size_t key_handle_len)
3766bff9521Sdjm@openbsd.org {
377642e06d0Sdjm@openbsd.org 	struct sk_usbhid **skv, *sk;
378642e06d0Sdjm@openbsd.org 	size_t skvcnt, i;
3796bff9521Sdjm@openbsd.org 
380642e06d0Sdjm@openbsd.org 	if ((skv = sk_openv(devlist, ndevs, &skvcnt)) == NULL) {
381642e06d0Sdjm@openbsd.org 		skdebug(__func__, "sk_openv failed");
382c312ca07Sdjm@openbsd.org 		return NULL;
383c312ca07Sdjm@openbsd.org 	}
384b969072cSdjm@openbsd.org 	if (skvcnt == 1) {
385b969072cSdjm@openbsd.org 		sk = skv[0];
386b969072cSdjm@openbsd.org 		skv[0] = NULL;
387b969072cSdjm@openbsd.org 		goto out;
388b969072cSdjm@openbsd.org 	}
389642e06d0Sdjm@openbsd.org 	sk = NULL;
390b969072cSdjm@openbsd.org 	for (i = 0; i < skvcnt; i++) {
391642e06d0Sdjm@openbsd.org 		if (sk_try(skv[i], application, key_handle,
392642e06d0Sdjm@openbsd.org 		    key_handle_len) == 0) {
393642e06d0Sdjm@openbsd.org 			sk = skv[i];
394642e06d0Sdjm@openbsd.org 			skv[i] = NULL;
395642e06d0Sdjm@openbsd.org 			skdebug(__func__, "found key in %s", sk->path);
396642e06d0Sdjm@openbsd.org 			break;
397642e06d0Sdjm@openbsd.org 		}
398b969072cSdjm@openbsd.org 	}
399b969072cSdjm@openbsd.org  out:
400642e06d0Sdjm@openbsd.org 	sk_closev(skv, skvcnt);
401642e06d0Sdjm@openbsd.org 	return sk;
402642e06d0Sdjm@openbsd.org }
403642e06d0Sdjm@openbsd.org 
404642e06d0Sdjm@openbsd.org static struct sk_usbhid *
sk_select_by_touch(const fido_dev_info_t * devlist,size_t ndevs)405642e06d0Sdjm@openbsd.org sk_select_by_touch(const fido_dev_info_t *devlist, size_t ndevs)
406642e06d0Sdjm@openbsd.org {
407642e06d0Sdjm@openbsd.org 	struct sk_usbhid **skv, *sk;
408642e06d0Sdjm@openbsd.org 	struct timeval tv_start, tv_now, tv_delta;
409642e06d0Sdjm@openbsd.org 	size_t skvcnt, idx;
410642e06d0Sdjm@openbsd.org 	int touch, ms_remain;
411642e06d0Sdjm@openbsd.org 
412642e06d0Sdjm@openbsd.org 	if ((skv = sk_openv(devlist, ndevs, &skvcnt)) == NULL) {
413642e06d0Sdjm@openbsd.org 		skdebug(__func__, "sk_openv failed");
414c312ca07Sdjm@openbsd.org 		return NULL;
415c312ca07Sdjm@openbsd.org 	}
416642e06d0Sdjm@openbsd.org 	sk = NULL;
417642e06d0Sdjm@openbsd.org 	if (skvcnt < 2) {
418642e06d0Sdjm@openbsd.org 		if (skvcnt == 1) {
419642e06d0Sdjm@openbsd.org 			/* single candidate */
420642e06d0Sdjm@openbsd.org 			sk = skv[0];
421642e06d0Sdjm@openbsd.org 			skv[0] = NULL;
422c312ca07Sdjm@openbsd.org 		}
423642e06d0Sdjm@openbsd.org 		goto out;
424642e06d0Sdjm@openbsd.org 	}
425ce178be0SDamien Miller #ifndef HAVE_FIDO_DEV_GET_TOUCH_STATUS
426ce178be0SDamien Miller 	skdebug(__func__, "libfido2 version does not support a feature needed for multiple tokens. Please upgrade to >=1.5.0");
427ce178be0SDamien Miller 	goto out;
428ce178be0SDamien Miller #endif
429ce178be0SDamien Miller 
430642e06d0Sdjm@openbsd.org 	if (sk_touch_begin(skv, skvcnt) == -1) {
431642e06d0Sdjm@openbsd.org 		skdebug(__func__, "sk_touch_begin failed");
432642e06d0Sdjm@openbsd.org 		goto out;
433642e06d0Sdjm@openbsd.org 	}
434642e06d0Sdjm@openbsd.org 	monotime_tv(&tv_start);
435642e06d0Sdjm@openbsd.org 	do {
436642e06d0Sdjm@openbsd.org 		if (sk_touch_poll(skv, skvcnt, &touch, &idx) == -1) {
437642e06d0Sdjm@openbsd.org 			skdebug(__func__, "sk_touch_poll failed");
438642e06d0Sdjm@openbsd.org 			goto out;
439642e06d0Sdjm@openbsd.org 		}
440642e06d0Sdjm@openbsd.org 		if (touch) {
441642e06d0Sdjm@openbsd.org 			sk = skv[idx];
442642e06d0Sdjm@openbsd.org 			skv[idx] = NULL;
443642e06d0Sdjm@openbsd.org 			goto out;
444642e06d0Sdjm@openbsd.org 		}
445642e06d0Sdjm@openbsd.org 		monotime_tv(&tv_now);
446642e06d0Sdjm@openbsd.org 		timersub(&tv_now, &tv_start, &tv_delta);
447642e06d0Sdjm@openbsd.org 		ms_remain = SELECT_MS - tv_delta.tv_sec * 1000 -
448642e06d0Sdjm@openbsd.org 		    tv_delta.tv_usec / 1000;
449642e06d0Sdjm@openbsd.org 	} while (ms_remain >= FIDO_POLL_MS);
450642e06d0Sdjm@openbsd.org 	skdebug(__func__, "timeout");
451642e06d0Sdjm@openbsd.org out:
452642e06d0Sdjm@openbsd.org 	sk_closev(skv, skvcnt);
453642e06d0Sdjm@openbsd.org 	return sk;
454642e06d0Sdjm@openbsd.org }
455642e06d0Sdjm@openbsd.org 
456642e06d0Sdjm@openbsd.org static struct sk_usbhid *
sk_probe(const char * application,const uint8_t * key_handle,size_t key_handle_len)457642e06d0Sdjm@openbsd.org sk_probe(const char *application, const uint8_t *key_handle,
458642e06d0Sdjm@openbsd.org     size_t key_handle_len)
459642e06d0Sdjm@openbsd.org {
460642e06d0Sdjm@openbsd.org 	struct sk_usbhid *sk;
461642e06d0Sdjm@openbsd.org 	fido_dev_info_t *devlist;
462642e06d0Sdjm@openbsd.org 	size_t ndevs;
463642e06d0Sdjm@openbsd.org 	int r;
464c312ca07Sdjm@openbsd.org 
4656bff9521Sdjm@openbsd.org 	if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) {
4666bff9521Sdjm@openbsd.org 		skdebug(__func__, "fido_dev_info_new failed");
467642e06d0Sdjm@openbsd.org 		return NULL;
4686bff9521Sdjm@openbsd.org 	}
4696bff9521Sdjm@openbsd.org 	if ((r = fido_dev_info_manifest(devlist, MAX_FIDO_DEVICES,
470642e06d0Sdjm@openbsd.org 	    &ndevs)) != FIDO_OK) {
471642e06d0Sdjm@openbsd.org 		skdebug(__func__, "fido_dev_info_manifest failed: %s",
472642e06d0Sdjm@openbsd.org 		    fido_strerr(r));
4736bff9521Sdjm@openbsd.org 		fido_dev_info_free(&devlist, MAX_FIDO_DEVICES);
474642e06d0Sdjm@openbsd.org 		return NULL;
475642e06d0Sdjm@openbsd.org 	}
476642e06d0Sdjm@openbsd.org 	skdebug(__func__, "%zu device(s) detected", ndevs);
477642e06d0Sdjm@openbsd.org 	if (ndevs == 0) {
478642e06d0Sdjm@openbsd.org 		sk = NULL;
479642e06d0Sdjm@openbsd.org 	} else if (application != NULL && key_handle != NULL) {
480642e06d0Sdjm@openbsd.org 		skdebug(__func__, "selecting sk by cred");
481642e06d0Sdjm@openbsd.org 		sk = sk_select_by_cred(devlist, ndevs, application, key_handle,
482642e06d0