10afa8e06SEd Maste /* 2*60a517b6SEd Maste * Copyright (c) 2018-2023 Yubico AB. All rights reserved. 30afa8e06SEd Maste * Use of this source code is governed by a BSD-style 40afa8e06SEd Maste * license that can be found in the LICENSE file. 52ccfa855SEd Maste * SPDX-License-Identifier: BSD-2-Clause 60afa8e06SEd Maste */ 70afa8e06SEd Maste 80afa8e06SEd Maste #include <fido.h> 90afa8e06SEd Maste #include <stdio.h> 100afa8e06SEd Maste #include <stdlib.h> 110afa8e06SEd Maste #include <string.h> 120afa8e06SEd Maste #ifdef HAVE_UNISTD_H 130afa8e06SEd Maste #include <unistd.h> 140afa8e06SEd Maste #endif 150afa8e06SEd Maste 160afa8e06SEd Maste #include "../openbsd-compat/openbsd-compat.h" 170afa8e06SEd Maste #include "extern.h" 180afa8e06SEd Maste 190afa8e06SEd Maste struct toggle { 200afa8e06SEd Maste fido_opt_t up; 210afa8e06SEd Maste fido_opt_t uv; 220afa8e06SEd Maste fido_opt_t pin; 230afa8e06SEd Maste }; 240afa8e06SEd Maste 250afa8e06SEd Maste static const char * 260afa8e06SEd Maste opt2str(fido_opt_t v) 270afa8e06SEd Maste { 280afa8e06SEd Maste switch (v) { 290afa8e06SEd Maste case FIDO_OPT_OMIT: 300afa8e06SEd Maste return "omit"; 310afa8e06SEd Maste case FIDO_OPT_TRUE: 320afa8e06SEd Maste return "true"; 330afa8e06SEd Maste case FIDO_OPT_FALSE: 340afa8e06SEd Maste return "false"; 350afa8e06SEd Maste default: 360afa8e06SEd Maste return "unknown"; 370afa8e06SEd Maste } 380afa8e06SEd Maste } 390afa8e06SEd Maste 400afa8e06SEd Maste static void 410afa8e06SEd Maste parse_toggle(const char *str, struct toggle *opt) 420afa8e06SEd Maste { 430afa8e06SEd Maste fido_opt_t *k; 440afa8e06SEd Maste fido_opt_t v; 450afa8e06SEd Maste char *assignment; 460afa8e06SEd Maste char *key; 470afa8e06SEd Maste char *val; 480afa8e06SEd Maste 490afa8e06SEd Maste if ((assignment = strdup(str)) == NULL) 500afa8e06SEd Maste err(1, "strdup"); 510afa8e06SEd Maste if ((val = strchr(assignment, '=')) == NULL) 520afa8e06SEd Maste errx(1, "invalid assignment '%s'", assignment); 530afa8e06SEd Maste 540afa8e06SEd Maste key = assignment; 550afa8e06SEd Maste *val++ = '\0'; 560afa8e06SEd Maste 570afa8e06SEd Maste if (!strcmp(val, "true")) 580afa8e06SEd Maste v = FIDO_OPT_TRUE; 590afa8e06SEd Maste else if (!strcmp(val, "false")) 600afa8e06SEd Maste v = FIDO_OPT_FALSE; 610afa8e06SEd Maste else 620afa8e06SEd Maste errx(1, "unknown value '%s'", val); 630afa8e06SEd Maste 640afa8e06SEd Maste if (!strcmp(key, "up")) 650afa8e06SEd Maste k = &opt->up; 660afa8e06SEd Maste else if (!strcmp(key, "uv")) 670afa8e06SEd Maste k = &opt->uv; 680afa8e06SEd Maste else if (!strcmp(key, "pin")) 690afa8e06SEd Maste k = &opt->pin; 700afa8e06SEd Maste else 710afa8e06SEd Maste errx(1, "unknown key '%s'", key); 720afa8e06SEd Maste 730afa8e06SEd Maste free(assignment); 740afa8e06SEd Maste 750afa8e06SEd Maste *k = v; 760afa8e06SEd Maste } 770afa8e06SEd Maste 780afa8e06SEd Maste static fido_assert_t * 790afa8e06SEd Maste prepare_assert(FILE *in_f, int flags, const struct toggle *opt) 800afa8e06SEd Maste { 810afa8e06SEd Maste fido_assert_t *assert = NULL; 820afa8e06SEd Maste struct blob cdh; 830afa8e06SEd Maste struct blob id; 840afa8e06SEd Maste struct blob hmac_salt; 850afa8e06SEd Maste char *rpid = NULL; 860afa8e06SEd Maste int r; 870afa8e06SEd Maste 880afa8e06SEd Maste memset(&cdh, 0, sizeof(cdh)); 890afa8e06SEd Maste memset(&id, 0, sizeof(id)); 900afa8e06SEd Maste memset(&hmac_salt, 0, sizeof(hmac_salt)); 910afa8e06SEd Maste 920afa8e06SEd Maste r = base64_read(in_f, &cdh); 930afa8e06SEd Maste r |= string_read(in_f, &rpid); 940afa8e06SEd Maste if ((flags & FLAG_RK) == 0) 950afa8e06SEd Maste r |= base64_read(in_f, &id); 960afa8e06SEd Maste if (flags & FLAG_HMAC) 970afa8e06SEd Maste r |= base64_read(in_f, &hmac_salt); 980afa8e06SEd Maste if (r < 0) 990afa8e06SEd Maste errx(1, "input error"); 1000afa8e06SEd Maste 1010afa8e06SEd Maste if (flags & FLAG_DEBUG) { 102*60a517b6SEd Maste fprintf(stderr, "client data%s:\n", 103*60a517b6SEd Maste flags & FLAG_CD ? "" : " hash"); 1040afa8e06SEd Maste xxd(cdh.ptr, cdh.len); 1050afa8e06SEd Maste fprintf(stderr, "relying party id: %s\n", rpid); 1060afa8e06SEd Maste if ((flags & FLAG_RK) == 0) { 1070afa8e06SEd Maste fprintf(stderr, "credential id:\n"); 1080afa8e06SEd Maste xxd(id.ptr, id.len); 1090afa8e06SEd Maste } 1100afa8e06SEd Maste fprintf(stderr, "up=%s\n", opt2str(opt->up)); 1110afa8e06SEd Maste fprintf(stderr, "uv=%s\n", opt2str(opt->uv)); 1120afa8e06SEd Maste fprintf(stderr, "pin=%s\n", opt2str(opt->pin)); 1130afa8e06SEd Maste } 1140afa8e06SEd Maste 1150afa8e06SEd Maste if ((assert = fido_assert_new()) == NULL) 1160afa8e06SEd Maste errx(1, "fido_assert_new"); 1170afa8e06SEd Maste 118*60a517b6SEd Maste if (flags & FLAG_CD) 119*60a517b6SEd Maste r = fido_assert_set_clientdata(assert, cdh.ptr, cdh.len); 120*60a517b6SEd Maste else 121*60a517b6SEd Maste r = fido_assert_set_clientdata_hash(assert, cdh.ptr, cdh.len); 122*60a517b6SEd Maste 123*60a517b6SEd Maste if (r != FIDO_OK || (r = fido_assert_set_rp(assert, rpid)) != FIDO_OK) 1240afa8e06SEd Maste errx(1, "fido_assert_set: %s", fido_strerr(r)); 1250afa8e06SEd Maste if ((r = fido_assert_set_up(assert, opt->up)) != FIDO_OK) 1260afa8e06SEd Maste errx(1, "fido_assert_set_up: %s", fido_strerr(r)); 1270afa8e06SEd Maste if ((r = fido_assert_set_uv(assert, opt->uv)) != FIDO_OK) 1280afa8e06SEd Maste errx(1, "fido_assert_set_uv: %s", fido_strerr(r)); 1290afa8e06SEd Maste 1300afa8e06SEd Maste if (flags & FLAG_HMAC) { 1310afa8e06SEd Maste if ((r = fido_assert_set_extensions(assert, 1320afa8e06SEd Maste FIDO_EXT_HMAC_SECRET)) != FIDO_OK) 1330afa8e06SEd Maste errx(1, "fido_assert_set_extensions: %s", 1340afa8e06SEd Maste fido_strerr(r)); 1350afa8e06SEd Maste if ((r = fido_assert_set_hmac_salt(assert, hmac_salt.ptr, 1360afa8e06SEd Maste hmac_salt.len)) != FIDO_OK) 1370afa8e06SEd Maste errx(1, "fido_assert_set_hmac_salt: %s", 1380afa8e06SEd Maste fido_strerr(r)); 1390afa8e06SEd Maste } 1400afa8e06SEd Maste if (flags & FLAG_LARGEBLOB) { 1410afa8e06SEd Maste if ((r = fido_assert_set_extensions(assert, 1420afa8e06SEd Maste FIDO_EXT_LARGEBLOB_KEY)) != FIDO_OK) 1430afa8e06SEd Maste errx(1, "fido_assert_set_extensions: %s", fido_strerr(r)); 1440afa8e06SEd Maste } 1450afa8e06SEd Maste if ((flags & FLAG_RK) == 0) { 1460afa8e06SEd Maste if ((r = fido_assert_allow_cred(assert, id.ptr, 1470afa8e06SEd Maste id.len)) != FIDO_OK) 1480afa8e06SEd Maste errx(1, "fido_assert_allow_cred: %s", fido_strerr(r)); 1490afa8e06SEd Maste } 1500afa8e06SEd Maste 1510afa8e06SEd Maste free(hmac_salt.ptr); 1520afa8e06SEd Maste free(cdh.ptr); 1530afa8e06SEd Maste free(id.ptr); 1540afa8e06SEd Maste free(rpid); 1550afa8e06SEd Maste 1560afa8e06SEd Maste return (assert); 1570afa8e06SEd Maste } 1580afa8e06SEd Maste 1590afa8e06SEd Maste static void 1600afa8e06SEd Maste print_assert(FILE *out_f, const fido_assert_t *assert, size_t idx, int flags) 1610afa8e06SEd Maste { 1620afa8e06SEd Maste char *cdh = NULL; 1630afa8e06SEd Maste char *authdata = NULL; 1640afa8e06SEd Maste char *sig = NULL; 1650afa8e06SEd Maste char *user_id = NULL; 1660afa8e06SEd Maste char *hmac_secret = NULL; 1670afa8e06SEd Maste char *key = NULL; 1680afa8e06SEd Maste int r; 1690afa8e06SEd Maste 1700afa8e06SEd Maste r = base64_encode(fido_assert_clientdata_hash_ptr(assert), 1710afa8e06SEd Maste fido_assert_clientdata_hash_len(assert), &cdh); 1720afa8e06SEd Maste r |= base64_encode(fido_assert_authdata_ptr(assert, idx), 1730afa8e06SEd Maste fido_assert_authdata_len(assert, 0), &authdata); 1740afa8e06SEd Maste r |= base64_encode(fido_assert_sig_ptr(assert, idx), 1750afa8e06SEd Maste fido_assert_sig_len(assert, idx), &sig); 1760afa8e06SEd Maste if (flags & FLAG_RK) 1770afa8e06SEd Maste r |= base64_encode(fido_assert_user_id_ptr(assert, idx), 1780afa8e06SEd Maste fido_assert_user_id_len(assert, idx), &user_id); 1790afa8e06SEd Maste if (flags & FLAG_HMAC) 1800afa8e06SEd Maste r |= base64_encode(fido_assert_hmac_secret_ptr(assert, idx), 1810afa8e06SEd Maste fido_assert_hmac_secret_len(assert, idx), &hmac_secret); 1820afa8e06SEd Maste if (flags & FLAG_LARGEBLOB) 1830afa8e06SEd Maste r |= base64_encode(fido_assert_largeblob_key_ptr(assert, idx), 1840afa8e06SEd Maste fido_assert_largeblob_key_len(assert, idx), &key); 1850afa8e06SEd Maste if (r < 0) 1860afa8e06SEd Maste errx(1, "output error"); 1870afa8e06SEd Maste 1880afa8e06SEd Maste fprintf(out_f, "%s\n", cdh); 1890afa8e06SEd Maste fprintf(out_f, "%s\n", fido_assert_rp_id(assert)); 1900afa8e06SEd Maste fprintf(out_f, "%s\n", authdata); 1910afa8e06SEd Maste fprintf(out_f, "%s\n", sig); 1920afa8e06SEd Maste if (flags & FLAG_RK) 1930afa8e06SEd Maste fprintf(out_f, "%s\n", user_id); 1940afa8e06SEd Maste if (hmac_secret) { 1950afa8e06SEd Maste fprintf(out_f, "%s\n", hmac_secret); 1960afa8e06SEd Maste explicit_bzero(hmac_secret, strlen(hmac_secret)); 1970afa8e06SEd Maste } 1980afa8e06SEd Maste if (key) { 1990afa8e06SEd Maste fprintf(out_f, "%s\n", key); 2000afa8e06SEd Maste explicit_bzero(key, strlen(key)); 2010afa8e06SEd Maste } 2020afa8e06SEd Maste 2030afa8e06SEd Maste free(key); 2040afa8e06SEd Maste free(hmac_secret); 2050afa8e06SEd Maste free(cdh); 2060afa8e06SEd Maste free(authdata); 2070afa8e06SEd Maste free(sig); 2080afa8e06SEd Maste free(user_id); 2090afa8e06SEd Maste } 2100afa8e06SEd Maste 2110afa8e06SEd Maste int 2120afa8e06SEd Maste assert_get(int argc, char **argv) 2130afa8e06SEd Maste { 2140afa8e06SEd Maste fido_dev_t *dev = NULL; 2150afa8e06SEd Maste fido_assert_t *assert = NULL; 2160afa8e06SEd Maste struct toggle opt; 2170afa8e06SEd Maste char prompt[1024]; 2182ccfa855SEd Maste char pin[128]; 2190afa8e06SEd Maste char *in_path = NULL; 2200afa8e06SEd Maste char *out_path = NULL; 2210afa8e06SEd Maste FILE *in_f = NULL; 2220afa8e06SEd Maste FILE *out_f = NULL; 2230afa8e06SEd Maste int flags = 0; 2240afa8e06SEd Maste int ch; 2250afa8e06SEd Maste int r; 2260afa8e06SEd Maste 2270afa8e06SEd Maste opt.up = opt.uv = opt.pin = FIDO_OPT_OMIT; 2280afa8e06SEd Maste 229*60a517b6SEd Maste while ((ch = getopt(argc, argv, "bdhi:o:prt:uvw")) != -1) { 2300afa8e06SEd Maste switch (ch) { 2310afa8e06SEd Maste case 'b': 2320afa8e06SEd Maste flags |= FLAG_LARGEBLOB; 2330afa8e06SEd Maste break; 2340afa8e06SEd Maste case 'd': 2350afa8e06SEd Maste flags |= FLAG_DEBUG; 2360afa8e06SEd Maste break; 2370afa8e06SEd Maste case 'h': 2380afa8e06SEd Maste flags |= FLAG_HMAC; 2390afa8e06SEd Maste break; 2400afa8e06SEd Maste case 'i': 2410afa8e06SEd Maste in_path = optarg; 2420afa8e06SEd Maste break; 2430afa8e06SEd Maste case 'o': 2440afa8e06SEd Maste out_path = optarg; 2450afa8e06SEd Maste break; 2460afa8e06SEd Maste case 'p': 2470afa8e06SEd Maste opt.up = FIDO_OPT_TRUE; 2480afa8e06SEd Maste break; 2490afa8e06SEd Maste case 'r': 2500afa8e06SEd Maste flags |= FLAG_RK; 2510afa8e06SEd Maste break; 2520afa8e06SEd Maste case 't' : 2530afa8e06SEd Maste parse_toggle(optarg, &opt); 2540afa8e06SEd Maste break; 2550afa8e06SEd Maste case 'u': 2560afa8e06SEd Maste flags |= FLAG_U2F; 2570afa8e06SEd Maste break; 2580afa8e06SEd Maste case 'v': 2590afa8e06SEd Maste /* -v implies both pin and uv for historical reasons */ 2600afa8e06SEd Maste opt.pin = FIDO_OPT_TRUE; 2610afa8e06SEd Maste opt.uv = FIDO_OPT_TRUE; 2620afa8e06SEd Maste break; 263*60a517b6SEd Maste case 'w': 264*60a517b6SEd Maste flags |= FLAG_CD; 265*60a517b6SEd Maste break; 2660afa8e06SEd Maste default: 2670afa8e06SEd Maste usage(); 2680afa8e06SEd Maste } 2690afa8e06SEd Maste } 2700afa8e06SEd Maste 2710afa8e06SEd Maste argc -= optind; 2720afa8e06SEd Maste argv += optind; 2730afa8e06SEd Maste 2740afa8e06SEd Maste if (argc < 1) 2750afa8e06SEd Maste usage(); 2760afa8e06SEd Maste 2770afa8e06SEd Maste in_f = open_read(in_path); 2780afa8e06SEd Maste out_f = open_write(out_path); 2790afa8e06SEd Maste 2800afa8e06SEd Maste fido_init((flags & FLAG_DEBUG) ? FIDO_DEBUG : 0); 2810afa8e06SEd Maste 2820afa8e06SEd Maste assert = prepare_assert(in_f, flags, &opt); 2830afa8e06SEd Maste 2840afa8e06SEd Maste dev = open_dev(argv[0]); 2850afa8e06SEd Maste if (flags & FLAG_U2F) 2860afa8e06SEd Maste fido_dev_force_u2f(dev); 2870afa8e06SEd Maste 2880afa8e06SEd Maste if (opt.pin == FIDO_OPT_TRUE) { 2890afa8e06SEd Maste r = snprintf(prompt, sizeof(prompt), "Enter PIN for %s: ", 2900afa8e06SEd Maste argv[0]); 2910afa8e06SEd Maste if (r < 0 || (size_t)r >= sizeof(prompt)) 2920afa8e06SEd Maste errx(1, "snprintf"); 2930afa8e06SEd Maste if (!readpassphrase(prompt, pin, sizeof(pin), RPP_ECHO_OFF)) 2940afa8e06SEd Maste errx(1, "readpassphrase"); 2952ccfa855SEd Maste if (strlen(pin) < 4 || strlen(pin) > 63) { 2962ccfa855SEd Maste explicit_bzero(pin, sizeof(pin)); 2972ccfa855SEd Maste errx(1, "invalid PIN length"); 2982ccfa855SEd Maste } 2990afa8e06SEd Maste r = fido_dev_get_assert(dev, assert, pin); 3000afa8e06SEd Maste } else 3010afa8e06SEd Maste r = fido_dev_get_assert(dev, assert, NULL); 3020afa8e06SEd Maste 3030afa8e06SEd Maste explicit_bzero(pin, sizeof(pin)); 3040afa8e06SEd Maste 3050afa8e06SEd Maste if (r != FIDO_OK) 3060afa8e06SEd Maste errx(1, "fido_dev_get_assert: %s", fido_strerr(r)); 3070afa8e06SEd Maste 3080afa8e06SEd Maste if (flags & FLAG_RK) { 3090afa8e06SEd Maste for (size_t idx = 0; idx < fido_assert_count(assert); idx++) 3100afa8e06SEd Maste print_assert(out_f, assert, idx, flags); 3110afa8e06SEd Maste } else { 3120afa8e06SEd Maste if (fido_assert_count(assert) != 1) 3130afa8e06SEd Maste errx(1, "fido_assert_count: %zu", 3140afa8e06SEd Maste fido_assert_count(assert)); 3150afa8e06SEd Maste print_assert(out_f, assert, 0, flags); 3160afa8e06SEd Maste } 3170afa8e06SEd Maste 3180afa8e06SEd Maste fido_dev_close(dev); 3190afa8e06SEd Maste fido_dev_free(&dev); 3200afa8e06SEd Maste fido_assert_free(&assert); 3210afa8e06SEd Maste 3220afa8e06SEd Maste fclose(in_f); 3230afa8e06SEd Maste fclose(out_f); 3240afa8e06SEd Maste in_f = NULL; 3250afa8e06SEd Maste out_f = NULL; 3260afa8e06SEd Maste 3270afa8e06SEd Maste exit(0); 3280afa8e06SEd Maste } 329