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