xref: /openssh-portable/sk-usbhid.c (revision 43ce9642)
1 /*
2  * Copyright (c) 2019 Markus Friedl
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16 
17 #include "includes.h"
18 
19 #ifdef ENABLE_SK_INTERNAL
20 
21 #include <stdint.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <stdio.h>
25 #include <stddef.h>
26 #include <stdarg.h>
27 
28 #ifdef WITH_OPENSSL
29 #include <openssl/opensslv.h>
30 #include <openssl/crypto.h>
31 #include <openssl/bn.h>
32 #include <openssl/ec.h>
33 #include <openssl/ecdsa.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 #endif
43 
44 /* #define SK_DEBUG 1 */
45 
46 #define MAX_FIDO_DEVICES	256
47 
48 /* Compatibility with OpenSSH 1.0.x */
49 #if (OPENSSL_VERSION_NUMBER < 0x10100000L)
50 #define ECDSA_SIG_get0(sig, pr, ps) \
51 	do { \
52 		(*pr) = sig->r; \
53 		(*ps) = sig->s; \
54 	} while (0)
55 #endif
56 
57 #define SK_VERSION_MAJOR	0x00030000 /* current API version */
58 
59 /* Flags */
60 #define SK_USER_PRESENCE_REQD		0x01
61 #define SK_USER_VERIFICATION_REQD	0x04
62 #define SK_RESIDENT_KEY			0x20
63 
64 /* Algs */
65 #define	SK_ECDSA		0x00
66 #define	SK_ED25519		0x01
67 
68 /* Error codes */
69 #define SSH_SK_ERR_GENERAL		-1
70 #define SSH_SK_ERR_UNSUPPORTED		-2
71 #define SSH_SK_ERR_PIN_REQUIRED		-3
72 
73 struct sk_enroll_response {
74 	uint8_t *public_key;
75 	size_t public_key_len;
76 	uint8_t *key_handle;
77 	size_t key_handle_len;
78 	uint8_t *signature;
79 	size_t signature_len;
80 	uint8_t *attestation_cert;
81 	size_t attestation_cert_len;
82 };
83 
84 struct sk_sign_response {
85 	uint8_t flags;
86 	uint32_t counter;
87 	uint8_t *sig_r;
88 	size_t sig_r_len;
89 	uint8_t *sig_s;
90 	size_t sig_s_len;
91 };
92 
93 struct sk_resident_key {
94 	uint8_t alg;
95 	size_t slot;
96 	char *application;
97 	struct sk_enroll_response key;
98 };
99 
100 /* If building as part of OpenSSH, then rename exported functions */
101 #if !defined(SK_STANDALONE)
102 #define sk_api_version		ssh_sk_api_version
103 #define sk_enroll		ssh_sk_enroll
104 #define sk_sign			ssh_sk_sign
105 #define sk_load_resident_keys	ssh_sk_load_resident_keys
106 #endif
107 
108 /* Return the version of the middleware API */
109 uint32_t sk_api_version(void);
110 
111 /* Enroll a U2F key (private key generation) */
112 int sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len,
113     const char *application, uint8_t flags, const char *pin,
114     struct sk_enroll_response **enroll_response);
115 
116 /* Sign a challenge */
117 int sk_sign(int alg, const uint8_t *message, size_t message_len,
118     const char *application, const uint8_t *key_handle, size_t key_handle_len,
119     uint8_t flags, const char *pin, struct sk_sign_response **sign_response);
120 
121 /* Load resident keys */
122 int sk_load_resident_keys(const char *pin,
123     struct sk_resident_key ***rks, size_t *nrks);
124 
125 static void skdebug(const char *func, const char *fmt, ...)
126     __attribute__((__format__ (printf, 2, 3)));
127 
128 static void
129 skdebug(const char *func, const char *fmt, ...)
130 {
131 #if !defined(SK_STANDALONE)
132 	char *msg;
133 	va_list ap;
134 
135 	va_start(ap, fmt);
136 	xvasprintf(&msg, fmt, ap);
137 	va_end(ap);
138 	debug("%s: %s", func, msg);
139 	free(msg);
140 #elif defined(SK_DEBUG)
141 	va_list ap;
142 
143 	va_start(ap, fmt);
144 	fprintf(stderr, "%s: ", func);
145 	vfprintf(stderr, fmt, ap);
146 	fputc('\n', stderr);
147 	va_end(ap);
148 #else
149 	(void)func; /* XXX */
150 	(void)fmt; /* XXX */
151 #endif
152 }
153 
154 uint32_t
155 sk_api_version(void)
156 {
157 	return SK_VERSION_MAJOR;
158 }
159 
160 /* Select the first identified FIDO device attached to the system */
161 static char *
162 pick_first_device(void)
163 {
164 	char *ret = NULL;
165 	fido_dev_info_t *devlist = NULL;
166 	size_t olen = 0;
167 	int r;
168 	const fido_dev_info_t *di;
169 
170 	if ((devlist = fido_dev_info_new(1)) == NULL) {
171 		skdebug(__func__, "fido_dev_info_new failed");
172 		goto out;
173 	}
174 	if ((r = fido_dev_info_manifest(devlist, 1, &olen)) != FIDO_OK) {
175 		skdebug(__func__, "fido_dev_info_manifest failed: %s",
176 		    fido_strerr(r));
177 		goto out;
178 	}
179 	if (olen != 1) {
180 		skdebug(__func__, "fido_dev_info_manifest bad len %zu", olen);
181 		goto out;
182 	}
183 	di = fido_dev_info_ptr(devlist, 0);
184 	if ((ret = strdup(fido_dev_info_path(di))) == NULL) {
185 		skdebug(__func__, "fido_dev_info_path failed");
186 		goto out;
187 	}
188  out:
189 	fido_dev_info_free(&devlist, 1);
190 	return ret;
191 }
192 
193 /* Check if the specified key handle exists on a given device. */
194 static int
195 try_device(fido_dev_t *dev, const uint8_t *message, size_t message_len,
196     const char *application, const uint8_t *key_handle, size_t key_handle_len)
197 {
198 	fido_assert_t *assert = NULL;
199 	int r = FIDO_ERR_INTERNAL;
200 
201 	if ((assert = fido_assert_new()) == NULL) {
202 		skdebug(__func__, "fido_assert_new failed");
203 		goto out;
204 	}
205 	if ((r = fido_assert_set_clientdata_hash(assert, message,
206 	    message_len)) != FIDO_OK) {
207 		skdebug(__func__, "fido_assert_set_clientdata_hash: %s",
208 		    fido_strerr(r));
209 		goto out;
210 	}
211 	if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) {
212 		skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r));
213 		goto out;
214 	}
215 	if ((r = fido_assert_allow_cred(assert, key_handle,
216 	    key_handle_len)) != FIDO_OK) {
217 		skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r));
218 		goto out;
219 	}
220 	if ((r = fido_assert_set_up(assert, FIDO_OPT_FALSE)) != FIDO_OK) {
221 		skdebug(__func__, "fido_assert_up: %s", fido_strerr(r));
222 		goto out;
223 	}
224 	r = fido_dev_get_assert(dev, assert, NULL);
225 	skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r));
226 	if (r == FIDO_ERR_USER_PRESENCE_REQUIRED) {
227 		/* U2F tokens may return this */
228 		r = FIDO_OK;
229 	}
230  out:
231 	fido_assert_free(&assert);
232 
233 	return r != FIDO_OK ? -1 : 0;
234 }
235 
236 /* Iterate over configured devices looking for a specific key handle */
237 static fido_dev_t *
238 find_device(const uint8_t *message, size_t message_len, const char *application,
239     const uint8_t *key_handle, size_t key_handle_len)
240 {
241 	fido_dev_info_t *devlist = NULL;
242 	fido_dev_t *dev = NULL;
243 	size_t devlist_len = 0, i;
244 	const char *path;
245 	int r;
246 
247 	if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) {
248 		skdebug(__func__, "fido_dev_info_new failed");
249 		goto out;
250 	}
251 	if ((r = fido_dev_info_manifest(devlist, MAX_FIDO_DEVICES,
252 	    &devlist_len)) != FIDO_OK) {
253 		skdebug(__func__, "fido_dev_info_manifest: %s", fido_strerr(r));
254 		goto out;
255 	}
256 
257 	skdebug(__func__, "found %zu device(s)", devlist_len);
258 
259 	for (i = 0; i < devlist_len; i++) {
260 		const fido_dev_info_t *di = fido_dev_info_ptr(devlist, i);
261 
262 		if (di == NULL) {
263 			skdebug(__func__, "fido_dev_info_ptr %zu failed", i);
264 			continue;
265 		}
266 		if ((path = fido_dev_info_path(di)) == NULL) {
267 			skdebug(__func__, "fido_dev_info_path %zu failed", i);
268 			continue;
269 		}
270 		skdebug(__func__, "trying device %zu: %s", i, path);
271 		if ((dev = fido_dev_new()) == NULL) {
272 			skdebug(__func__, "fido_dev_new failed");
273 			continue;
274 		}
275 		if ((r = fido_dev_open(dev, path)) != FIDO_OK) {
276 			skdebug(__func__, "fido_dev_open failed");
277 			fido_dev_free(&dev);
278 			continue;
279 		}
280 		if (try_device(dev, message, message_len, application,
281 		    key_handle, key_handle_len) == 0) {
282 			skdebug(__func__, "found key");
283 			break;
284 		}
285 		fido_dev_close(dev);
286 		fido_dev_free(&dev);
287 	}
288 
289  out:
290 	if (devlist != NULL)
291 		fido_dev_info_free(&devlist, MAX_FIDO_DEVICES);
292 
293 	return dev;
294 }
295 
296 #ifdef WITH_OPENSSL
297 /*
298  * The key returned via fido_cred_pubkey_ptr() is in affine coordinates,
299  * but the API expects a SEC1 octet string.
300  */
301 static int
302 pack_public_key_ecdsa(const fido_cred_t *cred,
303     struct sk_enroll_response *response)
304 {
305 	const uint8_t *ptr;
306 	BIGNUM *x = NULL, *y = NULL;
307 	EC_POINT *q = NULL;
308 	EC_GROUP *g = NULL;
309 	int ret = -1;
310 
311 	response->public_key = NULL;
312 	response->public_key_len = 0;
313 
314 	if ((x = BN_new()) == NULL ||
315 	    (y = BN_new()) == NULL ||
316 	    (g = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)) == NULL ||
317 	    (q = EC_POINT_new(g)) == NULL) {
318 		skdebug(__func__, "libcrypto setup failed");
319 		goto out;
320 	}
321 	if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) {
322 		skdebug(__func__, "fido_cred_pubkey_ptr failed");
323 		goto out;
324 	}
325 	if (fido_cred_pubkey_len(cred) != 64) {
326 		skdebug(__func__, "bad fido_cred_pubkey_len %zu",
327 		    fido_cred_pubkey_len(cred));
328 		goto out;
329 	}
330 
331 	if (BN_bin2bn(ptr, 32, x) == NULL ||
332 	    BN_bin2bn(ptr + 32, 32, y) == NULL) {
333 		skdebug(__func__, "BN_bin2bn failed");
334 		goto out;
335 	}
336 	if (EC_POINT_set_affine_coordinates_GFp(g, q, x, y, NULL) != 1) {
337 		skdebug(__func__, "EC_POINT_set_affine_coordinates_GFp failed");
338 		goto out;
339 	}
340 	response->public_key_len = EC_POINT_point2oct(g, q,
341 	    POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL);
342 	if (response->public_key_len == 0 || response->public_key_len > 2048) {
343 		skdebug(__func__, "bad pubkey length %zu",
344 		    response->public_key_len);
345 		goto out;
346 	}
347 	if ((response->public_key = malloc(response->public_key_len)) == NULL) {
348 		skdebug(__func__, "malloc pubkey failed");
349 		goto out;
350 	}
351 	if (EC_POINT_point2oct(g, q, POINT_CONVERSION_UNCOMPRESSED,
352 	    response->public_key, response->public_key_len, NULL) == 0) {
353 		skdebug(__func__, "EC_POINT_point2oct failed");
354 		goto out;
355 	}
356 	/* success */
357 	ret = 0;
358  out:
359 	if (ret != 0 && response->public_key != NULL) {
360 		memset(response->public_key, 0, response->public_key_len);
361 		free(response->public_key);
362 		response->public_key = NULL;
363 	}
364 	EC_POINT_free(q);
365 	EC_GROUP_free(g);
366 	BN_clear_free(x);
367 	BN_clear_free(y);
368 	return ret;
369 }
370 #endif /* WITH_OPENSSL */
371 
372 static int
373 pack_public_key_ed25519(const fido_cred_t *cred,
374     struct sk_enroll_response *response)
375 {
376 	const uint8_t *ptr;
377 	size_t len;
378 	int ret = -1;
379 
380 	response->public_key = NULL;
381 	response->public_key_len = 0;
382 
383 	if ((len = fido_cred_pubkey_len(cred)) != 32) {
384 		skdebug(__func__, "bad fido_cred_pubkey_len len %zu", len);
385 		goto out;
386 	}
387 	if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) {
388 		skdebug(__func__, "fido_cred_pubkey_ptr failed");
389 		goto out;
390 	}
391 	response->public_key_len = len;
392 	if ((response->public_key = malloc(response->public_key_len)) == NULL) {
393 		skdebug(__func__, "malloc pubkey failed");
394 		goto out;
395 	}
396 	memcpy(response->public_key, ptr, len);
397 	ret = 0;
398  out:
399 	if (ret != 0)
400 		free(response->public_key);
401 	return ret;
402 }
403 
404 static int
405 pack_public_key(int alg, const fido_cred_t *cred,
406     struct sk_enroll_response *response)
407 {
408 	switch(alg) {
409 #ifdef WITH_OPENSSL
410 	case SK_ECDSA:
411 		return pack_public_key_ecdsa(cred, response);
412 #endif /* WITH_OPENSSL */
413 	case SK_ED25519:
414 		return pack_public_key_ed25519(cred, response);
415 	default:
416 		return -1;
417 	}
418 }
419 
420 static int
421 fidoerr_to_skerr(int fidoerr)
422 {
423 	switch (fidoerr) {
424 	case FIDO_ERR_UNSUPPORTED_OPTION:
425 		return SSH_SK_ERR_UNSUPPORTED;
426 	case FIDO_ERR_PIN_REQUIRED:
427 	case FIDO_ERR_PIN_INVALID:
428 		return SSH_SK_ERR_PIN_REQUIRED;
429 	default:
430 		return -1;
431 	}
432 }
433 
434 int
435 sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len,
436     const char *application, uint8_t flags, const char *pin,
437     struct sk_enroll_response **enroll_response)
438 {
439 	fido_cred_t *cred = NULL;
440 	fido_dev_t *dev = NULL;
441 	const uint8_t *ptr;
442 	uint8_t user_id[32];
443 	struct sk_enroll_response *response = NULL;
444 	size_t len;
445 	int cose_alg;
446 	int ret = SSH_SK_ERR_GENERAL;
447 	int r;
448 	char *device = NULL;
449 
450 #ifdef SK_DEBUG
451 	fido_init(FIDO_DEBUG);
452 #endif
453 	if (enroll_response == NULL) {
454 		skdebug(__func__, "enroll_response == NULL");
455 		goto out;
456 	}
457 	*enroll_response = NULL;
458 	switch(alg) {
459 #ifdef WITH_OPENSSL
460 	case SK_ECDSA:
461 		cose_alg = COSE_ES256;
462 		break;
463 #endif /* WITH_OPENSSL */
464 	case SK_ED25519:
465 		cose_alg = COSE_EDDSA;
466 		break;
467 	default:
468 		skdebug(__func__, "unsupported key type %d", alg);
469 		goto out;
470 	}
471 	if ((device = pick_first_device()) == NULL) {
472 		skdebug(__func__, "pick_first_device failed");
473 		goto out;
474 	}
475 	skdebug(__func__, "using device %s", device);
476 	if ((cred = fido_cred_new()) == NULL) {
477 		skdebug(__func__, "fido_cred_new failed");
478 		goto out;
479 	}
480 	memset(user_id, 0, sizeof(user_id));
481 	if ((r = fido_cred_set_type(cred, cose_alg)) != FIDO_OK) {
482 		skdebug(__func__, "fido_cred_set_type: %s", fido_strerr(r));
483 		goto out;
484 	}
485 	if ((r = fido_cred_set_clientdata_hash(cred, challenge,
486 	    challenge_len)) != FIDO_OK) {
487 		skdebug(__func__, "fido_cred_set_clientdata_hash: %s",
488 		    fido_strerr(r));
489 		goto out;
490 	}
491 	if ((r = fido_cred_set_rk(cred, (flags & SK_RESIDENT_KEY) != 0 ?
492 	    FIDO_OPT_TRUE : FIDO_OPT_OMIT)) != FIDO_OK) {
493 		skdebug(__func__, "fido_cred_set_rk: %s", fido_strerr(r));
494 		goto out;
495 	}
496 	if ((r = fido_cred_set_user(cred, user_id, sizeof(user_id),
497 	    "openssh", "openssh", NULL)) != FIDO_OK) {
498 		skdebug(__func__, "fido_cred_set_user: %s", fido_strerr(r));
499 		goto out;
500 	}
501 	if ((r = fido_cred_set_rp(cred, application, NULL)) != FIDO_OK) {
502 		skdebug(__func__, "fido_cred_set_rp: %s", fido_strerr(r));
503 		goto out;
504 	}
505 	if ((dev = fido_dev_new()) == NULL) {
506 		skdebug(__func__, "fido_dev_new failed");
507 		goto out;
508 	}
509 	if ((r = fido_dev_open(dev, device)) != FIDO_OK) {
510 		skdebug(__func__, "fido_dev_open: %s", fido_strerr(r));
511 		goto out;
512 	}
513 	if ((r = fido_dev_make_cred(dev, cred, pin)) != FIDO_OK) {
514 		skdebug(__func__, "fido_dev_make_cred: %s", fido_strerr(r));
515 		ret = fidoerr_to_skerr(r);
516 		goto out;
517 	}
518 	if (fido_cred_x5c_ptr(cred) != NULL) {
519 		if ((r = fido_cred_verify(cred)) != FIDO_OK) {
520 			skdebug(__func__, "fido_cred_verify: %s",
521 			    fido_strerr(r));
522 			goto out;
523 		}
524 	} else {
525 		skdebug(__func__, "self-attested credential");
526 		if ((r = fido_cred_verify_self(cred)) != FIDO_OK) {
527 			skdebug(__func__, "fido_cred_verify_self: %s",
528 			    fido_strerr(r));
529 			goto out;
530 		}
531 	}
532 	if ((response = calloc(1, sizeof(*response))) == NULL) {
533 		skdebug(__func__, "calloc response failed");
534 		goto out;
535 	}
536 	if (pack_public_key(alg, cred, response) != 0) {
537 		skdebug(__func__, "pack_public_key failed");
538 		goto out;
539 	}
540 	if ((ptr = fido_cred_id_ptr(cred)) != NULL) {
541 		len = fido_cred_id_len(cred);
542 		if ((response->key_handle = calloc(1, len)) == NULL) {
543 			skdebug(__func__, "calloc key handle failed");
544 			goto out;
545 		}
546 		memcpy(response->key_handle, ptr, len);
547 		response->key_handle_len = len;
548 	}
549 	if ((ptr = fido_cred_sig_ptr(cred)) != NULL) {
550 		len = fido_cred_sig_len(cred);
551 		if ((response->signature = calloc(1, len)) == NULL) {
552 			skdebug(__func__, "calloc signature failed");
553 			goto out;
554 		}
555 		memcpy(response->signature, ptr, len);
556 		response->signature_len = len;
557 	}
558 	if ((ptr = fido_cred_x5c_ptr(cred)) != NULL) {
559 		len = fido_cred_x5c_len(cred);
560 		if ((response->attestation_cert = calloc(1, len)) == NULL) {
561 			skdebug(__func__, "calloc attestation cert failed");
562 			goto out;
563 		}
564 		memcpy(response->attestation_cert, ptr, len);
565 		response->attestation_cert_len = len;
566 	}
567 	*enroll_response = response;
568 	response = NULL;
569 	ret = 0;
570  out:
571 	free(device);
572 	if (response != NULL) {
573 		free(response->public_key);
574 		free(response->key_handle);
575 		free(response->signature);
576 		free(response->attestation_cert);
577 		free(response);
578 	}
579 	if (dev != NULL) {
580 		fido_dev_close(dev);
581 		fido_dev_free(&dev);
582 	}
583 	if (cred != NULL) {
584 		fido_cred_free(&cred);
585 	}
586 	return ret;
587 }
588 
589 #ifdef WITH_OPENSSL
590 static int
591 pack_sig_ecdsa(fido_assert_t *assert, struct sk_sign_response *response)
592 {
593 	ECDSA_SIG *sig = NULL;
594 	const BIGNUM *sig_r, *sig_s;
595 	const unsigned char *cp;
596 	size_t sig_len;
597 	int ret = -1;
598 
599 	cp = fido_assert_sig_ptr(assert, 0);
600 	sig_len = fido_assert_sig_len(assert, 0);
601 	if ((sig = d2i_ECDSA_SIG(NULL, &cp, sig_len)) == NULL) {
602 		skdebug(__func__, "d2i_ECDSA_SIG failed");
603 		goto out;
604 	}
605 	ECDSA_SIG_get0(sig, &sig_r, &sig_s);
606 	response->sig_r_len = BN_num_bytes(sig_r);
607 	response->sig_s_len = BN_num_bytes(sig_s);
608 	if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL ||
609 	    (response->sig_s = calloc(1, response->sig_s_len)) == NULL) {
610 		skdebug(__func__, "calloc signature failed");
611 		goto out;
612 	}
613 	BN_bn2bin(sig_r, response->sig_r);
614 	BN_bn2bin(sig_s, response->sig_s);
615 	ret = 0;
616  out:
617 	ECDSA_SIG_free(sig);
618 	if (ret != 0) {
619 		free(response->sig_r);
620 		free(response->sig_s);
621 		response->sig_r = NULL;
622 		response->sig_s = NULL;
623 	}
624 	return ret;
625 }
626 #endif /* WITH_OPENSSL */
627 
628 static int
629 pack_sig_ed25519(fido_assert_t *assert, struct sk_sign_response *response)
630 {
631 	const unsigned char *ptr;
632 	size_t len;
633 	int ret = -1;
634 
635 	ptr = fido_assert_sig_ptr(assert, 0);
636 	len = fido_assert_sig_len(assert, 0);
637 	if (len != 64) {
638 		skdebug(__func__, "bad length %zu", len);
639 		goto out;
640 	}
641 	response->sig_r_len = len;
642 	if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL) {
643 		skdebug(__func__, "calloc signature failed");
644 		goto out;
645 	}
646 	memcpy(response->sig_r, ptr, len);
647 	ret = 0;
648  out:
649 	if (ret != 0) {
650 		free(response->sig_r);
651 		response->sig_r = NULL;
652 	}
653 	return ret;
654 }
655 
656 static int
657 pack_sig(int alg, fido_assert_t *assert, struct sk_sign_response *response)
658 {
659 	switch(alg) {
660 #ifdef WITH_OPENSSL
661 	case SK_ECDSA:
662 		return pack_sig_ecdsa(assert, response);
663 #endif /* WITH_OPENSSL */
664 	case SK_ED25519:
665 		return pack_sig_ed25519(assert, response);
666 	default:
667 		return -1;
668 	}
669 }
670 
671 int
672 sk_sign(int alg, const uint8_t *message, size_t message_len,
673     const char *application,
674     const uint8_t *key_handle, size_t key_handle_len,
675     uint8_t flags, const char *pin, struct sk_sign_response **sign_response)
676 {
677 	fido_assert_t *assert = NULL;
678 	fido_dev_t *dev = NULL;
679 	struct sk_sign_response *response = NULL;
680 	int ret = SSH_SK_ERR_GENERAL;
681 	int r;
682 
683 #ifdef SK_DEBUG
684 	fido_init(FIDO_DEBUG);
685 #endif
686 
687 	if (sign_response == NULL) {
688 		skdebug(__func__, "sign_response == NULL");
689 		goto out;
690 	}
691 	*sign_response = NULL;
692 	if ((dev = find_device(message, message_len, application, key_handle,
693 	    key_handle_len)) == NULL) {
694 		skdebug(__func__, "couldn't find device for key handle");
695 		goto out;
696 	}
697 	if ((assert = fido_assert_new()) == NULL) {
698 		skdebug(__func__, "fido_assert_new failed");
699 		goto out;
700 	}
701 	if ((r = fido_assert_set_clientdata_hash(assert, message,
702 	    message_len)) != FIDO_OK) {
703 		skdebug(__func__, "fido_assert_set_clientdata_hash: %s",
704 		    fido_strerr(r));
705 		goto out;
706 	}
707 	if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) {
708 		skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r));
709 		goto out;
710 	}
711 	if ((r = fido_assert_allow_cred(assert, key_handle,
712 	    key_handle_len)) != FIDO_OK) {
713 		skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r));
714 		goto out;
715 	}
716 	if ((r = fido_assert_set_up(assert,
717 	    (flags & SK_USER_PRESENCE_REQD) ?
718 	    FIDO_OPT_TRUE : FIDO_OPT_FALSE)) != FIDO_OK) {
719 		skdebug(__func__, "fido_assert_set_up: %s", fido_strerr(r));
720 		goto out;
721 	}
722 	if ((r = fido_dev_get_assert(dev, assert, NULL)) != FIDO_OK) {
723 		skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r));
724 		goto out;
725 	}
726 	if ((response = calloc(1, sizeof(*response))) == NULL) {
727 		skdebug(__func__, "calloc response failed");
728 		goto out;
729 	}
730 	response->flags = fido_assert_flags(assert, 0);
731 	response->counter = fido_assert_sigcount(assert, 0);
732 	if (pack_sig(alg, assert, response) != 0) {
733 		skdebug(__func__, "pack_sig failed");
734 		goto out;
735 	}
736 	*sign_response = response;
737 	response = NULL;
738 	ret = 0;
739  out:
740 	if (response != NULL) {
741 		free(response->sig_r);
742 		free(response->sig_s);
743 		free(response);
744 	}
745 	if (dev != NULL) {
746 		fido_dev_close(dev);
747 		fido_dev_free(&dev);
748 	}
749 	if (assert != NULL) {
750 		fido_assert_free(&assert);
751 	}
752 	return ret;
753 }
754 
755 static int
756 read_rks(const char *devpath, const char *pin,
757     struct sk_resident_key ***rksp, size_t *nrksp)
758 {
759 	int ret = SSH_SK_ERR_GENERAL, r = -1;
760 	fido_dev_t *dev = NULL;
761 	fido_credman_metadata_t *metadata = NULL;
762 	fido_credman_rp_t *rp = NULL;
763 	fido_credman_rk_t *rk = NULL;
764 	size_t i, j, nrp, nrk;
765 	const fido_cred_t *cred;
766 	struct sk_resident_key *srk = NULL, **tmp;
767 
768 	if ((dev = fido_dev_new()) == NULL) {
769 		skdebug(__func__, "fido_dev_new failed");
770 		return ret;
771 	}
772 	if ((r = fido_dev_open(dev, devpath)) != FIDO_OK) {
773 		skdebug(__func__, "fido_dev_open %s failed: %s",
774 		    devpath, fido_strerr(r));
775 		fido_dev_free(&dev);
776 		return ret;
777 	}
778 	if ((metadata = fido_credman_metadata_new()) == NULL) {
779 		skdebug(__func__, "alloc failed");
780 		goto out;
781 	}
782 
783 	if ((r = fido_credman_get_dev_metadata(dev, metadata, pin)) != 0) {
784 		if (r == FIDO_ERR_INVALID_COMMAND) {
785 			skdebug(__func__, "device %s does not support "
786 			    "resident keys", devpath);
787 			ret = 0;
788 			goto out;
789 		}
790 		skdebug(__func__, "get metadata for %s failed: %s",
791 		    devpath, fido_strerr(r));
792 		goto out;
793 	}
794 	skdebug(__func__, "existing %llu, remaining %llu",
795 	    (unsigned long long)fido_credman_rk_existing(metadata),
796 	    (unsigned long long)fido_credman_rk_remaining(metadata));
797 	if ((rp = fido_credman_rp_new()) == NULL) {
798 		skdebug(__func__, "alloc rp failed");
799 		goto out;
800 	}
801 	if ((r = fido_credman_get_dev_rp(dev, rp, pin)) != 0) {
802 		skdebug(__func__, "get RPs for %s failed: %s",
803 		    devpath, fido_strerr(r));
804 		goto out;
805 	}
806 	nrp = fido_credman_rp_count(rp);
807 	skdebug(__func__, "Device %s has resident keys for %zu RPs",
808 	    devpath, nrp);
809 
810 	/* Iterate over RP IDs that have resident keys */
811 	for (i = 0; i < nrp; i++) {
812 		skdebug(__func__, "rp %zu: name=\"%s\" id=\"%s\" hashlen=%zu",
813 		    i, fido_credman_rp_name(rp, i), fido_credman_rp_id(rp, i),
814 		    fido_credman_rp_id_hash_len(rp, i));
815 
816 		/* Skip non-SSH RP IDs */
817 		if (strncasecmp(fido_credman_rp_id(rp, i), "ssh:", 4) != 0)
818 			continue;
819 
820 		fido_credman_rk_free(&rk);
821 		if ((rk = fido_credman_rk_new()) == NULL) {
822 			skdebug(__func__, "alloc rk failed");
823 			goto out;
824 		}
825 		if ((r = fido_credman_get_dev_rk(dev, fido_credman_rp_id(rp, i),
826 		    rk, pin)) != 0) {
827 			skdebug(__func__, "get RKs for %s slot %zu failed: %s",
828 			    devpath, i, fido_strerr(r));
829 			goto out;
830 		}
831 		nrk = fido_credman_rk_count(rk);
832 		skdebug(__func__, "RP \"%s\" has %zu resident keys",
833 		    fido_credman_rp_id(rp, i), nrk);
834 
835 		/* Iterate over resident keys for this RP ID */
836 		for (j = 0; j < nrk; j++) {
837 			if ((cred = fido_credman_rk(rk, j)) == NULL) {
838 				skdebug(__func__, "no RK in slot %zu", j);
839 				continue;
840 			}
841 			skdebug(__func__, "Device %s RP \"%s\" slot %zu: "
842 			    "type %d", devpath, fido_credman_rp_id(rp, i), j,
843 			    fido_cred_type(cred));
844 
845 			/* build response entry */
846 			if ((srk = calloc(1, sizeof(*srk))) == NULL ||
847 			    (srk->key.key_handle = calloc(1,
848 			    fido_cred_id_len(cred))) == NULL ||
849 			    (srk->application = strdup(fido_credman_rp_id(rp,
850 			    i))) == NULL) {
851 				skdebug(__func__, "alloc sk_resident_key");
852 				goto out;
853 			}
854 
855 			srk->key.key_handle_len = fido_cred_id_len(cred);
856 			memcpy(srk->key.key_handle,
857 			    fido_cred_id_ptr(cred),
858 			    srk->key.key_handle_len);
859 
860 			switch (fido_cred_type(cred)) {
861 			case COSE_ES256:
862 				srk->alg = SK_ECDSA;
863 				break;
864 			case COSE_EDDSA:
865 				srk->alg = SK_ED25519;
866 				break;
867 			default:
868 				skdebug(__func__, "unsupported key type %d",
869 				    fido_cred_type(cred));
870 				goto out;
871 			}
872 
873 			if ((r = pack_public_key(srk->alg, cred,
874 			    &srk->key)) != 0) {
875 				skdebug(__func__, "pack public key failed");
876 				goto out;
877 			}
878 			/* append */
879 			if ((tmp = recallocarray(*rksp, *nrksp, (*nrksp) + 1,
880 			    sizeof(**rksp))) == NULL) {
881 				skdebug(__func__, "alloc rksp");
882 				goto out;
883 			}
884 			*rksp = tmp;
885 			(*rksp)[(*nrksp)++] = srk;
886 			srk = NULL;
887 		}
888 	}
889 	/* Success */
890 	ret = 0;
891  out:
892 	if (srk != NULL) {
893 		free(srk->application);
894 		freezero(srk->key.public_key, srk->key.public_key_len);
895 		freezero(srk->key.key_handle, srk->key.key_handle_len);
896 		freezero(srk, sizeof(*srk));
897 	}
898 	fido_credman_rp_free(&rp);
899 	fido_credman_rk_free(&rk);
900 	fido_dev_close(dev);
901 	fido_dev_free(&dev);
902 	fido_credman_metadata_free(&metadata);
903 	return ret;
904 }
905 
906 int
907 sk_load_resident_keys(const char *pin,
908     struct sk_resident_key ***rksp, size_t *nrksp)
909 {
910 	int ret = SSH_SK_ERR_GENERAL, r = -1;
911 	fido_dev_info_t *devlist = NULL;
912 	size_t i, ndev = 0, nrks = 0;
913 	const fido_dev_info_t *di;
914 	struct sk_resident_key **rks = NULL;
915 	*rksp = NULL;
916 	*nrksp = 0;
917 
918 	if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) {
919 		skdebug(__func__, "fido_dev_info_new failed");
920 		goto out;
921 	}
922 	if ((r = fido_dev_info_manifest(devlist,
923 	    MAX_FIDO_DEVICES, &ndev)) != FIDO_OK) {
924 		skdebug(__func__, "fido_dev_info_manifest failed: %s",
925 		    fido_strerr(r));
926 		goto out;
927 	}
928 	for (i = 0; i < ndev; i++) {
929 		if ((di = fido_dev_info_ptr(devlist, i)) == NULL) {
930 			skdebug(__func__, "no dev info at %zu", i);
931 			continue;
932 		}
933 		skdebug(__func__, "trying %s", fido_dev_info_path(di));
934 		if ((r = read_rks(fido_dev_info_path(di), pin,
935 		    &rks, &nrks)) != 0) {
936 			skdebug(__func__, "read_rks failed for %s",
937 			    fido_dev_info_path(di));
938 			continue;
939 		}
940 	}
941 	/* success */
942 	ret = 0;
943 	*rksp = rks;
944 	*nrksp = nrks;
945 	rks = NULL;
946 	nrks = 0;
947  out:
948 	for (i = 0; i < nrks; i++) {
949 		free(rks[i]->application);
950 		freezero(rks[i]->key.public_key, rks[i]->key.public_key_len);
951 		freezero(rks[i]->key.key_handle, rks[i]->key.key_handle_len);
952 		freezero(rks[i], sizeof(*rks[i]));
953 	}
954 	free(rks);
955 	fido_dev_info_free(&devlist, MAX_FIDO_DEVICES);
956 	return ret;
957 }
958 
959 #endif /* ENABLE_SK_INTERNAL */
960