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