xref: /freebsd-src/contrib/libfido2/examples/cred.c (revision 60a517b66a69b8c011b04063ef63a938738719bd)
10afa8e06SEd Maste /*
2*60a517b6SEd Maste  * Copyright (c) 2018-2023 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.
52ccfa855SEd Maste  * SPDX-License-Identifier: BSD-2-Clause
60afa8e06SEd Maste  */
70afa8e06SEd Maste 
80afa8e06SEd Maste #include <errno.h>
90afa8e06SEd Maste #include <fido.h>
100afa8e06SEd Maste #include <stdbool.h>
110afa8e06SEd Maste #include <stdio.h>
120afa8e06SEd Maste #include <stdlib.h>
130afa8e06SEd Maste #include <string.h>
140afa8e06SEd Maste #ifdef HAVE_UNISTD_H
150afa8e06SEd Maste #include <unistd.h>
160afa8e06SEd Maste #endif
170afa8e06SEd Maste 
180afa8e06SEd Maste #include "../openbsd-compat/openbsd-compat.h"
190afa8e06SEd Maste #include "extern.h"
200afa8e06SEd Maste 
21f540a430SEd Maste static const unsigned char cd[32] = {
220afa8e06SEd Maste 	0xf9, 0x64, 0x57, 0xe7, 0x2d, 0x97, 0xf6, 0xbb,
230afa8e06SEd Maste 	0xdd, 0xd7, 0xfb, 0x06, 0x37, 0x62, 0xea, 0x26,
240afa8e06SEd Maste 	0x20, 0x44, 0x8e, 0x69, 0x7c, 0x03, 0xf2, 0x31,
250afa8e06SEd Maste 	0x2f, 0x99, 0xdc, 0xaf, 0x3e, 0x8a, 0x91, 0x6b,
260afa8e06SEd Maste };
270afa8e06SEd Maste 
280afa8e06SEd Maste static const unsigned char user_id[32] = {
290afa8e06SEd Maste 	0x78, 0x1c, 0x78, 0x60, 0xad, 0x88, 0xd2, 0x63,
300afa8e06SEd Maste 	0x32, 0x62, 0x2a, 0xf1, 0x74, 0x5d, 0xed, 0xb2,
310afa8e06SEd Maste 	0xe7, 0xa4, 0x2b, 0x44, 0x89, 0x29, 0x39, 0xc5,
320afa8e06SEd Maste 	0x56, 0x64, 0x01, 0x27, 0x0d, 0xbb, 0xc4, 0x49,
330afa8e06SEd Maste };
340afa8e06SEd Maste 
350afa8e06SEd Maste static void
usage(void)360afa8e06SEd Maste usage(void)
370afa8e06SEd Maste {
382ccfa855SEd Maste 	fprintf(stderr, "usage: cred [-t es256|es384|rs256|eddsa] [-k pubkey] "
39*60a517b6SEd Maste 	    "[-ei cred_id] [-P pin] [-T seconds] [-b blobkey] [-c cred_protect] [-hruv] "
400afa8e06SEd Maste 	    "<device>\n");
410afa8e06SEd Maste 	exit(EXIT_FAILURE);
420afa8e06SEd Maste }
430afa8e06SEd Maste 
440afa8e06SEd Maste static void
verify_cred(int type,const char * fmt,const unsigned char * authdata_ptr,size_t authdata_len,const unsigned char * attstmt_ptr,size_t attstmt_len,bool rk,bool uv,int ext,int cred_protect,const char * key_out,const char * id_out)450afa8e06SEd Maste verify_cred(int type, const char *fmt, const unsigned char *authdata_ptr,
46f540a430SEd Maste     size_t authdata_len, const unsigned char *attstmt_ptr, size_t attstmt_len,
47*60a517b6SEd Maste     bool rk, bool uv, int ext, int cred_protect, const char *key_out,
48*60a517b6SEd Maste     const char *id_out)
490afa8e06SEd Maste {
500afa8e06SEd Maste 	fido_cred_t	*cred;
510afa8e06SEd Maste 	int		 r;
520afa8e06SEd Maste 
530afa8e06SEd Maste 	if ((cred = fido_cred_new()) == NULL)
540afa8e06SEd Maste 		errx(1, "fido_cred_new");
550afa8e06SEd Maste 
560afa8e06SEd Maste 	/* type */
570afa8e06SEd Maste 	r = fido_cred_set_type(cred, type);
580afa8e06SEd Maste 	if (r != FIDO_OK)
590afa8e06SEd Maste 		errx(1, "fido_cred_set_type: %s (0x%x)", fido_strerr(r), r);
600afa8e06SEd Maste 
61f540a430SEd Maste 	/* client data */
62f540a430SEd Maste 	r = fido_cred_set_clientdata(cred, cd, sizeof(cd));
630afa8e06SEd Maste 	if (r != FIDO_OK)
64f540a430SEd Maste 		errx(1, "fido_cred_set_clientdata: %s (0x%x)", fido_strerr(r), r);
650afa8e06SEd Maste 
660afa8e06SEd Maste 	/* relying party */
670afa8e06SEd Maste 	r = fido_cred_set_rp(cred, "localhost", "sweet home localhost");
680afa8e06SEd Maste 	if (r != FIDO_OK)
690afa8e06SEd Maste 		errx(1, "fido_cred_set_rp: %s (0x%x)", fido_strerr(r), r);
700afa8e06SEd Maste 
710afa8e06SEd Maste 	/* authdata */
720afa8e06SEd Maste 	r = fido_cred_set_authdata(cred, authdata_ptr, authdata_len);
730afa8e06SEd Maste 	if (r != FIDO_OK)
740afa8e06SEd Maste 		errx(1, "fido_cred_set_authdata: %s (0x%x)", fido_strerr(r), r);
750afa8e06SEd Maste 
760afa8e06SEd Maste 	/* extensions */
770afa8e06SEd Maste 	r = fido_cred_set_extensions(cred, ext);
780afa8e06SEd Maste 	if (r != FIDO_OK)
790afa8e06SEd Maste 		errx(1, "fido_cred_set_extensions: %s (0x%x)", fido_strerr(r), r);
800afa8e06SEd Maste 
810afa8e06SEd Maste 	/* resident key */
820afa8e06SEd Maste 	if (rk && (r = fido_cred_set_rk(cred, FIDO_OPT_TRUE)) != FIDO_OK)
830afa8e06SEd Maste 		errx(1, "fido_cred_set_rk: %s (0x%x)", fido_strerr(r), r);
840afa8e06SEd Maste 
850afa8e06SEd Maste 	/* user verification */
860afa8e06SEd Maste 	if (uv && (r = fido_cred_set_uv(cred, FIDO_OPT_TRUE)) != FIDO_OK)
870afa8e06SEd Maste 		errx(1, "fido_cred_set_uv: %s (0x%x)", fido_strerr(r), r);
880afa8e06SEd Maste 
89*60a517b6SEd Maste 	/* credProt */
90*60a517b6SEd Maste 	if (cred_protect != 0 && (r = fido_cred_set_prot(cred,
91*60a517b6SEd Maste 	    cred_protect)) != FIDO_OK)
92*60a517b6SEd Maste 		errx(1, "fido_cred_set_prot: %s (0x%x)", fido_strerr(r), r);
93*60a517b6SEd Maste 
940afa8e06SEd Maste 	/* fmt */
950afa8e06SEd Maste 	r = fido_cred_set_fmt(cred, fmt);
960afa8e06SEd Maste 	if (r != FIDO_OK)
970afa8e06SEd Maste 		errx(1, "fido_cred_set_fmt: %s (0x%x)", fido_strerr(r), r);
980afa8e06SEd Maste 
990afa8e06SEd Maste 	if (!strcmp(fido_cred_fmt(cred), "none")) {
1000afa8e06SEd Maste 		warnx("no attestation data, skipping credential verification");
1010afa8e06SEd Maste 		goto out;
1020afa8e06SEd Maste 	}
1030afa8e06SEd Maste 
104f540a430SEd Maste 	/* attestation statement */
105f540a430SEd Maste 	r = fido_cred_set_attstmt(cred, attstmt_ptr, attstmt_len);
1060afa8e06SEd Maste 	if (r != FIDO_OK)
107f540a430SEd Maste 		errx(1, "fido_cred_set_attstmt: %s (0x%x)", fido_strerr(r), r);
1080afa8e06SEd Maste 
1090afa8e06SEd Maste 	r = fido_cred_verify(cred);
1100afa8e06SEd Maste 	if (r != FIDO_OK)
1110afa8e06SEd Maste 		errx(1, "fido_cred_verify: %s (0x%x)", fido_strerr(r), r);
1120afa8e06SEd Maste 
1130afa8e06SEd Maste out:
1140afa8e06SEd Maste 	if (key_out != NULL) {
1150afa8e06SEd Maste 		/* extract the credential pubkey */
1160afa8e06SEd Maste 		if (type == COSE_ES256) {
1172ccfa855SEd Maste 			if (write_es256_pubkey(key_out,
1182ccfa855SEd Maste 			    fido_cred_pubkey_ptr(cred),
1190afa8e06SEd Maste 			    fido_cred_pubkey_len(cred)) < 0)
1202ccfa855SEd Maste 				errx(1, "write_es256_pubkey");
1212ccfa855SEd Maste 		} else if (type == COSE_ES384) {
1222ccfa855SEd Maste 			if (write_es384_pubkey(key_out,
1232ccfa855SEd Maste 			    fido_cred_pubkey_ptr(cred),
1242ccfa855SEd Maste 			    fido_cred_pubkey_len(cred)) < 0)
1252ccfa855SEd Maste 				errx(1, "write_es384_pubkey");
1260afa8e06SEd Maste 		} else if (type == COSE_RS256) {
1272ccfa855SEd Maste 			if (write_rs256_pubkey(key_out,
1282ccfa855SEd Maste 			    fido_cred_pubkey_ptr(cred),
1290afa8e06SEd Maste 			    fido_cred_pubkey_len(cred)) < 0)
1302ccfa855SEd Maste 				errx(1, "write_rs256_pubkey");
1310afa8e06SEd Maste 		} else if (type == COSE_EDDSA) {
1322ccfa855SEd Maste 			if (write_eddsa_pubkey(key_out,
1332ccfa855SEd Maste 			    fido_cred_pubkey_ptr(cred),
1340afa8e06SEd Maste 			    fido_cred_pubkey_len(cred)) < 0)
1350afa8e06SEd Maste 				errx(1, "write_eddsa_pubkey");
1360afa8e06SEd Maste 		}
1370afa8e06SEd Maste 	}
1380afa8e06SEd Maste 
1390afa8e06SEd Maste 	if (id_out != NULL) {
1400afa8e06SEd Maste 		/* extract the credential id */
1410afa8e06SEd Maste 		if (write_blob(id_out, fido_cred_id_ptr(cred),
1420afa8e06SEd Maste 		    fido_cred_id_len(cred)) < 0)
1430afa8e06SEd Maste 			errx(1, "write_blob");
1440afa8e06SEd Maste 	}
1450afa8e06SEd Maste 
1460afa8e06SEd Maste 	fido_cred_free(&cred);
1470afa8e06SEd Maste }
1480afa8e06SEd Maste 
1490afa8e06SEd Maste int
main(int argc,char ** argv)1500afa8e06SEd Maste main(int argc, char **argv)
1510afa8e06SEd Maste {
1520afa8e06SEd Maste 	bool		 rk = false;
1530afa8e06SEd Maste 	bool		 uv = false;
1540afa8e06SEd Maste 	bool		 u2f = false;
1550afa8e06SEd Maste 	fido_dev_t	*dev;
1560afa8e06SEd Maste 	fido_cred_t	*cred = NULL;
1570afa8e06SEd Maste 	const char	*pin = NULL;
1580afa8e06SEd Maste 	const char	*blobkey_out = NULL;
1590afa8e06SEd Maste 	const char	*key_out = NULL;
1600afa8e06SEd Maste 	const char	*id_out = NULL;
1610afa8e06SEd Maste 	unsigned char	*body = NULL;
162f540a430SEd Maste 	long long	 ms = 0;
1630afa8e06SEd Maste 	size_t		 len;
1640afa8e06SEd Maste 	int		 type = COSE_ES256;
1650afa8e06SEd Maste 	int		 ext = 0;
1660afa8e06SEd Maste 	int		 ch;
1670afa8e06SEd Maste 	int		 r;
168*60a517b6SEd Maste 	long long cred_protect = 0;
1690afa8e06SEd Maste 
1700afa8e06SEd Maste 	if ((cred = fido_cred_new()) == NULL)
1710afa8e06SEd Maste 		errx(1, "fido_cred_new");
1720afa8e06SEd Maste 
173*60a517b6SEd Maste 	while ((ch = getopt(argc, argv, "P:T:b:e:hi:k:rt:uvc:")) != -1) {
1740afa8e06SEd Maste 		switch (ch) {
1750afa8e06SEd Maste 		case 'P':
1760afa8e06SEd Maste 			pin = optarg;
1770afa8e06SEd Maste 			break;
1780afa8e06SEd Maste 		case 'T':
179f540a430SEd Maste 			if (base10(optarg, &ms) < 0)
1800afa8e06SEd Maste 				errx(1, "base10: %s", optarg);
181f540a430SEd Maste 			if (ms <= 0 || ms > 30)
1820afa8e06SEd Maste 				errx(1, "-T: %s must be in (0,30]", optarg);
183f540a430SEd Maste 			ms *= 1000; /* seconds to milliseconds */
1840afa8e06SEd Maste 			break;
1850afa8e06SEd Maste 		case 'b':
1860afa8e06SEd Maste 			ext |= FIDO_EXT_LARGEBLOB_KEY;
1870afa8e06SEd Maste 			blobkey_out = optarg;
1880afa8e06SEd Maste 			break;
1890afa8e06SEd Maste 		case 'e':
1900afa8e06SEd Maste 			if (read_blob(optarg, &body, &len) < 0)
1910afa8e06SEd Maste 				errx(1, "read_blob: %s", optarg);
1920afa8e06SEd Maste 			r = fido_cred_exclude(cred, body, len);
1930afa8e06SEd Maste 			if (r != FIDO_OK)
1940afa8e06SEd Maste 				errx(1, "fido_cred_exclude: %s (0x%x)",
1950afa8e06SEd Maste 				    fido_strerr(r), r);
1960afa8e06SEd Maste 			free(body);
1970afa8e06SEd Maste 			body = NULL;
1980afa8e06SEd Maste 			break;
1990afa8e06SEd Maste 		case 'h':
2000afa8e06SEd Maste 			ext |= FIDO_EXT_HMAC_SECRET;
2010afa8e06SEd Maste 			break;
202*60a517b6SEd Maste 		case 'c':
203*60a517b6SEd Maste 			if (base10(optarg, &cred_protect) < 0)
204*60a517b6SEd Maste 				errx(1, "base10: %s", optarg);
205*60a517b6SEd Maste 			if (cred_protect <= 0 || cred_protect > 3)
206*60a517b6SEd Maste 				errx(1, "-c: %s must be in (1,3)", optarg);
207*60a517b6SEd Maste 			ext |= FIDO_EXT_CRED_PROTECT;
208*60a517b6SEd Maste 			break;
2090afa8e06SEd Maste 		case 'i':
2100afa8e06SEd Maste 			id_out = optarg;
2110afa8e06SEd Maste 			break;
2120afa8e06SEd Maste 		case 'k':
2130afa8e06SEd Maste 			key_out = optarg;
2140afa8e06SEd Maste 			break;
2150afa8e06SEd Maste 		case 'r':
2160afa8e06SEd Maste 			rk = true;
2170afa8e06SEd Maste 			break;
2180afa8e06SEd Maste 		case 't':
2192ccfa855SEd Maste 			if (strcmp(optarg, "es256") == 0)
2200afa8e06SEd Maste 				type = COSE_ES256;
2212ccfa855SEd Maste 			else if (strcmp(optarg, "es384") == 0)
2222ccfa855SEd Maste 				type = COSE_ES384;
2232ccfa855SEd Maste 			else if (strcmp(optarg, "rs256") == 0)
2240afa8e06SEd Maste 				type = COSE_RS256;
2250afa8e06SEd Maste 			else if (strcmp(optarg, "eddsa") == 0)
2260afa8e06SEd Maste 				type = COSE_EDDSA;
2270afa8e06SEd Maste 			else
2280afa8e06SEd Maste 				errx(1, "unknown type %s", optarg);
2290afa8e06SEd Maste 			break;
2300afa8e06SEd Maste 		case 'u':
2310afa8e06SEd Maste 			u2f = true;
2320afa8e06SEd Maste 			break;
2330afa8e06SEd Maste 		case 'v':
2340afa8e06SEd Maste 			uv = true;
2350afa8e06SEd Maste 			break;
2360afa8e06SEd Maste 		default:
2370afa8e06SEd Maste 			usage();
2380afa8e06SEd Maste 		}
2390afa8e06SEd Maste 	}
2400afa8e06SEd Maste 
2410afa8e06SEd Maste 	argc -= optind;
2420afa8e06SEd Maste 	argv += optind;
2430afa8e06SEd Maste 
244f540a430SEd Maste 	if (argc != 1)
2450afa8e06SEd Maste 		usage();
2460afa8e06SEd Maste 
247f540a430SEd Maste 	fido_init(0);
2480afa8e06SEd Maste 
249f540a430SEd Maste 	if ((dev = fido_dev_new()) == NULL)
250f540a430SEd Maste 		errx(1, "fido_dev_new");
251f540a430SEd Maste 
252f540a430SEd Maste 	r = fido_dev_open(dev, argv[0]);
253f540a430SEd Maste 	if (r != FIDO_OK)
254f540a430SEd Maste 		errx(1, "fido_dev_open: %s (0x%x)", fido_strerr(r), r);
2550afa8e06SEd Maste 	if (u2f)
2560afa8e06SEd Maste 		fido_dev_force_u2f(dev);
2570afa8e06SEd Maste 
2580afa8e06SEd Maste 	/* type */
2590afa8e06SEd Maste 	r = fido_cred_set_type(cred, type);
2600afa8e06SEd Maste 	if (r != FIDO_OK)
2610afa8e06SEd Maste 		errx(1, "fido_cred_set_type: %s (0x%x)", fido_strerr(r), r);
2620afa8e06SEd Maste 
263f540a430SEd Maste 	/* client data */
264f540a430SEd Maste 	r = fido_cred_set_clientdata(cred, cd, sizeof(cd));
2650afa8e06SEd Maste 	if (r != FIDO_OK)
266f540a430SEd Maste 		errx(1, "fido_cred_set_clientdata: %s (0x%x)", fido_strerr(r), r);
2670afa8e06SEd Maste 
2680afa8e06SEd Maste 	/* relying party */
2690afa8e06SEd Maste 	r = fido_cred_set_rp(cred, "localhost", "sweet home localhost");
2700afa8e06SEd Maste 	if (r != FIDO_OK)
2710afa8e06SEd Maste 		errx(1, "fido_cred_set_rp: %s (0x%x)", fido_strerr(r), r);
2720afa8e06SEd Maste 
2730afa8e06SEd Maste 	/* user */
2740afa8e06SEd Maste 	r = fido_cred_set_user(cred, user_id, sizeof(user_id), "john smith",
2750afa8e06SEd Maste 	    "jsmith", NULL);
2760afa8e06SEd Maste 	if (r != FIDO_OK)
2770afa8e06SEd Maste 		errx(1, "fido_cred_set_user: %s (0x%x)", fido_strerr(r), r);
2780afa8e06SEd Maste 
2790afa8e06SEd Maste 	/* extensions */
2800afa8e06SEd Maste 	r = fido_cred_set_extensions(cred, ext);
2810afa8e06SEd Maste 	if (r != FIDO_OK)
2820afa8e06SEd Maste 		errx(1, "fido_cred_set_extensions: %s (0x%x)", fido_strerr(r), r);
2830afa8e06SEd Maste 
2840afa8e06SEd Maste 	/* resident key */
2850afa8e06SEd Maste 	if (rk && (r = fido_cred_set_rk(cred, FIDO_OPT_TRUE)) != FIDO_OK)
2860afa8e06SEd Maste 		errx(1, "fido_cred_set_rk: %s (0x%x)", fido_strerr(r), r);
2870afa8e06SEd Maste 
2880afa8e06SEd Maste 	/* user verification */
2890afa8e06SEd Maste 	if (uv && (r = fido_cred_set_uv(cred, FIDO_OPT_TRUE)) != FIDO_OK)
2900afa8e06SEd Maste 		errx(1, "fido_cred_set_uv: %s (0x%x)", fido_strerr(r), r);
2910afa8e06SEd Maste 
292*60a517b6SEd Maste 	/* credProt */
293*60a517b6SEd Maste 	if (cred_protect != 0 && (r = fido_cred_set_prot(cred,
294*60a517b6SEd Maste 	    (int)cred_protect)) != FIDO_OK)
295*60a517b6SEd Maste 		errx(1, "fido_cred_set_prot: %s (0x%x)", fido_strerr(r), r);
296*60a517b6SEd Maste 
297f540a430SEd Maste 	/* timeout */
298f540a430SEd Maste 	if (ms != 0 && (r = fido_dev_set_timeout(dev, (int)ms)) != FIDO_OK)
299f540a430SEd Maste 		errx(1, "fido_dev_set_timeout: %s (0x%x)", fido_strerr(r), r);
3000afa8e06SEd Maste 
301f540a430SEd Maste 	if ((r = fido_dev_make_cred(dev, cred, pin)) != FIDO_OK) {
3020afa8e06SEd Maste 		fido_dev_cancel(dev);
3030afa8e06SEd Maste 		errx(1, "fido_makecred: %s (0x%x)", fido_strerr(r), r);
3040afa8e06SEd Maste 	}
3050afa8e06SEd Maste 
3060afa8e06SEd Maste 	r = fido_dev_close(dev);
3070afa8e06SEd Maste 	if (r != FIDO_OK)
3080afa8e06SEd Maste 		errx(1, "fido_dev_close: %s (0x%x)", fido_strerr(r), r);
3090afa8e06SEd Maste 
3100afa8e06SEd Maste 	fido_dev_free(&dev);
3110afa8e06SEd Maste 
3120afa8e06SEd Maste 	/* when verifying, pin implies uv */
3130afa8e06SEd Maste 	if (pin)
3140afa8e06SEd Maste 		uv = true;
3150afa8e06SEd Maste 
3160afa8e06SEd Maste 	verify_cred(type, fido_cred_fmt(cred), fido_cred_authdata_ptr(cred),
317f540a430SEd Maste 	    fido_cred_authdata_len(cred), fido_cred_attstmt_ptr(cred),
318*60a517b6SEd Maste 	    fido_cred_attstmt_len(cred), rk, uv, ext, fido_cred_prot(cred),
319*60a517b6SEd Maste 	    key_out, id_out);
3200afa8e06SEd Maste 
3210afa8e06SEd Maste 	if (blobkey_out != NULL) {
3220afa8e06SEd Maste 		/* extract the "largeBlob" key */
3230afa8e06SEd Maste 		if (write_blob(blobkey_out, fido_cred_largeblob_key_ptr(cred),
3240afa8e06SEd Maste 		    fido_cred_largeblob_key_len(cred)) < 0)
3250afa8e06SEd Maste 			errx(1, "write_blob");
3260afa8e06SEd Maste 	}
3270afa8e06SEd Maste 
3280afa8e06SEd Maste 	fido_cred_free(&cred);
3290afa8e06SEd Maste 
3300afa8e06SEd Maste 	exit(0);
3310afa8e06SEd Maste }
332