util.c revision 1.1.1.2 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