xref: /openbsd-src/usr.sbin/rpki-client/encoding.c (revision 5ddaa2183881cb2ea8ca6a868b4031ea3f1dd9b3)
1*5ddaa218Sderaadt /*	$OpenBSD: encoding.c,v 1.13 2022/05/15 15:00:53 deraadt Exp $ */
2087c4643Sclaudio /*
3087c4643Sclaudio  * Copyright (c) 2020 Claudio Jeker <claudio@openbsd.org>
4087c4643Sclaudio  *
5087c4643Sclaudio  * Permission to use, copy, modify, and distribute this software for any
6087c4643Sclaudio  * purpose with or without fee is hereby granted, provided that the above
7087c4643Sclaudio  * copyright notice and this permission notice appear in all copies.
8087c4643Sclaudio  *
9087c4643Sclaudio  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10087c4643Sclaudio  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11087c4643Sclaudio  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12087c4643Sclaudio  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13087c4643Sclaudio  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14087c4643Sclaudio  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15087c4643Sclaudio  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16087c4643Sclaudio  */
172cfd2d3bSclaudio #include <sys/stat.h>
182cfd2d3bSclaudio 
19087c4643Sclaudio #include <err.h>
20b3c9c1ceSclaudio #include <errno.h>
21aef00ae0Sclaudio #include <ctype.h>
222cfd2d3bSclaudio #include <fcntl.h>
23087c4643Sclaudio #include <limits.h>
24087c4643Sclaudio #include <stdlib.h>
25087c4643Sclaudio #include <string.h>
262cfd2d3bSclaudio #include <unistd.h>
27087c4643Sclaudio 
28087c4643Sclaudio #include <openssl/evp.h>
29087c4643Sclaudio 
30087c4643Sclaudio #include "extern.h"
31087c4643Sclaudio 
32087c4643Sclaudio /*
332cfd2d3bSclaudio  * Load file from disk and return the buffer and size.
342cfd2d3bSclaudio  */
352cfd2d3bSclaudio unsigned char *
load_file(const char * name,size_t * len)362cfd2d3bSclaudio load_file(const char *name, size_t *len)
372cfd2d3bSclaudio {
382cfd2d3bSclaudio 	unsigned char *buf = NULL;
392cfd2d3bSclaudio 	struct stat st;
402cfd2d3bSclaudio 	ssize_t n;
412cfd2d3bSclaudio 	size_t size;
42b3c9c1ceSclaudio 	int fd, saved_errno;
432cfd2d3bSclaudio 
442cfd2d3bSclaudio 	*len = 0;
452cfd2d3bSclaudio 
462cfd2d3bSclaudio 	if ((fd = open(name, O_RDONLY)) == -1)
472cfd2d3bSclaudio 		return NULL;
482cfd2d3bSclaudio 	if (fstat(fd, &st) != 0)
492cfd2d3bSclaudio 		goto err;
50b3c9c1ceSclaudio 	if (st.st_size <= 0 || st.st_size > MAX_FILE_SIZE) {
51b3c9c1ceSclaudio 		errno = EFBIG;
522cfd2d3bSclaudio 		goto err;
53b3c9c1ceSclaudio 	}
542cfd2d3bSclaudio 	size = (size_t)st.st_size;
552cfd2d3bSclaudio 	if ((buf = malloc(size)) == NULL)
562cfd2d3bSclaudio 		goto err;
572cfd2d3bSclaudio 	n = read(fd, buf, size);
58b3c9c1ceSclaudio 	if (n == -1)
592cfd2d3bSclaudio 		goto err;
60b3c9c1ceSclaudio 	if ((size_t)n != size) {
61b3c9c1ceSclaudio 		errno = EIO;
62b3c9c1ceSclaudio 		goto err;
63b3c9c1ceSclaudio 	}
642cfd2d3bSclaudio 	close(fd);
652cfd2d3bSclaudio 	*len = size;
662cfd2d3bSclaudio 	return buf;
672cfd2d3bSclaudio 
682cfd2d3bSclaudio err:
69b3c9c1ceSclaudio 	saved_errno = errno;
702cfd2d3bSclaudio 	close(fd);
712cfd2d3bSclaudio 	free(buf);
72b3c9c1ceSclaudio 	errno = saved_errno;
732cfd2d3bSclaudio 	return NULL;
742cfd2d3bSclaudio }
752cfd2d3bSclaudio 
762cfd2d3bSclaudio /*
771aea4e0eSclaudio  * Return the size of the data blob in outlen for an inlen sized base64 buffer.
781aea4e0eSclaudio  * Returns 0 on success and -1 if inlen would overflow an int.
791aea4e0eSclaudio  */
801aea4e0eSclaudio int
base64_decode_len(size_t inlen,size_t * outlen)811aea4e0eSclaudio base64_decode_len(size_t inlen, size_t *outlen)
821aea4e0eSclaudio {
831aea4e0eSclaudio 	*outlen = 0;
841aea4e0eSclaudio 	if (inlen >= INT_MAX - 3)
851aea4e0eSclaudio 		return -1;
861aea4e0eSclaudio 	*outlen = ((inlen + 3) / 4) * 3 + 1;
871aea4e0eSclaudio 	return 0;
881aea4e0eSclaudio }
891aea4e0eSclaudio 
901aea4e0eSclaudio /*
91087c4643Sclaudio  * Decode base64 encoded string into binary buffer returned in out.
92087c4643Sclaudio  * The out buffer size is stored in outlen.
93087c4643Sclaudio  * Returns 0 on success or -1 for any errors.
94087c4643Sclaudio  */
95087c4643Sclaudio int
base64_decode(const unsigned char * in,size_t inlen,unsigned char ** out,size_t * outlen)9641edc670Sclaudio base64_decode(const unsigned char *in, size_t inlen,
9741edc670Sclaudio     unsigned char **out, size_t *outlen)
98087c4643Sclaudio {
99c738bef7Sclaudio 	EVP_ENCODE_CTX *ctx;
100c738bef7Sclaudio 	unsigned char *to = NULL;
1011aea4e0eSclaudio 	size_t tolen;
1021aea4e0eSclaudio 	int evplen;
103087c4643Sclaudio 
104c738bef7Sclaudio 	if ((ctx = EVP_ENCODE_CTX_new()) == NULL)
105087c4643Sclaudio 		err(1, "EVP_ENCODE_CTX_new");
106087c4643Sclaudio 
107087c4643Sclaudio 	*out = NULL;
108087c4643Sclaudio 	*outlen = 0;
109087c4643Sclaudio 
1101aea4e0eSclaudio 	if (base64_decode_len(inlen, &tolen) == -1)
111c738bef7Sclaudio 		goto fail;
112087c4643Sclaudio 	if ((to = malloc(tolen)) == NULL)
113c738bef7Sclaudio 		err(1, NULL);
114087c4643Sclaudio 
1151aea4e0eSclaudio 	evplen = tolen;
116087c4643Sclaudio 	EVP_DecodeInit(ctx);
1171aea4e0eSclaudio 	if (EVP_DecodeUpdate(ctx, to, &evplen, in, inlen) == -1)
118087c4643Sclaudio 		goto fail;
1191aea4e0eSclaudio 	*outlen = evplen;
1201aea4e0eSclaudio 	if (EVP_DecodeFinal(ctx, to + evplen, &evplen) == -1)
121087c4643Sclaudio 		goto fail;
1221aea4e0eSclaudio 	*outlen += evplen;
123087c4643Sclaudio 	*out = to;
124c738bef7Sclaudio 
125c738bef7Sclaudio 	EVP_ENCODE_CTX_free(ctx);
126087c4643Sclaudio 	return 0;
127087c4643Sclaudio 
128087c4643Sclaudio fail:
129087c4643Sclaudio 	free(to);
130c738bef7Sclaudio 	EVP_ENCODE_CTX_free(ctx);
131087c4643Sclaudio 	return -1;
132087c4643Sclaudio }
133087c4643Sclaudio 
1341aea4e0eSclaudio /*
1351aea4e0eSclaudio  * Return the size of the base64 blob in outlen for a inlen sized binary buffer.
1361aea4e0eSclaudio  * Returns 0 on success and -1 if inlen would overflow the calculation.
1371aea4e0eSclaudio  */
1381aea4e0eSclaudio int
base64_encode_len(size_t inlen,size_t * outlen)1391aea4e0eSclaudio base64_encode_len(size_t inlen, size_t *outlen)
1401aea4e0eSclaudio {
1411aea4e0eSclaudio 	*outlen = 0;
1421aea4e0eSclaudio 	if (inlen >= INT_MAX / 2)
1431aea4e0eSclaudio 		return -1;
1441aea4e0eSclaudio 	*outlen = ((inlen + 2) / 3) * 4 + 1;
1451aea4e0eSclaudio 	return 0;
1461aea4e0eSclaudio }
1471aea4e0eSclaudio 
1481aea4e0eSclaudio /*
1491aea4e0eSclaudio  * Encode a binary buffer into a base64 encoded string returned in out.
1501aea4e0eSclaudio  * Returns 0 on success or -1 for any errors.
1511aea4e0eSclaudio  */
1526f704872Sclaudio int
base64_encode(const unsigned char * in,size_t inlen,char ** out)1536f704872Sclaudio base64_encode(const unsigned char *in, size_t inlen, char **out)
1546f704872Sclaudio {
1556f704872Sclaudio 	unsigned char *to;
156577ce9cfSclaudio 	size_t tolen;
1576f704872Sclaudio 
1586f704872Sclaudio 	*out = NULL;
1596f704872Sclaudio 
1601aea4e0eSclaudio 	if (base64_encode_len(inlen, &tolen) == -1)
1616f704872Sclaudio 		return -1;
1626f704872Sclaudio 	if ((to = malloc(tolen)) == NULL)
1636f704872Sclaudio 		return -1;
1646f704872Sclaudio 
165577ce9cfSclaudio 	EVP_EncodeBlock(to, in, inlen);
1666f704872Sclaudio 	*out = to;
1676f704872Sclaudio 	return 0;
1686f704872Sclaudio }
1696f704872Sclaudio 
170087c4643Sclaudio /*
171087c4643Sclaudio  * Convert binary buffer of size dsz into an upper-case hex-string.
172087c4643Sclaudio  * Returns pointer to the newly allocated string. Function can't fail.
173087c4643Sclaudio  */
174087c4643Sclaudio char *
hex_encode(const unsigned char * in,size_t insz)175087c4643Sclaudio hex_encode(const unsigned char *in, size_t insz)
176087c4643Sclaudio {
177087c4643Sclaudio 	const char hex[] = "0123456789ABCDEF";
178087c4643Sclaudio 	size_t i;
179087c4643Sclaudio 	char *out;
180087c4643Sclaudio 
181087c4643Sclaudio 	if ((out = calloc(2, insz + 1)) == NULL)
182087c4643Sclaudio 		err(1, NULL);
183087c4643Sclaudio 
184087c4643Sclaudio 	for (i = 0; i < insz; i++) {
185087c4643Sclaudio 		out[i * 2] = hex[in[i] >> 4];
186087c4643Sclaudio 		out[i * 2 + 1] = hex[in[i] & 0xf];
187087c4643Sclaudio 	}
188087c4643Sclaudio 	out[i * 2] = '\0';
189087c4643Sclaudio 
190087c4643Sclaudio 	return out;
191087c4643Sclaudio }
192aef00ae0Sclaudio 
193aef00ae0Sclaudio /*
194aef00ae0Sclaudio  * Hex decode hexstring into the supplied buffer.
195aef00ae0Sclaudio  * Return 0 on success else -1, if buffer too small or bad encoding.
196aef00ae0Sclaudio  */
197aef00ae0Sclaudio int
hex_decode(const char * hexstr,char * buf,size_t len)198aef00ae0Sclaudio hex_decode(const char *hexstr, char *buf, size_t len)
199aef00ae0Sclaudio {
200aef00ae0Sclaudio 	unsigned char ch, r;
201aef00ae0Sclaudio 	size_t pos = 0;
202aef00ae0Sclaudio 	int i;
203aef00ae0Sclaudio 
204aef00ae0Sclaudio 	while (*hexstr) {
205aef00ae0Sclaudio 		r = 0;
206aef00ae0Sclaudio 		for (i = 0; i < 2; i++) {
207aef00ae0Sclaudio 			ch = hexstr[i];
208aef00ae0Sclaudio 			if (isdigit(ch))
209aef00ae0Sclaudio 				ch -= '0';
210aef00ae0Sclaudio 			else if (islower(ch))
211aef00ae0Sclaudio 				ch -= ('a' - 10);
212aef00ae0Sclaudio 			else if (isupper(ch))
213aef00ae0Sclaudio 				ch -= ('A' - 10);
214aef00ae0Sclaudio 			else
215aef00ae0Sclaudio 				return -1;
216aef00ae0Sclaudio 			if (ch > 0xf)
217aef00ae0Sclaudio 				return -1;
218aef00ae0Sclaudio 			r = r << 4 | ch;
219aef00ae0Sclaudio 		}
220aef00ae0Sclaudio 		if (pos < len)
221aef00ae0Sclaudio 			buf[pos++] = r;
222aef00ae0Sclaudio 		else
223aef00ae0Sclaudio 			return -1;
224aef00ae0Sclaudio 
225aef00ae0Sclaudio 		hexstr += 2;
226aef00ae0Sclaudio 	}
227aef00ae0Sclaudio 	return 0;
228aef00ae0Sclaudio }
229