1 /* 2 * Copyright (c) 2018-2024 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] " 40 "[-a mode] [-hruv] " 41 "<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 *attstmt_ptr, size_t attstmt_len, 48 bool rk, bool uv, int ext, int cred_protect, const char *key_out, 49 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 */ 63 r = fido_cred_set_clientdata(cred, cd, sizeof(cd)); 64 if (r != FIDO_OK) 65 errx(1, "fido_cred_set_clientdata: %s (0x%x)", fido_strerr(r), r); 66 67 /* relying party */ 68 r = fido_cred_set_rp(cred, "localhost", "sweet home localhost"); 69 if (r != FIDO_OK) 70 errx(1, "fido_cred_set_rp: %s (0x%x)", fido_strerr(r), r); 71 72 /* authdata */ 73 r = fido_cred_set_authdata(cred, authdata_ptr, authdata_len); 74 if (r != FIDO_OK) 75 errx(1, "fido_cred_set_authdata: %s (0x%x)", fido_strerr(r), r); 76 77 /* extensions */ 78 r = fido_cred_set_extensions(cred, ext); 79 if (r != FIDO_OK) 80 errx(1, "fido_cred_set_extensions: %s (0x%x)", fido_strerr(r), r); 81 82 /* resident key */ 83 if (rk && (r = fido_cred_set_rk(cred, FIDO_OPT_TRUE)) != FIDO_OK) 84 errx(1, "fido_cred_set_rk: %s (0x%x)", fido_strerr(r), r); 85 86 /* user verification */ 87 if (uv && (r = fido_cred_set_uv(cred, FIDO_OPT_TRUE)) != FIDO_OK) 88 errx(1, "fido_cred_set_uv: %s (0x%x)", fido_strerr(r), r); 89 90 /* credProt */ 91 if (cred_protect != 0 && (r = fido_cred_set_prot(cred, 92 cred_protect)) != FIDO_OK) 93 errx(1, "fido_cred_set_prot: %s (0x%x)", fido_strerr(r), r); 94 95 /* fmt */ 96 r = fido_cred_set_fmt(cred, fmt); 97 if (r != FIDO_OK) 98 errx(1, "fido_cred_set_fmt: %s (0x%x)", fido_strerr(r), r); 99 100 if (!strcmp(fido_cred_fmt(cred), "none")) { 101 warnx("no attestation data, skipping credential verification"); 102 goto out; 103 } 104 105 /* attestation statement */ 106 r = fido_cred_set_attstmt(cred, attstmt_ptr, attstmt_len); 107 if (r != FIDO_OK) 108 errx(1, "fido_cred_set_attstmt: %s (0x%x)", fido_strerr(r), r); 109 110 if (fido_cred_x5c_ptr(cred) == NULL) { 111 if ((r = fido_cred_verify_self(cred)) != FIDO_OK) 112 errx(1, "fido_cred_verify_self: %s (0x%x)", fido_strerr(r), r); 113 } else { 114 if ((r = fido_cred_verify(cred)) != FIDO_OK) 115 errx(1, "fido_cred_verify: %s (0x%x)", fido_strerr(r), r); 116 } 117 118 out: 119 if (key_out != NULL) { 120 /* extract the credential pubkey */ 121 if (type == COSE_ES256) { 122 if (write_es256_pubkey(key_out, 123 fido_cred_pubkey_ptr(cred), 124 fido_cred_pubkey_len(cred)) < 0) 125 errx(1, "write_es256_pubkey"); 126 } else if (type == COSE_ES384) { 127 if (write_es384_pubkey(key_out, 128 fido_cred_pubkey_ptr(cred), 129 fido_cred_pubkey_len(cred)) < 0) 130 errx(1, "write_es384_pubkey"); 131 } else if (type == COSE_RS256) { 132 if (write_rs256_pubkey(key_out, 133 fido_cred_pubkey_ptr(cred), 134 fido_cred_pubkey_len(cred)) < 0) 135 errx(1, "write_rs256_pubkey"); 136 } else if (type == COSE_EDDSA) { 137 if (write_eddsa_pubkey(key_out, 138 fido_cred_pubkey_ptr(cred), 139 fido_cred_pubkey_len(cred)) < 0) 140 errx(1, "write_eddsa_pubkey"); 141 } 142 } 143 144 if (id_out != NULL) { 145 /* extract the credential id */ 146 if (write_blob(id_out, fido_cred_id_ptr(cred), 147 fido_cred_id_len(cred)) < 0) 148 errx(1, "write_blob"); 149 } 150 151 fido_cred_free(&cred); 152 } 153 154 int 155 main(int argc, char **argv) 156 { 157 bool rk = false; 158 bool uv = false; 159 bool u2f = false; 160 fido_dev_t *dev; 161 fido_cred_t *cred = NULL; 162 const char *pin = NULL; 163 const char *blobkey_out = NULL; 164 const char *key_out = NULL; 165 const char *id_out = NULL; 166 unsigned char *body = NULL; 167 long long ms = 0; 168 size_t len; 169 int type = COSE_ES256; 170 int ext = 0; 171 int ch; 172 int r; 173 long long cred_protect = 0; 174 long long ea = 0; 175 176 if ((cred = fido_cred_new()) == NULL) 177 errx(1, "fido_cred_new"); 178 179 while ((ch = getopt(argc, argv, "P:T:a:b:e:hi:k:rt:uvc:")) != -1) { 180 switch (ch) { 181 case 'P': 182 pin = optarg; 183 break; 184 case 'T': 185 if (base10(optarg, &ms) < 0) 186 errx(1, "base10: %s", optarg); 187 if (ms <= 0 || ms > 30) 188 errx(1, "-T: %s must be in (0,30]", optarg); 189 ms *= 1000; /* seconds to milliseconds */ 190 break; 191 case 'a': 192 if (base10(optarg, &ea) < 0) 193 errx(1, "base10: %s", optarg); 194 if (ea <= 0 || ea > 2) 195 errx(1, "-a: %s must be in (0,2]", optarg); 196 break; 197 case 'b': 198 ext |= FIDO_EXT_LARGEBLOB_KEY; 199 blobkey_out = optarg; 200 break; 201 case 'e': 202 if (read_blob(optarg, &body, &len) < 0) 203 errx(1, "read_blob: %s", optarg); 204 r = fido_cred_exclude(cred, body, len); 205 if (r != FIDO_OK) 206 errx(1, "fido_cred_exclude: %s (0x%x)", 207 fido_strerr(r), r); 208 free(body); 209 body = NULL; 210 break; 211 case 'h': 212 ext |= FIDO_EXT_HMAC_SECRET; 213 break; 214 case 'c': 215 if (base10(optarg, &cred_protect) < 0) 216 errx(1, "base10: %s", optarg); 217 if (cred_protect <= 0 || cred_protect > 3) 218 errx(1, "-c: %s must be in (0,3]", optarg); 219 ext |= FIDO_EXT_CRED_PROTECT; 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, "es256") == 0) 232 type = COSE_ES256; 233 else if (strcmp(optarg, "es384") == 0) 234 type = COSE_ES384; 235 else if (strcmp(optarg, "rs256") == 0) 236 type = COSE_RS256; 237 else if (strcmp(optarg, "eddsa") == 0) 238 type = COSE_EDDSA; 239 else 240 errx(1, "unknown type %s", optarg); 241 break; 242 case 'u': 243 u2f = true; 244 break; 245 case 'v': 246 uv = true; 247 break; 248 default: 249 usage(); 250 } 251 } 252 253 argc -= optind; 254 argv += optind; 255 256 if (argc != 1) 257 usage(); 258 259 fido_init(0); 260 261 if ((dev = fido_dev_new()) == NULL) 262 errx(1, "fido_dev_new"); 263 264 r = fido_dev_open(dev, argv[0]); 265 if (r != FIDO_OK) 266 errx(1, "fido_dev_open: %s (0x%x)", fido_strerr(r), r); 267 if (u2f) 268 fido_dev_force_u2f(dev); 269 270 /* type */ 271 r = fido_cred_set_type(cred, type); 272 if (r != FIDO_OK) 273 errx(1, "fido_cred_set_type: %s (0x%x)", fido_strerr(r), r); 274 275 /* client data */ 276 r = fido_cred_set_clientdata(cred, cd, sizeof(cd)); 277 if (r != FIDO_OK) 278 errx(1, "fido_cred_set_clientdata: %s (0x%x)", 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 /* credProt */ 305 if (cred_protect != 0 && (r = fido_cred_set_prot(cred, 306 (int)cred_protect)) != FIDO_OK) 307 errx(1, "fido_cred_set_prot: %s (0x%x)", fido_strerr(r), r); 308 309 if (ea != 0 && (r = fido_cred_set_entattest(cred, (int)ea)) != FIDO_OK) 310 errx(1, "fido_cred_set_entattest: %s (0x%x)", fido_strerr(r), r); 311 312 /* timeout */ 313 if (ms != 0 && (r = fido_dev_set_timeout(dev, (int)ms)) != FIDO_OK) 314 errx(1, "fido_dev_set_timeout: %s (0x%x)", fido_strerr(r), r); 315 316 if ((r = fido_dev_make_cred(dev, cred, pin)) != FIDO_OK) { 317 fido_dev_cancel(dev); 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_attstmt_ptr(cred), 333 fido_cred_attstmt_len(cred), rk, uv, ext, fido_cred_prot(cred), 334 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