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