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