Home | History | Annotate | Line # | Download | only in examples
      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