xref: /freebsd-src/contrib/libfido2/examples/assert.c (revision 2ccfa855b2fc331819953e3de1b1c15ce5b95a7e)
10afa8e06SEd Maste /*
2*2ccfa855SEd Maste  * Copyright (c) 2018-2022 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.
5*2ccfa855SEd Maste  * SPDX-License-Identifier: BSD-2-Clause
60afa8e06SEd Maste  */
70afa8e06SEd Maste 
80afa8e06SEd Maste #include <fido.h>
90afa8e06SEd Maste #include <fido/es256.h>
10*2ccfa855SEd Maste #include <fido/es384.h>
110afa8e06SEd Maste #include <fido/rs256.h>
120afa8e06SEd Maste #include <fido/eddsa.h>
130afa8e06SEd Maste 
140afa8e06SEd Maste #include <stdbool.h>
150afa8e06SEd Maste #include <stdio.h>
160afa8e06SEd Maste #include <stdlib.h>
170afa8e06SEd Maste #include <string.h>
180afa8e06SEd Maste #ifdef HAVE_UNISTD_H
190afa8e06SEd Maste #include <unistd.h>
200afa8e06SEd Maste #endif
210afa8e06SEd Maste 
220afa8e06SEd Maste #include "../openbsd-compat/openbsd-compat.h"
230afa8e06SEd Maste #include "extern.h"
240afa8e06SEd Maste 
25f540a430SEd Maste static const unsigned char cd[32] = {
260afa8e06SEd Maste 	0xec, 0x8d, 0x8f, 0x78, 0x42, 0x4a, 0x2b, 0xb7,
270afa8e06SEd Maste 	0x82, 0x34, 0xaa, 0xca, 0x07, 0xa1, 0xf6, 0x56,
280afa8e06SEd Maste 	0x42, 0x1c, 0xb6, 0xf6, 0xb3, 0x00, 0x86, 0x52,
290afa8e06SEd Maste 	0x35, 0x2d, 0xa2, 0x62, 0x4a, 0xbe, 0x89, 0x76,
300afa8e06SEd Maste };
310afa8e06SEd Maste 
320afa8e06SEd Maste static void
usage(void)330afa8e06SEd Maste usage(void)
340afa8e06SEd Maste {
35*2ccfa855SEd Maste 	fprintf(stderr, "usage: assert [-t es256|es384|rs256|eddsa] "
36*2ccfa855SEd Maste 	    "[-a cred_id] [-h hmac_secret] [-s hmac_salt] [-P pin] "
37*2ccfa855SEd Maste 	    "[-T seconds] [-b blobkey] [-puv] <pubkey> <device>\n");
380afa8e06SEd Maste 	exit(EXIT_FAILURE);
390afa8e06SEd Maste }
400afa8e06SEd Maste 
410afa8e06SEd Maste static void
verify_assert(int type,const unsigned char * authdata_ptr,size_t authdata_len,const unsigned char * sig_ptr,size_t sig_len,bool up,bool uv,int ext,const char * key)420afa8e06SEd Maste verify_assert(int type, const unsigned char *authdata_ptr, size_t authdata_len,
430afa8e06SEd Maste     const unsigned char *sig_ptr, size_t sig_len, bool up, bool uv, int ext,
440afa8e06SEd Maste     const char *key)
450afa8e06SEd Maste {
460afa8e06SEd Maste 	fido_assert_t	*assert = NULL;
470afa8e06SEd Maste 	EC_KEY		*ec = NULL;
480afa8e06SEd Maste 	RSA		*rsa = NULL;
490afa8e06SEd Maste 	EVP_PKEY	*eddsa = NULL;
500afa8e06SEd Maste 	es256_pk_t	*es256_pk = NULL;
51*2ccfa855SEd Maste 	es384_pk_t	*es384_pk = NULL;
520afa8e06SEd Maste 	rs256_pk_t	*rs256_pk = NULL;
530afa8e06SEd Maste 	eddsa_pk_t	*eddsa_pk = NULL;
540afa8e06SEd Maste 	void		*pk;
550afa8e06SEd Maste 	int		 r;
560afa8e06SEd Maste 
570afa8e06SEd Maste 	/* credential pubkey */
580afa8e06SEd Maste 	switch (type) {
590afa8e06SEd Maste 	case COSE_ES256:
600afa8e06SEd Maste 		if ((ec = read_ec_pubkey(key)) == NULL)
610afa8e06SEd Maste 			errx(1, "read_ec_pubkey");
620afa8e06SEd Maste 
630afa8e06SEd Maste 		if ((es256_pk = es256_pk_new()) == NULL)
640afa8e06SEd Maste 			errx(1, "es256_pk_new");
650afa8e06SEd Maste 
660afa8e06SEd Maste 		if (es256_pk_from_EC_KEY(es256_pk, ec) != FIDO_OK)
670afa8e06SEd Maste 			errx(1, "es256_pk_from_EC_KEY");
680afa8e06SEd Maste 
690afa8e06SEd Maste 		pk = es256_pk;
700afa8e06SEd Maste 		EC_KEY_free(ec);
710afa8e06SEd Maste 		ec = NULL;
720afa8e06SEd Maste 
730afa8e06SEd Maste 		break;
74*2ccfa855SEd Maste 	case COSE_ES384:
75*2ccfa855SEd Maste 		if ((ec = read_ec_pubkey(key)) == NULL)
76*2ccfa855SEd Maste 			errx(1, "read_ec_pubkey");
77*2ccfa855SEd Maste 
78*2ccfa855SEd Maste 		if ((es384_pk = es384_pk_new()) == NULL)
79*2ccfa855SEd Maste 			errx(1, "es384_pk_new");
80*2ccfa855SEd Maste 
81*2ccfa855SEd Maste 		if (es384_pk_from_EC_KEY(es384_pk, ec) != FIDO_OK)
82*2ccfa855SEd Maste 			errx(1, "es384_pk_from_EC_KEY");
83*2ccfa855SEd Maste 
84*2ccfa855SEd Maste 		pk = es384_pk;
85*2ccfa855SEd Maste 		EC_KEY_free(ec);
86*2ccfa855SEd Maste 		ec = NULL;
87*2ccfa855SEd Maste 
88*2ccfa855SEd Maste 		break;
890afa8e06SEd Maste 	case COSE_RS256:
900afa8e06SEd Maste 		if ((rsa = read_rsa_pubkey(key)) == NULL)
910afa8e06SEd Maste 			errx(1, "read_rsa_pubkey");
920afa8e06SEd Maste 
930afa8e06SEd Maste 		if ((rs256_pk = rs256_pk_new()) == NULL)
940afa8e06SEd Maste 			errx(1, "rs256_pk_new");
950afa8e06SEd Maste 
960afa8e06SEd Maste 		if (rs256_pk_from_RSA(rs256_pk, rsa) != FIDO_OK)
970afa8e06SEd Maste 			errx(1, "rs256_pk_from_RSA");
980afa8e06SEd Maste 
990afa8e06SEd Maste 		pk = rs256_pk;
1000afa8e06SEd Maste 		RSA_free(rsa);
1010afa8e06SEd Maste 		rsa = NULL;
1020afa8e06SEd Maste 
1030afa8e06SEd Maste 		break;
1040afa8e06SEd Maste 	case COSE_EDDSA:
1050afa8e06SEd Maste 		if ((eddsa = read_eddsa_pubkey(key)) == NULL)
1060afa8e06SEd Maste 			errx(1, "read_eddsa_pubkey");
1070afa8e06SEd Maste 
1080afa8e06SEd Maste 		if ((eddsa_pk = eddsa_pk_new()) == NULL)
1090afa8e06SEd Maste 			errx(1, "eddsa_pk_new");
1100afa8e06SEd Maste 
1110afa8e06SEd Maste 		if (eddsa_pk_from_EVP_PKEY(eddsa_pk, eddsa) != FIDO_OK)
1120afa8e06SEd Maste 			errx(1, "eddsa_pk_from_EVP_PKEY");
1130afa8e06SEd Maste 
1140afa8e06SEd Maste 		pk = eddsa_pk;
1150afa8e06SEd Maste 		EVP_PKEY_free(eddsa);
1160afa8e06SEd Maste 		eddsa = NULL;
1170afa8e06SEd Maste 
1180afa8e06SEd Maste 		break;
1190afa8e06SEd Maste 	default:
1200afa8e06SEd Maste 		errx(1, "unknown credential type %d", type);
1210afa8e06SEd Maste 	}
1220afa8e06SEd Maste 
1230afa8e06SEd Maste 	if ((assert = fido_assert_new()) == NULL)
1240afa8e06SEd Maste 		errx(1, "fido_assert_new");
1250afa8e06SEd Maste 
1260afa8e06SEd Maste 	/* client data hash */
127f540a430SEd Maste 	r = fido_assert_set_clientdata(assert, cd, sizeof(cd));
1280afa8e06SEd Maste 	if (r != FIDO_OK)
129f540a430SEd Maste 		errx(1, "fido_assert_set_clientdata: %s (0x%x)", fido_strerr(r), r);
1300afa8e06SEd Maste 
1310afa8e06SEd Maste 	/* relying party */
1320afa8e06SEd Maste 	r = fido_assert_set_rp(assert, "localhost");
1330afa8e06SEd Maste 	if (r != FIDO_OK)
1340afa8e06SEd Maste 		errx(1, "fido_assert_set_rp: %s (0x%x)", fido_strerr(r), r);
1350afa8e06SEd Maste 
1360afa8e06SEd Maste 	/* authdata */
1370afa8e06SEd Maste 	r = fido_assert_set_count(assert, 1);
1380afa8e06SEd Maste 	if (r != FIDO_OK)
1390afa8e06SEd Maste 		errx(1, "fido_assert_set_count: %s (0x%x)", fido_strerr(r), r);
1400afa8e06SEd Maste 	r = fido_assert_set_authdata(assert, 0, authdata_ptr, authdata_len);
1410afa8e06SEd Maste 	if (r != FIDO_OK)
1420afa8e06SEd Maste 		errx(1, "fido_assert_set_authdata: %s (0x%x)", fido_strerr(r), r);
1430afa8e06SEd Maste 
1440afa8e06SEd Maste 	/* extension */
1450afa8e06SEd Maste 	r = fido_assert_set_extensions(assert, ext);
1460afa8e06SEd Maste 	if (r != FIDO_OK)
1470afa8e06SEd Maste 		errx(1, "fido_assert_set_extensions: %s (0x%x)", fido_strerr(r),
1480afa8e06SEd Maste 		    r);
1490afa8e06SEd Maste 
1500afa8e06SEd Maste 	/* user presence */
1510afa8e06SEd Maste 	if (up && (r = fido_assert_set_up(assert, FIDO_OPT_TRUE)) != FIDO_OK)
1520afa8e06SEd Maste 		errx(1, "fido_assert_set_up: %s (0x%x)", fido_strerr(r), r);
1530afa8e06SEd Maste 
1540afa8e06SEd Maste 	/* user verification */
1550afa8e06SEd Maste 	if (uv && (r = fido_assert_set_uv(assert, FIDO_OPT_TRUE)) != FIDO_OK)
1560afa8e06SEd Maste 		errx(1, "fido_assert_set_uv: %s (0x%x)", fido_strerr(r), r);
1570afa8e06SEd Maste 
1580afa8e06SEd Maste 	/* sig */
1590afa8e06SEd Maste 	r = fido_assert_set_sig(assert, 0, sig_ptr, sig_len);
1600afa8e06SEd Maste 	if (r != FIDO_OK)
1610afa8e06SEd Maste 		errx(1, "fido_assert_set_sig: %s (0x%x)", fido_strerr(r), r);
1620afa8e06SEd Maste 
1630afa8e06SEd Maste 	r = fido_assert_verify(assert, 0, type, pk);
1640afa8e06SEd Maste 	if (r != FIDO_OK)
1650afa8e06SEd Maste 		errx(1, "fido_assert_verify: %s (0x%x)", fido_strerr(r), r);
1660afa8e06SEd Maste 
1670afa8e06SEd Maste 	es256_pk_free(&es256_pk);
168*2ccfa855SEd Maste 	es384_pk_free(&es384_pk);
1690afa8e06SEd Maste 	rs256_pk_free(&rs256_pk);
1700afa8e06SEd Maste 	eddsa_pk_free(&eddsa_pk);
1710afa8e06SEd Maste 
1720afa8e06SEd Maste 	fido_assert_free(&assert);
1730afa8e06SEd Maste }
1740afa8e06SEd Maste 
1750afa8e06SEd Maste int
main(int argc,char ** argv)1760afa8e06SEd Maste main(int argc, char **argv)
1770afa8e06SEd Maste {
1780afa8e06SEd Maste 	bool		 up = false;
1790afa8e06SEd Maste 	bool		 uv = false;
1800afa8e06SEd Maste 	bool		 u2f = false;
1810afa8e06SEd Maste 	fido_dev_t	*dev = NULL;
1820afa8e06SEd Maste 	fido_assert_t	*assert = NULL;
1830afa8e06SEd Maste 	const char	*pin = NULL;
1840afa8e06SEd Maste 	const char	*blobkey_out = NULL;
1850afa8e06SEd Maste 	const char	*hmac_out = NULL;
1860afa8e06SEd Maste 	unsigned char	*body = NULL;
187f540a430SEd Maste 	long long	 ms = 0;
1880afa8e06SEd Maste 	size_t		 len;
1890afa8e06SEd Maste 	int		 type = COSE_ES256;
1900afa8e06SEd Maste 	int		 ext = 0;
1910afa8e06SEd Maste 	int		 ch;
1920afa8e06SEd Maste 	int		 r;
1930afa8e06SEd Maste 
1940afa8e06SEd Maste 	if ((assert = fido_assert_new()) == NULL)
1950afa8e06SEd Maste 		errx(1, "fido_assert_new");
1960afa8e06SEd Maste 
1970afa8e06SEd Maste 	while ((ch = getopt(argc, argv, "P:T:a:b:h:ps:t:uv")) != -1) {
1980afa8e06SEd Maste 		switch (ch) {
1990afa8e06SEd Maste 		case 'P':
2000afa8e06SEd Maste 			pin = optarg;
2010afa8e06SEd Maste 			break;
2020afa8e06SEd Maste 		case 'T':
203f540a430SEd Maste 			if (base10(optarg, &ms) < 0)
2040afa8e06SEd Maste 				errx(1, "base10: %s", optarg);
205f540a430SEd Maste 			if (ms <= 0 || ms > 30)
2060afa8e06SEd Maste 				errx(1, "-T: %s must be in (0,30]", optarg);
207f540a430SEd Maste 			ms *= 1000; /* seconds to milliseconds */
2080afa8e06SEd Maste 			break;
2090afa8e06SEd Maste 		case 'a':
2100afa8e06SEd Maste 			if (read_blob(optarg, &body, &len) < 0)
2110afa8e06SEd Maste 				errx(1, "read_blob: %s", optarg);
2120afa8e06SEd Maste 			if ((r = fido_assert_allow_cred(assert, body,
2130afa8e06SEd Maste 			    len)) != FIDO_OK)
2140afa8e06SEd Maste 				errx(1, "fido_assert_allow_cred: %s (0x%x)",
2150afa8e06SEd Maste 				    fido_strerr(r), r);
2160afa8e06SEd Maste 			free(body);
2170afa8e06SEd Maste 			body = NULL;
2180afa8e06SEd Maste 			break;
2190afa8e06SEd Maste 		case 'b':
2200afa8e06SEd Maste 			ext |= FIDO_EXT_LARGEBLOB_KEY;
2210afa8e06SEd Maste 			blobkey_out = optarg;
2220afa8e06SEd Maste 			break;
2230afa8e06SEd Maste 		case 'h':
2240afa8e06SEd Maste 			hmac_out = optarg;
2250afa8e06SEd Maste 			break;
2260afa8e06SEd Maste 		case 'p':
2270afa8e06SEd Maste 			up = true;
2280afa8e06SEd Maste 			break;
2290afa8e06SEd Maste 		case 's':
2300afa8e06SEd Maste 			ext |= FIDO_EXT_HMAC_SECRET;
2310afa8e06SEd Maste 			if (read_blob(optarg, &body, &len) < 0)
2320afa8e06SEd Maste 				errx(1, "read_blob: %s", optarg);
2330afa8e06SEd Maste 			if ((r = fido_assert_set_hmac_salt(assert, body,
2340afa8e06SEd Maste 			    len)) != FIDO_OK)
2350afa8e06SEd Maste 				errx(1, "fido_assert_set_hmac_salt: %s (0x%x)",
2360afa8e06SEd Maste 				    fido_strerr(r), r);
2370afa8e06SEd Maste 			free(body);
2380afa8e06SEd Maste 			body = NULL;
2390afa8e06SEd Maste 			break;
2400afa8e06SEd Maste 		case 't':
241*2ccfa855SEd Maste 			if (strcmp(optarg, "es256") == 0)
2420afa8e06SEd Maste 				type = COSE_ES256;
243*2ccfa855SEd Maste 			else if (strcmp(optarg, "es384") == 0)
244*2ccfa855SEd Maste 				type = COSE_ES384;
245*2ccfa855SEd Maste 			else if (strcmp(optarg, "rs256") == 0)
2460afa8e06SEd Maste 				type = COSE_RS256;
2470afa8e06SEd Maste 			else if (strcmp(optarg, "eddsa") == 0)
2480afa8e06SEd Maste 				type = COSE_EDDSA;
2490afa8e06SEd Maste 			else
2500afa8e06SEd Maste 				errx(1, "unknown type %s", optarg);
2510afa8e06SEd Maste 			break;
2520afa8e06SEd Maste 		case 'u':
2530afa8e06SEd Maste 			u2f = true;
2540afa8e06SEd Maste 			break;
2550afa8e06SEd Maste 		case 'v':
2560afa8e06SEd Maste 			uv = true;
2570afa8e06SEd Maste 			break;
2580afa8e06SEd Maste 		default:
2590afa8e06SEd Maste 			usage();
2600afa8e06SEd Maste 		}
2610afa8e06SEd Maste 	}
2620afa8e06SEd Maste 
2630afa8e06SEd Maste 	argc -= optind;
2640afa8e06SEd Maste 	argv += optind;
2650afa8e06SEd Maste 
2660afa8e06SEd Maste 	if (argc != 2)
2670afa8e06SEd Maste 		usage();
2680afa8e06SEd Maste 
2690afa8e06SEd Maste 	fido_init(0);
2700afa8e06SEd Maste 
2710afa8e06SEd Maste 	if ((dev = fido_dev_new()) == NULL)
2720afa8e06SEd Maste 		errx(1, "fido_dev_new");
2730afa8e06SEd Maste 
2740afa8e06SEd Maste 	r = fido_dev_open(dev, argv[1]);
2750afa8e06SEd Maste 	if (r != FIDO_OK)
2760afa8e06SEd Maste 		errx(1, "fido_dev_open: %s (0x%x)", fido_strerr(r), r);
2770afa8e06SEd Maste 	if (u2f)
2780afa8e06SEd Maste 		fido_dev_force_u2f(dev);
2790afa8e06SEd Maste 
2800afa8e06SEd Maste 	/* client data hash */
281f540a430SEd Maste 	r = fido_assert_set_clientdata(assert, cd, sizeof(cd));
2820afa8e06SEd Maste 	if (r != FIDO_OK)
283f540a430SEd Maste 		errx(1, "fido_assert_set_clientdata: %s (0x%x)", fido_strerr(r), r);
2840afa8e06SEd Maste 
2850afa8e06SEd Maste 	/* relying party */
2860afa8e06SEd Maste 	r = fido_assert_set_rp(assert, "localhost");
2870afa8e06SEd Maste 	if (r != FIDO_OK)
2880afa8e06SEd Maste 		errx(1, "fido_assert_set_rp: %s (0x%x)", fido_strerr(r), r);
2890afa8e06SEd Maste 
2900afa8e06SEd Maste 	/* extensions */
2910afa8e06SEd Maste 	r = fido_assert_set_extensions(assert, ext);
2920afa8e06SEd Maste 	if (r != FIDO_OK)
2930afa8e06SEd Maste 		errx(1, "fido_assert_set_extensions: %s (0x%x)", fido_strerr(r),
2940afa8e06SEd Maste 		    r);
2950afa8e06SEd Maste 
2960afa8e06SEd Maste 	/* user presence */
2970afa8e06SEd Maste 	if (up && (r = fido_assert_set_up(assert, FIDO_OPT_TRUE)) != FIDO_OK)
2980afa8e06SEd Maste 		errx(1, "fido_assert_set_up: %s (0x%x)", fido_strerr(r), r);
2990afa8e06SEd Maste 
3000afa8e06SEd Maste 	/* user verification */
3010afa8e06SEd Maste 	if (uv && (r = fido_assert_set_uv(assert, FIDO_OPT_TRUE)) != FIDO_OK)
3020afa8e06SEd Maste 		errx(1, "fido_assert_set_uv: %s (0x%x)", fido_strerr(r), r);
3030afa8e06SEd Maste 
304f540a430SEd Maste 	/* timeout */
305f540a430SEd Maste 	if (ms != 0 && (r = fido_dev_set_timeout(dev, (int)ms)) != FIDO_OK)
306f540a430SEd Maste 		errx(1, "fido_dev_set_timeout: %s (0x%x)", fido_strerr(r), r);
3070afa8e06SEd Maste 
308f540a430SEd Maste 	if ((r = fido_dev_get_assert(dev, assert, pin)) != FIDO_OK) {
3090afa8e06SEd Maste 		fido_dev_cancel(dev);
3100afa8e06SEd Maste 		errx(1, "fido_dev_get_assert: %s (0x%x)", fido_strerr(r), r);
3110afa8e06SEd Maste 	}
3120afa8e06SEd Maste 
3130afa8e06SEd Maste 	r = fido_dev_close(dev);
3140afa8e06SEd Maste 	if (r != FIDO_OK)
3150afa8e06SEd Maste 		errx(1, "fido_dev_close: %s (0x%x)", fido_strerr(r), r);
3160afa8e06SEd Maste 
3170afa8e06SEd Maste 	fido_dev_free(&dev);
3180afa8e06SEd Maste 
3190afa8e06SEd Maste 	if (fido_assert_count(assert) != 1)
3200afa8e06SEd Maste 		errx(1, "fido_assert_count: %d signatures returned",
3210afa8e06SEd Maste 		    (int)fido_assert_count(assert));
3220afa8e06SEd Maste 
3230afa8e06SEd Maste 	/* when verifying, pin implies uv */
3240afa8e06SEd Maste 	if (pin)
3250afa8e06SEd Maste 		uv = true;
3260afa8e06SEd Maste 
3270afa8e06SEd Maste 	verify_assert(type, fido_assert_authdata_ptr(assert, 0),
3280afa8e06SEd Maste 	    fido_assert_authdata_len(assert, 0), fido_assert_sig_ptr(assert, 0),
3290afa8e06SEd Maste 	    fido_assert_sig_len(assert, 0), up, uv, ext, argv[0]);
3300afa8e06SEd Maste 
3310afa8e06SEd Maste 	if (hmac_out != NULL) {
3320afa8e06SEd Maste 		/* extract the hmac secret */
3330afa8e06SEd Maste 		if (write_blob(hmac_out, fido_assert_hmac_secret_ptr(assert, 0),
3340afa8e06SEd Maste 		    fido_assert_hmac_secret_len(assert, 0)) < 0)
3350afa8e06SEd Maste 			errx(1, "write_blob");
3360afa8e06SEd Maste 	}
3370afa8e06SEd Maste 
3380afa8e06SEd Maste 	if (blobkey_out != NULL) {
3390afa8e06SEd Maste 		/* extract the hmac secret */
3400afa8e06SEd Maste 		if (write_blob(blobkey_out,
3410afa8e06SEd Maste 		    fido_assert_largeblob_key_ptr(assert, 0),
3420afa8e06SEd Maste 		    fido_assert_largeblob_key_len(assert, 0)) < 0)
3430afa8e06SEd Maste 			errx(1, "write_blob");
3440afa8e06SEd Maste 	}
3450afa8e06SEd Maste 
3460afa8e06SEd Maste 	fido_assert_free(&assert);
3470afa8e06SEd Maste 
3480afa8e06SEd Maste 	exit(0);
3490afa8e06SEd Maste }
350