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