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