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