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