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