xref: /openbsd-src/usr.bin/ssh/sk-usbhid.c (revision 25c4e8bd056e974b28f4a0ffd39d76c190a56013)
1 /* $OpenBSD: sk-usbhid.c,v 1.41 2022/07/20 03:31:42 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: %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 static int
655 key_lookup(fido_dev_t *dev, const char *application, const uint8_t *user_id,
656     size_t user_id_len, const char *pin)
657 {
658 	fido_assert_t *assert = NULL;
659 	uint8_t message[32];
660 	int r = FIDO_ERR_INTERNAL;
661 	size_t i;
662 
663 	memset(message, '\0', sizeof(message));
664 	if (pin == NULL) {
665 		skdebug(__func__, "NULL pin");
666 		goto out;
667 	}
668 	if ((assert = fido_assert_new()) == NULL) {
669 		skdebug(__func__, "fido_assert_new failed");
670 		goto out;
671 	}
672 	/* generate an invalid signature on FIDO2 tokens */
673 	if ((r = fido_assert_set_clientdata(assert, message,
674 	    sizeof(message))) != FIDO_OK) {
675 		skdebug(__func__, "fido_assert_set_clientdata: %s",
676 		    fido_strerr(r));
677 		goto out;
678 	}
679 	if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) {
680 		skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r));
681 		goto out;
682 	}
683 	if ((r = fido_assert_set_up(assert, FIDO_OPT_FALSE)) != FIDO_OK) {
684 		skdebug(__func__, "fido_assert_up: %s", fido_strerr(r));
685 		goto out;
686 	}
687 	if ((r = fido_dev_get_assert(dev, assert, pin)) != FIDO_OK) {
688 		skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r));
689 		goto out;
690 	}
691 	r = FIDO_ERR_NO_CREDENTIALS;
692 	skdebug(__func__, "%zu signatures returned", fido_assert_count(assert));
693 	for (i = 0; i < fido_assert_count(assert); i++) {
694 		if (fido_assert_user_id_len(assert, i) == user_id_len &&
695 		    memcmp(fido_assert_user_id_ptr(assert, i), user_id,
696 		    user_id_len) == 0) {
697 			skdebug(__func__, "credential exists");
698 			r = FIDO_OK;
699 			goto out;
700 		}
701 	}
702  out:
703 	fido_assert_free(&assert);
704 
705 	return r;
706 }
707 
708 int
709 sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len,
710     const char *application, uint8_t flags, const char *pin,
711     struct sk_option **options, struct sk_enroll_response **enroll_response)
712 {
713 	fido_cred_t *cred = NULL;
714 	const uint8_t *ptr;
715 	uint8_t user_id[32];
716 	struct sk_usbhid *sk = NULL;
717 	struct sk_enroll_response *response = NULL;
718 	size_t len;
719 	int credprot;
720 	int internal_uv;
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);
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 ((flags & SSH_SK_USER_VERIFICATION_REQD)) {
847 		if (check_sk_options(sk->dev, "uv", &internal_uv) == 0 &&
848 		    internal_uv != -1) {
849 			/* user verification handled by token */
850 			response->flags &= ~SSH_SK_USER_VERIFICATION_REQD;
851 		}
852 	}
853 	if (pack_public_key(alg, cred, response) != 0) {
854 		skdebug(__func__, "pack_public_key failed");
855 		goto out;
856 	}
857 	if ((ptr = fido_cred_id_ptr(cred)) != NULL) {
858 		len = fido_cred_id_len(cred);
859 		if ((response->key_handle = calloc(1, len)) == NULL) {
860 			skdebug(__func__, "calloc key handle failed");
861 			goto out;
862 		}
863 		memcpy(response->key_handle, ptr, len);
864 		response->key_handle_len = len;
865 	}
866 	if ((ptr = fido_cred_sig_ptr(cred)) != NULL) {
867 		len = fido_cred_sig_len(cred);
868 		if ((response->signature = calloc(1, len)) == NULL) {
869 			skdebug(__func__, "calloc signature failed");
870 			goto out;
871 		}
872 		memcpy(response->signature, ptr, len);
873 		response->signature_len = len;
874 	}
875 	if ((ptr = fido_cred_x5c_ptr(cred)) != NULL) {
876 		len = fido_cred_x5c_len(cred);
877 		skdebug(__func__, "attestation cert len=%zu", len);
878 		if ((response->attestation_cert = calloc(1, len)) == NULL) {
879 			skdebug(__func__, "calloc attestation cert failed");
880 			goto out;
881 		}
882 		memcpy(response->attestation_cert, ptr, len);
883 		response->attestation_cert_len = len;
884 	}
885 	if ((ptr = fido_cred_authdata_ptr(cred)) != NULL) {
886 		len = fido_cred_authdata_len(cred);
887 		skdebug(__func__, "authdata len=%zu", len);
888 		if ((response->authdata = calloc(1, len)) == NULL) {
889 			skdebug(__func__, "calloc authdata failed");
890 			goto out;
891 		}
892 		memcpy(response->authdata, ptr, len);
893 		response->authdata_len = len;
894 	}
895 	*enroll_response = response;
896 	response = NULL;
897 	ret = 0;
898  out:
899 	free(device);
900 	if (response != NULL) {
901 		free(response->public_key);
902 		free(response->key_handle);
903 		free(response->signature);
904 		free(response->attestation_cert);
905 		free(response->authdata);
906 		free(response);
907 	}
908 	sk_close(sk);
909 	fido_cred_free(&cred);
910 	return ret;
911 }
912 
913 #ifdef WITH_OPENSSL
914 static int
915 pack_sig_ecdsa(fido_assert_t *assert, struct sk_sign_response *response)
916 {
917 	ECDSA_SIG *sig = NULL;
918 	const BIGNUM *sig_r, *sig_s;
919 	const unsigned char *cp;
920 	size_t sig_len;
921 	int ret = -1;
922 
923 	cp = fido_assert_sig_ptr(assert, 0);
924 	sig_len = fido_assert_sig_len(assert, 0);
925 	if ((sig = d2i_ECDSA_SIG(NULL, &cp, sig_len)) == NULL) {
926 		skdebug(__func__, "d2i_ECDSA_SIG failed");
927 		goto out;
928 	}
929 	ECDSA_SIG_get0(sig, &sig_r, &sig_s);
930 	response->sig_r_len = BN_num_bytes(sig_r);
931 	response->sig_s_len = BN_num_bytes(sig_s);
932 	if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL ||
933 	    (response->sig_s = calloc(1, response->sig_s_len)) == NULL) {
934 		skdebug(__func__, "calloc signature failed");
935 		goto out;
936 	}
937 	BN_bn2bin(sig_r, response->sig_r);
938 	BN_bn2bin(sig_s, response->sig_s);
939 	ret = 0;
940  out:
941 	ECDSA_SIG_free(sig);
942 	if (ret != 0) {
943 		free(response->sig_r);
944 		free(response->sig_s);
945 		response->sig_r = NULL;
946 		response->sig_s = NULL;
947 	}
948 	return ret;
949 }
950 #endif /* WITH_OPENSSL */
951 
952 static int
953 pack_sig_ed25519(fido_assert_t *assert, struct sk_sign_response *response)
954 {
955 	const unsigned char *ptr;
956 	size_t len;
957 	int ret = -1;
958 
959 	ptr = fido_assert_sig_ptr(assert, 0);
960 	len = fido_assert_sig_len(assert, 0);
961 	if (len != 64) {
962 		skdebug(__func__, "bad length %zu", len);
963 		goto out;
964 	}
965 	response->sig_r_len = len;
966 	if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL) {
967 		skdebug(__func__, "calloc signature failed");
968 		goto out;
969 	}
970 	memcpy(response->sig_r, ptr, len);
971 	ret = 0;
972  out:
973 	if (ret != 0) {
974 		free(response->sig_r);
975 		response->sig_r = NULL;
976 	}
977 	return ret;
978 }
979 
980 static int
981 pack_sig(uint32_t  alg, fido_assert_t *assert,
982     struct sk_sign_response *response)
983 {
984 	switch(alg) {
985 #ifdef WITH_OPENSSL
986 	case SSH_SK_ECDSA:
987 		return pack_sig_ecdsa(assert, response);
988 #endif /* WITH_OPENSSL */
989 	case SSH_SK_ED25519:
990 		return pack_sig_ed25519(assert, response);
991 	default:
992 		return -1;
993 	}
994 }
995 
996 /* Checks sk_options for sk_sign() and sk_load_resident_keys() */
997 static int
998 check_sign_load_resident_options(struct sk_option **options, char **devicep)
999 {
1000 	size_t i;
1001 
1002 	if (options == NULL)
1003 		return 0;
1004 	for (i = 0; options[i] != NULL; i++) {
1005 		if (strcmp(options[i]->name, "device") == 0) {
1006 			if ((*devicep = strdup(options[i]->value)) == NULL) {
1007 				skdebug(__func__, "strdup device failed");
1008 				return -1;
1009 			}
1010 			skdebug(__func__, "requested device %s", *devicep);
1011 		} else {
1012 			skdebug(__func__, "requested unsupported option %s",
1013 			    options[i]->name);
1014 			if (options[i]->required) {
1015 				skdebug(__func__, "unknown required option");
1016 				return -1;
1017 			}
1018 		}
1019 	}
1020 	return 0;
1021 }
1022 
1023 int
1024 sk_sign(uint32_t alg, const uint8_t *data, size_t datalen,
1025     const char *application,
1026     const uint8_t *key_handle, size_t key_handle_len,
1027     uint8_t flags, const char *pin, struct sk_option **options,
1028     struct sk_sign_response **sign_response)
1029 {
1030 	fido_assert_t *assert = NULL;
1031 	char *device = NULL;
1032 	struct sk_usbhid *sk = NULL;
1033 	struct sk_sign_response *response = NULL;
1034 	int ret = SSH_SK_ERR_GENERAL, internal_uv;
1035 	int r;
1036 
1037 	fido_init(SSH_FIDO_INIT_ARG);
1038 
1039 	if (sign_response == NULL) {
1040 		skdebug(__func__, "sign_response == NULL");
1041 		goto out;
1042 	}
1043 	*sign_response = NULL;
1044 	if (check_sign_load_resident_options(options, &device) != 0)
1045 		goto out; /* error already logged */
1046 	if (device != NULL)
1047 		sk = sk_open(device);
1048 	else if (pin != NULL || (flags & SSH_SK_USER_VERIFICATION_REQD))
1049 		sk = sk_probe(NULL, NULL, 0);
1050 	else
1051 		sk = sk_probe(application, key_handle, key_handle_len);
1052 	if (sk == NULL) {
1053 		ret = SSH_SK_ERR_DEVICE_NOT_FOUND;
1054 		skdebug(__func__, "failed to find sk");
1055 		goto out;
1056 	}
1057 	if ((assert = fido_assert_new()) == NULL) {
1058 		skdebug(__func__, "fido_assert_new failed");
1059 		goto out;
1060 	}
1061 	if ((r = fido_assert_set_clientdata(assert,
1062 	    data, datalen)) != FIDO_OK)  {
1063 		skdebug(__func__, "fido_assert_set_clientdata: %s",
1064 		    fido_strerr(r));
1065 		goto out;
1066 	}
1067 	if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) {
1068 		skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r));
1069 		goto out;
1070 	}
1071 	if ((r = fido_assert_allow_cred(assert, key_handle,
1072 	    key_handle_len)) != FIDO_OK) {
1073 		skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r));
1074 		goto out;
1075 	}
1076 	if ((r = fido_assert_set_up(assert,
1077 	    (flags & SSH_SK_USER_PRESENCE_REQD) ?
1078 	    FIDO_OPT_TRUE : FIDO_OPT_FALSE)) != FIDO_OK) {
1079 		skdebug(__func__, "fido_assert_set_up: %s", fido_strerr(r));
1080 		goto out;
1081 	}
1082 	if (pin == NULL && (flags & SSH_SK_USER_VERIFICATION_REQD)) {
1083 		if (check_sk_options(sk->dev, "uv", &internal_uv) < 0 ||
1084 		    internal_uv != 1) {
1085 			skdebug(__func__, "check_sk_options uv");
1086 			ret = SSH_SK_ERR_PIN_REQUIRED;
1087 			goto out;
1088 		}
1089 		if ((r = fido_assert_set_uv(assert,
1090 		    FIDO_OPT_TRUE)) != FIDO_OK) {
1091 			skdebug(__func__, "fido_assert_set_uv: %s",
1092 			    fido_strerr(r));
1093 			ret = fidoerr_to_skerr(r);
1094 			goto out;
1095 		}
1096 	}
1097 	if ((r = fido_dev_get_assert(sk->dev, assert, pin)) != FIDO_OK) {
1098 		skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r));
1099 		ret = fidoerr_to_skerr(r);
1100 		goto out;
1101 	}
1102 	if ((response = calloc(1, sizeof(*response))) == NULL) {
1103 		skdebug(__func__, "calloc response failed");
1104 		goto out;
1105 	}
1106 	response->flags = fido_assert_flags(assert, 0);
1107 	response->counter = fido_assert_sigcount(assert, 0);
1108 	if (pack_sig(alg, assert, response) != 0) {
1109 		skdebug(__func__, "pack_sig failed");
1110 		goto out;
1111 	}
1112 	*sign_response = response;
1113 	response = NULL;
1114 	ret = 0;
1115  out:
1116 	free(device);
1117 	if (response != NULL) {
1118 		free(response->sig_r);
1119 		free(response->sig_s);
1120 		free(response);
1121 	}
1122 	sk_close(sk);
1123 	fido_assert_free(&assert);
1124 	return ret;
1125 }
1126 
1127 static int
1128 read_rks(struct sk_usbhid *sk, const char *pin,
1129     struct sk_resident_key ***rksp, size_t *nrksp)
1130 {
1131 	int ret = SSH_SK_ERR_GENERAL, r = -1, internal_uv;
1132 	fido_credman_metadata_t *metadata = NULL;
1133 	fido_credman_rp_t *rp = NULL;
1134 	fido_credman_rk_t *rk = NULL;
1135 	size_t i, j, nrp, nrk, user_id_len;
1136 	const fido_cred_t *cred;
1137 	const char *rp_id, *rp_name, *user_name;
1138 	struct sk_resident_key *srk = NULL, **tmp;
1139 	const u_char *user_id;
1140 
1141 	if (pin == NULL) {
1142 		skdebug(__func__, "no PIN specified");
1143 		ret = SSH_SK_ERR_PIN_REQUIRED;
1144 		goto out;
1145 	}
1146 	if ((metadata = fido_credman_metadata_new()) == NULL) {
1147 		skdebug(__func__, "alloc failed");
1148 		goto out;
1149 	}
1150 	if (check_sk_options(sk->dev, "uv", &internal_uv) != 0) {
1151 		skdebug(__func__, "check_sk_options failed");
1152 		goto out;
1153 	}
1154 
1155 	if ((r = fido_credman_get_dev_metadata(sk->dev, metadata, pin)) != 0) {
1156 		if (r == FIDO_ERR_INVALID_COMMAND) {
1157 			skdebug(__func__, "device %s does not support "
1158 			    "resident keys", sk->path);
1159 			ret = 0;
1160 			goto out;
1161 		}
1162 		skdebug(__func__, "get metadata for %s failed: %s",
1163 		    sk->path, fido_strerr(r));
1164 		ret = fidoerr_to_skerr(r);
1165 		goto out;
1166 	}
1167 	skdebug(__func__, "existing %llu, remaining %llu",
1168 	    (unsigned long long)fido_credman_rk_existing(metadata),
1169 	    (unsigned long long)fido_credman_rk_remaining(metadata));
1170 	if ((rp = fido_credman_rp_new()) == NULL) {
1171 		skdebug(__func__, "alloc rp failed");
1172 		goto out;
1173 	}
1174 	if ((r = fido_credman_get_dev_rp(sk->dev, rp, pin)) != 0) {
1175 		skdebug(__func__, "get RPs for %s failed: %s",
1176 		    sk->path, fido_strerr(r));
1177 		goto out;
1178 	}
1179 	nrp = fido_credman_rp_count(rp);
1180 	skdebug(__func__, "Device %s has resident keys for %zu RPs",
1181 	    sk->path, nrp);
1182 
1183 	/* Iterate over RP IDs that have resident keys */
1184 	for (i = 0; i < nrp; i++) {
1185 		rp_id = fido_credman_rp_id(rp, i);
1186 		rp_name = fido_credman_rp_name(rp, i);
1187 		skdebug(__func__, "rp %zu: name=\"%s\" id=\"%s\" hashlen=%zu",
1188 		    i, rp_name == NULL ? "(none)" : rp_name,
1189 		    rp_id == NULL ? "(none)" : rp_id,
1190 		    fido_credman_rp_id_hash_len(rp, i));
1191 
1192 		/* Skip non-SSH RP IDs */
1193 		if (rp_id == NULL ||
1194 		    strncasecmp(fido_credman_rp_id(rp, i), "ssh:", 4) != 0)
1195 			continue;
1196 
1197 		fido_credman_rk_free(&rk);
1198 		if ((rk = fido_credman_rk_new()) == NULL) {
1199 			skdebug(__func__, "alloc rk failed");
1200 			goto out;
1201 		}
1202 		if ((r = fido_credman_get_dev_rk(sk->dev,
1203 		    fido_credman_rp_id(rp, i), rk, pin)) != 0) {
1204 			skdebug(__func__, "get RKs for %s slot %zu failed: %s",
1205 			    sk->path, i, fido_strerr(r));
1206 			goto out;
1207 		}
1208 		nrk = fido_credman_rk_count(rk);
1209 		skdebug(__func__, "RP \"%s\" has %zu resident keys",
1210 		    fido_credman_rp_id(rp, i), nrk);
1211 
1212 		/* Iterate over resident keys for this RP ID */
1213 		for (j = 0; j < nrk; j++) {
1214 			if ((cred = fido_credman_rk(rk, j)) == NULL) {
1215 				skdebug(__func__, "no RK in slot %zu", j);
1216 				continue;
1217 			}
1218 			if ((user_name = fido_cred_user_name(cred)) == NULL)
1219 				user_name = "";
1220 			user_id = fido_cred_user_id_ptr(cred);
1221 			user_id_len = fido_cred_user_id_len(cred);
1222 			skdebug(__func__, "Device %s RP \"%s\" user \"%s\" "
1223 			    "uidlen %zu slot %zu: type %d flags 0x%02x "
1224 			    "prot 0x%02x", sk->path, rp_id, user_name,
1225 			    user_id_len, j, fido_cred_type(cred),
1226 			    fido_cred_flags(cred), fido_cred_prot(cred));
1227 
1228 			/* build response entry */
1229 			if ((srk = calloc(1, sizeof(*srk))) == NULL ||
1230 			    (srk->key.key_handle = calloc(1,
1231 			    fido_cred_id_len(cred))) == NULL ||
1232 			    (srk->application = strdup(rp_id)) == NULL ||
1233 			    (user_id_len > 0 &&
1234 			     (srk->user_id = calloc(1, user_id_len)) == NULL)) {
1235 				skdebug(__func__, "alloc sk_resident_key");
1236 				goto out;
1237 			}
1238 
1239 			srk->key.key_handle_len = fido_cred_id_len(cred);
1240 			memcpy(srk->key.key_handle, fido_cred_id_ptr(cred),
1241 			    srk->key.key_handle_len);
1242 			srk->user_id_len = user_id_len;
1243 			if (srk->user_id_len != 0)
1244 				memcpy(srk->user_id, user_id, srk->user_id_len);
1245 
1246 			switch (fido_cred_type(cred)) {
1247 			case COSE_ES256:
1248 				srk->alg = SSH_SK_ECDSA;
1249 				break;
1250 			case COSE_EDDSA:
1251 				srk->alg = SSH_SK_ED25519;
1252 				break;
1253 			default:
1254 				skdebug(__func__, "unsupported key type %d",
1255 				    fido_cred_type(cred));
1256 				goto out; /* XXX free rk and continue */
1257 			}
1258 
1259 			if (fido_cred_prot(cred) == FIDO_CRED_PROT_UV_REQUIRED
1260 			    && internal_uv == -1)
1261 				srk->flags |=  SSH_SK_USER_VERIFICATION_REQD;
1262 
1263 			if ((r = pack_public_key(srk->alg, cred,
1264 			    &srk->key)) != 0) {
1265 				skdebug(__func__, "pack public key failed");
1266 				goto out;
1267 			}
1268 			/* append */
1269 			if ((tmp = recallocarray(*rksp, *nrksp, (*nrksp) + 1,
1270 			    sizeof(**rksp))) == NULL) {
1271 				skdebug(__func__, "alloc rksp");
1272 				goto out;
1273 			}
1274 			*rksp = tmp;
1275 			(*rksp)[(*nrksp)++] = srk;
1276 			srk = NULL;
1277 		}
1278 	}
1279 	/* Success */
1280 	ret = 0;
1281  out:
1282 	if (srk != NULL) {
1283 		free(srk->application);
1284 		freezero(srk->key.public_key, srk->key.public_key_len);
1285 		freezero(srk->key.key_handle, srk->key.key_handle_len);
1286 		freezero(srk->user_id, srk->user_id_len);
1287 		freezero(srk, sizeof(*srk));
1288 	}
1289 	fido_credman_rp_free(&rp);
1290 	fido_credman_rk_free(&rk);
1291 	fido_credman_metadata_free(&metadata);
1292 	return ret;
1293 }
1294 
1295 int
1296 sk_load_resident_keys(const char *pin, struct sk_option **options,
1297     struct sk_resident_key ***rksp, size_t *nrksp)
1298 {
1299 	int ret = SSH_SK_ERR_GENERAL, r = -1;
1300 	size_t i, nrks = 0;
1301 	struct sk_resident_key **rks = NULL;
1302 	struct sk_usbhid *sk = NULL;
1303 	char *device = NULL;
1304 
1305 	*rksp = NULL;
1306 	*nrksp = 0;
1307 
1308 	fido_init(SSH_FIDO_INIT_ARG);
1309 
1310 	if (check_sign_load_resident_options(options, &device) != 0)
1311 		goto out; /* error already logged */
1312 	if (device != NULL)
1313 		sk = sk_open(device);
1314 	else
1315 		sk = sk_probe(NULL, NULL, 0);
1316 	if (sk == NULL) {
1317 		ret = SSH_SK_ERR_DEVICE_NOT_FOUND;
1318 		skdebug(__func__, "failed to find sk");
1319 		goto out;
1320 	}
1321 	skdebug(__func__, "trying %s", sk->path);
1322 	if ((r = read_rks(sk, pin, &rks, &nrks)) != 0) {
1323 		skdebug(__func__, "read_rks failed for %s", sk->path);
1324 		ret = r;
1325 		goto out;
1326 	}
1327 	/* success, unless we have no keys but a specific error */
1328 	if (nrks > 0 || ret == SSH_SK_ERR_GENERAL)
1329 		ret = 0;
1330 	*rksp = rks;
1331 	*nrksp = nrks;
1332 	rks = NULL;
1333 	nrks = 0;
1334  out:
1335 	sk_close(sk);
1336 	for (i = 0; i < nrks; i++) {
1337 		free(rks[i]->application);
1338 		freezero(rks[i]->key.public_key, rks[i]->key.public_key_len);
1339 		freezero(rks[i]->key.key_handle, rks[i]->key.key_handle_len);
1340 		freezero(rks[i]->user_id, rks[i]->user_id_len);
1341 		freezero(rks[i], sizeof(*rks[i]));
1342 	}
1343 	free(device);
1344 	free(rks);
1345 	return ret;
1346 }
1347 
1348