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