Annotation of src/usr.bin/ssh/sk-usbhid.c, Revision 1.36
1.36 ! djm 1: /* $OpenBSD: sk-usbhid.c,v 1.35 2021/12/02 22:40:05 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) {
731: skdebug(__func__, "failed to find sk");
1.1 djm 732: goto out;
733: }
1.22 djm 734: skdebug(__func__, "using device %s", sk->path);
1.1 djm 735: if ((cred = fido_cred_new()) == NULL) {
736: skdebug(__func__, "fido_cred_new failed");
737: goto out;
738: }
739: if ((r = fido_cred_set_type(cred, cose_alg)) != FIDO_OK) {
740: skdebug(__func__, "fido_cred_set_type: %s", fido_strerr(r));
741: goto out;
742: }
1.30 djm 743: if (sha256_mem(challenge, challenge_len,
744: chall_hash, sizeof(chall_hash)) != 0) {
745: skdebug(__func__, "hash challenge failed");
746: goto out;
747: }
748: if ((r = fido_cred_set_clientdata_hash(cred, chall_hash,
749: sizeof(chall_hash))) != FIDO_OK) {
1.1 djm 750: skdebug(__func__, "fido_cred_set_clientdata_hash: %s",
751: fido_strerr(r));
1.8 djm 752: goto out;
753: }
1.15 djm 754: if ((r = fido_cred_set_rk(cred, (flags & SSH_SK_RESIDENT_KEY) != 0 ?
1.8 djm 755: FIDO_OPT_TRUE : FIDO_OPT_OMIT)) != FIDO_OK) {
756: skdebug(__func__, "fido_cred_set_rk: %s", fido_strerr(r));
1.1 djm 757: goto out;
758: }
759: if ((r = fido_cred_set_user(cred, user_id, sizeof(user_id),
760: "openssh", "openssh", NULL)) != FIDO_OK) {
761: skdebug(__func__, "fido_cred_set_user: %s", fido_strerr(r));
762: goto out;
763: }
764: if ((r = fido_cred_set_rp(cred, application, NULL)) != FIDO_OK) {
765: skdebug(__func__, "fido_cred_set_rp: %s", fido_strerr(r));
766: goto out;
767: }
1.21 djm 768: if ((flags & (SSH_SK_RESIDENT_KEY|SSH_SK_USER_VERIFICATION_REQD)) != 0) {
1.22 djm 769: if (!fido_dev_supports_cred_prot(sk->dev)) {
770: skdebug(__func__, "%s does not support credprot, "
771: "refusing to create unprotected "
772: "resident/verify-required key", sk->path);
1.19 djm 773: ret = SSH_SK_ERR_UNSUPPORTED;
774: goto out;
775: }
1.21 djm 776: if ((flags & SSH_SK_USER_VERIFICATION_REQD))
777: credprot = FIDO_CRED_PROT_UV_REQUIRED;
778: else
779: credprot = FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID;
780:
781: if ((r = fido_cred_set_prot(cred, credprot)) != FIDO_OK) {
1.19 djm 782: skdebug(__func__, "fido_cred_set_prot: %s",
783: fido_strerr(r));
784: ret = fidoerr_to_skerr(r);
785: goto out;
786: }
1.1 djm 787: }
1.22 djm 788: if ((r = fido_dev_make_cred(sk->dev, cred, pin)) != FIDO_OK) {
1.1 djm 789: skdebug(__func__, "fido_dev_make_cred: %s", fido_strerr(r));
1.11 djm 790: ret = fidoerr_to_skerr(r);
1.1 djm 791: goto out;
792: }
793: if (fido_cred_x5c_ptr(cred) != NULL) {
794: if ((r = fido_cred_verify(cred)) != FIDO_OK) {
795: skdebug(__func__, "fido_cred_verify: %s",
796: fido_strerr(r));
797: goto out;
798: }
799: } else {
800: skdebug(__func__, "self-attested credential");
801: if ((r = fido_cred_verify_self(cred)) != FIDO_OK) {
802: skdebug(__func__, "fido_cred_verify_self: %s",
803: fido_strerr(r));
804: goto out;
805: }
806: }
807: if ((response = calloc(1, sizeof(*response))) == NULL) {
808: skdebug(__func__, "calloc response failed");
809: goto out;
810: }
1.33 djm 811: response->flags = flags;
812: if ((flags & SSH_SK_USER_VERIFICATION_REQD)) {
813: if (check_sk_options(sk->dev, "uv", &internal_uv) == 0 &&
814: internal_uv != -1) {
815: /* user verification handled by token */
816: response->flags &= ~SSH_SK_USER_VERIFICATION_REQD;
817: }
818: }
1.1 djm 819: if (pack_public_key(alg, cred, response) != 0) {
820: skdebug(__func__, "pack_public_key failed");
821: goto out;
822: }
823: if ((ptr = fido_cred_id_ptr(cred)) != NULL) {
824: len = fido_cred_id_len(cred);
825: if ((response->key_handle = calloc(1, len)) == NULL) {
826: skdebug(__func__, "calloc key handle failed");
827: goto out;
828: }
829: memcpy(response->key_handle, ptr, len);
830: response->key_handle_len = len;
831: }
832: if ((ptr = fido_cred_sig_ptr(cred)) != NULL) {
833: len = fido_cred_sig_len(cred);
834: if ((response->signature = calloc(1, len)) == NULL) {
835: skdebug(__func__, "calloc signature failed");
836: goto out;
837: }
838: memcpy(response->signature, ptr, len);
839: response->signature_len = len;
840: }
841: if ((ptr = fido_cred_x5c_ptr(cred)) != NULL) {
842: len = fido_cred_x5c_len(cred);
1.28 djm 843: skdebug(__func__, "attestation cert len=%zu", len);
1.1 djm 844: if ((response->attestation_cert = calloc(1, len)) == NULL) {
845: skdebug(__func__, "calloc attestation cert failed");
846: goto out;
847: }
848: memcpy(response->attestation_cert, ptr, len);
849: response->attestation_cert_len = len;
850: }
1.26 djm 851: if ((ptr = fido_cred_authdata_ptr(cred)) != NULL) {
852: len = fido_cred_authdata_len(cred);
1.28 djm 853: skdebug(__func__, "authdata len=%zu", len);
1.26 djm 854: if ((response->authdata = calloc(1, len)) == NULL) {
855: skdebug(__func__, "calloc authdata failed");
856: goto out;
857: }
858: memcpy(response->authdata, ptr, len);
859: response->authdata_len = len;
860: }
1.6 markus 861: *enroll_response = response;
1.1 djm 862: response = NULL;
863: ret = 0;
864: out:
865: free(device);
866: if (response != NULL) {
867: free(response->public_key);
868: free(response->key_handle);
869: free(response->signature);
870: free(response->attestation_cert);
1.26 djm 871: free(response->authdata);
1.1 djm 872: free(response);
873: }
1.22 djm 874: sk_close(sk);
875: fido_cred_free(&cred);
1.1 djm 876: return ret;
877: }
878:
1.7 naddy 879: #ifdef WITH_OPENSSL
1.1 djm 880: static int
881: pack_sig_ecdsa(fido_assert_t *assert, struct sk_sign_response *response)
882: {
883: ECDSA_SIG *sig = NULL;
884: const BIGNUM *sig_r, *sig_s;
885: const unsigned char *cp;
886: size_t sig_len;
887: int ret = -1;
888:
889: cp = fido_assert_sig_ptr(assert, 0);
890: sig_len = fido_assert_sig_len(assert, 0);
891: if ((sig = d2i_ECDSA_SIG(NULL, &cp, sig_len)) == NULL) {
892: skdebug(__func__, "d2i_ECDSA_SIG failed");
893: goto out;
894: }
895: ECDSA_SIG_get0(sig, &sig_r, &sig_s);
896: response->sig_r_len = BN_num_bytes(sig_r);
897: response->sig_s_len = BN_num_bytes(sig_s);
898: if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL ||
899: (response->sig_s = calloc(1, response->sig_s_len)) == NULL) {
900: skdebug(__func__, "calloc signature failed");
901: goto out;
902: }
903: BN_bn2bin(sig_r, response->sig_r);
904: BN_bn2bin(sig_s, response->sig_s);
905: ret = 0;
906: out:
907: ECDSA_SIG_free(sig);
908: if (ret != 0) {
909: free(response->sig_r);
910: free(response->sig_s);
911: response->sig_r = NULL;
912: response->sig_s = NULL;
913: }
914: return ret;
915: }
1.7 naddy 916: #endif /* WITH_OPENSSL */
1.1 djm 917:
918: static int
919: pack_sig_ed25519(fido_assert_t *assert, struct sk_sign_response *response)
920: {
921: const unsigned char *ptr;
922: size_t len;
923: int ret = -1;
924:
925: ptr = fido_assert_sig_ptr(assert, 0);
926: len = fido_assert_sig_len(assert, 0);
927: if (len != 64) {
928: skdebug(__func__, "bad length %zu", len);
929: goto out;
930: }
931: response->sig_r_len = len;
932: if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL) {
933: skdebug(__func__, "calloc signature failed");
934: goto out;
935: }
936: memcpy(response->sig_r, ptr, len);
937: ret = 0;
938: out:
939: if (ret != 0) {
940: free(response->sig_r);
941: response->sig_r = NULL;
942: }
943: return ret;
944: }
945:
946: static int
1.12 djm 947: pack_sig(uint32_t alg, fido_assert_t *assert,
948: struct sk_sign_response *response)
1.1 djm 949: {
950: switch(alg) {
1.7 naddy 951: #ifdef WITH_OPENSSL
1.15 djm 952: case SSH_SK_ECDSA:
1.1 djm 953: return pack_sig_ecdsa(assert, response);
1.7 naddy 954: #endif /* WITH_OPENSSL */
1.15 djm 955: case SSH_SK_ED25519:
1.1 djm 956: return pack_sig_ed25519(assert, response);
957: default:
958: return -1;
959: }
960: }
961:
1.12 djm 962: /* Checks sk_options for sk_sign() and sk_load_resident_keys() */
963: static int
964: check_sign_load_resident_options(struct sk_option **options, char **devicep)
965: {
966: size_t i;
967:
968: if (options == NULL)
969: return 0;
970: for (i = 0; options[i] != NULL; i++) {
971: if (strcmp(options[i]->name, "device") == 0) {
972: if ((*devicep = strdup(options[i]->value)) == NULL) {
973: skdebug(__func__, "strdup device failed");
974: return -1;
975: }
976: skdebug(__func__, "requested device %s", *devicep);
977: } else {
978: skdebug(__func__, "requested unsupported option %s",
979: options[i]->name);
980: if (options[i]->required) {
981: skdebug(__func__, "unknown required option");
982: return -1;
983: }
984: }
985: }
986: return 0;
987: }
988:
1.1 djm 989: int
1.17 djm 990: sk_sign(uint32_t alg, const uint8_t *data, size_t datalen,
1.1 djm 991: const char *application,
992: const uint8_t *key_handle, size_t key_handle_len,
1.12 djm 993: uint8_t flags, const char *pin, struct sk_option **options,
994: struct sk_sign_response **sign_response)
1.1 djm 995: {
996: fido_assert_t *assert = NULL;
1.12 djm 997: char *device = NULL;
1.22 djm 998: struct sk_usbhid *sk = NULL;
1.1 djm 999: struct sk_sign_response *response = NULL;
1.17 djm 1000: uint8_t message[32];
1.33 djm 1001: int ret = SSH_SK_ERR_GENERAL, internal_uv;
1.1 djm 1002: int r;
1003:
1.18 djm 1004: fido_init(SSH_FIDO_INIT_ARG);
1.1 djm 1005:
1006: if (sign_response == NULL) {
1007: skdebug(__func__, "sign_response == NULL");
1008: goto out;
1009: }
1010: *sign_response = NULL;
1.12 djm 1011: if (check_sign_load_resident_options(options, &device) != 0)
1012: goto out; /* error already logged */
1.17 djm 1013: /* hash data to be signed before it goes to the security key */
1014: if ((r = sha256_mem(data, datalen, message, sizeof(message))) != 0) {
1015: skdebug(__func__, "hash message failed");
1016: goto out;
1017: }
1.22 djm 1018: if (device != NULL)
1019: sk = sk_open(device);
1020: else if (pin != NULL || (flags & SSH_SK_USER_VERIFICATION_REQD))
1021: sk = sk_probe(NULL, NULL, 0);
1022: else
1023: sk = sk_probe(application, key_handle, key_handle_len);
1024: if (sk == NULL) {
1025: skdebug(__func__, "failed to find sk");
1.1 djm 1026: goto out;
1027: }
1028: if ((assert = fido_assert_new()) == NULL) {
1029: skdebug(__func__, "fido_assert_new failed");
1030: goto out;
1031: }
1032: if ((r = fido_assert_set_clientdata_hash(assert, message,
1.17 djm 1033: sizeof(message))) != FIDO_OK) {
1.1 djm 1034: skdebug(__func__, "fido_assert_set_clientdata_hash: %s",
1035: fido_strerr(r));
1036: goto out;
1037: }
1038: if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) {
1039: skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r));
1040: goto out;
1041: }
1042: if ((r = fido_assert_allow_cred(assert, key_handle,
1043: key_handle_len)) != FIDO_OK) {
1044: skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r));
1045: goto out;
1046: }
1047: if ((r = fido_assert_set_up(assert,
1.15 djm 1048: (flags & SSH_SK_USER_PRESENCE_REQD) ?
1.1 djm 1049: FIDO_OPT_TRUE : FIDO_OPT_FALSE)) != FIDO_OK) {
1050: skdebug(__func__, "fido_assert_set_up: %s", fido_strerr(r));
1051: goto out;
1052: }
1.33 djm 1053: if (pin == NULL && (flags & SSH_SK_USER_VERIFICATION_REQD)) {
1054: if (check_sk_options(sk->dev, "uv", &internal_uv) < 0 ||
1055: internal_uv != 1) {
1056: skdebug(__func__, "check_sk_options uv");
1057: ret = SSH_SK_ERR_PIN_REQUIRED;
1058: goto out;
1059: }
1060: if ((r = fido_assert_set_uv(assert,
1061: FIDO_OPT_TRUE)) != FIDO_OK) {
1062: skdebug(__func__, "fido_assert_set_uv: %s",
1063: fido_strerr(r));
1064: ret = fidoerr_to_skerr(r);
1065: goto out;
1066: }
1.21 djm 1067: }
1.22 djm 1068: if ((r = fido_dev_get_assert(sk->dev, assert, pin)) != FIDO_OK) {
1.1 djm 1069: skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r));
1.21 djm 1070: ret = fidoerr_to_skerr(r);
1.1 djm 1071: goto out;
1072: }
1073: if ((response = calloc(1, sizeof(*response))) == NULL) {
1074: skdebug(__func__, "calloc response failed");
1075: goto out;
1076: }
1077: response->flags = fido_assert_flags(assert, 0);
1078: response->counter = fido_assert_sigcount(assert, 0);
1079: if (pack_sig(alg, assert, response) != 0) {
1080: skdebug(__func__, "pack_sig failed");
1081: goto out;
1082: }
1083: *sign_response = response;
1084: response = NULL;
1085: ret = 0;
1086: out:
1.17 djm 1087: explicit_bzero(message, sizeof(message));
1.12 djm 1088: free(device);
1.1 djm 1089: if (response != NULL) {
1090: free(response->sig_r);
1091: free(response->sig_s);
1092: free(response);
1093: }
1.22 djm 1094: sk_close(sk);
1095: fido_assert_free(&assert);
1.1 djm 1096: return ret;
1097: }
1.9 djm 1098:
1099: static int
1.22 djm 1100: read_rks(struct sk_usbhid *sk, const char *pin,
1.9 djm 1101: struct sk_resident_key ***rksp, size_t *nrksp)
1102: {
1.33 djm 1103: int ret = SSH_SK_ERR_GENERAL, r = -1, internal_uv;
1.9 djm 1104: fido_credman_metadata_t *metadata = NULL;
1105: fido_credman_rp_t *rp = NULL;
1106: fido_credman_rk_t *rk = NULL;
1.32 djm 1107: size_t i, j, nrp, nrk, user_id_len;
1.9 djm 1108: const fido_cred_t *cred;
1.32 djm 1109: const char *rp_id, *rp_name, *user_name;
1.9 djm 1110: struct sk_resident_key *srk = NULL, **tmp;
1.32 djm 1111: const u_char *user_id;
1.9 djm 1112:
1.22 djm 1113: if (pin == NULL) {
1114: skdebug(__func__, "no PIN specified");
1115: ret = SSH_SK_ERR_PIN_REQUIRED;
1116: goto out;
1.9 djm 1117: }
1118: if ((metadata = fido_credman_metadata_new()) == NULL) {
1119: skdebug(__func__, "alloc failed");
1120: goto out;
1121: }
1.33 djm 1122: if (check_sk_options(sk->dev, "uv", &internal_uv) != 0) {
1123: skdebug(__func__, "check_sk_options failed");
1124: goto out;
1125: }
1.9 djm 1126:
1.22 djm 1127: if ((r = fido_credman_get_dev_metadata(sk->dev, metadata, pin)) != 0) {
1.9 djm 1128: if (r == FIDO_ERR_INVALID_COMMAND) {
1129: skdebug(__func__, "device %s does not support "
1.22 djm 1130: "resident keys", sk->path);
1.11 djm 1131: ret = 0;
1.9 djm 1132: goto out;
1133: }
1134: skdebug(__func__, "get metadata for %s failed: %s",
1.22 djm 1135: sk->path, fido_strerr(r));
1.12 djm 1136: ret = fidoerr_to_skerr(r);
1.9 djm 1137: goto out;
1138: }
1139: skdebug(__func__, "existing %llu, remaining %llu",
1140: (unsigned long long)fido_credman_rk_existing(metadata),
1141: (unsigned long long)fido_credman_rk_remaining(metadata));
1142: if ((rp = fido_credman_rp_new()) == NULL) {
1143: skdebug(__func__, "alloc rp failed");
1144: goto out;
1145: }
1.22 djm 1146: if ((r = fido_credman_get_dev_rp(sk->dev, rp, pin)) != 0) {
1.9 djm 1147: skdebug(__func__, "get RPs for %s failed: %s",
1.22 djm 1148: sk->path, fido_strerr(r));
1.9 djm 1149: goto out;
1150: }
1151: nrp = fido_credman_rp_count(rp);
1152: skdebug(__func__, "Device %s has resident keys for %zu RPs",
1.22 djm 1153: sk->path, nrp);
1.9 djm 1154:
1155: /* Iterate over RP IDs that have resident keys */
1156: for (i = 0; i < nrp; i++) {
1.32 djm 1157: rp_id = fido_credman_rp_id(rp, i);
1158: rp_name = fido_credman_rp_name(rp, i);
1.9 djm 1159: skdebug(__func__, "rp %zu: name=\"%s\" id=\"%s\" hashlen=%zu",
1.32 djm 1160: i, rp_name == NULL ? "(none)" : rp_name,
1161: rp_id == NULL ? "(none)" : rp_id,
1.9 djm 1162: fido_credman_rp_id_hash_len(rp, i));
1163:
1164: /* Skip non-SSH RP IDs */
1.32 djm 1165: if (rp_id == NULL ||
1166: strncasecmp(fido_credman_rp_id(rp, i), "ssh:", 4) != 0)
1.9 djm 1167: continue;
1168:
1169: fido_credman_rk_free(&rk);
1170: if ((rk = fido_credman_rk_new()) == NULL) {
1171: skdebug(__func__, "alloc rk failed");
1172: goto out;
1173: }
1.22 djm 1174: if ((r = fido_credman_get_dev_rk(sk->dev,
1175: fido_credman_rp_id(rp, i), rk, pin)) != 0) {
1.9 djm 1176: skdebug(__func__, "get RKs for %s slot %zu failed: %s",
1.22 djm 1177: sk->path, i, fido_strerr(r));
1.9 djm 1178: goto out;
1179: }
1180: nrk = fido_credman_rk_count(rk);
1181: skdebug(__func__, "RP \"%s\" has %zu resident keys",
1182: fido_credman_rp_id(rp, i), nrk);
1183:
1184: /* Iterate over resident keys for this RP ID */
1185: for (j = 0; j < nrk; j++) {
1186: if ((cred = fido_credman_rk(rk, j)) == NULL) {
1187: skdebug(__func__, "no RK in slot %zu", j);
1188: continue;
1189: }
1.32 djm 1190: if ((user_name = fido_cred_user_name(cred)) == NULL)
1191: user_name = "";
1192: user_id = fido_cred_user_id_ptr(cred);
1193: user_id_len = fido_cred_user_id_len(cred);
1194: skdebug(__func__, "Device %s RP \"%s\" user \"%s\" "
1195: "uidlen %zu slot %zu: type %d flags 0x%02x "
1196: "prot 0x%02x", sk->path, rp_id, user_name,
1197: user_id_len, j, fido_cred_type(cred),
1.21 djm 1198: fido_cred_flags(cred), fido_cred_prot(cred));
1.9 djm 1199:
1200: /* build response entry */
1201: if ((srk = calloc(1, sizeof(*srk))) == NULL ||
1202: (srk->key.key_handle = calloc(1,
1203: fido_cred_id_len(cred))) == NULL ||
1.32 djm 1204: (srk->application = strdup(rp_id)) == NULL ||
1205: (user_id_len > 0 &&
1206: (srk->user_id = calloc(1, user_id_len)) == NULL)) {
1.9 djm 1207: skdebug(__func__, "alloc sk_resident_key");
1208: goto out;
1209: }
1210:
1211: srk->key.key_handle_len = fido_cred_id_len(cred);
1.23 djm 1212: memcpy(srk->key.key_handle, fido_cred_id_ptr(cred),
1.9 djm 1213: srk->key.key_handle_len);
1.32 djm 1214: srk->user_id_len = user_id_len;
1215: if (srk->user_id_len != 0)
1216: memcpy(srk->user_id, user_id, srk->user_id_len);
1.9 djm 1217:
1218: switch (fido_cred_type(cred)) {
1219: case COSE_ES256:
1.15 djm 1220: srk->alg = SSH_SK_ECDSA;
1.9 djm 1221: break;
1222: case COSE_EDDSA:
1.15 djm 1223: srk->alg = SSH_SK_ED25519;
1.9 djm 1224: break;
1225: default:
1226: skdebug(__func__, "unsupported key type %d",
1227: fido_cred_type(cred));
1.15 djm 1228: goto out; /* XXX free rk and continue */
1.9 djm 1229: }
1.23 djm 1230:
1.33 djm 1231: if (fido_cred_prot(cred) == FIDO_CRED_PROT_UV_REQUIRED
1232: && internal_uv == -1)
1.23 djm 1233: srk->flags |= SSH_SK_USER_VERIFICATION_REQD;
1.9 djm 1234:
1235: if ((r = pack_public_key(srk->alg, cred,
1236: &srk->key)) != 0) {
1237: skdebug(__func__, "pack public key failed");
1238: goto out;
1239: }
1240: /* append */
1241: if ((tmp = recallocarray(*rksp, *nrksp, (*nrksp) + 1,
1242: sizeof(**rksp))) == NULL) {
1243: skdebug(__func__, "alloc rksp");
1244: goto out;
1245: }
1246: *rksp = tmp;
1247: (*rksp)[(*nrksp)++] = srk;
1248: srk = NULL;
1249: }
1250: }
1251: /* Success */
1.11 djm 1252: ret = 0;
1.9 djm 1253: out:
1254: if (srk != NULL) {
1255: free(srk->application);
1256: freezero(srk->key.public_key, srk->key.public_key_len);
1257: freezero(srk->key.key_handle, srk->key.key_handle_len);
1.32 djm 1258: freezero(srk->user_id, srk->user_id_len);
1.9 djm 1259: freezero(srk, sizeof(*srk));
1260: }
1261: fido_credman_rp_free(&rp);
1262: fido_credman_rk_free(&rk);
1263: fido_credman_metadata_free(&metadata);
1.11 djm 1264: return ret;
1.9 djm 1265: }
1266:
1267: int
1.12 djm 1268: sk_load_resident_keys(const char *pin, struct sk_option **options,
1.9 djm 1269: struct sk_resident_key ***rksp, size_t *nrksp)
1270: {
1.11 djm 1271: int ret = SSH_SK_ERR_GENERAL, r = -1;
1.22 djm 1272: size_t i, nrks = 0;
1.9 djm 1273: struct sk_resident_key **rks = NULL;
1.22 djm 1274: struct sk_usbhid *sk = NULL;
1.12 djm 1275: char *device = NULL;
1.22 djm 1276:
1.9 djm 1277: *rksp = NULL;
1278: *nrksp = 0;
1.18 djm 1279:
1280: fido_init(SSH_FIDO_INIT_ARG);
1.9 djm 1281:
1.12 djm 1282: if (check_sign_load_resident_options(options, &device) != 0)
1283: goto out; /* error already logged */
1.22 djm 1284: if (device != NULL)
1285: sk = sk_open(device);
1286: else
1287: sk = sk_probe(NULL, NULL, 0);
1288: if (sk == NULL) {
1289: skdebug(__func__, "failed to find sk");
1290: goto out;
1291: }
1292: skdebug(__func__, "trying %s", sk->path);
1293: if ((r = read_rks(sk, pin, &rks, &nrks)) != 0) {
1294: skdebug(__func__, "read_rks failed for %s", sk->path);
1295: ret = r;
1296: goto out;
1.9 djm 1297: }
1.12 djm 1298: /* success, unless we have no keys but a specific error */
1299: if (nrks > 0 || ret == SSH_SK_ERR_GENERAL)
1300: ret = 0;
1.9 djm 1301: *rksp = rks;
1302: *nrksp = nrks;
1303: rks = NULL;
1304: nrks = 0;
1305: out:
1.22 djm 1306: sk_close(sk);
1.9 djm 1307: for (i = 0; i < nrks; i++) {
1308: free(rks[i]->application);
1309: freezero(rks[i]->key.public_key, rks[i]->key.public_key_len);
1310: freezero(rks[i]->key.key_handle, rks[i]->key.key_handle_len);
1.32 djm 1311: freezero(rks[i]->user_id, rks[i]->user_id_len);
1.9 djm 1312: freezero(rks[i], sizeof(*rks[i]));
1313: }
1314: free(rks);
1.11 djm 1315: return ret;
1.9 djm 1316: }
1317: