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