xref: /freebsd/contrib/libfido2/examples/cred.c (revision ba3c1f5972d7b90feb6e6da47905ff2757e0fe57)
1 /*
2  * Copyright (c) 2018-2021 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 cd[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 *attstmt_ptr, size_t attstmt_len,
46     bool rk, bool uv, int ext, const char *key_out, const char *id_out)
47 {
48 	fido_cred_t	*cred;
49 	int		 r;
50 
51 	if ((cred = fido_cred_new()) == NULL)
52 		errx(1, "fido_cred_new");
53 
54 	/* type */
55 	r = fido_cred_set_type(cred, type);
56 	if (r != FIDO_OK)
57 		errx(1, "fido_cred_set_type: %s (0x%x)", fido_strerr(r), r);
58 
59 	/* client data */
60 	r = fido_cred_set_clientdata(cred, cd, sizeof(cd));
61 	if (r != FIDO_OK)
62 		errx(1, "fido_cred_set_clientdata: %s (0x%x)", fido_strerr(r), r);
63 
64 	/* relying party */
65 	r = fido_cred_set_rp(cred, "localhost", "sweet home localhost");
66 	if (r != FIDO_OK)
67 		errx(1, "fido_cred_set_rp: %s (0x%x)", fido_strerr(r), r);
68 
69 	/* authdata */
70 	r = fido_cred_set_authdata(cred, authdata_ptr, authdata_len);
71 	if (r != FIDO_OK)
72 		errx(1, "fido_cred_set_authdata: %s (0x%x)", fido_strerr(r), r);
73 
74 	/* extensions */
75 	r = fido_cred_set_extensions(cred, ext);
76 	if (r != FIDO_OK)
77 		errx(1, "fido_cred_set_extensions: %s (0x%x)", fido_strerr(r), r);
78 
79 	/* resident key */
80 	if (rk && (r = fido_cred_set_rk(cred, FIDO_OPT_TRUE)) != FIDO_OK)
81 		errx(1, "fido_cred_set_rk: %s (0x%x)", fido_strerr(r), r);
82 
83 	/* user verification */
84 	if (uv && (r = fido_cred_set_uv(cred, FIDO_OPT_TRUE)) != FIDO_OK)
85 		errx(1, "fido_cred_set_uv: %s (0x%x)", fido_strerr(r), r);
86 
87 	/* fmt */
88 	r = fido_cred_set_fmt(cred, fmt);
89 	if (r != FIDO_OK)
90 		errx(1, "fido_cred_set_fmt: %s (0x%x)", fido_strerr(r), r);
91 
92 	if (!strcmp(fido_cred_fmt(cred), "none")) {
93 		warnx("no attestation data, skipping credential verification");
94 		goto out;
95 	}
96 
97 	/* attestation statement */
98 	r = fido_cred_set_attstmt(cred, attstmt_ptr, attstmt_len);
99 	if (r != FIDO_OK)
100 		errx(1, "fido_cred_set_attstmt: %s (0x%x)", fido_strerr(r), r);
101 
102 	r = fido_cred_verify(cred);
103 	if (r != FIDO_OK)
104 		errx(1, "fido_cred_verify: %s (0x%x)", fido_strerr(r), r);
105 
106 out:
107 	if (key_out != NULL) {
108 		/* extract the credential pubkey */
109 		if (type == COSE_ES256) {
110 			if (write_ec_pubkey(key_out, fido_cred_pubkey_ptr(cred),
111 			    fido_cred_pubkey_len(cred)) < 0)
112 				errx(1, "write_ec_pubkey");
113 		} else if (type == COSE_RS256) {
114 			if (write_rsa_pubkey(key_out, fido_cred_pubkey_ptr(cred),
115 			    fido_cred_pubkey_len(cred)) < 0)
116 				errx(1, "write_rsa_pubkey");
117 		} else if (type == COSE_EDDSA) {
118 			if (write_eddsa_pubkey(key_out, fido_cred_pubkey_ptr(cred),
119 			    fido_cred_pubkey_len(cred)) < 0)
120 				errx(1, "write_eddsa_pubkey");
121 		}
122 	}
123 
124 	if (id_out != NULL) {
125 		/* extract the credential id */
126 		if (write_blob(id_out, fido_cred_id_ptr(cred),
127 		    fido_cred_id_len(cred)) < 0)
128 			errx(1, "write_blob");
129 	}
130 
131 	fido_cred_free(&cred);
132 }
133 
134 int
135 main(int argc, char **argv)
136 {
137 	bool		 rk = false;
138 	bool		 uv = false;
139 	bool		 u2f = false;
140 	fido_dev_t	*dev;
141 	fido_cred_t	*cred = NULL;
142 	const char	*pin = NULL;
143 	const char	*blobkey_out = NULL;
144 	const char	*key_out = NULL;
145 	const char	*id_out = NULL;
146 	unsigned char	*body = NULL;
147 	long long	 ms = 0;
148 	size_t		 len;
149 	int		 type = COSE_ES256;
150 	int		 ext = 0;
151 	int		 ch;
152 	int		 r;
153 
154 	if ((cred = fido_cred_new()) == NULL)
155 		errx(1, "fido_cred_new");
156 
157 	while ((ch = getopt(argc, argv, "P:T:b:e:hi:k:rt:uv")) != -1) {
158 		switch (ch) {
159 		case 'P':
160 			pin = optarg;
161 			break;
162 		case 'T':
163 			if (base10(optarg, &ms) < 0)
164 				errx(1, "base10: %s", optarg);
165 			if (ms <= 0 || ms > 30)
166 				errx(1, "-T: %s must be in (0,30]", optarg);
167 			ms *= 1000; /* seconds to milliseconds */
168 			break;
169 		case 'b':
170 			ext |= FIDO_EXT_LARGEBLOB_KEY;
171 			blobkey_out = optarg;
172 			break;
173 		case 'e':
174 			if (read_blob(optarg, &body, &len) < 0)
175 				errx(1, "read_blob: %s", optarg);
176 			r = fido_cred_exclude(cred, body, len);
177 			if (r != FIDO_OK)
178 				errx(1, "fido_cred_exclude: %s (0x%x)",
179 				    fido_strerr(r), r);
180 			free(body);
181 			body = NULL;
182 			break;
183 		case 'h':
184 			ext |= FIDO_EXT_HMAC_SECRET;
185 			break;
186 		case 'i':
187 			id_out = optarg;
188 			break;
189 		case 'k':
190 			key_out = optarg;
191 			break;
192 		case 'r':
193 			rk = true;
194 			break;
195 		case 't':
196 			if (strcmp(optarg, "ecdsa") == 0)
197 				type = COSE_ES256;
198 			else if (strcmp(optarg, "rsa") == 0)
199 				type = COSE_RS256;
200 			else if (strcmp(optarg, "eddsa") == 0)
201 				type = COSE_EDDSA;
202 			else
203 				errx(1, "unknown type %s", optarg);
204 			break;
205 		case 'u':
206 			u2f = true;
207 			break;
208 		case 'v':
209 			uv = true;
210 			break;
211 		default:
212 			usage();
213 		}
214 	}
215 
216 	argc -= optind;
217 	argv += optind;
218 
219 	if (argc != 1)
220 		usage();
221 
222 	fido_init(0);
223 
224 	if ((dev = fido_dev_new()) == NULL)
225 		errx(1, "fido_dev_new");
226 
227 	r = fido_dev_open(dev, argv[0]);
228 	if (r != FIDO_OK)
229 		errx(1, "fido_dev_open: %s (0x%x)", fido_strerr(r), r);
230 	if (u2f)
231 		fido_dev_force_u2f(dev);
232 
233 	/* type */
234 	r = fido_cred_set_type(cred, type);
235 	if (r != FIDO_OK)
236 		errx(1, "fido_cred_set_type: %s (0x%x)", fido_strerr(r), r);
237 
238 	/* client data */
239 	r = fido_cred_set_clientdata(cred, cd, sizeof(cd));
240 	if (r != FIDO_OK)
241 		errx(1, "fido_cred_set_clientdata: %s (0x%x)", fido_strerr(r), r);
242 
243 	/* relying party */
244 	r = fido_cred_set_rp(cred, "localhost", "sweet home localhost");
245 	if (r != FIDO_OK)
246 		errx(1, "fido_cred_set_rp: %s (0x%x)", fido_strerr(r), r);
247 
248 	/* user */
249 	r = fido_cred_set_user(cred, user_id, sizeof(user_id), "john smith",
250 	    "jsmith", NULL);
251 	if (r != FIDO_OK)
252 		errx(1, "fido_cred_set_user: %s (0x%x)", fido_strerr(r), r);
253 
254 	/* extensions */
255 	r = fido_cred_set_extensions(cred, ext);
256 	if (r != FIDO_OK)
257 		errx(1, "fido_cred_set_extensions: %s (0x%x)", fido_strerr(r), r);
258 
259 	/* resident key */
260 	if (rk && (r = fido_cred_set_rk(cred, FIDO_OPT_TRUE)) != FIDO_OK)
261 		errx(1, "fido_cred_set_rk: %s (0x%x)", fido_strerr(r), r);
262 
263 	/* user verification */
264 	if (uv && (r = fido_cred_set_uv(cred, FIDO_OPT_TRUE)) != FIDO_OK)
265 		errx(1, "fido_cred_set_uv: %s (0x%x)", fido_strerr(r), r);
266 
267 	/* timeout */
268 	if (ms != 0 && (r = fido_dev_set_timeout(dev, (int)ms)) != FIDO_OK)
269 		errx(1, "fido_dev_set_timeout: %s (0x%x)", fido_strerr(r), r);
270 
271 	if ((r = fido_dev_make_cred(dev, cred, pin)) != FIDO_OK) {
272 		fido_dev_cancel(dev);
273 		errx(1, "fido_makecred: %s (0x%x)", fido_strerr(r), r);
274 	}
275 
276 	r = fido_dev_close(dev);
277 	if (r != FIDO_OK)
278 		errx(1, "fido_dev_close: %s (0x%x)", fido_strerr(r), r);
279 
280 	fido_dev_free(&dev);
281 
282 	/* when verifying, pin implies uv */
283 	if (pin)
284 		uv = true;
285 
286 	verify_cred(type, fido_cred_fmt(cred), fido_cred_authdata_ptr(cred),
287 	    fido_cred_authdata_len(cred), fido_cred_attstmt_ptr(cred),
288 	    fido_cred_attstmt_len(cred), rk, uv, ext, key_out, id_out);
289 
290 	if (blobkey_out != NULL) {
291 		/* extract the "largeBlob" key */
292 		if (write_blob(blobkey_out, fido_cred_largeblob_key_ptr(cred),
293 		    fido_cred_largeblob_key_len(cred)) < 0)
294 			errx(1, "write_blob");
295 	}
296 
297 	fido_cred_free(&cred);
298 
299 	exit(0);
300 }
301