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 static fido_cred_t * 20 prepare_cred(FILE *in_f, int type, int flags) 21 { 22 fido_cred_t *cred = NULL; 23 struct blob cdh; 24 struct blob uid; 25 char *rpid = NULL; 26 char *uname = NULL; 27 int r; 28 29 memset(&cdh, 0, sizeof(cdh)); 30 memset(&uid, 0, sizeof(uid)); 31 32 r = base64_read(in_f, &cdh); 33 r |= string_read(in_f, &rpid); 34 r |= string_read(in_f, &uname); 35 r |= base64_read(in_f, &uid); 36 if (r < 0) 37 errx(1, "input error"); 38 39 if (flags & FLAG_DEBUG) { 40 fprintf(stderr, "client data hash:\n"); 41 xxd(cdh.ptr, cdh.len); 42 fprintf(stderr, "relying party id: %s\n", rpid); 43 fprintf(stderr, "user name: %s\n", uname); 44 fprintf(stderr, "user id:\n"); 45 xxd(uid.ptr, uid.len); 46 } 47 48 if ((cred = fido_cred_new()) == NULL) 49 errx(1, "fido_cred_new"); 50 51 if ((r = fido_cred_set_type(cred, type)) != FIDO_OK || 52 (r = fido_cred_set_clientdata_hash(cred, cdh.ptr, 53 cdh.len)) != FIDO_OK || 54 (r = fido_cred_set_rp(cred, rpid, NULL)) != FIDO_OK || 55 (r = fido_cred_set_user(cred, uid.ptr, uid.len, uname, NULL, 56 NULL)) != FIDO_OK) 57 errx(1, "fido_cred_set: %s", fido_strerr(r)); 58 59 if (flags & FLAG_RK) { 60 if ((r = fido_cred_set_rk(cred, FIDO_OPT_TRUE)) != FIDO_OK) 61 errx(1, "fido_cred_set_rk: %s", fido_strerr(r)); 62 } 63 if (flags & FLAG_UV) { 64 if ((r = fido_cred_set_uv(cred, FIDO_OPT_TRUE)) != FIDO_OK) 65 errx(1, "fido_cred_set_uv: %s", fido_strerr(r)); 66 } 67 if (flags & FLAG_HMAC) { 68 if ((r = fido_cred_set_extensions(cred, 69 FIDO_EXT_HMAC_SECRET)) != FIDO_OK) 70 errx(1, "fido_cred_set_extensions: %s", fido_strerr(r)); 71 } 72 if (flags & FLAG_LARGEBLOB) { 73 if ((r = fido_cred_set_extensions(cred, 74 FIDO_EXT_LARGEBLOB_KEY)) != FIDO_OK) 75 errx(1, "fido_cred_set_extensions: %s", fido_strerr(r)); 76 } 77 78 free(cdh.ptr); 79 free(uid.ptr); 80 free(rpid); 81 free(uname); 82 83 return (cred); 84 } 85 86 static void 87 print_attcred(FILE *out_f, const fido_cred_t *cred) 88 { 89 char *cdh = NULL; 90 char *authdata = NULL; 91 char *id = NULL; 92 char *sig = NULL; 93 char *x5c = NULL; 94 char *key = NULL; 95 int r; 96 97 r = base64_encode(fido_cred_clientdata_hash_ptr(cred), 98 fido_cred_clientdata_hash_len(cred), &cdh); 99 r |= base64_encode(fido_cred_authdata_ptr(cred), 100 fido_cred_authdata_len(cred), &authdata); 101 r |= base64_encode(fido_cred_id_ptr(cred), fido_cred_id_len(cred), 102 &id); 103 r |= base64_encode(fido_cred_sig_ptr(cred), fido_cred_sig_len(cred), 104 &sig); 105 if (fido_cred_x5c_ptr(cred) != NULL) 106 r |= base64_encode(fido_cred_x5c_ptr(cred), 107 fido_cred_x5c_len(cred), &x5c); 108 if (fido_cred_largeblob_key_ptr(cred) != NULL) 109 r |= base64_encode(fido_cred_largeblob_key_ptr(cred), 110 fido_cred_largeblob_key_len(cred), &key); 111 if (r < 0) 112 errx(1, "output error"); 113 114 fprintf(out_f, "%s\n", cdh); 115 fprintf(out_f, "%s\n", fido_cred_rp_id(cred)); 116 fprintf(out_f, "%s\n", fido_cred_fmt(cred)); 117 fprintf(out_f, "%s\n", authdata); 118 fprintf(out_f, "%s\n", id); 119 fprintf(out_f, "%s\n", sig); 120 if (x5c != NULL) 121 fprintf(out_f, "%s\n", x5c); 122 if (key != NULL) { 123 fprintf(out_f, "%s\n", key); 124 explicit_bzero(key, strlen(key)); 125 } 126 127 free(cdh); 128 free(authdata); 129 free(id); 130 free(sig); 131 free(x5c); 132 free(key); 133 } 134 135 int 136 cred_make(int argc, char **argv) 137 { 138 fido_dev_t *dev = NULL; 139 fido_cred_t *cred = NULL; 140 char prompt[1024]; 141 char pin[128]; 142 char *in_path = NULL; 143 char *out_path = NULL; 144 FILE *in_f = NULL; 145 FILE *out_f = NULL; 146 int type = COSE_ES256; 147 int flags = 0; 148 int cred_protect = -1; 149 int ch; 150 int r; 151 152 while ((ch = getopt(argc, argv, "bc:dhi:o:qruv")) != -1) { 153 switch (ch) { 154 case 'b': 155 flags |= FLAG_LARGEBLOB; 156 break; 157 case 'c': 158 if ((cred_protect = base10(optarg)) < 0) 159 errx(1, "-c: invalid argument '%s'", optarg); 160 break; 161 case 'd': 162 flags |= FLAG_DEBUG; 163 break; 164 case 'h': 165 flags |= FLAG_HMAC; 166 break; 167 case 'i': 168 in_path = optarg; 169 break; 170 case 'o': 171 out_path = optarg; 172 break; 173 case 'q': 174 flags |= FLAG_QUIET; 175 break; 176 case 'r': 177 flags |= FLAG_RK; 178 break; 179 case 'u': 180 flags |= FLAG_U2F; 181 break; 182 case 'v': 183 flags |= FLAG_UV; 184 break; 185 default: 186 usage(); 187 } 188 } 189 190 argc -= optind; 191 argv += optind; 192 193 if (argc < 1 || argc > 2) 194 usage(); 195 196 in_f = open_read(in_path); 197 out_f = open_write(out_path); 198 199 if (argc > 1 && cose_type(argv[1], &type) < 0) 200 errx(1, "unknown type %s", argv[1]); 201 202 fido_init((flags & FLAG_DEBUG) ? FIDO_DEBUG : 0); 203 204 cred = prepare_cred(in_f, type, flags); 205 206 dev = open_dev(argv[0]); 207 if (flags & FLAG_U2F) 208 fido_dev_force_u2f(dev); 209 210 if (cred_protect > 0) { 211 r = fido_cred_set_prot(cred, cred_protect); 212 if (r != FIDO_OK) { 213 errx(1, "fido_cred_set_prot: %s", fido_strerr(r)); 214 } 215 } 216 217 r = fido_dev_make_cred(dev, cred, NULL); 218 if (r == FIDO_ERR_PIN_REQUIRED && !(flags & FLAG_QUIET)) { 219 r = snprintf(prompt, sizeof(prompt), "Enter PIN for %s: ", 220 argv[0]); 221 if (r < 0 || (size_t)r >= sizeof(prompt)) 222 errx(1, "snprintf"); 223 if (!readpassphrase(prompt, pin, sizeof(pin), RPP_ECHO_OFF)) 224 errx(1, "readpassphrase"); 225 if (strlen(pin) < 4 || strlen(pin) > 63) { 226 explicit_bzero(pin, sizeof(pin)); 227 errx(1, "invalid PIN length"); 228 } 229 r = fido_dev_make_cred(dev, cred, pin); 230 } 231 232 explicit_bzero(pin, sizeof(pin)); 233 if (r != FIDO_OK) 234 errx(1, "fido_dev_make_cred: %s", fido_strerr(r)); 235 print_attcred(out_f, cred); 236 237 fido_dev_close(dev); 238 fido_dev_free(&dev); 239 fido_cred_free(&cred); 240 241 fclose(in_f); 242 fclose(out_f); 243 in_f = NULL; 244 out_f = NULL; 245 246 exit(0); 247 } 248