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