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