source: azure_iot_hub_f767zi/trunk/azure_iot_sdk/c-utility/src/azure_base64.c@ 457

Last change on this file since 457 was 457, checked in by coas-nagasima, 4 years ago

ファイルを追加

  • Property svn:eol-style set to native
  • Property svn:mime-type set to text/x-csrc;charset=UTF-8
File size: 11.7 KB
Line 
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
14static 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
42static 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
53static char base64b8(unsigned char val)
54{
55 const uint32_t base64b8values = joinChars('A', 'Q', 'g', 'w');
56 return splitInt(base64b8values, val);
57}
58
59static 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
90static 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.*/
103static 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
127static 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
186BUFFER_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
235static 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
315STRING_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
335STRING_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}
Note: See TracBrowser for help on using the repository browser.