1 /*-------------------------------------------------------------------------
2  *
3  * base64.c
4  *	  Encoding and decoding routines for base64 without whitespace.
5  *
6  * Copyright (c) 2001-2020, PostgreSQL Global Development Group
7  *
8  *
9  * IDENTIFICATION
10  *	  src/common/base64.c
11  *
12  *-------------------------------------------------------------------------
13  */
14 
15 #ifndef FRONTEND
16 #include "postgres.h"
17 #else
18 #include "postgres_fe.h"
19 #endif
20 
21 #include "common/base64.h"
22 
23 /*
24  * BASE64
25  */
26 
27 static const char _base64[] =
28 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
29 
30 static const int8 b64lookup[128] = {
31 	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
32 	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
33 	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
34 	52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
35 	-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
36 	15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
37 	-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
38 	41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
39 };
40 
41 /*
42  * pg_b64_encode
43  *
44  * Encode into base64 the given string.  Returns the length of the encoded
45  * string, and -1 in the event of an error with the result buffer zeroed
46  * for safety.
47  */
48 int
pg_b64_encode(const char * src,int len,char * dst,int dstlen)49 pg_b64_encode(const char *src, int len, char *dst, int dstlen)
50 {
51 	char	   *p;
52 	const char *s,
53 			   *end = src + len;
54 	int			pos = 2;
55 	uint32		buf = 0;
56 
57 	s = src;
58 	p = dst;
59 
60 	while (s < end)
61 	{
62 		buf |= (unsigned char) *s << (pos << 3);
63 		pos--;
64 		s++;
65 
66 		/* write it out */
67 		if (pos < 0)
68 		{
69 			/*
70 			 * Leave if there is an overflow in the area allocated for the
71 			 * encoded string.
72 			 */
73 			if ((p - dst + 4) > dstlen)
74 				goto error;
75 
76 			*p++ = _base64[(buf >> 18) & 0x3f];
77 			*p++ = _base64[(buf >> 12) & 0x3f];
78 			*p++ = _base64[(buf >> 6) & 0x3f];
79 			*p++ = _base64[buf & 0x3f];
80 
81 			pos = 2;
82 			buf = 0;
83 		}
84 	}
85 	if (pos != 2)
86 	{
87 		/*
88 		 * Leave if there is an overflow in the area allocated for the encoded
89 		 * string.
90 		 */
91 		if ((p - dst + 4) > dstlen)
92 			goto error;
93 
94 		*p++ = _base64[(buf >> 18) & 0x3f];
95 		*p++ = _base64[(buf >> 12) & 0x3f];
96 		*p++ = (pos == 0) ? _base64[(buf >> 6) & 0x3f] : '=';
97 		*p++ = '=';
98 	}
99 
100 	Assert((p - dst) <= dstlen);
101 	return p - dst;
102 
103 error:
104 	memset(dst, 0, dstlen);
105 	return -1;
106 }
107 
108 /*
109  * pg_b64_decode
110  *
111  * Decode the given base64 string.  Returns the length of the decoded
112  * string on success, and -1 in the event of an error with the result
113  * buffer zeroed for safety.
114  */
115 int
pg_b64_decode(const char * src,int len,char * dst,int dstlen)116 pg_b64_decode(const char *src, int len, char *dst, int dstlen)
117 {
118 	const char *srcend = src + len,
119 			   *s = src;
120 	char	   *p = dst;
121 	char		c;
122 	int			b = 0;
123 	uint32		buf = 0;
124 	int			pos = 0,
125 				end = 0;
126 
127 	while (s < srcend)
128 	{
129 		c = *s++;
130 
131 		/* Leave if a whitespace is found */
132 		if (c == ' ' || c == '\t' || c == '\n' || c == '\r')
133 			goto error;
134 
135 		if (c == '=')
136 		{
137 			/* end sequence */
138 			if (!end)
139 			{
140 				if (pos == 2)
141 					end = 1;
142 				else if (pos == 3)
143 					end = 2;
144 				else
145 				{
146 					/*
147 					 * Unexpected "=" character found while decoding base64
148 					 * sequence.
149 					 */
150 					goto error;
151 				}
152 			}
153 			b = 0;
154 		}
155 		else
156 		{
157 			b = -1;
158 			if (c > 0 && c < 127)
159 				b = b64lookup[(unsigned char) c];
160 			if (b < 0)
161 			{
162 				/* invalid symbol found */
163 				goto error;
164 			}
165 		}
166 		/* add it to buffer */
167 		buf = (buf << 6) + b;
168 		pos++;
169 		if (pos == 4)
170 		{
171 			/*
172 			 * Leave if there is an overflow in the area allocated for the
173 			 * decoded string.
174 			 */
175 			if ((p - dst + 1) > dstlen)
176 				goto error;
177 			*p++ = (buf >> 16) & 255;
178 
179 			if (end == 0 || end > 1)
180 			{
181 				/* overflow check */
182 				if ((p - dst + 1) > dstlen)
183 					goto error;
184 				*p++ = (buf >> 8) & 255;
185 			}
186 			if (end == 0 || end > 2)
187 			{
188 				/* overflow check */
189 				if ((p - dst + 1) > dstlen)
190 					goto error;
191 				*p++ = buf & 255;
192 			}
193 			buf = 0;
194 			pos = 0;
195 		}
196 	}
197 
198 	if (pos != 0)
199 	{
200 		/*
201 		 * base64 end sequence is invalid.  Input data is missing padding, is
202 		 * truncated or is otherwise corrupted.
203 		 */
204 		goto error;
205 	}
206 
207 	Assert((p - dst) <= dstlen);
208 	return p - dst;
209 
210 error:
211 	memset(dst, 0, dstlen);
212 	return -1;
213 }
214 
215 /*
216  * pg_b64_enc_len
217  *
218  * Returns to caller the length of the string if it were encoded with
219  * base64 based on the length provided by caller.  This is useful to
220  * estimate how large a buffer allocation needs to be done before doing
221  * the actual encoding.
222  */
223 int
pg_b64_enc_len(int srclen)224 pg_b64_enc_len(int srclen)
225 {
226 	/* 3 bytes will be converted to 4 */
227 	return (srclen + 2) * 4 / 3;
228 }
229 
230 /*
231  * pg_b64_dec_len
232  *
233  * Returns to caller the length of the string if it were to be decoded
234  * with base64, based on the length given by caller.  This is useful to
235  * estimate how large a buffer allocation needs to be done before doing
236  * the actual decoding.
237  */
238 int
pg_b64_dec_len(int srclen)239 pg_b64_dec_len(int srclen)
240 {
241 	return (srclen * 3) >> 2;
242 }
243