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 <openssl/ec.h> 8 #include <openssl/pem.h> 9 10 #include <errno.h> 11 #include <stdbool.h> 12 #include <stdio.h> 13 #include <stdlib.h> 14 #include <string.h> 15 #ifdef HAVE_UNISTD_H 16 #include <unistd.h> 17 #endif 18 19 #include "fido.h" 20 #include "extern.h" 21 #include "../openbsd-compat/openbsd-compat.h" 22 23 static const unsigned char cdh[32] = { 24 0xf9, 0x64, 0x57, 0xe7, 0x2d, 0x97, 0xf6, 0xbb, 25 0xdd, 0xd7, 0xfb, 0x06, 0x37, 0x62, 0xea, 0x26, 26 0x20, 0x44, 0x8e, 0x69, 0x7c, 0x03, 0xf2, 0x31, 27 0x2f, 0x99, 0xdc, 0xaf, 0x3e, 0x8a, 0x91, 0x6b, 28 }; 29 30 static const unsigned char user_id[32] = { 31 0x78, 0x1c, 0x78, 0x60, 0xad, 0x88, 0xd2, 0x63, 32 0x32, 0x62, 0x2a, 0xf1, 0x74, 0x5d, 0xed, 0xb2, 33 0xe7, 0xa4, 0x2b, 0x44, 0x89, 0x29, 0x39, 0xc5, 34 0x56, 0x64, 0x01, 0x27, 0x0d, 0xbb, 0xc4, 0x49, 35 }; 36 37 static void 38 usage(void) 39 { 40 fprintf(stderr, "usage: cred [-t ecdsa|rsa|eddsa] [-k pubkey] " 41 "[-ei cred_id] [-P pin] [-T seconds] [-hruv] <device>\n"); 42 exit(EXIT_FAILURE); 43 } 44 45 static void 46 verify_cred(int type, const char *fmt, const unsigned char *authdata_ptr, 47 size_t authdata_len, const unsigned char *x509_ptr, size_t x509_len, 48 const unsigned char *sig_ptr, size_t sig_len, bool rk, bool uv, int ext, 49 const char *key_out, const char *id_out) 50 { 51 fido_cred_t *cred; 52 int r; 53 54 if ((cred = fido_cred_new()) == NULL) 55 errx(1, "fido_cred_new"); 56 57 /* type */ 58 r = fido_cred_set_type(cred, type); 59 if (r != FIDO_OK) 60 errx(1, "fido_cred_set_type: %s (0x%x)", fido_strerr(r), r); 61 62 /* client data hash */ 63 r = fido_cred_set_clientdata_hash(cred, cdh, sizeof(cdh)); 64 if (r != FIDO_OK) 65 errx(1, "fido_cred_set_clientdata_hash: %s (0x%x)", 66 fido_strerr(r), r); 67 68 /* relying party */ 69 r = fido_cred_set_rp(cred, "localhost", "sweet home localhost"); 70 if (r != FIDO_OK) 71 errx(1, "fido_cred_set_rp: %s (0x%x)", fido_strerr(r), r); 72 73 /* authdata */ 74 r = fido_cred_set_authdata(cred, authdata_ptr, authdata_len); 75 if (r != FIDO_OK) 76 errx(1, "fido_cred_set_authdata: %s (0x%x)", fido_strerr(r), r); 77 78 /* extensions */ 79 r = fido_cred_set_extensions(cred, ext); 80 if (r != FIDO_OK) 81 errx(1, "fido_cred_set_extensions: %s (0x%x)", fido_strerr(r), r); 82 83 /* resident key */ 84 if (rk && (r = fido_cred_set_rk(cred, FIDO_OPT_TRUE)) != FIDO_OK) 85 errx(1, "fido_cred_set_rk: %s (0x%x)", fido_strerr(r), r); 86 87 /* user verification */ 88 if (uv && (r = fido_cred_set_uv(cred, FIDO_OPT_TRUE)) != FIDO_OK) 89 errx(1, "fido_cred_set_uv: %s (0x%x)", fido_strerr(r), r); 90 91 /* x509 */ 92 r = fido_cred_set_x509(cred, x509_ptr, x509_len); 93 if (r != FIDO_OK) 94 errx(1, "fido_cred_set_x509: %s (0x%x)", fido_strerr(r), r); 95 96 /* sig */ 97 r = fido_cred_set_sig(cred, sig_ptr, sig_len); 98 if (r != FIDO_OK) 99 errx(1, "fido_cred_set_sig: %s (0x%x)", fido_strerr(r), r); 100 101 /* fmt */ 102 r = fido_cred_set_fmt(cred, fmt); 103 if (r != FIDO_OK) 104 errx(1, "fido_cred_set_fmt: %s (0x%x)", fido_strerr(r), r); 105 106 r = fido_cred_verify(cred); 107 if (r != FIDO_OK) 108 errx(1, "fido_cred_verify: %s (0x%x)", fido_strerr(r), r); 109 110 if (key_out != NULL) { 111 /* extract the credential pubkey */ 112 if (type == COSE_ES256) { 113 if (write_ec_pubkey(key_out, fido_cred_pubkey_ptr(cred), 114 fido_cred_pubkey_len(cred)) < 0) 115 errx(1, "write_ec_pubkey"); 116 } else if (type == COSE_RS256) { 117 if (write_rsa_pubkey(key_out, fido_cred_pubkey_ptr(cred), 118 fido_cred_pubkey_len(cred)) < 0) 119 errx(1, "write_rsa_pubkey"); 120 } else if (type == COSE_EDDSA) { 121 if (write_eddsa_pubkey(key_out, fido_cred_pubkey_ptr(cred), 122 fido_cred_pubkey_len(cred)) < 0) 123 errx(1, "write_eddsa_pubkey"); 124 } 125 } 126 127 if (id_out != NULL) { 128 /* extract the credential id */ 129 if (write_blob(id_out, fido_cred_id_ptr(cred), 130 fido_cred_id_len(cred)) < 0) 131 errx(1, "write_blob"); 132 } 133 134 fido_cred_free(&cred); 135 } 136 137 static fido_dev_t * 138 open_from_manifest(const fido_dev_info_t *dev_infos, size_t len, 139 const char *path) 140 { 141 size_t i; 142 fido_dev_t *dev; 143 144 for (i = 0; i < len; i++) { 145 const fido_dev_info_t *curr = fido_dev_info_ptr(dev_infos, i); 146 if (path == NULL || 147 strcmp(path, fido_dev_info_path(curr)) == 0) { 148 dev = fido_dev_new_with_info(curr); 149 if (fido_dev_open_with_info(dev) == FIDO_OK) 150 return (dev); 151 fido_dev_free(&dev); 152 } 153 } 154 155 return (NULL); 156 } 157 158 int 159 main(int argc, char **argv) 160 { 161 bool rk = false; 162 bool uv = false; 163 bool u2f = false; 164 fido_dev_t *dev; 165 fido_cred_t *cred = NULL; 166 const char *pin = NULL; 167 const char *key_out = NULL; 168 const char *id_out = NULL; 169 const char *path = NULL; 170 unsigned char *body = NULL; 171 long long seconds = 0; 172 size_t len; 173 int type = COSE_ES256; 174 int ext = 0; 175 int ch; 176 int r; 177 fido_dev_info_t *dev_infos = NULL; 178 size_t dev_infos_len = 0; 179 180 if ((cred = fido_cred_new()) == NULL) 181 errx(1, "fido_cred_new"); 182 183 while ((ch = getopt(argc, argv, "P:T:e:hi:k:rt:uv")) != -1) { 184 switch (ch) { 185 case 'P': 186 pin = optarg; 187 break; 188 case 'T': 189 #ifndef SIGNAL_EXAMPLE 190 (void)seconds; 191 errx(1, "-T not supported"); 192 #else 193 if (base10(optarg, &seconds) < 0) 194 errx(1, "base10: %s", optarg); 195 if (seconds <= 0 || seconds > 30) 196 errx(1, "-T: %s must be in (0,30]", optarg); 197 break; 198 #endif 199 case 'e': 200 if (read_blob(optarg, &body, &len) < 0) 201 errx(1, "read_blob: %s", optarg); 202 r = fido_cred_exclude(cred, body, len); 203 if (r != FIDO_OK) 204 errx(1, "fido_cred_exclude: %s (0x%x)", 205 fido_strerr(r), r); 206 free(body); 207 body = NULL; 208 break; 209 case 'h': 210 ext = FIDO_EXT_HMAC_SECRET; 211 break; 212 case 'i': 213 id_out = optarg; 214 break; 215 case 'k': 216 key_out = optarg; 217 break; 218 case 'r': 219 rk = true; 220 break; 221 case 't': 222 if (strcmp(optarg, "ecdsa") == 0) 223 type = COSE_ES256; 224 else if (strcmp(optarg, "rsa") == 0) 225 type = COSE_RS256; 226 else if (strcmp(optarg, "eddsa") == 0) 227 type = COSE_EDDSA; 228 else 229 errx(1, "unknown type %s", optarg); 230 break; 231 case 'u': 232 u2f = true; 233 break; 234 case 'v': 235 uv = true; 236 break; 237 default: 238 usage(); 239 } 240 } 241 242 fido_init(0); 243 244 argc -= optind; 245 argv += optind; 246 247 if (argc > 1) 248 usage(); 249 dev_infos = fido_dev_info_new(16); 250 fido_dev_info_manifest(dev_infos, 16, &dev_infos_len); 251 if (argc == 1) 252 path = argv[0]; 253 254 if ((dev = open_from_manifest(dev_infos, dev_infos_len, path)) == NULL) 255 errx(1, "open_from_manifest"); 256 257 if (u2f) 258 fido_dev_force_u2f(dev); 259 260 /* type */ 261 r = fido_cred_set_type(cred, type); 262 if (r != FIDO_OK) 263 errx(1, "fido_cred_set_type: %s (0x%x)", fido_strerr(r), r); 264 265 /* client data hash */ 266 r = fido_cred_set_clientdata_hash(cred, cdh, sizeof(cdh)); 267 if (r != FIDO_OK) 268 errx(1, "fido_cred_set_clientdata_hash: %s (0x%x)", 269 fido_strerr(r), r); 270 271 /* relying party */ 272 r = fido_cred_set_rp(cred, "localhost", "sweet home localhost"); 273 if (r != FIDO_OK) 274 errx(1, "fido_cred_set_rp: %s (0x%x)", fido_strerr(r), r); 275 276 /* user */ 277 r = fido_cred_set_user(cred, user_id, sizeof(user_id), "john smith", 278 "jsmith", NULL); 279 if (r != FIDO_OK) 280 errx(1, "fido_cred_set_user: %s (0x%x)", fido_strerr(r), r); 281 282 /* extensions */ 283 r = fido_cred_set_extensions(cred, ext); 284 if (r != FIDO_OK) 285 errx(1, "fido_cred_set_extensions: %s (0x%x)", fido_strerr(r), r); 286 287 /* resident key */ 288 if (rk && (r = fido_cred_set_rk(cred, FIDO_OPT_TRUE)) != FIDO_OK) 289 errx(1, "fido_cred_set_rk: %s (0x%x)", fido_strerr(r), r); 290 291 /* user verification */ 292 if (uv && (r = fido_cred_set_uv(cred, FIDO_OPT_TRUE)) != FIDO_OK) 293 errx(1, "fido_cred_set_uv: %s (0x%x)", fido_strerr(r), r); 294 295 #ifdef SIGNAL_EXAMPLE 296 prepare_signal_handler(SIGINT); 297 if (seconds) { 298 prepare_signal_handler(SIGALRM); 299 alarm((unsigned)seconds); 300 } 301 #endif 302 303 r = fido_dev_make_cred(dev, cred, pin); 304 if (r != FIDO_OK) { 305 #ifdef SIGNAL_EXAMPLE 306 if (got_signal) 307 fido_dev_cancel(dev); 308 #endif 309 errx(1, "fido_makecred: %s (0x%x)", fido_strerr(r), r); 310 } 311 312 r = fido_dev_close(dev); 313 if (r != FIDO_OK) 314 errx(1, "fido_dev_close: %s (0x%x)", fido_strerr(r), r); 315 316 fido_dev_free(&dev); 317 318 /* when verifying, pin implies uv */ 319 if (pin) 320 uv = true; 321 322 verify_cred(type, fido_cred_fmt(cred), fido_cred_authdata_ptr(cred), 323 fido_cred_authdata_len(cred), fido_cred_x5c_ptr(cred), 324 fido_cred_x5c_len(cred), fido_cred_sig_ptr(cred), 325 fido_cred_sig_len(cred), rk, uv, ext, key_out, id_out); 326 327 fido_cred_free(&cred); 328 329 exit(0); 330 } 331