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