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