encoding.c revision 1.10
1/*	$OpenBSD: encoding.c,v 1.10 2021/11/24 15:24:16 claudio Exp $  */
2/*
3 * Copyright (c) 2020 Claudio Jeker <claudio@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17#include <sys/stat.h>
18
19#include <err.h>
20#include <errno.h>
21#include <ctype.h>
22#include <fcntl.h>
23#include <limits.h>
24#include <stdlib.h>
25#include <string.h>
26#include <unistd.h>
27
28#include <openssl/evp.h>
29
30#include "extern.h"
31
32/*
33 * Load file from disk and return the buffer and size.
34 */
35unsigned char *
36load_file(const char *name, size_t *len)
37{
38	unsigned char *buf = NULL;
39	struct stat st;
40	ssize_t n;
41	size_t size;
42	int fd, saved_errno;
43
44	*len = 0;
45
46	if ((fd = open(name, O_RDONLY)) == -1)
47		return NULL;
48	if (fstat(fd, &st) != 0)
49		goto err;
50	if (st.st_size <= 0 || st.st_size > MAX_FILE_SIZE) {
51		errno = EFBIG;
52		goto err;
53	}
54	size = (size_t)st.st_size;
55	if ((buf = malloc(size)) == NULL)
56		goto err;
57	n = read(fd, buf, size);
58	if (n == -1)
59		goto err;
60	if ((size_t)n != size) {
61		errno = EIO;
62		goto err;
63	}
64	close(fd);
65	*len = size;
66	return buf;
67
68err:
69	saved_errno = errno;
70	close(fd);
71	free(buf);
72	errno = saved_errno;
73	return NULL;
74}
75
76/*
77 * Return the size of the data blob in outlen for an inlen sized base64 buffer.
78 * Returns 0 on success and -1 if inlen would overflow an int.
79 */
80int
81base64_decode_len(size_t inlen, size_t *outlen)
82{
83	*outlen = 0;
84	if (inlen >= INT_MAX - 3)
85		return -1;
86	*outlen = ((inlen + 3) / 4) * 3 + 1;
87	return 0;
88}
89
90/*
91 * Decode base64 encoded string into binary buffer returned in out.
92 * The out buffer size is stored in outlen.
93 * Returns 0 on success or -1 for any errors.
94 */
95int
96base64_decode(const unsigned char *in, size_t inlen,
97    unsigned char **out, size_t *outlen)
98{
99	static EVP_ENCODE_CTX *ctx;
100	unsigned char *to;
101	size_t tolen;
102	int evplen;
103
104	if (ctx == NULL && (ctx = EVP_ENCODE_CTX_new()) == NULL)
105		err(1, "EVP_ENCODE_CTX_new");
106
107	*out = NULL;
108	*outlen = 0;
109
110	if (base64_decode_len(inlen, &tolen) == -1)
111		return -1;
112	if ((to = malloc(tolen)) == NULL)
113		return -1;
114
115	evplen = tolen;
116	EVP_DecodeInit(ctx);
117	if (EVP_DecodeUpdate(ctx, to, &evplen, in, inlen) == -1)
118		goto fail;
119	*outlen = evplen;
120	if (EVP_DecodeFinal(ctx, to + evplen, &evplen) == -1)
121		goto fail;
122	*outlen += evplen;
123	*out = to;
124	return 0;
125
126fail:
127	free(to);
128	return -1;
129}
130
131/*
132 * Return the size of the base64 blob in outlen for a inlen sized binary buffer.
133 * Returns 0 on success and -1 if inlen would overflow the calculation.
134 */
135int
136base64_encode_len(size_t inlen, size_t *outlen)
137{
138	*outlen = 0;
139	if (inlen >= INT_MAX / 2)
140		return -1;
141	*outlen = ((inlen + 2) / 3) * 4 + 1;
142	return 0;
143}
144
145/*
146 * Encode a binary buffer into a base64 encoded string returned in out.
147 * Returns 0 on success or -1 for any errors.
148 */
149int
150base64_encode(const unsigned char *in, size_t inlen, char **out)
151{
152	unsigned char *to;
153	size_t tolen;
154
155	*out = NULL;
156
157	if (base64_encode_len(inlen, &tolen) == -1)
158		return -1;
159	if ((to = malloc(tolen)) == NULL)
160		return -1;
161
162	EVP_EncodeBlock(to, in, inlen);
163	*out = to;
164	return 0;
165}
166
167/*
168 * Convert binary buffer of size dsz into an upper-case hex-string.
169 * Returns pointer to the newly allocated string. Function can't fail.
170 */
171char *
172hex_encode(const unsigned char *in, size_t insz)
173{
174	const char hex[] = "0123456789ABCDEF";
175	size_t i;
176	char *out;
177
178	if ((out = calloc(2, insz + 1)) == NULL)
179		err(1, NULL);
180
181	for (i = 0; i < insz; i++) {
182		out[i * 2] = hex[in[i] >> 4];
183		out[i * 2 + 1] = hex[in[i] & 0xf];
184	}
185	out[i * 2] = '\0';
186
187	return out;
188}
189
190/*
191 * Hex decode hexstring into the supplied buffer.
192 * Return 0 on success else -1, if buffer too small or bad encoding.
193 */
194int
195hex_decode(const char *hexstr, char *buf, size_t len)
196{
197	unsigned char ch, r;
198	size_t pos = 0;
199	int i;
200
201	while (*hexstr) {
202		r = 0;
203		for (i = 0; i < 2; i++) {
204			ch = hexstr[i];
205			if (isdigit(ch))
206				ch -= '0';
207			else if (islower(ch))
208				ch -= ('a' - 10);
209			else if (isupper(ch))
210				ch -= ('A' - 10);
211			else
212				return -1;
213			if (ch > 0xf)
214				return -1;
215			r = r << 4 | ch;
216		}
217		if (pos < len)
218			buf[pos++] = r;
219		else
220			return -1;
221
222		hexstr += 2;
223	}
224	return 0;
225}
226
227