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