base64.c
Go to the documentation of this file.
1 
9 #include "base64.h"
10 #include "mcl_core/mcl_assert.h"
11 #include "mcl_core/mcl_memory.h"
13 
14 // Define padding char.
15 #define PADDING_CHAR '='
16 
17 // Define size of a quantum (4 bytes data group).
18 #define QUANTUM_SIZE 4
19 #define INPUT_GROUP_SIZE 3
20 
21 // Only maximum of 2 padding chars are allowed.
22 #define MAX_PADDING_CHAR_COUNT 2
23 #define BASE64_BIT_COUNT_PER_CHARACTER 6
24 
25 // Mask for last 6 bit, 00111111
26 #define MASK_FOR_ENCODING 0x3FU
27 
28 // Mask for least significant byte.
29 #define MASK_FOR_LOW_BYTE 0xFFU
30 #define BYTE_BIT_COUNT 8
31 
32 // Base64 encoding table.
33 static const char base64_encode_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
34 
35 //J-
36 // Base64 decoding table.
38 {
39  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
40  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
41  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3E, 0xFF, 0xFF, 0xFF, 0x3F,
42  0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
43  0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
44  0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
45  0xFF, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
46  0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
47  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
48  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
49  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
50  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
51  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
52  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
53  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
54  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
55 };
56 //J+
57 
58 // Base64 url encoding table (see RFC 4648 document section 5).
59 static const char base64_url_encode_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
60 
61 //J-
62 // Base64 url decoding table.
64 {
65  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
66  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
67  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3E, 0xFF, 0xFF,
68  0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
69  0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
70  0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F,
71  0xFF, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
72  0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
73  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
74  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
75  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
76  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
77  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
78  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
79  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
80  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
81 };
82 //J+
83 
84 // This function is used to encode data by using table.
85 static mcl_error_t _encode_with_table(const char *table, const mcl_uint8_t *data, mcl_size_t data_size, char **encoded_data);
86 
87 // This function is used to decode data by using table.
88 static mcl_error_t _decode_with_table(const mcl_uint8_t *table, const char *encoded_data, mcl_uint8_t **decoded_data, mcl_size_t *decoded_data_size);
89 
90 // This function is used to decode quantum.
91 static mcl_error_t _decode_quantum(const mcl_uint8_t *table, const char *source, mcl_uint8_t *destination);
92 
93 mcl_error_t base64_decode(const char *encoded_data, mcl_uint8_t **decoded_data, mcl_size_t *decoded_data_size)
94 {
95  mcl_error_t code;
96 
97  MCL_DEBUG_ENTRY("const char *encoded_data = <%p>, mcl_uint8_t **decoded_data = <%p>, mcl_size_t *decoded_data_size = <%p>",
98  encoded_data, decoded_data, decoded_data_size);
99 
100  code = _decode_with_table(base64_decode_table, encoded_data, decoded_data, decoded_data_size);
101 
102  MCL_DEBUG_LEAVE("retVal = <%d>", code);
103  return code;
104 }
105 
106 mcl_error_t base64_url_decode(const char *encoded_data, mcl_uint8_t **decoded_data, mcl_size_t *decoded_data_size)
107 {
108  mcl_error_t code;
109 
110  MCL_DEBUG_ENTRY("const char *encoded_data = <%p>, mcl_uint8_t **decoded_data = <%p>, mcl_size_t *decoded_data_size = <%p>",
111  encoded_data, decoded_data, decoded_data_size);
112 
113  code = _decode_with_table(base64_url_decode_table, encoded_data, decoded_data, decoded_data_size);
114 
115  MCL_DEBUG_LEAVE("retVal = <%d>", code);
116  return code;
117 }
118 
119 mcl_error_t base64_encode(const mcl_uint8_t *data, mcl_size_t data_size, char **encoded_data)
120 {
121  mcl_error_t code;
122 
123  MCL_DEBUG_ENTRY("const mcl_uint8_t *data = <%p>, mcl_size_t data_size = <%u>, char **encoded_data = <%p>", data, data_size, encoded_data);
124 
125  code = _encode_with_table(base64_encode_table, data, data_size, encoded_data);
126 
127  MCL_DEBUG_LEAVE("retVal = <%d>", code);
128  return code;
129 }
130 
131 mcl_error_t base64_url_encode(const mcl_uint8_t *data, mcl_size_t data_size, char **encoded_data)
132 {
133  mcl_error_t code;
134 
135  MCL_DEBUG_ENTRY("const mcl_uint8_t *data = <%p>, mcl_size_t data_size = <%u>, char **encoded_data = <%p>", data, data_size, encoded_data);
136 
137  code = _encode_with_table(base64_url_encode_table, data, data_size, encoded_data);
138 
139  MCL_DEBUG_LEAVE("retVal = <%d>", code);
140  return code;
141 }
142 
143 static mcl_error_t _decode_with_table(const mcl_uint8_t *table, const char *encoded_data, mcl_uint8_t **decoded_data, mcl_size_t *decoded_data_size)
144 {
145  mcl_error_t code = MCL_OK;
146  mcl_size_t number_of_quantums;
147  mcl_size_t index = 0;
148  mcl_uint8_t padding_count = 0;
149  mcl_size_t encoded_data_length;
150  const char *encoded_position = encoded_data;
151 
152  MCL_DEBUG_ENTRY("const mcl_uint8_t *table = <%p>, const char *encoded_data = <%p>, mcl_uint8_t **decoded_data = <%p>, mcl_size_t *decoded_data_size = <%p>",
153  table, encoded_data, decoded_data, decoded_data_size);
154 
155  *decoded_data = MCL_NULL;
156  *decoded_data_size = 0;
157 
158  encoded_data_length = mcl_string_util_strlen(encoded_data);
159 
160  // Check the length of the input string is valid.
161  MCL_ASSERT_CODE_MESSAGE((encoded_data_length > 0) && (encoded_data_length % QUANTUM_SIZE == 0), MCL_BAD_CONTENT_ENCODING,
162  "Length of encoded data must be positive and multiples of 4.");
163 
164  // Look for padding characters.
165  for (index = (encoded_data_length - 1); (index > 0) && (MCL_OK == code); --index)
166  {
167  if (PADDING_CHAR == encoded_data[index])
168  {
169  ++padding_count;
170 
171  if (MAX_PADDING_CHAR_COUNT < padding_count)
172  {
174  }
175  }
176  else
177  {
178  break;
179  }
180  }
181 
182  // Verify that there is no padding character until the end.
183  if (MCL_OK == code)
184  {
185  for (index = 0; index < (encoded_data_length - (1 + padding_count)); ++index)
186  {
187  if (PADDING_CHAR == encoded_data[index])
188  {
190  break;
191  }
192  }
193  }
194 
195  if (MCL_OK == code)
196  {
197  // Calculate the number of quantums.
198  number_of_quantums = encoded_data_length / QUANTUM_SIZE;
199  MCL_VERBOSE("Number of quantums (4 Byte groups) = <%lu>", number_of_quantums);
200 
201  // Calculate the size of the decoded string.
202  *decoded_data_size = (number_of_quantums * INPUT_GROUP_SIZE) - padding_count;
203  MCL_VERBOSE("Length of resulting string will be = <%lu>", *decoded_data_size);
204 
205  // Allocate buffer.
206  (*decoded_data) = MCL_MALLOC((*decoded_data_size) + MCL_NULL_CHAR_SIZE);
207 
208  if (MCL_NULL == (*decoded_data))
209  {
210  code = MCL_OUT_OF_MEMORY;
211  }
212  }
213 
214  if (MCL_OK == code)
215  {
216  mcl_uint8_t *decode_position;
217 
218  decode_position = *decoded_data;
219 
220  // Decode the quantums.
221  for (index = 0; (index < number_of_quantums) && (MCL_OK == code); ++index)
222  {
223  code = _decode_quantum(table, encoded_position, decode_position);
224 
225  decode_position += INPUT_GROUP_SIZE;
226  encoded_position += QUANTUM_SIZE;
227  }
228 
229  (*decoded_data)[*decoded_data_size] = MCL_NULL_CHAR;
230  }
231 
232  if (MCL_OK != code)
233  {
234  MCL_FREE(*decoded_data);
235  *decoded_data_size = 0;
236  }
237 
238  MCL_DEBUG_LEAVE("retVal = <%d>", code);
239  return code;
240 }
241 
242 static mcl_error_t _decode_quantum(const mcl_uint8_t *table, const char *source, mcl_uint8_t *destination)
243 {
244  mcl_error_t code = MCL_OK;
245  mcl_int8_t index;
246  mcl_uint32_t data = 0;
247  mcl_uint8_t padding_count = 0;
248 
249  MCL_DEBUG_ENTRY("const mcl_uint8_t *table = <%p>, const char *source = <%s>, mcl_uint8_t *destination = <%p>", table, source, destination);
250 
251  // Compose 24 bit value using encoded characters.
252  for (index = 0; (index < QUANTUM_SIZE) && (MCL_OK == code); ++index)
253  {
254  char current_char = source[index];
255 
256  if (PADDING_CHAR == current_char)
257  {
258  data = (data << BASE64_BIT_COUNT_PER_CHARACTER);
259  ++padding_count;
260 
261  if (MAX_PADDING_CHAR_COUNT < padding_count)
262  {
264  }
265  }
266  else
267  {
268  mcl_uint8_t table_index = table[(mcl_uint8_t) current_char];
269  mcl_uint8_t invalid_index = 0xFF;
270 
271  if (invalid_index == table_index)
272  {
274  }
275  else
276  {
277  MCL_VERBOSE("Current char = <%c> in table found at index = <%u>", current_char, table_index);
278  data = (data << BASE64_BIT_COUNT_PER_CHARACTER) + table_index;
279  }
280  }
281  }
282 
283  // Decode 24 bit value.
284  if (MCL_OK == code)
285  {
286  for (index = (INPUT_GROUP_SIZE - 1); index >= 0; --index)
287  {
288  if (INPUT_GROUP_SIZE > (padding_count + index))
289  {
290  destination[index] = (mcl_uint8_t)data & MASK_FOR_LOW_BYTE;
291  }
292 
293  data = data >> BYTE_BIT_COUNT;
294  }
295  }
296 
297  MCL_DEBUG_LEAVE("retVal = <%d>", code);
298  return code;
299 }
300 
301 static mcl_error_t _encode_with_table(const char *table, const mcl_uint8_t *data, mcl_size_t data_size, char **encoded_data)
302 {
303  mcl_size_t iteration;
304  mcl_size_t iteration_count;
305  mcl_size_t buffer_size;
306  mcl_uint8_t remainder;
307 
308  MCL_DEBUG_ENTRY("const char *table = <%p>, const mcl_uint8_t *data = <%p>, mcl_size_t data_size = <%u>, char **encoded_data = <%p>",
309  table, data, data_size, encoded_data);
310 
311  iteration_count = data_size / INPUT_GROUP_SIZE;
312  remainder = data_size % INPUT_GROUP_SIZE;
313 
314  buffer_size = (iteration_count * QUANTUM_SIZE) + MCL_NULL_CHAR_SIZE;
315 
316  if (0 != remainder)
317  {
318  buffer_size += QUANTUM_SIZE;
319  }
320 
321  // Return pointer to new data.
322  *encoded_data = MCL_MALLOC(buffer_size);
323  MCL_ASSERT_CODE_MESSAGE(MCL_NULL != *encoded_data, MCL_OUT_OF_MEMORY, "Memory to store encoded data couldn't be allocated!");
324 
325  for (iteration = 0; iteration < iteration_count; ++iteration)
326  {
327  mcl_int8_t index;
328  mcl_uint32_t input = (mcl_uint32_t) data[iteration * INPUT_GROUP_SIZE] << 16;
329 
330  input |= (mcl_uint32_t) data[(iteration * INPUT_GROUP_SIZE) + 1] << BYTE_BIT_COUNT;
331  input |= (mcl_uint32_t) data[(iteration * INPUT_GROUP_SIZE) + 2];
332 
333  for (index = (QUANTUM_SIZE - 1); index >= 0; --index)
334  {
335  (*encoded_data)[(iteration * QUANTUM_SIZE) + index] = table[input & MASK_FOR_ENCODING];
336  input = (input >> BASE64_BIT_COUNT_PER_CHARACTER);
337  }
338  }
339 
340  if (0 != remainder)
341  {
342  mcl_int8_t index;
343  mcl_uint32_t input = (mcl_uint32_t) data[iteration_count * INPUT_GROUP_SIZE] << 16;
344 
345  if (1 < remainder)
346  {
347  input |= (mcl_uint32_t) data[(iteration * INPUT_GROUP_SIZE) + 1] << BYTE_BIT_COUNT;
348  }
349 
350  for (index = (QUANTUM_SIZE - 1); index >= 0; --index)
351  {
352  if (index > remainder)
353  {
354  (*encoded_data)[(iteration_count * QUANTUM_SIZE) + index] = PADDING_CHAR;
355  }
356  else
357  {
358  (*encoded_data)[(iteration_count * QUANTUM_SIZE) + index] = table[input & MASK_FOR_ENCODING];
359  }
360 
361  input = (input >> BASE64_BIT_COUNT_PER_CHARACTER);
362  }
363  }
364 
365  (*encoded_data)[buffer_size - 1] = MCL_NULL_CHAR;
366 
367  MCL_DEBUG_LEAVE("retVal = <%d>", MCL_OK);
368  return MCL_OK;
369 }
#define INPUT_GROUP_SIZE
Definition: base64.c:19
#define BYTE_BIT_COUNT
Definition: base64.c:30
size_t mcl_size_t
Assert module header file.
Success.
static mcl_error_t _decode_quantum(const mcl_uint8_t *table, const char *source, mcl_uint8_t *destination)
Definition: base64.c:242
#define QUANTUM_SIZE
Definition: base64.c:18
static const mcl_uint8_t base64_decode_table[]
Definition: base64.c:37
#define MASK_FOR_ENCODING
Definition: base64.c:26
mcl_int32_t mcl_error_t
static const char base64_url_encode_table[]
Definition: base64.c:59
#define MCL_DEBUG_ENTRY(...)
Definition: mcl_log_util.h:115
uint32_t mcl_uint32_t
#define MCL_VERBOSE(...)
Definition: mcl_log_util.h:102
If given content for Base64 encoding is bad.
MCL_CORE_EXPORT mcl_size_t mcl_string_util_strlen(const char *buffer)
Definition: string_util.c:23
static mcl_error_t _encode_with_table(const char *table, const mcl_uint8_t *data, mcl_size_t data_size, char **encoded_data)
Definition: base64.c:301
#define MCL_ASSERT_CODE_MESSAGE(condition, return_code,...)
Definition: mcl_assert.h:77
mcl_error_t base64_decode(const char *encoded_data, mcl_uint8_t **decoded_data, mcl_size_t *decoded_data_size)
Definition: base64.c:93
static const char base64_encode_table[]
Definition: base64.c:33
#define MCL_NULL
#define PADDING_CHAR
Definition: base64.c:15
#define MAX_PADDING_CHAR_COUNT
Definition: base64.c:22
mcl_error_t base64_encode(const mcl_uint8_t *data, mcl_size_t data_size, char **encoded_data)
Definition: base64.c:119
#define MCL_FREE(p)
Definition: mcl_memory.h:59
uint8_t mcl_uint8_t
#define MASK_FOR_LOW_BYTE
Definition: base64.c:29
mcl_error_t base64_url_encode(const mcl_uint8_t *data, mcl_size_t data_size, char **encoded_data)
Definition: base64.c:131
mcl_error_t base64_url_decode(const char *encoded_data, mcl_uint8_t **decoded_data, mcl_size_t *decoded_data_size)
Definition: base64.c:106
static mcl_error_t _decode_with_table(const mcl_uint8_t *table, const char *encoded_data, mcl_uint8_t **decoded_data, mcl_size_t *decoded_data_size)
Definition: base64.c:143
Base64 module header file.
#define BASE64_BIT_COUNT_PER_CHARACTER
Definition: base64.c:23
String utility module interface header file.
int8_t mcl_int8_t
Memory allocation fail.
#define MCL_NULL_CHAR_SIZE
#define MCL_MALLOC(bytes)
Definition: mcl_memory.h:54
#define MCL_DEBUG_LEAVE(...)
Definition: mcl_log_util.h:116
#define MCL_NULL_CHAR
static const mcl_uint8_t base64_url_decode_table[]
Definition: base64.c:63
Memory module interface header file.