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