Annotation of src/usr.bin/ssh/sk-usbhid.c, Revision 1.22
1.1 djm 1: /*
2: * Copyright (c) 2019 Markus Friedl
1.22 ! djm 3: * Copyright (c) 2020 Pedro Martelletto
1.1 djm 4: *
5: * Permission to use, copy, modify, and distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
8: *
9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16: */
17:
18: #include <stdint.h>
19: #include <stdlib.h>
20: #include <string.h>
21: #include <stdio.h>
22: #include <stddef.h>
23: #include <stdarg.h>
1.17 djm 24: #include <sha2.h>
1.1 djm 25:
1.7 naddy 26: #ifdef WITH_OPENSSL
1.1 djm 27: #include <openssl/opensslv.h>
28: #include <openssl/crypto.h>
29: #include <openssl/bn.h>
30: #include <openssl/ec.h>
31: #include <openssl/ecdsa.h>
1.17 djm 32: #include <openssl/evp.h>
1.7 naddy 33: #endif /* WITH_OPENSSL */
1.1 djm 34:
35: #include <fido.h>
1.9 djm 36: #include <fido/credman.h>
1.1 djm 37:
38: #ifndef SK_STANDALONE
1.15 djm 39: # include "log.h"
40: # include "xmalloc.h"
1.22 ! djm 41: # include "misc.h"
1.15 djm 42: /*
43: * If building as part of OpenSSH, then rename exported functions.
44: * This must be done before including sk-api.h.
45: */
46: # define sk_api_version ssh_sk_api_version
47: # define sk_enroll ssh_sk_enroll
48: # define sk_sign ssh_sk_sign
49: # define sk_load_resident_keys ssh_sk_load_resident_keys
50: #endif /* !SK_STANDALONE */
51:
52: #include "sk-api.h"
1.1 djm 53:
54: /* #define SK_DEBUG 1 */
55:
1.18 djm 56: #ifdef SK_DEBUG
57: #define SSH_FIDO_INIT_ARG FIDO_DEBUG
58: #else
59: #define SSH_FIDO_INIT_ARG 0
60: #endif
61:
1.22 ! djm 62: #define MAX_FIDO_DEVICES 8
! 63: #define FIDO_POLL_MS 50
! 64: #define SELECT_MS 15000
! 65: #define POLL_SLEEP_NS 200000000
1.1 djm 66:
67: /* Compatibility with OpenSSH 1.0.x */
68: #if (OPENSSL_VERSION_NUMBER < 0x10100000L)
69: #define ECDSA_SIG_get0(sig, pr, ps) \
70: do { \
71: (*pr) = sig->r; \
72: (*ps) = sig->s; \
73: } while (0)
74: #endif
75:
1.22 ! djm 76: struct sk_usbhid {
! 77: fido_dev_t *dev;
! 78: char *path;
! 79: };
! 80:
1.1 djm 81: /* Return the version of the middleware API */
82: uint32_t sk_api_version(void);
83:
84: /* Enroll a U2F key (private key generation) */
1.12 djm 85: int sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len,
1.10 djm 86: const char *application, uint8_t flags, const char *pin,
1.12 djm 87: struct sk_option **options, struct sk_enroll_response **enroll_response);
1.1 djm 88:
89: /* Sign a challenge */
1.12 djm 90: int sk_sign(uint32_t alg, const uint8_t *message, size_t message_len,
1.1 djm 91: const char *application, const uint8_t *key_handle, size_t key_handle_len,
1.12 djm 92: uint8_t flags, const char *pin, struct sk_option **options,
93: struct sk_sign_response **sign_response);
1.1 djm 94:
1.9 djm 95: /* Load resident keys */
1.12 djm 96: int sk_load_resident_keys(const char *pin, struct sk_option **options,
1.9 djm 97: struct sk_resident_key ***rks, size_t *nrks);
98:
1.1 djm 99: static void skdebug(const char *func, const char *fmt, ...)
100: __attribute__((__format__ (printf, 2, 3)));
101:
102: static void
103: skdebug(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);
1.2 djm 112: debug("%s: %s", func, msg);
1.1 djm 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:
128: uint32_t
129: sk_api_version(void)
130: {
1.15 djm 131: return SSH_SK_VERSION_MAJOR;
1.1 djm 132: }
133:
1.22 ! djm 134: static struct sk_usbhid *
! 135: sk_open(const char *path)
! 136: {
! 137: struct sk_usbhid *sk;
1.1 djm 138: int r;
139:
1.22 ! djm 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;
1.1 djm 158: }
1.22 ! djm 159: if ((r = fido_dev_open(sk->dev, sk->path)) != FIDO_OK) {
! 160: skdebug(__func__, "fido_dev_open %s failed: %s", sk->path,
1.1 djm 161: fido_strerr(r));
1.22 ! djm 162: fido_dev_free(&sk->dev);
! 163: free(sk->path);
! 164: free(sk);
! 165: return NULL;
! 166: }
! 167: return sk;
! 168: }
! 169:
! 170: static void
! 171: sk_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:
! 182: static struct sk_usbhid **
! 183: sk_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)++;
1.1 djm 201: }
1.22 ! djm 202: if (*nopen == 0) {
! 203: for (i = 0; i < ndevs; i++)
! 204: sk_close(skv[i]);
! 205: free(skv);
! 206: skv = NULL;
1.1 djm 207: }
1.22 ! djm 208:
! 209: return skv;
! 210: }
! 211:
! 212: static void
! 213: sk_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:
! 222: static int
! 223: sk_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:
! 238: static int
! 239: sk_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: }
1.1 djm 267: }
1.22 ! djm 268: *touch = 0;
! 269: return 0;
! 270: }
! 271:
! 272: /* Calculate SHA256(m) */
! 273: static int
! 274: sha256_mem(const void *m, size_t mlen, u_char *d, size_t dlen)
! 275: {
! 276: #ifdef WITH_OPENSSL
! 277: u_int mdlen;
! 278: #endif
! 279:
! 280: if (dlen != 32)
! 281: return -1;
! 282: #ifdef WITH_OPENSSL
! 283: mdlen = dlen;
! 284: if (!EVP_Digest(m, mlen, d, &mdlen, EVP_sha256(), NULL))
! 285: return -1;
! 286: #else
! 287: SHA256Data(m, mlen, d);
! 288: #endif
! 289: return 0;
1.1 djm 290: }
291:
1.22 ! djm 292: /* Check if the specified key handle exists on a given sk. */
1.1 djm 293: static int
1.22 ! djm 294: sk_try(const struct sk_usbhid *sk, const char *application,
! 295: const uint8_t *key_handle, size_t key_handle_len)
1.1 djm 296: {
297: fido_assert_t *assert = NULL;
1.22 ! djm 298: /* generate an invalid signature on FIDO2 tokens */
! 299: const char *data = "";
! 300: uint8_t message[32];
1.1 djm 301: int r = FIDO_ERR_INTERNAL;
302:
1.22 ! djm 303: if (sha256_mem(data, strlen(data), message, sizeof(message)) != 0) {
! 304: skdebug(__func__, "hash message failed");
! 305: goto out;
! 306: }
1.1 djm 307: if ((assert = fido_assert_new()) == NULL) {
308: skdebug(__func__, "fido_assert_new failed");
309: goto out;
310: }
311: if ((r = fido_assert_set_clientdata_hash(assert, message,
1.22 ! djm 312: sizeof(message))) != FIDO_OK) {
1.1 djm 313: skdebug(__func__, "fido_assert_set_clientdata_hash: %s",
314: fido_strerr(r));
315: goto out;
316: }
317: if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) {
318: skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r));
319: goto out;
320: }
321: if ((r = fido_assert_allow_cred(assert, key_handle,
322: key_handle_len)) != FIDO_OK) {
323: skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r));
324: goto out;
325: }
326: if ((r = fido_assert_set_up(assert, FIDO_OPT_FALSE)) != FIDO_OK) {
327: skdebug(__func__, "fido_assert_up: %s", fido_strerr(r));
328: goto out;
329: }
1.22 ! djm 330: r = fido_dev_get_assert(sk->dev, assert, NULL);
1.1 djm 331: skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r));
1.3 djm 332: if (r == FIDO_ERR_USER_PRESENCE_REQUIRED) {
333: /* U2F tokens may return this */
334: r = FIDO_OK;
335: }
1.1 djm 336: out:
337: fido_assert_free(&assert);
338:
339: return r != FIDO_OK ? -1 : 0;
340: }
341:
1.22 ! djm 342: static struct sk_usbhid *
! 343: sk_select_by_cred(const fido_dev_info_t *devlist, size_t ndevs,
! 344: const char *application, const uint8_t *key_handle, size_t key_handle_len)
1.1 djm 345: {
1.22 ! djm 346: struct sk_usbhid **skv, *sk;
! 347: size_t skvcnt, i;
1.1 djm 348:
1.22 ! djm 349: if ((skv = sk_openv(devlist, ndevs, &skvcnt)) == NULL) {
! 350: skdebug(__func__, "sk_openv failed");
! 351: return NULL;
! 352: }
! 353: sk = NULL;
! 354: for (i = 0; i < skvcnt; i++)
! 355: if (sk_try(skv[i], application, key_handle,
! 356: key_handle_len) == 0) {
! 357: sk = skv[i];
! 358: skv[i] = NULL;
! 359: skdebug(__func__, "found key in %s", sk->path);
! 360: break;
1.12 djm 361: }
1.22 ! djm 362: sk_closev(skv, skvcnt);
! 363: return sk;
! 364: }
! 365:
! 366: static struct sk_usbhid *
! 367: sk_select_by_touch(const fido_dev_info_t *devlist, size_t ndevs)
! 368: {
! 369: struct sk_usbhid **skv, *sk;
! 370: struct timeval tv_start, tv_now, tv_delta;
! 371: size_t skvcnt, idx;
! 372: int touch, ms_remain;
! 373:
! 374: if ((skv = sk_openv(devlist, ndevs, &skvcnt)) == NULL) {
! 375: skdebug(__func__, "sk_openv failed");
! 376: return NULL;
! 377: }
! 378: sk = NULL;
! 379: if (skvcnt < 2) {
! 380: if (skvcnt == 1) {
! 381: /* single candidate */
! 382: sk = skv[0];
! 383: skv[0] = NULL;
1.12 djm 384: }
1.22 ! djm 385: goto out;
1.12 djm 386: }
1.22 ! djm 387: if (sk_touch_begin(skv, skvcnt) == -1) {
! 388: skdebug(__func__, "sk_touch_begin failed");
! 389: goto out;
! 390: }
! 391: monotime_tv(&tv_start);
! 392: do {
! 393: if (sk_touch_poll(skv, skvcnt, &touch, &idx) == -1) {
! 394: skdebug(__func__, "sk_touch_poll failed");
! 395: goto out;
! 396: }
! 397: if (touch) {
! 398: sk = skv[idx];
! 399: skv[idx] = NULL;
! 400: goto out;
! 401: }
! 402: monotime_tv(&tv_now);
! 403: timersub(&tv_now, &tv_start, &tv_delta);
! 404: ms_remain = SELECT_MS - tv_delta.tv_sec * 1000 -
! 405: tv_delta.tv_usec / 1000;
! 406: } while (ms_remain >= FIDO_POLL_MS);
! 407: skdebug(__func__, "timeout");
! 408: out:
! 409: sk_closev(skv, skvcnt);
! 410: return sk;
! 411: }
! 412:
! 413: static struct sk_usbhid *
! 414: sk_probe(const char *application, const uint8_t *key_handle,
! 415: size_t key_handle_len)
! 416: {
! 417: struct sk_usbhid *sk;
! 418: fido_dev_info_t *devlist;
! 419: size_t ndevs;
! 420: int r;
1.12 djm 421:
1.1 djm 422: if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) {
423: skdebug(__func__, "fido_dev_info_new failed");
1.22 ! djm 424: return NULL;
1.1 djm 425: }
426: if ((r = fido_dev_info_manifest(devlist, MAX_FIDO_DEVICES,
1.22 ! djm 427: &ndevs)) != FIDO_OK) {
! 428: skdebug(__func__, "fido_dev_info_manifest failed: %s",
! 429: fido_strerr(r));
! 430: fido_dev_info_free(&devlist, MAX_FIDO_DEVICES);
! 431: return NULL;
1.1 djm 432: }
1.22 ! djm 433: skdebug(__func__, "%zu device(s) detected", ndevs);
! 434: if (ndevs == 0) {
! 435: sk = NULL;
! 436: } else if (application != NULL && key_handle != NULL) {
! 437: skdebug(__func__, "selecting sk by cred");
! 438: sk = sk_select_by_cred(devlist, ndevs, application, key_handle,
! 439: key_handle_len);
! 440: } else {
! 441: skdebug(__func__, "selecting sk by touch");
! 442: sk = sk_select_by_touch(devlist, ndevs);
1.1 djm 443: }
1.22 ! djm 444: fido_dev_info_free(&devlist, MAX_FIDO_DEVICES);
! 445: return sk;
1.1 djm 446: }
447:
1.7 naddy 448: #ifdef WITH_OPENSSL
1.1 djm 449: /*
450: * The key returned via fido_cred_pubkey_ptr() is in affine coordinates,
451: * but the API expects a SEC1 octet string.
452: */
453: static int
1.9 djm 454: pack_public_key_ecdsa(const fido_cred_t *cred,
455: struct sk_enroll_response *response)
1.1 djm 456: {
457: const uint8_t *ptr;
458: BIGNUM *x = NULL, *y = NULL;
459: EC_POINT *q = NULL;
460: EC_GROUP *g = NULL;
461: int ret = -1;
462:
463: response->public_key = NULL;
464: response->public_key_len = 0;
465:
1.5 djm 466: if ((x = BN_new()) == NULL ||
467: (y = BN_new()) == NULL ||
1.1 djm 468: (g = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)) == NULL ||
469: (q = EC_POINT_new(g)) == NULL) {
470: skdebug(__func__, "libcrypto setup failed");
471: goto out;
472: }
473: if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) {
474: skdebug(__func__, "fido_cred_pubkey_ptr failed");
475: goto out;
476: }
477: if (fido_cred_pubkey_len(cred) != 64) {
478: skdebug(__func__, "bad fido_cred_pubkey_len %zu",
479: fido_cred_pubkey_len(cred));
480: goto out;
481: }
482:
483: if (BN_bin2bn(ptr, 32, x) == NULL ||
484: BN_bin2bn(ptr + 32, 32, y) == NULL) {
485: skdebug(__func__, "BN_bin2bn failed");
486: goto out;
487: }
1.5 djm 488: if (EC_POINT_set_affine_coordinates_GFp(g, q, x, y, NULL) != 1) {
1.1 djm 489: skdebug(__func__, "EC_POINT_set_affine_coordinates_GFp failed");
490: goto out;
491: }
492: response->public_key_len = EC_POINT_point2oct(g, q,
1.5 djm 493: POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL);
1.1 djm 494: if (response->public_key_len == 0 || response->public_key_len > 2048) {
495: skdebug(__func__, "bad pubkey length %zu",
496: response->public_key_len);
497: goto out;
498: }
499: if ((response->public_key = malloc(response->public_key_len)) == NULL) {
500: skdebug(__func__, "malloc pubkey failed");
501: goto out;
502: }
503: if (EC_POINT_point2oct(g, q, POINT_CONVERSION_UNCOMPRESSED,
1.5 djm 504: response->public_key, response->public_key_len, NULL) == 0) {
1.1 djm 505: skdebug(__func__, "EC_POINT_point2oct failed");
506: goto out;
507: }
508: /* success */
509: ret = 0;
510: out:
511: if (ret != 0 && response->public_key != NULL) {
512: memset(response->public_key, 0, response->public_key_len);
513: free(response->public_key);
514: response->public_key = NULL;
515: }
516: EC_POINT_free(q);
517: EC_GROUP_free(g);
1.5 djm 518: BN_clear_free(x);
519: BN_clear_free(y);
1.1 djm 520: return ret;
521: }
1.7 naddy 522: #endif /* WITH_OPENSSL */
1.1 djm 523:
524: static int
1.9 djm 525: pack_public_key_ed25519(const fido_cred_t *cred,
526: struct sk_enroll_response *response)
1.1 djm 527: {
528: const uint8_t *ptr;
529: size_t len;
530: int ret = -1;
531:
532: response->public_key = NULL;
533: response->public_key_len = 0;
534:
535: if ((len = fido_cred_pubkey_len(cred)) != 32) {
536: skdebug(__func__, "bad fido_cred_pubkey_len len %zu", len);
537: goto out;
538: }
539: if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) {
540: skdebug(__func__, "fido_cred_pubkey_ptr failed");
541: goto out;
542: }
543: response->public_key_len = len;
544: if ((response->public_key = malloc(response->public_key_len)) == NULL) {
545: skdebug(__func__, "malloc pubkey failed");
546: goto out;
547: }
548: memcpy(response->public_key, ptr, len);
549: ret = 0;
550: out:
551: if (ret != 0)
552: free(response->public_key);
553: return ret;
554: }
555:
556: static int
1.12 djm 557: pack_public_key(uint32_t alg, const fido_cred_t *cred,
1.9 djm 558: struct sk_enroll_response *response)
1.1 djm 559: {
560: switch(alg) {
1.7 naddy 561: #ifdef WITH_OPENSSL
1.15 djm 562: case SSH_SK_ECDSA:
1.1 djm 563: return pack_public_key_ecdsa(cred, response);
1.7 naddy 564: #endif /* WITH_OPENSSL */
1.15 djm 565: case SSH_SK_ED25519:
1.1 djm 566: return pack_public_key_ed25519(cred, response);
567: default:
568: return -1;
569: }
570: }
571:
1.11 djm 572: static int
573: fidoerr_to_skerr(int fidoerr)
574: {
575: switch (fidoerr) {
576: case FIDO_ERR_UNSUPPORTED_OPTION:
1.15 djm 577: case FIDO_ERR_UNSUPPORTED_ALGORITHM:
1.11 djm 578: return SSH_SK_ERR_UNSUPPORTED;
579: case FIDO_ERR_PIN_REQUIRED:
580: case FIDO_ERR_PIN_INVALID:
581: return SSH_SK_ERR_PIN_REQUIRED;
582: default:
583: return -1;
584: }
585: }
586:
1.12 djm 587: static int
588: check_enroll_options(struct sk_option **options, char **devicep,
589: uint8_t *user_id, size_t user_id_len)
590: {
591: size_t i;
592:
593: if (options == NULL)
594: return 0;
595: for (i = 0; options[i] != NULL; i++) {
596: if (strcmp(options[i]->name, "device") == 0) {
597: if ((*devicep = strdup(options[i]->value)) == NULL) {
598: skdebug(__func__, "strdup device failed");
599: return -1;
600: }
601: skdebug(__func__, "requested device %s", *devicep);
1.14 djm 602: } else if (strcmp(options[i]->name, "user") == 0) {
1.12 djm 603: if (strlcpy(user_id, options[i]->value, user_id_len) >=
604: user_id_len) {
605: skdebug(__func__, "user too long");
606: return -1;
607: }
608: skdebug(__func__, "requested user %s",
609: (char *)user_id);
610: } else {
611: skdebug(__func__, "requested unsupported option %s",
612: options[i]->name);
613: if (options[i]->required) {
614: skdebug(__func__, "unknown required option");
615: return -1;
616: }
617: }
618: }
619: return 0;
620: }
621:
1.1 djm 622: int
1.12 djm 623: sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len,
1.10 djm 624: const char *application, uint8_t flags, const char *pin,
1.12 djm 625: struct sk_option **options, struct sk_enroll_response **enroll_response)
1.1 djm 626: {
627: fido_cred_t *cred = NULL;
628: const uint8_t *ptr;
629: uint8_t user_id[32];
1.22 ! djm 630: struct sk_usbhid *sk = NULL;
1.1 djm 631: struct sk_enroll_response *response = NULL;
632: size_t len;
1.19 djm 633: int credprot;
1.1 djm 634: int cose_alg;
1.11 djm 635: int ret = SSH_SK_ERR_GENERAL;
1.1 djm 636: int r;
637: char *device = NULL;
638:
1.18 djm 639: fido_init(SSH_FIDO_INIT_ARG);
640:
1.6 markus 641: if (enroll_response == NULL) {
642: skdebug(__func__, "enroll_response == NULL");
1.1 djm 643: goto out;
644: }
1.22 ! djm 645: *enroll_response = NULL;
1.12 djm 646: memset(user_id, 0, sizeof(user_id));
1.22 ! djm 647: if (check_enroll_options(options, &device, user_id,
! 648: sizeof(user_id)) != 0)
1.12 djm 649: goto out; /* error already logged */
650:
1.1 djm 651: switch(alg) {
1.7 naddy 652: #ifdef WITH_OPENSSL
1.15 djm 653: case SSH_SK_ECDSA:
1.1 djm 654: cose_alg = COSE_ES256;
655: break;
1.7 naddy 656: #endif /* WITH_OPENSSL */
1.15 djm 657: case SSH_SK_ED25519:
1.1 djm 658: cose_alg = COSE_EDDSA;
659: break;
660: default:
661: skdebug(__func__, "unsupported key type %d", alg);
662: goto out;
663: }
1.22 ! djm 664: if (device != NULL)
! 665: sk = sk_open(device);
! 666: else
! 667: sk = sk_probe(NULL, NULL, 0);
! 668: if (sk == NULL) {
! 669: skdebug(__func__, "failed to find sk");
1.1 djm 670: goto out;
671: }
1.22 ! djm 672: skdebug(__func__, "using device %s", sk->path);
1.1 djm 673: if ((cred = fido_cred_new()) == NULL) {
674: skdebug(__func__, "fido_cred_new failed");
675: goto out;
676: }
677: if ((r = fido_cred_set_type(cred, cose_alg)) != FIDO_OK) {
678: skdebug(__func__, "fido_cred_set_type: %s", fido_strerr(r));
679: goto out;
680: }
681: if ((r = fido_cred_set_clientdata_hash(cred, challenge,
682: challenge_len)) != FIDO_OK) {
683: skdebug(__func__, "fido_cred_set_clientdata_hash: %s",
684: fido_strerr(r));
1.8 djm 685: goto out;
686: }
1.15 djm 687: if ((r = fido_cred_set_rk(cred, (flags & SSH_SK_RESIDENT_KEY) != 0 ?
1.8 djm 688: FIDO_OPT_TRUE : FIDO_OPT_OMIT)) != FIDO_OK) {
689: skdebug(__func__, "fido_cred_set_rk: %s", fido_strerr(r));
1.1 djm 690: goto out;
691: }
692: if ((r = fido_cred_set_user(cred, user_id, sizeof(user_id),
693: "openssh", "openssh", NULL)) != FIDO_OK) {
694: skdebug(__func__, "fido_cred_set_user: %s", fido_strerr(r));
695: goto out;
696: }
697: if ((r = fido_cred_set_rp(cred, application, NULL)) != FIDO_OK) {
698: skdebug(__func__, "fido_cred_set_rp: %s", fido_strerr(r));
699: goto out;
700: }
1.21 djm 701: if ((flags & (SSH_SK_RESIDENT_KEY|SSH_SK_USER_VERIFICATION_REQD)) != 0) {
1.22 ! djm 702: if (!fido_dev_supports_cred_prot(sk->dev)) {
! 703: skdebug(__func__, "%s does not support credprot, "
! 704: "refusing to create unprotected "
! 705: "resident/verify-required key", sk->path);
1.19 djm 706: ret = SSH_SK_ERR_UNSUPPORTED;
707: goto out;
708: }
1.21 djm 709: if ((flags & SSH_SK_USER_VERIFICATION_REQD))
710: credprot = FIDO_CRED_PROT_UV_REQUIRED;
711: else
712: credprot = FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID;
713:
714: if ((r = fido_cred_set_prot(cred, credprot)) != FIDO_OK) {
1.19 djm 715: skdebug(__func__, "fido_cred_set_prot: %s",
716: fido_strerr(r));
717: ret = fidoerr_to_skerr(r);
718: goto out;
719: }
1.1 djm 720: }
1.22 ! djm 721: if ((r = fido_dev_make_cred(sk->dev, cred, pin)) != FIDO_OK) {
1.1 djm 722: skdebug(__func__, "fido_dev_make_cred: %s", fido_strerr(r));
1.11 djm 723: ret = fidoerr_to_skerr(r);
1.1 djm 724: goto out;
725: }
726: if (fido_cred_x5c_ptr(cred) != NULL) {
727: if ((r = fido_cred_verify(cred)) != FIDO_OK) {
728: skdebug(__func__, "fido_cred_verify: %s",
729: fido_strerr(r));
730: goto out;
731: }
732: } else {
733: skdebug(__func__, "self-attested credential");
734: if ((r = fido_cred_verify_self(cred)) != FIDO_OK) {
735: skdebug(__func__, "fido_cred_verify_self: %s",
736: fido_strerr(r));
737: goto out;
738: }
739: }
740: if ((response = calloc(1, sizeof(*response))) == NULL) {
741: skdebug(__func__, "calloc response failed");
742: goto out;
743: }
744: if (pack_public_key(alg, cred, response) != 0) {
745: skdebug(__func__, "pack_public_key failed");
746: goto out;
747: }
748: if ((ptr = fido_cred_id_ptr(cred)) != NULL) {
749: len = fido_cred_id_len(cred);
750: if ((response->key_handle = calloc(1, len)) == NULL) {
751: skdebug(__func__, "calloc key handle failed");
752: goto out;
753: }
754: memcpy(response->key_handle, ptr, len);
755: response->key_handle_len = len;
756: }
757: if ((ptr = fido_cred_sig_ptr(cred)) != NULL) {
758: len = fido_cred_sig_len(cred);
759: if ((response->signature = calloc(1, len)) == NULL) {
760: skdebug(__func__, "calloc signature failed");
761: goto out;
762: }
763: memcpy(response->signature, ptr, len);
764: response->signature_len = len;
765: }
766: if ((ptr = fido_cred_x5c_ptr(cred)) != NULL) {
767: len = fido_cred_x5c_len(cred);
1.16 djm 768: debug3("%s: attestation cert len=%zu", __func__, len);
1.1 djm 769: if ((response->attestation_cert = calloc(1, len)) == NULL) {
770: skdebug(__func__, "calloc attestation cert failed");
771: goto out;
772: }
773: memcpy(response->attestation_cert, ptr, len);
774: response->attestation_cert_len = len;
775: }
1.6 markus 776: *enroll_response = response;
1.1 djm 777: response = NULL;
778: ret = 0;
779: out:
780: free(device);
781: if (response != NULL) {
782: free(response->public_key);
783: free(response->key_handle);
784: free(response->signature);
785: free(response->attestation_cert);
786: free(response);
787: }
1.22 ! djm 788: sk_close(sk);
! 789: fido_cred_free(&cred);
1.1 djm 790: return ret;
791: }
792:
1.7 naddy 793: #ifdef WITH_OPENSSL
1.1 djm 794: static int
795: pack_sig_ecdsa(fido_assert_t *assert, struct sk_sign_response *response)
796: {
797: ECDSA_SIG *sig = NULL;
798: const BIGNUM *sig_r, *sig_s;
799: const unsigned char *cp;
800: size_t sig_len;
801: int ret = -1;
802:
803: cp = fido_assert_sig_ptr(assert, 0);
804: sig_len = fido_assert_sig_len(assert, 0);
805: if ((sig = d2i_ECDSA_SIG(NULL, &cp, sig_len)) == NULL) {
806: skdebug(__func__, "d2i_ECDSA_SIG failed");
807: goto out;
808: }
809: ECDSA_SIG_get0(sig, &sig_r, &sig_s);
810: response->sig_r_len = BN_num_bytes(sig_r);
811: response->sig_s_len = BN_num_bytes(sig_s);
812: if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL ||
813: (response->sig_s = calloc(1, response->sig_s_len)) == NULL) {
814: skdebug(__func__, "calloc signature failed");
815: goto out;
816: }
817: BN_bn2bin(sig_r, response->sig_r);
818: BN_bn2bin(sig_s, response->sig_s);
819: ret = 0;
820: out:
821: ECDSA_SIG_free(sig);
822: if (ret != 0) {
823: free(response->sig_r);
824: free(response->sig_s);
825: response->sig_r = NULL;
826: response->sig_s = NULL;
827: }
828: return ret;
829: }
1.7 naddy 830: #endif /* WITH_OPENSSL */
1.1 djm 831:
832: static int
833: pack_sig_ed25519(fido_assert_t *assert, struct sk_sign_response *response)
834: {
835: const unsigned char *ptr;
836: size_t len;
837: int ret = -1;
838:
839: ptr = fido_assert_sig_ptr(assert, 0);
840: len = fido_assert_sig_len(assert, 0);
841: if (len != 64) {
842: skdebug(__func__, "bad length %zu", len);
843: goto out;
844: }
845: response->sig_r_len = len;
846: if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL) {
847: skdebug(__func__, "calloc signature failed");
848: goto out;
849: }
850: memcpy(response->sig_r, ptr, len);
851: ret = 0;
852: out:
853: if (ret != 0) {
854: free(response->sig_r);
855: response->sig_r = NULL;
856: }
857: return ret;
858: }
859:
860: static int
1.12 djm 861: pack_sig(uint32_t alg, fido_assert_t *assert,
862: struct sk_sign_response *response)
1.1 djm 863: {
864: switch(alg) {
1.7 naddy 865: #ifdef WITH_OPENSSL
1.15 djm 866: case SSH_SK_ECDSA:
1.1 djm 867: return pack_sig_ecdsa(assert, response);
1.7 naddy 868: #endif /* WITH_OPENSSL */
1.15 djm 869: case SSH_SK_ED25519:
1.1 djm 870: return pack_sig_ed25519(assert, response);
871: default:
872: return -1;
873: }
874: }
875:
1.12 djm 876: /* Checks sk_options for sk_sign() and sk_load_resident_keys() */
877: static int
878: check_sign_load_resident_options(struct sk_option **options, char **devicep)
879: {
880: size_t i;
881:
882: if (options == NULL)
883: return 0;
884: for (i = 0; options[i] != NULL; i++) {
885: if (strcmp(options[i]->name, "device") == 0) {
886: if ((*devicep = strdup(options[i]->value)) == NULL) {
887: skdebug(__func__, "strdup device failed");
888: return -1;
889: }
890: skdebug(__func__, "requested device %s", *devicep);
891: } else {
892: skdebug(__func__, "requested unsupported option %s",
893: options[i]->name);
894: if (options[i]->required) {
895: skdebug(__func__, "unknown required option");
896: return -1;
897: }
898: }
899: }
900: return 0;
901: }
902:
1.1 djm 903: int
1.17 djm 904: sk_sign(uint32_t alg, const uint8_t *data, size_t datalen,
1.1 djm 905: const char *application,
906: const uint8_t *key_handle, size_t key_handle_len,
1.12 djm 907: uint8_t flags, const char *pin, struct sk_option **options,
908: struct sk_sign_response **sign_response)
1.1 djm 909: {
910: fido_assert_t *assert = NULL;
1.12 djm 911: char *device = NULL;
1.22 ! djm 912: struct sk_usbhid *sk = NULL;
1.1 djm 913: struct sk_sign_response *response = NULL;
1.17 djm 914: uint8_t message[32];
1.11 djm 915: int ret = SSH_SK_ERR_GENERAL;
1.1 djm 916: int r;
917:
1.18 djm 918: fido_init(SSH_FIDO_INIT_ARG);
1.1 djm 919:
920: if (sign_response == NULL) {
921: skdebug(__func__, "sign_response == NULL");
922: goto out;
923: }
924: *sign_response = NULL;
1.12 djm 925: if (check_sign_load_resident_options(options, &device) != 0)
926: goto out; /* error already logged */
1.17 djm 927: /* hash data to be signed before it goes to the security key */
928: if ((r = sha256_mem(data, datalen, message, sizeof(message))) != 0) {
929: skdebug(__func__, "hash message failed");
930: goto out;
931: }
1.22 ! djm 932: if (device != NULL)
! 933: sk = sk_open(device);
! 934: else if (pin != NULL || (flags & SSH_SK_USER_VERIFICATION_REQD))
! 935: sk = sk_probe(NULL, NULL, 0);
! 936: else
! 937: sk = sk_probe(application, key_handle, key_handle_len);
! 938: if (sk == NULL) {
! 939: skdebug(__func__, "failed to find sk");
1.1 djm 940: goto out;
941: }
942: if ((assert = fido_assert_new()) == NULL) {
943: skdebug(__func__, "fido_assert_new failed");
944: goto out;
945: }
946: if ((r = fido_assert_set_clientdata_hash(assert, message,
1.17 djm 947: sizeof(message))) != FIDO_OK) {
1.1 djm 948: skdebug(__func__, "fido_assert_set_clientdata_hash: %s",
949: fido_strerr(r));
950: goto out;
951: }
952: if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) {
953: skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r));
954: goto out;
955: }
956: if ((r = fido_assert_allow_cred(assert, key_handle,
957: key_handle_len)) != FIDO_OK) {
958: skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r));
959: goto out;
960: }
961: if ((r = fido_assert_set_up(assert,
1.15 djm 962: (flags & SSH_SK_USER_PRESENCE_REQD) ?
1.1 djm 963: FIDO_OPT_TRUE : FIDO_OPT_FALSE)) != FIDO_OK) {
964: skdebug(__func__, "fido_assert_set_up: %s", fido_strerr(r));
965: goto out;
966: }
1.21 djm 967: if (pin == NULL && (flags & SSH_SK_USER_VERIFICATION_REQD) &&
968: (r = fido_assert_set_uv(assert, FIDO_OPT_TRUE)) != FIDO_OK) {
969: skdebug(__func__, "fido_assert_set_uv: %s", fido_strerr(r));
970: ret = FIDO_ERR_PIN_REQUIRED;
971: goto out;
972: }
1.22 ! djm 973: if ((r = fido_dev_get_assert(sk->dev, assert, pin)) != FIDO_OK) {
1.1 djm 974: skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r));
1.21 djm 975: ret = fidoerr_to_skerr(r);
1.1 djm 976: goto out;
977: }
978: if ((response = calloc(1, sizeof(*response))) == NULL) {
979: skdebug(__func__, "calloc response failed");
980: goto out;
981: }
982: response->flags = fido_assert_flags(assert, 0);
983: response->counter = fido_assert_sigcount(assert, 0);
984: if (pack_sig(alg, assert, response) != 0) {
985: skdebug(__func__, "pack_sig failed");
986: goto out;
987: }
988: *sign_response = response;
989: response = NULL;
990: ret = 0;
991: out:
1.17 djm 992: explicit_bzero(message, sizeof(message));
1.12 djm 993: free(device);
1.1 djm 994: if (response != NULL) {
995: free(response->sig_r);
996: free(response->sig_s);
997: free(response);
998: }
1.22 ! djm 999: sk_close(sk);
! 1000: fido_assert_free(&assert);
1.1 djm 1001: return ret;
1002: }
1.9 djm 1003:
1004: static int
1.22 ! djm 1005: read_rks(struct sk_usbhid *sk, const char *pin,
1.9 djm 1006: struct sk_resident_key ***rksp, size_t *nrksp)
1007: {
1.11 djm 1008: int ret = SSH_SK_ERR_GENERAL, r = -1;
1.9 djm 1009: fido_credman_metadata_t *metadata = NULL;
1010: fido_credman_rp_t *rp = NULL;
1011: fido_credman_rk_t *rk = NULL;
1012: size_t i, j, nrp, nrk;
1013: const fido_cred_t *cred;
1014: struct sk_resident_key *srk = NULL, **tmp;
1015:
1.22 ! djm 1016: if (pin == NULL) {
! 1017: skdebug(__func__, "no PIN specified");
! 1018: ret = SSH_SK_ERR_PIN_REQUIRED;
! 1019: goto out;
1.9 djm 1020: }
1021: if ((metadata = fido_credman_metadata_new()) == NULL) {
1022: skdebug(__func__, "alloc failed");
1023: goto out;
1024: }
1025:
1.22 ! djm 1026: if ((r = fido_credman_get_dev_metadata(sk->dev, metadata, pin)) != 0) {
1.9 djm 1027: if (r == FIDO_ERR_INVALID_COMMAND) {
1028: skdebug(__func__, "device %s does not support "
1.22 ! djm 1029: "resident keys", sk->path);
1.11 djm 1030: ret = 0;
1.9 djm 1031: goto out;
1032: }
1033: skdebug(__func__, "get metadata for %s failed: %s",
1.22 ! djm 1034: sk->path, fido_strerr(r));
1.12 djm 1035: ret = fidoerr_to_skerr(r);
1.9 djm 1036: goto out;
1037: }
1038: skdebug(__func__, "existing %llu, remaining %llu",
1039: (unsigned long long)fido_credman_rk_existing(metadata),
1040: (unsigned long long)fido_credman_rk_remaining(metadata));
1041: if ((rp = fido_credman_rp_new()) == NULL) {
1042: skdebug(__func__, "alloc rp failed");
1043: goto out;
1044: }
1.22 ! djm 1045: if ((r = fido_credman_get_dev_rp(sk->dev, rp, pin)) != 0) {
1.9 djm 1046: skdebug(__func__, "get RPs for %s failed: %s",
1.22 ! djm 1047: sk->path, fido_strerr(r));
1.9 djm 1048: goto out;
1049: }
1050: nrp = fido_credman_rp_count(rp);
1051: skdebug(__func__, "Device %s has resident keys for %zu RPs",
1.22 ! djm 1052: sk->path, nrp);
1.9 djm 1053:
1054: /* Iterate over RP IDs that have resident keys */
1055: for (i = 0; i < nrp; i++) {
1056: skdebug(__func__, "rp %zu: name=\"%s\" id=\"%s\" hashlen=%zu",
1057: i, fido_credman_rp_name(rp, i), fido_credman_rp_id(rp, i),
1058: fido_credman_rp_id_hash_len(rp, i));
1059:
1060: /* Skip non-SSH RP IDs */
1061: if (strncasecmp(fido_credman_rp_id(rp, i), "ssh:", 4) != 0)
1062: continue;
1063:
1064: fido_credman_rk_free(&rk);
1065: if ((rk = fido_credman_rk_new()) == NULL) {
1066: skdebug(__func__, "alloc rk failed");
1067: goto out;
1068: }
1.22 ! djm 1069: if ((r = fido_credman_get_dev_rk(sk->dev,
! 1070: fido_credman_rp_id(rp, i), rk, pin)) != 0) {
1.9 djm 1071: skdebug(__func__, "get RKs for %s slot %zu failed: %s",
1.22 ! djm 1072: sk->path, i, fido_strerr(r));
1.9 djm 1073: goto out;
1074: }
1075: nrk = fido_credman_rk_count(rk);
1076: skdebug(__func__, "RP \"%s\" has %zu resident keys",
1077: fido_credman_rp_id(rp, i), nrk);
1078:
1079: /* Iterate over resident keys for this RP ID */
1080: for (j = 0; j < nrk; j++) {
1081: if ((cred = fido_credman_rk(rk, j)) == NULL) {
1082: skdebug(__func__, "no RK in slot %zu", j);
1083: continue;
1084: }
1085: skdebug(__func__, "Device %s RP \"%s\" slot %zu: "
1.22 ! djm 1086: "type %d flags 0x%02x prot 0x%02x", sk->path,
1.21 djm 1087: fido_credman_rp_id(rp, i), j, fido_cred_type(cred),
1088: fido_cred_flags(cred), fido_cred_prot(cred));
1.9 djm 1089:
1090: /* build response entry */
1091: if ((srk = calloc(1, sizeof(*srk))) == NULL ||
1092: (srk->key.key_handle = calloc(1,
1093: fido_cred_id_len(cred))) == NULL ||
1094: (srk->application = strdup(fido_credman_rp_id(rp,
1095: i))) == NULL) {
1096: skdebug(__func__, "alloc sk_resident_key");
1097: goto out;
1098: }
1099:
1100: srk->key.key_handle_len = fido_cred_id_len(cred);
1101: memcpy(srk->key.key_handle,
1102: fido_cred_id_ptr(cred),
1103: srk->key.key_handle_len);
1104:
1105: switch (fido_cred_type(cred)) {
1106: case COSE_ES256:
1.15 djm 1107: srk->alg = SSH_SK_ECDSA;
1.9 djm 1108: break;
1109: case COSE_EDDSA:
1.15 djm 1110: srk->alg = SSH_SK_ED25519;
1.9 djm 1111: break;
1112: default:
1113: skdebug(__func__, "unsupported key type %d",
1114: fido_cred_type(cred));
1.15 djm 1115: goto out; /* XXX free rk and continue */
1.9 djm 1116: }
1117:
1118: if ((r = pack_public_key(srk->alg, cred,
1119: &srk->key)) != 0) {
1120: skdebug(__func__, "pack public key failed");
1121: goto out;
1122: }
1123: /* append */
1124: if ((tmp = recallocarray(*rksp, *nrksp, (*nrksp) + 1,
1125: sizeof(**rksp))) == NULL) {
1126: skdebug(__func__, "alloc rksp");
1127: goto out;
1128: }
1129: *rksp = tmp;
1130: (*rksp)[(*nrksp)++] = srk;
1131: srk = NULL;
1132: }
1133: }
1134: /* Success */
1.11 djm 1135: ret = 0;
1.9 djm 1136: out:
1137: if (srk != NULL) {
1138: free(srk->application);
1139: freezero(srk->key.public_key, srk->key.public_key_len);
1140: freezero(srk->key.key_handle, srk->key.key_handle_len);
1141: freezero(srk, sizeof(*srk));
1142: }
1143: fido_credman_rp_free(&rp);
1144: fido_credman_rk_free(&rk);
1145: fido_credman_metadata_free(&metadata);
1.11 djm 1146: return ret;
1.9 djm 1147: }
1148:
1149: int
1.12 djm 1150: sk_load_resident_keys(const char *pin, struct sk_option **options,
1.9 djm 1151: struct sk_resident_key ***rksp, size_t *nrksp)
1152: {
1.11 djm 1153: int ret = SSH_SK_ERR_GENERAL, r = -1;
1.22 ! djm 1154: size_t i, nrks = 0;
1.9 djm 1155: struct sk_resident_key **rks = NULL;
1.22 ! djm 1156: struct sk_usbhid *sk = NULL;
1.12 djm 1157: char *device = NULL;
1.22 ! djm 1158:
1.9 djm 1159: *rksp = NULL;
1160: *nrksp = 0;
1.18 djm 1161:
1162: fido_init(SSH_FIDO_INIT_ARG);
1.9 djm 1163:
1.12 djm 1164: if (check_sign_load_resident_options(options, &device) != 0)
1165: goto out; /* error already logged */
1.22 ! djm 1166: if (device != NULL)
! 1167: sk = sk_open(device);
! 1168: else
! 1169: sk = sk_probe(NULL, NULL, 0);
! 1170: if (sk == NULL) {
! 1171: skdebug(__func__, "failed to find sk");
! 1172: goto out;
! 1173: }
! 1174: skdebug(__func__, "trying %s", sk->path);
! 1175: if ((r = read_rks(sk, pin, &rks, &nrks)) != 0) {
! 1176: skdebug(__func__, "read_rks failed for %s", sk->path);
! 1177: ret = r;
! 1178: goto out;
1.9 djm 1179: }
1.12 djm 1180: /* success, unless we have no keys but a specific error */
1181: if (nrks > 0 || ret == SSH_SK_ERR_GENERAL)
1182: ret = 0;
1.9 djm 1183: *rksp = rks;
1184: *nrksp = nrks;
1185: rks = NULL;
1186: nrks = 0;
1187: out:
1.22 ! djm 1188: sk_close(sk);
1.9 djm 1189: for (i = 0; i < nrks; i++) {
1190: free(rks[i]->application);
1191: freezero(rks[i]->key.public_key, rks[i]->key.public_key_len);
1192: freezero(rks[i]->key.key_handle, rks[i]->key.key_handle_len);
1193: freezero(rks[i], sizeof(*rks[i]));
1194: }
1195: free(rks);
1.11 djm 1196: return ret;
1.9 djm 1197: }
1198: