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