1/*	$NetBSD: sk-usbhid.c,v 1.9 2023/10/25 20:19:57 christos Exp $	*/
2/* $OpenBSD: sk-usbhid.c,v 1.46 2023/03/28 06:12:38 dtucker Exp $ */
3
4/*
5 * Copyright (c) 2019 Markus Friedl
6 * Copyright (c) 2020 Pedro Martelletto
7 *
8 * Permission to use, copy, modify, and distribute this software for any
9 * purpose with or without fee is hereby granted, provided that the above
10 * copyright notice and this permission notice appear in all copies.
11 *
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 */
20#include "includes.h"
21__RCSID("$NetBSD: sk-usbhid.c,v 1.9 2023/10/25 20:19:57 christos Exp $");
22
23#include <stdint.h>
24#include <stdlib.h>
25#include <string.h>
26#include <stdio.h>
27#include <stddef.h>
28#include <stdarg.h>
29#include <time.h>
30
31#ifdef WITH_OPENSSL
32#include <openssl/opensslv.h>
33#include <openssl/crypto.h>
34#include <openssl/bn.h>
35#include <openssl/ec.h>
36#include <openssl/ecdsa.h>
37#include <openssl/evp.h>
38#endif /* WITH_OPENSSL */
39
40#include <fido.h>
41#include <fido/credman.h>
42
43#ifndef SK_STANDALONE
44# include "log.h"
45# include "xmalloc.h"
46# include "misc.h"
47/*
48 * If building as part of OpenSSH, then rename exported functions.
49 * This must be done before including sk-api.h.
50 */
51# define sk_api_version		ssh_sk_api_version
52# define sk_enroll		ssh_sk_enroll
53# define sk_sign		ssh_sk_sign
54# define sk_load_resident_keys	ssh_sk_load_resident_keys
55#endif /* !SK_STANDALONE */
56
57#include "sk-api.h"
58
59/* #define SK_DEBUG 1 */
60
61#ifdef SK_DEBUG
62#define SSH_FIDO_INIT_ARG	FIDO_DEBUG
63#else
64#define SSH_FIDO_INIT_ARG	0
65#endif
66
67#define MAX_FIDO_DEVICES	8
68#define FIDO_POLL_MS		50
69#define SELECT_MS		15000
70#define POLL_SLEEP_NS		200000000
71
72#ifndef FIDO_ERR_OPERATION_DENIED
73#define FIDO_ERR_OPERATION_DENIED 0x27
74#endif
75
76struct sk_usbhid {
77	fido_dev_t *dev;
78	char *path;
79};
80
81/* Return the version of the middleware API */
82uint32_t sk_api_version(void);
83
84/* Enroll a U2F key (private key generation) */
85int sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len,
86    const char *application, uint8_t flags, const char *pin,
87    struct sk_option **options, struct sk_enroll_response **enroll_response);
88
89/* Sign a challenge */
90int sk_sign(uint32_t alg, const uint8_t *data, size_t data_len,
91    const char *application, const uint8_t *key_handle, size_t key_handle_len,
92    uint8_t flags, const char *pin, struct sk_option **options,
93    struct sk_sign_response **sign_response);
94
95/* Load resident keys */
96int sk_load_resident_keys(const char *pin, struct sk_option **options,
97    struct sk_resident_key ***rks, size_t *nrks);
98
99static void skdebug(const char *func, const char *fmt, ...)
100    __attribute__((__format__ (printf, 2, 3)));
101
102static void
103skdebug(const char *func, const char *fmt, ...)
104{
105#if !defined(SK_STANDALONE)
106	char *msg;
107	va_list ap;
108
109	va_start(ap, fmt);
110	xvasprintf(&msg, fmt, ap);
111	va_end(ap);
112	debug("%s: %s", func, msg);
113	free(msg);
114#elif defined(SK_DEBUG)
115	va_list ap;
116
117	va_start(ap, fmt);
118	fprintf(stderr, "%s: ", func);
119	vfprintf(stderr, fmt, ap);
120	fputc('\n', stderr);
121	va_end(ap);
122#else
123	(void)func; /* XXX */
124	(void)fmt; /* XXX */
125#endif
126}
127
128uint32_t
129sk_api_version(void)
130{
131	return SSH_SK_VERSION_MAJOR;
132}
133
134static struct sk_usbhid *
135sk_open(const char *path)
136{
137	struct sk_usbhid *sk;
138	int r;
139
140	if (path == NULL) {
141		skdebug(__func__, "path == NULL");
142		return NULL;
143	}
144	if ((sk = calloc(1, sizeof(*sk))) == NULL) {
145		skdebug(__func__, "calloc sk failed");
146		return NULL;
147	}
148	if ((sk->path = strdup(path)) == NULL) {
149		skdebug(__func__, "strdup path failed");
150		free(sk);
151		return NULL;
152	}
153	if ((sk->dev = fido_dev_new()) == NULL) {
154		skdebug(__func__, "fido_dev_new failed");
155		free(sk->path);
156		free(sk);
157		return NULL;
158	}
159	if ((r = fido_dev_open(sk->dev, sk->path)) != FIDO_OK) {
160		skdebug(__func__, "fido_dev_open %s failed: %s", sk->path,
161		    fido_strerr(r));
162		fido_dev_free(&sk->dev);
163		free(sk->path);
164		free(sk);
165		return NULL;
166	}
167	return sk;
168}
169
170static void
171sk_close(struct sk_usbhid *sk)
172{
173	if (sk == NULL)
174		return;
175	fido_dev_cancel(sk->dev); /* cancel any pending operation */
176	fido_dev_close(sk->dev);
177	fido_dev_free(&sk->dev);
178	free(sk->path);
179	free(sk);
180}
181
182static struct sk_usbhid **
183sk_openv(const fido_dev_info_t *devlist, size_t ndevs, size_t *nopen)
184{
185	const fido_dev_info_t *di;
186	struct sk_usbhid **skv;
187	size_t i;
188
189	*nopen = 0;
190	if ((skv = calloc(ndevs, sizeof(*skv))) == NULL) {
191		skdebug(__func__, "calloc skv failed");
192		return NULL;
193	}
194	for (i = 0; i < ndevs; i++) {
195		if ((di = fido_dev_info_ptr(devlist, i)) == NULL)
196			skdebug(__func__, "fido_dev_info_ptr failed");
197		else if ((skv[*nopen] = sk_open(fido_dev_info_path(di))) == NULL)
198			skdebug(__func__, "sk_open failed");
199		else
200			(*nopen)++;
201	}
202	if (*nopen == 0) {
203		for (i = 0; i < ndevs; i++)
204			sk_close(skv[i]);
205		free(skv);
206		skv = NULL;
207	}
208
209	return skv;
210}
211
212static void
213sk_closev(struct sk_usbhid **skv, size_t nsk)
214{
215	size_t i;
216
217	for (i = 0; i < nsk; i++)
218		sk_close(skv[i]);
219	free(skv);
220}
221
222static int
223sk_touch_begin(struct sk_usbhid **skv, size_t nsk)
224{
225	size_t i, ok = 0;
226	int r;
227
228	for (i = 0; i < nsk; i++)
229		if ((r = fido_dev_get_touch_begin(skv[i]->dev)) != FIDO_OK)
230			skdebug(__func__, "fido_dev_get_touch_begin %s failed:"
231			    " %s", skv[i]->path, fido_strerr(r));
232		else
233			ok++;
234
235	return ok ? 0 : -1;
236}
237
238static int
239sk_touch_poll(struct sk_usbhid **skv, size_t nsk, int *touch, size_t *idx)
240{
241	struct timespec ts_pause;
242	size_t npoll, i;
243	int r;
244
245	ts_pause.tv_sec = 0;
246	ts_pause.tv_nsec = POLL_SLEEP_NS;
247	nanosleep(&ts_pause, NULL);
248	npoll = nsk;
249	for (i = 0; i < nsk; i++) {
250		if (skv[i] == NULL)
251			continue; /* device discarded */
252		skdebug(__func__, "polling %s", skv[i]->path);
253		if ((r = fido_dev_get_touch_status(skv[i]->dev, touch,
254		    FIDO_POLL_MS)) != FIDO_OK) {
255			skdebug(__func__, "fido_dev_get_touch_status %s: %s",
256			    skv[i]->path, fido_strerr(r));
257			sk_close(skv[i]); /* discard device */
258			skv[i] = NULL;
259			if (--npoll == 0) {
260				skdebug(__func__, "no device left to poll");
261				return -1;
262			}
263		} else if (*touch) {
264			*idx = i;
265			return 0;
266		}
267	}
268	*touch = 0;
269	return 0;
270}
271
272/* Check if the specified key handle exists on a given sk. */
273static int
274sk_try(const struct sk_usbhid *sk, const char *application,
275    const uint8_t *key_handle, size_t key_handle_len)
276{
277	fido_assert_t *assert = NULL;
278	int r = FIDO_ERR_INTERNAL;
279	uint8_t message[32];
280
281	memset(message, '\0', sizeof(message));
282	if ((assert = fido_assert_new()) == NULL) {
283		skdebug(__func__, "fido_assert_new failed");
284		goto out;
285	}
286	/* generate an invalid signature on FIDO2 tokens */
287	if ((r = fido_assert_set_clientdata(assert, message,
288	    sizeof(message))) != FIDO_OK) {
289		skdebug(__func__, "fido_assert_set_clientdata: %s",
290		    fido_strerr(r));
291		goto out;
292	}
293	if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) {
294		skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r));
295		goto out;
296	}
297	if ((r = fido_assert_allow_cred(assert, key_handle,
298	    key_handle_len)) != FIDO_OK) {
299		skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r));
300		goto out;
301	}
302	if ((r = fido_assert_set_up(assert, FIDO_OPT_FALSE)) != FIDO_OK) {
303		skdebug(__func__, "fido_assert_up: %s", fido_strerr(r));
304		goto out;
305	}
306	r = fido_dev_get_assert(sk->dev, assert, NULL);
307	skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r));
308	if (r == FIDO_ERR_USER_PRESENCE_REQUIRED) {
309		/* U2F tokens may return this */
310		r = FIDO_OK;
311	}
312 out:
313	fido_assert_free(&assert);
314
315	return r != FIDO_OK ? -1 : 0;
316}
317
318static int
319check_sk_options(fido_dev_t *dev, const char *opt, int *ret)
320{
321	fido_cbor_info_t *info;
322	char * const *name;
323	const bool *value;
324	size_t len, i;
325	int r;
326
327	*ret = -1;
328
329	if (!fido_dev_is_fido2(dev)) {
330		skdebug(__func__, "device is not fido2");
331		return 0;
332	}
333	if ((info = fido_cbor_info_new()) == NULL) {
334		skdebug(__func__, "fido_cbor_info_new failed");
335		return -1;
336	}
337	if ((r = fido_dev_get_cbor_info(dev, info)) != FIDO_OK) {
338		skdebug(__func__, "fido_dev_get_cbor_info: %s", fido_strerr(r));
339		fido_cbor_info_free(&info);
340		return -1;
341	}
342	name = fido_cbor_info_options_name_ptr(info);
343	value = fido_cbor_info_options_value_ptr(info);
344	len = fido_cbor_info_options_len(info);
345	for (i = 0; i < len; i++) {
346		if (!strcmp(name[i], opt)) {
347			*ret = value[i];
348			break;
349		}
350	}
351	fido_cbor_info_free(&info);
352	if (*ret == -1)
353		skdebug(__func__, "option %s is unknown", opt);
354	else
355		skdebug(__func__, "option %s is %s", opt, *ret ? "on" : "off");
356
357	return 0;
358}
359
360static struct sk_usbhid *
361sk_select_by_cred(const fido_dev_info_t *devlist, size_t ndevs,
362    const char *application, const uint8_t *key_handle, size_t key_handle_len)
363{
364	struct sk_usbhid **skv, *sk;
365	size_t skvcnt, i;
366	int internal_uv;
367
368	if ((skv = sk_openv(devlist, ndevs, &skvcnt)) == NULL) {
369		skdebug(__func__, "sk_openv failed");
370		return NULL;
371	}
372	if (skvcnt == 1 && check_sk_options(skv[0]->dev, "uv",
373	    &internal_uv) == 0 && internal_uv != -1) {
374		sk = skv[0];
375		skv[0] = NULL;
376		goto out;
377	}
378	sk = NULL;
379	for (i = 0; i < skvcnt; i++) {
380		if (sk_try(skv[i], application, key_handle,
381		    key_handle_len) == 0) {
382			sk = skv[i];
383			skv[i] = NULL;
384			skdebug(__func__, "found key in %s", sk->path);
385			break;
386		}
387	}
388 out:
389	sk_closev(skv, skvcnt);
390	return sk;
391}
392
393static struct sk_usbhid *
394sk_select_by_touch(const fido_dev_info_t *devlist, size_t ndevs)
395{
396	struct sk_usbhid **skv, *sk;
397	struct timeval tv_start, tv_now, tv_delta;
398	size_t skvcnt, idx;
399	int touch, ms_remain;
400
401	if ((skv = sk_openv(devlist, ndevs, &skvcnt)) == NULL) {
402		skdebug(__func__, "sk_openv failed");
403		return NULL;
404	}
405	sk = NULL;
406	if (skvcnt < 2) {
407		if (skvcnt == 1) {
408			/* single candidate */
409			sk = skv[0];
410			skv[0] = NULL;
411		}
412		goto out;
413	}
414	if (sk_touch_begin(skv, skvcnt) == -1) {
415		skdebug(__func__, "sk_touch_begin failed");
416		goto out;
417	}
418	monotime_tv(&tv_start);
419	do {
420		if (sk_touch_poll(skv, skvcnt, &touch, &idx) == -1) {
421			skdebug(__func__, "sk_touch_poll failed");
422			goto out;
423		}
424		if (touch) {
425			sk = skv[idx];
426			skv[idx] = NULL;
427			goto out;
428		}
429		monotime_tv(&tv_now);
430		timersub(&tv_now, &tv_start, &tv_delta);
431		ms_remain = SELECT_MS - tv_delta.tv_sec * 1000 -
432		    tv_delta.tv_usec / 1000;
433	} while (ms_remain >= FIDO_POLL_MS);
434	skdebug(__func__, "timeout");
435out:
436	sk_closev(skv, skvcnt);
437	return sk;
438}
439
440static struct sk_usbhid *
441sk_probe(const char *application, const uint8_t *key_handle,
442    size_t key_handle_len, int probe_resident)
443{
444	struct sk_usbhid *sk;
445	fido_dev_info_t *devlist;
446	size_t ndevs;
447	int r;
448
449	if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) {
450		skdebug(__func__, "fido_dev_info_new failed");
451		return NULL;
452	}
453	if ((r = fido_dev_info_manifest(devlist, MAX_FIDO_DEVICES,
454	    &ndevs)) != FIDO_OK) {
455		skdebug(__func__, "fido_dev_info_manifest failed: %s",
456		    fido_strerr(r));
457		fido_dev_info_free(&devlist, MAX_FIDO_DEVICES);
458		return NULL;
459	}
460	skdebug(__func__, "%zu device(s) detected", ndevs);
461	if (ndevs == 0) {
462		sk = NULL;
463	} else if (application != NULL && key_handle != NULL) {
464		skdebug(__func__, "selecting sk by cred");
465		sk = sk_select_by_cred(devlist, ndevs, application, key_handle,
466		    key_handle_len);
467	} else {
468		skdebug(__func__, "selecting sk by touch");
469		sk = sk_select_by_touch(devlist, ndevs);
470	}
471	fido_dev_info_free(&devlist, MAX_FIDO_DEVICES);
472	return sk;
473}
474
475#ifdef WITH_OPENSSL
476/*
477 * The key returned via fido_cred_pubkey_ptr() is in affine coordinates,
478 * but the API expects a SEC1 octet string.
479 */
480static int
481pack_public_key_ecdsa(const fido_cred_t *cred,
482    struct sk_enroll_response *response)
483{
484	const uint8_t *ptr;
485	BIGNUM *x = NULL, *y = NULL;
486	EC_POINT *q = NULL;
487	EC_GROUP *g = NULL;
488	int ret = -1;
489
490	response->public_key = NULL;
491	response->public_key_len = 0;
492
493	if ((x = BN_new()) == NULL ||
494	    (y = BN_new()) == NULL ||
495	    (g = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)) == NULL ||
496	    (q = EC_POINT_new(g)) == NULL) {
497		skdebug(__func__, "libcrypto setup failed");
498		goto out;
499	}
500	if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) {
501		skdebug(__func__, "fido_cred_pubkey_ptr failed");
502		goto out;
503	}
504	if (fido_cred_pubkey_len(cred) != 64) {
505		skdebug(__func__, "bad fido_cred_pubkey_len %zu",
506		    fido_cred_pubkey_len(cred));
507		goto out;
508	}
509
510	if (BN_bin2bn(ptr, 32, x) == NULL ||
511	    BN_bin2bn(ptr + 32, 32, y) == NULL) {
512		skdebug(__func__, "BN_bin2bn failed");
513		goto out;
514	}
515	if (EC_POINT_set_affine_coordinates_GFp(g, q, x, y, NULL) != 1) {
516		skdebug(__func__, "EC_POINT_set_affine_coordinates_GFp failed");
517		goto out;
518	}
519	response->public_key_len = EC_POINT_point2oct(g, q,
520	    POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL);
521	if (response->public_key_len == 0 || response->public_key_len > 2048) {
522		skdebug(__func__, "bad pubkey length %zu",
523		    response->public_key_len);
524		goto out;
525	}
526	if ((response->public_key = malloc(response->public_key_len)) == NULL) {
527		skdebug(__func__, "malloc pubkey failed");
528		goto out;
529	}
530	if (EC_POINT_point2oct(g, q, POINT_CONVERSION_UNCOMPRESSED,
531	    response->public_key, response->public_key_len, NULL) == 0) {
532		skdebug(__func__, "EC_POINT_point2oct failed");
533		goto out;
534	}
535	/* success */
536	ret = 0;
537 out:
538	if (ret != 0 && response->public_key != NULL) {
539		memset(response->public_key, 0, response->public_key_len);
540		free(response->public_key);
541		response->public_key = NULL;
542	}
543	EC_POINT_free(q);
544	EC_GROUP_free(g);
545	BN_clear_free(x);
546	BN_clear_free(y);
547	return ret;
548}
549#endif /* WITH_OPENSSL */
550
551static int
552pack_public_key_ed25519(const fido_cred_t *cred,
553    struct sk_enroll_response *response)
554{
555	const uint8_t *ptr;
556	size_t len;
557	int ret = -1;
558
559	response->public_key = NULL;
560	response->public_key_len = 0;
561
562	if ((len = fido_cred_pubkey_len(cred)) != 32) {
563		skdebug(__func__, "bad fido_cred_pubkey_len len %zu", len);
564		goto out;
565	}
566	if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) {
567		skdebug(__func__, "fido_cred_pubkey_ptr failed");
568		goto out;
569	}
570	response->public_key_len = len;
571	if ((response->public_key = malloc(response->public_key_len)) == NULL) {
572		skdebug(__func__, "malloc pubkey failed");
573		goto out;
574	}
575	memcpy(response->public_key, ptr, len);
576	ret = 0;
577 out:
578	if (ret != 0)
579		free(response->public_key);
580	return ret;
581}
582
583static int
584pack_public_key(uint32_t alg, const fido_cred_t *cred,
585    struct sk_enroll_response *response)
586{
587	switch(alg) {
588#ifdef WITH_OPENSSL
589	case SSH_SK_ECDSA:
590		return pack_public_key_ecdsa(cred, response);
591#endif /* WITH_OPENSSL */
592	case SSH_SK_ED25519:
593		return pack_public_key_ed25519(cred, response);
594	default:
595		return -1;
596	}
597}
598
599static int
600fidoerr_to_skerr(int fidoerr)
601{
602	switch (fidoerr) {
603	case FIDO_ERR_UNSUPPORTED_OPTION:
604	case FIDO_ERR_UNSUPPORTED_ALGORITHM:
605		return SSH_SK_ERR_UNSUPPORTED;
606	case FIDO_ERR_PIN_REQUIRED:
607	case FIDO_ERR_PIN_INVALID:
608	case FIDO_ERR_OPERATION_DENIED:
609		return SSH_SK_ERR_PIN_REQUIRED;
610	default:
611		return -1;
612	}
613}
614
615static int
616check_enroll_options(struct sk_option **options, char **devicep,
617    uint8_t *user_id, size_t user_id_len)
618{
619	size_t i;
620
621	if (options == NULL)
622		return 0;
623	for (i = 0; options[i] != NULL; i++) {
624		if (strcmp(options[i]->name, "device") == 0) {
625			if ((*devicep = strdup(options[i]->value)) == NULL) {
626				skdebug(__func__, "strdup device failed");
627				return -1;
628			}
629			skdebug(__func__, "requested device %s", *devicep);
630		} else if (strcmp(options[i]->name, "user") == 0) {
631			if (strlcpy((char *)user_id, options[i]->value, user_id_len) >=
632			    user_id_len) {
633				skdebug(__func__, "user too long");
634				return -1;
635			}
636			skdebug(__func__, "requested user %s",
637			    (char *)user_id);
638		} else {
639			skdebug(__func__, "requested unsupported option %s",
640			    options[i]->name);
641			if (options[i]->required) {
642				skdebug(__func__, "unknown required option");
643				return -1;
644			}
645		}
646	}
647	return 0;
648}
649
650static int
651key_lookup(fido_dev_t *dev, const char *application, const uint8_t *user_id,
652    size_t user_id_len, const char *pin)
653{
654	fido_assert_t *assert = NULL;
655	uint8_t message[32];
656	int r = FIDO_ERR_INTERNAL;
657	int sk_supports_uv, uv;
658	size_t i;
659
660	memset(message, '\0', sizeof(message));
661	if ((assert = fido_assert_new()) == NULL) {
662		skdebug(__func__, "fido_assert_new failed");
663		goto out;
664	}
665	/* generate an invalid signature on FIDO2 tokens */
666	if ((r = fido_assert_set_clientdata(assert, message,
667	    sizeof(message))) != FIDO_OK) {
668		skdebug(__func__, "fido_assert_set_clientdata: %s",
669		    fido_strerr(r));
670		goto out;
671	}
672	if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) {
673		skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r));
674		goto out;
675	}
676	if ((r = fido_assert_set_up(assert, FIDO_OPT_FALSE)) != FIDO_OK) {
677		skdebug(__func__, "fido_assert_set_up: %s", fido_strerr(r));
678		goto out;
679	}
680	uv = FIDO_OPT_OMIT;
681	if (pin == NULL && check_sk_options(dev, "uv", &sk_supports_uv) == 0 &&
682	    sk_supports_uv != -1)
683		uv = FIDO_OPT_TRUE;
684	if ((r = fido_assert_set_uv(assert, uv)) != FIDO_OK) {
685		skdebug(__func__, "fido_assert_set_uv: %s", fido_strerr(r));
686		goto out;
687	}
688	if ((r = fido_dev_get_assert(dev, assert, pin)) != FIDO_OK) {
689		skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r));
690		goto out;
691	}
692	r = FIDO_ERR_NO_CREDENTIALS;
693	skdebug(__func__, "%zu signatures returned", fido_assert_count(assert));
694	for (i = 0; i < fido_assert_count(assert); i++) {
695		if (fido_assert_user_id_len(assert, i) == user_id_len &&
696		    memcmp(fido_assert_user_id_ptr(assert, i), user_id,
697		    user_id_len) == 0) {
698			skdebug(__func__, "credential exists");
699			r = FIDO_OK;
700			goto out;
701		}
702	}
703 out:
704	fido_assert_free(&assert);
705
706	return r;
707}
708
709int
710sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len,
711    const char *application, uint8_t flags, const char *pin,
712    struct sk_option **options, struct sk_enroll_response **enroll_response)
713{
714	fido_cred_t *cred = NULL;
715	const uint8_t *ptr;
716	uint8_t user_id[32];
717	struct sk_usbhid *sk = NULL;
718	struct sk_enroll_response *response = NULL;
719	size_t len;
720	int credprot;
721	int cose_alg;
722	int ret = SSH_SK_ERR_GENERAL;
723	int r;
724	char *device = NULL;
725
726	fido_init(SSH_FIDO_INIT_ARG);
727
728	if (enroll_response == NULL) {
729		skdebug(__func__, "enroll_response == NULL");
730		goto out;
731	}
732	*enroll_response = NULL;
733	memset(user_id, 0, sizeof(user_id));
734	if (check_enroll_options(options, &device, user_id,
735	    sizeof(user_id)) != 0)
736		goto out; /* error already logged */
737
738	switch(alg) {
739#ifdef WITH_OPENSSL
740	case SSH_SK_ECDSA:
741		cose_alg = COSE_ES256;
742		break;
743#endif /* WITH_OPENSSL */
744	case SSH_SK_ED25519:
745		cose_alg = COSE_EDDSA;
746		break;
747	default:
748		skdebug(__func__, "unsupported key type %d", alg);
749		goto out;
750	}
751	if (device != NULL)
752		sk = sk_open(device);
753	else
754		sk = sk_probe(NULL, NULL, 0, 0);
755	if (sk == NULL) {
756		ret = SSH_SK_ERR_DEVICE_NOT_FOUND;
757		skdebug(__func__, "failed to find sk");
758		goto out;
759	}
760	skdebug(__func__, "using device %s", sk->path);
761	if ((flags & SSH_SK_RESIDENT_KEY) != 0 &&
762	    (flags & SSH_SK_FORCE_OPERATION) == 0 &&
763	    (r = key_lookup(sk->dev, application, user_id, sizeof(user_id),
764	    pin)) != FIDO_ERR_NO_CREDENTIALS) {
765		if (r != FIDO_OK) {
766			ret = fidoerr_to_skerr(r);
767			skdebug(__func__, "key_lookup failed");
768		} else {
769			ret = SSH_SK_ERR_CREDENTIAL_EXISTS;
770			skdebug(__func__, "key exists");
771		}
772		goto out;
773	}
774	if ((cred = fido_cred_new()) == NULL) {
775		skdebug(__func__, "fido_cred_new failed");
776		goto out;
777	}
778	if ((r = fido_cred_set_type(cred, cose_alg)) != FIDO_OK) {
779		skdebug(__func__, "fido_cred_set_type: %s", fido_strerr(r));
780		goto out;
781	}
782	if ((r = fido_cred_set_clientdata(cred,
783	    challenge, challenge_len)) != FIDO_OK) {
784		skdebug(__func__, "fido_cred_set_clientdata: %s",
785		    fido_strerr(r));
786		goto out;
787	}
788	if ((r = fido_cred_set_rk(cred, (flags & SSH_SK_RESIDENT_KEY) != 0 ?
789	    FIDO_OPT_TRUE : FIDO_OPT_OMIT)) != FIDO_OK) {
790		skdebug(__func__, "fido_cred_set_rk: %s", fido_strerr(r));
791		goto out;
792	}
793	if ((r = fido_cred_set_user(cred, user_id, sizeof(user_id),
794	    "openssh", "openssh", NULL)) != FIDO_OK) {
795		skdebug(__func__, "fido_cred_set_user: %s", fido_strerr(r));
796		goto out;
797	}
798	if ((r = fido_cred_set_rp(cred, application, NULL)) != FIDO_OK) {
799		skdebug(__func__, "fido_cred_set_rp: %s", fido_strerr(r));
800		goto out;
801	}
802	if ((flags & (SSH_SK_RESIDENT_KEY|SSH_SK_USER_VERIFICATION_REQD)) != 0) {
803		if (!fido_dev_supports_cred_prot(sk->dev)) {
804			skdebug(__func__, "%s does not support credprot, "
805			    "refusing to create unprotected "
806			    "resident/verify-required key", sk->path);
807			ret = SSH_SK_ERR_UNSUPPORTED;
808			goto out;
809		}
810		if ((flags & SSH_SK_USER_VERIFICATION_REQD))
811			credprot = FIDO_CRED_PROT_UV_REQUIRED;
812		else
813			credprot = FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID;
814
815		if ((r = fido_cred_set_prot(cred, credprot)) != FIDO_OK) {
816			skdebug(__func__, "fido_cred_set_prot: %s",
817			    fido_strerr(r));
818			ret = fidoerr_to_skerr(r);
819			goto out;
820		}
821	}
822	if ((r = fido_dev_make_cred(sk->dev, cred, pin)) != FIDO_OK) {
823		skdebug(__func__, "fido_dev_make_cred: %s", fido_strerr(r));
824		ret = fidoerr_to_skerr(r);
825		goto out;
826	}
827	if (fido_cred_x5c_ptr(cred) != NULL) {
828		if ((r = fido_cred_verify(cred)) != FIDO_OK) {
829			skdebug(__func__, "fido_cred_verify: %s",
830			    fido_strerr(r));
831			goto out;
832		}
833	} else {
834		skdebug(__func__, "self-attested credential");
835		if ((r = fido_cred_verify_self(cred)) != FIDO_OK) {
836			skdebug(__func__, "fido_cred_verify_self: %s",
837			    fido_strerr(r));
838			goto out;
839		}
840	}
841	if ((response = calloc(1, sizeof(*response))) == NULL) {
842		skdebug(__func__, "calloc response failed");
843		goto out;
844	}
845	response->flags = flags;
846	if (pack_public_key(alg, cred, response) != 0) {
847		skdebug(__func__, "pack_public_key failed");
848		goto out;
849	}
850	if ((ptr = fido_cred_id_ptr(cred)) != NULL) {
851		len = fido_cred_id_len(cred);
852		if ((response->key_handle = calloc(1, len)) == NULL) {
853			skdebug(__func__, "calloc key handle failed");
854			goto out;
855		}
856		memcpy(response->key_handle, ptr, len);
857		response->key_handle_len = len;
858	}
859	if ((ptr = fido_cred_sig_ptr(cred)) != NULL) {
860		len = fido_cred_sig_len(cred);
861		if ((response->signature = calloc(1, len)) == NULL) {
862			skdebug(__func__, "calloc signature failed");
863			goto out;
864		}
865		memcpy(response->signature, ptr, len);
866		response->signature_len = len;
867	}
868	if ((ptr = fido_cred_x5c_ptr(cred)) != NULL) {
869		len = fido_cred_x5c_len(cred);
870		skdebug(__func__, "attestation cert len=%zu", len);
871		if ((response->attestation_cert = calloc(1, len)) == NULL) {
872			skdebug(__func__, "calloc attestation cert failed");
873			goto out;
874		}
875		memcpy(response->attestation_cert, ptr, len);
876		response->attestation_cert_len = len;
877	}
878	if ((ptr = fido_cred_authdata_ptr(cred)) != NULL) {
879		len = fido_cred_authdata_len(cred);
880		skdebug(__func__, "authdata len=%zu", len);
881		if ((response->authdata = calloc(1, len)) == NULL) {
882			skdebug(__func__, "calloc authdata failed");
883			goto out;
884		}
885		memcpy(response->authdata, ptr, len);
886		response->authdata_len = len;
887	}
888	*enroll_response = response;
889	response = NULL;
890	ret = 0;
891 out:
892	free(device);
893	if (response != NULL) {
894		free(response->public_key);
895		free(response->key_handle);
896		free(response->signature);
897		free(response->attestation_cert);
898		free(response->authdata);
899		free(response);
900	}
901	sk_close(sk);
902	fido_cred_free(&cred);
903	return ret;
904}
905
906#ifdef WITH_OPENSSL
907static int
908pack_sig_ecdsa(fido_assert_t *assert, struct sk_sign_response *response)
909{
910	ECDSA_SIG *sig = NULL;
911	const BIGNUM *sig_r, *sig_s;
912	const unsigned char *cp;
913	size_t sig_len;
914	int ret = -1;
915
916	cp = fido_assert_sig_ptr(assert, 0);
917	sig_len = fido_assert_sig_len(assert, 0);
918	if ((sig = d2i_ECDSA_SIG(NULL, &cp, sig_len)) == NULL) {
919		skdebug(__func__, "d2i_ECDSA_SIG failed");
920		goto out;
921	}
922	ECDSA_SIG_get0(sig, &sig_r, &sig_s);
923	response->sig_r_len = BN_num_bytes(sig_r);
924	response->sig_s_len = BN_num_bytes(sig_s);
925	if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL ||
926	    (response->sig_s = calloc(1, response->sig_s_len)) == NULL) {
927		skdebug(__func__, "calloc signature failed");
928		goto out;
929	}
930	BN_bn2bin(sig_r, response->sig_r);
931	BN_bn2bin(sig_s, response->sig_s);
932	ret = 0;
933 out:
934	ECDSA_SIG_free(sig);
935	if (ret != 0) {
936		free(response->sig_r);
937		free(response->sig_s);
938		response->sig_r = NULL;
939		response->sig_s = NULL;
940	}
941	return ret;
942}
943#endif /* WITH_OPENSSL */
944
945static int
946pack_sig_ed25519(fido_assert_t *assert, struct sk_sign_response *response)
947{
948	const unsigned char *ptr;
949	size_t len;
950	int ret = -1;
951
952	ptr = fido_assert_sig_ptr(assert, 0);
953	len = fido_assert_sig_len(assert, 0);
954	if (len != 64) {
955		skdebug(__func__, "bad length %zu", len);
956		goto out;
957	}
958	response->sig_r_len = len;
959	if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL) {
960		skdebug(__func__, "calloc signature failed");
961		goto out;
962	}
963	memcpy(response->sig_r, ptr, len);
964	ret = 0;
965 out:
966	if (ret != 0) {
967		free(response->sig_r);
968		response->sig_r = NULL;
969	}
970	return ret;
971}
972
973static int
974pack_sig(uint32_t  alg, fido_assert_t *assert,
975    struct sk_sign_response *response)
976{
977	switch(alg) {
978#ifdef WITH_OPENSSL
979	case SSH_SK_ECDSA:
980		return pack_sig_ecdsa(assert, response);
981#endif /* WITH_OPENSSL */
982	case SSH_SK_ED25519:
983		return pack_sig_ed25519(assert, response);
984	default:
985		return -1;
986	}
987}
988
989/* Checks sk_options for sk_sign() and sk_load_resident_keys() */
990static int
991check_sign_load_resident_options(struct sk_option **options, char **devicep)
992{
993	size_t i;
994
995	if (options == NULL)
996		return 0;
997	for (i = 0; options[i] != NULL; i++) {
998		if (strcmp(options[i]->name, "device") == 0) {
999			if ((*devicep = strdup(options[i]->value)) == NULL) {
1000				skdebug(__func__, "strdup device failed");
1001				return -1;
1002			}
1003			skdebug(__func__, "requested device %s", *devicep);
1004		} else {
1005			skdebug(__func__, "requested unsupported option %s",
1006			    options[i]->name);
1007			if (options[i]->required) {
1008				skdebug(__func__, "unknown required option");
1009				return -1;
1010			}
1011		}
1012	}
1013	return 0;
1014}
1015
1016int
1017sk_sign(uint32_t alg, const uint8_t *data, size_t datalen,
1018    const char *application,
1019    const uint8_t *key_handle, size_t key_handle_len,
1020    uint8_t flags, const char *pin, struct sk_option **options,
1021    struct sk_sign_response **sign_response)
1022{
1023	fido_assert_t *assert = NULL;
1024	char *device = NULL;
1025	struct sk_usbhid *sk = NULL;
1026	struct sk_sign_response *response = NULL;
1027	int ret = SSH_SK_ERR_GENERAL, internal_uv;
1028	int r;
1029
1030	fido_init(SSH_FIDO_INIT_ARG);
1031
1032	if (sign_response == NULL) {
1033		skdebug(__func__, "sign_response == NULL");
1034		goto out;
1035	}
1036	*sign_response = NULL;
1037	if (check_sign_load_resident_options(options, &device) != 0)
1038		goto out; /* error already logged */
1039	if (device != NULL)
1040		sk = sk_open(device);
1041	else if (pin != NULL || (flags & SSH_SK_USER_VERIFICATION_REQD))
1042		sk = sk_probe(NULL, NULL, 0, 0);
1043	else
1044		sk = sk_probe(application, key_handle, key_handle_len, 0);
1045	if (sk == NULL) {
1046		ret = SSH_SK_ERR_DEVICE_NOT_FOUND;
1047		skdebug(__func__, "failed to find sk");
1048		goto out;
1049	}
1050	if ((assert = fido_assert_new()) == NULL) {
1051		skdebug(__func__, "fido_assert_new failed");
1052		goto out;
1053	}
1054	if ((r = fido_assert_set_clientdata(assert,
1055	    data, datalen)) != FIDO_OK)  {
1056		skdebug(__func__, "fido_assert_set_clientdata: %s",
1057		    fido_strerr(r));
1058		goto out;
1059	}
1060	if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) {
1061		skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r));
1062		goto out;
1063	}
1064	if ((r = fido_assert_allow_cred(assert, key_handle,
1065	    key_handle_len)) != FIDO_OK) {
1066		skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r));
1067		goto out;
1068	}
1069	if ((r = fido_assert_set_up(assert,
1070	    (flags & SSH_SK_USER_PRESENCE_REQD) ?
1071	    FIDO_OPT_TRUE : FIDO_OPT_FALSE)) != FIDO_OK) {
1072		skdebug(__func__, "fido_assert_set_up: %s", fido_strerr(r));
1073		goto out;
1074	}
1075	if (pin == NULL && (flags & SSH_SK_USER_VERIFICATION_REQD)) {
1076		if (check_sk_options(sk->dev, "uv", &internal_uv) < 0 ||
1077		    internal_uv != 1) {
1078			skdebug(__func__, "check_sk_options uv");
1079			ret = SSH_SK_ERR_PIN_REQUIRED;
1080			goto out;
1081		}
1082		if ((r = fido_assert_set_uv(assert,
1083		    FIDO_OPT_TRUE)) != FIDO_OK) {
1084			skdebug(__func__, "fido_assert_set_uv: %s",
1085			    fido_strerr(r));
1086			ret = fidoerr_to_skerr(r);
1087			goto out;
1088		}
1089	}
1090	if ((r = fido_dev_get_assert(sk->dev, assert, pin)) != FIDO_OK) {
1091		skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r));
1092		ret = fidoerr_to_skerr(r);
1093		goto out;
1094	}
1095	if ((response = calloc(1, sizeof(*response))) == NULL) {
1096		skdebug(__func__, "calloc response failed");
1097		goto out;
1098	}
1099	response->flags = fido_assert_flags(assert, 0);
1100	response->counter = fido_assert_sigcount(assert, 0);
1101	if (pack_sig(alg, assert, response) != 0) {
1102		skdebug(__func__, "pack_sig failed");
1103		goto out;
1104	}
1105	*sign_response = response;
1106	response = NULL;
1107	ret = 0;
1108 out:
1109	free(device);
1110	if (response != NULL) {
1111		free(response->sig_r);
1112		free(response->sig_s);
1113		free(response);
1114	}
1115	sk_close(sk);
1116	fido_assert_free(&assert);
1117	return ret;
1118}
1119
1120static int
1121read_rks(struct sk_usbhid *sk, const char *pin,
1122    struct sk_resident_key ***rksp, size_t *nrksp)
1123{
1124	int ret = SSH_SK_ERR_GENERAL, r = -1, internal_uv;
1125	fido_credman_metadata_t *metadata = NULL;
1126	fido_credman_rp_t *rp = NULL;
1127	fido_credman_rk_t *rk = NULL;
1128	size_t i, j, nrp, nrk, user_id_len;
1129	const fido_cred_t *cred;
1130	const char *rp_id, *rp_name, *user_name;
1131	struct sk_resident_key *srk = NULL, **tmp;
1132	const u_char *user_id;
1133
1134	if (pin == NULL) {
1135		skdebug(__func__, "no PIN specified");
1136		ret = SSH_SK_ERR_PIN_REQUIRED;
1137		goto out;
1138	}
1139	if ((metadata = fido_credman_metadata_new()) == NULL) {
1140		skdebug(__func__, "alloc failed");
1141		goto out;
1142	}
1143	if (check_sk_options(sk->dev, "uv", &internal_uv) != 0) {
1144		skdebug(__func__, "check_sk_options failed");
1145		goto out;
1146	}
1147
1148	if ((r = fido_credman_get_dev_metadata(sk->dev, metadata, pin)) != 0) {
1149		if (r == FIDO_ERR_INVALID_COMMAND) {
1150			skdebug(__func__, "device %s does not support "
1151			    "resident keys", sk->path);
1152			ret = 0;
1153			goto out;
1154		}
1155		skdebug(__func__, "get metadata for %s failed: %s",
1156		    sk->path, fido_strerr(r));
1157		ret = fidoerr_to_skerr(r);
1158		goto out;
1159	}
1160	skdebug(__func__, "existing %llu, remaining %llu",
1161	    (unsigned long long)fido_credman_rk_existing(metadata),
1162	    (unsigned long long)fido_credman_rk_remaining(metadata));
1163	if ((rp = fido_credman_rp_new()) == NULL) {
1164		skdebug(__func__, "alloc rp failed");
1165		goto out;
1166	}
1167	if ((r = fido_credman_get_dev_rp(sk->dev, rp, pin)) != 0) {
1168		skdebug(__func__, "get RPs for %s failed: %s",
1169		    sk->path, fido_strerr(r));
1170		goto out;
1171	}
1172	nrp = fido_credman_rp_count(rp);
1173	skdebug(__func__, "Device %s has resident keys for %zu RPs",
1174	    sk->path, nrp);
1175
1176	/* Iterate over RP IDs that have resident keys */
1177	for (i = 0; i < nrp; i++) {
1178		rp_id = fido_credman_rp_id(rp, i);
1179		rp_name = fido_credman_rp_name(rp, i);
1180		skdebug(__func__, "rp %zu: name=\"%s\" id=\"%s\" hashlen=%zu",
1181		    i, rp_name == NULL ? "(none)" : rp_name,
1182		    rp_id == NULL ? "(none)" : rp_id,
1183		    fido_credman_rp_id_hash_len(rp, i));
1184
1185		/* Skip non-SSH RP IDs */
1186		if (rp_id == NULL ||
1187		    strncasecmp(fido_credman_rp_id(rp, i), "ssh:", 4) != 0)
1188			continue;
1189
1190		fido_credman_rk_free(&rk);
1191		if ((rk = fido_credman_rk_new()) == NULL) {
1192			skdebug(__func__, "alloc rk failed");
1193			goto out;
1194		}
1195		if ((r = fido_credman_get_dev_rk(sk->dev,
1196		    fido_credman_rp_id(rp, i), rk, pin)) != 0) {
1197			skdebug(__func__, "get RKs for %s slot %zu failed: %s",
1198			    sk->path, i, fido_strerr(r));
1199			goto out;
1200		}
1201		nrk = fido_credman_rk_count(rk);
1202		skdebug(__func__, "RP \"%s\" has %zu resident keys",
1203		    fido_credman_rp_id(rp, i), nrk);
1204
1205		/* Iterate over resident keys for this RP ID */
1206		for (j = 0; j < nrk; j++) {
1207			if ((cred = fido_credman_rk(rk, j)) == NULL) {
1208				skdebug(__func__, "no RK in slot %zu", j);
1209				continue;
1210			}
1211			if ((user_name = fido_cred_user_name(cred)) == NULL)
1212				user_name = "";
1213			user_id = fido_cred_user_id_ptr(cred);
1214			user_id_len = fido_cred_user_id_len(cred);
1215			skdebug(__func__, "Device %s RP \"%s\" user \"%s\" "
1216			    "uidlen %zu slot %zu: type %d flags 0x%02x "
1217			    "prot 0x%02x", sk->path, rp_id, user_name,
1218			    user_id_len, j, fido_cred_type(cred),
1219			    fido_cred_flags(cred), fido_cred_prot(cred));
1220
1221			/* build response entry */
1222			if ((srk = calloc(1, sizeof(*srk))) == NULL ||
1223			    (srk->key.key_handle = calloc(1,
1224			    fido_cred_id_len(cred))) == NULL ||
1225			    (srk->application = strdup(rp_id)) == NULL ||
1226			    (user_id_len > 0 &&
1227			     (srk->user_id = calloc(1, user_id_len)) == NULL)) {
1228				skdebug(__func__, "alloc sk_resident_key");
1229				goto out;
1230			}
1231
1232			srk->key.key_handle_len = fido_cred_id_len(cred);
1233			memcpy(srk->key.key_handle, fido_cred_id_ptr(cred),
1234			    srk->key.key_handle_len);
1235			srk->user_id_len = user_id_len;
1236			if (srk->user_id_len != 0)
1237				memcpy(srk->user_id, user_id, srk->user_id_len);
1238
1239			switch (fido_cred_type(cred)) {
1240			case COSE_ES256:
1241				srk->alg = SSH_SK_ECDSA;
1242				break;
1243			case COSE_EDDSA:
1244				srk->alg = SSH_SK_ED25519;
1245				break;
1246			default:
1247				skdebug(__func__, "unsupported key type %d",
1248				    fido_cred_type(cred));
1249				goto out; /* XXX free rk and continue */
1250			}
1251
1252			if (fido_cred_prot(cred) == FIDO_CRED_PROT_UV_REQUIRED
1253			    && internal_uv == -1)
1254				srk->flags |=  SSH_SK_USER_VERIFICATION_REQD;
1255
1256			if ((r = pack_public_key(srk->alg, cred,
1257			    &srk->key)) != 0) {
1258				skdebug(__func__, "pack public key failed");
1259				goto out;
1260			}
1261			/* append */
1262			if ((tmp = recallocarray(*rksp, *nrksp, (*nrksp) + 1,
1263			    sizeof(**rksp))) == NULL) {
1264				skdebug(__func__, "alloc rksp");
1265				goto out;
1266			}
1267			*rksp = tmp;
1268			(*rksp)[(*nrksp)++] = srk;
1269			srk = NULL;
1270		}
1271	}
1272	/* Success */
1273	ret = 0;
1274 out:
1275	if (srk != NULL) {
1276		free(srk->application);
1277		freezero(srk->key.public_key, srk->key.public_key_len);
1278		freezero(srk->key.key_handle, srk->key.key_handle_len);
1279		freezero(srk->user_id, srk->user_id_len);
1280		freezero(srk, sizeof(*srk));
1281	}
1282	fido_credman_rp_free(&rp);
1283	fido_credman_rk_free(&rk);
1284	fido_credman_metadata_free(&metadata);
1285	return ret;
1286}
1287
1288int
1289sk_load_resident_keys(const char *pin, struct sk_option **options,
1290    struct sk_resident_key ***rksp, size_t *nrksp)
1291{
1292	int ret = SSH_SK_ERR_GENERAL, r = -1;
1293	size_t i, nrks = 0;
1294	struct sk_resident_key **rks = NULL;
1295	struct sk_usbhid *sk = NULL;
1296	char *device = NULL;
1297
1298	*rksp = NULL;
1299	*nrksp = 0;
1300
1301	fido_init(SSH_FIDO_INIT_ARG);
1302
1303	if (check_sign_load_resident_options(options, &device) != 0)
1304		goto out; /* error already logged */
1305	if (device != NULL)
1306		sk = sk_open(device);
1307	else
1308		sk = sk_probe(NULL, NULL, 0, 1);
1309	if (sk == NULL) {
1310		ret = SSH_SK_ERR_DEVICE_NOT_FOUND;
1311		skdebug(__func__, "failed to find sk");
1312		goto out;
1313	}
1314	skdebug(__func__, "trying %s", sk->path);
1315	if ((r = read_rks(sk, pin, &rks, &nrks)) != 0) {
1316		skdebug(__func__, "read_rks failed for %s", sk->path);
1317		ret = r;
1318		goto out;
1319	}
1320	/* success, unless we have no keys but a specific error */
1321	if (nrks > 0 || ret == SSH_SK_ERR_GENERAL)
1322		ret = 0;
1323	*rksp = rks;
1324	*nrksp = nrks;
1325	rks = NULL;
1326	nrks = 0;
1327 out:
1328	sk_close(sk);
1329	for (i = 0; i < nrks; i++) {
1330		free(rks[i]->application);
1331		freezero(rks[i]->key.public_key, rks[i]->key.public_key_len);
1332		freezero(rks[i]->key.key_handle, rks[i]->key.key_handle_len);
1333		freezero(rks[i]->user_id, rks[i]->user_id_len);
1334		freezero(rks[i], sizeof(*rks[i]));
1335	}
1336	free(device);
1337	free(rks);
1338	return ret;
1339}
1340
1341