fix old typo (s/SYSVINITSTOPT/SYSVINITSTOP/)
[openssh.git] / sk-usbhid.c
blob427431b9a4575ba77e873a54cc6aa910b71fb485
1 /* $OpenBSD: sk-usbhid.c,v 1.47 2024/12/03 08:31:49 djm Exp $ */
2 /*
3 * Copyright (c) 2019 Markus Friedl
4 * Copyright (c) 2020 Pedro Martelletto
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 #include "includes.h"
21 #ifdef ENABLE_SK_INTERNAL
23 #ifdef HAVE_STDINT_H
24 # include <stdint.h>
25 #endif
26 #include <stdlib.h>
27 #include <string.h>
28 #include <stdio.h>
29 #include <stddef.h>
30 #include <stdarg.h>
31 #include <time.h>
32 #ifdef HAVE_SHA2_H
33 #include <sha2.h>
34 #endif
37 * Almost every use of OpenSSL in this file is for ECDSA-NISTP256.
38 * This is strictly a larger hammer than necessary, but it reduces changes
39 * with upstream.
41 #ifndef OPENSSL_HAS_ECC
42 # undef WITH_OPENSSL
43 #endif
45 #ifdef WITH_OPENSSL
46 #include <openssl/opensslv.h>
47 #include <openssl/crypto.h>
48 #include <openssl/bn.h>
49 #include <openssl/ec.h>
50 #include <openssl/ecdsa.h>
51 #include <openssl/evp.h>
52 #endif /* WITH_OPENSSL */
54 #include <fido.h>
55 #include <fido/credman.h>
57 /* backwards compat for libfido2 */
58 #ifndef HAVE_FIDO_CRED_PROT
59 #define fido_cred_prot(x) (0)
60 #endif
61 #ifndef HAVE_FIDO_CRED_SET_PROT
62 #define fido_cred_set_prot(x, y) (FIDO_ERR_UNSUPPORTED_OPTION)
63 #endif
64 #ifndef HAVE_FIDO_DEV_SUPPORTS_CRED_PROT
65 #define fido_dev_supports_cred_prot(x) (0)
66 #endif
67 #ifndef HAVE_FIDO_DEV_GET_TOUCH_BEGIN
68 #define fido_dev_get_touch_begin(x) (FIDO_ERR_UNSUPPORTED_OPTION)
69 #endif
70 #ifndef HAVE_FIDO_DEV_GET_TOUCH_STATUS
71 #define fido_dev_get_touch_status(x, y, z) (FIDO_ERR_UNSUPPORTED_OPTION)
72 #endif
73 #ifndef FIDO_CRED_PROT_UV_REQUIRED
74 #define FIDO_CRED_PROT_UV_REQUIRED 0
75 #endif
76 #ifndef FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID
77 #define FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID 0
78 #endif
80 # include "misc.h"
82 #ifndef SK_STANDALONE
83 # include "log.h"
84 # include "xmalloc.h"
86 * If building as part of OpenSSH, then rename exported functions.
87 * This must be done before including sk-api.h.
89 # define sk_api_version ssh_sk_api_version
90 # define sk_enroll ssh_sk_enroll
91 # define sk_sign ssh_sk_sign
92 # define sk_load_resident_keys ssh_sk_load_resident_keys
93 #endif /* !SK_STANDALONE */
95 #include "sk-api.h"
97 /* #define SK_DEBUG 1 */
99 #ifdef SK_DEBUG
100 #define SSH_FIDO_INIT_ARG FIDO_DEBUG
101 #else
102 #define SSH_FIDO_INIT_ARG 0
103 #endif
105 #define MAX_FIDO_DEVICES 8
106 #define FIDO_POLL_MS 50
107 #define SELECT_MS 15000
108 #define POLL_SLEEP_NS 200000000
110 #ifndef FIDO_ERR_OPERATION_DENIED
111 #define FIDO_ERR_OPERATION_DENIED 0x27
112 #endif
114 struct sk_usbhid {
115 fido_dev_t *dev;
116 char *path;
119 /* Return the version of the middleware API */
120 uint32_t sk_api_version(void);
122 /* Enroll a U2F key (private key generation) */
123 int sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len,
124 const char *application, uint8_t flags, const char *pin,
125 struct sk_option **options, struct sk_enroll_response **enroll_response);
127 /* Sign a challenge */
128 int sk_sign(uint32_t alg, const uint8_t *data, size_t data_len,
129 const char *application, const uint8_t *key_handle, size_t key_handle_len,
130 uint8_t flags, const char *pin, struct sk_option **options,
131 struct sk_sign_response **sign_response);
133 /* Load resident keys */
134 int sk_load_resident_keys(const char *pin, struct sk_option **options,
135 struct sk_resident_key ***rks, size_t *nrks);
137 static void skdebug(const char *func, const char *fmt, ...)
138 __attribute__((__format__ (printf, 2, 3)));
140 static void
141 skdebug(const char *func, const char *fmt, ...)
143 #if !defined(SK_STANDALONE)
144 char *msg;
145 va_list ap;
147 va_start(ap, fmt);
148 xvasprintf(&msg, fmt, ap);
149 va_end(ap);
150 debug("%s: %s", func, msg);
151 free(msg);
152 #elif defined(SK_DEBUG)
153 va_list ap;
155 va_start(ap, fmt);
156 fprintf(stderr, "%s: ", func);
157 vfprintf(stderr, fmt, ap);
158 fputc('\n', stderr);
159 va_end(ap);
160 #else
161 (void)func; /* XXX */
162 (void)fmt; /* XXX */
163 #endif
166 uint32_t
167 sk_api_version(void)
169 return SSH_SK_VERSION_MAJOR;
172 static struct sk_usbhid *
173 sk_open(const char *path)
175 struct sk_usbhid *sk;
176 int r;
178 if (path == NULL) {
179 skdebug(__func__, "path == NULL");
180 return NULL;
182 if ((sk = calloc(1, sizeof(*sk))) == NULL) {
183 skdebug(__func__, "calloc sk failed");
184 return NULL;
186 if ((sk->path = strdup(path)) == NULL) {
187 skdebug(__func__, "strdup path failed");
188 free(sk);
189 return NULL;
191 if ((sk->dev = fido_dev_new()) == NULL) {
192 skdebug(__func__, "fido_dev_new failed");
193 free(sk->path);
194 free(sk);
195 return NULL;
197 if ((r = fido_dev_open(sk->dev, sk->path)) != FIDO_OK) {
198 skdebug(__func__, "fido_dev_open %s failed: %s", sk->path,
199 fido_strerr(r));
200 fido_dev_free(&sk->dev);
201 free(sk->path);
202 free(sk);
203 return NULL;
205 return sk;
208 static void
209 sk_close(struct sk_usbhid *sk)
211 if (sk == NULL)
212 return;
213 fido_dev_cancel(sk->dev); /* cancel any pending operation */
214 fido_dev_close(sk->dev);
215 fido_dev_free(&sk->dev);
216 free(sk->path);
217 free(sk);
220 static struct sk_usbhid **
221 sk_openv(const fido_dev_info_t *devlist, size_t ndevs, size_t *nopen)
223 const fido_dev_info_t *di;
224 struct sk_usbhid **skv;
225 size_t i;
227 *nopen = 0;
228 if ((skv = calloc(ndevs, sizeof(*skv))) == NULL) {
229 skdebug(__func__, "calloc skv failed");
230 return NULL;
232 for (i = 0; i < ndevs; i++) {
233 if ((di = fido_dev_info_ptr(devlist, i)) == NULL)
234 skdebug(__func__, "fido_dev_info_ptr failed");
235 else if ((skv[*nopen] = sk_open(fido_dev_info_path(di))) == NULL)
236 skdebug(__func__, "sk_open failed");
237 else
238 (*nopen)++;
240 if (*nopen == 0) {
241 for (i = 0; i < ndevs; i++)
242 sk_close(skv[i]);
243 free(skv);
244 skv = NULL;
247 return skv;
250 static void
251 sk_closev(struct sk_usbhid **skv, size_t nsk)
253 size_t i;
255 for (i = 0; i < nsk; i++)
256 sk_close(skv[i]);
257 free(skv);
260 static int
261 sk_touch_begin(struct sk_usbhid **skv, size_t nsk)
263 size_t i, ok = 0;
264 int r;
266 for (i = 0; i < nsk; i++)
267 if ((r = fido_dev_get_touch_begin(skv[i]->dev)) != FIDO_OK)
268 skdebug(__func__, "fido_dev_get_touch_begin %s failed:"
269 " %s", skv[i]->path, fido_strerr(r));
270 else
271 ok++;
273 return ok ? 0 : -1;
276 static int
277 sk_touch_poll(struct sk_usbhid **skv, size_t nsk, int *touch, size_t *idx)
279 struct timespec ts_pause;
280 size_t npoll, i;
281 int r;
283 ts_pause.tv_sec = 0;
284 ts_pause.tv_nsec = POLL_SLEEP_NS;
285 nanosleep(&ts_pause, NULL);
286 npoll = nsk;
287 for (i = 0; i < nsk; i++) {
288 if (skv[i] == NULL)
289 continue; /* device discarded */
290 skdebug(__func__, "polling %s", skv[i]->path);
291 if ((r = fido_dev_get_touch_status(skv[i]->dev, touch,
292 FIDO_POLL_MS)) != FIDO_OK) {
293 skdebug(__func__, "fido_dev_get_touch_status %s: %s",
294 skv[i]->path, fido_strerr(r));
295 sk_close(skv[i]); /* discard device */
296 skv[i] = NULL;
297 if (--npoll == 0) {
298 skdebug(__func__, "no device left to poll");
299 return -1;
301 } else if (*touch) {
302 *idx = i;
303 return 0;
306 *touch = 0;
307 return 0;
310 #if !defined(HAVE_FIDO_ASSERT_SET_CLIENTDATA) || \
311 !defined(HAVE_FIDO_CRED_SET_CLIENTDATA)
312 /* Calculate SHA256(m) */
313 static int
314 sha256_mem(const void *m, size_t mlen, u_char *d, size_t dlen)
316 #ifdef WITH_OPENSSL
317 u_int mdlen;
318 #else
319 SHA2_CTX ctx;
320 #endif
322 if (dlen != 32)
323 return -1;
324 #ifdef WITH_OPENSSL
325 mdlen = dlen;
326 if (!EVP_Digest(m, mlen, d, &mdlen, EVP_sha256(), NULL))
327 return -1;
328 #else
329 SHA256Init(&ctx);
330 SHA256Update(&ctx, (const uint8_t *)m, mlen);
331 SHA256Final(d, &ctx);
332 #endif
333 return 0;
335 #endif /* !HAVE_FIDO_ASSERT_SET_CLIENTDATA || !HAVE_FIDO_CRED_SET_CLIENTDATA */
337 #ifndef HAVE_FIDO_CRED_SET_CLIENTDATA
338 static int
339 fido_cred_set_clientdata(fido_cred_t *cred, const u_char *ptr, size_t len)
341 uint8_t d[32];
342 int r;
344 if (sha256_mem(ptr, len, d, sizeof(d)) != 0) {
345 skdebug(__func__, "hash challenge failed");
346 return FIDO_ERR_INTERNAL;
348 r = fido_cred_set_clientdata_hash(cred, d, sizeof(d));
349 explicit_bzero(d, sizeof(d));
350 if (r != FIDO_OK) {
351 skdebug(__func__, "fido_cred_set_clientdata_hash failed: %s",
352 fido_strerr(r));
354 return r;
356 #endif /* HAVE_FIDO_CRED_SET_CLIENTDATA */
358 #ifndef HAVE_FIDO_ASSERT_SET_CLIENTDATA
359 static int
360 fido_assert_set_clientdata(fido_assert_t *assert, const u_char *ptr, size_t len)
362 uint8_t d[32];
363 int r;
365 if (sha256_mem(ptr, len, d, sizeof(d)) != 0) {
366 skdebug(__func__, "hash challenge failed");
367 return FIDO_ERR_INTERNAL;
369 r = fido_assert_set_clientdata_hash(assert, d, sizeof(d));
370 explicit_bzero(d, sizeof(d));
371 if (r != FIDO_OK) {
372 skdebug(__func__, "fido_assert_set_clientdata_hash failed: %s",
373 fido_strerr(r));
375 return r;
377 #endif /* HAVE_FIDO_ASSERT_SET_CLIENTDATA */
379 #ifndef HAVE_FIDO_DEV_IS_WINHELLO
380 static bool
381 fido_dev_is_winhello(const fido_dev_t *fdev)
383 return 0;
385 #endif /* HAVE_FIDO_DEV_IS_WINHELLO */
387 /* Check if the specified key handle exists on a given sk. */
388 static int
389 sk_try(const struct sk_usbhid *sk, const char *application,
390 const uint8_t *key_handle, size_t key_handle_len)
392 fido_assert_t *assert = NULL;
393 int r = FIDO_ERR_INTERNAL;
394 uint8_t message[32];
396 memset(message, '\0', sizeof(message));
397 if ((assert = fido_assert_new()) == NULL) {
398 skdebug(__func__, "fido_assert_new failed");
399 goto out;
401 /* generate an invalid signature on FIDO2 tokens */
402 if ((r = fido_assert_set_clientdata(assert, message,
403 sizeof(message))) != FIDO_OK) {
404 skdebug(__func__, "fido_assert_set_clientdata: %s",
405 fido_strerr(r));
406 goto out;
408 if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) {
409 skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r));
410 goto out;
412 if ((r = fido_assert_allow_cred(assert, key_handle,
413 key_handle_len)) != FIDO_OK) {
414 skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r));
415 goto out;
417 if ((r = fido_assert_set_up(assert, FIDO_OPT_FALSE)) != FIDO_OK) {
418 skdebug(__func__, "fido_assert_up: %s", fido_strerr(r));
419 goto out;
421 r = fido_dev_get_assert(sk->dev, assert, NULL);
422 skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r));
423 if (r == FIDO_ERR_USER_PRESENCE_REQUIRED) {
424 /* U2F tokens may return this */
425 r = FIDO_OK;
427 out:
428 fido_assert_free(&assert);
430 return r != FIDO_OK ? -1 : 0;
433 static int
434 check_sk_options(fido_dev_t *dev, const char *opt, int *ret)
436 fido_cbor_info_t *info;
437 char * const *name;
438 const bool *value;
439 size_t len, i;
440 int r;
442 *ret = -1;
444 if (!fido_dev_is_fido2(dev)) {
445 skdebug(__func__, "device is not fido2");
446 return 0;
448 if ((info = fido_cbor_info_new()) == NULL) {
449 skdebug(__func__, "fido_cbor_info_new failed");
450 return -1;
452 if ((r = fido_dev_get_cbor_info(dev, info)) != FIDO_OK) {
453 skdebug(__func__, "fido_dev_get_cbor_info: %s", fido_strerr(r));
454 fido_cbor_info_free(&info);
455 return -1;
457 name = fido_cbor_info_options_name_ptr(info);
458 value = fido_cbor_info_options_value_ptr(info);
459 len = fido_cbor_info_options_len(info);
460 for (i = 0; i < len; i++) {
461 if (!strcmp(name[i], opt)) {
462 *ret = value[i];
463 break;
466 fido_cbor_info_free(&info);
467 if (*ret == -1)
468 skdebug(__func__, "option %s is unknown", opt);
469 else
470 skdebug(__func__, "option %s is %s", opt, *ret ? "on" : "off");
472 return 0;
475 static struct sk_usbhid *
476 sk_select_by_cred(const fido_dev_info_t *devlist, size_t ndevs,
477 const char *application, const uint8_t *key_handle, size_t key_handle_len)
479 struct sk_usbhid **skv, *sk;
480 size_t skvcnt, i;
481 int internal_uv;
483 if ((skv = sk_openv(devlist, ndevs, &skvcnt)) == NULL) {
484 skdebug(__func__, "sk_openv failed");
485 return NULL;
487 if (skvcnt == 1 && check_sk_options(skv[0]->dev, "uv",
488 &internal_uv) == 0 && internal_uv != -1) {
489 sk = skv[0];
490 skv[0] = NULL;
491 goto out;
493 sk = NULL;
494 for (i = 0; i < skvcnt; i++) {
495 if (sk_try(skv[i], application, key_handle,
496 key_handle_len) == 0) {
497 sk = skv[i];
498 skv[i] = NULL;
499 skdebug(__func__, "found key in %s", sk->path);
500 break;
503 out:
504 sk_closev(skv, skvcnt);
505 return sk;
508 static struct sk_usbhid *
509 sk_select_by_touch(const fido_dev_info_t *devlist, size_t ndevs)
511 struct sk_usbhid **skv, *sk;
512 struct timeval tv_start, tv_now, tv_delta;
513 size_t skvcnt, idx;
514 int touch, ms_remain;
516 if ((skv = sk_openv(devlist, ndevs, &skvcnt)) == NULL) {
517 skdebug(__func__, "sk_openv failed");
518 return NULL;
520 sk = NULL;
521 if (skvcnt < 2) {
522 if (skvcnt == 1) {
523 /* single candidate */
524 sk = skv[0];
525 skv[0] = NULL;
527 goto out;
529 #ifndef HAVE_FIDO_DEV_GET_TOUCH_STATUS
530 skdebug(__func__, "libfido2 version does not support a feature needed for multiple tokens. Please upgrade to >=1.5.0");
531 goto out;
532 #endif
534 if (sk_touch_begin(skv, skvcnt) == -1) {
535 skdebug(__func__, "sk_touch_begin failed");
536 goto out;
538 monotime_tv(&tv_start);
539 do {
540 if (sk_touch_poll(skv, skvcnt, &touch, &idx) == -1) {
541 skdebug(__func__, "sk_touch_poll failed");
542 goto out;
544 if (touch) {
545 sk = skv[idx];
546 skv[idx] = NULL;
547 goto out;
549 monotime_tv(&tv_now);
550 timersub(&tv_now, &tv_start, &tv_delta);
551 ms_remain = SELECT_MS - tv_delta.tv_sec * 1000 -
552 tv_delta.tv_usec / 1000;
553 } while (ms_remain >= FIDO_POLL_MS);
554 skdebug(__func__, "timeout");
555 out:
556 sk_closev(skv, skvcnt);
557 return sk;
560 static struct sk_usbhid *
561 sk_probe(const char *application, const uint8_t *key_handle,
562 size_t key_handle_len, int probe_resident)
564 struct sk_usbhid *sk;
565 fido_dev_info_t *devlist;
566 size_t ndevs;
567 int r;
569 #ifdef HAVE_CYGWIN
570 if (!probe_resident && (sk = sk_open("windows://hello")) != NULL)
571 return sk;
572 #endif /* HAVE_CYGWIN */
573 if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) {
574 skdebug(__func__, "fido_dev_info_new failed");
575 return NULL;
577 if ((r = fido_dev_info_manifest(devlist, MAX_FIDO_DEVICES,
578 &ndevs)) != FIDO_OK) {
579 skdebug(__func__, "fido_dev_info_manifest failed: %s",
580 fido_strerr(r));
581 fido_dev_info_free(&devlist, MAX_FIDO_DEVICES);
582 return NULL;
584 skdebug(__func__, "%zu device(s) detected", ndevs);
585 if (ndevs == 0) {
586 sk = NULL;
587 } else if (application != NULL && key_handle != NULL) {
588 skdebug(__func__, "selecting sk by cred");
589 sk = sk_select_by_cred(devlist, ndevs, application, key_handle,
590 key_handle_len);
591 } else {
592 skdebug(__func__, "selecting sk by touch");
593 sk = sk_select_by_touch(devlist, ndevs);
595 fido_dev_info_free(&devlist, MAX_FIDO_DEVICES);
596 return sk;
599 #ifdef WITH_OPENSSL
601 * The key returned via fido_cred_pubkey_ptr() is in affine coordinates,
602 * but the API expects a SEC1 octet string.
604 static int
605 pack_public_key_ecdsa(const fido_cred_t *cred,
606 struct sk_enroll_response *response)
608 const uint8_t *ptr;
609 BIGNUM *x = NULL, *y = NULL;
610 EC_POINT *q = NULL;
611 EC_GROUP *g = NULL;
612 int ret = -1;
614 response->public_key = NULL;
615 response->public_key_len = 0;
617 if ((x = BN_new()) == NULL ||
618 (y = BN_new()) == NULL ||
619 (g = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)) == NULL ||
620 (q = EC_POINT_new(g)) == NULL) {
621 skdebug(__func__, "libcrypto setup failed");
622 goto out;
624 if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) {
625 skdebug(__func__, "fido_cred_pubkey_ptr failed");
626 goto out;
628 if (fido_cred_pubkey_len(cred) != 64) {
629 skdebug(__func__, "bad fido_cred_pubkey_len %zu",
630 fido_cred_pubkey_len(cred));
631 goto out;
634 if (BN_bin2bn(ptr, 32, x) == NULL ||
635 BN_bin2bn(ptr + 32, 32, y) == NULL) {
636 skdebug(__func__, "BN_bin2bn failed");
637 goto out;
639 if (EC_POINT_set_affine_coordinates_GFp(g, q, x, y, NULL) != 1) {
640 skdebug(__func__, "EC_POINT_set_affine_coordinates_GFp failed");
641 goto out;
643 response->public_key_len = EC_POINT_point2oct(g, q,
644 POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL);
645 if (response->public_key_len == 0 || response->public_key_len > 2048) {
646 skdebug(__func__, "bad pubkey length %zu",
647 response->public_key_len);
648 goto out;
650 if ((response->public_key = malloc(response->public_key_len)) == NULL) {
651 skdebug(__func__, "malloc pubkey failed");
652 goto out;
654 if (EC_POINT_point2oct(g, q, POINT_CONVERSION_UNCOMPRESSED,
655 response->public_key, response->public_key_len, NULL) == 0) {
656 skdebug(__func__, "EC_POINT_point2oct failed");
657 goto out;
659 /* success */
660 ret = 0;
661 out:
662 if (ret != 0 && response->public_key != NULL) {
663 memset(response->public_key, 0, response->public_key_len);
664 free(response->public_key);
665 response->public_key = NULL;
667 EC_POINT_free(q);
668 EC_GROUP_free(g);
669 BN_clear_free(x);
670 BN_clear_free(y);
671 return ret;
673 #endif /* WITH_OPENSSL */
675 static int
676 pack_public_key_ed25519(const fido_cred_t *cred,
677 struct sk_enroll_response *response)
679 const uint8_t *ptr;
680 size_t len;
681 int ret = -1;
683 response->public_key = NULL;
684 response->public_key_len = 0;
686 if ((len = fido_cred_pubkey_len(cred)) != 32) {
687 skdebug(__func__, "bad fido_cred_pubkey_len len %zu", len);
688 goto out;
690 if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) {
691 skdebug(__func__, "fido_cred_pubkey_ptr failed");
692 goto out;
694 response->public_key_len = len;
695 if ((response->public_key = malloc(response->public_key_len)) == NULL) {
696 skdebug(__func__, "malloc pubkey failed");
697 goto out;
699 memcpy(response->public_key, ptr, len);
700 ret = 0;
701 out:
702 if (ret != 0)
703 free(response->public_key);
704 return ret;
707 static int
708 pack_public_key(uint32_t alg, const fido_cred_t *cred,
709 struct sk_enroll_response *response)
711 switch(alg) {
712 #ifdef WITH_OPENSSL
713 case SSH_SK_ECDSA:
714 return pack_public_key_ecdsa(cred, response);
715 #endif /* WITH_OPENSSL */
716 case SSH_SK_ED25519:
717 return pack_public_key_ed25519(cred, response);
718 default:
719 return -1;
723 static int
724 fidoerr_to_skerr(int fidoerr)
726 switch (fidoerr) {
727 case FIDO_ERR_UNSUPPORTED_OPTION:
728 case FIDO_ERR_UNSUPPORTED_ALGORITHM:
729 return SSH_SK_ERR_UNSUPPORTED;
730 case FIDO_ERR_PIN_REQUIRED:
731 case FIDO_ERR_PIN_INVALID:
732 case FIDO_ERR_OPERATION_DENIED:
733 return SSH_SK_ERR_PIN_REQUIRED;
734 default:
735 return -1;
739 static int
740 check_enroll_options(struct sk_option **options, char **devicep,
741 uint8_t *user_id, size_t user_id_len)
743 size_t i;
745 if (options == NULL)
746 return 0;
747 for (i = 0; options[i] != NULL; i++) {
748 if (strcmp(options[i]->name, "device") == 0) {
749 if ((*devicep = strdup(options[i]->value)) == NULL) {
750 skdebug(__func__, "strdup device failed");
751 return -1;
753 skdebug(__func__, "requested device %s", *devicep);
754 } else if (strcmp(options[i]->name, "user") == 0) {
755 if (strlcpy(user_id, options[i]->value, user_id_len) >=
756 user_id_len) {
757 skdebug(__func__, "user too long");
758 return -1;
760 skdebug(__func__, "requested user %s",
761 (char *)user_id);
762 } else {
763 skdebug(__func__, "requested unsupported option %s",
764 options[i]->name);
765 if (options[i]->required) {
766 skdebug(__func__, "unknown required option");
767 return -1;
771 return 0;
774 static int
775 key_lookup(fido_dev_t *dev, const char *application, const uint8_t *user_id,
776 size_t user_id_len, const char *pin)
778 fido_assert_t *assert = NULL;
779 uint8_t message[32];
780 int r = FIDO_ERR_INTERNAL;
781 int sk_supports_uv, uv;
782 size_t i;
784 memset(message, '\0', sizeof(message));
785 if ((assert = fido_assert_new()) == NULL) {
786 skdebug(__func__, "fido_assert_new failed");
787 goto out;
789 /* generate an invalid signature on FIDO2 tokens */
790 if ((r = fido_assert_set_clientdata(assert, message,
791 sizeof(message))) != FIDO_OK) {
792 skdebug(__func__, "fido_assert_set_clientdata: %s",
793 fido_strerr(r));
794 goto out;
796 if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) {
797 skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r));
798 goto out;
800 if ((r = fido_assert_set_up(assert, FIDO_OPT_FALSE)) != FIDO_OK) {
801 skdebug(__func__, "fido_assert_set_up: %s", fido_strerr(r));
802 goto out;
804 uv = FIDO_OPT_OMIT;
805 if (pin == NULL && check_sk_options(dev, "uv", &sk_supports_uv) == 0 &&
806 sk_supports_uv != -1)
807 uv = FIDO_OPT_TRUE;
808 if ((r = fido_assert_set_uv(assert, uv)) != FIDO_OK) {
809 skdebug(__func__, "fido_assert_set_uv: %s", fido_strerr(r));
810 goto out;
812 if ((r = fido_dev_get_assert(dev, assert, pin)) != FIDO_OK) {
813 skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r));
814 goto out;
816 r = FIDO_ERR_NO_CREDENTIALS;
817 skdebug(__func__, "%zu signatures returned", fido_assert_count(assert));
818 for (i = 0; i < fido_assert_count(assert); i++) {
819 if (fido_assert_user_id_len(assert, i) == user_id_len &&
820 memcmp(fido_assert_user_id_ptr(assert, i), user_id,
821 user_id_len) == 0) {
822 skdebug(__func__, "credential exists");
823 r = FIDO_OK;
824 goto out;
827 out:
828 fido_assert_free(&assert);
830 return r;
834 sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len,
835 const char *application, uint8_t flags, const char *pin,
836 struct sk_option **options, struct sk_enroll_response **enroll_response)
838 fido_cred_t *cred = NULL;
839 const uint8_t *ptr;
840 uint8_t user_id[32];
841 struct sk_usbhid *sk = NULL;
842 struct sk_enroll_response *response = NULL;
843 size_t len;
844 int credprot;
845 int cose_alg;
846 int ret = SSH_SK_ERR_GENERAL;
847 int r;
848 char *device = NULL;
850 fido_init(SSH_FIDO_INIT_ARG);
852 if (enroll_response == NULL) {
853 skdebug(__func__, "enroll_response == NULL");
854 goto out;
856 *enroll_response = NULL;
857 memset(user_id, 0, sizeof(user_id));
858 if (check_enroll_options(options, &device, user_id,
859 sizeof(user_id)) != 0)
860 goto out; /* error already logged */
862 switch(alg) {
863 #ifdef WITH_OPENSSL
864 case SSH_SK_ECDSA:
865 cose_alg = COSE_ES256;
866 break;
867 #endif /* WITH_OPENSSL */
868 case SSH_SK_ED25519:
869 cose_alg = COSE_EDDSA;
870 break;
871 default:
872 skdebug(__func__, "unsupported key type %d", alg);
873 goto out;
875 if (device != NULL)
876 sk = sk_open(device);
877 else
878 sk = sk_probe(NULL, NULL, 0, 0);
879 if (sk == NULL) {
880 ret = SSH_SK_ERR_DEVICE_NOT_FOUND;
881 skdebug(__func__, "failed to find sk");
882 goto out;
884 skdebug(__func__, "using device %s", sk->path);
885 if ((flags & SSH_SK_RESIDENT_KEY) != 0 &&
886 (flags & SSH_SK_FORCE_OPERATION) == 0 &&
887 (r = key_lookup(sk->dev, application, user_id, sizeof(user_id),
888 pin)) != FIDO_ERR_NO_CREDENTIALS) {
889 if (r != FIDO_OK) {
890 ret = fidoerr_to_skerr(r);
891 skdebug(__func__, "key_lookup failed");
892 } else {
893 ret = SSH_SK_ERR_CREDENTIAL_EXISTS;
894 skdebug(__func__, "key exists");
896 goto out;
898 if ((cred = fido_cred_new()) == NULL) {
899 skdebug(__func__, "fido_cred_new failed");
900 goto out;
902 if ((r = fido_cred_set_type(cred, cose_alg)) != FIDO_OK) {
903 skdebug(__func__, "fido_cred_set_type: %s", fido_strerr(r));
904 goto out;
906 if ((r = fido_cred_set_clientdata(cred,
907 challenge, challenge_len)) != FIDO_OK) {
908 skdebug(__func__, "fido_cred_set_clientdata: %s",
909 fido_strerr(r));
910 goto out;
912 if ((r = fido_cred_set_rk(cred, (flags & SSH_SK_RESIDENT_KEY) != 0 ?
913 FIDO_OPT_TRUE : FIDO_OPT_OMIT)) != FIDO_OK) {
914 skdebug(__func__, "fido_cred_set_rk: %s", fido_strerr(r));
915 goto out;
917 if ((r = fido_cred_set_user(cred, user_id, sizeof(user_id),
918 "openssh", "openssh", NULL)) != FIDO_OK) {
919 skdebug(__func__, "fido_cred_set_user: %s", fido_strerr(r));
920 goto out;
922 if ((r = fido_cred_set_rp(cred, application, NULL)) != FIDO_OK) {
923 skdebug(__func__, "fido_cred_set_rp: %s", fido_strerr(r));
924 goto out;
926 if ((flags & (SSH_SK_RESIDENT_KEY|SSH_SK_USER_VERIFICATION_REQD)) != 0) {
927 #if !defined(HAVE_FIDO_DEV_SUPPORTS_CRED_PROT) || \
928 !defined(HAVE_FIDO_CRED_SET_PROT)
929 skdebug(__func__, "libfido2 version does not support a feature required for this operation. Please upgrade to >=1.5.0");
930 ret = SSH_SK_ERR_UNSUPPORTED;
931 goto out;
932 credprot = 0; (void)credprot; /* avoid warning */
933 #endif
934 if (!fido_dev_supports_cred_prot(sk->dev)) {
935 skdebug(__func__, "%s does not support credprot, "
936 "refusing to create unprotected "
937 "resident/verify-required key", sk->path);
938 ret = SSH_SK_ERR_UNSUPPORTED;
939 goto out;
941 if ((flags & SSH_SK_USER_VERIFICATION_REQD))
942 credprot = FIDO_CRED_PROT_UV_REQUIRED;
943 else
944 credprot = FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID;
946 if ((r = fido_cred_set_prot(cred, credprot)) != FIDO_OK) {
947 skdebug(__func__, "fido_cred_set_prot: %s",
948 fido_strerr(r));
949 ret = fidoerr_to_skerr(r);
950 goto out;
953 if ((r = fido_dev_make_cred(sk->dev, cred, pin)) != FIDO_OK) {
954 skdebug(__func__, "fido_dev_make_cred: %s", fido_strerr(r));
955 ret = fidoerr_to_skerr(r);
956 goto out;
958 if (fido_cred_x5c_ptr(cred) != NULL) {
959 if ((r = fido_cred_verify(cred)) != FIDO_OK) {
960 skdebug(__func__, "fido_cred_verify: %s",
961 fido_strerr(r));
962 goto out;
964 } else if (strcmp(fido_cred_fmt(cred), "none") != 0) {
965 skdebug(__func__, "self-attested credential");
966 if ((r = fido_cred_verify_self(cred)) != FIDO_OK) {
967 skdebug(__func__, "fido_cred_verify_self: %s",
968 fido_strerr(r));
969 goto out;
971 } else {
972 skdebug(__func__, "no attestation data");
974 if ((response = calloc(1, sizeof(*response))) == NULL) {
975 skdebug(__func__, "calloc response failed");
976 goto out;
978 response->flags = flags;
979 if (pack_public_key(alg, cred, response) != 0) {
980 skdebug(__func__, "pack_public_key failed");
981 goto out;
983 if ((ptr = fido_cred_id_ptr(cred)) != NULL) {
984 len = fido_cred_id_len(cred);
985 if ((response->key_handle = calloc(1, len)) == NULL) {
986 skdebug(__func__, "calloc key handle failed");
987 goto out;
989 memcpy(response->key_handle, ptr, len);
990 response->key_handle_len = len;
992 if ((ptr = fido_cred_sig_ptr(cred)) != NULL) {
993 len = fido_cred_sig_len(cred);
994 if ((response->signature = calloc(1, len)) == NULL) {
995 skdebug(__func__, "calloc signature failed");
996 goto out;
998 memcpy(response->signature, ptr, len);
999 response->signature_len = len;
1001 if ((ptr = fido_cred_x5c_ptr(cred)) != NULL) {
1002 len = fido_cred_x5c_len(cred);
1003 skdebug(__func__, "attestation cert len=%zu", len);
1004 if ((response->attestation_cert = calloc(1, len)) == NULL) {
1005 skdebug(__func__, "calloc attestation cert failed");
1006 goto out;
1008 memcpy(response->attestation_cert, ptr, len);
1009 response->attestation_cert_len = len;
1011 if ((ptr = fido_cred_authdata_ptr(cred)) != NULL) {
1012 len = fido_cred_authdata_len(cred);
1013 skdebug(__func__, "authdata len=%zu", len);
1014 if ((response->authdata = calloc(1, len)) == NULL) {
1015 skdebug(__func__, "calloc authdata failed");
1016 goto out;
1018 memcpy(response->authdata, ptr, len);
1019 response->authdata_len = len;
1021 *enroll_response = response;
1022 response = NULL;
1023 ret = 0;
1024 out:
1025 free(device);
1026 if (response != NULL) {
1027 free(response->public_key);
1028 free(response->key_handle);
1029 free(response->signature);
1030 free(response->attestation_cert);
1031 free(response->authdata);
1032 free(response);
1034 sk_close(sk);
1035 fido_cred_free(&cred);
1036 return ret;
1039 #ifdef WITH_OPENSSL
1040 static int
1041 pack_sig_ecdsa(fido_assert_t *assert, struct sk_sign_response *response)
1043 ECDSA_SIG *sig = NULL;
1044 const BIGNUM *sig_r, *sig_s;
1045 const unsigned char *cp;
1046 size_t sig_len;
1047 int ret = -1;
1049 cp = fido_assert_sig_ptr(assert, 0);
1050 sig_len = fido_assert_sig_len(assert, 0);
1051 if ((sig = d2i_ECDSA_SIG(NULL, &cp, sig_len)) == NULL) {
1052 skdebug(__func__, "d2i_ECDSA_SIG failed");
1053 goto out;
1055 ECDSA_SIG_get0(sig, &sig_r, &sig_s);
1056 response->sig_r_len = BN_num_bytes(sig_r);
1057 response->sig_s_len = BN_num_bytes(sig_s);
1058 if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL ||
1059 (response->sig_s = calloc(1, response->sig_s_len)) == NULL) {
1060 skdebug(__func__, "calloc signature failed");
1061 goto out;
1063 BN_bn2bin(sig_r, response->sig_r);
1064 BN_bn2bin(sig_s, response->sig_s);
1065 ret = 0;
1066 out:
1067 ECDSA_SIG_free(sig);
1068 if (ret != 0) {
1069 free(response->sig_r);
1070 free(response->sig_s);
1071 response->sig_r = NULL;
1072 response->sig_s = NULL;
1074 return ret;
1076 #endif /* WITH_OPENSSL */
1078 static int
1079 pack_sig_ed25519(fido_assert_t *assert, struct sk_sign_response *response)
1081 const unsigned char *ptr;
1082 size_t len;
1083 int ret = -1;
1085 ptr = fido_assert_sig_ptr(assert, 0);
1086 len = fido_assert_sig_len(assert, 0);
1087 if (len != 64) {
1088 skdebug(__func__, "bad length %zu", len);
1089 goto out;
1091 response->sig_r_len = len;
1092 if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL) {
1093 skdebug(__func__, "calloc signature failed");
1094 goto out;
1096 memcpy(response->sig_r, ptr, len);
1097 ret = 0;
1098 out:
1099 if (ret != 0) {
1100 free(response->sig_r);
1101 response->sig_r = NULL;
1103 return ret;
1106 static int
1107 pack_sig(uint32_t alg, fido_assert_t *assert,
1108 struct sk_sign_response *response)
1110 switch(alg) {
1111 #ifdef WITH_OPENSSL
1112 case SSH_SK_ECDSA:
1113 return pack_sig_ecdsa(assert, response);
1114 #endif /* WITH_OPENSSL */
1115 case SSH_SK_ED25519:
1116 return pack_sig_ed25519(assert, response);
1117 default:
1118 return -1;
1122 /* Checks sk_options for sk_sign() and sk_load_resident_keys() */
1123 static int
1124 check_sign_load_resident_options(struct sk_option **options, char **devicep)
1126 size_t i;
1128 if (options == NULL)
1129 return 0;
1130 for (i = 0; options[i] != NULL; i++) {
1131 if (strcmp(options[i]->name, "device") == 0) {
1132 if ((*devicep = strdup(options[i]->value)) == NULL) {
1133 skdebug(__func__, "strdup device failed");
1134 return -1;
1136 skdebug(__func__, "requested device %s", *devicep);
1137 } else {
1138 skdebug(__func__, "requested unsupported option %s",
1139 options[i]->name);
1140 if (options[i]->required) {
1141 skdebug(__func__, "unknown required option");
1142 return -1;
1146 return 0;
1150 sk_sign(uint32_t alg, const uint8_t *data, size_t datalen,
1151 const char *application,
1152 const uint8_t *key_handle, size_t key_handle_len,
1153 uint8_t flags, const char *pin, struct sk_option **options,
1154 struct sk_sign_response **sign_response)
1156 fido_assert_t *assert = NULL;
1157 char *device = NULL;
1158 struct sk_usbhid *sk = NULL;
1159 struct sk_sign_response *response = NULL;
1160 int ret = SSH_SK_ERR_GENERAL, internal_uv;
1161 int r;
1163 fido_init(SSH_FIDO_INIT_ARG);
1165 if (sign_response == NULL) {
1166 skdebug(__func__, "sign_response == NULL");
1167 goto out;
1169 *sign_response = NULL;
1170 if (check_sign_load_resident_options(options, &device) != 0)
1171 goto out; /* error already logged */
1172 if (device != NULL)
1173 sk = sk_open(device);
1174 else if (pin != NULL || (flags & SSH_SK_USER_VERIFICATION_REQD))
1175 sk = sk_probe(NULL, NULL, 0, 0);
1176 else
1177 sk = sk_probe(application, key_handle, key_handle_len, 0);
1178 if (sk == NULL) {
1179 ret = SSH_SK_ERR_DEVICE_NOT_FOUND;
1180 skdebug(__func__, "failed to find sk");
1181 goto out;
1183 if ((assert = fido_assert_new()) == NULL) {
1184 skdebug(__func__, "fido_assert_new failed");
1185 goto out;
1187 if ((r = fido_assert_set_clientdata(assert,
1188 data, datalen)) != FIDO_OK) {
1189 skdebug(__func__, "fido_assert_set_clientdata: %s",
1190 fido_strerr(r));
1191 goto out;
1193 if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) {
1194 skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r));
1195 goto out;
1197 if ((r = fido_assert_allow_cred(assert, key_handle,
1198 key_handle_len)) != FIDO_OK) {
1199 skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r));
1200 goto out;
1202 if ((r = fido_assert_set_up(assert,
1203 (flags & SSH_SK_USER_PRESENCE_REQD) ?
1204 FIDO_OPT_TRUE : FIDO_OPT_FALSE)) != FIDO_OK) {
1205 skdebug(__func__, "fido_assert_set_up: %s", fido_strerr(r));
1206 goto out;
1209 * WinHello requests the PIN by default. Make "uv" request explicit
1210 * to allow keys with and without -O verify-required to make sense.
1212 if (pin == NULL && fido_dev_is_winhello (sk->dev) &&
1213 (r = fido_assert_set_uv(assert, FIDO_OPT_FALSE)) != FIDO_OK) {
1214 skdebug(__func__, "fido_assert_set_uv: %s", fido_strerr(r));
1216 if (pin == NULL && (flags & SSH_SK_USER_VERIFICATION_REQD)) {
1217 if (check_sk_options(sk->dev, "uv", &internal_uv) < 0 ||
1218 internal_uv != 1) {
1219 skdebug(__func__, "check_sk_options uv");
1220 ret = SSH_SK_ERR_PIN_REQUIRED;
1221 goto out;
1223 if ((r = fido_assert_set_uv(assert,
1224 FIDO_OPT_TRUE)) != FIDO_OK) {
1225 skdebug(__func__, "fido_assert_set_uv: %s",
1226 fido_strerr(r));
1227 ret = fidoerr_to_skerr(r);
1228 goto out;
1231 if ((r = fido_dev_get_assert(sk->dev, assert, pin)) != FIDO_OK) {
1232 skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r));
1233 ret = fidoerr_to_skerr(r);
1234 goto out;
1236 if ((response = calloc(1, sizeof(*response))) == NULL) {
1237 skdebug(__func__, "calloc response failed");
1238 goto out;
1240 response->flags = fido_assert_flags(assert, 0);
1241 response->counter = fido_assert_sigcount(assert, 0);
1242 if (pack_sig(alg, assert, response) != 0) {
1243 skdebug(__func__, "pack_sig failed");
1244 goto out;
1246 *sign_response = response;
1247 response = NULL;
1248 ret = 0;
1249 out:
1250 free(device);
1251 if (response != NULL) {
1252 free(response->sig_r);
1253 free(response->sig_s);
1254 free(response);
1256 sk_close(sk);
1257 fido_assert_free(&assert);
1258 return ret;
1261 static int
1262 read_rks(struct sk_usbhid *sk, const char *pin,
1263 struct sk_resident_key ***rksp, size_t *nrksp)
1265 int ret = SSH_SK_ERR_GENERAL, r = -1, internal_uv;
1266 fido_credman_metadata_t *metadata = NULL;
1267 fido_credman_rp_t *rp = NULL;
1268 fido_credman_rk_t *rk = NULL;
1269 size_t i, j, nrp, nrk, user_id_len;
1270 const fido_cred_t *cred;
1271 const char *rp_id, *rp_name, *user_name;
1272 struct sk_resident_key *srk = NULL, **tmp;
1273 const u_char *user_id;
1275 if (pin == NULL) {
1276 skdebug(__func__, "no PIN specified");
1277 ret = SSH_SK_ERR_PIN_REQUIRED;
1278 goto out;
1280 if ((metadata = fido_credman_metadata_new()) == NULL) {
1281 skdebug(__func__, "alloc failed");
1282 goto out;
1284 if (check_sk_options(sk->dev, "uv", &internal_uv) != 0) {
1285 skdebug(__func__, "check_sk_options failed");
1286 goto out;
1289 if ((r = fido_credman_get_dev_metadata(sk->dev, metadata, pin)) != 0) {
1290 if (r == FIDO_ERR_INVALID_COMMAND) {
1291 skdebug(__func__, "device %s does not support "
1292 "resident keys", sk->path);
1293 ret = 0;
1294 goto out;
1296 skdebug(__func__, "get metadata for %s failed: %s",
1297 sk->path, fido_strerr(r));
1298 ret = fidoerr_to_skerr(r);
1299 goto out;
1301 skdebug(__func__, "existing %llu, remaining %llu",
1302 (unsigned long long)fido_credman_rk_existing(metadata),
1303 (unsigned long long)fido_credman_rk_remaining(metadata));
1304 if ((rp = fido_credman_rp_new()) == NULL) {
1305 skdebug(__func__, "alloc rp failed");
1306 goto out;
1308 if ((r = fido_credman_get_dev_rp(sk->dev, rp, pin)) != 0) {
1309 skdebug(__func__, "get RPs for %s failed: %s",
1310 sk->path, fido_strerr(r));
1311 goto out;
1313 nrp = fido_credman_rp_count(rp);
1314 skdebug(__func__, "Device %s has resident keys for %zu RPs",
1315 sk->path, nrp);
1317 /* Iterate over RP IDs that have resident keys */
1318 for (i = 0; i < nrp; i++) {
1319 rp_id = fido_credman_rp_id(rp, i);
1320 rp_name = fido_credman_rp_name(rp, i);
1321 skdebug(__func__, "rp %zu: name=\"%s\" id=\"%s\" hashlen=%zu",
1322 i, rp_name == NULL ? "(none)" : rp_name,
1323 rp_id == NULL ? "(none)" : rp_id,
1324 fido_credman_rp_id_hash_len(rp, i));
1326 /* Skip non-SSH RP IDs */
1327 if (rp_id == NULL ||
1328 strncasecmp(fido_credman_rp_id(rp, i), "ssh:", 4) != 0)
1329 continue;
1331 fido_credman_rk_free(&rk);
1332 if ((rk = fido_credman_rk_new()) == NULL) {
1333 skdebug(__func__, "alloc rk failed");
1334 goto out;
1336 if ((r = fido_credman_get_dev_rk(sk->dev,
1337 fido_credman_rp_id(rp, i), rk, pin)) != 0) {
1338 skdebug(__func__, "get RKs for %s slot %zu failed: %s",
1339 sk->path, i, fido_strerr(r));
1340 goto out;
1342 nrk = fido_credman_rk_count(rk);
1343 skdebug(__func__, "RP \"%s\" has %zu resident keys",
1344 fido_credman_rp_id(rp, i), nrk);
1346 /* Iterate over resident keys for this RP ID */
1347 for (j = 0; j < nrk; j++) {
1348 if ((cred = fido_credman_rk(rk, j)) == NULL) {
1349 skdebug(__func__, "no RK in slot %zu", j);
1350 continue;
1352 if ((user_name = fido_cred_user_name(cred)) == NULL)
1353 user_name = "";
1354 user_id = fido_cred_user_id_ptr(cred);
1355 user_id_len = fido_cred_user_id_len(cred);
1356 skdebug(__func__, "Device %s RP \"%s\" user \"%s\" "
1357 "uidlen %zu slot %zu: type %d flags 0x%02x "
1358 "prot 0x%02x", sk->path, rp_id, user_name,
1359 user_id_len, j, fido_cred_type(cred),
1360 fido_cred_flags(cred), fido_cred_prot(cred));
1362 /* build response entry */
1363 if ((srk = calloc(1, sizeof(*srk))) == NULL ||
1364 (srk->key.key_handle = calloc(1,
1365 fido_cred_id_len(cred))) == NULL ||
1366 (srk->application = strdup(rp_id)) == NULL ||
1367 (user_id_len > 0 &&
1368 (srk->user_id = calloc(1, user_id_len)) == NULL)) {
1369 skdebug(__func__, "alloc sk_resident_key");
1370 goto out;
1373 srk->key.key_handle_len = fido_cred_id_len(cred);
1374 memcpy(srk->key.key_handle, fido_cred_id_ptr(cred),
1375 srk->key.key_handle_len);
1376 srk->user_id_len = user_id_len;
1377 if (srk->user_id_len != 0)
1378 memcpy(srk->user_id, user_id, srk->user_id_len);
1380 switch (fido_cred_type(cred)) {
1381 case COSE_ES256:
1382 srk->alg = SSH_SK_ECDSA;
1383 break;
1384 case COSE_EDDSA:
1385 srk->alg = SSH_SK_ED25519;
1386 break;
1387 default:
1388 skdebug(__func__, "unsupported key type %d",
1389 fido_cred_type(cred));
1390 goto out; /* XXX free rk and continue */
1393 if (fido_cred_prot(cred) == FIDO_CRED_PROT_UV_REQUIRED
1394 && internal_uv == -1)
1395 srk->flags |= SSH_SK_USER_VERIFICATION_REQD;
1397 if ((r = pack_public_key(srk->alg, cred,
1398 &srk->key)) != 0) {
1399 skdebug(__func__, "pack public key failed");
1400 goto out;
1402 /* append */
1403 if ((tmp = recallocarray(*rksp, *nrksp, (*nrksp) + 1,
1404 sizeof(**rksp))) == NULL) {
1405 skdebug(__func__, "alloc rksp");
1406 goto out;
1408 *rksp = tmp;
1409 (*rksp)[(*nrksp)++] = srk;
1410 srk = NULL;
1413 /* Success */
1414 ret = 0;
1415 out:
1416 if (srk != NULL) {
1417 free(srk->application);
1418 freezero(srk->key.public_key, srk->key.public_key_len);
1419 freezero(srk->key.key_handle, srk->key.key_handle_len);
1420 freezero(srk->user_id, srk->user_id_len);
1421 freezero(srk, sizeof(*srk));
1423 fido_credman_rp_free(&rp);
1424 fido_credman_rk_free(&rk);
1425 fido_credman_metadata_free(&metadata);
1426 return ret;
1430 sk_load_resident_keys(const char *pin, struct sk_option **options,
1431 struct sk_resident_key ***rksp, size_t *nrksp)
1433 int ret = SSH_SK_ERR_GENERAL, r = -1;
1434 size_t i, nrks = 0;
1435 struct sk_resident_key **rks = NULL;
1436 struct sk_usbhid *sk = NULL;
1437 char *device = NULL;
1439 *rksp = NULL;
1440 *nrksp = 0;
1442 fido_init(SSH_FIDO_INIT_ARG);
1444 if (check_sign_load_resident_options(options, &device) != 0)
1445 goto out; /* error already logged */
1446 if (device != NULL)
1447 sk = sk_open(device);
1448 else
1449 sk = sk_probe(NULL, NULL, 0, 1);
1450 if (sk == NULL) {
1451 ret = SSH_SK_ERR_DEVICE_NOT_FOUND;
1452 skdebug(__func__, "failed to find sk");
1453 goto out;
1455 skdebug(__func__, "trying %s", sk->path);
1456 if ((r = read_rks(sk, pin, &rks, &nrks)) != 0) {
1457 skdebug(__func__, "read_rks failed for %s", sk->path);
1458 ret = r;
1459 goto out;
1461 /* success, unless we have no keys but a specific error */
1462 if (nrks > 0 || ret == SSH_SK_ERR_GENERAL)
1463 ret = 0;
1464 *rksp = rks;
1465 *nrksp = nrks;
1466 rks = NULL;
1467 nrks = 0;
1468 out:
1469 sk_close(sk);
1470 for (i = 0; i < nrks; i++) {
1471 free(rks[i]->application);
1472 freezero(rks[i]->key.public_key, rks[i]->key.public_key_len);
1473 freezero(rks[i]->key.key_handle, rks[i]->key.key_handle_len);
1474 freezero(rks[i]->user_id, rks[i]->user_id_len);
1475 freezero(rks[i], sizeof(*rks[i]));
1477 free(device);
1478 free(rks);
1479 return ret;
1482 #endif /* ENABLE_SK_INTERNAL */