xref: /openbmc/libcper/base64.c (revision 3e72806517cfd324023e6e6268000eb5dc783678)
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 	"\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80"
76 	"\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80"
77 	"\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x3e\x80\x80\x80\x3f"
78 	"\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x80\x80\x80\x00\x80\x80"
79 	"\x80\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e"
80 	"\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x80\x80\x80\x80\x80"
81 	"\x80\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28"
82 	"\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x80\x80\x80\x80\x80"
83 	"\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80"
84 	"\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80"
85 	"\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80"
86 	"\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80"
87 	"\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80"
88 	"\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80"
89 	"\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80"
90 	"\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80";
91 
92 /**
93  *
94  * Caller is responsible for freeing the returned buffer.
95  */
base64_decode(const CHAR8 * src,INT32 len,INT32 * out_len)96 UINT8 *base64_decode(const CHAR8 *src, INT32 len, INT32 *out_len)
97 {
98 	UINT8 *out = NULL;
99 	UINT8 *pos = NULL;
100 	UINT8 block[4];
101 	INT32 block_index = 0;
102 	INT32 src_index = 0;
103 
104 	if (!out_len) {
105 		goto error;
106 	}
107 
108 	// Malloc might be up to 2 larger dependent on padding
109 	*out_len = len / 4 * 3 + 2;
110 	pos = out = malloc(*out_len);
111 	if (out == NULL) {
112 		goto error;
113 	}
114 
115 	block_index = 0;
116 	for (src_index = 0; src_index < len; src_index++) {
117 		char current_char = src[src_index];
118 		if (current_char == '=') {
119 			break;
120 		}
121 		// If the final character is a newline, as can occur in many editors
122 		// then ignore it.
123 		if (src_index + 1 == len && current_char == '\n') {
124 			printf("Ignoring trailing newline.\n");
125 			break;
126 		}
127 
128 		block[block_index] = decode_table[(UINT8)current_char];
129 		if (block[block_index] == 0x80) {
130 			printf("Invalid character \"%c\".\n", current_char);
131 			goto error;
132 		}
133 
134 		block_index++;
135 		if (block_index == 4) {
136 			*pos++ = (block[0] << 2) | (block[1] >> 4);
137 			*pos++ = (block[1] << 4) | (block[2] >> 2);
138 			*pos++ = (block[2] << 6) | block[3];
139 			block_index = 0;
140 		}
141 	}
142 	if (block_index == 0) {
143 		// mod 4 Even number of characters, no padding.
144 	} else if (block_index == 1) {
145 		printf("Invalid base64 input length.  Last character truncated.\n");
146 		goto error;
147 	} else if (block_index == 2) {
148 		*pos++ = (block[0] << 2) | (block[1] >> 4);
149 	} else if (block_index == 3) {
150 		*pos++ = (block[0] << 2) | (block[1] >> 4);
151 		*pos++ = (block[1] << 4) | (block[2] >> 2);
152 	} else {
153 		/* Invalid pad_counting */
154 		printf("Invalid base64 input length %d.\n", block_index);
155 		goto error;
156 	}
157 
158 	*out_len = pos - out;
159 	return out;
160 
161 error:
162 	free(out);
163 	return NULL;
164 }
165