Annotation of src/usr.bin/ssh/sk-usbhid.c, Revision 1.33
1.33 ! djm 1: /* $OpenBSD: sk-usbhid.c,v 1.32 2021/10/28 02:54:18 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.22 djm 351: static struct sk_usbhid *
352: sk_select_by_cred(const fido_dev_info_t *devlist, size_t ndevs,
353: const char *application, const uint8_t *key_handle, size_t key_handle_len)
1.1 djm 354: {
1.22 djm 355: struct sk_usbhid **skv, *sk;
356: size_t skvcnt, i;
1.1 djm 357:
1.22 djm 358: if ((skv = sk_openv(devlist, ndevs, &skvcnt)) == NULL) {
359: skdebug(__func__, "sk_openv failed");
360: return NULL;
361: }
1.24 djm 362: if (skvcnt == 1) {
363: sk = skv[0];
364: skv[0] = NULL;
365: goto out;
366: }
1.22 djm 367: sk = NULL;
1.24 djm 368: for (i = 0; i < skvcnt; i++) {
1.22 djm 369: if (sk_try(skv[i], application, key_handle,
370: key_handle_len) == 0) {
371: sk = skv[i];
372: skv[i] = NULL;
373: skdebug(__func__, "found key in %s", sk->path);
374: break;
1.12 djm 375: }
1.24 djm 376: }
377: out:
1.22 djm 378: sk_closev(skv, skvcnt);
379: return sk;
380: }
381:
382: static struct sk_usbhid *
383: sk_select_by_touch(const fido_dev_info_t *devlist, size_t ndevs)
384: {
385: struct sk_usbhid **skv, *sk;
386: struct timeval tv_start, tv_now, tv_delta;
387: size_t skvcnt, idx;
388: int touch, ms_remain;
389:
390: if ((skv = sk_openv(devlist, ndevs, &skvcnt)) == NULL) {
391: skdebug(__func__, "sk_openv failed");
392: return NULL;
393: }
394: sk = NULL;
395: if (skvcnt < 2) {
396: if (skvcnt == 1) {
397: /* single candidate */
398: sk = skv[0];
399: skv[0] = NULL;
1.12 djm 400: }
1.22 djm 401: goto out;
1.12 djm 402: }
1.22 djm 403: if (sk_touch_begin(skv, skvcnt) == -1) {
404: skdebug(__func__, "sk_touch_begin failed");
405: goto out;
406: }
407: monotime_tv(&tv_start);
408: do {
409: if (sk_touch_poll(skv, skvcnt, &touch, &idx) == -1) {
410: skdebug(__func__, "sk_touch_poll failed");
411: goto out;
412: }
413: if (touch) {
414: sk = skv[idx];
415: skv[idx] = NULL;
416: goto out;
417: }
418: monotime_tv(&tv_now);
419: timersub(&tv_now, &tv_start, &tv_delta);
420: ms_remain = SELECT_MS - tv_delta.tv_sec * 1000 -
421: tv_delta.tv_usec / 1000;
422: } while (ms_remain >= FIDO_POLL_MS);
423: skdebug(__func__, "timeout");
424: out:
425: sk_closev(skv, skvcnt);
426: return sk;
427: }
428:
429: static struct sk_usbhid *
430: sk_probe(const char *application, const uint8_t *key_handle,
431: size_t key_handle_len)
432: {
433: struct sk_usbhid *sk;
434: fido_dev_info_t *devlist;
435: size_t ndevs;
436: int r;
1.12 djm 437:
1.1 djm 438: if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) {
439: skdebug(__func__, "fido_dev_info_new failed");
1.22 djm 440: return NULL;
1.1 djm 441: }
442: if ((r = fido_dev_info_manifest(devlist, MAX_FIDO_DEVICES,
1.22 djm 443: &ndevs)) != FIDO_OK) {
444: skdebug(__func__, "fido_dev_info_manifest failed: %s",
445: fido_strerr(r));
446: fido_dev_info_free(&devlist, MAX_FIDO_DEVICES);
447: return NULL;
1.1 djm 448: }
1.22 djm 449: skdebug(__func__, "%zu device(s) detected", ndevs);
450: if (ndevs == 0) {
451: sk = NULL;
452: } else if (application != NULL && key_handle != NULL) {
453: skdebug(__func__, "selecting sk by cred");
454: sk = sk_select_by_cred(devlist, ndevs, application, key_handle,
455: key_handle_len);
456: } else {
457: skdebug(__func__, "selecting sk by touch");
458: sk = sk_select_by_touch(devlist, ndevs);
1.1 djm 459: }
1.22 djm 460: fido_dev_info_free(&devlist, MAX_FIDO_DEVICES);
461: return sk;
1.1 djm 462: }
463:
1.33 ! djm 464: static int
! 465: check_sk_options(fido_dev_t *dev, const char *opt, int *ret)
! 466: {
! 467: fido_cbor_info_t *info;
! 468: char * const *name;
! 469: const bool *value;
! 470: size_t len;
! 471: int r;
! 472:
! 473: *ret = -1;
! 474:
! 475: if (!fido_dev_is_fido2(dev)) {
! 476: skdebug(__func__, "device is not fido2");
! 477: return 0;
! 478: }
! 479: if ((info = fido_cbor_info_new()) == NULL) {
! 480: skdebug(__func__, "fido_cbor_info_new failed");
! 481: return -1;
! 482: }
! 483: if ((r = fido_dev_get_cbor_info(dev, info)) != FIDO_OK) {
! 484: skdebug(__func__, "fido_dev_get_cbor_info: %s", fido_strerr(r));
! 485: fido_cbor_info_free(&info);
! 486: return -1;
! 487: }
! 488: name = fido_cbor_info_options_name_ptr(info);
! 489: value = fido_cbor_info_options_value_ptr(info);
! 490: len = fido_cbor_info_options_len(info);
! 491: for (size_t i = 0; i < len; i++) {
! 492: if (!strcmp(name[i], opt)) {
! 493: *ret = value[i];
! 494: break;
! 495: }
! 496: }
! 497: fido_cbor_info_free(&info);
! 498: if (*ret == -1)
! 499: skdebug(__func__, "option %s is unknown", opt);
! 500: else
! 501: skdebug(__func__, "option %s is %s", opt, *ret ? "on" : "off");
! 502:
! 503: return 0;
! 504: }
! 505:
1.7 naddy 506: #ifdef WITH_OPENSSL
1.1 djm 507: /*
508: * The key returned via fido_cred_pubkey_ptr() is in affine coordinates,
509: * but the API expects a SEC1 octet string.
510: */
511: static int
1.9 djm 512: pack_public_key_ecdsa(const fido_cred_t *cred,
513: struct sk_enroll_response *response)
1.1 djm 514: {
515: const uint8_t *ptr;
516: BIGNUM *x = NULL, *y = NULL;
517: EC_POINT *q = NULL;
518: EC_GROUP *g = NULL;
519: int ret = -1;
520:
521: response->public_key = NULL;
522: response->public_key_len = 0;
523:
1.5 djm 524: if ((x = BN_new()) == NULL ||
525: (y = BN_new()) == NULL ||
1.1 djm 526: (g = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)) == NULL ||
527: (q = EC_POINT_new(g)) == NULL) {
528: skdebug(__func__, "libcrypto setup failed");
529: goto out;
530: }
531: if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) {
532: skdebug(__func__, "fido_cred_pubkey_ptr failed");
533: goto out;
534: }
535: if (fido_cred_pubkey_len(cred) != 64) {
536: skdebug(__func__, "bad fido_cred_pubkey_len %zu",
537: fido_cred_pubkey_len(cred));
538: goto out;
539: }
540:
541: if (BN_bin2bn(ptr, 32, x) == NULL ||
542: BN_bin2bn(ptr + 32, 32, y) == NULL) {
543: skdebug(__func__, "BN_bin2bn failed");
544: goto out;
545: }
1.5 djm 546: if (EC_POINT_set_affine_coordinates_GFp(g, q, x, y, NULL) != 1) {
1.1 djm 547: skdebug(__func__, "EC_POINT_set_affine_coordinates_GFp failed");
548: goto out;
549: }
550: response->public_key_len = EC_POINT_point2oct(g, q,
1.5 djm 551: POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL);
1.1 djm 552: if (response->public_key_len == 0 || response->public_key_len > 2048) {
553: skdebug(__func__, "bad pubkey length %zu",
554: response->public_key_len);
555: goto out;
556: }
557: if ((response->public_key = malloc(response->public_key_len)) == NULL) {
558: skdebug(__func__, "malloc pubkey failed");
559: goto out;
560: }
561: if (EC_POINT_point2oct(g, q, POINT_CONVERSION_UNCOMPRESSED,
1.5 djm 562: response->public_key, response->public_key_len, NULL) == 0) {
1.1 djm 563: skdebug(__func__, "EC_POINT_point2oct failed");
564: goto out;
565: }
566: /* success */
567: ret = 0;
568: out:
569: if (ret != 0 && response->public_key != NULL) {
570: memset(response->public_key, 0, response->public_key_len);
571: free(response->public_key);
572: response->public_key = NULL;
573: }
574: EC_POINT_free(q);
575: EC_GROUP_free(g);
1.5 djm 576: BN_clear_free(x);
577: BN_clear_free(y);
1.1 djm 578: return ret;
579: }
1.7 naddy 580: #endif /* WITH_OPENSSL */
1.1 djm 581:
582: static int
1.9 djm 583: pack_public_key_ed25519(const fido_cred_t *cred,
584: struct sk_enroll_response *response)
1.1 djm 585: {
586: const uint8_t *ptr;
587: size_t len;
588: int ret = -1;
589:
590: response->public_key = NULL;
591: response->public_key_len = 0;
592:
593: if ((len = fido_cred_pubkey_len(cred)) != 32) {
594: skdebug(__func__, "bad fido_cred_pubkey_len len %zu", len);
595: goto out;
596: }
597: if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) {
598: skdebug(__func__, "fido_cred_pubkey_ptr failed");
599: goto out;
600: }
601: response->public_key_len = len;
602: if ((response->public_key = malloc(response->public_key_len)) == NULL) {
603: skdebug(__func__, "malloc pubkey failed");
604: goto out;
605: }
606: memcpy(response->public_key, ptr, len);
607: ret = 0;
608: out:
609: if (ret != 0)
610: free(response->public_key);
611: return ret;
612: }
613:
614: static int
1.12 djm 615: pack_public_key(uint32_t alg, const fido_cred_t *cred,
1.9 djm 616: struct sk_enroll_response *response)
1.1 djm 617: {
618: switch(alg) {
1.7 naddy 619: #ifdef WITH_OPENSSL
1.15 djm 620: case SSH_SK_ECDSA:
1.1 djm 621: return pack_public_key_ecdsa(cred, response);
1.7 naddy 622: #endif /* WITH_OPENSSL */
1.15 djm 623: case SSH_SK_ED25519:
1.1 djm 624: return pack_public_key_ed25519(cred, response);
625: default:
626: return -1;
627: }
628: }
629:
1.11 djm 630: static int
631: fidoerr_to_skerr(int fidoerr)
632: {
633: switch (fidoerr) {
634: case FIDO_ERR_UNSUPPORTED_OPTION:
1.15 djm 635: case FIDO_ERR_UNSUPPORTED_ALGORITHM:
1.11 djm 636: return SSH_SK_ERR_UNSUPPORTED;
637: case FIDO_ERR_PIN_REQUIRED:
638: case FIDO_ERR_PIN_INVALID:
1.33 ! djm 639: case FIDO_ERR_OPERATION_DENIED:
1.11 djm 640: return SSH_SK_ERR_PIN_REQUIRED;
641: default:
642: return -1;
643: }
644: }
645:
1.12 djm 646: static int
647: check_enroll_options(struct sk_option **options, char **devicep,
648: uint8_t *user_id, size_t user_id_len)
649: {
650: size_t i;
651:
652: if (options == NULL)
653: return 0;
654: for (i = 0; options[i] != NULL; i++) {
655: if (strcmp(options[i]->name, "device") == 0) {
656: if ((*devicep = strdup(options[i]->value)) == NULL) {
657: skdebug(__func__, "strdup device failed");
658: return -1;
659: }
660: skdebug(__func__, "requested device %s", *devicep);
1.14 djm 661: } else if (strcmp(options[i]->name, "user") == 0) {
1.12 djm 662: if (strlcpy(user_id, options[i]->value, user_id_len) >=
663: user_id_len) {
664: skdebug(__func__, "user too long");
665: return -1;
666: }
667: skdebug(__func__, "requested user %s",
668: (char *)user_id);
669: } else {
670: skdebug(__func__, "requested unsupported option %s",
671: options[i]->name);
672: if (options[i]->required) {
673: skdebug(__func__, "unknown required option");
674: return -1;
675: }
676: }
677: }
678: return 0;
679: }
680:
1.1 djm 681: int
1.12 djm 682: sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len,
1.10 djm 683: const char *application, uint8_t flags, const char *pin,
1.12 djm 684: struct sk_option **options, struct sk_enroll_response **enroll_response)
1.1 djm 685: {
686: fido_cred_t *cred = NULL;
687: const uint8_t *ptr;
1.30 djm 688: uint8_t user_id[32], chall_hash[32];
1.22 djm 689: struct sk_usbhid *sk = NULL;
1.1 djm 690: struct sk_enroll_response *response = NULL;
691: size_t len;
1.19 djm 692: int credprot;
1.33 ! djm 693: int internal_uv;
1.1 djm 694: int cose_alg;
1.11 djm 695: int ret = SSH_SK_ERR_GENERAL;
1.1 djm 696: int r;
697: char *device = NULL;
698:
1.18 djm 699: fido_init(SSH_FIDO_INIT_ARG);
700:
1.6 markus 701: if (enroll_response == NULL) {
702: skdebug(__func__, "enroll_response == NULL");
1.1 djm 703: goto out;
704: }
1.22 djm 705: *enroll_response = NULL;
1.12 djm 706: memset(user_id, 0, sizeof(user_id));
1.22 djm 707: if (check_enroll_options(options, &device, user_id,
708: sizeof(user_id)) != 0)
1.12 djm 709: goto out; /* error already logged */
710:
1.1 djm 711: switch(alg) {
1.7 naddy 712: #ifdef WITH_OPENSSL
1.15 djm 713: case SSH_SK_ECDSA:
1.1 djm 714: cose_alg = COSE_ES256;
715: break;
1.7 naddy 716: #endif /* WITH_OPENSSL */
1.15 djm 717: case SSH_SK_ED25519:
1.1 djm 718: cose_alg = COSE_EDDSA;
719: break;
720: default:
721: skdebug(__func__, "unsupported key type %d", alg);
722: goto out;
723: }
1.22 djm 724: if (device != NULL)
725: sk = sk_open(device);
726: else
727: sk = sk_probe(NULL, NULL, 0);
728: if (sk == NULL) {
729: skdebug(__func__, "failed to find sk");
1.1 djm 730: goto out;
731: }
1.22 djm 732: skdebug(__func__, "using device %s", sk->path);
1.1 djm 733: if ((cred = fido_cred_new()) == NULL) {
734: skdebug(__func__, "fido_cred_new failed");
735: goto out;
736: }
737: if ((r = fido_cred_set_type(cred, cose_alg)) != FIDO_OK) {
738: skdebug(__func__, "fido_cred_set_type: %s", fido_strerr(r));
739: goto out;
740: }
1.30 djm 741: if (sha256_mem(challenge, challenge_len,
742: chall_hash, sizeof(chall_hash)) != 0) {
743: skdebug(__func__, "hash challenge failed");
744: goto out;
745: }
746: if ((r = fido_cred_set_clientdata_hash(cred, chall_hash,
747: sizeof(chall_hash))) != FIDO_OK) {
1.1 djm 748: skdebug(__func__, "fido_cred_set_clientdata_hash: %s",
749: fido_strerr(r));
1.8 djm 750: goto out;
751: }
1.15 djm 752: if ((r = fido_cred_set_rk(cred, (flags & SSH_SK_RESIDENT_KEY) != 0 ?
1.8 djm 753: FIDO_OPT_TRUE : FIDO_OPT_OMIT)) != FIDO_OK) {
754: skdebug(__func__, "fido_cred_set_rk: %s", fido_strerr(r));
1.1 djm 755: goto out;
756: }
757: if ((r = fido_cred_set_user(cred, user_id, sizeof(user_id),
758: "openssh", "openssh", NULL)) != FIDO_OK) {
759: skdebug(__func__, "fido_cred_set_user: %s", fido_strerr(r));
760: goto out;
761: }
762: if ((r = fido_cred_set_rp(cred, application, NULL)) != FIDO_OK) {
763: skdebug(__func__, "fido_cred_set_rp: %s", fido_strerr(r));
764: goto out;
765: }
1.21 djm 766: if ((flags & (SSH_SK_RESIDENT_KEY|SSH_SK_USER_VERIFICATION_REQD)) != 0) {
1.22 djm 767: if (!fido_dev_supports_cred_prot(sk->dev)) {
768: skdebug(__func__, "%s does not support credprot, "
769: "refusing to create unprotected "
770: "resident/verify-required key", sk->path);
1.19 djm 771: ret = SSH_SK_ERR_UNSUPPORTED;
772: goto out;
773: }
1.21 djm 774: if ((flags & SSH_SK_USER_VERIFICATION_REQD))
775: credprot = FIDO_CRED_PROT_UV_REQUIRED;
776: else
777: credprot = FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID;
778:
779: if ((r = fido_cred_set_prot(cred, credprot)) != FIDO_OK) {
1.19 djm 780: skdebug(__func__, "fido_cred_set_prot: %s",
781: fido_strerr(r));
782: ret = fidoerr_to_skerr(r);
783: goto out;
784: }
1.1 djm 785: }
1.22 djm 786: if ((r = fido_dev_make_cred(sk->dev, cred, pin)) != FIDO_OK) {
1.1 djm 787: skdebug(__func__, "fido_dev_make_cred: %s", fido_strerr(r));
1.11 djm 788: ret = fidoerr_to_skerr(r);
1.1 djm 789: goto out;
790: }
791: if (fido_cred_x5c_ptr(cred) != NULL) {
792: if ((r = fido_cred_verify(cred)) != FIDO_OK) {
793: skdebug(__func__, "fido_cred_verify: %s",
794: fido_strerr(r));
795: goto out;
796: }
797: } else {
798: skdebug(__func__, "self-attested credential");
799: if ((r = fido_cred_verify_self(cred)) != FIDO_OK) {
800: skdebug(__func__, "fido_cred_verify_self: %s",
801: fido_strerr(r));
802: goto out;
803: }
804: }
805: if ((response = calloc(1, sizeof(*response))) == NULL) {
806: skdebug(__func__, "calloc response failed");
807: goto out;
808: }
1.33 ! djm 809: response->flags = flags;
! 810: if ((flags & SSH_SK_USER_VERIFICATION_REQD)) {
! 811: if (check_sk_options(sk->dev, "uv", &internal_uv) == 0 &&
! 812: internal_uv != -1) {
! 813: /* user verification handled by token */
! 814: response->flags &= ~SSH_SK_USER_VERIFICATION_REQD;
! 815: }
! 816: }
1.1 djm 817: if (pack_public_key(alg, cred, response) != 0) {
818: skdebug(__func__, "pack_public_key failed");
819: goto out;
820: }
821: if ((ptr = fido_cred_id_ptr(cred)) != NULL) {
822: len = fido_cred_id_len(cred);
823: if ((response->key_handle = calloc(1, len)) == NULL) {
824: skdebug(__func__, "calloc key handle failed");
825: goto out;
826: }
827: memcpy(response->key_handle, ptr, len);
828: response->key_handle_len = len;
829: }
830: if ((ptr = fido_cred_sig_ptr(cred)) != NULL) {
831: len = fido_cred_sig_len(cred);
832: if ((response->signature = calloc(1, len)) == NULL) {
833: skdebug(__func__, "calloc signature failed");
834: goto out;
835: }
836: memcpy(response->signature, ptr, len);
837: response->signature_len = len;
838: }
839: if ((ptr = fido_cred_x5c_ptr(cred)) != NULL) {
840: len = fido_cred_x5c_len(cred);
1.28 djm 841: skdebug(__func__, "attestation cert len=%zu", len);
1.1 djm 842: if ((response->attestation_cert = calloc(1, len)) == NULL) {
843: skdebug(__func__, "calloc attestation cert failed");
844: goto out;
845: }
846: memcpy(response->attestation_cert, ptr, len);
847: response->attestation_cert_len = len;
848: }
1.26 djm 849: if ((ptr = fido_cred_authdata_ptr(cred)) != NULL) {
850: len = fido_cred_authdata_len(cred);
1.28 djm 851: skdebug(__func__, "authdata len=%zu", len);
1.26 djm 852: if ((response->authdata = calloc(1, len)) == NULL) {
853: skdebug(__func__, "calloc authdata failed");
854: goto out;
855: }
856: memcpy(response->authdata, ptr, len);
857: response->authdata_len = len;
858: }
1.6 markus 859: *enroll_response = response;
1.1 djm 860: response = NULL;
861: ret = 0;
862: out:
863: free(device);
864: if (response != NULL) {
865: free(response->public_key);
866: free(response->key_handle);
867: free(response->signature);
868: free(response->attestation_cert);
1.26 djm 869: free(response->authdata);
1.1 djm 870: free(response);
871: }
1.22 djm 872: sk_close(sk);
873: fido_cred_free(&cred);
1.1 djm 874: return ret;
875: }
876:
1.7 naddy 877: #ifdef WITH_OPENSSL
1.1 djm 878: static int
879: pack_sig_ecdsa(fido_assert_t *assert, struct sk_sign_response *response)
880: {
881: ECDSA_SIG *sig = NULL;
882: const BIGNUM *sig_r, *sig_s;
883: const unsigned char *cp;
884: size_t sig_len;
885: int ret = -1;
886:
887: cp = fido_assert_sig_ptr(assert, 0);
888: sig_len = fido_assert_sig_len(assert, 0);
889: if ((sig = d2i_ECDSA_SIG(NULL, &cp, sig_len)) == NULL) {
890: skdebug(__func__, "d2i_ECDSA_SIG failed");
891: goto out;
892: }
893: ECDSA_SIG_get0(sig, &sig_r, &sig_s);
894: response->sig_r_len = BN_num_bytes(sig_r);
895: response->sig_s_len = BN_num_bytes(sig_s);
896: if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL ||
897: (response->sig_s = calloc(1, response->sig_s_len)) == NULL) {
898: skdebug(__func__, "calloc signature failed");
899: goto out;
900: }
901: BN_bn2bin(sig_r, response->sig_r);
902: BN_bn2bin(sig_s, response->sig_s);
903: ret = 0;
904: out:
905: ECDSA_SIG_free(sig);
906: if (ret != 0) {
907: free(response->sig_r);
908: free(response->sig_s);
909: response->sig_r = NULL;
910: response->sig_s = NULL;
911: }
912: return ret;
913: }
1.7 naddy 914: #endif /* WITH_OPENSSL */
1.1 djm 915:
916: static int
917: pack_sig_ed25519(fido_assert_t *assert, struct sk_sign_response *response)
918: {
919: const unsigned char *ptr;
920: size_t len;
921: int ret = -1;
922:
923: ptr = fido_assert_sig_ptr(assert, 0);
924: len = fido_assert_sig_len(assert, 0);
925: if (len != 64) {
926: skdebug(__func__, "bad length %zu", len);
927: goto out;
928: }
929: response->sig_r_len = len;
930: if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL) {
931: skdebug(__func__, "calloc signature failed");
932: goto out;
933: }
934: memcpy(response->sig_r, ptr, len);
935: ret = 0;
936: out:
937: if (ret != 0) {
938: free(response->sig_r);
939: response->sig_r = NULL;
940: }
941: return ret;
942: }
943:
944: static int
1.12 djm 945: pack_sig(uint32_t alg, fido_assert_t *assert,
946: struct sk_sign_response *response)
1.1 djm 947: {
948: switch(alg) {
1.7 naddy 949: #ifdef WITH_OPENSSL
1.15 djm 950: case SSH_SK_ECDSA:
1.1 djm 951: return pack_sig_ecdsa(assert, response);
1.7 naddy 952: #endif /* WITH_OPENSSL */
1.15 djm 953: case SSH_SK_ED25519:
1.1 djm 954: return pack_sig_ed25519(assert, response);
955: default:
956: return -1;
957: }
958: }
959:
1.12 djm 960: /* Checks sk_options for sk_sign() and sk_load_resident_keys() */
961: static int
962: check_sign_load_resident_options(struct sk_option **options, char **devicep)
963: {
964: size_t i;
965:
966: if (options == NULL)
967: return 0;
968: for (i = 0; options[i] != NULL; i++) {
969: if (strcmp(options[i]->name, "device") == 0) {
970: if ((*devicep = strdup(options[i]->value)) == NULL) {
971: skdebug(__func__, "strdup device failed");
972: return -1;
973: }
974: skdebug(__func__, "requested device %s", *devicep);
975: } else {
976: skdebug(__func__, "requested unsupported option %s",
977: options[i]->name);
978: if (options[i]->required) {
979: skdebug(__func__, "unknown required option");
980: return -1;
981: }
982: }
983: }
984: return 0;
985: }
986:
1.1 djm 987: int
1.17 djm 988: sk_sign(uint32_t alg, const uint8_t *data, size_t datalen,
1.1 djm 989: const char *application,
990: const uint8_t *key_handle, size_t key_handle_len,
1.12 djm 991: uint8_t flags, const char *pin, struct sk_option **options,
992: struct sk_sign_response **sign_response)
1.1 djm 993: {
994: fido_assert_t *assert = NULL;
1.12 djm 995: char *device = NULL;
1.22 djm 996: struct sk_usbhid *sk = NULL;
1.1 djm 997: struct sk_sign_response *response = NULL;
1.17 djm 998: uint8_t message[32];
1.33 ! djm 999: int ret = SSH_SK_ERR_GENERAL, internal_uv;
1.1 djm 1000: int r;
1001:
1.18 djm 1002: fido_init(SSH_FIDO_INIT_ARG);
1.1 djm 1003:
1004: if (sign_response == NULL) {
1005: skdebug(__func__, "sign_response == NULL");
1006: goto out;
1007: }
1008: *sign_response = NULL;
1.12 djm 1009: if (check_sign_load_resident_options(options, &device) != 0)
1010: goto out; /* error already logged */
1.17 djm 1011: /* hash data to be signed before it goes to the security key */
1012: if ((r = sha256_mem(data, datalen, message, sizeof(message))) != 0) {
1013: skdebug(__func__, "hash message failed");
1014: goto out;
1015: }
1.22 djm 1016: if (device != NULL)
1017: sk = sk_open(device);
1018: else if (pin != NULL || (flags & SSH_SK_USER_VERIFICATION_REQD))
1019: sk = sk_probe(NULL, NULL, 0);
1020: else
1021: sk = sk_probe(application, key_handle, key_handle_len);
1022: if (sk == NULL) {
1023: skdebug(__func__, "failed to find sk");
1.1 djm 1024: goto out;
1025: }
1026: if ((assert = fido_assert_new()) == NULL) {
1027: skdebug(__func__, "fido_assert_new failed");
1028: goto out;
1029: }
1030: if ((r = fido_assert_set_clientdata_hash(assert, message,
1.17 djm 1031: sizeof(message))) != FIDO_OK) {
1.1 djm 1032: skdebug(__func__, "fido_assert_set_clientdata_hash: %s",
1033: fido_strerr(r));
1034: goto out;
1035: }
1036: if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) {
1037: skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r));
1038: goto out;
1039: }
1040: if ((r = fido_assert_allow_cred(assert, key_handle,
1041: key_handle_len)) != FIDO_OK) {
1042: skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r));
1043: goto out;
1044: }
1045: if ((r = fido_assert_set_up(assert,
1.15 djm 1046: (flags & SSH_SK_USER_PRESENCE_REQD) ?
1.1 djm 1047: FIDO_OPT_TRUE : FIDO_OPT_FALSE)) != FIDO_OK) {
1048: skdebug(__func__, "fido_assert_set_up: %s", fido_strerr(r));
1049: goto out;
1050: }
1.33 ! djm 1051: if (pin == NULL && (flags & SSH_SK_USER_VERIFICATION_REQD)) {
! 1052: if (check_sk_options(sk->dev, "uv", &internal_uv) < 0 ||
! 1053: internal_uv != 1) {
! 1054: skdebug(__func__, "check_sk_options uv");
! 1055: ret = SSH_SK_ERR_PIN_REQUIRED;
! 1056: goto out;
! 1057: }
! 1058: if ((r = fido_assert_set_uv(assert,
! 1059: FIDO_OPT_TRUE)) != FIDO_OK) {
! 1060: skdebug(__func__, "fido_assert_set_uv: %s",
! 1061: fido_strerr(r));
! 1062: ret = fidoerr_to_skerr(r);
! 1063: goto out;
! 1064: }
1.21 djm 1065: }
1.22 djm 1066: if ((r = fido_dev_get_assert(sk->dev, assert, pin)) != FIDO_OK) {
1.1 djm 1067: skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r));
1.21 djm 1068: ret = fidoerr_to_skerr(r);
1.1 djm 1069: goto out;
1070: }
1071: if ((response = calloc(1, sizeof(*response))) == NULL) {
1072: skdebug(__func__, "calloc response failed");
1073: goto out;
1074: }
1075: response->flags = fido_assert_flags(assert, 0);
1076: response->counter = fido_assert_sigcount(assert, 0);
1077: if (pack_sig(alg, assert, response) != 0) {
1078: skdebug(__func__, "pack_sig failed");
1079: goto out;
1080: }
1081: *sign_response = response;
1082: response = NULL;
1083: ret = 0;
1084: out:
1.17 djm 1085: explicit_bzero(message, sizeof(message));
1.12 djm 1086: free(device);
1.1 djm 1087: if (response != NULL) {
1088: free(response->sig_r);
1089: free(response->sig_s);
1090: free(response);
1091: }
1.22 djm 1092: sk_close(sk);
1093: fido_assert_free(&assert);
1.1 djm 1094: return ret;
1095: }
1.9 djm 1096:
1097: static int
1.22 djm 1098: read_rks(struct sk_usbhid *sk, const char *pin,
1.9 djm 1099: struct sk_resident_key ***rksp, size_t *nrksp)
1100: {
1.33 ! djm 1101: int ret = SSH_SK_ERR_GENERAL, r = -1, internal_uv;
1.9 djm 1102: fido_credman_metadata_t *metadata = NULL;
1103: fido_credman_rp_t *rp = NULL;
1104: fido_credman_rk_t *rk = NULL;
1.32 djm 1105: size_t i, j, nrp, nrk, user_id_len;
1.9 djm 1106: const fido_cred_t *cred;
1.32 djm 1107: const char *rp_id, *rp_name, *user_name;
1.9 djm 1108: struct sk_resident_key *srk = NULL, **tmp;
1.32 djm 1109: const u_char *user_id;
1.9 djm 1110:
1.22 djm 1111: if (pin == NULL) {
1112: skdebug(__func__, "no PIN specified");
1113: ret = SSH_SK_ERR_PIN_REQUIRED;
1114: goto out;
1.9 djm 1115: }
1116: if ((metadata = fido_credman_metadata_new()) == NULL) {
1117: skdebug(__func__, "alloc failed");
1118: goto out;
1119: }
1.33 ! djm 1120: if (check_sk_options(sk->dev, "uv", &internal_uv) != 0) {
! 1121: skdebug(__func__, "check_sk_options failed");
! 1122: goto out;
! 1123: }
1.9 djm 1124:
1.22 djm 1125: if ((r = fido_credman_get_dev_metadata(sk->dev, metadata, pin)) != 0) {
1.9 djm 1126: if (r == FIDO_ERR_INVALID_COMMAND) {
1127: skdebug(__func__, "device %s does not support "
1.22 djm 1128: "resident keys", sk->path);
1.11 djm 1129: ret = 0;
1.9 djm 1130: goto out;
1131: }
1132: skdebug(__func__, "get metadata for %s failed: %s",
1.22 djm 1133: sk->path, fido_strerr(r));
1.12 djm 1134: ret = fidoerr_to_skerr(r);
1.9 djm 1135: goto out;
1136: }
1137: skdebug(__func__, "existing %llu, remaining %llu",
1138: (unsigned long long)fido_credman_rk_existing(metadata),
1139: (unsigned long long)fido_credman_rk_remaining(metadata));
1140: if ((rp = fido_credman_rp_new()) == NULL) {
1141: skdebug(__func__, "alloc rp failed");
1142: goto out;
1143: }
1.22 djm 1144: if ((r = fido_credman_get_dev_rp(sk->dev, rp, pin)) != 0) {
1.9 djm 1145: skdebug(__func__, "get RPs for %s failed: %s",
1.22 djm 1146: sk->path, fido_strerr(r));
1.9 djm 1147: goto out;
1148: }
1149: nrp = fido_credman_rp_count(rp);
1150: skdebug(__func__, "Device %s has resident keys for %zu RPs",
1.22 djm 1151: sk->path, nrp);
1.9 djm 1152:
1153: /* Iterate over RP IDs that have resident keys */
1154: for (i = 0; i < nrp; i++) {
1.32 djm 1155: rp_id = fido_credman_rp_id(rp, i);
1156: rp_name = fido_credman_rp_name(rp, i);
1.9 djm 1157: skdebug(__func__, "rp %zu: name=\"%s\" id=\"%s\" hashlen=%zu",
1.32 djm 1158: i, rp_name == NULL ? "(none)" : rp_name,
1159: rp_id == NULL ? "(none)" : rp_id,
1.9 djm 1160: fido_credman_rp_id_hash_len(rp, i));
1161:
1162: /* Skip non-SSH RP IDs */
1.32 djm 1163: if (rp_id == NULL ||
1164: strncasecmp(fido_credman_rp_id(rp, i), "ssh:", 4) != 0)
1.9 djm 1165: continue;
1166:
1167: fido_credman_rk_free(&rk);
1168: if ((rk = fido_credman_rk_new()) == NULL) {
1169: skdebug(__func__, "alloc rk failed");
1170: goto out;
1171: }
1.22 djm 1172: if ((r = fido_credman_get_dev_rk(sk->dev,
1173: fido_credman_rp_id(rp, i), rk, pin)) != 0) {
1.9 djm 1174: skdebug(__func__, "get RKs for %s slot %zu failed: %s",
1.22 djm 1175: sk->path, i, fido_strerr(r));
1.9 djm 1176: goto out;
1177: }
1178: nrk = fido_credman_rk_count(rk);
1179: skdebug(__func__, "RP \"%s\" has %zu resident keys",
1180: fido_credman_rp_id(rp, i), nrk);
1181:
1182: /* Iterate over resident keys for this RP ID */
1183: for (j = 0; j < nrk; j++) {
1184: if ((cred = fido_credman_rk(rk, j)) == NULL) {
1185: skdebug(__func__, "no RK in slot %zu", j);
1186: continue;
1187: }
1.32 djm 1188: if ((user_name = fido_cred_user_name(cred)) == NULL)
1189: user_name = "";
1190: user_id = fido_cred_user_id_ptr(cred);
1191: user_id_len = fido_cred_user_id_len(cred);
1192: skdebug(__func__, "Device %s RP \"%s\" user \"%s\" "
1193: "uidlen %zu slot %zu: type %d flags 0x%02x "
1194: "prot 0x%02x", sk->path, rp_id, user_name,
1195: user_id_len, j, fido_cred_type(cred),
1.21 djm 1196: fido_cred_flags(cred), fido_cred_prot(cred));
1.9 djm 1197:
1198: /* build response entry */
1199: if ((srk = calloc(1, sizeof(*srk))) == NULL ||
1200: (srk->key.key_handle = calloc(1,
1201: fido_cred_id_len(cred))) == NULL ||
1.32 djm 1202: (srk->application = strdup(rp_id)) == NULL ||
1203: (user_id_len > 0 &&
1204: (srk->user_id = calloc(1, user_id_len)) == NULL)) {
1.9 djm 1205: skdebug(__func__, "alloc sk_resident_key");
1206: goto out;
1207: }
1208:
1209: srk->key.key_handle_len = fido_cred_id_len(cred);
1.23 djm 1210: memcpy(srk->key.key_handle, fido_cred_id_ptr(cred),
1.9 djm 1211: srk->key.key_handle_len);
1.32 djm 1212: srk->user_id_len = user_id_len;
1213: if (srk->user_id_len != 0)
1214: memcpy(srk->user_id, user_id, srk->user_id_len);
1.9 djm 1215:
1216: switch (fido_cred_type(cred)) {
1217: case COSE_ES256:
1.15 djm 1218: srk->alg = SSH_SK_ECDSA;
1.9 djm 1219: break;
1220: case COSE_EDDSA:
1.15 djm 1221: srk->alg = SSH_SK_ED25519;
1.9 djm 1222: break;
1223: default:
1224: skdebug(__func__, "unsupported key type %d",
1225: fido_cred_type(cred));
1.15 djm 1226: goto out; /* XXX free rk and continue */
1.9 djm 1227: }
1.23 djm 1228:
1.33 ! djm 1229: if (fido_cred_prot(cred) == FIDO_CRED_PROT_UV_REQUIRED
! 1230: && internal_uv == -1)
1.23 djm 1231: srk->flags |= SSH_SK_USER_VERIFICATION_REQD;
1.9 djm 1232:
1233: if ((r = pack_public_key(srk->alg, cred,
1234: &srk->key)) != 0) {
1235: skdebug(__func__, "pack public key failed");
1236: goto out;
1237: }
1238: /* append */
1239: if ((tmp = recallocarray(*rksp, *nrksp, (*nrksp) + 1,
1240: sizeof(**rksp))) == NULL) {
1241: skdebug(__func__, "alloc rksp");
1242: goto out;
1243: }
1244: *rksp = tmp;
1245: (*rksp)[(*nrksp)++] = srk;
1246: srk = NULL;
1247: }
1248: }
1249: /* Success */
1.11 djm 1250: ret = 0;
1.9 djm 1251: out:
1252: if (srk != NULL) {
1253: free(srk->application);
1254: freezero(srk->key.public_key, srk->key.public_key_len);
1255: freezero(srk->key.key_handle, srk->key.key_handle_len);
1.32 djm 1256: freezero(srk->user_id, srk->user_id_len);
1.9 djm 1257: freezero(srk, sizeof(*srk));
1258: }
1259: fido_credman_rp_free(&rp);
1260: fido_credman_rk_free(&rk);
1261: fido_credman_metadata_free(&metadata);
1.11 djm 1262: return ret;
1.9 djm 1263: }
1264:
1265: int
1.12 djm 1266: sk_load_resident_keys(const char *pin, struct sk_option **options,
1.9 djm 1267: struct sk_resident_key ***rksp, size_t *nrksp)
1268: {
1.11 djm 1269: int ret = SSH_SK_ERR_GENERAL, r = -1;
1.22 djm 1270: size_t i, nrks = 0;
1.9 djm 1271: struct sk_resident_key **rks = NULL;
1.22 djm 1272: struct sk_usbhid *sk = NULL;
1.12 djm 1273: char *device = NULL;
1.22 djm 1274:
1.9 djm 1275: *rksp = NULL;
1276: *nrksp = 0;
1.18 djm 1277:
1278: fido_init(SSH_FIDO_INIT_ARG);
1.9 djm 1279:
1.12 djm 1280: if (check_sign_load_resident_options(options, &device) != 0)
1281: goto out; /* error already logged */
1.22 djm 1282: if (device != NULL)
1283: sk = sk_open(device);
1284: else
1285: sk = sk_probe(NULL, NULL, 0);
1286: if (sk == NULL) {
1287: skdebug(__func__, "failed to find sk");
1288: goto out;
1289: }
1290: skdebug(__func__, "trying %s", sk->path);
1291: if ((r = read_rks(sk, pin, &rks, &nrks)) != 0) {
1292: skdebug(__func__, "read_rks failed for %s", sk->path);
1293: ret = r;
1294: goto out;
1.9 djm 1295: }
1.12 djm 1296: /* success, unless we have no keys but a specific error */
1297: if (nrks > 0 || ret == SSH_SK_ERR_GENERAL)
1298: ret = 0;
1.9 djm 1299: *rksp = rks;
1300: *nrksp = nrks;
1301: rks = NULL;
1302: nrks = 0;
1303: out:
1.22 djm 1304: sk_close(sk);
1.9 djm 1305: for (i = 0; i < nrks; i++) {
1306: free(rks[i]->application);
1307: freezero(rks[i]->key.public_key, rks[i]->key.public_key_len);
1308: freezero(rks[i]->key.key_handle, rks[i]->key.key_handle_len);
1.32 djm 1309: freezero(rks[i]->user_id, rks[i]->user_id_len);
1.9 djm 1310: freezero(rks[i], sizeof(*rks[i]));
1311: }
1312: free(rks);
1.11 djm 1313: return ret;
1.9 djm 1314: }
1315: