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