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