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