1 | // Copyright (c) Microsoft. All rights reserved.
|
---|
2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
---|
3 |
|
---|
4 | #include <stdlib.h>
|
---|
5 | #include <stdint.h>
|
---|
6 | #include "azure_c_shared_utility/gballoc.h"
|
---|
7 | #include "azure_c_shared_utility/azure_base64.h"
|
---|
8 | #include "azure_c_shared_utility/xlogging.h"
|
---|
9 |
|
---|
10 |
|
---|
11 | #define splitInt(intVal, bytePos) (char)((intVal >> (bytePos << 3)) & 0xFF)
|
---|
12 | #define joinChars(a, b, c, d) (uint32_t)((uint32_t)a + ((uint32_t)b << 8) + ((uint32_t)c << 16) + ((uint32_t)d << 24))
|
---|
13 |
|
---|
14 | static char base64char(unsigned char val)
|
---|
15 | {
|
---|
16 | char result;
|
---|
17 |
|
---|
18 | if (val < 26)
|
---|
19 | {
|
---|
20 | result = 'A' + (char)val;
|
---|
21 | }
|
---|
22 | else if (val < 52)
|
---|
23 | {
|
---|
24 | result = 'a' + ((char)val - 26);
|
---|
25 | }
|
---|
26 | else if (val < 62)
|
---|
27 | {
|
---|
28 | result = '0' + ((char)val - 52);
|
---|
29 | }
|
---|
30 | else if (val == 62)
|
---|
31 | {
|
---|
32 | result = '+';
|
---|
33 | }
|
---|
34 | else
|
---|
35 | {
|
---|
36 | result = '/';
|
---|
37 | }
|
---|
38 |
|
---|
39 | return result;
|
---|
40 | }
|
---|
41 |
|
---|
42 | static char base64b16(unsigned char val)
|
---|
43 | {
|
---|
44 | const uint32_t base64b16values[4] = {
|
---|
45 | joinChars('A', 'E', 'I', 'M'),
|
---|
46 | joinChars('Q', 'U', 'Y', 'c'),
|
---|
47 | joinChars('g', 'k', 'o', 's'),
|
---|
48 | joinChars('w', '0', '4', '8')
|
---|
49 | };
|
---|
50 | return splitInt(base64b16values[val >> 2], (val & 0x03));
|
---|
51 | }
|
---|
52 |
|
---|
53 | static char base64b8(unsigned char val)
|
---|
54 | {
|
---|
55 | const uint32_t base64b8values = joinChars('A', 'Q', 'g', 'w');
|
---|
56 | return splitInt(base64b8values, val);
|
---|
57 | }
|
---|
58 |
|
---|
59 | static int base64toValue(char base64character, unsigned char* value)
|
---|
60 | {
|
---|
61 | int result = 0;
|
---|
62 | if (('A' <= base64character) && (base64character <= 'Z'))
|
---|
63 | {
|
---|
64 | *value = base64character - 'A';
|
---|
65 | }
|
---|
66 | else if (('a' <= base64character) && (base64character <= 'z'))
|
---|
67 | {
|
---|
68 | *value = ('Z' - 'A') + 1 + (base64character - 'a');
|
---|
69 | }
|
---|
70 | else if (('0' <= base64character) && (base64character <= '9'))
|
---|
71 | {
|
---|
72 | *value = ('Z' - 'A') + 1 + ('z' - 'a') + 1 + (base64character - '0');
|
---|
73 | }
|
---|
74 | else if ('+' == base64character)
|
---|
75 | {
|
---|
76 | *value = 62;
|
---|
77 | }
|
---|
78 | else if ('/' == base64character)
|
---|
79 | {
|
---|
80 | *value = 63;
|
---|
81 | }
|
---|
82 | else
|
---|
83 | {
|
---|
84 | *value = 0;
|
---|
85 | result = -1;
|
---|
86 | }
|
---|
87 | return result;
|
---|
88 | }
|
---|
89 |
|
---|
90 | static size_t numberOfBase64Characters(const char* encodedString)
|
---|
91 | {
|
---|
92 | size_t length = 0;
|
---|
93 | unsigned char junkChar;
|
---|
94 | while (base64toValue(encodedString[length],&junkChar) != -1)
|
---|
95 | {
|
---|
96 | length++;
|
---|
97 | }
|
---|
98 | return length;
|
---|
99 | }
|
---|
100 |
|
---|
101 | /*returns the count of original bytes before being base64 encoded*/
|
---|
102 | /*notice NO validation of the content of encodedString. Its length is validated to be a multiple of 4.*/
|
---|
103 | static size_t Base64decode_len(const char *encodedString)
|
---|
104 | {
|
---|
105 | size_t result;
|
---|
106 | size_t sourceLength = strlen(encodedString);
|
---|
107 |
|
---|
108 | if (sourceLength == 0)
|
---|
109 | {
|
---|
110 | result = 0;
|
---|
111 | }
|
---|
112 | else
|
---|
113 | {
|
---|
114 | result = sourceLength / 4 * 3;
|
---|
115 | if (encodedString[sourceLength - 1] == '=')
|
---|
116 | {
|
---|
117 | if (encodedString[sourceLength - 2] == '=')
|
---|
118 | {
|
---|
119 | result --;
|
---|
120 | }
|
---|
121 | result--;
|
---|
122 | }
|
---|
123 | }
|
---|
124 | return result;
|
---|
125 | }
|
---|
126 |
|
---|
127 | static void Base64decode(unsigned char *decodedString, const char *base64String)
|
---|
128 | {
|
---|
129 |
|
---|
130 | size_t numberOfEncodedChars;
|
---|
131 | size_t indexOfFirstEncodedChar;
|
---|
132 | size_t decodedIndex;
|
---|
133 |
|
---|
134 | //
|
---|
135 | // We can only operate on individual bytes. If we attempt to work
|
---|
136 | // on anything larger we could get an alignment fault on some
|
---|
137 | // architectures
|
---|
138 | //
|
---|
139 |
|
---|
140 | numberOfEncodedChars = numberOfBase64Characters(base64String);
|
---|
141 | indexOfFirstEncodedChar = 0;
|
---|
142 | decodedIndex = 0;
|
---|
143 | while (numberOfEncodedChars >= 4)
|
---|
144 | {
|
---|
145 | unsigned char c1;
|
---|
146 | unsigned char c2;
|
---|
147 | unsigned char c3;
|
---|
148 | unsigned char c4;
|
---|
149 | (void)base64toValue(base64String[indexOfFirstEncodedChar], &c1);
|
---|
150 | (void)base64toValue(base64String[indexOfFirstEncodedChar + 1], &c2);
|
---|
151 | (void)base64toValue(base64String[indexOfFirstEncodedChar + 2], &c3);
|
---|
152 | (void)base64toValue(base64String[indexOfFirstEncodedChar + 3], &c4);
|
---|
153 | decodedString[decodedIndex] = (c1 << 2) | (c2 >> 4);
|
---|
154 | decodedIndex++;
|
---|
155 | decodedString[decodedIndex] = ((c2 & 0x0f) << 4) | (c3 >> 2);
|
---|
156 | decodedIndex++;
|
---|
157 | decodedString[decodedIndex] = ((c3 & 0x03) << 6) | c4;
|
---|
158 | decodedIndex++;
|
---|
159 | numberOfEncodedChars -= 4;
|
---|
160 | indexOfFirstEncodedChar += 4;
|
---|
161 |
|
---|
162 | }
|
---|
163 |
|
---|
164 | if (numberOfEncodedChars == 2)
|
---|
165 | {
|
---|
166 | unsigned char c1;
|
---|
167 | unsigned char c2;
|
---|
168 | (void)base64toValue(base64String[indexOfFirstEncodedChar], &c1);
|
---|
169 | (void)base64toValue(base64String[indexOfFirstEncodedChar + 1], &c2);
|
---|
170 | decodedString[decodedIndex] = (c1 << 2) | (c2 >> 4);
|
---|
171 | }
|
---|
172 | else if (numberOfEncodedChars == 3)
|
---|
173 | {
|
---|
174 | unsigned char c1;
|
---|
175 | unsigned char c2;
|
---|
176 | unsigned char c3;
|
---|
177 | (void)base64toValue(base64String[indexOfFirstEncodedChar], &c1);
|
---|
178 | (void)base64toValue(base64String[indexOfFirstEncodedChar + 1], &c2);
|
---|
179 | (void)base64toValue(base64String[indexOfFirstEncodedChar + 2], &c3);
|
---|
180 | decodedString[decodedIndex] = (c1 << 2) | (c2 >> 4);
|
---|
181 | decodedIndex++;
|
---|
182 | decodedString[decodedIndex] = ((c2 & 0x0f) << 4) | (c3 >> 2);
|
---|
183 | }
|
---|
184 | }
|
---|
185 |
|
---|
186 | BUFFER_HANDLE Azure_Base64_Decode(const char* source)
|
---|
187 | {
|
---|
188 | BUFFER_HANDLE result;
|
---|
189 | /*Codes_SRS_BASE64_06_008: [If source is NULL then Azure_Base64_Decode shall return NULL.]*/
|
---|
190 | if (source == NULL)
|
---|
191 | {
|
---|
192 | LogError("invalid parameter const char* source=%p", source);
|
---|
193 | result = NULL;
|
---|
194 | }
|
---|
195 | else
|
---|
196 | {
|
---|
197 | if ((strlen(source) % 4) != 0)
|
---|
198 | {
|
---|
199 | /*Codes_SRS_BASE64_06_011: [If the source string has an invalid length for a base 64 encoded string then Azure_Base64_Decode shall return NULL.]*/
|
---|
200 | LogError("Invalid length Base64 string!");
|
---|
201 | result = NULL;
|
---|
202 | }
|
---|
203 | else
|
---|
204 | {
|
---|
205 | if ((result = BUFFER_new()) == NULL)
|
---|
206 | {
|
---|
207 | /*Codes_SRS_BASE64_06_010: [If there is any memory allocation failure during the decode then Azure_Base64_Decode shall return NULL.]*/
|
---|
208 | LogError("Could not create a buffer to decoding.");
|
---|
209 | }
|
---|
210 | else
|
---|
211 | {
|
---|
212 | size_t sizeOfOutputBuffer = Base64decode_len(source);
|
---|
213 | /*Codes_SRS_BASE64_06_009: [If the string pointed to by source is zero length then the handle returned shall refer to a zero length buffer.]*/
|
---|
214 | if (sizeOfOutputBuffer > 0)
|
---|
215 | {
|
---|
216 | if (BUFFER_pre_build(result, sizeOfOutputBuffer) != 0)
|
---|
217 | {
|
---|
218 | /*Codes_SRS_BASE64_06_010: [If there is any memory allocation failure during the decode then Azure_Base64_Decode shall return NULL.]*/
|
---|
219 | LogError("Could not prebuild a buffer for base 64 decoding.");
|
---|
220 | BUFFER_delete(result);
|
---|
221 | result = NULL;
|
---|
222 | }
|
---|
223 | else
|
---|
224 | {
|
---|
225 | Base64decode(BUFFER_u_char(result), source);
|
---|
226 | }
|
---|
227 | }
|
---|
228 | }
|
---|
229 | }
|
---|
230 | }
|
---|
231 | return result;
|
---|
232 | }
|
---|
233 |
|
---|
234 |
|
---|
235 | static STRING_HANDLE Base64_Encode_Internal(const unsigned char* source, size_t size)
|
---|
236 | {
|
---|
237 | STRING_HANDLE result;
|
---|
238 | size_t neededSize = 0;
|
---|
239 | char* encoded;
|
---|
240 | size_t currentPosition = 0;
|
---|
241 | neededSize += (size == 0) ? (0) : ((((size - 1) / 3) + 1) * 4);
|
---|
242 | neededSize += 1; /*+1 because \0 at the end of the string*/
|
---|
243 | /*Codes_SRS_BASE64_06_006: [If when allocating memory to produce the encoding a failure occurs then Azure_Base64_Encode shall return NULL.]*/
|
---|
244 | encoded = (char*)malloc(neededSize);
|
---|
245 | if (encoded == NULL)
|
---|
246 | {
|
---|
247 | result = NULL;
|
---|
248 | LogError("Azure_Base64_Encode:: Allocation failed.");
|
---|
249 | }
|
---|
250 | else
|
---|
251 | {
|
---|
252 | /*b0 b1(+1) b2(+2)
|
---|
253 | 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
|
---|
254 | |----c1---| |----c2---| |----c3---| |----c4---|
|
---|
255 | */
|
---|
256 |
|
---|
257 | size_t destinationPosition = 0;
|
---|
258 | while (size - currentPosition >= 3)
|
---|
259 | {
|
---|
260 | char c1 = base64char(source[currentPosition] >> 2);
|
---|
261 | char c2 = base64char(
|
---|
262 | ((source[currentPosition] & 3) << 4) |
|
---|
263 | (source[currentPosition + 1] >> 4)
|
---|
264 | );
|
---|
265 | char c3 = base64char(
|
---|
266 | ((source[currentPosition + 1] & 0x0F) << 2) |
|
---|
267 | ((source[currentPosition + 2] >> 6) & 3)
|
---|
268 | );
|
---|
269 | char c4 = base64char(
|
---|
270 | source[currentPosition + 2] & 0x3F
|
---|
271 | );
|
---|
272 | currentPosition += 3;
|
---|
273 | encoded[destinationPosition++] = c1;
|
---|
274 | encoded[destinationPosition++] = c2;
|
---|
275 | encoded[destinationPosition++] = c3;
|
---|
276 | encoded[destinationPosition++] = c4;
|
---|
277 |
|
---|
278 | }
|
---|
279 | if (size - currentPosition == 2)
|
---|
280 | {
|
---|
281 | char c1 = base64char(source[currentPosition] >> 2);
|
---|
282 | char c2 = base64char(
|
---|
283 | ((source[currentPosition] & 0x03) << 4) |
|
---|
284 | (source[currentPosition + 1] >> 4)
|
---|
285 | );
|
---|
286 | char c3 = base64b16(source[currentPosition + 1] & 0x0F);
|
---|
287 | encoded[destinationPosition++] = c1;
|
---|
288 | encoded[destinationPosition++] = c2;
|
---|
289 | encoded[destinationPosition++] = c3;
|
---|
290 | encoded[destinationPosition++] = '=';
|
---|
291 | }
|
---|
292 | else if (size - currentPosition == 1)
|
---|
293 | {
|
---|
294 | char c1 = base64char(source[currentPosition] >> 2);
|
---|
295 | char c2 = base64b8(source[currentPosition] & 0x03);
|
---|
296 | encoded[destinationPosition++] = c1;
|
---|
297 | encoded[destinationPosition++] = c2;
|
---|
298 | encoded[destinationPosition++] = '=';
|
---|
299 | encoded[destinationPosition++] = '=';
|
---|
300 | }
|
---|
301 |
|
---|
302 | /*null terminating the string*/
|
---|
303 | encoded[destinationPosition] = '\0';
|
---|
304 | /*Codes_SRS_BASE64_06_007: [Otherwise Azure_Base64_Encode shall return a pointer to STRING, that string contains the base 64 encoding of input.]*/
|
---|
305 | result = STRING_new_with_memory(encoded);
|
---|
306 | if (result == NULL)
|
---|
307 | {
|
---|
308 | free(encoded);
|
---|
309 | LogError("Azure_Base64_Encode:: Allocation failed for return value.");
|
---|
310 | }
|
---|
311 | }
|
---|
312 | return result;
|
---|
313 | }
|
---|
314 |
|
---|
315 | STRING_HANDLE Azure_Base64_Encode_Bytes(const unsigned char* source, size_t size)
|
---|
316 | {
|
---|
317 | STRING_HANDLE result;
|
---|
318 | /*Codes_SRS_BASE64_02_001: [If source is NULL then Azure_Base64_Encode_Bytes shall return NULL.] */
|
---|
319 | if (source == NULL)
|
---|
320 | {
|
---|
321 | result = NULL;
|
---|
322 | }
|
---|
323 | /*Codes_SRS_BASE64_02_002: [If source is not NULL and size is zero, then Azure_Base64_Encode_Bytes shall produce an empty STRING_HANDLE.] */
|
---|
324 | else if (size == 0)
|
---|
325 | {
|
---|
326 | result = STRING_new(); /*empty string*/
|
---|
327 | }
|
---|
328 | else
|
---|
329 | {
|
---|
330 | result = Base64_Encode_Internal(source, size);
|
---|
331 | }
|
---|
332 | return result;
|
---|
333 | }
|
---|
334 |
|
---|
335 | STRING_HANDLE Azure_Base64_Encode(BUFFER_HANDLE input)
|
---|
336 | {
|
---|
337 | STRING_HANDLE result;
|
---|
338 | /*the following will happen*/
|
---|
339 | /*1. the "data" of the binary shall be "eaten" 3 characters at a time and produce 4 base64 encoded characters for as long as there are more than 3 characters still to process*/
|
---|
340 | /*2. the remaining characters (1 or 2) shall be encoded.*/
|
---|
341 | /*there's a level of assumption that 'a' corresponds to 0b000000 and that '_' corresponds to 0b111111*/
|
---|
342 | /*the encoding will use the optional [=] or [==] at the end of the encoded string, so that other less standard aware libraries can do their work*/
|
---|
343 | /*these are the bits of the 3 normal bytes to be encoded*/
|
---|
344 |
|
---|
345 | /*Codes_SRS_BASE64_06_001: [If input is NULL then Azure_Base64_Encode shall return NULL.]*/
|
---|
346 | if (input == NULL)
|
---|
347 | {
|
---|
348 | result = NULL;
|
---|
349 | LogError("Azure_Base64_Encode:: NULL input");
|
---|
350 | }
|
---|
351 | else
|
---|
352 | {
|
---|
353 | size_t inputSize;
|
---|
354 | const unsigned char* inputBinary;
|
---|
355 | if ((BUFFER_content(input, &inputBinary) != 0) ||
|
---|
356 | (BUFFER_size(input, &inputSize) != 0))
|
---|
357 | {
|
---|
358 | result = NULL;
|
---|
359 | LogError("Azure_Base64_Encode:: BUFFER_routines failure.");
|
---|
360 | }
|
---|
361 | else
|
---|
362 | {
|
---|
363 | result = Base64_Encode_Internal(inputBinary, inputSize);
|
---|
364 | }
|
---|
365 | }
|
---|
366 | return result;
|
---|
367 | }
|
---|