xref: /netbsd-src/external/bsd/libfido2/dist/examples/assert.c (revision 154bfe8e089c1a0a4e9ed8414f08d3da90949162)
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 
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 
19 #include "fido.h"
20 #include "fido/es256.h"
21 #include "fido/rs256.h"
22 #include "fido/eddsa.h"
23 #include "extern.h"
24 
25 #ifdef SIGNAL_EXAMPLE
26 extern volatile sig_atomic_t got_signal;
27 #endif
28 
29 static const unsigned char cdh[32] = {
30 	0xec, 0x8d, 0x8f, 0x78, 0x42, 0x4a, 0x2b, 0xb7,
31 	0x82, 0x34, 0xaa, 0xca, 0x07, 0xa1, 0xf6, 0x56,
32 	0x42, 0x1c, 0xb6, 0xf6, 0xb3, 0x00, 0x86, 0x52,
33 	0x35, 0x2d, 0xa2, 0x62, 0x4a, 0xbe, 0x89, 0x76,
34 };
35 
36 static void
37 usage(void)
38 {
39 	fprintf(stderr, "usage: assert [-t ecdsa|rsa|eddsa] [-a cred_id] "
40 	    "[-h hmac_secret] [-s hmac_salt] [-P pin] [-T seconds] [-puv] "
41 	    "<pubkey> <device>\n");
42 	exit(EXIT_FAILURE);
43 }
44 
45 static void
46 verify_assert(int type, const unsigned char *authdata_ptr, size_t authdata_len,
47     const unsigned char *sig_ptr, size_t sig_len, bool up, bool uv, int ext,
48     const char *key)
49 {
50 	fido_assert_t	*assert = NULL;
51 	EC_KEY		*ec = NULL;
52 	RSA		*rsa = NULL;
53 	EVP_PKEY	*eddsa = NULL;
54 	es256_pk_t	*es256_pk = NULL;
55 	rs256_pk_t	*rs256_pk = NULL;
56 	eddsa_pk_t	*eddsa_pk = NULL;
57 	void		*pk;
58 	int		 r;
59 
60 	/* credential pubkey */
61 	switch (type) {
62 	case COSE_ES256:
63 		if ((ec = read_ec_pubkey(key)) == NULL)
64 			errx(1, "read_ec_pubkey");
65 
66 		if ((es256_pk = es256_pk_new()) == NULL)
67 			errx(1, "es256_pk_new");
68 
69 		if (es256_pk_from_EC_KEY(es256_pk, ec) != FIDO_OK)
70 			errx(1, "es256_pk_from_EC_KEY");
71 
72 		pk = es256_pk;
73 		EC_KEY_free(ec);
74 		ec = NULL;
75 
76 		break;
77 	case COSE_RS256:
78 		if ((rsa = read_rsa_pubkey(key)) == NULL)
79 			errx(1, "read_rsa_pubkey");
80 
81 		if ((rs256_pk = rs256_pk_new()) == NULL)
82 			errx(1, "rs256_pk_new");
83 
84 		if (rs256_pk_from_RSA(rs256_pk, rsa) != FIDO_OK)
85 			errx(1, "rs256_pk_from_RSA");
86 
87 		pk = rs256_pk;
88 		RSA_free(rsa);
89 		rsa = NULL;
90 
91 		break;
92 	case COSE_EDDSA:
93 		if ((eddsa = read_eddsa_pubkey(key)) == NULL)
94 			errx(1, "read_eddsa_pubkey");
95 
96 		if ((eddsa_pk = eddsa_pk_new()) == NULL)
97 			errx(1, "eddsa_pk_new");
98 
99 		if (eddsa_pk_from_EVP_PKEY(eddsa_pk, eddsa) != FIDO_OK)
100 			errx(1, "eddsa_pk_from_EVP_PKEY");
101 
102 		pk = eddsa_pk;
103 		EVP_PKEY_free(eddsa);
104 		eddsa = NULL;
105 
106 		break;
107 	default:
108 		errx(1, "unknown credential type %d", type);
109 	}
110 
111 	if ((assert = fido_assert_new()) == NULL)
112 		errx(1, "fido_assert_new");
113 
114 	/* client data hash */
115 	r = fido_assert_set_clientdata_hash(assert, cdh, sizeof(cdh));
116 	if (r != FIDO_OK)
117 		errx(1, "fido_assert_set_clientdata_hash: %s (0x%x)",
118 		    fido_strerr(r), r);
119 
120 	/* relying party */
121 	r = fido_assert_set_rp(assert, "localhost");
122 	if (r != FIDO_OK)
123 		errx(1, "fido_assert_set_rp: %s (0x%x)", fido_strerr(r), r);
124 
125 	/* authdata */
126 	r = fido_assert_set_count(assert, 1);
127 	if (r != FIDO_OK)
128 		errx(1, "fido_assert_set_count: %s (0x%x)", fido_strerr(r), r);
129 	r = fido_assert_set_authdata(assert, 0, authdata_ptr, authdata_len);
130 	if (r != FIDO_OK)
131 		errx(1, "fido_assert_set_authdata: %s (0x%x)", fido_strerr(r), r);
132 
133 	/* extension */
134 	r = fido_assert_set_extensions(assert, ext);
135 	if (r != FIDO_OK)
136 		errx(1, "fido_assert_set_extensions: %s (0x%x)", fido_strerr(r),
137 		    r);
138 
139 	/* user presence */
140 	if (up && (r = fido_assert_set_up(assert, FIDO_OPT_TRUE)) != FIDO_OK)
141 		errx(1, "fido_assert_set_up: %s (0x%x)", fido_strerr(r), r);
142 
143 	/* user verification */
144 	if (uv && (r = fido_assert_set_uv(assert, FIDO_OPT_TRUE)) != FIDO_OK)
145 		errx(1, "fido_assert_set_uv: %s (0x%x)", fido_strerr(r), r);
146 
147 	/* sig */
148 	r = fido_assert_set_sig(assert, 0, sig_ptr, sig_len);
149 	if (r != FIDO_OK)
150 		errx(1, "fido_assert_set_sig: %s (0x%x)", fido_strerr(r), r);
151 
152 	r = fido_assert_verify(assert, 0, type, pk);
153 	if (r != FIDO_OK)
154 		errx(1, "fido_assert_verify: %s (0x%x)", fido_strerr(r), r);
155 
156 	es256_pk_free(&es256_pk);
157 	rs256_pk_free(&rs256_pk);
158 	eddsa_pk_free(&eddsa_pk);
159 
160 	fido_assert_free(&assert);
161 }
162 
163 int
164 main(int argc, char **argv)
165 {
166 	bool		 up = false;
167 	bool		 uv = false;
168 	bool		 u2f = false;
169 	fido_dev_t	*dev = NULL;
170 	fido_assert_t	*assert = NULL;
171 	const char	*pin = NULL;
172 	const char	*hmac_out = NULL;
173 	unsigned char	*body = NULL;
174 	long long	 seconds = 0;
175 	size_t		 len;
176 	int		 type = COSE_ES256;
177 	int		 ext = 0;
178 	int		 ch;
179 	int		 r;
180 
181 	if ((assert = fido_assert_new()) == NULL)
182 		errx(1, "fido_assert_new");
183 
184 	while ((ch = getopt(argc, argv, "P:T:a:h:ps:t:uv")) != -1) {
185 		switch (ch) {
186 		case 'P':
187 			pin = optarg;
188 			break;
189 		case 'T':
190 #ifndef SIGNAL_EXAMPLE
191 			errx(1, "-T not supported");
192 #endif
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 		case 'a':
199 			if (read_blob(optarg, &body, &len) < 0)
200 				errx(1, "read_blob: %s", optarg);
201 			if ((r = fido_assert_allow_cred(assert, body,
202 			    len)) != FIDO_OK)
203 				errx(1, "fido_assert_allow_cred: %s (0x%x)",
204 				    fido_strerr(r), r);
205 			free(body);
206 			body = NULL;
207 			break;
208 		case 'h':
209 			hmac_out = optarg;
210 			break;
211 		case 'p':
212 			up = true;
213 			break;
214 		case 's':
215 			ext = FIDO_EXT_HMAC_SECRET;
216 			if (read_blob(optarg, &body, &len) < 0)
217 				errx(1, "read_blob: %s", optarg);
218 			if ((r = fido_assert_set_hmac_salt(assert, body,
219 			    len)) != FIDO_OK)
220 				errx(1, "fido_assert_set_hmac_salt: %s (0x%x)",
221 				    fido_strerr(r), r);
222 			free(body);
223 			body = NULL;
224 			break;
225 		case 't':
226 			if (strcmp(optarg, "ecdsa") == 0)
227 				type = COSE_ES256;
228 			else if (strcmp(optarg, "rsa") == 0)
229 				type = COSE_RS256;
230 			else if (strcmp(optarg, "eddsa") == 0)
231 				type = COSE_EDDSA;
232 			else
233 				errx(1, "unknown type %s", optarg);
234 			break;
235 		case 'u':
236 			u2f = true;
237 			break;
238 		case 'v':
239 			uv = true;
240 			break;
241 		default:
242 			usage();
243 		}
244 	}
245 
246 	argc -= optind;
247 	argv += optind;
248 
249 	if (argc != 2)
250 		usage();
251 
252 	fido_init(0);
253 
254 	if ((dev = fido_dev_new()) == NULL)
255 		errx(1, "fido_dev_new");
256 
257 	r = fido_dev_open(dev, argv[1]);
258 	if (r != FIDO_OK)
259 		errx(1, "fido_dev_open: %s (0x%x)", fido_strerr(r), r);
260 	if (u2f)
261 		fido_dev_force_u2f(dev);
262 
263 	/* client data hash */
264 	r = fido_assert_set_clientdata_hash(assert, cdh, sizeof(cdh));
265 	if (r != FIDO_OK)
266 		errx(1, "fido_assert_set_clientdata_hash: %s (0x%x)",
267 		    fido_strerr(r), r);
268 
269 	/* relying party */
270 	r = fido_assert_set_rp(assert, "localhost");
271 	if (r != FIDO_OK)
272 		errx(1, "fido_assert_set_rp: %s (0x%x)", fido_strerr(r), r);
273 
274 	/* extensions */
275 	r = fido_assert_set_extensions(assert, ext);
276 	if (r != FIDO_OK)
277 		errx(1, "fido_assert_set_extensions: %s (0x%x)", fido_strerr(r),
278 		    r);
279 
280 	/* user presence */
281 	if (up && (r = fido_assert_set_up(assert, FIDO_OPT_TRUE)) != FIDO_OK)
282 		errx(1, "fido_assert_set_up: %s (0x%x)", fido_strerr(r), r);
283 
284 	/* user verification */
285 	if (uv && (r = fido_assert_set_uv(assert, FIDO_OPT_TRUE)) != FIDO_OK)
286 		errx(1, "fido_assert_set_uv: %s (0x%x)", fido_strerr(r), r);
287 
288 #ifdef SIGNAL_EXAMPLE
289 	prepare_signal_handler(SIGINT);
290 	if (seconds) {
291 		prepare_signal_handler(SIGALRM);
292 		alarm((unsigned)seconds);
293 	}
294 #endif
295 
296 	r = fido_dev_get_assert(dev, assert, pin);
297 	if (r != FIDO_OK) {
298 #ifdef SIGNAL_EXAMPLE
299 		if (got_signal)
300 			fido_dev_cancel(dev);
301 #endif
302 		errx(1, "fido_dev_get_assert: %s (0x%x)", fido_strerr(r), r);
303 	}
304 
305 	r = fido_dev_close(dev);
306 	if (r != FIDO_OK)
307 		errx(1, "fido_dev_close: %s (0x%x)", fido_strerr(r), r);
308 
309 	fido_dev_free(&dev);
310 
311 	if (fido_assert_count(assert) != 1)
312 		errx(1, "fido_assert_count: %d signatures returned",
313 		    (int)fido_assert_count(assert));
314 
315 	verify_assert(type, fido_assert_authdata_ptr(assert, 0),
316 	    fido_assert_authdata_len(assert, 0), fido_assert_sig_ptr(assert, 0),
317 	    fido_assert_sig_len(assert, 0), up, uv, ext, argv[0]);
318 
319 	if (hmac_out != NULL) {
320 		/* extract the hmac secret */
321 		if (write_blob(hmac_out, fido_assert_hmac_secret_ptr(assert, 0),
322 		    fido_assert_hmac_secret_len(assert, 0)) < 0)
323 			errx(1, "write_blob");
324 	}
325 
326 	fido_assert_free(&assert);
327 
328 	exit(0);
329 }
330