1e5b75505Sopenharmony_ci/*
2e5b75505Sopenharmony_ci * Base64 encoding/decoding (RFC1341)
3e5b75505Sopenharmony_ci * Copyright (c) 2005-2019, Jouni Malinen <j@w1.fi>
4e5b75505Sopenharmony_ci *
5e5b75505Sopenharmony_ci * This software may be distributed under the terms of the BSD license.
6e5b75505Sopenharmony_ci * See README for more details.
7e5b75505Sopenharmony_ci */
8e5b75505Sopenharmony_ci
9e5b75505Sopenharmony_ci#include "includes.h"
10e5b75505Sopenharmony_ci#include <stdint.h>
11e5b75505Sopenharmony_ci
12e5b75505Sopenharmony_ci#include "os.h"
13e5b75505Sopenharmony_ci#include "base64.h"
14e5b75505Sopenharmony_ci
15e5b75505Sopenharmony_cistatic const unsigned char base64_table[65] =
16e5b75505Sopenharmony_ci	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
17e5b75505Sopenharmony_cistatic const unsigned char base64_url_table[65] =
18e5b75505Sopenharmony_ci	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
19e5b75505Sopenharmony_ci
20e5b75505Sopenharmony_ci
21e5b75505Sopenharmony_cistatic unsigned char * base64_gen_encode(const unsigned char *src, size_t len,
22e5b75505Sopenharmony_ci					 size_t *out_len,
23e5b75505Sopenharmony_ci					 const unsigned char *table,
24e5b75505Sopenharmony_ci					 int add_pad)
25e5b75505Sopenharmony_ci{
26e5b75505Sopenharmony_ci	unsigned char *out, *pos;
27e5b75505Sopenharmony_ci	const unsigned char *end, *in;
28e5b75505Sopenharmony_ci	size_t olen;
29e5b75505Sopenharmony_ci	int line_len;
30e5b75505Sopenharmony_ci
31e5b75505Sopenharmony_ci	if (len >= SIZE_MAX / 4)
32e5b75505Sopenharmony_ci		return NULL;
33e5b75505Sopenharmony_ci	olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */
34e5b75505Sopenharmony_ci	if (add_pad)
35e5b75505Sopenharmony_ci		olen += olen / 72; /* line feeds */
36e5b75505Sopenharmony_ci	olen++; /* nul termination */
37e5b75505Sopenharmony_ci	if (olen < len)
38e5b75505Sopenharmony_ci		return NULL; /* integer overflow */
39e5b75505Sopenharmony_ci	out = os_malloc(olen);
40e5b75505Sopenharmony_ci	if (out == NULL)
41e5b75505Sopenharmony_ci		return NULL;
42e5b75505Sopenharmony_ci
43e5b75505Sopenharmony_ci	end = src + len;
44e5b75505Sopenharmony_ci	in = src;
45e5b75505Sopenharmony_ci	pos = out;
46e5b75505Sopenharmony_ci	line_len = 0;
47e5b75505Sopenharmony_ci	while (end - in >= 3) {
48e5b75505Sopenharmony_ci		*pos++ = table[(in[0] >> 2) & 0x3f];
49e5b75505Sopenharmony_ci		*pos++ = table[(((in[0] & 0x03) << 4) | (in[1] >> 4)) & 0x3f];
50e5b75505Sopenharmony_ci		*pos++ = table[(((in[1] & 0x0f) << 2) | (in[2] >> 6)) & 0x3f];
51e5b75505Sopenharmony_ci		*pos++ = table[in[2] & 0x3f];
52e5b75505Sopenharmony_ci		in += 3;
53e5b75505Sopenharmony_ci		line_len += 4;
54e5b75505Sopenharmony_ci		if (add_pad && line_len >= 72) {
55e5b75505Sopenharmony_ci			*pos++ = '\n';
56e5b75505Sopenharmony_ci			line_len = 0;
57e5b75505Sopenharmony_ci		}
58e5b75505Sopenharmony_ci	}
59e5b75505Sopenharmony_ci
60e5b75505Sopenharmony_ci	if (end - in) {
61e5b75505Sopenharmony_ci		*pos++ = table[(in[0] >> 2) & 0x3f];
62e5b75505Sopenharmony_ci		if (end - in == 1) {
63e5b75505Sopenharmony_ci			*pos++ = table[((in[0] & 0x03) << 4) & 0x3f];
64e5b75505Sopenharmony_ci			if (add_pad)
65e5b75505Sopenharmony_ci				*pos++ = '=';
66e5b75505Sopenharmony_ci		} else {
67e5b75505Sopenharmony_ci			*pos++ = table[(((in[0] & 0x03) << 4) |
68e5b75505Sopenharmony_ci					(in[1] >> 4)) & 0x3f];
69e5b75505Sopenharmony_ci			*pos++ = table[((in[1] & 0x0f) << 2) & 0x3f];
70e5b75505Sopenharmony_ci		}
71e5b75505Sopenharmony_ci		if (add_pad)
72e5b75505Sopenharmony_ci			*pos++ = '=';
73e5b75505Sopenharmony_ci		line_len += 4;
74e5b75505Sopenharmony_ci	}
75e5b75505Sopenharmony_ci
76e5b75505Sopenharmony_ci	if (add_pad && line_len)
77e5b75505Sopenharmony_ci		*pos++ = '\n';
78e5b75505Sopenharmony_ci
79e5b75505Sopenharmony_ci	*pos = '\0';
80e5b75505Sopenharmony_ci	if (out_len)
81e5b75505Sopenharmony_ci		*out_len = pos - out;
82e5b75505Sopenharmony_ci	return out;
83e5b75505Sopenharmony_ci}
84e5b75505Sopenharmony_ci
85e5b75505Sopenharmony_ci
86e5b75505Sopenharmony_cistatic unsigned char * base64_gen_decode(const unsigned char *src, size_t len,
87e5b75505Sopenharmony_ci					 size_t *out_len,
88e5b75505Sopenharmony_ci					 const unsigned char *table)
89e5b75505Sopenharmony_ci{
90e5b75505Sopenharmony_ci	unsigned char dtable[256], *out, *pos, block[4], tmp;
91e5b75505Sopenharmony_ci	size_t i, count, olen;
92e5b75505Sopenharmony_ci	int pad = 0;
93e5b75505Sopenharmony_ci	size_t extra_pad;
94e5b75505Sopenharmony_ci
95e5b75505Sopenharmony_ci	os_memset(dtable, 0x80, 256);
96e5b75505Sopenharmony_ci	for (i = 0; i < sizeof(base64_table) - 1; i++)
97e5b75505Sopenharmony_ci		dtable[table[i]] = (unsigned char) i;
98e5b75505Sopenharmony_ci	dtable['='] = 0;
99e5b75505Sopenharmony_ci
100e5b75505Sopenharmony_ci	count = 0;
101e5b75505Sopenharmony_ci	for (i = 0; i < len; i++) {
102e5b75505Sopenharmony_ci		if (dtable[src[i]] != 0x80)
103e5b75505Sopenharmony_ci			count++;
104e5b75505Sopenharmony_ci	}
105e5b75505Sopenharmony_ci
106e5b75505Sopenharmony_ci	if (count == 0)
107e5b75505Sopenharmony_ci		return NULL;
108e5b75505Sopenharmony_ci	extra_pad = (4 - count % 4) % 4;
109e5b75505Sopenharmony_ci
110e5b75505Sopenharmony_ci	olen = (count + extra_pad) / 4 * 3;
111e5b75505Sopenharmony_ci	pos = out = os_malloc(olen);
112e5b75505Sopenharmony_ci	if (out == NULL)
113e5b75505Sopenharmony_ci		return NULL;
114e5b75505Sopenharmony_ci
115e5b75505Sopenharmony_ci	count = 0;
116e5b75505Sopenharmony_ci	for (i = 0; i < len + extra_pad; i++) {
117e5b75505Sopenharmony_ci		unsigned char val;
118e5b75505Sopenharmony_ci
119e5b75505Sopenharmony_ci		if (i >= len)
120e5b75505Sopenharmony_ci			val = '=';
121e5b75505Sopenharmony_ci		else
122e5b75505Sopenharmony_ci			val = src[i];
123e5b75505Sopenharmony_ci		tmp = dtable[val];
124e5b75505Sopenharmony_ci		if (tmp == 0x80)
125e5b75505Sopenharmony_ci			continue;
126e5b75505Sopenharmony_ci
127e5b75505Sopenharmony_ci		if (val == '=')
128e5b75505Sopenharmony_ci			pad++;
129e5b75505Sopenharmony_ci		block[count] = tmp;
130e5b75505Sopenharmony_ci		count++;
131e5b75505Sopenharmony_ci		if (count == 4) {
132e5b75505Sopenharmony_ci			*pos++ = (block[0] << 2) | (block[1] >> 4);
133e5b75505Sopenharmony_ci			*pos++ = (block[1] << 4) | (block[2] >> 2);
134e5b75505Sopenharmony_ci			*pos++ = (block[2] << 6) | block[3];
135e5b75505Sopenharmony_ci			count = 0;
136e5b75505Sopenharmony_ci			if (pad) {
137e5b75505Sopenharmony_ci				if (pad == 1)
138e5b75505Sopenharmony_ci					pos--;
139e5b75505Sopenharmony_ci				else if (pad == 2)
140e5b75505Sopenharmony_ci					pos -= 2;
141e5b75505Sopenharmony_ci				else {
142e5b75505Sopenharmony_ci					/* Invalid padding */
143e5b75505Sopenharmony_ci					os_free(out);
144e5b75505Sopenharmony_ci					return NULL;
145e5b75505Sopenharmony_ci				}
146e5b75505Sopenharmony_ci				break;
147e5b75505Sopenharmony_ci			}
148e5b75505Sopenharmony_ci		}
149e5b75505Sopenharmony_ci	}
150e5b75505Sopenharmony_ci
151e5b75505Sopenharmony_ci	*out_len = pos - out;
152e5b75505Sopenharmony_ci	return out;
153e5b75505Sopenharmony_ci}
154e5b75505Sopenharmony_ci
155e5b75505Sopenharmony_ci
156e5b75505Sopenharmony_ci/**
157e5b75505Sopenharmony_ci * base64_encode - Base64 encode
158e5b75505Sopenharmony_ci * @src: Data to be encoded
159e5b75505Sopenharmony_ci * @len: Length of the data to be encoded
160e5b75505Sopenharmony_ci * @out_len: Pointer to output length variable, or %NULL if not used
161e5b75505Sopenharmony_ci * Returns: Allocated buffer of out_len bytes of encoded data,
162e5b75505Sopenharmony_ci * or %NULL on failure
163e5b75505Sopenharmony_ci *
164e5b75505Sopenharmony_ci * Caller is responsible for freeing the returned buffer. Returned buffer is
165e5b75505Sopenharmony_ci * nul terminated to make it easier to use as a C string. The nul terminator is
166e5b75505Sopenharmony_ci * not included in out_len.
167e5b75505Sopenharmony_ci */
168e5b75505Sopenharmony_ciunsigned char * base64_encode(const unsigned char *src, size_t len,
169e5b75505Sopenharmony_ci			      size_t *out_len)
170e5b75505Sopenharmony_ci{
171e5b75505Sopenharmony_ci	return base64_gen_encode(src, len, out_len, base64_table, 1);
172e5b75505Sopenharmony_ci}
173e5b75505Sopenharmony_ci
174e5b75505Sopenharmony_ci
175e5b75505Sopenharmony_ciunsigned char * base64_url_encode(const unsigned char *src, size_t len,
176e5b75505Sopenharmony_ci				  size_t *out_len, int add_pad)
177e5b75505Sopenharmony_ci{
178e5b75505Sopenharmony_ci	return base64_gen_encode(src, len, out_len, base64_url_table, add_pad);
179e5b75505Sopenharmony_ci}
180e5b75505Sopenharmony_ci
181e5b75505Sopenharmony_ci
182e5b75505Sopenharmony_ci/**
183e5b75505Sopenharmony_ci * base64_decode - Base64 decode
184e5b75505Sopenharmony_ci * @src: Data to be decoded
185e5b75505Sopenharmony_ci * @len: Length of the data to be decoded
186e5b75505Sopenharmony_ci * @out_len: Pointer to output length variable
187e5b75505Sopenharmony_ci * Returns: Allocated buffer of out_len bytes of decoded data,
188e5b75505Sopenharmony_ci * or %NULL on failure
189e5b75505Sopenharmony_ci *
190e5b75505Sopenharmony_ci * Caller is responsible for freeing the returned buffer.
191e5b75505Sopenharmony_ci */
192e5b75505Sopenharmony_ciunsigned char * base64_decode(const unsigned char *src, size_t len,
193e5b75505Sopenharmony_ci			      size_t *out_len)
194e5b75505Sopenharmony_ci{
195e5b75505Sopenharmony_ci	return base64_gen_decode(src, len, out_len, base64_table);
196e5b75505Sopenharmony_ci}
197e5b75505Sopenharmony_ci
198e5b75505Sopenharmony_ci
199e5b75505Sopenharmony_ciunsigned char * base64_url_decode(const unsigned char *src, size_t len,
200e5b75505Sopenharmony_ci				  size_t *out_len)
201e5b75505Sopenharmony_ci{
202e5b75505Sopenharmony_ci	return base64_gen_decode(src, len, out_len, base64_url_table);
203e5b75505Sopenharmony_ci}
204