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 * SPDX-License-Identifier: BSD-2-Clause
6 */
7
8#include <openssl/bio.h>
9#include <openssl/evp.h>
10
11#include <limits.h>
12#include <stdint.h>
13#include <string.h>
14
15#include "../openbsd-compat/openbsd-compat.h"
16#include "extern.h"
17
18int
19base64_encode(const void *ptr, size_t len, char **out)
20{
21	BIO  *bio_b64 = NULL;
22	BIO  *bio_mem = NULL;
23	char *b64_ptr = NULL;
24	long  b64_len;
25	int   n;
26	int   ok = -1;
27
28	if (ptr == NULL || out == NULL || len > INT_MAX)
29		return (-1);
30
31	*out = NULL;
32
33	if ((bio_b64 = BIO_new(BIO_f_base64())) == NULL)
34		goto fail;
35	if ((bio_mem = BIO_new(BIO_s_mem())) == NULL)
36		goto fail;
37
38	BIO_set_flags(bio_b64, BIO_FLAGS_BASE64_NO_NL);
39	BIO_push(bio_b64, bio_mem);
40
41	n = BIO_write(bio_b64, ptr, (int)len);
42	if (n < 0 || (size_t)n != len)
43		goto fail;
44
45	if (BIO_flush(bio_b64) < 0)
46		goto fail;
47
48	b64_len = BIO_get_mem_data(bio_b64, &b64_ptr);
49	if (b64_len < 0 || (size_t)b64_len == SIZE_MAX || b64_ptr == NULL)
50		goto fail;
51	if ((*out = calloc(1, (size_t)b64_len + 1)) == NULL)
52		goto fail;
53
54	memcpy(*out, b64_ptr, (size_t)b64_len);
55	ok = 0;
56
57fail:
58	BIO_free(bio_b64);
59	BIO_free(bio_mem);
60
61	return (ok);
62}
63
64int
65base64_decode(const char *in, void **ptr, size_t *len)
66{
67	BIO    *bio_mem = NULL;
68	BIO    *bio_b64 = NULL;
69	size_t  alloc_len;
70	int     n;
71	int     ok = -1;
72
73	if (in == NULL || ptr == NULL || len == NULL || strlen(in) > INT_MAX)
74		return (-1);
75
76	*ptr = NULL;
77	*len = 0;
78
79	if ((bio_b64 = BIO_new(BIO_f_base64())) == NULL)
80		goto fail;
81	if ((bio_mem = BIO_new_mem_buf((const void *)in, -1)) == NULL)
82		goto fail;
83
84	BIO_set_flags(bio_b64, BIO_FLAGS_BASE64_NO_NL);
85	BIO_push(bio_b64, bio_mem);
86
87	alloc_len = strlen(in);
88	if ((*ptr = calloc(1, alloc_len)) == NULL)
89		goto fail;
90
91	n = BIO_read(bio_b64, *ptr, (int)alloc_len);
92	if (n <= 0 || BIO_eof(bio_b64) == 0)
93		goto fail;
94
95	*len = (size_t)n;
96	ok = 0;
97
98fail:
99	BIO_free(bio_b64);
100	BIO_free(bio_mem);
101
102	if (ok < 0) {
103		free(*ptr);
104		*ptr = NULL;
105		*len = 0;
106	}
107
108	return (ok);
109}
110
111int
112base64_read(FILE *f, struct blob *out)
113{
114	char *line = NULL;
115	size_t linesize = 0;
116	ssize_t n;
117
118	out->ptr = NULL;
119	out->len = 0;
120
121	if ((n = getline(&line, &linesize, f)) <= 0 ||
122	    (size_t)n != strlen(line)) {
123		free(line); /* XXX should be free'd _even_ if getline() fails */
124		return (-1);
125	}
126
127	if (base64_decode(line, (void **)&out->ptr, &out->len) < 0) {
128		free(line);
129		return (-1);
130	}
131
132	free(line);
133
134	return (0);
135}
136