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