xref: /freebsd/contrib/libfido2/examples/cred.c (revision e5b786625f7f82a1fa91e41823332459ea5550f9)
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 
21 static 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 
28 static 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 
35 static void
36 usage(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 
44 static void
45 verify_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 
107 out:
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 
143 int
144 main(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