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