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