Annotation of src/usr.bin/ssh/sk-usbhid.c, Revision 1.15
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);
569: if ((response->attestation_cert = calloc(1, len)) == NULL) {
570: skdebug(__func__, "calloc attestation cert failed");
571: goto out;
572: }
573: memcpy(response->attestation_cert, ptr, len);
574: response->attestation_cert_len = len;
575: }
1.6 markus 576: *enroll_response = response;
1.1 djm 577: response = NULL;
578: ret = 0;
579: out:
580: free(device);
581: if (response != NULL) {
582: free(response->public_key);
583: free(response->key_handle);
584: free(response->signature);
585: free(response->attestation_cert);
586: free(response);
587: }
588: if (dev != NULL) {
589: fido_dev_close(dev);
590: fido_dev_free(&dev);
591: }
592: if (cred != NULL) {
593: fido_cred_free(&cred);
594: }
595: return ret;
596: }
597:
1.7 naddy 598: #ifdef WITH_OPENSSL
1.1 djm 599: static int
600: pack_sig_ecdsa(fido_assert_t *assert, struct sk_sign_response *response)
601: {
602: ECDSA_SIG *sig = NULL;
603: const BIGNUM *sig_r, *sig_s;
604: const unsigned char *cp;
605: size_t sig_len;
606: int ret = -1;
607:
608: cp = fido_assert_sig_ptr(assert, 0);
609: sig_len = fido_assert_sig_len(assert, 0);
610: if ((sig = d2i_ECDSA_SIG(NULL, &cp, sig_len)) == NULL) {
611: skdebug(__func__, "d2i_ECDSA_SIG failed");
612: goto out;
613: }
614: ECDSA_SIG_get0(sig, &sig_r, &sig_s);
615: response->sig_r_len = BN_num_bytes(sig_r);
616: response->sig_s_len = BN_num_bytes(sig_s);
617: if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL ||
618: (response->sig_s = calloc(1, response->sig_s_len)) == NULL) {
619: skdebug(__func__, "calloc signature failed");
620: goto out;
621: }
622: BN_bn2bin(sig_r, response->sig_r);
623: BN_bn2bin(sig_s, response->sig_s);
624: ret = 0;
625: out:
626: ECDSA_SIG_free(sig);
627: if (ret != 0) {
628: free(response->sig_r);
629: free(response->sig_s);
630: response->sig_r = NULL;
631: response->sig_s = NULL;
632: }
633: return ret;
634: }
1.7 naddy 635: #endif /* WITH_OPENSSL */
1.1 djm 636:
637: static int
638: pack_sig_ed25519(fido_assert_t *assert, struct sk_sign_response *response)
639: {
640: const unsigned char *ptr;
641: size_t len;
642: int ret = -1;
643:
644: ptr = fido_assert_sig_ptr(assert, 0);
645: len = fido_assert_sig_len(assert, 0);
646: if (len != 64) {
647: skdebug(__func__, "bad length %zu", len);
648: goto out;
649: }
650: response->sig_r_len = len;
651: if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL) {
652: skdebug(__func__, "calloc signature failed");
653: goto out;
654: }
655: memcpy(response->sig_r, ptr, len);
656: ret = 0;
657: out:
658: if (ret != 0) {
659: free(response->sig_r);
660: response->sig_r = NULL;
661: }
662: return ret;
663: }
664:
665: static int
1.12 djm 666: pack_sig(uint32_t alg, fido_assert_t *assert,
667: struct sk_sign_response *response)
1.1 djm 668: {
669: switch(alg) {
1.7 naddy 670: #ifdef WITH_OPENSSL
1.15 ! djm 671: case SSH_SK_ECDSA:
1.1 djm 672: return pack_sig_ecdsa(assert, response);
1.7 naddy 673: #endif /* WITH_OPENSSL */
1.15 ! djm 674: case SSH_SK_ED25519:
1.1 djm 675: return pack_sig_ed25519(assert, response);
676: default:
677: return -1;
678: }
679: }
680:
1.12 djm 681: /* Checks sk_options for sk_sign() and sk_load_resident_keys() */
682: static int
683: check_sign_load_resident_options(struct sk_option **options, char **devicep)
684: {
685: size_t i;
686:
687: if (options == NULL)
688: return 0;
689: for (i = 0; options[i] != NULL; i++) {
690: if (strcmp(options[i]->name, "device") == 0) {
691: if ((*devicep = strdup(options[i]->value)) == NULL) {
692: skdebug(__func__, "strdup device failed");
693: return -1;
694: }
695: skdebug(__func__, "requested device %s", *devicep);
696: } else {
697: skdebug(__func__, "requested unsupported option %s",
698: options[i]->name);
699: if (options[i]->required) {
700: skdebug(__func__, "unknown required option");
701: return -1;
702: }
703: }
704: }
705: return 0;
706: }
707:
1.1 djm 708: int
1.12 djm 709: sk_sign(uint32_t alg, const uint8_t *message, size_t message_len,
1.1 djm 710: const char *application,
711: const uint8_t *key_handle, size_t key_handle_len,
1.12 djm 712: uint8_t flags, const char *pin, struct sk_option **options,
713: struct sk_sign_response **sign_response)
1.1 djm 714: {
715: fido_assert_t *assert = NULL;
1.12 djm 716: char *device = NULL;
1.1 djm 717: fido_dev_t *dev = NULL;
718: struct sk_sign_response *response = NULL;
1.11 djm 719: int ret = SSH_SK_ERR_GENERAL;
1.1 djm 720: int r;
721:
722: #ifdef SK_DEBUG
723: fido_init(FIDO_DEBUG);
724: #endif
725:
726: if (sign_response == NULL) {
727: skdebug(__func__, "sign_response == NULL");
728: goto out;
729: }
730: *sign_response = NULL;
1.12 djm 731: if (check_sign_load_resident_options(options, &device) != 0)
732: goto out; /* error already logged */
733: if ((dev = find_device(device, message, message_len,
734: application, key_handle, key_handle_len)) == NULL) {
1.1 djm 735: skdebug(__func__, "couldn't find device for key handle");
736: goto out;
737: }
738: if ((assert = fido_assert_new()) == NULL) {
739: skdebug(__func__, "fido_assert_new failed");
740: goto out;
741: }
742: if ((r = fido_assert_set_clientdata_hash(assert, message,
743: message_len)) != FIDO_OK) {
744: skdebug(__func__, "fido_assert_set_clientdata_hash: %s",
745: fido_strerr(r));
746: goto out;
747: }
748: if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) {
749: skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r));
750: goto out;
751: }
752: if ((r = fido_assert_allow_cred(assert, key_handle,
753: key_handle_len)) != FIDO_OK) {
754: skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r));
755: goto out;
756: }
757: if ((r = fido_assert_set_up(assert,
1.15 ! djm 758: (flags & SSH_SK_USER_PRESENCE_REQD) ?
1.1 djm 759: FIDO_OPT_TRUE : FIDO_OPT_FALSE)) != FIDO_OK) {
760: skdebug(__func__, "fido_assert_set_up: %s", fido_strerr(r));
761: goto out;
762: }
763: if ((r = fido_dev_get_assert(dev, assert, NULL)) != FIDO_OK) {
764: skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r));
765: goto out;
766: }
767: if ((response = calloc(1, sizeof(*response))) == NULL) {
768: skdebug(__func__, "calloc response failed");
769: goto out;
770: }
771: response->flags = fido_assert_flags(assert, 0);
772: response->counter = fido_assert_sigcount(assert, 0);
773: if (pack_sig(alg, assert, response) != 0) {
774: skdebug(__func__, "pack_sig failed");
775: goto out;
776: }
777: *sign_response = response;
778: response = NULL;
779: ret = 0;
780: out:
1.12 djm 781: free(device);
1.1 djm 782: if (response != NULL) {
783: free(response->sig_r);
784: free(response->sig_s);
785: free(response);
786: }
787: if (dev != NULL) {
788: fido_dev_close(dev);
789: fido_dev_free(&dev);
790: }
791: if (assert != NULL) {
792: fido_assert_free(&assert);
793: }
794: return ret;
795: }
1.9 djm 796:
797: static int
798: read_rks(const char *devpath, const char *pin,
799: struct sk_resident_key ***rksp, size_t *nrksp)
800: {
1.11 djm 801: int ret = SSH_SK_ERR_GENERAL, r = -1;
1.9 djm 802: fido_dev_t *dev = NULL;
803: fido_credman_metadata_t *metadata = NULL;
804: fido_credman_rp_t *rp = NULL;
805: fido_credman_rk_t *rk = NULL;
806: size_t i, j, nrp, nrk;
807: const fido_cred_t *cred;
808: struct sk_resident_key *srk = NULL, **tmp;
809:
810: if ((dev = fido_dev_new()) == NULL) {
811: skdebug(__func__, "fido_dev_new failed");
1.11 djm 812: return ret;
1.9 djm 813: }
814: if ((r = fido_dev_open(dev, devpath)) != FIDO_OK) {
815: skdebug(__func__, "fido_dev_open %s failed: %s",
816: devpath, fido_strerr(r));
817: fido_dev_free(&dev);
1.11 djm 818: return ret;
1.9 djm 819: }
820: if ((metadata = fido_credman_metadata_new()) == NULL) {
821: skdebug(__func__, "alloc failed");
822: goto out;
823: }
824:
825: if ((r = fido_credman_get_dev_metadata(dev, metadata, pin)) != 0) {
826: if (r == FIDO_ERR_INVALID_COMMAND) {
827: skdebug(__func__, "device %s does not support "
828: "resident keys", devpath);
1.11 djm 829: ret = 0;
1.9 djm 830: goto out;
831: }
832: skdebug(__func__, "get metadata for %s failed: %s",
833: devpath, fido_strerr(r));
1.12 djm 834: ret = fidoerr_to_skerr(r);
1.9 djm 835: goto out;
836: }
837: skdebug(__func__, "existing %llu, remaining %llu",
838: (unsigned long long)fido_credman_rk_existing(metadata),
839: (unsigned long long)fido_credman_rk_remaining(metadata));
840: if ((rp = fido_credman_rp_new()) == NULL) {
841: skdebug(__func__, "alloc rp failed");
842: goto out;
843: }
844: if ((r = fido_credman_get_dev_rp(dev, rp, pin)) != 0) {
845: skdebug(__func__, "get RPs for %s failed: %s",
846: devpath, fido_strerr(r));
847: goto out;
848: }
849: nrp = fido_credman_rp_count(rp);
850: skdebug(__func__, "Device %s has resident keys for %zu RPs",
851: devpath, nrp);
852:
853: /* Iterate over RP IDs that have resident keys */
854: for (i = 0; i < nrp; i++) {
855: skdebug(__func__, "rp %zu: name=\"%s\" id=\"%s\" hashlen=%zu",
856: i, fido_credman_rp_name(rp, i), fido_credman_rp_id(rp, i),
857: fido_credman_rp_id_hash_len(rp, i));
858:
859: /* Skip non-SSH RP IDs */
860: if (strncasecmp(fido_credman_rp_id(rp, i), "ssh:", 4) != 0)
861: continue;
862:
863: fido_credman_rk_free(&rk);
864: if ((rk = fido_credman_rk_new()) == NULL) {
865: skdebug(__func__, "alloc rk failed");
866: goto out;
867: }
868: if ((r = fido_credman_get_dev_rk(dev, fido_credman_rp_id(rp, i),
869: rk, pin)) != 0) {
870: skdebug(__func__, "get RKs for %s slot %zu failed: %s",
871: devpath, i, fido_strerr(r));
872: goto out;
873: }
874: nrk = fido_credman_rk_count(rk);
875: skdebug(__func__, "RP \"%s\" has %zu resident keys",
876: fido_credman_rp_id(rp, i), nrk);
877:
878: /* Iterate over resident keys for this RP ID */
879: for (j = 0; j < nrk; j++) {
880: if ((cred = fido_credman_rk(rk, j)) == NULL) {
881: skdebug(__func__, "no RK in slot %zu", j);
882: continue;
883: }
884: skdebug(__func__, "Device %s RP \"%s\" slot %zu: "
885: "type %d", devpath, fido_credman_rp_id(rp, i), j,
886: fido_cred_type(cred));
887:
888: /* build response entry */
889: if ((srk = calloc(1, sizeof(*srk))) == NULL ||
890: (srk->key.key_handle = calloc(1,
891: fido_cred_id_len(cred))) == NULL ||
892: (srk->application = strdup(fido_credman_rp_id(rp,
893: i))) == NULL) {
894: skdebug(__func__, "alloc sk_resident_key");
895: goto out;
896: }
897:
898: srk->key.key_handle_len = fido_cred_id_len(cred);
899: memcpy(srk->key.key_handle,
900: fido_cred_id_ptr(cred),
901: srk->key.key_handle_len);
902:
903: switch (fido_cred_type(cred)) {
904: case COSE_ES256:
1.15 ! djm 905: srk->alg = SSH_SK_ECDSA;
1.9 djm 906: break;
907: case COSE_EDDSA:
1.15 ! djm 908: srk->alg = SSH_SK_ED25519;
1.9 djm 909: break;
910: default:
911: skdebug(__func__, "unsupported key type %d",
912: fido_cred_type(cred));
1.15 ! djm 913: goto out; /* XXX free rk and continue */
1.9 djm 914: }
915:
916: if ((r = pack_public_key(srk->alg, cred,
917: &srk->key)) != 0) {
918: skdebug(__func__, "pack public key failed");
919: goto out;
920: }
921: /* append */
922: if ((tmp = recallocarray(*rksp, *nrksp, (*nrksp) + 1,
923: sizeof(**rksp))) == NULL) {
924: skdebug(__func__, "alloc rksp");
925: goto out;
926: }
927: *rksp = tmp;
928: (*rksp)[(*nrksp)++] = srk;
929: srk = NULL;
930: }
931: }
932: /* Success */
1.11 djm 933: ret = 0;
1.9 djm 934: out:
935: if (srk != NULL) {
936: free(srk->application);
937: freezero(srk->key.public_key, srk->key.public_key_len);
938: freezero(srk->key.key_handle, srk->key.key_handle_len);
939: freezero(srk, sizeof(*srk));
940: }
941: fido_credman_rp_free(&rp);
942: fido_credman_rk_free(&rk);
943: fido_dev_close(dev);
944: fido_dev_free(&dev);
945: fido_credman_metadata_free(&metadata);
1.11 djm 946: return ret;
1.9 djm 947: }
948:
949: int
1.12 djm 950: sk_load_resident_keys(const char *pin, struct sk_option **options,
1.9 djm 951: struct sk_resident_key ***rksp, size_t *nrksp)
952: {
1.11 djm 953: int ret = SSH_SK_ERR_GENERAL, r = -1;
1.9 djm 954: fido_dev_info_t *devlist = NULL;
955: size_t i, ndev = 0, nrks = 0;
956: const fido_dev_info_t *di;
957: struct sk_resident_key **rks = NULL;
1.12 djm 958: char *device = NULL;
1.9 djm 959: *rksp = NULL;
960: *nrksp = 0;
961:
1.12 djm 962: if (check_sign_load_resident_options(options, &device) != 0)
963: goto out; /* error already logged */
964: if (device != NULL) {
965: skdebug(__func__, "trying %s", device);
966: if ((r = read_rks(device, pin, &rks, &nrks)) != 0) {
1.13 djm 967: skdebug(__func__, "read_rks failed for %s", device);
1.12 djm 968: ret = r;
969: goto out;
970: }
971: } else {
972: /* Try all devices */
973: if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) {
974: skdebug(__func__, "fido_dev_info_new failed");
975: goto out;
976: }
977: if ((r = fido_dev_info_manifest(devlist,
978: MAX_FIDO_DEVICES, &ndev)) != FIDO_OK) {
979: skdebug(__func__, "fido_dev_info_manifest failed: %s",
980: fido_strerr(r));
981: goto out;
982: }
983: for (i = 0; i < ndev; i++) {
984: if ((di = fido_dev_info_ptr(devlist, i)) == NULL) {
985: skdebug(__func__, "no dev info at %zu", i);
986: continue;
987: }
988: skdebug(__func__, "trying %s", fido_dev_info_path(di));
989: if ((r = read_rks(fido_dev_info_path(di), pin,
990: &rks, &nrks)) != 0) {
991: skdebug(__func__, "read_rks failed for %s",
992: fido_dev_info_path(di));
993: /* remember last error */
994: ret = r;
995: continue;
996: }
1.9 djm 997: }
998: }
1.12 djm 999: /* success, unless we have no keys but a specific error */
1000: if (nrks > 0 || ret == SSH_SK_ERR_GENERAL)
1001: ret = 0;
1.9 djm 1002: *rksp = rks;
1003: *nrksp = nrks;
1004: rks = NULL;
1005: nrks = 0;
1006: out:
1.12 djm 1007: free(device);
1.9 djm 1008: for (i = 0; i < nrks; i++) {
1009: free(rks[i]->application);
1010: freezero(rks[i]->key.public_key, rks[i]->key.public_key_len);
1011: freezero(rks[i]->key.key_handle, rks[i]->key.key_handle_len);
1012: freezero(rks[i], sizeof(*rks[i]));
1013: }
1014: free(rks);
1015: fido_dev_info_free(&devlist, MAX_FIDO_DEVICES);
1.11 djm 1016: return ret;
1.9 djm 1017: }
1018: