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 <errno.h>
9#include <fido.h>
10#include <stdbool.h>
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14#ifdef HAVE_UNISTD_H
15#include <unistd.h>
16#endif
17
18#include "../openbsd-compat/openbsd-compat.h"
19#include "extern.h"
20
21static const unsigned char cd[32] = {
22	0xf9, 0x64, 0x57, 0xe7, 0x2d, 0x97, 0xf6, 0xbb,
23	0xdd, 0xd7, 0xfb, 0x06, 0x37, 0x62, 0xea, 0x26,
24	0x20, 0x44, 0x8e, 0x69, 0x7c, 0x03, 0xf2, 0x31,
25	0x2f, 0x99, 0xdc, 0xaf, 0x3e, 0x8a, 0x91, 0x6b,
26};
27
28static const unsigned char user_id[32] = {
29	0x78, 0x1c, 0x78, 0x60, 0xad, 0x88, 0xd2, 0x63,
30	0x32, 0x62, 0x2a, 0xf1, 0x74, 0x5d, 0xed, 0xb2,
31	0xe7, 0xa4, 0x2b, 0x44, 0x89, 0x29, 0x39, 0xc5,
32	0x56, 0x64, 0x01, 0x27, 0x0d, 0xbb, 0xc4, 0x49,
33};
34
35static void
36usage(void)
37{
38	fprintf(stderr, "usage: cred [-t es256|es384|rs256|eddsa] [-k pubkey] "
39	    "[-ei cred_id] [-P pin] [-T seconds] [-b blobkey] [-hruv] "
40	    "<device>\n");
41	exit(EXIT_FAILURE);
42}
43
44static void
45verify_cred(int type, const char *fmt, const unsigned char *authdata_ptr,
46    size_t authdata_len, const unsigned char *attstmt_ptr, size_t attstmt_len,
47    bool rk, bool uv, int ext, const char *key_out, const char *id_out)
48{
49	fido_cred_t	*cred;
50	int		 r;
51
52	if ((cred = fido_cred_new()) == NULL)
53		errx(1, "fido_cred_new");
54
55	/* type */
56	r = fido_cred_set_type(cred, type);
57	if (r != FIDO_OK)
58		errx(1, "fido_cred_set_type: %s (0x%x)", fido_strerr(r), r);
59
60	/* client data */
61	r = fido_cred_set_clientdata(cred, cd, sizeof(cd));
62	if (r != FIDO_OK)
63		errx(1, "fido_cred_set_clientdata: %s (0x%x)", fido_strerr(r), r);
64
65	/* relying party */
66	r = fido_cred_set_rp(cred, "localhost", "sweet home localhost");
67	if (r != FIDO_OK)
68		errx(1, "fido_cred_set_rp: %s (0x%x)", fido_strerr(r), r);
69
70	/* authdata */
71	r = fido_cred_set_authdata(cred, authdata_ptr, authdata_len);
72	if (r != FIDO_OK)
73		errx(1, "fido_cred_set_authdata: %s (0x%x)", fido_strerr(r), r);
74
75	/* extensions */
76	r = fido_cred_set_extensions(cred, ext);
77	if (r != FIDO_OK)
78		errx(1, "fido_cred_set_extensions: %s (0x%x)", fido_strerr(r), r);
79
80	/* resident key */
81	if (rk && (r = fido_cred_set_rk(cred, FIDO_OPT_TRUE)) != FIDO_OK)
82		errx(1, "fido_cred_set_rk: %s (0x%x)", fido_strerr(r), r);
83
84	/* user verification */
85	if (uv && (r = fido_cred_set_uv(cred, FIDO_OPT_TRUE)) != FIDO_OK)
86		errx(1, "fido_cred_set_uv: %s (0x%x)", fido_strerr(r), r);
87
88	/* fmt */
89	r = fido_cred_set_fmt(cred, fmt);
90	if (r != FIDO_OK)
91		errx(1, "fido_cred_set_fmt: %s (0x%x)", fido_strerr(r), r);
92
93	if (!strcmp(fido_cred_fmt(cred), "none")) {
94		warnx("no attestation data, skipping credential verification");
95		goto out;
96	}
97
98	/* attestation statement */
99	r = fido_cred_set_attstmt(cred, attstmt_ptr, attstmt_len);
100	if (r != FIDO_OK)
101		errx(1, "fido_cred_set_attstmt: %s (0x%x)", fido_strerr(r), r);
102
103	r = fido_cred_verify(cred);
104	if (r != FIDO_OK)
105		errx(1, "fido_cred_verify: %s (0x%x)", fido_strerr(r), r);
106
107out:
108	if (key_out != NULL) {
109		/* extract the credential pubkey */
110		if (type == COSE_ES256) {
111			if (write_es256_pubkey(key_out,
112			    fido_cred_pubkey_ptr(cred),
113			    fido_cred_pubkey_len(cred)) < 0)
114				errx(1, "write_es256_pubkey");
115		} else if (type == COSE_ES384) {
116			if (write_es384_pubkey(key_out,
117			    fido_cred_pubkey_ptr(cred),
118			    fido_cred_pubkey_len(cred)) < 0)
119				errx(1, "write_es384_pubkey");
120		} else if (type == COSE_RS256) {
121			if (write_rs256_pubkey(key_out,
122			    fido_cred_pubkey_ptr(cred),
123			    fido_cred_pubkey_len(cred)) < 0)
124				errx(1, "write_rs256_pubkey");
125		} else if (type == COSE_EDDSA) {
126			if (write_eddsa_pubkey(key_out,
127			    fido_cred_pubkey_ptr(cred),
128			    fido_cred_pubkey_len(cred)) < 0)
129				errx(1, "write_eddsa_pubkey");
130		}
131	}
132
133	if (id_out != NULL) {
134		/* extract the credential id */
135		if (write_blob(id_out, fido_cred_id_ptr(cred),
136		    fido_cred_id_len(cred)) < 0)
137			errx(1, "write_blob");
138	}
139
140	fido_cred_free(&cred);
141}
142
143int
144main(int argc, char **argv)
145{
146	bool		 rk = false;
147	bool		 uv = false;
148	bool		 u2f = false;
149	fido_dev_t	*dev;
150	fido_cred_t	*cred = NULL;
151	const char	*pin = NULL;
152	const char	*blobkey_out = NULL;
153	const char	*key_out = NULL;
154	const char	*id_out = NULL;
155	unsigned char	*body = NULL;
156	long long	 ms = 0;
157	size_t		 len;
158	int		 type = COSE_ES256;
159	int		 ext = 0;
160	int		 ch;
161	int		 r;
162
163	if ((cred = fido_cred_new()) == NULL)
164		errx(1, "fido_cred_new");
165
166	while ((ch = getopt(argc, argv, "P:T:b:e:hi:k:rt:uv")) != -1) {
167		switch (ch) {
168		case 'P':
169			pin = optarg;
170			break;
171		case 'T':
172			if (base10(optarg, &ms) < 0)
173				errx(1, "base10: %s", optarg);
174			if (ms <= 0 || ms > 30)
175				errx(1, "-T: %s must be in (0,30]", optarg);
176			ms *= 1000; /* seconds to milliseconds */
177			break;
178		case 'b':
179			ext |= FIDO_EXT_LARGEBLOB_KEY;
180			blobkey_out = optarg;
181			break;
182		case 'e':
183			if (read_blob(optarg, &body, &len) < 0)
184				errx(1, "read_blob: %s", optarg);
185			r = fido_cred_exclude(cred, body, len);
186			if (r != FIDO_OK)
187				errx(1, "fido_cred_exclude: %s (0x%x)",
188				    fido_strerr(r), r);
189			free(body);
190			body = NULL;
191			break;
192		case 'h':
193			ext |= FIDO_EXT_HMAC_SECRET;
194			break;
195		case 'i':
196			id_out = optarg;
197			break;
198		case 'k':
199			key_out = optarg;
200			break;
201		case 'r':
202			rk = true;
203			break;
204		case 't':
205			if (strcmp(optarg, "es256") == 0)
206				type = COSE_ES256;
207			else if (strcmp(optarg, "es384") == 0)
208				type = COSE_ES384;
209			else if (strcmp(optarg, "rs256") == 0)
210				type = COSE_RS256;
211			else if (strcmp(optarg, "eddsa") == 0)
212				type = COSE_EDDSA;
213			else
214				errx(1, "unknown type %s", optarg);
215			break;
216		case 'u':
217			u2f = true;
218			break;
219		case 'v':
220			uv = true;
221			break;
222		default:
223			usage();
224		}
225	}
226
227	argc -= optind;
228	argv += optind;
229
230	if (argc != 1)
231		usage();
232
233	fido_init(0);
234
235	if ((dev = fido_dev_new()) == NULL)
236		errx(1, "fido_dev_new");
237
238	r = fido_dev_open(dev, argv[0]);
239	if (r != FIDO_OK)
240		errx(1, "fido_dev_open: %s (0x%x)", fido_strerr(r), r);
241	if (u2f)
242		fido_dev_force_u2f(dev);
243
244	/* type */
245	r = fido_cred_set_type(cred, type);
246	if (r != FIDO_OK)
247		errx(1, "fido_cred_set_type: %s (0x%x)", fido_strerr(r), r);
248
249	/* client data */
250	r = fido_cred_set_clientdata(cred, cd, sizeof(cd));
251	if (r != FIDO_OK)
252		errx(1, "fido_cred_set_clientdata: %s (0x%x)", fido_strerr(r), r);
253
254	/* relying party */
255	r = fido_cred_set_rp(cred, "localhost", "sweet home localhost");
256	if (r != FIDO_OK)
257		errx(1, "fido_cred_set_rp: %s (0x%x)", fido_strerr(r), r);
258
259	/* user */
260	r = fido_cred_set_user(cred, user_id, sizeof(user_id), "john smith",
261	    "jsmith", NULL);
262	if (r != FIDO_OK)
263		errx(1, "fido_cred_set_user: %s (0x%x)", fido_strerr(r), r);
264
265	/* extensions */
266	r = fido_cred_set_extensions(cred, ext);
267	if (r != FIDO_OK)
268		errx(1, "fido_cred_set_extensions: %s (0x%x)", fido_strerr(r), r);
269
270	/* resident key */
271	if (rk && (r = fido_cred_set_rk(cred, FIDO_OPT_TRUE)) != FIDO_OK)
272		errx(1, "fido_cred_set_rk: %s (0x%x)", fido_strerr(r), r);
273
274	/* user verification */
275	if (uv && (r = fido_cred_set_uv(cred, FIDO_OPT_TRUE)) != FIDO_OK)
276		errx(1, "fido_cred_set_uv: %s (0x%x)", fido_strerr(r), r);
277
278	/* timeout */
279	if (ms != 0 && (r = fido_dev_set_timeout(dev, (int)ms)) != FIDO_OK)
280		errx(1, "fido_dev_set_timeout: %s (0x%x)", fido_strerr(r), r);
281
282	if ((r = fido_dev_make_cred(dev, cred, pin)) != FIDO_OK) {
283		fido_dev_cancel(dev);
284		errx(1, "fido_makecred: %s (0x%x)", fido_strerr(r), r);
285	}
286
287	r = fido_dev_close(dev);
288	if (r != FIDO_OK)
289		errx(1, "fido_dev_close: %s (0x%x)", fido_strerr(r), r);
290
291	fido_dev_free(&dev);
292
293	/* when verifying, pin implies uv */
294	if (pin)
295		uv = true;
296
297	verify_cred(type, fido_cred_fmt(cred), fido_cred_authdata_ptr(cred),
298	    fido_cred_authdata_len(cred), fido_cred_attstmt_ptr(cred),
299	    fido_cred_attstmt_len(cred), rk, uv, ext, key_out, id_out);
300
301	if (blobkey_out != NULL) {
302		/* extract the "largeBlob" key */
303		if (write_blob(blobkey_out, fido_cred_largeblob_key_ptr(cred),
304		    fido_cred_largeblob_key_len(cred)) < 0)
305			errx(1, "write_blob");
306	}
307
308	fido_cred_free(&cred);
309
310	exit(0);
311}
312