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