1 /* 2 * Copyright (c) 2018 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 hash:\n"); 103 xxd(cdh.ptr, cdh.len); 104 fprintf(stderr, "relying party id: %s\n", rpid); 105 if ((flags & FLAG_RK) == 0) { 106 fprintf(stderr, "credential id:\n"); 107 xxd(id.ptr, id.len); 108 } 109 fprintf(stderr, "up=%s\n", opt2str(opt->up)); 110 fprintf(stderr, "uv=%s\n", opt2str(opt->uv)); 111 fprintf(stderr, "pin=%s\n", opt2str(opt->pin)); 112 } 113 114 if ((assert = fido_assert_new()) == NULL) 115 errx(1, "fido_assert_new"); 116 117 if ((r = fido_assert_set_clientdata_hash(assert, cdh.ptr, 118 cdh.len)) != FIDO_OK || 119 (r = fido_assert_set_rp(assert, rpid)) != FIDO_OK) 120 errx(1, "fido_assert_set: %s", fido_strerr(r)); 121 if ((r = fido_assert_set_up(assert, opt->up)) != FIDO_OK) 122 errx(1, "fido_assert_set_up: %s", fido_strerr(r)); 123 if ((r = fido_assert_set_uv(assert, opt->uv)) != FIDO_OK) 124 errx(1, "fido_assert_set_uv: %s", fido_strerr(r)); 125 126 if (flags & FLAG_HMAC) { 127 if ((r = fido_assert_set_extensions(assert, 128 FIDO_EXT_HMAC_SECRET)) != FIDO_OK) 129 errx(1, "fido_assert_set_extensions: %s", 130 fido_strerr(r)); 131 if ((r = fido_assert_set_hmac_salt(assert, hmac_salt.ptr, 132 hmac_salt.len)) != FIDO_OK) 133 errx(1, "fido_assert_set_hmac_salt: %s", 134 fido_strerr(r)); 135 } 136 if (flags & FLAG_LARGEBLOB) { 137 if ((r = fido_assert_set_extensions(assert, 138 FIDO_EXT_LARGEBLOB_KEY)) != FIDO_OK) 139 errx(1, "fido_assert_set_extensions: %s", fido_strerr(r)); 140 } 141 if ((flags & FLAG_RK) == 0) { 142 if ((r = fido_assert_allow_cred(assert, id.ptr, 143 id.len)) != FIDO_OK) 144 errx(1, "fido_assert_allow_cred: %s", fido_strerr(r)); 145 } 146 147 free(hmac_salt.ptr); 148 free(cdh.ptr); 149 free(id.ptr); 150 free(rpid); 151 152 return (assert); 153 } 154 155 static void 156 print_assert(FILE *out_f, const fido_assert_t *assert, size_t idx, int flags) 157 { 158 char *cdh = NULL; 159 char *authdata = NULL; 160 char *sig = NULL; 161 char *user_id = NULL; 162 char *hmac_secret = NULL; 163 char *key = NULL; 164 int r; 165 166 r = base64_encode(fido_assert_clientdata_hash_ptr(assert), 167 fido_assert_clientdata_hash_len(assert), &cdh); 168 r |= base64_encode(fido_assert_authdata_ptr(assert, idx), 169 fido_assert_authdata_len(assert, 0), &authdata); 170 r |= base64_encode(fido_assert_sig_ptr(assert, idx), 171 fido_assert_sig_len(assert, idx), &sig); 172 if (flags & FLAG_RK) 173 r |= base64_encode(fido_assert_user_id_ptr(assert, idx), 174 fido_assert_user_id_len(assert, idx), &user_id); 175 if (flags & FLAG_HMAC) 176 r |= base64_encode(fido_assert_hmac_secret_ptr(assert, idx), 177 fido_assert_hmac_secret_len(assert, idx), &hmac_secret); 178 if (flags & FLAG_LARGEBLOB) 179 r |= base64_encode(fido_assert_largeblob_key_ptr(assert, idx), 180 fido_assert_largeblob_key_len(assert, idx), &key); 181 if (r < 0) 182 errx(1, "output error"); 183 184 fprintf(out_f, "%s\n", cdh); 185 fprintf(out_f, "%s\n", fido_assert_rp_id(assert)); 186 fprintf(out_f, "%s\n", authdata); 187 fprintf(out_f, "%s\n", sig); 188 if (flags & FLAG_RK) 189 fprintf(out_f, "%s\n", user_id); 190 if (hmac_secret) { 191 fprintf(out_f, "%s\n", hmac_secret); 192 explicit_bzero(hmac_secret, strlen(hmac_secret)); 193 } 194 if (key) { 195 fprintf(out_f, "%s\n", key); 196 explicit_bzero(key, strlen(key)); 197 } 198 199 free(key); 200 free(hmac_secret); 201 free(cdh); 202 free(authdata); 203 free(sig); 204 free(user_id); 205 } 206 207 int 208 assert_get(int argc, char **argv) 209 { 210 fido_dev_t *dev = NULL; 211 fido_assert_t *assert = NULL; 212 struct toggle opt; 213 char prompt[1024]; 214 char pin[128]; 215 char *in_path = NULL; 216 char *out_path = NULL; 217 FILE *in_f = NULL; 218 FILE *out_f = NULL; 219 int flags = 0; 220 int ch; 221 int r; 222 223 opt.up = opt.uv = opt.pin = FIDO_OPT_OMIT; 224 225 while ((ch = getopt(argc, argv, "bdhi:o:prt:uv")) != -1) { 226 switch (ch) { 227 case 'b': 228 flags |= FLAG_LARGEBLOB; 229 break; 230 case 'd': 231 flags |= FLAG_DEBUG; 232 break; 233 case 'h': 234 flags |= FLAG_HMAC; 235 break; 236 case 'i': 237 in_path = optarg; 238 break; 239 case 'o': 240 out_path = optarg; 241 break; 242 case 'p': 243 opt.up = FIDO_OPT_TRUE; 244 break; 245 case 'r': 246 flags |= FLAG_RK; 247 break; 248 case 't' : 249 parse_toggle(optarg, &opt); 250 break; 251 case 'u': 252 flags |= FLAG_U2F; 253 break; 254 case 'v': 255 /* -v implies both pin and uv for historical reasons */ 256 opt.pin = FIDO_OPT_TRUE; 257 opt.uv = FIDO_OPT_TRUE; 258 break; 259 default: 260 usage(); 261 } 262 } 263 264 argc -= optind; 265 argv += optind; 266 267 if (argc < 1) 268 usage(); 269 270 in_f = open_read(in_path); 271 out_f = open_write(out_path); 272 273 fido_init((flags & FLAG_DEBUG) ? FIDO_DEBUG : 0); 274 275 assert = prepare_assert(in_f, flags, &opt); 276 277 dev = open_dev(argv[0]); 278 if (flags & FLAG_U2F) 279 fido_dev_force_u2f(dev); 280 281 if (opt.pin == FIDO_OPT_TRUE) { 282 r = snprintf(prompt, sizeof(prompt), "Enter PIN for %s: ", 283 argv[0]); 284 if (r < 0 || (size_t)r >= sizeof(prompt)) 285 errx(1, "snprintf"); 286 if (!readpassphrase(prompt, pin, sizeof(pin), RPP_ECHO_OFF)) 287 errx(1, "readpassphrase"); 288 if (strlen(pin) < 4 || strlen(pin) > 63) { 289 explicit_bzero(pin, sizeof(pin)); 290 errx(1, "invalid PIN length"); 291 } 292 r = fido_dev_get_assert(dev, assert, pin); 293 } else 294 r = fido_dev_get_assert(dev, assert, NULL); 295 296 explicit_bzero(pin, sizeof(pin)); 297 298 if (r != FIDO_OK) 299 errx(1, "fido_dev_get_assert: %s", fido_strerr(r)); 300 301 if (flags & FLAG_RK) { 302 for (size_t idx = 0; idx < fido_assert_count(assert); idx++) 303 print_assert(out_f, assert, idx, flags); 304 } else { 305 if (fido_assert_count(assert) != 1) 306 errx(1, "fido_assert_count: %zu", 307 fido_assert_count(assert)); 308 print_assert(out_f, assert, 0, flags); 309 } 310 311 fido_dev_close(dev); 312 fido_dev_free(&dev); 313 fido_assert_free(&assert); 314 315 fclose(in_f); 316 fclose(out_f); 317 in_f = NULL; 318 out_f = NULL; 319 320 exit(0); 321 } 322