xref: /openbmc/libcper/base64.c (revision e42fb487839b242371b0150ab5b0b89c2d232976)
1*e42fb487SThu Nguyen #include <libcper/base64.h>
2*e42fb487SThu Nguyen #include <libcper/BaseTypes.h>
3a7d2cdddSEd Tanous 
4a7d2cdddSEd Tanous #include <stdlib.h>
5a7d2cdddSEd Tanous #include <string.h>
6a7d2cdddSEd Tanous #include <stdio.h>
7a7d2cdddSEd Tanous #include <assert.h>
8a7d2cdddSEd Tanous 
9a7d2cdddSEd Tanous static const UINT8 encode_table[65] =
10a7d2cdddSEd Tanous 	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
11a7d2cdddSEd Tanous 
12a7d2cdddSEd Tanous /**
13a7d2cdddSEd Tanous  *
14a7d2cdddSEd Tanous  * Caller is responsible for freeing the returned buffer.
15a7d2cdddSEd Tanous  */
base64_encode(const UINT8 * src,INT32 len,INT32 * out_len)16a7d2cdddSEd Tanous CHAR8 *base64_encode(const UINT8 *src, INT32 len, INT32 *out_len)
17a7d2cdddSEd Tanous {
18a7d2cdddSEd Tanous 	CHAR8 *out;
19a7d2cdddSEd Tanous 	CHAR8 *out_pos;
20a7d2cdddSEd Tanous 	const UINT8 *src_end;
21a7d2cdddSEd Tanous 	const UINT8 *in_pos;
22a7d2cdddSEd Tanous 
23a7d2cdddSEd Tanous 	if (!out_len) {
24a7d2cdddSEd Tanous 		return NULL;
25a7d2cdddSEd Tanous 	}
26a7d2cdddSEd Tanous 
27a7d2cdddSEd Tanous 	// 3 byte blocks to 4 byte blocks plus up to 2 bytes of padding
28a7d2cdddSEd Tanous 	*out_len = 4 * ((len + 2) / 3);
29a7d2cdddSEd Tanous 
30a7d2cdddSEd Tanous 	// Handle overflows
31a7d2cdddSEd Tanous 	if (*out_len < len) {
32a7d2cdddSEd Tanous 		return NULL;
33a7d2cdddSEd Tanous 	}
34a7d2cdddSEd Tanous 
35a7d2cdddSEd Tanous 	out = malloc(*out_len);
36a7d2cdddSEd Tanous 	if (out == NULL) {
37a7d2cdddSEd Tanous 		return NULL;
38a7d2cdddSEd Tanous 	}
39a7d2cdddSEd Tanous 
40a7d2cdddSEd Tanous 	src_end = src + len;
41a7d2cdddSEd Tanous 	in_pos = src;
42a7d2cdddSEd Tanous 	out_pos = out;
43a7d2cdddSEd Tanous 	while (src_end - in_pos >= 3) {
44a7d2cdddSEd Tanous 		*out_pos++ = encode_table[in_pos[0] >> 2];
45a7d2cdddSEd Tanous 		*out_pos++ = encode_table[((in_pos[0] & 0x03) << 4) |
46a7d2cdddSEd Tanous 					  (in_pos[1] >> 4)];
47a7d2cdddSEd Tanous 		*out_pos++ = encode_table[((in_pos[1] & 0x0f) << 2) |
48a7d2cdddSEd Tanous 					  (in_pos[2] >> 6)];
49a7d2cdddSEd Tanous 		*out_pos++ = encode_table[in_pos[2] & 0x3f];
50a7d2cdddSEd Tanous 		in_pos += 3;
51a7d2cdddSEd Tanous 	}
52a7d2cdddSEd Tanous 
53a7d2cdddSEd Tanous 	if (src_end - in_pos) {
54a7d2cdddSEd Tanous 		*out_pos++ = encode_table[in_pos[0] >> 2];
55a7d2cdddSEd Tanous 		if (src_end - in_pos == 1) {
56a7d2cdddSEd Tanous 			*out_pos++ = encode_table[(in_pos[0] & 0x03) << 4];
57a7d2cdddSEd Tanous 			*out_pos++ = '=';
58a7d2cdddSEd Tanous 		} else {
59a7d2cdddSEd Tanous 			*out_pos++ = encode_table[((in_pos[0] & 0x03) << 4) |
60a7d2cdddSEd Tanous 						  (in_pos[1] >> 4)];
61a7d2cdddSEd Tanous 			*out_pos++ = encode_table[(in_pos[1] & 0x0f) << 2];
62a7d2cdddSEd Tanous 		}
63a7d2cdddSEd Tanous 		*out_pos++ = '=';
64a7d2cdddSEd Tanous 	}
65a7d2cdddSEd Tanous 
66a7d2cdddSEd Tanous 	return out;
67a7d2cdddSEd Tanous }
68a7d2cdddSEd Tanous 
69a7d2cdddSEd Tanous // Base64 decode table.  Invalid values are specified with 0x80.
70a7d2cdddSEd Tanous UINT8 decode_table[256] =
71a7d2cdddSEd Tanous 	"\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80"
72a7d2cdddSEd Tanous 	"\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80"
73a7d2cdddSEd Tanous 	"\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x3e\x80\x80\x80\x3f"
74a7d2cdddSEd Tanous 	"\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x80\x80\x80\x00\x80\x80"
75a7d2cdddSEd Tanous 	"\x80\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e"
76a7d2cdddSEd Tanous 	"\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x80\x80\x80\x80\x80"
77a7d2cdddSEd Tanous 	"\x80\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28"
78a7d2cdddSEd Tanous 	"\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x80\x80\x80\x80\x80"
79a7d2cdddSEd Tanous 	"\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80"
80a7d2cdddSEd Tanous 	"\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80"
81a7d2cdddSEd Tanous 	"\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80"
82a7d2cdddSEd Tanous 	"\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80"
83a7d2cdddSEd Tanous 	"\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80"
84a7d2cdddSEd Tanous 	"\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80"
85a7d2cdddSEd Tanous 	"\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80"
86a7d2cdddSEd Tanous 	"\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80";
87a7d2cdddSEd Tanous 
88a7d2cdddSEd Tanous /**
89a7d2cdddSEd Tanous  *
90a7d2cdddSEd Tanous  * Caller is responsible for freeing the returned buffer.
91a7d2cdddSEd Tanous  */
base64_decode(const CHAR8 * src,INT32 len,INT32 * out_len)92a7d2cdddSEd Tanous UINT8 *base64_decode(const CHAR8 *src, INT32 len, INT32 *out_len)
93a7d2cdddSEd Tanous {
94a7d2cdddSEd Tanous 	UINT8 *out;
95a7d2cdddSEd Tanous 	UINT8 *pos;
96a7d2cdddSEd Tanous 	UINT8 block[4];
97a7d2cdddSEd Tanous 	UINT8 tmp;
98a7d2cdddSEd Tanous 	INT32 block_index;
99a7d2cdddSEd Tanous 	INT32 src_index;
100a7d2cdddSEd Tanous 	UINT32 pad_count = 0;
101a7d2cdddSEd Tanous 
102a7d2cdddSEd Tanous 	if (!out_len) {
103a7d2cdddSEd Tanous 		return NULL;
104a7d2cdddSEd Tanous 	}
105a7d2cdddSEd Tanous 
106a7d2cdddSEd Tanous 	// Malloc might be up to 2 larger dependent on padding
107a7d2cdddSEd Tanous 	*out_len = len / 4 * 3;
108a7d2cdddSEd Tanous 	pos = out = malloc(*out_len);
109a7d2cdddSEd Tanous 	if (out == NULL) {
110a7d2cdddSEd Tanous 		return NULL;
111a7d2cdddSEd Tanous 	}
112a7d2cdddSEd Tanous 
113a7d2cdddSEd Tanous 	block_index = 0;
114a7d2cdddSEd Tanous 	for (src_index = 0; src_index < len; src_index++) {
115a7d2cdddSEd Tanous 		tmp = decode_table[(UINT8)src[src_index]];
116a7d2cdddSEd Tanous 		if (tmp == 0x80) {
117a7d2cdddSEd Tanous 			free(out);
118a7d2cdddSEd Tanous 			return NULL;
119a7d2cdddSEd Tanous 		}
120a7d2cdddSEd Tanous 
121a7d2cdddSEd Tanous 		if (src[src_index] == '=') {
122a7d2cdddSEd Tanous 			pad_count++;
123a7d2cdddSEd Tanous 		}
124a7d2cdddSEd Tanous 
125a7d2cdddSEd Tanous 		block[block_index] = tmp;
126a7d2cdddSEd Tanous 		block_index++;
127a7d2cdddSEd Tanous 		if (block_index == 4) {
128a7d2cdddSEd Tanous 			*pos++ = (block[0] << 2) | (block[1] >> 4);
129a7d2cdddSEd Tanous 			*pos++ = (block[1] << 4) | (block[2] >> 2);
130a7d2cdddSEd Tanous 			*pos++ = (block[2] << 6) | block[3];
131a7d2cdddSEd Tanous 			if (pad_count > 0) {
132a7d2cdddSEd Tanous 				if (pad_count == 1) {
133a7d2cdddSEd Tanous 					pos--;
134a7d2cdddSEd Tanous 				} else if (pad_count == 2) {
135a7d2cdddSEd Tanous 					pos -= 2;
136a7d2cdddSEd Tanous 				} else {
137a7d2cdddSEd Tanous 					/* Invalid pad_counting */
138a7d2cdddSEd Tanous 					free(out);
139a7d2cdddSEd Tanous 					return NULL;
140a7d2cdddSEd Tanous 				}
141a7d2cdddSEd Tanous 				break;
142a7d2cdddSEd Tanous 			}
143a7d2cdddSEd Tanous 			block_index = 0;
144a7d2cdddSEd Tanous 		}
145a7d2cdddSEd Tanous 	}
146a7d2cdddSEd Tanous 
147a7d2cdddSEd Tanous 	*out_len = pos - out;
148a7d2cdddSEd Tanous 	return out;
149a7d2cdddSEd Tanous }
150