suricata
util-base64.c
Go to the documentation of this file.
1 /* Copyright (C) 2007-2012 Open Information Security Foundation
2  *
3  * You can copy, redistribute or modify this Program under the terms of
4  * the GNU General Public License version 2 as published by the Free
5  * Software Foundation.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * version 2 along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15  * 02110-1301, USA.
16  */
17 
18 /**
19  * \file
20  *
21  * \author David Abarbanel <david.abarbanel@baesystems.com>
22  *
23  */
24 
25 #include "util-base64.h"
26 #include "util-debug.h"
27 #include "util-unittest.h"
28 /* Constants */
29 #define BASE64_TABLE_MAX 122
30 
31 /* Base64 character to index conversion table */
32 /* Characters are mapped as "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" */
33 static const int b64table[] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
34  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
35  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
36  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
37  -1, -1, -1, 62, -1, -1, -1, 63, 52, 53,
38  54, 55, 56, 57, 58, 59, 60, 61, -1, -1,
39  -1, -1, -1, -1, -1, 0, 1, 2, 3, 4,
40  5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
41  15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
42  25, -1, -1, -1, -1, -1, -1, 26, 27, 28,
43  29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
44  39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
45  49, 50, 51 };
46 
47 /**
48  * \brief Gets a base64-decoded value from an encoded character
49  *
50  * \param c The encoded character
51  *
52  * \return The decoded value (0 or above), or -1 if the parameter is invalid
53  */
54 static inline int GetBase64Value(uint8_t c)
55 {
56  int val = -1;
57 
58  /* Pull from conversion table */
59  if (c <= BASE64_TABLE_MAX) {
60  val = b64table[(int) c];
61  }
62 
63  return val;
64 }
65 
66 /**
67  * \brief Checks if the given char in a byte array is Base64 alphabet
68  *
69  * \param Char that needs to be checked
70  *
71  * \return True if the char was Base64 alphabet, False otherwise
72  */
73 bool IsBase64Alphabet(uint8_t encoded_byte)
74 {
75  if (GetBase64Value(encoded_byte) < 0 && encoded_byte != '=') {
76  return false;
77  }
78  return true;
79 }
80 
81 /**
82  * \brief Decodes a 4-byte base64-encoded block into a 3-byte ascii-encoded block
83  *
84  * \param ascii the 3-byte ascii output block
85  * \param b64 the 4-byte base64 input block
86  *
87  * \return none
88  */
89 static inline void DecodeBase64Block(uint8_t ascii[ASCII_BLOCK], uint8_t b64[B64_BLOCK])
90 {
91  ascii[0] = (uint8_t) (b64[0] << 2) | (b64[1] >> 4);
92  ascii[1] = (uint8_t) (b64[1] << 4) | (b64[2] >> 2);
93  ascii[2] = (uint8_t) (b64[2] << 6) | (b64[3]);
94 }
95 
96 /**
97  * \brief Decodes a base64-encoded string buffer into an ascii-encoded byte buffer
98  *
99  * \param dest The destination byte buffer
100  * \param dest_size The destination byte buffer size
101  * \param src The source string
102  * \param len The length of the source string
103  * \param consumed_bytes The bytes that were actually processed/consumed
104  * \param decoded_bytes The bytes that were decoded
105  * \param mode The mode in which decoding should happen
106  *
107  * \return Error code indicating success or failures with parsing
108  */
109 Base64Ecode DecodeBase64(uint8_t *dest, uint32_t dest_size, const uint8_t *src, uint32_t len,
110  uint32_t *consumed_bytes, uint32_t *decoded_bytes, Base64Mode mode)
111 {
112  int val;
113  uint32_t padding = 0, bbidx = 0, sp = 0, leading_sp = 0;
114  uint8_t *dptr = dest;
115  uint8_t b64[B64_BLOCK] = { 0,0,0,0 };
116  bool valid = true;
118  *decoded_bytes = 0;
119 
120  /* Traverse through each alpha-numeric letter in the source array */
121  for (uint32_t i = 0; i < len; i++) {
122  /* Get decimal representation */
123  val = GetBase64Value(src[i]);
124  if (val < 0) {
125  if (mode == BASE64_MODE_RFC2045 && src[i] != '=') {
126  if (bbidx == 0) {
127  /* Special case where last block of data has a leading space or invalid char */
128  leading_sp++;
129  }
130  sp++;
131  continue;
132  }
133  /* Invalid character found, so decoding fails */
134  if (src[i] != '=') {
135  valid = false;
136  ecode = BASE64_ECODE_ERR;
137  if (mode == BASE64_MODE_STRICT) {
138  *decoded_bytes = 0;
139  }
140  break;
141  }
142  padding++;
143  }
144 
145  /* For each alpha-numeric letter in the source array, find the numeric
146  * value */
147  b64[bbidx++] = (val > 0 ? (uint8_t)val : 0);
148 
149  /* Decode every 4 base64 bytes into 3 ascii bytes */
150  if (bbidx == B64_BLOCK) {
151 
152  /* For every 4 bytes, add 3 bytes but deduct the '=' padded blocks */
153  uint32_t numDecoded_blk = ASCII_BLOCK - (padding < B64_BLOCK ? padding : ASCII_BLOCK);
154  if (dest_size < *decoded_bytes + numDecoded_blk) {
155  SCLogDebug("Destination buffer full");
156  ecode = BASE64_ECODE_BUF;
157  break;
158  }
159 
160  /* Decode base-64 block into ascii block and move pointer */
161  DecodeBase64Block(dptr, b64);
162  dptr += numDecoded_blk;
163  *decoded_bytes += numDecoded_blk;
164  /* Reset base-64 block and index */
165  bbidx = 0;
166  padding = 0;
167  *consumed_bytes += B64_BLOCK + sp;
168  sp = 0;
169  leading_sp = 0;
170  memset(&b64, 0, sizeof(b64));
171  }
172  }
173 
174  if (bbidx > 0 && bbidx < 4 && ((!valid && mode == BASE64_MODE_RFC4648))) {
175  /* Decoded bytes for 1 or 2 base64 encoded bytes is 1 */
176  padding = bbidx > 1 ? B64_BLOCK - bbidx : 2;
177  uint32_t numDecoded_blk = ASCII_BLOCK - (padding < B64_BLOCK ? padding : ASCII_BLOCK);
178  if (dest_size < *decoded_bytes + numDecoded_blk) {
179  SCLogDebug("Destination buffer full");
180  ecode = BASE64_ECODE_BUF;
181  return ecode;
182  }
183  /* if the destination size is not at least 3 Bytes long, it'll give a dynamic
184  * buffer overflow while decoding, so, return and let the caller take care of the
185  * remaining bytes to be decoded which should always be < 4 at this stage */
186  if (dest_size - *decoded_bytes < 3)
187  return BASE64_ECODE_BUF;
188  *decoded_bytes += numDecoded_blk;
189  DecodeBase64Block(dptr, b64);
190  *consumed_bytes += bbidx;
191  }
192 
193  /* Finish remaining b64 bytes by padding */
194  if (valid && bbidx > 0 && (mode != BASE64_MODE_RFC2045)) {
195  /* Decode remaining */
196  *decoded_bytes += ASCII_BLOCK - (B64_BLOCK - bbidx);
197  DecodeBase64Block(dptr, b64);
198  }
199 
200  if (*decoded_bytes == 0) {
201  SCLogDebug("base64 decoding failed");
202  }
203 
204  *consumed_bytes += leading_sp;
205  return ecode;
206 }
207 
208 #ifdef UNITTESTS
209 
210 #define TEST_RFC2045(src, fin_str, dest_size, exp_decoded, exp_consumed, ecode) \
211  { \
212  uint32_t consumed_bytes = 0, num_decoded = 0; \
213  uint8_t dst[dest_size]; \
214  Base64Ecode code = DecodeBase64(dst, dest_size, (const uint8_t *)src, strlen(src), \
215  &consumed_bytes, &num_decoded, BASE64_MODE_RFC2045); \
216  FAIL_IF(code != ecode); \
217  FAIL_IF(memcmp(dst, fin_str, strlen(fin_str)) != 0); \
218  FAIL_IF(num_decoded != exp_decoded); \
219  FAIL_IF(consumed_bytes != exp_consumed); \
220  }
221 
222 #define TEST_RFC4648(src, fin_str, dest_size, exp_decoded, exp_consumed, ecode) \
223  { \
224  uint32_t consumed_bytes = 0, num_decoded = 0; \
225  uint8_t dst[dest_size]; \
226  Base64Ecode code = DecodeBase64(dst, dest_size, (const uint8_t *)src, strlen(src), \
227  &consumed_bytes, &num_decoded, BASE64_MODE_RFC4648); \
228  FAIL_IF(code != ecode); \
229  FAIL_IF(memcmp(dst, fin_str, strlen(fin_str)) != 0); \
230  FAIL_IF(num_decoded != exp_decoded); \
231  FAIL_IF(consumed_bytes != exp_consumed); \
232  }
233 
234 static int B64DecodeCompleteString(void)
235 {
236  /*
237  * SGVsbG8gV29ybGR6 : Hello Worldz
238  * */
239  const char *src = "SGVsbG8gV29ybGR6";
240  const char *fin_str = "Hello Worldz";
241  TEST_RFC2045(src, fin_str, strlen(fin_str), strlen(fin_str), strlen(src), BASE64_ECODE_OK);
242  PASS;
243 }
244 
245 static int B64DecodeInCompleteString(void)
246 {
247  /*
248  * SGVsbG8gV29ybGR6 : Hello Worldz
249  * */
250  const char *src = "SGVsbG8gV29ybGR";
251  const char *fin_str = "Hello Wor";
252  TEST_RFC2045(src, fin_str, strlen(fin_str), strlen(fin_str), strlen(src) - 3, BASE64_ECODE_OK);
253  PASS;
254 }
255 
256 static int B64DecodeCompleteStringWSp(void)
257 {
258  /*
259  * SGVsbG8gV29ybGQ= : Hello World
260  * */
261 
262  const char *src = "SGVs bG8 gV29y bGQ=";
263  const char *fin_str = "Hello World";
264  TEST_RFC2045(src, fin_str, strlen(fin_str) + 3, strlen(fin_str), strlen(src), BASE64_ECODE_OK);
265  PASS;
266 }
267 
268 static int B64DecodeInCompleteStringWSp(void)
269 {
270  /*
271  * SGVsbG8gV29ybGQ= : Hello World
272  * Special handling for this case (sp in remainder) done in ProcessBase64Remainder
273  * */
274 
275  const char *src = "SGVs bG8 gV29y bGQ";
276  const char *fin_str = "Hello Wor";
277  TEST_RFC2045(src, fin_str, strlen(fin_str) + 1 /* 12 B in dest_size */, strlen(fin_str),
278  strlen(src) - 3, BASE64_ECODE_OK);
279  PASS;
280 }
281 
282 static int B64DecodeStringBiggerThanBuffer(void)
283 {
284  /*
285  * SGVsbG8gV29ybGQ= : Hello World
286  * */
287 
288  const char *src = "SGVs bG8 gV29y bGQ=";
289  const char *fin_str = "Hello Wor";
290  TEST_RFC2045(
291  src, fin_str, strlen(fin_str) + 1, strlen(fin_str), strlen(src) - 4, BASE64_ECODE_BUF);
292  PASS;
293 }
294 
295 static int B64DecodeStringEndingSpaces(void)
296 {
297  const char *src = "0YPhA d H";
298  uint32_t consumed_bytes = 0, num_decoded = 0;
299  uint8_t dst[10];
300  Base64Ecode code = DecodeBase64(dst, sizeof(dst), (const uint8_t *)src, strlen(src),
301  &consumed_bytes, &num_decoded, BASE64_MODE_RFC2045);
303  FAIL_IF(num_decoded != 3);
304  FAIL_IF(consumed_bytes != 4);
305  PASS;
306 }
307 
308 static int B64TestVectorsRFC2045(void)
309 {
310  const char *src1 = "";
311  const char *fin_str1 = "";
312 
313  const char *src2 = "Zg==";
314  const char *fin_str2 = "f";
315 
316  const char *src3 = "Zm8=";
317  const char *fin_str3 = "fo";
318 
319  const char *src4 = "Zm9v";
320  const char *fin_str4 = "foo";
321 
322  const char *src5 = "Zm9vYg==";
323  const char *fin_str5 = "foob";
324 
325  const char *src6 = "Zm9vYmE=";
326  const char *fin_str6 = "fooba";
327 
328  const char *src7 = "Zm9vYmFy";
329  const char *fin_str7 = "foobar";
330 
331  const char *src8 = "Zm 9v Ym Fy";
332  const char *fin_str8 = "foobar";
333 
334  const char *src9 = "Zm$9vYm.Fy";
335  const char *fin_str9 = "foobar";
336 
337  const char *src10 = "Y21Wd2IzSjBaVzFoYVd4bWNtRjFaRUJoZEc4dVoyOTJMbUYxOmpqcHh4b3Rhb2w%5";
338  const char *fin_str10 = "cmVwb3J0ZW1haWxmcmF1ZEBhdG8uZ292LmF1:jjpxxotaol9";
339 
340  TEST_RFC2045(src1, fin_str1, ASCII_BLOCK * 2, strlen(fin_str1), strlen(src1), BASE64_ECODE_OK);
341  TEST_RFC2045(src2, fin_str2, ASCII_BLOCK * 2, strlen(fin_str2), strlen(src2), BASE64_ECODE_OK);
342  TEST_RFC2045(src3, fin_str3, ASCII_BLOCK * 2, strlen(fin_str3), strlen(src3), BASE64_ECODE_OK);
343  TEST_RFC2045(src4, fin_str4, ASCII_BLOCK * 2, strlen(fin_str4), strlen(src4), BASE64_ECODE_OK);
344  TEST_RFC2045(src5, fin_str5, ASCII_BLOCK * 2, strlen(fin_str5), strlen(src5), BASE64_ECODE_OK);
345  TEST_RFC2045(src6, fin_str6, ASCII_BLOCK * 2, strlen(fin_str6), strlen(src6), BASE64_ECODE_OK);
346  TEST_RFC2045(src7, fin_str7, ASCII_BLOCK * 2, strlen(fin_str7), strlen(src7), BASE64_ECODE_OK);
347  TEST_RFC2045(src8, fin_str8, ASCII_BLOCK * 2, strlen(fin_str8), strlen(src8), BASE64_ECODE_OK);
348  TEST_RFC2045(src9, fin_str9, ASCII_BLOCK * 2, strlen(fin_str9), strlen(src9), BASE64_ECODE_OK);
349  TEST_RFC2045(src10, fin_str10, strlen(fin_str10) + 2, strlen(fin_str10), strlen(src10),
351  PASS;
352 }
353 
354 static int B64TestVectorsRFC4648(void)
355 {
356  const char *src1 = "";
357  const char *fin_str1 = "";
358 
359  const char *src2 = "Zg==";
360  const char *fin_str2 = "f";
361 
362  const char *src3 = "Zm8=";
363  const char *fin_str3 = "fo";
364 
365  const char *src4 = "Zm9v";
366  const char *fin_str4 = "foo";
367 
368  const char *src5 = "Zm9vYg==";
369  const char *fin_str5 = "foob";
370 
371  const char *src6 = "Zm9vYmE=";
372  const char *fin_str6 = "fooba";
373 
374  const char *src7 = "Zm9vYmFy";
375  const char *fin_str7 = "foobar";
376 
377  const char *src8 = "Zm 9v Ym Fy";
378  const char *fin_str8 = "f";
379 
380  const char *src9 = "Zm$9vYm.Fy";
381  const char *fin_str9 = "f";
382 
383  const char *src10 = "Y21Wd2IzSjBaVzFoYVd4bWNtRjFaRUJoZEc4dVoyOTJMbUYxOmpqcHh4b3Rhb2w%3D";
384  const char *fin_str10 = "cmVwb3J0ZW1haWxmcmF1ZEBhdG8uZ292LmF1:jjpxxotaol";
385 
386  TEST_RFC4648(src1, fin_str1, ASCII_BLOCK * 2, strlen(fin_str1), strlen(src1), BASE64_ECODE_OK);
387  TEST_RFC4648(src2, fin_str2, ASCII_BLOCK * 2, strlen(fin_str2), strlen(src2), BASE64_ECODE_OK);
388  TEST_RFC4648(src3, fin_str3, ASCII_BLOCK * 2, strlen(fin_str3), strlen(src3), BASE64_ECODE_OK);
389  TEST_RFC4648(src4, fin_str4, ASCII_BLOCK * 2, strlen(fin_str4), strlen(src4), BASE64_ECODE_OK);
390  TEST_RFC4648(src5, fin_str5, ASCII_BLOCK * 2, strlen(fin_str5), strlen(src5), BASE64_ECODE_OK);
391  TEST_RFC4648(src6, fin_str6, ASCII_BLOCK * 2, strlen(fin_str6), strlen(src6), BASE64_ECODE_OK);
392  TEST_RFC4648(src7, fin_str7, ASCII_BLOCK * 2, strlen(fin_str7), strlen(src7), BASE64_ECODE_OK);
393  TEST_RFC4648(src8, fin_str8, ASCII_BLOCK * 2, 1 /* f */, 2 /* Zm */, BASE64_ECODE_ERR);
394  TEST_RFC4648(src9, fin_str9, ASCII_BLOCK * 2, 1 /* f */, 2 /* Zm */, BASE64_ECODE_ERR);
395  TEST_RFC4648(src10, fin_str10, strlen(fin_str10) + 1, strlen(fin_str10), strlen(src10) - 3,
397  PASS;
398 }
399 
401 {
402  UtRegisterTest("B64DecodeCompleteStringWSp", B64DecodeCompleteStringWSp);
403  UtRegisterTest("B64DecodeInCompleteStringWSp", B64DecodeInCompleteStringWSp);
404  UtRegisterTest("B64DecodeCompleteString", B64DecodeCompleteString);
405  UtRegisterTest("B64DecodeInCompleteString", B64DecodeInCompleteString);
406  UtRegisterTest("B64DecodeStringBiggerThanBuffer", B64DecodeStringBiggerThanBuffer);
407  UtRegisterTest("B64DecodeStringEndingSpaces", B64DecodeStringEndingSpaces);
408  UtRegisterTest("B64TestVectorsRFC2045", B64TestVectorsRFC2045);
409  UtRegisterTest("B64TestVectorsRFC4648", B64TestVectorsRFC4648);
410 }
411 #endif
BASE64_ECODE_BUF
@ BASE64_ECODE_BUF
Definition: util-base64.h:75
len
uint8_t len
Definition: app-layer-dnp3.h:2
TEST_RFC4648
#define TEST_RFC4648(src, fin_str, dest_size, exp_decoded, exp_consumed, ecode)
Definition: util-base64.c:222
BASE64_TABLE_MAX
#define BASE64_TABLE_MAX
Definition: util-base64.c:29
BASE64_ECODE_ERR
@ BASE64_ECODE_ERR
Definition: util-base64.h:73
UtRegisterTest
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
Definition: util-unittest.c:103
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:269
util-base64.h
util-unittest.h
BASE64_MODE_RFC2045
@ BASE64_MODE_RFC2045
Definition: util-base64.h:51
util-debug.h
PASS
#define PASS
Pass the test.
Definition: util-unittest.h:105
BASE64_MODE_RFC4648
@ BASE64_MODE_RFC4648
Definition: util-base64.h:68
Base64Mode
Base64Mode
Definition: util-base64.h:34
BASE64_MODE_STRICT
@ BASE64_MODE_STRICT
Definition: util-base64.h:52
padding
uint32_t padding
Definition: decode-erspan.h:2
IsBase64Alphabet
bool IsBase64Alphabet(uint8_t encoded_byte)
Checks if the given char in a byte array is Base64 alphabet.
Definition: util-base64.c:73
FAIL_IF
#define FAIL_IF(expr)
Fail a test if expression evaluates to true.
Definition: util-unittest.h:71
Base64RegisterTests
void Base64RegisterTests(void)
Definition: util-base64.c:400
BASE64_ECODE_OK
@ BASE64_ECODE_OK
Definition: util-base64.h:74
TEST_RFC2045
#define TEST_RFC2045(src, fin_str, dest_size, exp_decoded, exp_consumed, ecode)
Definition: util-base64.c:210
src
uint16_t src
Definition: app-layer-dnp3.h:5
B64_BLOCK
#define B64_BLOCK
Definition: util-base64.h:32
ASCII_BLOCK
#define ASCII_BLOCK
Definition: util-base64.h:31
dst
uint16_t dst
Definition: app-layer-dnp3.h:4
code
uint8_t code
Definition: decode-icmpv4.h:1
Base64Ecode
Base64Ecode
Definition: util-base64.h:72
DecodeBase64
Base64Ecode DecodeBase64(uint8_t *dest, uint32_t dest_size, const uint8_t *src, uint32_t len, uint32_t *consumed_bytes, uint32_t *decoded_bytes, Base64Mode mode)
Decodes a base64-encoded string buffer into an ascii-encoded byte buffer.
Definition: util-base64.c:109