xref: /freebsd-src/contrib/libfido2/examples/cred.c (revision f540a43052c12c76d3453ead881248d5467a1ab0)
10afa8e06SEd Maste /*
2*f540a430SEd Maste  * Copyright (c) 2018-2021 Yubico AB. All rights reserved.
30afa8e06SEd Maste  * Use of this source code is governed by a BSD-style
40afa8e06SEd Maste  * license that can be found in the LICENSE file.
50afa8e06SEd Maste  */
60afa8e06SEd Maste 
70afa8e06SEd Maste #include <errno.h>
80afa8e06SEd Maste #include <fido.h>
90afa8e06SEd Maste #include <stdbool.h>
100afa8e06SEd Maste #include <stdio.h>
110afa8e06SEd Maste #include <stdlib.h>
120afa8e06SEd Maste #include <string.h>
130afa8e06SEd Maste #ifdef HAVE_UNISTD_H
140afa8e06SEd Maste #include <unistd.h>
150afa8e06SEd Maste #endif
160afa8e06SEd Maste 
170afa8e06SEd Maste #include "../openbsd-compat/openbsd-compat.h"
180afa8e06SEd Maste #include "extern.h"
190afa8e06SEd Maste 
20*f540a430SEd Maste static const unsigned char cd[32] = {
210afa8e06SEd Maste 	0xf9, 0x64, 0x57, 0xe7, 0x2d, 0x97, 0xf6, 0xbb,
220afa8e06SEd Maste 	0xdd, 0xd7, 0xfb, 0x06, 0x37, 0x62, 0xea, 0x26,
230afa8e06SEd Maste 	0x20, 0x44, 0x8e, 0x69, 0x7c, 0x03, 0xf2, 0x31,
240afa8e06SEd Maste 	0x2f, 0x99, 0xdc, 0xaf, 0x3e, 0x8a, 0x91, 0x6b,
250afa8e06SEd Maste };
260afa8e06SEd Maste 
270afa8e06SEd Maste static const unsigned char user_id[32] = {
280afa8e06SEd Maste 	0x78, 0x1c, 0x78, 0x60, 0xad, 0x88, 0xd2, 0x63,
290afa8e06SEd Maste 	0x32, 0x62, 0x2a, 0xf1, 0x74, 0x5d, 0xed, 0xb2,
300afa8e06SEd Maste 	0xe7, 0xa4, 0x2b, 0x44, 0x89, 0x29, 0x39, 0xc5,
310afa8e06SEd Maste 	0x56, 0x64, 0x01, 0x27, 0x0d, 0xbb, 0xc4, 0x49,
320afa8e06SEd Maste };
330afa8e06SEd Maste 
340afa8e06SEd Maste static void
350afa8e06SEd Maste usage(void)
360afa8e06SEd Maste {
370afa8e06SEd Maste 	fprintf(stderr, "usage: cred [-t ecdsa|rsa|eddsa] [-k pubkey] "
380afa8e06SEd Maste 	    "[-ei cred_id] [-P pin] [-T seconds] [-b blobkey] [-hruv] "
390afa8e06SEd Maste 	    "<device>\n");
400afa8e06SEd Maste 	exit(EXIT_FAILURE);
410afa8e06SEd Maste }
420afa8e06SEd Maste 
430afa8e06SEd Maste static void
440afa8e06SEd Maste verify_cred(int type, const char *fmt, const unsigned char *authdata_ptr,
45*f540a430SEd Maste     size_t authdata_len, const unsigned char *attstmt_ptr, size_t attstmt_len,
46*f540a430SEd Maste     bool rk, bool uv, int ext, const char *key_out, const char *id_out)
470afa8e06SEd Maste {
480afa8e06SEd Maste 	fido_cred_t	*cred;
490afa8e06SEd Maste 	int		 r;
500afa8e06SEd Maste 
510afa8e06SEd Maste 	if ((cred = fido_cred_new()) == NULL)
520afa8e06SEd Maste 		errx(1, "fido_cred_new");
530afa8e06SEd Maste 
540afa8e06SEd Maste 	/* type */
550afa8e06SEd Maste 	r = fido_cred_set_type(cred, type);
560afa8e06SEd Maste 	if (r != FIDO_OK)
570afa8e06SEd Maste 		errx(1, "fido_cred_set_type: %s (0x%x)", fido_strerr(r), r);
580afa8e06SEd Maste 
59*f540a430SEd Maste 	/* client data */
60*f540a430SEd Maste 	r = fido_cred_set_clientdata(cred, cd, sizeof(cd));
610afa8e06SEd Maste 	if (r != FIDO_OK)
62*f540a430SEd Maste 		errx(1, "fido_cred_set_clientdata: %s (0x%x)", fido_strerr(r), r);
630afa8e06SEd Maste 
640afa8e06SEd Maste 	/* relying party */
650afa8e06SEd Maste 	r = fido_cred_set_rp(cred, "localhost", "sweet home localhost");
660afa8e06SEd Maste 	if (r != FIDO_OK)
670afa8e06SEd Maste 		errx(1, "fido_cred_set_rp: %s (0x%x)", fido_strerr(r), r);
680afa8e06SEd Maste 
690afa8e06SEd Maste 	/* authdata */
700afa8e06SEd Maste 	r = fido_cred_set_authdata(cred, authdata_ptr, authdata_len);
710afa8e06SEd Maste 	if (r != FIDO_OK)
720afa8e06SEd Maste 		errx(1, "fido_cred_set_authdata: %s (0x%x)", fido_strerr(r), r);
730afa8e06SEd Maste 
740afa8e06SEd Maste 	/* extensions */
750afa8e06SEd Maste 	r = fido_cred_set_extensions(cred, ext);
760afa8e06SEd Maste 	if (r != FIDO_OK)
770afa8e06SEd Maste 		errx(1, "fido_cred_set_extensions: %s (0x%x)", fido_strerr(r), r);
780afa8e06SEd Maste 
790afa8e06SEd Maste 	/* resident key */
800afa8e06SEd Maste 	if (rk && (r = fido_cred_set_rk(cred, FIDO_OPT_TRUE)) != FIDO_OK)
810afa8e06SEd Maste 		errx(1, "fido_cred_set_rk: %s (0x%x)", fido_strerr(r), r);
820afa8e06SEd Maste 
830afa8e06SEd Maste 	/* user verification */
840afa8e06SEd Maste 	if (uv && (r = fido_cred_set_uv(cred, FIDO_OPT_TRUE)) != FIDO_OK)
850afa8e06SEd Maste 		errx(1, "fido_cred_set_uv: %s (0x%x)", fido_strerr(r), r);
860afa8e06SEd Maste 
870afa8e06SEd Maste 	/* fmt */
880afa8e06SEd Maste 	r = fido_cred_set_fmt(cred, fmt);
890afa8e06SEd Maste 	if (r != FIDO_OK)
900afa8e06SEd Maste 		errx(1, "fido_cred_set_fmt: %s (0x%x)", fido_strerr(r), r);
910afa8e06SEd Maste 
920afa8e06SEd Maste 	if (!strcmp(fido_cred_fmt(cred), "none")) {
930afa8e06SEd Maste 		warnx("no attestation data, skipping credential verification");
940afa8e06SEd Maste 		goto out;
950afa8e06SEd Maste 	}
960afa8e06SEd Maste 
97*f540a430SEd Maste 	/* attestation statement */
98*f540a430SEd Maste 	r = fido_cred_set_attstmt(cred, attstmt_ptr, attstmt_len);
990afa8e06SEd Maste 	if (r != FIDO_OK)
100*f540a430SEd Maste 		errx(1, "fido_cred_set_attstmt: %s (0x%x)", fido_strerr(r), r);
1010afa8e06SEd Maste 
1020afa8e06SEd Maste 	r = fido_cred_verify(cred);
1030afa8e06SEd Maste 	if (r != FIDO_OK)
1040afa8e06SEd Maste 		errx(1, "fido_cred_verify: %s (0x%x)", fido_strerr(r), r);
1050afa8e06SEd Maste 
1060afa8e06SEd Maste out:
1070afa8e06SEd Maste 	if (key_out != NULL) {
1080afa8e06SEd Maste 		/* extract the credential pubkey */
1090afa8e06SEd Maste 		if (type == COSE_ES256) {
1100afa8e06SEd Maste 			if (write_ec_pubkey(key_out, fido_cred_pubkey_ptr(cred),
1110afa8e06SEd Maste 			    fido_cred_pubkey_len(cred)) < 0)
1120afa8e06SEd Maste 				errx(1, "write_ec_pubkey");
1130afa8e06SEd Maste 		} else if (type == COSE_RS256) {
1140afa8e06SEd Maste 			if (write_rsa_pubkey(key_out, fido_cred_pubkey_ptr(cred),
1150afa8e06SEd Maste 			    fido_cred_pubkey_len(cred)) < 0)
1160afa8e06SEd Maste 				errx(1, "write_rsa_pubkey");
1170afa8e06SEd Maste 		} else if (type == COSE_EDDSA) {
1180afa8e06SEd Maste 			if (write_eddsa_pubkey(key_out, fido_cred_pubkey_ptr(cred),
1190afa8e06SEd Maste 			    fido_cred_pubkey_len(cred)) < 0)
1200afa8e06SEd Maste 				errx(1, "write_eddsa_pubkey");
1210afa8e06SEd Maste 		}
1220afa8e06SEd Maste 	}
1230afa8e06SEd Maste 
1240afa8e06SEd Maste 	if (id_out != NULL) {
1250afa8e06SEd Maste 		/* extract the credential id */
1260afa8e06SEd Maste 		if (write_blob(id_out, fido_cred_id_ptr(cred),
1270afa8e06SEd Maste 		    fido_cred_id_len(cred)) < 0)
1280afa8e06SEd Maste 			errx(1, "write_blob");
1290afa8e06SEd Maste 	}
1300afa8e06SEd Maste 
1310afa8e06SEd Maste 	fido_cred_free(&cred);
1320afa8e06SEd Maste }
1330afa8e06SEd Maste 
1340afa8e06SEd Maste int
1350afa8e06SEd Maste main(int argc, char **argv)
1360afa8e06SEd Maste {
1370afa8e06SEd Maste 	bool		 rk = false;
1380afa8e06SEd Maste 	bool		 uv = false;
1390afa8e06SEd Maste 	bool		 u2f = false;
1400afa8e06SEd Maste 	fido_dev_t	*dev;
1410afa8e06SEd Maste 	fido_cred_t	*cred = NULL;
1420afa8e06SEd Maste 	const char	*pin = NULL;
1430afa8e06SEd Maste 	const char	*blobkey_out = NULL;
1440afa8e06SEd Maste 	const char	*key_out = NULL;
1450afa8e06SEd Maste 	const char	*id_out = NULL;
1460afa8e06SEd Maste 	unsigned char	*body = NULL;
147*f540a430SEd Maste 	long long	 ms = 0;
1480afa8e06SEd Maste 	size_t		 len;
1490afa8e06SEd Maste 	int		 type = COSE_ES256;
1500afa8e06SEd Maste 	int		 ext = 0;
1510afa8e06SEd Maste 	int		 ch;
1520afa8e06SEd Maste 	int		 r;
1530afa8e06SEd Maste 
1540afa8e06SEd Maste 	if ((cred = fido_cred_new()) == NULL)
1550afa8e06SEd Maste 		errx(1, "fido_cred_new");
1560afa8e06SEd Maste 
1570afa8e06SEd Maste 	while ((ch = getopt(argc, argv, "P:T:b:e:hi:k:rt:uv")) != -1) {
1580afa8e06SEd Maste 		switch (ch) {
1590afa8e06SEd Maste 		case 'P':
1600afa8e06SEd Maste 			pin = optarg;
1610afa8e06SEd Maste 			break;
1620afa8e06SEd Maste 		case 'T':
163*f540a430SEd Maste 			if (base10(optarg, &ms) < 0)
1640afa8e06SEd Maste 				errx(1, "base10: %s", optarg);
165*f540a430SEd Maste 			if (ms <= 0 || ms > 30)
1660afa8e06SEd Maste 				errx(1, "-T: %s must be in (0,30]", optarg);
167*f540a430SEd Maste 			ms *= 1000; /* seconds to milliseconds */
1680afa8e06SEd Maste 			break;
1690afa8e06SEd Maste 		case 'b':
1700afa8e06SEd Maste 			ext |= FIDO_EXT_LARGEBLOB_KEY;
1710afa8e06SEd Maste 			blobkey_out = optarg;
1720afa8e06SEd Maste 			break;
1730afa8e06SEd Maste 		case 'e':
1740afa8e06SEd Maste 			if (read_blob(optarg, &body, &len) < 0)
1750afa8e06SEd Maste 				errx(1, "read_blob: %s", optarg);
1760afa8e06SEd Maste 			r = fido_cred_exclude(cred, body, len);
1770afa8e06SEd Maste 			if (r != FIDO_OK)
1780afa8e06SEd Maste 				errx(1, "fido_cred_exclude: %s (0x%x)",
1790afa8e06SEd Maste 				    fido_strerr(r), r);
1800afa8e06SEd Maste 			free(body);
1810afa8e06SEd Maste 			body = NULL;
1820afa8e06SEd Maste 			break;
1830afa8e06SEd Maste 		case 'h':
1840afa8e06SEd Maste 			ext |= FIDO_EXT_HMAC_SECRET;
1850afa8e06SEd Maste 			break;
1860afa8e06SEd Maste 		case 'i':
1870afa8e06SEd Maste 			id_out = optarg;
1880afa8e06SEd Maste 			break;
1890afa8e06SEd Maste 		case 'k':
1900afa8e06SEd Maste 			key_out = optarg;
1910afa8e06SEd Maste 			break;
1920afa8e06SEd Maste 		case 'r':
1930afa8e06SEd Maste 			rk = true;
1940afa8e06SEd Maste 			break;
1950afa8e06SEd Maste 		case 't':
1960afa8e06SEd Maste 			if (strcmp(optarg, "ecdsa") == 0)
1970afa8e06SEd Maste 				type = COSE_ES256;
1980afa8e06SEd Maste 			else if (strcmp(optarg, "rsa") == 0)
1990afa8e06SEd Maste 				type = COSE_RS256;
2000afa8e06SEd Maste 			else if (strcmp(optarg, "eddsa") == 0)
2010afa8e06SEd Maste 				type = COSE_EDDSA;
2020afa8e06SEd Maste 			else
2030afa8e06SEd Maste 				errx(1, "unknown type %s", optarg);
2040afa8e06SEd Maste 			break;
2050afa8e06SEd Maste 		case 'u':
2060afa8e06SEd Maste 			u2f = true;
2070afa8e06SEd Maste 			break;
2080afa8e06SEd Maste 		case 'v':
2090afa8e06SEd Maste 			uv = true;
2100afa8e06SEd Maste 			break;
2110afa8e06SEd Maste 		default:
2120afa8e06SEd Maste 			usage();
2130afa8e06SEd Maste 		}
2140afa8e06SEd Maste 	}
2150afa8e06SEd Maste 
2160afa8e06SEd Maste 	argc -= optind;
2170afa8e06SEd Maste 	argv += optind;
2180afa8e06SEd Maste 
219*f540a430SEd Maste 	if (argc != 1)
2200afa8e06SEd Maste 		usage();
2210afa8e06SEd Maste 
222*f540a430SEd Maste 	fido_init(0);
2230afa8e06SEd Maste 
224*f540a430SEd Maste 	if ((dev = fido_dev_new()) == NULL)
225*f540a430SEd Maste 		errx(1, "fido_dev_new");
226*f540a430SEd Maste 
227*f540a430SEd Maste 	r = fido_dev_open(dev, argv[0]);
228*f540a430SEd Maste 	if (r != FIDO_OK)
229*f540a430SEd Maste 		errx(1, "fido_dev_open: %s (0x%x)", fido_strerr(r), r);
2300afa8e06SEd Maste 	if (u2f)
2310afa8e06SEd Maste 		fido_dev_force_u2f(dev);
2320afa8e06SEd Maste 
2330afa8e06SEd Maste 	/* type */
2340afa8e06SEd Maste 	r = fido_cred_set_type(cred, type);
2350afa8e06SEd Maste 	if (r != FIDO_OK)
2360afa8e06SEd Maste 		errx(1, "fido_cred_set_type: %s (0x%x)", fido_strerr(r), r);
2370afa8e06SEd Maste 
238*f540a430SEd Maste 	/* client data */
239*f540a430SEd Maste 	r = fido_cred_set_clientdata(cred, cd, sizeof(cd));
2400afa8e06SEd Maste 	if (r != FIDO_OK)
241*f540a430SEd Maste 		errx(1, "fido_cred_set_clientdata: %s (0x%x)", fido_strerr(r), r);
2420afa8e06SEd Maste 
2430afa8e06SEd Maste 	/* relying party */
2440afa8e06SEd Maste 	r = fido_cred_set_rp(cred, "localhost", "sweet home localhost");
2450afa8e06SEd Maste 	if (r != FIDO_OK)
2460afa8e06SEd Maste 		errx(1, "fido_cred_set_rp: %s (0x%x)", fido_strerr(r), r);
2470afa8e06SEd Maste 
2480afa8e06SEd Maste 	/* user */
2490afa8e06SEd Maste 	r = fido_cred_set_user(cred, user_id, sizeof(user_id), "john smith",
2500afa8e06SEd Maste 	    "jsmith", NULL);
2510afa8e06SEd Maste 	if (r != FIDO_OK)
2520afa8e06SEd Maste 		errx(1, "fido_cred_set_user: %s (0x%x)", fido_strerr(r), r);
2530afa8e06SEd Maste 
2540afa8e06SEd Maste 	/* extensions */
2550afa8e06SEd Maste 	r = fido_cred_set_extensions(cred, ext);
2560afa8e06SEd Maste 	if (r != FIDO_OK)
2570afa8e06SEd Maste 		errx(1, "fido_cred_set_extensions: %s (0x%x)", fido_strerr(r), r);
2580afa8e06SEd Maste 
2590afa8e06SEd Maste 	/* resident key */
2600afa8e06SEd Maste 	if (rk && (r = fido_cred_set_rk(cred, FIDO_OPT_TRUE)) != FIDO_OK)
2610afa8e06SEd Maste 		errx(1, "fido_cred_set_rk: %s (0x%x)", fido_strerr(r), r);
2620afa8e06SEd Maste 
2630afa8e06SEd Maste 	/* user verification */
2640afa8e06SEd Maste 	if (uv && (r = fido_cred_set_uv(cred, FIDO_OPT_TRUE)) != FIDO_OK)
2650afa8e06SEd Maste 		errx(1, "fido_cred_set_uv: %s (0x%x)", fido_strerr(r), r);
2660afa8e06SEd Maste 
267*f540a430SEd Maste 	/* timeout */
268*f540a430SEd Maste 	if (ms != 0 && (r = fido_dev_set_timeout(dev, (int)ms)) != FIDO_OK)
269*f540a430SEd Maste 		errx(1, "fido_dev_set_timeout: %s (0x%x)", fido_strerr(r), r);
2700afa8e06SEd Maste 
271*f540a430SEd Maste 	if ((r = fido_dev_make_cred(dev, cred, pin)) != FIDO_OK) {
2720afa8e06SEd Maste 		fido_dev_cancel(dev);
2730afa8e06SEd Maste 		errx(1, "fido_makecred: %s (0x%x)", fido_strerr(r), r);
2740afa8e06SEd Maste 	}
2750afa8e06SEd Maste 
2760afa8e06SEd Maste 	r = fido_dev_close(dev);
2770afa8e06SEd Maste 	if (r != FIDO_OK)
2780afa8e06SEd Maste 		errx(1, "fido_dev_close: %s (0x%x)", fido_strerr(r), r);
2790afa8e06SEd Maste 
2800afa8e06SEd Maste 	fido_dev_free(&dev);
2810afa8e06SEd Maste 
2820afa8e06SEd Maste 	/* when verifying, pin implies uv */
2830afa8e06SEd Maste 	if (pin)
2840afa8e06SEd Maste 		uv = true;
2850afa8e06SEd Maste 
2860afa8e06SEd Maste 	verify_cred(type, fido_cred_fmt(cred), fido_cred_authdata_ptr(cred),
287*f540a430SEd Maste 	    fido_cred_authdata_len(cred), fido_cred_attstmt_ptr(cred),
288*f540a430SEd Maste 	    fido_cred_attstmt_len(cred), rk, uv, ext, key_out, id_out);
2890afa8e06SEd Maste 
2900afa8e06SEd Maste 	if (blobkey_out != NULL) {
2910afa8e06SEd Maste 		/* extract the "largeBlob" key */
2920afa8e06SEd Maste 		if (write_blob(blobkey_out, fido_cred_largeblob_key_ptr(cred),
2930afa8e06SEd Maste 		    fido_cred_largeblob_key_len(cred)) < 0)
2940afa8e06SEd Maste 			errx(1, "write_blob");
2950afa8e06SEd Maste 	}
2960afa8e06SEd Maste 
2970afa8e06SEd Maste 	fido_cred_free(&cred);
2980afa8e06SEd Maste 
2990afa8e06SEd Maste 	exit(0);
3000afa8e06SEd Maste }
301