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