xref: /netbsd-src/external/bsd/libfido2/dist/examples/assert.c (revision 2d40c4512a84c0d064ec30a492c5e2a14d230bc3)
1ba9bdd8bSchristos /*
2*2d40c451Schristos  * Copyright (c) 2018-2022 Yubico AB. All rights reserved.
3ba9bdd8bSchristos  * Use of this source code is governed by a BSD-style
4ba9bdd8bSchristos  * license that can be found in the LICENSE file.
5*2d40c451Schristos  * SPDX-License-Identifier: BSD-2-Clause
6ba9bdd8bSchristos  */
7ba9bdd8bSchristos 
895dbdf32Schristos #include <fido.h>
995dbdf32Schristos #include <fido/es256.h>
10*2d40c451Schristos #include <fido/es384.h>
1195dbdf32Schristos #include <fido/rs256.h>
1295dbdf32Schristos #include <fido/eddsa.h>
13ba9bdd8bSchristos 
14ba9bdd8bSchristos #include <stdbool.h>
15ba9bdd8bSchristos #include <stdio.h>
16ba9bdd8bSchristos #include <stdlib.h>
17ba9bdd8bSchristos #include <string.h>
18ba9bdd8bSchristos #ifdef HAVE_UNISTD_H
19ba9bdd8bSchristos #include <unistd.h>
20ba9bdd8bSchristos #endif
21ba9bdd8bSchristos 
221fc1e710Schristos #include "../openbsd-compat/openbsd-compat.h"
2395dbdf32Schristos #include "extern.h"
24ba9bdd8bSchristos 
25*2d40c451Schristos static const unsigned char cd[32] = {
26ba9bdd8bSchristos 	0xec, 0x8d, 0x8f, 0x78, 0x42, 0x4a, 0x2b, 0xb7,
27ba9bdd8bSchristos 	0x82, 0x34, 0xaa, 0xca, 0x07, 0xa1, 0xf6, 0x56,
28ba9bdd8bSchristos 	0x42, 0x1c, 0xb6, 0xf6, 0xb3, 0x00, 0x86, 0x52,
29ba9bdd8bSchristos 	0x35, 0x2d, 0xa2, 0x62, 0x4a, 0xbe, 0x89, 0x76,
30ba9bdd8bSchristos };
31ba9bdd8bSchristos 
32ba9bdd8bSchristos static void
usage(void)33ba9bdd8bSchristos usage(void)
34ba9bdd8bSchristos {
35*2d40c451Schristos 	fprintf(stderr, "usage: assert [-t es256|es384|rs256|eddsa] "
36*2d40c451Schristos 	    "[-a cred_id] [-h hmac_secret] [-s hmac_salt] [-P pin] "
37*2d40c451Schristos 	    "[-T seconds] [-b blobkey] [-puv] <pubkey> <device>\n");
38ba9bdd8bSchristos 	exit(EXIT_FAILURE);
39ba9bdd8bSchristos }
40ba9bdd8bSchristos 
41ba9bdd8bSchristos 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)42ba9bdd8bSchristos verify_assert(int type, const unsigned char *authdata_ptr, size_t authdata_len,
43ba9bdd8bSchristos     const unsigned char *sig_ptr, size_t sig_len, bool up, bool uv, int ext,
44ba9bdd8bSchristos     const char *key)
45ba9bdd8bSchristos {
46ba9bdd8bSchristos 	fido_assert_t	*assert = NULL;
47ba9bdd8bSchristos 	EC_KEY		*ec = NULL;
48ba9bdd8bSchristos 	RSA		*rsa = NULL;
49ba9bdd8bSchristos 	EVP_PKEY	*eddsa = NULL;
50ba9bdd8bSchristos 	es256_pk_t	*es256_pk = NULL;
51*2d40c451Schristos 	es384_pk_t	*es384_pk = NULL;
52ba9bdd8bSchristos 	rs256_pk_t	*rs256_pk = NULL;
53ba9bdd8bSchristos 	eddsa_pk_t	*eddsa_pk = NULL;
54ba9bdd8bSchristos 	void		*pk;
55ba9bdd8bSchristos 	int		 r;
56ba9bdd8bSchristos 
57ba9bdd8bSchristos 	/* credential pubkey */
58ba9bdd8bSchristos 	switch (type) {
59ba9bdd8bSchristos 	case COSE_ES256:
60ba9bdd8bSchristos 		if ((ec = read_ec_pubkey(key)) == NULL)
61ba9bdd8bSchristos 			errx(1, "read_ec_pubkey");
62ba9bdd8bSchristos 
63ba9bdd8bSchristos 		if ((es256_pk = es256_pk_new()) == NULL)
64ba9bdd8bSchristos 			errx(1, "es256_pk_new");
65ba9bdd8bSchristos 
66ba9bdd8bSchristos 		if (es256_pk_from_EC_KEY(es256_pk, ec) != FIDO_OK)
67ba9bdd8bSchristos 			errx(1, "es256_pk_from_EC_KEY");
68ba9bdd8bSchristos 
69ba9bdd8bSchristos 		pk = es256_pk;
70ba9bdd8bSchristos 		EC_KEY_free(ec);
71ba9bdd8bSchristos 		ec = NULL;
72ba9bdd8bSchristos 
73ba9bdd8bSchristos 		break;
74*2d40c451Schristos 	case COSE_ES384:
75*2d40c451Schristos 		if ((ec = read_ec_pubkey(key)) == NULL)
76*2d40c451Schristos 			errx(1, "read_ec_pubkey");
77*2d40c451Schristos 
78*2d40c451Schristos 		if ((es384_pk = es384_pk_new()) == NULL)
79*2d40c451Schristos 			errx(1, "es384_pk_new");
80*2d40c451Schristos 
81*2d40c451Schristos 		if (es384_pk_from_EC_KEY(es384_pk, ec) != FIDO_OK)
82*2d40c451Schristos 			errx(1, "es384_pk_from_EC_KEY");
83*2d40c451Schristos 
84*2d40c451Schristos 		pk = es384_pk;
85*2d40c451Schristos 		EC_KEY_free(ec);
86*2d40c451Schristos 		ec = NULL;
87*2d40c451Schristos 
88*2d40c451Schristos 		break;
89ba9bdd8bSchristos 	case COSE_RS256:
90ba9bdd8bSchristos 		if ((rsa = read_rsa_pubkey(key)) == NULL)
91ba9bdd8bSchristos 			errx(1, "read_rsa_pubkey");
92ba9bdd8bSchristos 
93ba9bdd8bSchristos 		if ((rs256_pk = rs256_pk_new()) == NULL)
94ba9bdd8bSchristos 			errx(1, "rs256_pk_new");
95ba9bdd8bSchristos 
96ba9bdd8bSchristos 		if (rs256_pk_from_RSA(rs256_pk, rsa) != FIDO_OK)
97ba9bdd8bSchristos 			errx(1, "rs256_pk_from_RSA");
98ba9bdd8bSchristos 
99ba9bdd8bSchristos 		pk = rs256_pk;
100ba9bdd8bSchristos 		RSA_free(rsa);
101ba9bdd8bSchristos 		rsa = NULL;
102ba9bdd8bSchristos 
103ba9bdd8bSchristos 		break;
104ba9bdd8bSchristos 	case COSE_EDDSA:
105ba9bdd8bSchristos 		if ((eddsa = read_eddsa_pubkey(key)) == NULL)
106ba9bdd8bSchristos 			errx(1, "read_eddsa_pubkey");
107ba9bdd8bSchristos 
108ba9bdd8bSchristos 		if ((eddsa_pk = eddsa_pk_new()) == NULL)
109ba9bdd8bSchristos 			errx(1, "eddsa_pk_new");
110ba9bdd8bSchristos 
111ba9bdd8bSchristos 		if (eddsa_pk_from_EVP_PKEY(eddsa_pk, eddsa) != FIDO_OK)
112ba9bdd8bSchristos 			errx(1, "eddsa_pk_from_EVP_PKEY");
113ba9bdd8bSchristos 
114ba9bdd8bSchristos 		pk = eddsa_pk;
115ba9bdd8bSchristos 		EVP_PKEY_free(eddsa);
116ba9bdd8bSchristos 		eddsa = NULL;
117ba9bdd8bSchristos 
118ba9bdd8bSchristos 		break;
119ba9bdd8bSchristos 	default:
120ba9bdd8bSchristos 		errx(1, "unknown credential type %d", type);
121ba9bdd8bSchristos 	}
122ba9bdd8bSchristos 
123ba9bdd8bSchristos 	if ((assert = fido_assert_new()) == NULL)
124ba9bdd8bSchristos 		errx(1, "fido_assert_new");
125ba9bdd8bSchristos 
126ba9bdd8bSchristos 	/* client data hash */
127*2d40c451Schristos 	r = fido_assert_set_clientdata(assert, cd, sizeof(cd));
128ba9bdd8bSchristos 	if (r != FIDO_OK)
129*2d40c451Schristos 		errx(1, "fido_assert_set_clientdata: %s (0x%x)", fido_strerr(r), r);
130ba9bdd8bSchristos 
131ba9bdd8bSchristos 	/* relying party */
132ba9bdd8bSchristos 	r = fido_assert_set_rp(assert, "localhost");
133ba9bdd8bSchristos 	if (r != FIDO_OK)
134ba9bdd8bSchristos 		errx(1, "fido_assert_set_rp: %s (0x%x)", fido_strerr(r), r);
135ba9bdd8bSchristos 
136ba9bdd8bSchristos 	/* authdata */
137ba9bdd8bSchristos 	r = fido_assert_set_count(assert, 1);
138ba9bdd8bSchristos 	if (r != FIDO_OK)
139ba9bdd8bSchristos 		errx(1, "fido_assert_set_count: %s (0x%x)", fido_strerr(r), r);
140ba9bdd8bSchristos 	r = fido_assert_set_authdata(assert, 0, authdata_ptr, authdata_len);
141ba9bdd8bSchristos 	if (r != FIDO_OK)
142ba9bdd8bSchristos 		errx(1, "fido_assert_set_authdata: %s (0x%x)", fido_strerr(r), r);
143ba9bdd8bSchristos 
144ba9bdd8bSchristos 	/* extension */
145ba9bdd8bSchristos 	r = fido_assert_set_extensions(assert, ext);
146ba9bdd8bSchristos 	if (r != FIDO_OK)
147ba9bdd8bSchristos 		errx(1, "fido_assert_set_extensions: %s (0x%x)", fido_strerr(r),
148ba9bdd8bSchristos 		    r);
149ba9bdd8bSchristos 
150ba9bdd8bSchristos 	/* user presence */
151ba9bdd8bSchristos 	if (up && (r = fido_assert_set_up(assert, FIDO_OPT_TRUE)) != FIDO_OK)
152ba9bdd8bSchristos 		errx(1, "fido_assert_set_up: %s (0x%x)", fido_strerr(r), r);
153ba9bdd8bSchristos 
154ba9bdd8bSchristos 	/* user verification */
155ba9bdd8bSchristos 	if (uv && (r = fido_assert_set_uv(assert, FIDO_OPT_TRUE)) != FIDO_OK)
156ba9bdd8bSchristos 		errx(1, "fido_assert_set_uv: %s (0x%x)", fido_strerr(r), r);
157ba9bdd8bSchristos 
158ba9bdd8bSchristos 	/* sig */
159ba9bdd8bSchristos 	r = fido_assert_set_sig(assert, 0, sig_ptr, sig_len);
160ba9bdd8bSchristos 	if (r != FIDO_OK)
161ba9bdd8bSchristos 		errx(1, "fido_assert_set_sig: %s (0x%x)", fido_strerr(r), r);
162ba9bdd8bSchristos 
163ba9bdd8bSchristos 	r = fido_assert_verify(assert, 0, type, pk);
164ba9bdd8bSchristos 	if (r != FIDO_OK)
165ba9bdd8bSchristos 		errx(1, "fido_assert_verify: %s (0x%x)", fido_strerr(r), r);
166ba9bdd8bSchristos 
167ba9bdd8bSchristos 	es256_pk_free(&es256_pk);
168*2d40c451Schristos 	es384_pk_free(&es384_pk);
169ba9bdd8bSchristos 	rs256_pk_free(&rs256_pk);
170ba9bdd8bSchristos 	eddsa_pk_free(&eddsa_pk);
171ba9bdd8bSchristos 
172ba9bdd8bSchristos 	fido_assert_free(&assert);
173ba9bdd8bSchristos }
174ba9bdd8bSchristos 
175ba9bdd8bSchristos int
main(int argc,char ** argv)176ba9bdd8bSchristos main(int argc, char **argv)
177ba9bdd8bSchristos {
178ba9bdd8bSchristos 	bool		 up = false;
179ba9bdd8bSchristos 	bool		 uv = false;
180ba9bdd8bSchristos 	bool		 u2f = false;
181ba9bdd8bSchristos 	fido_dev_t	*dev = NULL;
182ba9bdd8bSchristos 	fido_assert_t	*assert = NULL;
183ba9bdd8bSchristos 	const char	*pin = NULL;
18495dbdf32Schristos 	const char	*blobkey_out = NULL;
185ba9bdd8bSchristos 	const char	*hmac_out = NULL;
186ba9bdd8bSchristos 	unsigned char	*body = NULL;
187*2d40c451Schristos 	long long	 ms = 0;
188ba9bdd8bSchristos 	size_t		 len;
189ba9bdd8bSchristos 	int		 type = COSE_ES256;
190ba9bdd8bSchristos 	int		 ext = 0;
191ba9bdd8bSchristos 	int		 ch;
192ba9bdd8bSchristos 	int		 r;
193ba9bdd8bSchristos 
194ba9bdd8bSchristos 	if ((assert = fido_assert_new()) == NULL)
195ba9bdd8bSchristos 		errx(1, "fido_assert_new");
196ba9bdd8bSchristos 
19795dbdf32Schristos 	while ((ch = getopt(argc, argv, "P:T:a:b:h:ps:t:uv")) != -1) {
198ba9bdd8bSchristos 		switch (ch) {
199ba9bdd8bSchristos 		case 'P':
200ba9bdd8bSchristos 			pin = optarg;
201ba9bdd8bSchristos 			break;
202ba9bdd8bSchristos 		case 'T':
203*2d40c451Schristos 			if (base10(optarg, &ms) < 0)
204ba9bdd8bSchristos 				errx(1, "base10: %s", optarg);
205*2d40c451Schristos 			if (ms <= 0 || ms > 30)
206ba9bdd8bSchristos 				errx(1, "-T: %s must be in (0,30]", optarg);
207*2d40c451Schristos 			ms *= 1000; /* seconds to milliseconds */
208ba9bdd8bSchristos 			break;
209ba9bdd8bSchristos 		case 'a':
210ba9bdd8bSchristos 			if (read_blob(optarg, &body, &len) < 0)
211ba9bdd8bSchristos 				errx(1, "read_blob: %s", optarg);
212ba9bdd8bSchristos 			if ((r = fido_assert_allow_cred(assert, body,
213ba9bdd8bSchristos 			    len)) != FIDO_OK)
214ba9bdd8bSchristos 				errx(1, "fido_assert_allow_cred: %s (0x%x)",
215ba9bdd8bSchristos 				    fido_strerr(r), r);
216ba9bdd8bSchristos 			free(body);
217ba9bdd8bSchristos 			body = NULL;
218ba9bdd8bSchristos 			break;
21995dbdf32Schristos 		case 'b':
22095dbdf32Schristos 			ext |= FIDO_EXT_LARGEBLOB_KEY;
22195dbdf32Schristos 			blobkey_out = optarg;
22295dbdf32Schristos 			break;
223ba9bdd8bSchristos 		case 'h':
224ba9bdd8bSchristos 			hmac_out = optarg;
225ba9bdd8bSchristos 			break;
226ba9bdd8bSchristos 		case 'p':
227ba9bdd8bSchristos 			up = true;
228ba9bdd8bSchristos 			break;
229ba9bdd8bSchristos 		case 's':
23095dbdf32Schristos 			ext |= FIDO_EXT_HMAC_SECRET;
231ba9bdd8bSchristos 			if (read_blob(optarg, &body, &len) < 0)
232ba9bdd8bSchristos 				errx(1, "read_blob: %s", optarg);
233ba9bdd8bSchristos 			if ((r = fido_assert_set_hmac_salt(assert, body,
234ba9bdd8bSchristos 			    len)) != FIDO_OK)
235ba9bdd8bSchristos 				errx(1, "fido_assert_set_hmac_salt: %s (0x%x)",
236ba9bdd8bSchristos 				    fido_strerr(r), r);
237ba9bdd8bSchristos 			free(body);
238ba9bdd8bSchristos 			body = NULL;
239ba9bdd8bSchristos 			break;
240ba9bdd8bSchristos 		case 't':
241*2d40c451Schristos 			if (strcmp(optarg, "es256") == 0)
242ba9bdd8bSchristos 				type = COSE_ES256;
243*2d40c451Schristos 			else if (strcmp(optarg, "es384") == 0)
244*2d40c451Schristos 				type = COSE_ES384;
245*2d40c451Schristos 			else if (strcmp(optarg, "rs256") == 0)
246ba9bdd8bSchristos 				type = COSE_RS256;
247ba9bdd8bSchristos 			else if (strcmp(optarg, "eddsa") == 0)
248ba9bdd8bSchristos 				type = COSE_EDDSA;
249ba9bdd8bSchristos 			else
250ba9bdd8bSchristos 				errx(1, "unknown type %s", optarg);
251ba9bdd8bSchristos 			break;
252ba9bdd8bSchristos 		case 'u':
253ba9bdd8bSchristos 			u2f = true;
254ba9bdd8bSchristos 			break;
255ba9bdd8bSchristos 		case 'v':
256ba9bdd8bSchristos 			uv = true;
257ba9bdd8bSchristos 			break;
258ba9bdd8bSchristos 		default:
259ba9bdd8bSchristos 			usage();
260ba9bdd8bSchristos 		}
261ba9bdd8bSchristos 	}
262ba9bdd8bSchristos 
263ba9bdd8bSchristos 	argc -= optind;
264ba9bdd8bSchristos 	argv += optind;
265ba9bdd8bSchristos 
266ba9bdd8bSchristos 	if (argc != 2)
267ba9bdd8bSchristos 		usage();
268ba9bdd8bSchristos 
269ba9bdd8bSchristos 	fido_init(0);
270ba9bdd8bSchristos 
271ba9bdd8bSchristos 	if ((dev = fido_dev_new()) == NULL)
272ba9bdd8bSchristos 		errx(1, "fido_dev_new");
273ba9bdd8bSchristos 
274ba9bdd8bSchristos 	r = fido_dev_open(dev, argv[1]);
275ba9bdd8bSchristos 	if (r != FIDO_OK)
276ba9bdd8bSchristos 		errx(1, "fido_dev_open: %s (0x%x)", fido_strerr(r), r);
277ba9bdd8bSchristos 	if (u2f)
278ba9bdd8bSchristos 		fido_dev_force_u2f(dev);
279ba9bdd8bSchristos 
280ba9bdd8bSchristos 	/* client data hash */
281*2d40c451Schristos 	r = fido_assert_set_clientdata(assert, cd, sizeof(cd));
282ba9bdd8bSchristos 	if (r != FIDO_OK)
283*2d40c451Schristos 		errx(1, "fido_assert_set_clientdata: %s (0x%x)", fido_strerr(r), r);
284ba9bdd8bSchristos 
285ba9bdd8bSchristos 	/* relying party */
286ba9bdd8bSchristos 	r = fido_assert_set_rp(assert, "localhost");
287ba9bdd8bSchristos 	if (r != FIDO_OK)
288ba9bdd8bSchristos 		errx(1, "fido_assert_set_rp: %s (0x%x)", fido_strerr(r), r);
289ba9bdd8bSchristos 
290ba9bdd8bSchristos 	/* extensions */
291ba9bdd8bSchristos 	r = fido_assert_set_extensions(assert, ext);
292ba9bdd8bSchristos 	if (r != FIDO_OK)
293ba9bdd8bSchristos 		errx(1, "fido_assert_set_extensions: %s (0x%x)", fido_strerr(r),
294ba9bdd8bSchristos 		    r);
295ba9bdd8bSchristos 
296ba9bdd8bSchristos 	/* user presence */
297ba9bdd8bSchristos 	if (up && (r = fido_assert_set_up(assert, FIDO_OPT_TRUE)) != FIDO_OK)
298ba9bdd8bSchristos 		errx(1, "fido_assert_set_up: %s (0x%x)", fido_strerr(r), r);
299ba9bdd8bSchristos 
300ba9bdd8bSchristos 	/* user verification */
301ba9bdd8bSchristos 	if (uv && (r = fido_assert_set_uv(assert, FIDO_OPT_TRUE)) != FIDO_OK)
302ba9bdd8bSchristos 		errx(1, "fido_assert_set_uv: %s (0x%x)", fido_strerr(r), r);
303ba9bdd8bSchristos 
304*2d40c451Schristos 	/* timeout */
305*2d40c451Schristos 	if (ms != 0 && (r = fido_dev_set_timeout(dev, (int)ms)) != FIDO_OK)
306*2d40c451Schristos 		errx(1, "fido_dev_set_timeout: %s (0x%x)", fido_strerr(r), r);
307ba9bdd8bSchristos 
308*2d40c451Schristos 	if ((r = fido_dev_get_assert(dev, assert, pin)) != FIDO_OK) {
309ba9bdd8bSchristos 		fido_dev_cancel(dev);
310ba9bdd8bSchristos 		errx(1, "fido_dev_get_assert: %s (0x%x)", fido_strerr(r), r);
311ba9bdd8bSchristos 	}
312ba9bdd8bSchristos 
313ba9bdd8bSchristos 	r = fido_dev_close(dev);
314ba9bdd8bSchristos 	if (r != FIDO_OK)
315ba9bdd8bSchristos 		errx(1, "fido_dev_close: %s (0x%x)", fido_strerr(r), r);
316ba9bdd8bSchristos 
317ba9bdd8bSchristos 	fido_dev_free(&dev);
318ba9bdd8bSchristos 
319ba9bdd8bSchristos 	if (fido_assert_count(assert) != 1)
320ba9bdd8bSchristos 		errx(1, "fido_assert_count: %d signatures returned",
321ba9bdd8bSchristos 		    (int)fido_assert_count(assert));
322ba9bdd8bSchristos 
3231fc1e710Schristos 	/* when verifying, pin implies uv */
3241fc1e710Schristos 	if (pin)
3251fc1e710Schristos 		uv = true;
3261fc1e710Schristos 
327ba9bdd8bSchristos 	verify_assert(type, fido_assert_authdata_ptr(assert, 0),
328ba9bdd8bSchristos 	    fido_assert_authdata_len(assert, 0), fido_assert_sig_ptr(assert, 0),
329ba9bdd8bSchristos 	    fido_assert_sig_len(assert, 0), up, uv, ext, argv[0]);
330ba9bdd8bSchristos 
331ba9bdd8bSchristos 	if (hmac_out != NULL) {
332ba9bdd8bSchristos 		/* extract the hmac secret */
333ba9bdd8bSchristos 		if (write_blob(hmac_out, fido_assert_hmac_secret_ptr(assert, 0),
334ba9bdd8bSchristos 		    fido_assert_hmac_secret_len(assert, 0)) < 0)
335ba9bdd8bSchristos 			errx(1, "write_blob");
336ba9bdd8bSchristos 	}
337ba9bdd8bSchristos 
33895dbdf32Schristos 	if (blobkey_out != NULL) {
33995dbdf32Schristos 		/* extract the hmac secret */
34095dbdf32Schristos 		if (write_blob(blobkey_out,
34195dbdf32Schristos 		    fido_assert_largeblob_key_ptr(assert, 0),
34295dbdf32Schristos 		    fido_assert_largeblob_key_len(assert, 0)) < 0)
34395dbdf32Schristos 			errx(1, "write_blob");
34495dbdf32Schristos 	}
34595dbdf32Schristos 
346ba9bdd8bSchristos 	fido_assert_free(&assert);
347ba9bdd8bSchristos 
348ba9bdd8bSchristos 	exit(0);
349ba9bdd8bSchristos }
350