xref: /netbsd-src/external/bsd/libfido2/dist/tools/util.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 <sys/types.h>
8 #include <sys/stat.h>
9 
10 #include <openssl/ec.h>
11 #include <openssl/evp.h>
12 #include <openssl/pem.h>
13 
14 #include <fido.h>
15 #include <fido/es256.h>
16 #include <fido/rs256.h>
17 #include <fido/eddsa.h>
18 
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <limits.h>
22 #include <stdint.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 
27 #include "../openbsd-compat/openbsd-compat.h"
28 #ifdef _MSC_VER
29 #include "../openbsd-compat/posix_win.h"
30 #endif
31 
32 #include "extern.h"
33 
34 void
35 read_pin(const char *path, char *buf, size_t len)
36 {
37 	char prompt[1024];
38 	int r;
39 
40 	r = snprintf(prompt, sizeof(prompt), "Enter PIN for %s: ", path);
41 	if (r < 0 || (size_t)r >= sizeof(prompt))
42 		errx(1, "snprintf");
43 	if (!readpassphrase(prompt, buf, len, RPP_ECHO_OFF))
44 		errx(1, "readpassphrase");
45 }
46 
47 FILE *
48 open_write(const char *file)
49 {
50 	int fd;
51 	FILE *f;
52 
53 	if (file == NULL || strcmp(file, "-") == 0)
54 		return (stdout);
55 	if ((fd = open(file, O_WRONLY | O_CREAT, 0600)) < 0)
56 		err(1, "open %s", file);
57 	if ((f = fdopen(fd, "w")) == NULL)
58 		err(1, "fdopen %s", file);
59 
60 	return (f);
61 }
62 
63 FILE *
64 open_read(const char *file)
65 {
66 	int fd;
67 	FILE *f;
68 
69 	if (file == NULL || strcmp(file, "-") == 0) {
70 #ifdef FIDO_FUZZ
71 		setvbuf(stdin, NULL, _IONBF, 0);
72 #endif
73 		return (stdin);
74 	}
75 	if ((fd = open(file, O_RDONLY)) < 0)
76 		err(1, "open %s", file);
77 	if ((f = fdopen(fd, "r")) == NULL)
78 		err(1, "fdopen %s", file);
79 
80 	return (f);
81 }
82 
83 int
84 base10(const char *str)
85 {
86 	char *ep;
87 	long long ll;
88 
89 	ll = strtoll(str, &ep, 10);
90 	if (str == ep || *ep != '\0')
91 		return (-1);
92 	else if (ll == LLONG_MIN && errno == ERANGE)
93 		return (-1);
94 	else if (ll == LLONG_MAX && errno == ERANGE)
95 		return (-1);
96 	else if (ll < 0 || ll > INT_MAX)
97 		return (-1);
98 
99 	return ((int)ll);
100 }
101 
102 void
103 xxd(const void *buf, size_t count)
104 {
105 	const uint8_t	*ptr = buf;
106 	size_t		 i;
107 
108 	fprintf(stderr, "  ");
109 
110 	for (i = 0; i < count; i++) {
111 		fprintf(stderr, "%02x ", *ptr++);
112 		if ((i + 1) % 16 == 0 && i + 1 < count)
113 			fprintf(stderr, "\n  ");
114 	}
115 
116 	fprintf(stderr, "\n");
117 	fflush(stderr);
118 }
119 
120 int
121 string_read(FILE *f, char **out)
122 {
123 	char *line = NULL;
124 	size_t linesize = 0;
125 	ssize_t n;
126 
127 	*out = NULL;
128 
129 	if ((n = getline(&line, &linesize, f)) <= 0 ||
130 	    (size_t)n != strlen(line)) {
131 		free(line);
132 		return (-1);
133 	}
134 
135 	line[n - 1] = '\0'; /* trim \n */
136 	*out = line;
137 
138 	return (0);
139 }
140 
141 fido_dev_t *
142 open_dev(const char *path)
143 {
144 	fido_dev_t *dev;
145 	int r;
146 
147 	if ((dev = fido_dev_new()) == NULL)
148 		errx(1, "fido_dev_new");
149 
150 	r = fido_dev_open(dev, path);
151 	if (r != FIDO_OK)
152 		errx(1, "fido_dev_open %s: %s", path, fido_strerr(r));
153 
154 	return (dev);
155 }
156 
157 EC_KEY *
158 read_ec_pubkey(const char *path)
159 {
160 	FILE *fp = NULL;
161 	EVP_PKEY *pkey = NULL;
162 	EC_KEY *ec = NULL;
163 
164 	if ((fp = fopen(path, "r")) == NULL) {
165 		warn("fopen");
166 		goto fail;
167 	}
168 
169 	if ((pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) {
170 		warnx("PEM_read_PUBKEY");
171 		goto fail;
172 	}
173 	if ((ec = EVP_PKEY_get1_EC_KEY(pkey)) == NULL) {
174 		warnx("EVP_PKEY_get1_EC_KEY");
175 		goto fail;
176 	}
177 
178 fail:
179 	if (fp) {
180 		fclose(fp);
181 	}
182 	if (pkey) {
183 		EVP_PKEY_free(pkey);
184 	}
185 
186 	return (ec);
187 }
188 
189 int
190 write_ec_pubkey(FILE *f, const void *ptr, size_t len)
191 {
192 	EVP_PKEY *pkey = NULL;
193 	es256_pk_t *pk = NULL;
194 	int ok = -1;
195 
196 	if ((pk = es256_pk_new()) == NULL) {
197 		warnx("es256_pk_new");
198 		goto fail;
199 	}
200 
201 	if (es256_pk_from_ptr(pk, ptr, len) != FIDO_OK) {
202 		warnx("es256_pk_from_ptr");
203 		goto fail;
204 	}
205 
206 	if ((pkey = es256_pk_to_EVP_PKEY(pk)) == NULL) {
207 		warnx("es256_pk_to_EVP_PKEY");
208 		goto fail;
209 	}
210 
211 	if (PEM_write_PUBKEY(f, pkey) == 0) {
212 		warnx("PEM_write_PUBKEY");
213 		goto fail;
214 	}
215 
216 	ok = 0;
217 fail:
218 	es256_pk_free(&pk);
219 
220 	if (pkey != NULL) {
221 		EVP_PKEY_free(pkey);
222 	}
223 
224 	return (ok);
225 }
226 
227 RSA *
228 read_rsa_pubkey(const char *path)
229 {
230 	FILE *fp = NULL;
231 	EVP_PKEY *pkey = NULL;
232 	RSA *rsa = NULL;
233 
234 	if ((fp = fopen(path, "r")) == NULL) {
235 		warn("fopen");
236 		goto fail;
237 	}
238 
239 	if ((pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) {
240 		warnx("PEM_read_PUBKEY");
241 		goto fail;
242 	}
243 	if ((rsa = EVP_PKEY_get1_RSA(pkey)) == NULL) {
244 		warnx("EVP_PKEY_get1_RSA");
245 		goto fail;
246 	}
247 
248 fail:
249 	if (fp) {
250 		fclose(fp);
251 	}
252 	if (pkey) {
253 		EVP_PKEY_free(pkey);
254 	}
255 
256 	return (rsa);
257 }
258 
259 int
260 write_rsa_pubkey(FILE *f, const void *ptr, size_t len)
261 {
262 	EVP_PKEY *pkey = NULL;
263 	rs256_pk_t *pk = NULL;
264 	int ok = -1;
265 
266 	if ((pk = rs256_pk_new()) == NULL) {
267 		warnx("rs256_pk_new");
268 		goto fail;
269 	}
270 
271 	if (rs256_pk_from_ptr(pk, ptr, len) != FIDO_OK) {
272 		warnx("rs256_pk_from_ptr");
273 		goto fail;
274 	}
275 
276 	if ((pkey = rs256_pk_to_EVP_PKEY(pk)) == NULL) {
277 		warnx("rs256_pk_to_EVP_PKEY");
278 		goto fail;
279 	}
280 
281 	if (PEM_write_PUBKEY(f, pkey) == 0) {
282 		warnx("PEM_write_PUBKEY");
283 		goto fail;
284 	}
285 
286 	ok = 0;
287 fail:
288 	rs256_pk_free(&pk);
289 
290 	if (pkey != NULL) {
291 		EVP_PKEY_free(pkey);
292 	}
293 
294 	return (ok);
295 }
296 
297 EVP_PKEY *
298 read_eddsa_pubkey(const char *path)
299 {
300 	FILE *fp = NULL;
301 	EVP_PKEY *pkey = NULL;
302 
303 	if ((fp = fopen(path, "r")) == NULL) {
304 		warn("fopen");
305 		goto fail;
306 	}
307 
308 	if ((pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) {
309 		warnx("PEM_read_PUBKEY");
310 		goto fail;
311 	}
312 
313 fail:
314 	if (fp) {
315 		fclose(fp);
316 	}
317 
318 	return (pkey);
319 }
320 
321 int
322 write_eddsa_pubkey(FILE *f, const void *ptr, size_t len)
323 {
324 	EVP_PKEY *pkey = NULL;
325 	eddsa_pk_t *pk = NULL;
326 	int ok = -1;
327 
328 	if ((pk = eddsa_pk_new()) == NULL) {
329 		warnx("eddsa_pk_new");
330 		goto fail;
331 	}
332 
333 	if (eddsa_pk_from_ptr(pk, ptr, len) != FIDO_OK) {
334 		warnx("eddsa_pk_from_ptr");
335 		goto fail;
336 	}
337 
338 	if ((pkey = eddsa_pk_to_EVP_PKEY(pk)) == NULL) {
339 		warnx("eddsa_pk_to_EVP_PKEY");
340 		goto fail;
341 	}
342 
343 	if (PEM_write_PUBKEY(f, pkey) == 0) {
344 		warnx("PEM_write_PUBKEY");
345 		goto fail;
346 	}
347 
348 	ok = 0;
349 fail:
350 	eddsa_pk_free(&pk);
351 
352 	if (pkey != NULL) {
353 		EVP_PKEY_free(pkey);
354 	}
355 
356 	return (ok);
357 }
358 
359 void
360 print_cred(FILE *out_f, int type, const fido_cred_t *cred)
361 {
362 	char *id;
363 	int r;
364 
365 	r = base64_encode(fido_cred_id_ptr(cred), fido_cred_id_len(cred), &id);
366 	if (r < 0)
367 		errx(1, "output error");
368 
369 	fprintf(out_f, "%s\n", id);
370 
371 	if (type == COSE_ES256) {
372 		write_ec_pubkey(out_f, fido_cred_pubkey_ptr(cred),
373 		    fido_cred_pubkey_len(cred));
374 	} else if (type == COSE_RS256) {
375 		write_rsa_pubkey(out_f, fido_cred_pubkey_ptr(cred),
376 		    fido_cred_pubkey_len(cred));
377 	} else if (type == COSE_EDDSA) {
378 		write_eddsa_pubkey(out_f, fido_cred_pubkey_ptr(cred),
379 		    fido_cred_pubkey_len(cred));
380 	} else {
381 		errx(1, "print_cred: unknown type");
382 	}
383 
384 	free(id);
385 }
386 
387 int
388 cose_type(const char *str, int *type)
389 {
390 	if (strcmp(str, "es256") == 0)
391 		*type = COSE_ES256;
392 	else if (strcmp(str, "rs256") == 0)
393 		*type = COSE_RS256;
394 	else if (strcmp(str, "eddsa") == 0)
395 		*type = COSE_EDDSA;
396 	else {
397 		*type = 0;
398 		return (-1);
399 	}
400 
401 	return (0);
402 }
403 
404 const char *
405 cose_string(int type)
406 {
407 	switch (type) {
408 	case COSE_EDDSA:
409 		return ("eddsa");
410 	case COSE_ES256:
411 		return ("es256");
412 	case COSE_RS256:
413 		return ("rs256");
414 	default:
415 		return ("unknown");
416 	}
417 }
418 
419 const char *
420 prot_string(int prot)
421 {
422 	switch (prot) {
423 	case FIDO_CRED_PROT_UV_OPTIONAL:
424 		return ("uvopt");
425 	case FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID:
426 		return ("uvopt+id");
427 	case FIDO_CRED_PROT_UV_REQUIRED:
428 		return ("uvreq");
429 	default:
430 		return ("unknown");
431 	}
432 }
433