1/*
2 * Copyright (c) 2018-2022 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 * SPDX-License-Identifier: BSD-2-Clause
6 */
7
8#include <sys/types.h>
9#include <sys/stat.h>
10
11#include <openssl/ec.h>
12#include <openssl/evp.h>
13#include <openssl/pem.h>
14
15#include <fido.h>
16#include <fido/es256.h>
17#include <fido/es384.h>
18#include <fido/rs256.h>
19#include <fido/eddsa.h>
20
21#include <errno.h>
22#include <fcntl.h>
23#include <limits.h>
24#include <stdlib.h>
25#include <string.h>
26#ifdef HAVE_UNISTD_H
27#include <unistd.h>
28#endif
29#ifdef _MSC_VER
30#include "../openbsd-compat/posix_win.h"
31#endif
32#include "../openbsd-compat/openbsd-compat.h"
33#include "extern.h"
34
35int
36base10(const char *str, long long *ll)
37{
38	char *ep;
39
40	*ll = strtoll(str, &ep, 10);
41	if (str == ep || *ep != '\0')
42		return (-1);
43	else if (*ll == LLONG_MIN && errno == ERANGE)
44		return (-1);
45	else if (*ll == LLONG_MAX && errno == ERANGE)
46		return (-1);
47
48	return (0);
49}
50
51int
52write_blob(const char *path, const unsigned char *ptr, size_t len)
53{
54	int fd, ok = -1;
55	ssize_t n;
56
57	if ((fd = open(path, O_WRONLY | O_CREAT, 0600)) < 0) {
58		warn("open %s", path);
59		goto fail;
60	}
61
62	if ((n = write(fd, ptr, len)) < 0) {
63		warn("write");
64		goto fail;
65	}
66	if ((size_t)n != len) {
67		warnx("write");
68		goto fail;
69	}
70
71	ok = 0;
72fail:
73	if (fd != -1) {
74		close(fd);
75	}
76
77	return (ok);
78}
79
80int
81read_blob(const char *path, unsigned char **ptr, size_t *len)
82{
83	int fd, ok = -1;
84	struct stat st;
85	ssize_t n;
86
87	*ptr = NULL;
88	*len = 0;
89
90	if ((fd = open(path, O_RDONLY)) < 0) {
91		warn("open %s", path);
92		goto fail;
93	}
94	if (fstat(fd, &st) < 0) {
95		warn("stat %s", path);
96		goto fail;
97	}
98	if (st.st_size < 0) {
99		warnx("stat %s: invalid size", path);
100		goto fail;
101	}
102	*len = (size_t)st.st_size;
103	if ((*ptr = malloc(*len)) == NULL) {
104		warn("malloc");
105		goto fail;
106	}
107	if ((n = read(fd, *ptr, *len)) < 0) {
108		warn("read");
109		goto fail;
110	}
111	if ((size_t)n != *len) {
112		warnx("read");
113		goto fail;
114	}
115
116	ok = 0;
117fail:
118	if (fd != -1) {
119		close(fd);
120	}
121	if (ok < 0) {
122		free(*ptr);
123		*ptr = NULL;
124		*len = 0;
125	}
126
127	return (ok);
128}
129
130EC_KEY *
131read_ec_pubkey(const char *path)
132{
133	FILE *fp = NULL;
134	EVP_PKEY *pkey = NULL;
135	EC_KEY *ec = NULL;
136
137	if ((fp = fopen(path, "r")) == NULL) {
138		warn("fopen");
139		goto fail;
140	}
141
142	if ((pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) {
143		warnx("PEM_read_PUBKEY");
144		goto fail;
145	}
146	if ((ec = EVP_PKEY_get1_EC_KEY(pkey)) == NULL) {
147		warnx("EVP_PKEY_get1_EC_KEY");
148		goto fail;
149	}
150
151fail:
152	if (fp != NULL) {
153		fclose(fp);
154	}
155	if (pkey != NULL) {
156		EVP_PKEY_free(pkey);
157	}
158
159	return (ec);
160}
161
162int
163write_es256_pubkey(const char *path, const void *ptr, size_t len)
164{
165	FILE *fp = NULL;
166	EVP_PKEY *pkey = NULL;
167	es256_pk_t *pk = NULL;
168	int fd = -1;
169	int ok = -1;
170
171	if ((pk = es256_pk_new()) == NULL) {
172		warnx("es256_pk_new");
173		goto fail;
174	}
175
176	if (es256_pk_from_ptr(pk, ptr, len) != FIDO_OK) {
177		warnx("es256_pk_from_ptr");
178		goto fail;
179	}
180
181	if ((fd = open(path, O_WRONLY | O_CREAT, 0644)) < 0) {
182		warn("open %s", path);
183		goto fail;
184	}
185
186	if ((fp = fdopen(fd, "w")) == NULL) {
187		warn("fdopen");
188		goto fail;
189	}
190	fd = -1; /* owned by fp now */
191
192	if ((pkey = es256_pk_to_EVP_PKEY(pk)) == NULL) {
193		warnx("es256_pk_to_EVP_PKEY");
194		goto fail;
195	}
196
197	if (PEM_write_PUBKEY(fp, pkey) == 0) {
198		warnx("PEM_write_PUBKEY");
199		goto fail;
200	}
201
202	ok = 0;
203fail:
204	es256_pk_free(&pk);
205
206	if (fp != NULL) {
207		fclose(fp);
208	}
209	if (fd != -1) {
210		close(fd);
211	}
212	if (pkey != NULL) {
213		EVP_PKEY_free(pkey);
214	}
215
216	return (ok);
217}
218
219int
220write_es384_pubkey(const char *path, const void *ptr, size_t len)
221{
222	FILE *fp = NULL;
223	EVP_PKEY *pkey = NULL;
224	es384_pk_t *pk = NULL;
225	int fd = -1;
226	int ok = -1;
227
228	if ((pk = es384_pk_new()) == NULL) {
229		warnx("es384_pk_new");
230		goto fail;
231	}
232
233	if (es384_pk_from_ptr(pk, ptr, len) != FIDO_OK) {
234		warnx("es384_pk_from_ptr");
235		goto fail;
236	}
237
238	if ((fd = open(path, O_WRONLY | O_CREAT, 0644)) < 0) {
239		warn("open %s", path);
240		goto fail;
241	}
242
243	if ((fp = fdopen(fd, "w")) == NULL) {
244		warn("fdopen");
245		goto fail;
246	}
247	fd = -1; /* owned by fp now */
248
249	if ((pkey = es384_pk_to_EVP_PKEY(pk)) == NULL) {
250		warnx("es384_pk_to_EVP_PKEY");
251		goto fail;
252	}
253
254	if (PEM_write_PUBKEY(fp, pkey) == 0) {
255		warnx("PEM_write_PUBKEY");
256		goto fail;
257	}
258
259	ok = 0;
260fail:
261	es384_pk_free(&pk);
262
263	if (fp != NULL) {
264		fclose(fp);
265	}
266	if (fd != -1) {
267		close(fd);
268	}
269	if (pkey != NULL) {
270		EVP_PKEY_free(pkey);
271	}
272
273	return (ok);
274}
275
276RSA *
277read_rsa_pubkey(const char *path)
278{
279	FILE *fp = NULL;
280	EVP_PKEY *pkey = NULL;
281	RSA *rsa = NULL;
282
283	if ((fp = fopen(path, "r")) == NULL) {
284		warn("fopen");
285		goto fail;
286	}
287
288	if ((pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) {
289		warnx("PEM_read_PUBKEY");
290		goto fail;
291	}
292	if ((rsa = EVP_PKEY_get1_RSA(pkey)) == NULL) {
293		warnx("EVP_PKEY_get1_RSA");
294		goto fail;
295	}
296
297fail:
298	if (fp != NULL) {
299		fclose(fp);
300	}
301	if (pkey != NULL) {
302		EVP_PKEY_free(pkey);
303	}
304
305	return (rsa);
306}
307
308int
309write_rs256_pubkey(const char *path, const void *ptr, size_t len)
310{
311	FILE *fp = NULL;
312	EVP_PKEY *pkey = NULL;
313	rs256_pk_t *pk = NULL;
314	int fd = -1;
315	int ok = -1;
316
317	if ((pk = rs256_pk_new()) == NULL) {
318		warnx("rs256_pk_new");
319		goto fail;
320	}
321
322	if (rs256_pk_from_ptr(pk, ptr, len) != FIDO_OK) {
323		warnx("rs256_pk_from_ptr");
324		goto fail;
325	}
326
327	if ((fd = open(path, O_WRONLY | O_CREAT, 0644)) < 0) {
328		warn("open %s", path);
329		goto fail;
330	}
331
332	if ((fp = fdopen(fd, "w")) == NULL) {
333		warn("fdopen");
334		goto fail;
335	}
336	fd = -1; /* owned by fp now */
337
338	if ((pkey = rs256_pk_to_EVP_PKEY(pk)) == NULL) {
339		warnx("rs256_pk_to_EVP_PKEY");
340		goto fail;
341	}
342
343	if (PEM_write_PUBKEY(fp, pkey) == 0) {
344		warnx("PEM_write_PUBKEY");
345		goto fail;
346	}
347
348	ok = 0;
349fail:
350	rs256_pk_free(&pk);
351
352	if (fp != NULL) {
353		fclose(fp);
354	}
355	if (fd != -1) {
356		close(fd);
357	}
358	if (pkey != NULL) {
359		EVP_PKEY_free(pkey);
360	}
361
362	return (ok);
363}
364
365EVP_PKEY *
366read_eddsa_pubkey(const char *path)
367{
368	FILE *fp = NULL;
369	EVP_PKEY *pkey = NULL;
370
371	if ((fp = fopen(path, "r")) == NULL) {
372		warn("fopen");
373		goto fail;
374	}
375
376	if ((pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) {
377		warnx("PEM_read_PUBKEY");
378		goto fail;
379	}
380
381fail:
382	if (fp) {
383		fclose(fp);
384	}
385
386	return (pkey);
387}
388
389int
390write_eddsa_pubkey(const char *path, const void *ptr, size_t len)
391{
392	FILE *fp = NULL;
393	EVP_PKEY *pkey = NULL;
394	eddsa_pk_t *pk = NULL;
395	int fd = -1;
396	int ok = -1;
397
398	if ((pk = eddsa_pk_new()) == NULL) {
399		warnx("eddsa_pk_new");
400		goto fail;
401	}
402
403	if (eddsa_pk_from_ptr(pk, ptr, len) != FIDO_OK) {
404		warnx("eddsa_pk_from_ptr");
405		goto fail;
406	}
407
408	if ((fd = open(path, O_WRONLY | O_CREAT, 0644)) < 0) {
409		warn("open %s", path);
410		goto fail;
411	}
412
413	if ((fp = fdopen(fd, "w")) == NULL) {
414		warn("fdopen");
415		goto fail;
416	}
417	fd = -1; /* owned by fp now */
418
419	if ((pkey = eddsa_pk_to_EVP_PKEY(pk)) == NULL) {
420		warnx("eddsa_pk_to_EVP_PKEY");
421		goto fail;
422	}
423
424	if (PEM_write_PUBKEY(fp, pkey) == 0) {
425		warnx("PEM_write_PUBKEY");
426		goto fail;
427	}
428
429	ok = 0;
430fail:
431	eddsa_pk_free(&pk);
432
433	if (fp != NULL) {
434		fclose(fp);
435	}
436	if (fd != -1) {
437		close(fd);
438	}
439	if (pkey != NULL) {
440		EVP_PKEY_free(pkey);
441	}
442
443	return (ok);
444}
445