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