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