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