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