base64.c
Go to the documentation of this file.
1 /*!**********************************************************************
2  *
3  * @copyright Copyright (C) 2016 Siemens Aktiengesellschaft.\n
4  * All rights reserved.
5  *
6  *************************************************************************
7  *
8  * @file base64.c
9  * @date Aug 5, 2016
10  * @brief Base64 module implementation file.
11  *
12  ************************************************************************/
13 
14 #include "base64.h"
15 #include "memory.h"
16 #include "log_util.h"
17 #include "definitions.h"
18 
19 // define padding char
20 #define PADDING_CHAR '='
21 
22 // define size of a quantum (4 Bytes data group)
23 #define QUANTUM_SIZE 4
24 #define INPUT_GROUP_SIZE 3
25 
26 // only maximum of 2 padding chars are allowed
27 #define UPPER_COUNT_PADDING 3
28 
29 // Base64 encoding/decoding table
30 static const char base64_encode_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
31 //J-
33 {
34  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
35  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
36  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3E, 0xFF, 0xFF, 0xFF, 0x3F,
37  0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
38  0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
39  0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
40  0xFF, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
41  0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
42  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
43  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
44  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
45  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
46  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 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 };
51 //J+
52 
53 // The Base 64 encoding with an URL and filename safe alphabet, RFC 4648 section 5
54 static const char base64_url_encode_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
55 //J-
57 {
58  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
59  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
60  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3E, 0xFF, 0xFF,
61  0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
62  0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
63  0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F,
64  0xFF, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
65  0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 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, 0xFF, 0xFF, 0xFF,
68  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
69  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
70  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
71  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
72  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
73  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
74 };
75 //J+
76 
77 // Private functions
78 static E_MCL_ERROR_CODE _encode_with_table(const char *table, const mcl_uint8_t *data, mcl_size_t data_size, string_t **encoded_data);
79 static E_MCL_ERROR_CODE _decode_with_table(const mcl_uint8_t *table, const string_t *encoded_data, mcl_uint8_t **decoded_data, mcl_size_t *decoded_data_size);
80 static E_MCL_ERROR_CODE _decode_quantum(const mcl_uint8_t *table, const char *source, mcl_uint8_t *destination, mcl_size_t *padding);
81 
82 E_MCL_ERROR_CODE base64_decode(const string_t *encoded_data, mcl_uint8_t **decoded_data, mcl_size_t *decoded_data_size)
83 {
84  DEBUG_ENTRY("const string_t *encoded_data = <%p>, mcl_uint8_t **decoded_data = <%p>, mcl_size_t *decoded_data_size = <%p>", encoded_data, decoded_data, decoded_data_size)
85 
86  E_MCL_ERROR_CODE code = _decode_with_table(base64_decode_table, encoded_data, decoded_data, decoded_data_size);
87 
88  DEBUG_LEAVE("retVal = <%d>", code);
89  return code;
90 }
91 
92 E_MCL_ERROR_CODE base64_url_decode(const string_t *encoded_data, mcl_uint8_t **decoded_data, mcl_size_t *decoded_data_size)
93 {
94  DEBUG_ENTRY("const string_t *encoded_data = <%p>, mcl_uint8_t **decoded_data = <%p>, mcl_size_t *decoded_data_size = <%p>", encoded_data, decoded_data, decoded_data_size)
95 
96  E_MCL_ERROR_CODE code = _decode_with_table(base64_url_decode_table, encoded_data, decoded_data, decoded_data_size);
97 
98  DEBUG_LEAVE("retVal = <%d>", code);
99  return code;
100 }
101 
102 E_MCL_ERROR_CODE base64_encode(const mcl_uint8_t *data, mcl_size_t data_size, string_t **encoded_data)
103 {
104  DEBUG_ENTRY("const mcl_uint8_t *data = <%p>, mcl_size_t data_size = <%u>, string_t **encoded_data = <%p>", data, data_size, encoded_data)
105 
106  E_MCL_ERROR_CODE code = _encode_with_table(base64_encode_table, data, data_size, encoded_data);
107 
108  DEBUG_LEAVE("retVal = <%d>", code);
109  return code;
110 }
111 
112 E_MCL_ERROR_CODE base64_url_encode(const mcl_uint8_t *data, mcl_size_t data_size, string_t **encoded_data)
113 {
114  DEBUG_ENTRY("const mcl_uint8_t *data = <%p>, mcl_size_t data_size = <%u>, string_t **encoded_data = <%p>", data, data_size, encoded_data)
115 
116  E_MCL_ERROR_CODE code = _encode_with_table(base64_url_encode_table, data, data_size, encoded_data);
117 
118  DEBUG_LEAVE("retVal = <%d>", code);
119  return code;
120 }
121 
122 static E_MCL_ERROR_CODE _decode_with_table(const mcl_uint8_t *table, const string_t *encoded_data, mcl_uint8_t **decoded_data, mcl_size_t *decoded_data_size)
123 {
124  DEBUG_ENTRY("const mcl_uint8_t *table = <%p>, const string_t *encoded_data = <%p>, mcl_uint8_t **decoded_data = <%p>, mcl_size_t *decoded_data_size = <%p>", table,
125  encoded_data, decoded_data, decoded_data_size)
126 
127  char *encoded_buffer;
128  mcl_size_t number_of_quantums;
129  mcl_size_t raw_length;
130  mcl_uint8_t *decode_buffer;
131  mcl_uint8_t *position;
132  mcl_size_t index = 0;
133  mcl_size_t padding = 0;
134 
135  // Check the length of the input string is valid
136  ASSERT_CODE_MESSAGE((encoded_data->length > 0) && (encoded_data->length % QUANTUM_SIZE == 0), MCL_BAD_CONTENT_ENCODING,
137  "Length of encoded data must be positive and multiples of 4");
138 
139  *decoded_data = MCL_NULL;
140  *decoded_data_size = 0;
141 
142  // Find the position of any = padding characters
143  encoded_buffer = encoded_data->buffer;
144  while ((PADDING_CHAR != encoded_buffer[index]) && (MCL_NULL_CHAR != encoded_buffer[index]))
145  {
146  index++;
147  }
148  MCL_DEBUG("Found first padding symbol '=' at index = <%u>", index);
149 
150  // A maximum of two = padding characters is allowed
151  if (PADDING_CHAR == encoded_buffer[index])
152  {
153  padding++;
154  if (PADDING_CHAR == encoded_buffer[index + 1])
155  {
156  padding++;
157  }
158  }
159  MCL_DEBUG("Number of padding chars = <%u>", padding);
160 
161  // Check the = padding characters weren't part way through the input
162  ASSERT_CODE_MESSAGE(index + padding == encoded_data->length, MCL_BAD_CONTENT_ENCODING, "Padding must be located at the end and only maximum of 2 padding chars allowed");
163 
164  // Calculate the number of quantums
165  number_of_quantums = encoded_data->length / QUANTUM_SIZE;
166  MCL_DEBUG("Number of quantums (4 Byte groups) = <%u>", number_of_quantums);
167 
168  // Calculate the size of the decoded string
169  raw_length = (number_of_quantums * INPUT_GROUP_SIZE) - padding;
170  MCL_DEBUG("Length of resulting string will be = <%u>", raw_length);
171 
172  // Allocate our buffer including room for a zero terminator
173  decode_buffer = MCL_MALLOC(raw_length + 1);
174  ASSERT_CODE_MESSAGE(MCL_NULL != decode_buffer, MCL_OUT_OF_MEMORY, "Memory for storing decode result couldn't be allocated!");
175 
176  position = decode_buffer;
177 
178  // Decode the quantums
179  for (index = 0; index < number_of_quantums; index++)
180  {
181  MCL_DEBUG("Decoding quantum = <%u> at input data = <%s>", index, encoded_buffer);
182  ASSERT_STATEMENT_CODE_MESSAGE(MCL_OK == _decode_quantum(table, encoded_buffer, position, &padding), MCL_FREE(decode_buffer), MCL_BAD_CONTENT_ENCODING,
183  "Decode of quantum failed!");
184 
185  position += padding;
186  encoded_buffer += QUANTUM_SIZE;
187  }
188 
189  // Zero terminate
190  *position = MCL_NULL_CHAR;
191 
192  // Return the decoded data
193  *decoded_data = decode_buffer;
194  *decoded_data_size = raw_length;
195 
196  MCL_DEBUG("decode_buffer = <%p>", decode_buffer);
197  MCL_DEBUG("decoded_data_size = <%u>", *decoded_data_size);
198 
199  DEBUG_LEAVE("retVal = <%d>", MCL_OK);
200  return MCL_OK;
201 }
202 
203 static E_MCL_ERROR_CODE _decode_quantum(const mcl_uint8_t *table, const char *source, mcl_uint8_t *destination, mcl_size_t *padding)
204 {
205  DEBUG_ENTRY("const mcl_uint8_t *table = <%p>, const char *source = <%s>, mcl_uint8_t *destination = <%p>, mcl_size_t *padding = <%p>", table, source, destination,
206  padding)
207 
208  mcl_size_t index;
209  const char *current_char = source;
210  mcl_uint32_t data = 0;
211  *padding = 0;
212  for (index = 0; index < QUANTUM_SIZE; index++, current_char++)
213  {
214  if (*current_char == PADDING_CHAR)
215  {
216  data = (data << 6);
217  (*padding)++;
218  MCL_DEBUG("Padding char found. Current number of paddings = <%u>", *padding);
219  ASSERT_CODE_MESSAGE(*padding < UPPER_COUNT_PADDING, MCL_BAD_CONTENT_ENCODING, "Padding exceeds allowed maximum number!");
220  }
221  else
222  {
223  mcl_uint8_t table_index;
224  MCL_DEBUG("Look up current char = <%c> in table.", *current_char);
225 
226  table_index = table[(mcl_uint8_t)*current_char];
227  ASSERT_CODE_MESSAGE(0xFF != table_index, MCL_BAD_CONTENT_ENCODING, "Current char in table not found!");
228 
229  MCL_DEBUG("Current char = <%c> in table found at index = <%u>", *current_char, table_index);
230  data = (data << 6) + table_index;
231  }
232  }
233 
234  if (*padding < 1)
235  {
236  destination[2] = (char)(data & 0xFFUL);
237  }
238 
239  data >>= 8;
240  if (*padding < 2)
241  {
242  destination[1] = (char)(data & 0xFFUL);
243  }
244 
245  data >>= 8;
246  destination[0] = (char)(data & 0xFFUL);
247  *padding = UPPER_COUNT_PADDING - (*padding);
248 
249  DEBUG_LEAVE("retVal = <%d>", MCL_OK);
250  return MCL_OK;
251 }
252 
253 static E_MCL_ERROR_CODE _encode_with_table(const char *table, const mcl_uint8_t *data, mcl_size_t data_size, string_t **encoded_data)
254 {
255  DEBUG_ENTRY("const char *table = <%s>, const mcl_uint8_t *data = <%p>, mcl_size_t data_size = <%u>, string_t **encoded_data = <%p>", table, data, data_size, encoded_data)
256 
257  char *output;
258  char *output_start;
259  mcl_uint8_t input_buffer[INPUT_GROUP_SIZE];
260  mcl_uint8_t output_buffer[QUANTUM_SIZE];
261 
262  // return pointer to new data, allocated memory
263  output = MCL_MALLOC(data_size * QUANTUM_SIZE / INPUT_GROUP_SIZE + QUANTUM_SIZE);
264  ASSERT_CODE_MESSAGE(MCL_NULL != output, MCL_OUT_OF_MEMORY, "Memory to store encoded data couldn't be allocated!");
265 
266  MCL_DEBUG("Start to encode.");
267 
268  output_start = output;
269 
270  while (data_size > 0)
271  {
272  // counts input data if available, o.w. used to add padding chars accordingly
273  mcl_size_t input_parts = 0;
274  mcl_size_t index;
275 
276  // Take next 3 bytes from input data if available, o.w. set to 0.
277  for (index = 0; index < INPUT_GROUP_SIZE; index++)
278  {
279  if (data_size > 0)
280  {
281  input_parts++;
282  input_buffer[index] = *data;
283  data++;
284  data_size--;
285  }
286  else
287  {
288  input_buffer[index] = 0;
289  }
290  }
291  MCL_DEBUG("data_size = <%u> left to encode.", data_size);
292 
293  // now we have 3 bytes from input, calculate output quantum of 4 bytes
294  output_buffer[0] = (mcl_uint8_t)((input_buffer[0] & 0xFC) >> 2);
295  output_buffer[1] = (mcl_uint8_t)(((input_buffer[0] & 0x03) << 4) | ((input_buffer[1] & 0xF0) >> 4));
296  output_buffer[2] = (mcl_uint8_t)(((input_buffer[1] & 0x0F) << 2) | ((input_buffer[2] & 0xC0) >> 6));
297  output_buffer[3] = (mcl_uint8_t)(input_buffer[2] & 0x3F);
298 
299  // now we have output quantum, we can construct string with given transformation table (add also padding if required)
300  output[0] = table[output_buffer[0]];
301  output[1] = table[output_buffer[1]];
302  switch (input_parts)
303  {
304  case 1 : // only one byte read
305  output[2] = PADDING_CHAR;
306  output[3] = PADDING_CHAR;
307 
308  break;
309  case 2 : // two bytes read
310  output[2] = table[output_buffer[2]];
311  output[3] = PADDING_CHAR;
312 
313  break;
314  default :
315  output[2] = table[output_buffer[2]];
316  output[3] = table[output_buffer[3]];
317 
318  break;
319  }
320 
321  // set output to next 4 byte for next iteration
322  output += 4;
323  MCL_DEBUG("4 bytes written to output = <%u>", output - output_start);
324  }
325 
326  // terminate the output
327  *output = MCL_NULL_CHAR;
328 
329  // return the length of the new data
330  string_initialize_dynamic(output_start, 0, encoded_data);
331 
332  MCL_DEBUG("End of encode reached. encoded_data = <%s>, length encoded_data = <%u>.", (*encoded_data)->buffer, (*encoded_data)->length);
333  DEBUG_LEAVE("retVal = <%d>", MCL_OK);
334  return MCL_OK;
335 }
#define INPUT_GROUP_SIZE
Definition: base64.c:24
Memory module header file.
#define DEBUG_LEAVE(...)
Definition: log_util.h:81
E_MCL_ERROR_CODE base64_url_encode(const mcl_uint8_t *data, mcl_size_t data_size, string_t **encoded_data)
Definition: base64.c:112
#define DEBUG_ENTRY(...)
Definition: log_util.h:80
#define QUANTUM_SIZE
Definition: base64.c:23
static const mcl_uint8_t base64_decode_table[]
Definition: base64.c:32
char * buffer
Buffer of string handle.
Definition: string_type.h:45
static const char base64_url_encode_table[]
Definition: base64.c:54
static E_MCL_ERROR_CODE _encode_with_table(const char *table, const mcl_uint8_t *data, mcl_size_t data_size, string_t **encoded_data)
Definition: base64.c:253
#define UPPER_COUNT_PADDING
Definition: base64.c:27
#define MCL_DEBUG(...)
Definition: log_util.h:70
#define MCL_MALLOC(bytes)
Definition: memory.h:120
Log utility module header file.
E_MCL_ERROR_CODE
MCL Error code definitions. Every function returning an error code uses this enum values...
Definition: mcl_common.h:137
#define MCL_FREE(p)
Definition: memory.h:125
uint32_t mcl_uint32_t
Definition: mcl_common.h:45
static const char base64_encode_table[]
Definition: base64.c:30
#define PADDING_CHAR
Definition: base64.c:20
#define ASSERT_STATEMENT_CODE_MESSAGE(condition, statement, return_code,...)
Definition: definitions.h:121
If given content for Base64 encoding is bad.
Definition: mcl_common.h:207
uint8_t mcl_uint8_t
Definition: mcl_common.h:43
static E_MCL_ERROR_CODE _decode_with_table(const mcl_uint8_t *table, const string_t *encoded_data, mcl_uint8_t **decoded_data, mcl_size_t *decoded_data_size)
Definition: base64.c:122
#define ASSERT_CODE_MESSAGE(condition, return_code,...)
Definition: definitions.h:105
E_MCL_ERROR_CODE base64_url_decode(const string_t *encoded_data, mcl_uint8_t **decoded_data, mcl_size_t *decoded_data_size)
Definition: base64.c:92
Definitions module header file.
E_MCL_ERROR_CODE string_initialize_dynamic(const char *value, mcl_size_t value_length, string_t **string)
Initializes a dynamic string_t object with the given value and length.
Definition: string_type.c:68
mcl_size_t length
Length of buffer.
Definition: string_type.h:46
E_MCL_ERROR_CODE base64_encode(const mcl_uint8_t *data, mcl_size_t data_size, string_t **encoded_data)
Definition: base64.c:102
Base64 module header file.
size_t mcl_size_t
Definition: mcl_common.h:38
static E_MCL_ERROR_CODE _decode_quantum(const mcl_uint8_t *table, const char *source, mcl_uint8_t *destination, mcl_size_t *padding)
Definition: base64.c:203
E_MCL_ERROR_CODE base64_decode(const string_t *encoded_data, mcl_uint8_t **decoded_data, mcl_size_t *decoded_data_size)
Definition: base64.c:82
Success.
Definition: mcl_common.h:140
Memory allocation fail.
Definition: mcl_common.h:143
#define MCL_NULL
Definition: definitions.h:24
#define MCL_NULL_CHAR
Definition: definitions.h:26
static const mcl_uint8_t base64_url_decode_table[]
Definition: base64.c:56