xref: /openssh-portable/sk-usbhid.c (revision 24c0f752)
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
skdebug(const char * func,const char * fmt,...)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
sk_api_version(void)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 *
pick_first_device(void)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
try_device(fido_dev_t * dev,const uint8_t * message,size_t message_len,const char * application,const uint8_t * key_handle,size_t key_handle_len)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 *
find_device(const char * path,const uint8_t * message,size_t message_len,const char * application,const uint8_t * key_handle,size_t key_handle_len)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
pack_public_key_ecdsa(const fido_cred_t * cred,struct sk_enroll_response * response)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
pack_public_key_ed25519(const fido_cred_t * cred,struct sk_enroll_response * response)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
pack_public_key(uint32_t alg,const fido_cred_t * cred,struct sk_enroll_response * response)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
fidoerr_to_skerr(int fidoerr)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
check_enroll_options(struct sk_option ** options,char ** devicep,uint8_t * user_id,size_t user_id_len)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
sk_enroll(uint32_t alg,const uint8_t * challenge,size_t challenge_len,const char * application,uint8_t flags,const char * pin,struct sk_option ** options,struct sk_enroll_response ** enroll_response)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 		debug3("%s: attestation cert len=%zu", __func__, len);
574 		if ((response->attestation_cert = calloc(1, len)) == NULL) {
575 			skdebug(__func__, "calloc attestation cert failed");
576 			goto out;
577 		}
578 		memcpy(response->attestation_cert, ptr, len);
579 		response->attestation_cert_len = len;
580 	}
581 	*enroll_response = response;
582 	response = NULL;
583 	ret = 0;
584  out:
585 	free(device);
586 	if (response != NULL) {
587 		free(response->public_key);
588 		free(response->key_handle);
589 		free(response->signature);
590 		free(response->attestation_cert);
591 		free(response);
592 	}
593 	if (dev != NULL) {
594 		fido_dev_close(dev);
595 		fido_dev_free(&dev);
596 	}
597 	if (cred != NULL) {
598 		fido_cred_free(&cred);
599 	}
600 	return ret;
601 }
602 
603 #ifdef WITH_OPENSSL
604 static int
pack_sig_ecdsa(fido_assert_t * assert,struct sk_sign_response * response)605 pack_sig_ecdsa(fido_assert_t *assert, struct sk_sign_response *response)
606 {
607 	ECDSA_SIG *sig = NULL;
608 	const BIGNUM *sig_r, *sig_s;
609 	const unsigned char *cp;
610 	size_t sig_len;
611 	int ret = -1;
612 
613 	cp = fido_assert_sig_ptr(assert, 0);
614 	sig_len = fido_assert_sig_len(assert, 0);
615 	if ((sig = d2i_ECDSA_SIG(NULL, &cp, sig_len)) == NULL) {
616 		skdebug(__func__, "d2i_ECDSA_SIG failed");
617 		goto out;
618 	}
619 	ECDSA_SIG_get0(sig, &sig_r, &sig_s);
620 	response->sig_r_len = BN_num_bytes(sig_r);
621 	response->sig_s_len = BN_num_bytes(sig_s);
622 	if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL ||
623 	    (response->sig_s = calloc(1, response->sig_s_len)) == NULL) {
624 		skdebug(__func__, "calloc signature failed");
625 		goto out;
626 	}
627 	BN_bn2bin(sig_r, response->sig_r);
628 	BN_bn2bin(sig_s, response->sig_s);
629 	ret = 0;
630  out:
631 	ECDSA_SIG_free(sig);
632 	if (ret != 0) {
633 		free(response->sig_r);
634 		free(response->sig_s);
635 		response->sig_r = NULL;
636 		response->sig_s = NULL;
637 	}
638 	return ret;
639 }
640 #endif /* WITH_OPENSSL */
641 
642 static int
pack_sig_ed25519(fido_assert_t * assert,struct sk_sign_response * response)643 pack_sig_ed25519(fido_assert_t *assert, struct sk_sign_response *response)
644 {
645 	const unsigned char *ptr;
646 	size_t len;
647 	int ret = -1;
648 
649 	ptr = fido_assert_sig_ptr(assert, 0);
650 	len = fido_assert_sig_len(assert, 0);
651 	if (len != 64) {
652 		skdebug(__func__, "bad length %zu", len);
653 		goto out;
654 	}
655 	response->sig_r_len = len;
656 	if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL) {
657 		skdebug(__func__, "calloc signature failed");
658 		goto out;
659 	}
660 	memcpy(response->sig_r, ptr, len);
661 	ret = 0;
662  out:
663 	if (ret != 0) {
664 		free(response->sig_r);
665 		response->sig_r = NULL;
666 	}
667 	return ret;
668 }
669 
670 static int
pack_sig(uint32_t alg,fido_assert_t * assert,struct sk_sign_response * response)671 pack_sig(uint32_t  alg, fido_assert_t *assert,
672     struct sk_sign_response *response)
673 {
674 	switch(alg) {
675 #ifdef WITH_OPENSSL
676 	case SSH_SK_ECDSA:
677 		return pack_sig_ecdsa(assert, response);
678 #endif /* WITH_OPENSSL */
679 	case SSH_SK_ED25519:
680 		return pack_sig_ed25519(assert, response);
681 	default:
682 		return -1;
683 	}
684 }
685 
686 /* Checks sk_options for sk_sign() and sk_load_resident_keys() */
687 static int
check_sign_load_resident_options(struct sk_option ** options,char ** devicep)688 check_sign_load_resident_options(struct sk_option **options, char **devicep)
689 {
690 	size_t i;
691 
692 	if (options == NULL)
693 		return 0;
694 	for (i = 0; options[i] != NULL; i++) {
695 		if (strcmp(options[i]->name, "device") == 0) {
696 			if ((*devicep = strdup(options[i]->value)) == NULL) {
697 				skdebug(__func__, "strdup device failed");
698 				return -1;
699 			}
700 			skdebug(__func__, "requested device %s", *devicep);
701 		} else {
702 			skdebug(__func__, "requested unsupported option %s",
703 			    options[i]->name);
704 			if (options[i]->required) {
705 				skdebug(__func__, "unknown required option");
706 				return -1;
707 			}
708 		}
709 	}
710 	return 0;
711 }
712 
713 int
sk_sign(uint32_t alg,const uint8_t * message,size_t message_len,const char * application,const uint8_t * key_handle,size_t key_handle_len,uint8_t flags,const char * pin,struct sk_option ** options,struct sk_sign_response ** sign_response)714 sk_sign(uint32_t alg, const uint8_t *message, size_t message_len,
715     const char *application,
716     const uint8_t *key_handle, size_t key_handle_len,
717     uint8_t flags, const char *pin, struct sk_option **options,
718     struct sk_sign_response **sign_response)
719 {
720 	fido_assert_t *assert = NULL;
721 	char *device = NULL;
722 	fido_dev_t *dev = NULL;
723 	struct sk_sign_response *response = NULL;
724 	int ret = SSH_SK_ERR_GENERAL;
725 	int r;
726 
727 #ifdef SK_DEBUG
728 	fido_init(FIDO_DEBUG);
729 #endif
730 
731 	if (sign_response == NULL) {
732 		skdebug(__func__, "sign_response == NULL");
733 		goto out;
734 	}
735 	*sign_response = NULL;
736 	if (check_sign_load_resident_options(options, &device) != 0)
737 		goto out; /* error already logged */
738 	if ((dev = find_device(device, message, message_len,
739 	    application, key_handle, key_handle_len)) == NULL) {
740 		skdebug(__func__, "couldn't find device for key handle");
741 		goto out;
742 	}
743 	if ((assert = fido_assert_new()) == NULL) {
744 		skdebug(__func__, "fido_assert_new failed");
745 		goto out;
746 	}
747 	if ((r = fido_assert_set_clientdata_hash(assert, message,
748 	    message_len)) != FIDO_OK) {
749 		skdebug(__func__, "fido_assert_set_clientdata_hash: %s",
750 		    fido_strerr(r));
751 		goto out;
752 	}
753 	if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) {
754 		skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r));
755 		goto out;
756 	}
757 	if ((r = fido_assert_allow_cred(assert, key_handle,
758 	    key_handle_len)) != FIDO_OK) {
759 		skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r));
760 		goto out;
761 	}
762 	if ((r = fido_assert_set_up(assert,
763 	    (flags & SSH_SK_USER_PRESENCE_REQD) ?
764 	    FIDO_OPT_TRUE : FIDO_OPT_FALSE)) != FIDO_OK) {
765 		skdebug(__func__, "fido_assert_set_up: %s", fido_strerr(r));
766 		goto out;
767 	}
768 	if ((r = fido_dev_get_assert(dev, assert, NULL)) != FIDO_OK) {
769 		skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r));
770 		goto out;
771 	}
772 	if ((response = calloc(1, sizeof(*response))) == NULL) {
773 		skdebug(__func__, "calloc response failed");
774 		goto out;
775 	}
776 	response->flags = fido_assert_flags(assert, 0);
777 	response->counter = fido_assert_sigcount(assert, 0);
778 	if (pack_sig(alg, assert, response) != 0) {
779 		skdebug(__func__, "pack_sig failed");
780 		goto out;
781 	}
782 	*sign_response = response;
783 	response = NULL;
784 	ret = 0;
785  out:
786 	free(device);
787 	if (response != NULL) {
788 		free(response->sig_r);
789 		free(response->sig_s);
790 		free(response);
791 	}
792 	if (dev != NULL) {
793 		fido_dev_close(dev);
794 		fido_dev_free(&dev);
795 	}
796 	if (assert != NULL) {
797 		fido_assert_free(&assert);
798 	}
799 	return ret;
800 }
801 
802 static int
read_rks(const char * devpath,const char * pin,struct sk_resident_key *** rksp,size_t * nrksp)803 read_rks(const char *devpath, const char *pin,
804     struct sk_resident_key ***rksp, size_t *nrksp)
805 {
806 	int ret = SSH_SK_ERR_GENERAL, r = -1;
807 	fido_dev_t *dev = NULL;
808 	fido_credman_metadata_t *metadata = NULL;
809 	fido_credman_rp_t *rp = NULL;
810 	fido_credman_rk_t *rk = NULL;
811 	size_t i, j, nrp, nrk;
812 	const fido_cred_t *cred;
813 	struct sk_resident_key *srk = NULL, **tmp;
814 
815 	if ((dev = fido_dev_new()) == NULL) {
816 		skdebug(__func__, "fido_dev_new failed");
817 		return ret;
818 	}
819 	if ((r = fido_dev_open(dev, devpath)) != FIDO_OK) {
820 		skdebug(__func__, "fido_dev_open %s failed: %s",
821 		    devpath, fido_strerr(r));
822 		fido_dev_free(&dev);
823 		return ret;
824 	}
825 	if ((metadata = fido_credman_metadata_new()) == NULL) {
826 		skdebug(__func__, "alloc failed");
827 		goto out;
828 	}
829 
830 	if ((r = fido_credman_get_dev_metadata(dev, metadata, pin)) != 0) {
831 		if (r == FIDO_ERR_INVALID_COMMAND) {
832 			skdebug(__func__, "device %s does not support "
833 			    "resident keys", devpath);
834 			ret = 0;
835 			goto out;
836 		}
837 		skdebug(__func__, "get metadata for %s failed: %s",
838 		    devpath, fido_strerr(r));
839 		ret = fidoerr_to_skerr(r);
840 		goto out;
841 	}
842 	skdebug(__func__, "existing %llu, remaining %llu",
843 	    (unsigned long long)fido_credman_rk_existing(metadata),
844 	    (unsigned long long)fido_credman_rk_remaining(metadata));
845 	if ((rp = fido_credman_rp_new()) == NULL) {
846 		skdebug(__func__, "alloc rp failed");
847 		goto out;
848 	}
849 	if ((r = fido_credman_get_dev_rp(dev, rp, pin)) != 0) {
850 		skdebug(__func__, "get RPs for %s failed: %s",
851 		    devpath, fido_strerr(r));
852 		goto out;
853 	}
854 	nrp = fido_credman_rp_count(rp);
855 	skdebug(__func__, "Device %s has resident keys for %zu RPs",
856 	    devpath, nrp);
857 
858 	/* Iterate over RP IDs that have resident keys */
859 	for (i = 0; i < nrp; i++) {
860 		skdebug(__func__, "rp %zu: name=\"%s\" id=\"%s\" hashlen=%zu",
861 		    i, fido_credman_rp_name(rp, i), fido_credman_rp_id(rp, i),
862 		    fido_credman_rp_id_hash_len(rp, i));
863 
864 		/* Skip non-SSH RP IDs */
865 		if (strncasecmp(fido_credman_rp_id(rp, i), "ssh:", 4) != 0)
866 			continue;
867 
868 		fido_credman_rk_free(&rk);
869 		if ((rk = fido_credman_rk_new()) == NULL) {
870 			skdebug(__func__, "alloc rk failed");
871 			goto out;
872 		}
873 		if ((r = fido_credman_get_dev_rk(dev, fido_credman_rp_id(rp, i),
874 		    rk, pin)) != 0) {
875 			skdebug(__func__, "get RKs for %s slot %zu failed: %s",
876 			    devpath, i, fido_strerr(r));
877 			goto out;
878 		}
879 		nrk = fido_credman_rk_count(rk);
880 		skdebug(__func__, "RP \"%s\" has %zu resident keys",
881 		    fido_credman_rp_id(rp, i), nrk);
882 
883 		/* Iterate over resident keys for this RP ID */
884 		for (j = 0; j < nrk; j++) {
885 			if ((cred = fido_credman_rk(rk, j)) == NULL) {
886 				skdebug(__func__, "no RK in slot %zu", j);
887 				continue;
888 			}
889 			skdebug(__func__, "Device %s RP \"%s\" slot %zu: "
890 			    "type %d", devpath, fido_credman_rp_id(rp, i), j,
891 			    fido_cred_type(cred));
892 
893 			/* build response entry */
894 			if ((srk = calloc(1, sizeof(*srk))) == NULL ||
895 			    (srk->key.key_handle = calloc(1,
896 			    fido_cred_id_len(cred))) == NULL ||
897 			    (srk->application = strdup(fido_credman_rp_id(rp,
898 			    i))) == NULL) {
899 				skdebug(__func__, "alloc sk_resident_key");
900 				goto out;
901 			}
902 
903 			srk->key.key_handle_len = fido_cred_id_len(cred);
904 			memcpy(srk->key.key_handle,
905 			    fido_cred_id_ptr(cred),
906 			    srk->key.key_handle_len);
907 
908 			switch (fido_cred_type(cred)) {
909 			case COSE_ES256:
910 				srk->alg = SSH_SK_ECDSA;
911 				break;
912 			case COSE_EDDSA:
913 				srk->alg = SSH_SK_ED25519;
914 				break;
915 			default:
916 				skdebug(__func__, "unsupported key type %d",
917 				    fido_cred_type(cred));
918 				goto out; /* XXX free rk and continue */
919 			}
920 
921 			if ((r = pack_public_key(srk->alg, cred,
922 			    &srk->key)) != 0) {
923 				skdebug(__func__, "pack public key failed");
924 				goto out;
925 			}
926 			/* append */
927 			if ((tmp = recallocarray(*rksp, *nrksp, (*nrksp) + 1,
928 			    sizeof(**rksp))) == NULL) {
929 				skdebug(__func__, "alloc rksp");
930 				goto out;
931 			}
932 			*rksp = tmp;
933 			(*rksp)[(*nrksp)++] = srk;
934 			srk = NULL;
935 		}
936 	}
937 	/* Success */
938 	ret = 0;
939  out:
940 	if (srk != NULL) {
941 		free(srk->application);
942 		freezero(srk->key.public_key, srk->key.public_key_len);
943 		freezero(srk->key.key_handle, srk->key.key_handle_len);
944 		freezero(srk, sizeof(*srk));
945 	}
946 	fido_credman_rp_free(&rp);
947 	fido_credman_rk_free(&rk);
948 	fido_dev_close(dev);
949 	fido_dev_free(&dev);
950 	fido_credman_metadata_free(&metadata);
951 	return ret;
952 }
953 
954 int
sk_load_resident_keys(const char * pin,struct sk_option ** options,struct sk_resident_key *** rksp,size_t * nrksp)955 sk_load_resident_keys(const char *pin, struct sk_option **options,
956     struct sk_resident_key ***rksp, size_t *nrksp)
957 {
958 	int ret = SSH_SK_ERR_GENERAL, r = -1;
959 	fido_dev_info_t *devlist = NULL;
960 	size_t i, ndev = 0, nrks = 0;
961 	const fido_dev_info_t *di;
962 	struct sk_resident_key **rks = NULL;
963 	char *device = NULL;
964 	*rksp = NULL;
965 	*nrksp = 0;
966 
967 	if (check_sign_load_resident_options(options, &device) != 0)
968 		goto out; /* error already logged */
969 	if (device != NULL) {
970 		skdebug(__func__, "trying %s", device);
971 		if ((r = read_rks(device, pin, &rks, &nrks)) != 0) {
972 			skdebug(__func__, "read_rks failed for %s", device);
973 			ret = r;
974 			goto out;
975 		}
976 	} else {
977 		/* Try all devices */
978 		if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) {
979 			skdebug(__func__, "fido_dev_info_new failed");
980 			goto out;
981 		}
982 		if ((r = fido_dev_info_manifest(devlist,
983 		    MAX_FIDO_DEVICES, &ndev)) != FIDO_OK) {
984 			skdebug(__func__, "fido_dev_info_manifest failed: %s",
985 			    fido_strerr(r));
986 			goto out;
987 		}
988 		for (i = 0; i < ndev; i++) {
989 			if ((di = fido_dev_info_ptr(devlist, i)) == NULL) {
990 				skdebug(__func__, "no dev info at %zu", i);
991 				continue;
992 			}
993 			skdebug(__func__, "trying %s", fido_dev_info_path(di));
994 			if ((r = read_rks(fido_dev_info_path(di), pin,
995 			    &rks, &nrks)) != 0) {
996 				skdebug(__func__, "read_rks failed for %s",
997 				    fido_dev_info_path(di));
998 				/* remember last error */
999 				ret = r;
1000 				continue;
1001 			}
1002 		}
1003 	}
1004 	/* success, unless we have no keys but a specific error */
1005 	if (nrks > 0 || ret == SSH_SK_ERR_GENERAL)
1006 		ret = 0;
1007 	*rksp = rks;
1008 	*nrksp = nrks;
1009 	rks = NULL;
1010 	nrks = 0;
1011  out:
1012 	free(device);
1013 	for (i = 0; i < nrks; i++) {
1014 		free(rks[i]->application);
1015 		freezero(rks[i]->key.public_key, rks[i]->key.public_key_len);
1016 		freezero(rks[i]->key.key_handle, rks[i]->key.key_handle_len);
1017 		freezero(rks[i], sizeof(*rks[i]));
1018 	}
1019 	free(rks);
1020 	fido_dev_info_free(&devlist, MAX_FIDO_DEVICES);
1021 	return ret;
1022 }
1023 
1024 #endif /* ENABLE_SK_INTERNAL */
1025