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_RK) == 0) { 136 if ((r = fido_assert_allow_cred(assert, id.ptr, 137 id.len)) != FIDO_OK) 138 errx(1, "fido_assert_allow_cred: %s", fido_strerr(r)); 139 } 140 141 free(hmac_salt.ptr); 142 free(cdh.ptr); 143 free(id.ptr); 144 free(rpid); 145 146 return (assert); 147 } 148 149 static void 150 print_assert(FILE *out_f, const fido_assert_t *assert, size_t idx, int flags) 151 { 152 char *cdh = NULL; 153 char *authdata = NULL; 154 char *sig = NULL; 155 char *user_id = NULL; 156 char *hmac_secret = NULL; 157 int r; 158 159 r = base64_encode(fido_assert_clientdata_hash_ptr(assert), 160 fido_assert_clientdata_hash_len(assert), &cdh); 161 r |= base64_encode(fido_assert_authdata_ptr(assert, idx), 162 fido_assert_authdata_len(assert, 0), &authdata); 163 r |= base64_encode(fido_assert_sig_ptr(assert, idx), 164 fido_assert_sig_len(assert, idx), &sig); 165 if (flags & FLAG_RK) 166 r |= base64_encode(fido_assert_user_id_ptr(assert, idx), 167 fido_assert_user_id_len(assert, idx), &user_id); 168 if (flags & FLAG_HMAC) 169 r |= base64_encode(fido_assert_hmac_secret_ptr(assert, idx), 170 fido_assert_hmac_secret_len(assert, idx), &hmac_secret); 171 if (r < 0) 172 errx(1, "output error"); 173 174 fprintf(out_f, "%s\n", cdh); 175 fprintf(out_f, "%s\n", fido_assert_rp_id(assert)); 176 fprintf(out_f, "%s\n", authdata); 177 fprintf(out_f, "%s\n", sig); 178 if (flags & FLAG_RK) 179 fprintf(out_f, "%s\n", user_id); 180 if (hmac_secret) { 181 fprintf(out_f, "%s\n", hmac_secret); 182 explicit_bzero(hmac_secret, strlen(hmac_secret)); 183 } 184 185 free(hmac_secret); 186 free(cdh); 187 free(authdata); 188 free(sig); 189 free(user_id); 190 } 191 192 int 193 assert_get(int argc, char **argv) 194 { 195 fido_dev_t *dev = NULL; 196 fido_assert_t *assert = NULL; 197 struct toggle opt; 198 char pin[1024]; 199 char prompt[1024]; 200 char *in_path = NULL; 201 char *out_path = NULL; 202 FILE *in_f = NULL; 203 FILE *out_f = NULL; 204 int flags = 0; 205 int ch; 206 int r; 207 208 opt.up = opt.uv = opt.pin = FIDO_OPT_OMIT; 209 210 while ((ch = getopt(argc, argv, "dhi:o:prt:uv")) != -1) { 211 switch (ch) { 212 case 'd': 213 flags |= FLAG_DEBUG; 214 break; 215 case 'h': 216 flags |= FLAG_HMAC; 217 break; 218 case 'i': 219 in_path = optarg; 220 break; 221 case 'o': 222 out_path = optarg; 223 break; 224 case 'p': 225 opt.up = FIDO_OPT_TRUE; 226 break; 227 case 'r': 228 flags |= FLAG_RK; 229 break; 230 case 't' : 231 parse_toggle(optarg, &opt); 232 break; 233 case 'u': 234 flags |= FLAG_U2F; 235 break; 236 case 'v': 237 /* -v implies both pin and uv for historical reasons */ 238 opt.pin = FIDO_OPT_TRUE; 239 opt.uv = FIDO_OPT_TRUE; 240 break; 241 default: 242 usage(); 243 } 244 } 245 246 argc -= optind; 247 argv += optind; 248 249 if (argc < 1) 250 usage(); 251 252 in_f = open_read(in_path); 253 out_f = open_write(out_path); 254 255 fido_init((flags & FLAG_DEBUG) ? FIDO_DEBUG : 0); 256 257 assert = prepare_assert(in_f, flags, &opt); 258 259 dev = open_dev(argv[0]); 260 if (flags & FLAG_U2F) 261 fido_dev_force_u2f(dev); 262 263 if (opt.pin == FIDO_OPT_TRUE) { 264 r = snprintf(prompt, sizeof(prompt), "Enter PIN for %s: ", 265 argv[0]); 266 if (r < 0 || (size_t)r >= sizeof(prompt)) 267 errx(1, "snprintf"); 268 if (!readpassphrase(prompt, pin, sizeof(pin), RPP_ECHO_OFF)) 269 errx(1, "readpassphrase"); 270 r = fido_dev_get_assert(dev, assert, pin); 271 } else 272 r = fido_dev_get_assert(dev, assert, NULL); 273 274 explicit_bzero(pin, sizeof(pin)); 275 276 if (r != FIDO_OK) 277 errx(1, "fido_dev_get_assert: %s", fido_strerr(r)); 278 279 if (flags & FLAG_RK) { 280 for (size_t idx = 0; idx < fido_assert_count(assert); idx++) 281 print_assert(out_f, assert, idx, flags); 282 } else { 283 if (fido_assert_count(assert) != 1) 284 errx(1, "fido_assert_count: %zu", 285 fido_assert_count(assert)); 286 print_assert(out_f, assert, 0, flags); 287 } 288 289 fido_dev_close(dev); 290 fido_dev_free(&dev); 291 fido_assert_free(&assert); 292 293 fclose(in_f); 294 fclose(out_f); 295 in_f = NULL; 296 out_f = NULL; 297 298 exit(0); 299 } 300