1 1.1 christos /* 2 1.1.1.5 christos * Copyright (c) 2018-2023 Yubico AB. All rights reserved. 3 1.1 christos * Use of this source code is governed by a BSD-style 4 1.1 christos * license that can be found in the LICENSE file. 5 1.1.1.4 christos * SPDX-License-Identifier: BSD-2-Clause 6 1.1 christos */ 7 1.1 christos 8 1.1 christos #include <fido.h> 9 1.1 christos #include <stdio.h> 10 1.1 christos #include <stdlib.h> 11 1.1 christos #include <string.h> 12 1.1 christos #ifdef HAVE_UNISTD_H 13 1.1 christos #include <unistd.h> 14 1.1 christos #endif 15 1.1 christos 16 1.1 christos #include "../openbsd-compat/openbsd-compat.h" 17 1.1 christos #include "extern.h" 18 1.1 christos 19 1.1.1.2 christos struct toggle { 20 1.1.1.2 christos fido_opt_t up; 21 1.1.1.2 christos fido_opt_t uv; 22 1.1.1.2 christos fido_opt_t pin; 23 1.1.1.2 christos }; 24 1.1.1.2 christos 25 1.1.1.2 christos static const char * 26 1.1.1.2 christos opt2str(fido_opt_t v) 27 1.1.1.2 christos { 28 1.1.1.2 christos switch (v) { 29 1.1.1.2 christos case FIDO_OPT_OMIT: 30 1.1.1.2 christos return "omit"; 31 1.1.1.2 christos case FIDO_OPT_TRUE: 32 1.1.1.2 christos return "true"; 33 1.1.1.2 christos case FIDO_OPT_FALSE: 34 1.1.1.2 christos return "false"; 35 1.1.1.2 christos default: 36 1.1.1.2 christos return "unknown"; 37 1.1.1.2 christos } 38 1.1.1.2 christos } 39 1.1.1.2 christos 40 1.1.1.2 christos static void 41 1.1.1.2 christos parse_toggle(const char *str, struct toggle *opt) 42 1.1.1.2 christos { 43 1.1.1.2 christos fido_opt_t *k; 44 1.1.1.2 christos fido_opt_t v; 45 1.1.1.2 christos char *assignment; 46 1.1.1.2 christos char *key; 47 1.1.1.2 christos char *val; 48 1.1.1.2 christos 49 1.1.1.2 christos if ((assignment = strdup(str)) == NULL) 50 1.1.1.2 christos err(1, "strdup"); 51 1.1.1.2 christos if ((val = strchr(assignment, '=')) == NULL) 52 1.1.1.2 christos errx(1, "invalid assignment '%s'", assignment); 53 1.1.1.2 christos 54 1.1.1.2 christos key = assignment; 55 1.1.1.2 christos *val++ = '\0'; 56 1.1.1.2 christos 57 1.1.1.2 christos if (!strcmp(val, "true")) 58 1.1.1.2 christos v = FIDO_OPT_TRUE; 59 1.1.1.2 christos else if (!strcmp(val, "false")) 60 1.1.1.2 christos v = FIDO_OPT_FALSE; 61 1.1.1.2 christos else 62 1.1.1.2 christos errx(1, "unknown value '%s'", val); 63 1.1.1.2 christos 64 1.1.1.2 christos if (!strcmp(key, "up")) 65 1.1.1.2 christos k = &opt->up; 66 1.1.1.2 christos else if (!strcmp(key, "uv")) 67 1.1.1.2 christos k = &opt->uv; 68 1.1.1.2 christos else if (!strcmp(key, "pin")) 69 1.1.1.2 christos k = &opt->pin; 70 1.1.1.2 christos else 71 1.1.1.2 christos errx(1, "unknown key '%s'", key); 72 1.1.1.2 christos 73 1.1.1.2 christos free(assignment); 74 1.1.1.2 christos 75 1.1.1.2 christos *k = v; 76 1.1.1.2 christos } 77 1.1.1.2 christos 78 1.1 christos static fido_assert_t * 79 1.1.1.2 christos prepare_assert(FILE *in_f, int flags, const struct toggle *opt) 80 1.1 christos { 81 1.1 christos fido_assert_t *assert = NULL; 82 1.1 christos struct blob cdh; 83 1.1 christos struct blob id; 84 1.1 christos struct blob hmac_salt; 85 1.1 christos char *rpid = NULL; 86 1.1 christos int r; 87 1.1 christos 88 1.1 christos memset(&cdh, 0, sizeof(cdh)); 89 1.1 christos memset(&id, 0, sizeof(id)); 90 1.1 christos memset(&hmac_salt, 0, sizeof(hmac_salt)); 91 1.1 christos 92 1.1 christos r = base64_read(in_f, &cdh); 93 1.1 christos r |= string_read(in_f, &rpid); 94 1.1 christos if ((flags & FLAG_RK) == 0) 95 1.1 christos r |= base64_read(in_f, &id); 96 1.1 christos if (flags & FLAG_HMAC) 97 1.1 christos r |= base64_read(in_f, &hmac_salt); 98 1.1 christos if (r < 0) 99 1.1 christos errx(1, "input error"); 100 1.1 christos 101 1.1 christos if (flags & FLAG_DEBUG) { 102 1.1.1.5 christos fprintf(stderr, "client data%s:\n", 103 1.1.1.5 christos flags & FLAG_CD ? "" : " hash"); 104 1.1 christos xxd(cdh.ptr, cdh.len); 105 1.1 christos fprintf(stderr, "relying party id: %s\n", rpid); 106 1.1 christos if ((flags & FLAG_RK) == 0) { 107 1.1 christos fprintf(stderr, "credential id:\n"); 108 1.1 christos xxd(id.ptr, id.len); 109 1.1 christos } 110 1.1.1.2 christos fprintf(stderr, "up=%s\n", opt2str(opt->up)); 111 1.1.1.2 christos fprintf(stderr, "uv=%s\n", opt2str(opt->uv)); 112 1.1.1.2 christos fprintf(stderr, "pin=%s\n", opt2str(opt->pin)); 113 1.1 christos } 114 1.1 christos 115 1.1 christos if ((assert = fido_assert_new()) == NULL) 116 1.1 christos errx(1, "fido_assert_new"); 117 1.1 christos 118 1.1.1.5 christos if (flags & FLAG_CD) 119 1.1.1.5 christos r = fido_assert_set_clientdata(assert, cdh.ptr, cdh.len); 120 1.1.1.5 christos else 121 1.1.1.5 christos r = fido_assert_set_clientdata_hash(assert, cdh.ptr, cdh.len); 122 1.1.1.5 christos 123 1.1.1.5 christos if (r != FIDO_OK || (r = fido_assert_set_rp(assert, rpid)) != FIDO_OK) 124 1.1 christos errx(1, "fido_assert_set: %s", fido_strerr(r)); 125 1.1.1.2 christos if ((r = fido_assert_set_up(assert, opt->up)) != FIDO_OK) 126 1.1.1.2 christos errx(1, "fido_assert_set_up: %s", fido_strerr(r)); 127 1.1.1.2 christos if ((r = fido_assert_set_uv(assert, opt->uv)) != FIDO_OK) 128 1.1.1.2 christos errx(1, "fido_assert_set_uv: %s", fido_strerr(r)); 129 1.1 christos 130 1.1 christos if (flags & FLAG_HMAC) { 131 1.1 christos if ((r = fido_assert_set_extensions(assert, 132 1.1 christos FIDO_EXT_HMAC_SECRET)) != FIDO_OK) 133 1.1 christos errx(1, "fido_assert_set_extensions: %s", 134 1.1 christos fido_strerr(r)); 135 1.1 christos if ((r = fido_assert_set_hmac_salt(assert, hmac_salt.ptr, 136 1.1 christos hmac_salt.len)) != FIDO_OK) 137 1.1 christos errx(1, "fido_assert_set_hmac_salt: %s", 138 1.1 christos fido_strerr(r)); 139 1.1 christos } 140 1.1.1.3 christos if (flags & FLAG_LARGEBLOB) { 141 1.1.1.3 christos if ((r = fido_assert_set_extensions(assert, 142 1.1.1.3 christos FIDO_EXT_LARGEBLOB_KEY)) != FIDO_OK) 143 1.1.1.3 christos errx(1, "fido_assert_set_extensions: %s", fido_strerr(r)); 144 1.1.1.3 christos } 145 1.1 christos if ((flags & FLAG_RK) == 0) { 146 1.1 christos if ((r = fido_assert_allow_cred(assert, id.ptr, 147 1.1 christos id.len)) != FIDO_OK) 148 1.1 christos errx(1, "fido_assert_allow_cred: %s", fido_strerr(r)); 149 1.1 christos } 150 1.1 christos 151 1.1 christos free(hmac_salt.ptr); 152 1.1 christos free(cdh.ptr); 153 1.1 christos free(id.ptr); 154 1.1 christos free(rpid); 155 1.1 christos 156 1.1 christos return (assert); 157 1.1 christos } 158 1.1 christos 159 1.1 christos static void 160 1.1 christos print_assert(FILE *out_f, const fido_assert_t *assert, size_t idx, int flags) 161 1.1 christos { 162 1.1 christos char *cdh = NULL; 163 1.1 christos char *authdata = NULL; 164 1.1 christos char *sig = NULL; 165 1.1 christos char *user_id = NULL; 166 1.1 christos char *hmac_secret = NULL; 167 1.1.1.3 christos char *key = NULL; 168 1.1 christos int r; 169 1.1 christos 170 1.1 christos r = base64_encode(fido_assert_clientdata_hash_ptr(assert), 171 1.1 christos fido_assert_clientdata_hash_len(assert), &cdh); 172 1.1 christos r |= base64_encode(fido_assert_authdata_ptr(assert, idx), 173 1.1 christos fido_assert_authdata_len(assert, 0), &authdata); 174 1.1 christos r |= base64_encode(fido_assert_sig_ptr(assert, idx), 175 1.1 christos fido_assert_sig_len(assert, idx), &sig); 176 1.1 christos if (flags & FLAG_RK) 177 1.1 christos r |= base64_encode(fido_assert_user_id_ptr(assert, idx), 178 1.1 christos fido_assert_user_id_len(assert, idx), &user_id); 179 1.1 christos if (flags & FLAG_HMAC) 180 1.1 christos r |= base64_encode(fido_assert_hmac_secret_ptr(assert, idx), 181 1.1 christos fido_assert_hmac_secret_len(assert, idx), &hmac_secret); 182 1.1.1.3 christos if (flags & FLAG_LARGEBLOB) 183 1.1.1.3 christos r |= base64_encode(fido_assert_largeblob_key_ptr(assert, idx), 184 1.1.1.3 christos fido_assert_largeblob_key_len(assert, idx), &key); 185 1.1 christos if (r < 0) 186 1.1 christos errx(1, "output error"); 187 1.1 christos 188 1.1 christos fprintf(out_f, "%s\n", cdh); 189 1.1 christos fprintf(out_f, "%s\n", fido_assert_rp_id(assert)); 190 1.1 christos fprintf(out_f, "%s\n", authdata); 191 1.1 christos fprintf(out_f, "%s\n", sig); 192 1.1 christos if (flags & FLAG_RK) 193 1.1 christos fprintf(out_f, "%s\n", user_id); 194 1.1 christos if (hmac_secret) { 195 1.1 christos fprintf(out_f, "%s\n", hmac_secret); 196 1.1 christos explicit_bzero(hmac_secret, strlen(hmac_secret)); 197 1.1 christos } 198 1.1.1.3 christos if (key) { 199 1.1.1.3 christos fprintf(out_f, "%s\n", key); 200 1.1.1.3 christos explicit_bzero(key, strlen(key)); 201 1.1.1.3 christos } 202 1.1 christos 203 1.1.1.3 christos free(key); 204 1.1 christos free(hmac_secret); 205 1.1 christos free(cdh); 206 1.1 christos free(authdata); 207 1.1 christos free(sig); 208 1.1 christos free(user_id); 209 1.1 christos } 210 1.1 christos 211 1.1 christos int 212 1.1 christos assert_get(int argc, char **argv) 213 1.1 christos { 214 1.1 christos fido_dev_t *dev = NULL; 215 1.1 christos fido_assert_t *assert = NULL; 216 1.1.1.2 christos struct toggle opt; 217 1.1 christos char prompt[1024]; 218 1.1.1.4 christos char pin[128]; 219 1.1 christos char *in_path = NULL; 220 1.1 christos char *out_path = NULL; 221 1.1 christos FILE *in_f = NULL; 222 1.1 christos FILE *out_f = NULL; 223 1.1 christos int flags = 0; 224 1.1 christos int ch; 225 1.1 christos int r; 226 1.1 christos 227 1.1.1.2 christos opt.up = opt.uv = opt.pin = FIDO_OPT_OMIT; 228 1.1.1.2 christos 229 1.1.1.5 christos while ((ch = getopt(argc, argv, "bdhi:o:prt:uvw")) != -1) { 230 1.1 christos switch (ch) { 231 1.1.1.3 christos case 'b': 232 1.1.1.3 christos flags |= FLAG_LARGEBLOB; 233 1.1.1.3 christos break; 234 1.1 christos case 'd': 235 1.1 christos flags |= FLAG_DEBUG; 236 1.1 christos break; 237 1.1 christos case 'h': 238 1.1 christos flags |= FLAG_HMAC; 239 1.1 christos break; 240 1.1 christos case 'i': 241 1.1 christos in_path = optarg; 242 1.1 christos break; 243 1.1 christos case 'o': 244 1.1 christos out_path = optarg; 245 1.1 christos break; 246 1.1 christos case 'p': 247 1.1.1.2 christos opt.up = FIDO_OPT_TRUE; 248 1.1 christos break; 249 1.1 christos case 'r': 250 1.1 christos flags |= FLAG_RK; 251 1.1 christos break; 252 1.1.1.2 christos case 't' : 253 1.1.1.2 christos parse_toggle(optarg, &opt); 254 1.1.1.2 christos break; 255 1.1 christos case 'u': 256 1.1 christos flags |= FLAG_U2F; 257 1.1 christos break; 258 1.1 christos case 'v': 259 1.1.1.2 christos /* -v implies both pin and uv for historical reasons */ 260 1.1.1.2 christos opt.pin = FIDO_OPT_TRUE; 261 1.1.1.2 christos opt.uv = FIDO_OPT_TRUE; 262 1.1 christos break; 263 1.1.1.5 christos case 'w': 264 1.1.1.5 christos flags |= FLAG_CD; 265 1.1.1.5 christos break; 266 1.1 christos default: 267 1.1 christos usage(); 268 1.1 christos } 269 1.1 christos } 270 1.1 christos 271 1.1 christos argc -= optind; 272 1.1 christos argv += optind; 273 1.1 christos 274 1.1 christos if (argc < 1) 275 1.1 christos usage(); 276 1.1 christos 277 1.1 christos in_f = open_read(in_path); 278 1.1 christos out_f = open_write(out_path); 279 1.1 christos 280 1.1 christos fido_init((flags & FLAG_DEBUG) ? FIDO_DEBUG : 0); 281 1.1 christos 282 1.1.1.2 christos assert = prepare_assert(in_f, flags, &opt); 283 1.1 christos 284 1.1 christos dev = open_dev(argv[0]); 285 1.1 christos if (flags & FLAG_U2F) 286 1.1 christos fido_dev_force_u2f(dev); 287 1.1 christos 288 1.1.1.2 christos if (opt.pin == FIDO_OPT_TRUE) { 289 1.1 christos r = snprintf(prompt, sizeof(prompt), "Enter PIN for %s: ", 290 1.1 christos argv[0]); 291 1.1 christos if (r < 0 || (size_t)r >= sizeof(prompt)) 292 1.1 christos errx(1, "snprintf"); 293 1.1 christos if (!readpassphrase(prompt, pin, sizeof(pin), RPP_ECHO_OFF)) 294 1.1 christos errx(1, "readpassphrase"); 295 1.1.1.4 christos if (strlen(pin) < 4 || strlen(pin) > 63) { 296 1.1.1.4 christos explicit_bzero(pin, sizeof(pin)); 297 1.1.1.4 christos errx(1, "invalid PIN length"); 298 1.1.1.4 christos } 299 1.1 christos r = fido_dev_get_assert(dev, assert, pin); 300 1.1 christos } else 301 1.1 christos r = fido_dev_get_assert(dev, assert, NULL); 302 1.1 christos 303 1.1 christos explicit_bzero(pin, sizeof(pin)); 304 1.1 christos 305 1.1 christos if (r != FIDO_OK) 306 1.1 christos errx(1, "fido_dev_get_assert: %s", fido_strerr(r)); 307 1.1 christos 308 1.1 christos if (flags & FLAG_RK) { 309 1.1 christos for (size_t idx = 0; idx < fido_assert_count(assert); idx++) 310 1.1 christos print_assert(out_f, assert, idx, flags); 311 1.1 christos } else { 312 1.1 christos if (fido_assert_count(assert) != 1) 313 1.1 christos errx(1, "fido_assert_count: %zu", 314 1.1 christos fido_assert_count(assert)); 315 1.1 christos print_assert(out_f, assert, 0, flags); 316 1.1 christos } 317 1.1 christos 318 1.1 christos fido_dev_close(dev); 319 1.1 christos fido_dev_free(&dev); 320 1.1 christos fido_assert_free(&assert); 321 1.1 christos 322 1.1 christos fclose(in_f); 323 1.1 christos fclose(out_f); 324 1.1 christos in_f = NULL; 325 1.1 christos out_f = NULL; 326 1.1 christos 327 1.1 christos exit(0); 328 1.1 christos } 329