1 /* $NetBSD: sk-usbhid.c,v 1.8 2022/10/05 22:39:36 christos Exp $ */
2 /* $OpenBSD: sk-usbhid.c,v 1.45 2022/09/14 00:14:37 djm Exp $ */
3
4 /*
5 * Copyright (c) 2019 Markus Friedl
6 * Copyright (c) 2020 Pedro Martelletto
7 *
8 * Permission to use, copy, modify, and distribute this software for any
9 * purpose with or without fee is hereby granted, provided that the above
10 * copyright notice and this permission notice appear in all copies.
11 *
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 */
20 #include "includes.h"
21 __RCSID("$NetBSD: sk-usbhid.c,v 1.8 2022/10/05 22:39:36 christos Exp $");
22
23 #include <stdint.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <stdio.h>
27 #include <stddef.h>
28 #include <stdarg.h>
29 #include <time.h>
30
31 #ifdef WITH_OPENSSL
32 #include <openssl/opensslv.h>
33 #include <openssl/crypto.h>
34 #include <openssl/bn.h>
35 #include <openssl/ec.h>
36 #include <openssl/ecdsa.h>
37 #include <openssl/evp.h>
38 #endif /* WITH_OPENSSL */
39
40 #include <fido.h>
41 #include <fido/credman.h>
42
43 #ifndef SK_STANDALONE
44 # include "log.h"
45 # include "xmalloc.h"
46 # include "misc.h"
47 /*
48 * If building as part of OpenSSH, then rename exported functions.
49 * This must be done before including sk-api.h.
50 */
51 # define sk_api_version ssh_sk_api_version
52 # define sk_enroll ssh_sk_enroll
53 # define sk_sign ssh_sk_sign
54 # define sk_load_resident_keys ssh_sk_load_resident_keys
55 #endif /* !SK_STANDALONE */
56
57 #include "sk-api.h"
58
59 /* #define SK_DEBUG 1 */
60
61 #ifdef SK_DEBUG
62 #define SSH_FIDO_INIT_ARG FIDO_DEBUG
63 #else
64 #define SSH_FIDO_INIT_ARG 0
65 #endif
66
67 #define MAX_FIDO_DEVICES 8
68 #define FIDO_POLL_MS 50
69 #define SELECT_MS 15000
70 #define POLL_SLEEP_NS 200000000
71
72 /* Compatibility with OpenSSH 1.0.x */
73 #if (OPENSSL_VERSION_NUMBER < 0x10100000L)
74 #define ECDSA_SIG_get0(sig, pr, ps) \
75 do { \
76 (*pr) = sig->r; \
77 (*ps) = sig->s; \
78 } while (0)
79 #endif
80 #ifndef FIDO_ERR_OPERATION_DENIED
81 #define FIDO_ERR_OPERATION_DENIED 0x27
82 #endif
83
84 struct sk_usbhid {
85 fido_dev_t *dev;
86 char *path;
87 };
88
89 /* Return the version of the middleware API */
90 uint32_t sk_api_version(void);
91
92 /* Enroll a U2F key (private key generation) */
93 int sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len,
94 const char *application, uint8_t flags, const char *pin,
95 struct sk_option **options, struct sk_enroll_response **enroll_response);
96
97 /* Sign a challenge */
98 int sk_sign(uint32_t alg, const uint8_t *data, size_t data_len,
99 const char *application, const uint8_t *key_handle, size_t key_handle_len,
100 uint8_t flags, const char *pin, struct sk_option **options,
101 struct sk_sign_response **sign_response);
102
103 /* Load resident keys */
104 int sk_load_resident_keys(const char *pin, struct sk_option **options,
105 struct sk_resident_key ***rks, size_t *nrks);
106
107 static void skdebug(const char *func, const char *fmt, ...)
108 __attribute__((__format__ (printf, 2, 3)));
109
110 static void
skdebug(const char * func,const char * fmt,...)111 skdebug(const char *func, const char *fmt, ...)
112 {
113 #if !defined(SK_STANDALONE)
114 char *msg;
115 va_list ap;
116
117 va_start(ap, fmt);
118 xvasprintf(&msg, fmt, ap);
119 va_end(ap);
120 debug("%s: %s", func, msg);
121 free(msg);
122 #elif defined(SK_DEBUG)
123 va_list ap;
124
125 va_start(ap, fmt);
126 fprintf(stderr, "%s: ", func);
127 vfprintf(stderr, fmt, ap);
128 fputc('\n', stderr);
129 va_end(ap);
130 #else
131 (void)func; /* XXX */
132 (void)fmt; /* XXX */
133 #endif
134 }
135
136 uint32_t
sk_api_version(void)137 sk_api_version(void)
138 {
139 return SSH_SK_VERSION_MAJOR;
140 }
141
142 static struct sk_usbhid *
sk_open(const char * path)143 sk_open(const char *path)
144 {
145 struct sk_usbhid *sk;
146 int r;
147
148 if (path == NULL) {
149 skdebug(__func__, "path == NULL");
150 return NULL;
151 }
152 if ((sk = calloc(1, sizeof(*sk))) == NULL) {
153 skdebug(__func__, "calloc sk failed");
154 return NULL;
155 }
156 if ((sk->path = strdup(path)) == NULL) {
157 skdebug(__func__, "strdup path failed");
158 free(sk);
159 return NULL;
160 }
161 if ((sk->dev = fido_dev_new()) == NULL) {
162 skdebug(__func__, "fido_dev_new failed");
163 free(sk->path);
164 free(sk);
165 return NULL;
166 }
167 if ((r = fido_dev_open(sk->dev, sk->path)) != FIDO_OK) {
168 skdebug(__func__, "fido_dev_open %s failed: %s", sk->path,
169 fido_strerr(r));
170 fido_dev_free(&sk->dev);
171 free(sk->path);
172 free(sk);
173 return NULL;
174 }
175 return sk;
176 }
177
178 static void
sk_close(struct sk_usbhid * sk)179 sk_close(struct sk_usbhid *sk)
180 {
181 if (sk == NULL)
182 return;
183 fido_dev_cancel(sk->dev); /* cancel any pending operation */
184 fido_dev_close(sk->dev);
185 fido_dev_free(&sk->dev);
186 free(sk->path);
187 free(sk);
188 }
189
190 static struct sk_usbhid **
sk_openv(const fido_dev_info_t * devlist,size_t ndevs,size_t * nopen)191 sk_openv(const fido_dev_info_t *devlist, size_t ndevs, size_t *nopen)
192 {
193 const fido_dev_info_t *di;
194 struct sk_usbhid **skv;
195 size_t i;
196
197 *nopen = 0;
198 if ((skv = calloc(ndevs, sizeof(*skv))) == NULL) {
199 skdebug(__func__, "calloc skv failed");
200 return NULL;
201 }
202 for (i = 0; i < ndevs; i++) {
203 if ((di = fido_dev_info_ptr(devlist, i)) == NULL)
204 skdebug(__func__, "fido_dev_info_ptr failed");
205 else if ((skv[*nopen] = sk_open(fido_dev_info_path(di))) == NULL)
206 skdebug(__func__, "sk_open failed");
207 else
208 (*nopen)++;
209 }
210 if (*nopen == 0) {
211 for (i = 0; i < ndevs; i++)
212 sk_close(skv[i]);
213 free(skv);
214 skv = NULL;
215 }
216
217 return skv;
218 }
219
220 static void
sk_closev(struct sk_usbhid ** skv,size_t nsk)221 sk_closev(struct sk_usbhid **skv, size_t nsk)
222 {
223 size_t i;
224
225 for (i = 0; i < nsk; i++)
226 sk_close(skv[i]);
227 free(skv);
228 }
229
230 static int
sk_touch_begin(struct sk_usbhid ** skv,size_t nsk)231 sk_touch_begin(struct sk_usbhid **skv, size_t nsk)
232 {
233 size_t i, ok = 0;
234 int r;
235
236 for (i = 0; i < nsk; i++)
237 if ((r = fido_dev_get_touch_begin(skv[i]->dev)) != FIDO_OK)
238 skdebug(__func__, "fido_dev_get_touch_begin %s failed:"
239 " %s", skv[i]->path, fido_strerr(r));
240 else
241 ok++;
242
243 return ok ? 0 : -1;
244 }
245
246 static int
sk_touch_poll(struct sk_usbhid ** skv,size_t nsk,int * touch,size_t * idx)247 sk_touch_poll(struct sk_usbhid **skv, size_t nsk, int *touch, size_t *idx)
248 {
249 struct timespec ts_pause;
250 size_t npoll, i;
251 int r;
252
253 ts_pause.tv_sec = 0;
254 ts_pause.tv_nsec = POLL_SLEEP_NS;
255 nanosleep(&ts_pause, NULL);
256 npoll = nsk;
257 for (i = 0; i < nsk; i++) {
258 if (skv[i] == NULL)
259 continue; /* device discarded */
260 skdebug(__func__, "polling %s", skv[i]->path);
261 if ((r = fido_dev_get_touch_status(skv[i]->dev, touch,
262 FIDO_POLL_MS)) != FIDO_OK) {
263 skdebug(__func__, "fido_dev_get_touch_status %s: %s",
264 skv[i]->path, fido_strerr(r));
265 sk_close(skv[i]); /* discard device */
266 skv[i] = NULL;
267 if (--npoll == 0) {
268 skdebug(__func__, "no device left to poll");
269 return -1;
270 }
271 } else if (*touch) {
272 *idx = i;
273 return 0;
274 }
275 }
276 *touch = 0;
277 return 0;
278 }
279
280 /* Check if the specified key handle exists on a given sk. */
281 static int
sk_try(const struct sk_usbhid * sk,const char * application,const uint8_t * key_handle,size_t key_handle_len)282 sk_try(const struct sk_usbhid *sk, const char *application,
283 const uint8_t *key_handle, size_t key_handle_len)
284 {
285 fido_assert_t *assert = NULL;
286 int r = FIDO_ERR_INTERNAL;
287 uint8_t message[32];
288
289 memset(message, '\0', sizeof(message));
290 if ((assert = fido_assert_new()) == NULL) {
291 skdebug(__func__, "fido_assert_new failed");
292 goto out;
293 }
294 /* generate an invalid signature on FIDO2 tokens */
295 if ((r = fido_assert_set_clientdata(assert, message,
296 sizeof(message))) != FIDO_OK) {
297 skdebug(__func__, "fido_assert_set_clientdata: %s",
298 fido_strerr(r));
299 goto out;
300 }
301 if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) {
302 skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r));
303 goto out;
304 }
305 if ((r = fido_assert_allow_cred(assert, key_handle,
306 key_handle_len)) != FIDO_OK) {
307 skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r));
308 goto out;
309 }
310 if ((r = fido_assert_set_up(assert, FIDO_OPT_FALSE)) != FIDO_OK) {
311 skdebug(__func__, "fido_assert_up: %s", fido_strerr(r));
312 goto out;
313 }
314 r = fido_dev_get_assert(sk->dev, assert, NULL);
315 skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r));
316 if (r == FIDO_ERR_USER_PRESENCE_REQUIRED) {
317 /* U2F tokens may return this */
318 r = FIDO_OK;
319 }
320 out:
321 fido_assert_free(&assert);
322
323 return r != FIDO_OK ? -1 : 0;
324 }
325
326 static int
check_sk_options(fido_dev_t * dev,const char * opt,int * ret)327 check_sk_options(fido_dev_t *dev, const char *opt, int *ret)
328 {
329 fido_cbor_info_t *info;
330 char * const *name;
331 const bool *value;
332 size_t len, i;
333 int r;
334
335 *ret = -1;
336
337 if (!fido_dev_is_fido2(dev)) {
338 skdebug(__func__, "device is not fido2");
339 return 0;
340 }
341 if ((info = fido_cbor_info_new()) == NULL) {
342 skdebug(__func__, "fido_cbor_info_new failed");
343 return -1;
344 }
345 if ((r = fido_dev_get_cbor_info(dev, info)) != FIDO_OK) {
346 skdebug(__func__, "fido_dev_get_cbor_info: %s", fido_strerr(r));
347 fido_cbor_info_free(&info);
348 return -1;
349 }
350 name = fido_cbor_info_options_name_ptr(info);
351 value = fido_cbor_info_options_value_ptr(info);
352 len = fido_cbor_info_options_len(info);
353 for (i = 0; i < len; i++) {
354 if (!strcmp(name[i], opt)) {
355 *ret = value[i];
356 break;
357 }
358 }
359 fido_cbor_info_free(&info);
360 if (*ret == -1)
361 skdebug(__func__, "option %s is unknown", opt);
362 else
363 skdebug(__func__, "option %s is %s", opt, *ret ? "on" : "off");
364
365 return 0;
366 }
367
368 static struct sk_usbhid *
sk_select_by_cred(const fido_dev_info_t * devlist,size_t ndevs,const char * application,const uint8_t * key_handle,size_t key_handle_len)369 sk_select_by_cred(const fido_dev_info_t *devlist, size_t ndevs,
370 const char *application, const uint8_t *key_handle, size_t key_handle_len)
371 {
372 struct sk_usbhid **skv, *sk;
373 size_t skvcnt, i;
374 int internal_uv;
375
376 if ((skv = sk_openv(devlist, ndevs, &skvcnt)) == NULL) {
377 skdebug(__func__, "sk_openv failed");
378 return NULL;
379 }
380 if (skvcnt == 1 && check_sk_options(skv[0]->dev, "uv",
381 &internal_uv) == 0 && internal_uv != -1) {
382 sk = skv[0];
383 skv[0] = NULL;
384 goto out;
385 }
386 sk = NULL;
387 for (i = 0; i < skvcnt; i++) {
388 if (sk_try(skv[i], application, key_handle,
389 key_handle_len) == 0) {
390 sk = skv[i];
391 skv[i] = NULL;
392 skdebug(__func__, "found key in %s", sk->path);
393 break;
394 }
395 }
396 out:
397 sk_closev(skv, skvcnt);
398 return sk;
399 }
400
401 static struct sk_usbhid *
sk_select_by_touch(const fido_dev_info_t * devlist,size_t ndevs)402 sk_select_by_touch(const fido_dev_info_t *devlist, size_t ndevs)
403 {
404 struct sk_usbhid **skv, *sk;
405 struct timeval tv_start, tv_now, tv_delta;
406 size_t skvcnt, idx;
407 int touch, ms_remain;
408
409 if ((skv = sk_openv(devlist, ndevs, &skvcnt)) == NULL) {
410 skdebug(__func__, "sk_openv failed");
411 return NULL;
412 }
413 sk = NULL;
414 if (skvcnt < 2) {
415 if (skvcnt == 1) {
416 /* single candidate */
417 sk = skv[0];
418 skv[0] = NULL;
419 }
420 goto out;
421 }
422 if (sk_touch_begin(skv, skvcnt) == -1) {
423 skdebug(__func__, "sk_touch_begin failed");
424 goto out;
425 }
426 monotime_tv(&tv_start);
427 do {
428 if (sk_touch_poll(skv, skvcnt, &touch, &idx) == -1) {
429 skdebug(__func__, "sk_touch_poll failed");
430 goto out;
431 }
432 if (touch) {
433 sk = skv[idx];
434 skv[idx] = NULL;
435 goto out;
436 }
437 monotime_tv(&tv_now);
438 timersub(&tv_now, &tv_start, &tv_delta);
439 ms_remain = SELECT_MS - tv_delta.tv_sec * 1000 -
440 tv_delta.tv_usec / 1000;
441 } while (ms_remain >= FIDO_POLL_MS);
442 skdebug(__func__, "timeout");
443 out:
444 sk_closev(skv, skvcnt);
445 return sk;
446 }
447
448 static struct sk_usbhid *
sk_probe(const char * application,const uint8_t * key_handle,size_t key_handle_len,int probe_resident)449 sk_probe(const char *application, const uint8_t *key_handle,
450 size_t key_handle_len, int probe_resident)
451 {
452 struct sk_usbhid *sk;
453 fido_dev_info_t *devlist;
454 size_t ndevs;
455 int r;
456
457 if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) {
458 skdebug(__func__, "fido_dev_info_new failed");
459 return NULL;
460 }
461 if ((r = fido_dev_info_manifest(devlist, MAX_FIDO_DEVICES,
462 &ndevs)) != FIDO_OK) {
463 skdebug(__func__, "fido_dev_info_manifest failed: %s",
464 fido_strerr(r));
465 fido_dev_info_free(&devlist, MAX_FIDO_DEVICES);
466 return NULL;
467 }
468 skdebug(__func__, "%zu device(s) detected", ndevs);
469 if (ndevs == 0) {
470 sk = NULL;
471 } else if (application != NULL && key_handle != NULL) {
472 skdebug(__func__, "selecting sk by cred");
473 sk = sk_select_by_cred(devlist, ndevs, application, key_handle,
474 key_handle_len);
475 } else {
476 skdebug(__func__, "selecting sk by touch");
477 sk = sk_select_by_touch(devlist, ndevs);
478 }
479 fido_dev_info_free(&devlist, MAX_FIDO_DEVICES);
480 return sk;
481 }
482
483 #ifdef WITH_OPENSSL
484 /*
485 * The key returned via fido_cred_pubkey_ptr() is in affine coordinates,
486 * but the API expects a SEC1 octet string.
487 */
488 static int
pack_public_key_ecdsa(const fido_cred_t * cred,struct sk_enroll_response * response)489 pack_public_key_ecdsa(const fido_cred_t *cred,
490 struct sk_enroll_response *response)
491 {
492 const uint8_t *ptr;
493 BIGNUM *x = NULL, *y = NULL;
494 EC_POINT *q = NULL;
495 EC_GROUP *g = NULL;
496 int ret = -1;
497
498 response->public_key = NULL;
499 response->public_key_len = 0;
500
501 if ((x = BN_new()) == NULL ||
502 (y = BN_new()) == NULL ||
503 (g = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)) == NULL ||
504 (q = EC_POINT_new(g)) == NULL) {
505 skdebug(__func__, "libcrypto setup failed");
506 goto out;
507 }
508 if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) {
509 skdebug(__func__, "fido_cred_pubkey_ptr failed");
510 goto out;
511 }
512 if (fido_cred_pubkey_len(cred) != 64) {
513 skdebug(__func__, "bad fido_cred_pubkey_len %zu",
514 fido_cred_pubkey_len(cred));
515 goto out;
516 }
517
518 if (BN_bin2bn(ptr, 32, x) == NULL ||
519 BN_bin2bn(ptr + 32, 32, y) == NULL) {
520 skdebug(__func__, "BN_bin2bn failed");
521 goto out;
522 }
523 if (EC_POINT_set_affine_coordinates_GFp(g, q, x, y, NULL) != 1) {
524 skdebug(__func__, "EC_POINT_set_affine_coordinates_GFp failed");
525 goto out;
526 }
527 response->public_key_len = EC_POINT_point2oct(g, q,
528 POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL);
529 if (response->public_key_len == 0 || response->public_key_len > 2048) {
530 skdebug(__func__, "bad pubkey length %zu",
531 response->public_key_len);
532 goto out;
533 }
534 if ((response->public_key = malloc(response->public_key_len)) == NULL) {
535 skdebug(__func__, "malloc pubkey failed");
536 goto out;
537 }
538 if (EC_POINT_point2oct(g, q, POINT_CONVERSION_UNCOMPRESSED,
539 response->public_key, response->public_key_len, NULL) == 0) {
540 skdebug(__func__, "EC_POINT_point2oct failed");
541 goto out;
542 }
543 /* success */
544 ret = 0;
545 out:
546 if (ret != 0 && response->public_key != NULL) {
547 memset(response->public_key, 0, response->public_key_len);
548 free(response->public_key);
549 response->public_key = NULL;
550 }
551 EC_POINT_free(q);
552 EC_GROUP_free(g);
553 BN_clear_free(x);
554 BN_clear_free(y);
555 return ret;
556 }
557 #endif /* WITH_OPENSSL */
558
559 static int
pack_public_key_ed25519(const fido_cred_t * cred,struct sk_enroll_response * response)560 pack_public_key_ed25519(const fido_cred_t *cred,
561 struct sk_enroll_response *response)
562 {
563 const uint8_t *ptr;
564 size_t len;
565 int ret = -1;
566
567 response->public_key = NULL;
568 response->public_key_len = 0;
569
570 if ((len = fido_cred_pubkey_len(cred)) != 32) {
571 skdebug(__func__, "bad fido_cred_pubkey_len len %zu", len);
572 goto out;
573 }
574 if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) {
575 skdebug(__func__, "fido_cred_pubkey_ptr failed");
576 goto out;
577 }
578 response->public_key_len = len;
579 if ((response->public_key = malloc(response->public_key_len)) == NULL) {
580 skdebug(__func__, "malloc pubkey failed");
581 goto out;
582 }
583 memcpy(response->public_key, ptr, len);
584 ret = 0;
585 out:
586 if (ret != 0)
587 free(response->public_key);
588 return ret;
589 }
590
591 static int
pack_public_key(uint32_t alg,const fido_cred_t * cred,struct sk_enroll_response * response)592 pack_public_key(uint32_t alg, const fido_cred_t *cred,
593 struct sk_enroll_response *response)
594 {
595 switch(alg) {
596 #ifdef WITH_OPENSSL
597 case SSH_SK_ECDSA:
598 return pack_public_key_ecdsa(cred, response);
599 #endif /* WITH_OPENSSL */
600 case SSH_SK_ED25519:
601 return pack_public_key_ed25519(cred, response);
602 default:
603 return -1;
604 }
605 }
606
607 static int
fidoerr_to_skerr(int fidoerr)608 fidoerr_to_skerr(int fidoerr)
609 {
610 switch (fidoerr) {
611 case FIDO_ERR_UNSUPPORTED_OPTION:
612 case FIDO_ERR_UNSUPPORTED_ALGORITHM:
613 return SSH_SK_ERR_UNSUPPORTED;
614 case FIDO_ERR_PIN_REQUIRED:
615 case FIDO_ERR_PIN_INVALID:
616 case FIDO_ERR_OPERATION_DENIED:
617 return SSH_SK_ERR_PIN_REQUIRED;
618 default:
619 return -1;
620 }
621 }
622
623 static int
check_enroll_options(struct sk_option ** options,char ** devicep,uint8_t * user_id,size_t user_id_len)624 check_enroll_options(struct sk_option **options, char **devicep,
625 uint8_t *user_id, size_t user_id_len)
626 {
627 size_t i;
628
629 if (options == NULL)
630 return 0;
631 for (i = 0; options[i] != NULL; i++) {
632 if (strcmp(options[i]->name, "device") == 0) {
633 if ((*devicep = strdup(options[i]->value)) == NULL) {
634 skdebug(__func__, "strdup device failed");
635 return -1;
636 }
637 skdebug(__func__, "requested device %s", *devicep);
638 } else if (strcmp(options[i]->name, "user") == 0) {
639 if (strlcpy((char *)user_id, options[i]->value, user_id_len) >=
640 user_id_len) {
641 skdebug(__func__, "user too long");
642 return -1;
643 }
644 skdebug(__func__, "requested user %s",
645 (char *)user_id);
646 } else {
647 skdebug(__func__, "requested unsupported option %s",
648 options[i]->name);
649 if (options[i]->required) {
650 skdebug(__func__, "unknown required option");
651 return -1;
652 }
653 }
654 }
655 return 0;
656 }
657
658 static int
key_lookup(fido_dev_t * dev,const char * application,const uint8_t * user_id,size_t user_id_len,const char * pin)659 key_lookup(fido_dev_t *dev, const char *application, const uint8_t *user_id,
660 size_t user_id_len, const char *pin)
661 {
662 fido_assert_t *assert = NULL;
663 uint8_t message[32];
664 int r = FIDO_ERR_INTERNAL;
665 int sk_supports_uv, uv;
666 size_t i;
667
668 memset(message, '\0', sizeof(message));
669 if ((assert = fido_assert_new()) == NULL) {
670 skdebug(__func__, "fido_assert_new failed");
671 goto out;
672 }
673 /* generate an invalid signature on FIDO2 tokens */
674 if ((r = fido_assert_set_clientdata(assert, message,
675 sizeof(message))) != FIDO_OK) {
676 skdebug(__func__, "fido_assert_set_clientdata: %s",
677 fido_strerr(r));
678 goto out;
679 }
680 if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) {
681 skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r));
682 goto out;
683 }
684 if ((r = fido_assert_set_up(assert, FIDO_OPT_FALSE)) != FIDO_OK) {
685 skdebug(__func__, "fido_assert_set_up: %s", fido_strerr(r));
686 goto out;
687 }
688 uv = FIDO_OPT_OMIT;
689 if (pin == NULL && check_sk_options(dev, "uv", &sk_supports_uv) == 0 &&
690 sk_supports_uv != -1)
691 uv = FIDO_OPT_TRUE;
692 if ((r = fido_assert_set_uv(assert, uv)) != FIDO_OK) {
693 skdebug(__func__, "fido_assert_set_uv: %s", fido_strerr(r));
694 goto out;
695 }
696 if ((r = fido_dev_get_assert(dev, assert, pin)) != FIDO_OK) {
697 skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r));
698 goto out;
699 }
700 r = FIDO_ERR_NO_CREDENTIALS;
701 skdebug(__func__, "%zu signatures returned", fido_assert_count(assert));
702 for (i = 0; i < fido_assert_count(assert); i++) {
703 if (fido_assert_user_id_len(assert, i) == user_id_len &&
704 memcmp(fido_assert_user_id_ptr(assert, i), user_id,
705 user_id_len) == 0) {
706 skdebug(__func__, "credential exists");
707 r = FIDO_OK;
708 goto out;
709 }
710 }
711 out:
712 fido_assert_free(&assert);
713
714 return r;
715 }
716
717 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)718 sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len,
719 const char *application, uint8_t flags, const char *pin,
720 struct sk_option **options, struct sk_enroll_response **enroll_response)
721 {
722 fido_cred_t *cred = NULL;
723 const uint8_t *ptr;
724 uint8_t user_id[32];
725 struct sk_usbhid *sk = NULL;
726 struct sk_enroll_response *response = NULL;
727 size_t len;
728 int credprot;
729 int cose_alg;
730 int ret = SSH_SK_ERR_GENERAL;
731 int r;
732 char *device = NULL;
733
734 fido_init(SSH_FIDO_INIT_ARG);
735
736 if (enroll_response == NULL) {
737 skdebug(__func__, "enroll_response == NULL");
738 goto out;
739 }
740 *enroll_response = NULL;
741 memset(user_id, 0, sizeof(user_id));
742 if (check_enroll_options(options, &device, user_id,
743 sizeof(user_id)) != 0)
744 goto out; /* error already logged */
745
746 switch(alg) {
747 #ifdef WITH_OPENSSL
748 case SSH_SK_ECDSA:
749 cose_alg = COSE_ES256;
750 break;
751 #endif /* WITH_OPENSSL */
752 case SSH_SK_ED25519:
753 cose_alg = COSE_EDDSA;
754 break;
755 default:
756 skdebug(__func__, "unsupported key type %d", alg);
757 goto out;
758 }
759 if (device != NULL)
760 sk = sk_open(device);
761 else
762 sk = sk_probe(NULL, NULL, 0, 0);
763 if (sk == NULL) {
764 ret = SSH_SK_ERR_DEVICE_NOT_FOUND;
765 skdebug(__func__, "failed to find sk");
766 goto out;
767 }
768 skdebug(__func__, "using device %s", sk->path);
769 if ((flags & SSH_SK_RESIDENT_KEY) != 0 &&
770 (flags & SSH_SK_FORCE_OPERATION) == 0 &&
771 (r = key_lookup(sk->dev, application, user_id, sizeof(user_id),
772 pin)) != FIDO_ERR_NO_CREDENTIALS) {
773 if (r != FIDO_OK) {
774 ret = fidoerr_to_skerr(r);
775 skdebug(__func__, "key_lookup failed");
776 } else {
777 ret = SSH_SK_ERR_CREDENTIAL_EXISTS;
778 skdebug(__func__, "key exists");
779 }
780 goto out;
781 }
782 if ((cred = fido_cred_new()) == NULL) {
783 skdebug(__func__, "fido_cred_new failed");
784 goto out;
785 }
786 if ((r = fido_cred_set_type(cred, cose_alg)) != FIDO_OK) {
787 skdebug(__func__, "fido_cred_set_type: %s", fido_strerr(r));
788 goto out;
789 }
790 if ((r = fido_cred_set_clientdata(cred,
791 challenge, challenge_len)) != FIDO_OK) {
792 skdebug(__func__, "fido_cred_set_clientdata: %s",
793 fido_strerr(r));
794 goto out;
795 }
796 if ((r = fido_cred_set_rk(cred, (flags & SSH_SK_RESIDENT_KEY) != 0 ?
797 FIDO_OPT_TRUE : FIDO_OPT_OMIT)) != FIDO_OK) {
798 skdebug(__func__, "fido_cred_set_rk: %s", fido_strerr(r));
799 goto out;
800 }
801 if ((r = fido_cred_set_user(cred, user_id, sizeof(user_id),
802 "openssh", "openssh", NULL)) != FIDO_OK) {
803 skdebug(__func__, "fido_cred_set_user: %s", fido_strerr(r));
804 goto out;
805 }
806 if ((r = fido_cred_set_rp(cred, application, NULL)) != FIDO_OK) {
807 skdebug(__func__, "fido_cred_set_rp: %s", fido_strerr(r));
808 goto out;
809 }
810 if ((flags & (SSH_SK_RESIDENT_KEY|SSH_SK_USER_VERIFICATION_REQD)) != 0) {
811 if (!fido_dev_supports_cred_prot(sk->dev)) {
812 skdebug(__func__, "%s does not support credprot, "
813 "refusing to create unprotected "
814 "resident/verify-required key", sk->path);
815 ret = SSH_SK_ERR_UNSUPPORTED;
816 goto out;
817 }
818 if ((flags & SSH_SK_USER_VERIFICATION_REQD))
819 credprot = FIDO_CRED_PROT_UV_REQUIRED;
820 else
821 credprot = FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID;
822
823 if ((r = fido_cred_set_prot(cred, credprot)) != FIDO_OK) {
824 skdebug(__func__, "fido_cred_set_prot: %s",
825 fido_strerr(r));
826 ret = fidoerr_to_skerr(r);
827 goto out;
828 }
829 }
830 if ((r = fido_dev_make_cred(sk->dev, cred, pin)) != FIDO_OK) {
831 skdebug(__func__, "fido_dev_make_cred: %s", fido_strerr(r));
832 ret = fidoerr_to_skerr(r);
833 goto out;
834 }
835 if (fido_cred_x5c_ptr(cred) != NULL) {
836 if ((r = fido_cred_verify(cred)) != FIDO_OK) {
837 skdebug(__func__, "fido_cred_verify: %s",
838 fido_strerr(r));
839 goto out;
840 }
841 } else {
842 skdebug(__func__, "self-attested credential");
843 if ((r = fido_cred_verify_self(cred)) != FIDO_OK) {
844 skdebug(__func__, "fido_cred_verify_self: %s",
845 fido_strerr(r));
846 goto out;
847 }
848 }
849 if ((response = calloc(1, sizeof(*response))) == NULL) {
850 skdebug(__func__, "calloc response failed");
851 goto out;
852 }
853 response->flags = flags;
854 if (pack_public_key(alg, cred, response) != 0) {
855 skdebug(__func__, "pack_public_key failed");
856 goto out;
857 }
858 if ((ptr = fido_cred_id_ptr(cred)) != NULL) {
859 len = fido_cred_id_len(cred);
860 if ((response->key_handle = calloc(1, len)) == NULL) {
861 skdebug(__func__, "calloc key handle failed");
862 goto out;
863 }
864 memcpy(response->key_handle, ptr, len);
865 response->key_handle_len = len;
866 }
867 if ((ptr = fido_cred_sig_ptr(cred)) != NULL) {
868 len = fido_cred_sig_len(cred);
869 if ((response->signature = calloc(1, len)) == NULL) {
870 skdebug(__func__, "calloc signature failed");
871 goto out;
872 }
873 memcpy(response->signature, ptr, len);
874 response->signature_len = len;
875 }
876 if ((ptr = fido_cred_x5c_ptr(cred)) != NULL) {
877 len = fido_cred_x5c_len(cred);
878 skdebug(__func__, "attestation cert len=%zu", len);
879 if ((response->attestation_cert = calloc(1, len)) == NULL) {
880 skdebug(__func__, "calloc attestation cert failed");
881 goto out;
882 }
883 memcpy(response->attestation_cert, ptr, len);
884 response->attestation_cert_len = len;
885 }
886 if ((ptr = fido_cred_authdata_ptr(cred)) != NULL) {
887 len = fido_cred_authdata_len(cred);
888 skdebug(__func__, "authdata len=%zu", len);
889 if ((response->authdata = calloc(1, len)) == NULL) {
890 skdebug(__func__, "calloc authdata failed");
891 goto out;
892 }
893 memcpy(response->authdata, ptr, len);
894 response->authdata_len = len;
895 }
896 *enroll_response = response;
897 response = NULL;
898 ret = 0;
899 out:
900 free(device);
901 if (response != NULL) {
902 free(response->public_key);
903 free(response->key_handle);
904 free(response->signature);
905 free(response->attestation_cert);
906 free(response->authdata);
907 free(response);
908 }
909 sk_close(sk);
910 fido_cred_free(&cred);
911 return ret;
912 }
913
914 #ifdef WITH_OPENSSL
915 static int
pack_sig_ecdsa(fido_assert_t * assert,struct sk_sign_response * response)916 pack_sig_ecdsa(fido_assert_t *assert, struct sk_sign_response *response)
917 {
918 ECDSA_SIG *sig = NULL;
919 const BIGNUM *sig_r, *sig_s;
920 const unsigned char *cp;
921 size_t sig_len;
922 int ret = -1;
923
924 cp = fido_assert_sig_ptr(assert, 0);
925 sig_len = fido_assert_sig_len(assert, 0);
926 if ((sig = d2i_ECDSA_SIG(NULL, &cp, sig_len)) == NULL) {
927 skdebug(__func__, "d2i_ECDSA_SIG failed");
928 goto out;
929 }
930 ECDSA_SIG_get0(sig, &sig_r, &sig_s);
931 response->sig_r_len = BN_num_bytes(sig_r);
932 response->sig_s_len = BN_num_bytes(sig_s);
933 if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL ||
934 (response->sig_s = calloc(1, response->sig_s_len)) == NULL) {
935 skdebug(__func__, "calloc signature failed");
936 goto out;
937 }
938 BN_bn2bin(sig_r, response->sig_r);
939 BN_bn2bin(sig_s, response->sig_s);
940 ret = 0;
941 out:
942 ECDSA_SIG_free(sig);
943 if (ret != 0) {
944 free(response->sig_r);
945 free(response->sig_s);
946 response->sig_r = NULL;
947 response->sig_s = NULL;
948 }
949 return ret;
950 }
951 #endif /* WITH_OPENSSL */
952
953 static int
pack_sig_ed25519(fido_assert_t * assert,struct sk_sign_response * response)954 pack_sig_ed25519(fido_assert_t *assert, struct sk_sign_response *response)
955 {
956 const unsigned char *ptr;
957 size_t len;
958 int ret = -1;
959
960 ptr = fido_assert_sig_ptr(assert, 0);
961 len = fido_assert_sig_len(assert, 0);
962 if (len != 64) {
963 skdebug(__func__, "bad length %zu", len);
964 goto out;
965 }
966 response->sig_r_len = len;
967 if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL) {
968 skdebug(__func__, "calloc signature failed");
969 goto out;
970 }
971 memcpy(response->sig_r, ptr, len);
972 ret = 0;
973 out:
974 if (ret != 0) {
975 free(response->sig_r);
976 response->sig_r = NULL;
977 }
978 return ret;
979 }
980
981 static int
pack_sig(uint32_t alg,fido_assert_t * assert,struct sk_sign_response * response)982 pack_sig(uint32_t alg, fido_assert_t *assert,
983 struct sk_sign_response *response)
984 {
985 switch(alg) {
986 #ifdef WITH_OPENSSL
987 case SSH_SK_ECDSA:
988 return pack_sig_ecdsa(assert, response);
989 #endif /* WITH_OPENSSL */
990 case SSH_SK_ED25519:
991 return pack_sig_ed25519(assert, response);
992 default:
993 return -1;
994 }
995 }
996
997 /* Checks sk_options for sk_sign() and sk_load_resident_keys() */
998 static int
check_sign_load_resident_options(struct sk_option ** options,char ** devicep)999 check_sign_load_resident_options(struct sk_option **options, char **devicep)
1000 {
1001 size_t i;
1002
1003 if (options == NULL)
1004 return 0;
1005 for (i = 0; options[i] != NULL; i++) {
1006 if (strcmp(options[i]->name, "device") == 0) {
1007 if ((*devicep = strdup(options[i]->value)) == NULL) {
1008 skdebug(__func__, "strdup device failed");
1009 return -1;
1010 }
1011 skdebug(__func__, "requested device %s", *devicep);
1012 } else {
1013 skdebug(__func__, "requested unsupported option %s",
1014 options[i]->name);
1015 if (options[i]->required) {
1016 skdebug(__func__, "unknown required option");
1017 return -1;
1018 }
1019 }
1020 }
1021 return 0;
1022 }
1023
1024 int
sk_sign(uint32_t alg,const uint8_t * data,size_t datalen,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)1025 sk_sign(uint32_t alg, const uint8_t *data, size_t datalen,
1026 const char *application,
1027 const uint8_t *key_handle, size_t key_handle_len,
1028 uint8_t flags, const char *pin, struct sk_option **options,
1029 struct sk_sign_response **sign_response)
1030 {
1031 fido_assert_t *assert = NULL;
1032 char *device = NULL;
1033 struct sk_usbhid *sk = NULL;
1034 struct sk_sign_response *response = NULL;
1035 int ret = SSH_SK_ERR_GENERAL, internal_uv;
1036 int r;
1037
1038 fido_init(SSH_FIDO_INIT_ARG);
1039
1040 if (sign_response == NULL) {
1041 skdebug(__func__, "sign_response == NULL");
1042 goto out;
1043 }
1044 *sign_response = NULL;
1045 if (check_sign_load_resident_options(options, &device) != 0)
1046 goto out; /* error already logged */
1047 if (device != NULL)
1048 sk = sk_open(device);
1049 else if (pin != NULL || (flags & SSH_SK_USER_VERIFICATION_REQD))
1050 sk = sk_probe(NULL, NULL, 0, 0);
1051 else
1052 sk = sk_probe(application, key_handle, key_handle_len, 0);
1053 if (sk == NULL) {
1054 ret = SSH_SK_ERR_DEVICE_NOT_FOUND;
1055 skdebug(__func__, "failed to find sk");
1056 goto out;
1057 }
1058 if ((assert = fido_assert_new()) == NULL) {
1059 skdebug(__func__, "fido_assert_new failed");
1060 goto out;
1061 }
1062 if ((r = fido_assert_set_clientdata(assert,
1063 data, datalen)) != FIDO_OK) {
1064 skdebug(__func__, "fido_assert_set_clientdata: %s",
1065 fido_strerr(r));
1066 goto out;
1067 }
1068 if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) {
1069 skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r));
1070 goto out;
1071 }
1072 if ((r = fido_assert_allow_cred(assert, key_handle,
1073 key_handle_len)) != FIDO_OK) {
1074 skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r));
1075 goto out;
1076 }
1077 if ((r = fido_assert_set_up(assert,
1078 (flags & SSH_SK_USER_PRESENCE_REQD) ?
1079 FIDO_OPT_TRUE : FIDO_OPT_FALSE)) != FIDO_OK) {
1080 skdebug(__func__, "fido_assert_set_up: %s", fido_strerr(r));
1081 goto out;
1082 }
1083 if (pin == NULL && (flags & SSH_SK_USER_VERIFICATION_REQD)) {
1084 if (check_sk_options(sk->dev, "uv", &internal_uv) < 0 ||
1085 internal_uv != 1) {
1086 skdebug(__func__, "check_sk_options uv");
1087 ret = SSH_SK_ERR_PIN_REQUIRED;
1088 goto out;
1089 }
1090 if ((r = fido_assert_set_uv(assert,
1091 FIDO_OPT_TRUE)) != FIDO_OK) {
1092 skdebug(__func__, "fido_assert_set_uv: %s",
1093 fido_strerr(r));
1094 ret = fidoerr_to_skerr(r);
1095 goto out;
1096 }
1097 }
1098 if ((r = fido_dev_get_assert(sk->dev, assert, pin)) != FIDO_OK) {
1099 skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r));
1100 ret = fidoerr_to_skerr(r);
1101 goto out;
1102 }
1103 if ((response = calloc(1, sizeof(*response))) == NULL) {
1104 skdebug(__func__, "calloc response failed");
1105 goto out;
1106 }
1107 response->flags = fido_assert_flags(assert, 0);
1108 response->counter = fido_assert_sigcount(assert, 0);
1109 if (pack_sig(alg, assert, response) != 0) {
1110 skdebug(__func__, "pack_sig failed");
1111 goto out;
1112 }
1113 *sign_response = response;
1114 response = NULL;
1115 ret = 0;
1116 out:
1117 free(device);
1118 if (response != NULL) {
1119 free(response->sig_r);
1120 free(response->sig_s);
1121 free(response);
1122 }
1123 sk_close(sk);
1124 fido_assert_free(&assert);
1125 return ret;
1126 }
1127
1128 static int
read_rks(struct sk_usbhid * sk,const char * pin,struct sk_resident_key *** rksp,size_t * nrksp)1129 read_rks(struct sk_usbhid *sk, const char *pin,
1130 struct sk_resident_key ***rksp, size_t *nrksp)
1131 {
1132 int ret = SSH_SK_ERR_GENERAL, r = -1, internal_uv;
1133 fido_credman_metadata_t *metadata = NULL;
1134 fido_credman_rp_t *rp = NULL;
1135 fido_credman_rk_t *rk = NULL;
1136 size_t i, j, nrp, nrk, user_id_len;
1137 const fido_cred_t *cred;
1138 const char *rp_id, *rp_name, *user_name;
1139 struct sk_resident_key *srk = NULL, **tmp;
1140 const u_char *user_id;
1141
1142 if (pin == NULL) {
1143 skdebug(__func__, "no PIN specified");
1144 ret = SSH_SK_ERR_PIN_REQUIRED;
1145 goto out;
1146 }
1147 if ((metadata = fido_credman_metadata_new()) == NULL) {
1148 skdebug(__func__, "alloc failed");
1149 goto out;
1150 }
1151 if (check_sk_options(sk->dev, "uv", &internal_uv) != 0) {
1152 skdebug(__func__, "check_sk_options failed");
1153 goto out;
1154 }
1155
1156 if ((r = fido_credman_get_dev_metadata(sk->dev, metadata, pin)) != 0) {
1157 if (r == FIDO_ERR_INVALID_COMMAND) {
1158 skdebug(__func__, "device %s does not support "
1159 "resident keys", sk->path);
1160 ret = 0;
1161 goto out;
1162 }
1163 skdebug(__func__, "get metadata for %s failed: %s",
1164 sk->path, fido_strerr(r));
1165 ret = fidoerr_to_skerr(r);
1166 goto out;
1167 }
1168 skdebug(__func__, "existing %llu, remaining %llu",
1169 (unsigned long long)fido_credman_rk_existing(metadata),
1170 (unsigned long long)fido_credman_rk_remaining(metadata));
1171 if ((rp = fido_credman_rp_new()) == NULL) {
1172 skdebug(__func__, "alloc rp failed");
1173 goto out;
1174 }
1175 if ((r = fido_credman_get_dev_rp(sk->dev, rp, pin)) != 0) {
1176 skdebug(__func__, "get RPs for %s failed: %s",
1177 sk->path, fido_strerr(r));
1178 goto out;
1179 }
1180 nrp = fido_credman_rp_count(rp);
1181 skdebug(__func__, "Device %s has resident keys for %zu RPs",
1182 sk->path, nrp);
1183
1184 /* Iterate over RP IDs that have resident keys */
1185 for (i = 0; i < nrp; i++) {
1186 rp_id = fido_credman_rp_id(rp, i);
1187 rp_name = fido_credman_rp_name(rp, i);
1188 skdebug(__func__, "rp %zu: name=\"%s\" id=\"%s\" hashlen=%zu",
1189 i, rp_name == NULL ? "(none)" : rp_name,
1190 rp_id == NULL ? "(none)" : rp_id,
1191 fido_credman_rp_id_hash_len(rp, i));
1192
1193 /* Skip non-SSH RP IDs */
1194 if (rp_id == NULL ||
1195 strncasecmp(fido_credman_rp_id(rp, i), "ssh:", 4) != 0)
1196 continue;
1197
1198 fido_credman_rk_free(&rk);
1199 if ((rk = fido_credman_rk_new()) == NULL) {
1200 skdebug(__func__, "alloc rk failed");
1201 goto out;
1202 }
1203 if ((r = fido_credman_get_dev_rk(sk->dev,
1204 fido_credman_rp_id(rp, i), rk, pin)) != 0) {
1205 skdebug(__func__, "get RKs for %s slot %zu failed: %s",
1206 sk->path, i, fido_strerr(r));
1207 goto out;
1208 }
1209 nrk = fido_credman_rk_count(rk);
1210 skdebug(__func__, "RP \"%s\" has %zu resident keys",
1211 fido_credman_rp_id(rp, i), nrk);
1212
1213 /* Iterate over resident keys for this RP ID */
1214 for (j = 0; j < nrk; j++) {
1215 if ((cred = fido_credman_rk(rk, j)) == NULL) {
1216 skdebug(__func__, "no RK in slot %zu", j);
1217 continue;
1218 }
1219 if ((user_name = fido_cred_user_name(cred)) == NULL)
1220 user_name = "";
1221 user_id = fido_cred_user_id_ptr(cred);
1222 user_id_len = fido_cred_user_id_len(cred);
1223 skdebug(__func__, "Device %s RP \"%s\" user \"%s\" "
1224 "uidlen %zu slot %zu: type %d flags 0x%02x "
1225 "prot 0x%02x", sk->path, rp_id, user_name,
1226 user_id_len, j, fido_cred_type(cred),
1227 fido_cred_flags(cred), fido_cred_prot(cred));
1228
1229 /* build response entry */
1230 if ((srk = calloc(1, sizeof(*srk))) == NULL ||
1231 (srk->key.key_handle = calloc(1,
1232 fido_cred_id_len(cred))) == NULL ||
1233 (srk->application = strdup(rp_id)) == NULL ||
1234 (user_id_len > 0 &&
1235 (srk->user_id = calloc(1, user_id_len)) == NULL)) {
1236 skdebug(__func__, "alloc sk_resident_key");
1237 goto out;
1238 }
1239
1240 srk->key.key_handle_len = fido_cred_id_len(cred);
1241 memcpy(srk->key.key_handle, fido_cred_id_ptr(cred),
1242 srk->key.key_handle_len);
1243 srk->user_id_len = user_id_len;
1244 if (srk->user_id_len != 0)
1245 memcpy(srk->user_id, user_id, srk->user_id_len);
1246
1247 switch (fido_cred_type(cred)) {
1248 case COSE_ES256:
1249 srk->alg = SSH_SK_ECDSA;
1250 break;
1251 case COSE_EDDSA:
1252 srk->alg = SSH_SK_ED25519;
1253 break;
1254 default:
1255 skdebug(__func__, "unsupported key type %d",
1256 fido_cred_type(cred));
1257 goto out; /* XXX free rk and continue */
1258 }
1259
1260 if (fido_cred_prot(cred) == FIDO_CRED_PROT_UV_REQUIRED
1261 && internal_uv == -1)
1262 srk->flags |= SSH_SK_USER_VERIFICATION_REQD;
1263
1264 if ((r = pack_public_key(srk->alg, cred,
1265 &srk->key)) != 0) {
1266 skdebug(__func__, "pack public key failed");
1267 goto out;
1268 }
1269 /* append */
1270 if ((tmp = recallocarray(*rksp, *nrksp, (*nrksp) + 1,
1271 sizeof(**rksp))) == NULL) {
1272 skdebug(__func__, "alloc rksp");
1273 goto out;
1274 }
1275 *rksp = tmp;
1276 (*rksp)[(*nrksp)++] = srk;
1277 srk = NULL;
1278 }
1279 }
1280 /* Success */
1281 ret = 0;
1282 out:
1283 if (srk != NULL) {
1284 free(srk->application);
1285 freezero(srk->key.public_key, srk->key.public_key_len);
1286 freezero(srk->key.key_handle, srk->key.key_handle_len);
1287 freezero(srk->user_id, srk->user_id_len);
1288 freezero(srk, sizeof(*srk));
1289 }
1290 fido_credman_rp_free(&rp);
1291 fido_credman_rk_free(&rk);
1292 fido_credman_metadata_free(&metadata);
1293 return ret;
1294 }
1295
1296 int
sk_load_resident_keys(const char * pin,struct sk_option ** options,struct sk_resident_key *** rksp,size_t * nrksp)1297 sk_load_resident_keys(const char *pin, struct sk_option **options,
1298 struct sk_resident_key ***rksp, size_t *nrksp)
1299 {
1300 int ret = SSH_SK_ERR_GENERAL, r = -1;
1301 size_t i, nrks = 0;
1302 struct sk_resident_key **rks = NULL;
1303 struct sk_usbhid *sk = NULL;
1304 char *device = NULL;
1305
1306 *rksp = NULL;
1307 *nrksp = 0;
1308
1309 fido_init(SSH_FIDO_INIT_ARG);
1310
1311 if (check_sign_load_resident_options(options, &device) != 0)
1312 goto out; /* error already logged */
1313 if (device != NULL)
1314 sk = sk_open(device);
1315 else
1316 sk = sk_probe(NULL, NULL, 0, 1);
1317 if (sk == NULL) {
1318 ret = SSH_SK_ERR_DEVICE_NOT_FOUND;
1319 skdebug(__func__, "failed to find sk");
1320 goto out;
1321 }
1322 skdebug(__func__, "trying %s", sk->path);
1323 if ((r = read_rks(sk, pin, &rks, &nrks)) != 0) {
1324 skdebug(__func__, "read_rks failed for %s", sk->path);
1325 ret = r;
1326 goto out;
1327 }
1328 /* success, unless we have no keys but a specific error */
1329 if (nrks > 0 || ret == SSH_SK_ERR_GENERAL)
1330 ret = 0;
1331 *rksp = rks;
1332 *nrksp = nrks;
1333 rks = NULL;
1334 nrks = 0;
1335 out:
1336 sk_close(sk);
1337 for (i = 0; i < nrks; i++) {
1338 free(rks[i]->application);
1339 freezero(rks[i]->key.public_key, rks[i]->key.public_key_len);
1340 freezero(rks[i]->key.key_handle, rks[i]->key.key_handle_len);
1341 freezero(rks[i]->user_id, rks[i]->user_id_len);
1342 freezero(rks[i], sizeof(*rks[i]));
1343 }
1344 free(device);
1345 free(rks);
1346 return ret;
1347 }
1348
1349