1 #include <stdbool.h>
2 #include <string.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include "../include/libbase64.h"
6 #include "codec_supported.h"
7 #include "moby_dick.h"
8
9 static char out[2000];
10 static size_t outlen;
11
12 static bool
assert_enc(int flags,const char * src,const char * dst)13 assert_enc (int flags, const char *src, const char *dst)
14 {
15 size_t srclen = strlen(src);
16 size_t dstlen = strlen(dst);
17
18 base64_encode(src, srclen, out, &outlen, flags);
19
20 if (outlen != dstlen) {
21 printf("FAIL: encoding of '%s': length expected %lu, got %lu\n", src,
22 (unsigned long)dstlen,
23 (unsigned long)outlen
24 );
25 return true;
26 }
27 if (strncmp(dst, out, outlen) != 0) {
28 out[outlen] = '\0';
29 printf("FAIL: encoding of '%s': expected output '%s', got '%s'\n", src, dst, out);
30 return true;
31 }
32 return false;
33 }
34
35 static bool
assert_dec(int flags,const char * src,const char * dst)36 assert_dec (int flags, const char *src, const char *dst)
37 {
38 size_t srclen = strlen(src);
39 size_t dstlen = strlen(dst);
40
41 if (!base64_decode(src, srclen, out, &outlen, flags)) {
42 printf("FAIL: decoding of '%s': decoding error\n", src);
43 return true;
44 }
45 if (outlen != dstlen) {
46 printf("FAIL: encoding of '%s': "
47 "length expected %lu, got %lu\n", src,
48 (unsigned long)dstlen,
49 (unsigned long)outlen
50 );
51 return true;
52 }
53 if (strncmp(dst, out, outlen) != 0) {
54 out[outlen] = '\0';
55 printf("FAIL: decoding of '%s': expected output '%s', got '%s'\n", src, dst, out);
56 return true;
57 }
58 return false;
59 }
60
61 static int
assert_roundtrip(int flags,const char * src)62 assert_roundtrip (int flags, const char *src)
63 {
64 char tmp[1500];
65 size_t tmplen;
66 size_t srclen = strlen(src);
67
68 // Encode the input into global buffer:
69 base64_encode(src, srclen, out, &outlen, flags);
70
71 // Decode the global buffer into local temp buffer:
72 if (!base64_decode(out, outlen, tmp, &tmplen, flags)) {
73 printf("FAIL: decoding of '%s': decoding error\n", out);
74 return true;
75 }
76
77 // Check that 'src' is identical to 'tmp':
78 if (srclen != tmplen) {
79 printf("FAIL: roundtrip of '%s': "
80 "length expected %lu, got %lu\n", src,
81 (unsigned long)srclen,
82 (unsigned long)tmplen
83 );
84 return true;
85 }
86 if (strncmp(src, tmp, tmplen) != 0) {
87 tmp[tmplen] = '\0';
88 printf("FAIL: roundtrip of '%s': got '%s'\n", src, tmp);
89 return true;
90 }
91
92 return false;
93 }
94
95 static int
test_char_table(int flags,bool use_malloc)96 test_char_table (int flags, bool use_malloc)
97 {
98 bool fail = false;
99 char chr[256];
100 char enc[400], dec[400];
101 size_t enclen, declen;
102
103 // Fill array with all characters 0..255:
104 for (int i = 0; i < 256; i++)
105 chr[i] = (unsigned char)i;
106
107 // Loop, using each char as a starting position to increase test coverage:
108 for (int i = 0; i < 256; i++) {
109
110 size_t chrlen = 256 - i;
111 char* src = &chr[i];
112 if (use_malloc) {
113 src = malloc(chrlen); /* malloc/copy this so valgrind can find out-of-bound access */
114 if (src == NULL) {
115 printf(
116 "FAIL: encoding @ %d: allocation of %lu bytes failed\n",
117 i, (unsigned long)chrlen
118 );
119 fail = true;
120 continue;
121 }
122 memcpy(src, &chr[i], chrlen);
123 }
124
125 base64_encode(src, chrlen, enc, &enclen, flags);
126 if (use_malloc) {
127 free(src);
128 }
129
130 if (!base64_decode(enc, enclen, dec, &declen, flags)) {
131 printf("FAIL: decoding @ %d: decoding error\n", i);
132 fail = true;
133 continue;
134 }
135 if (declen != chrlen) {
136 printf("FAIL: roundtrip @ %d: "
137 "length expected %lu, got %lu\n", i,
138 (unsigned long)chrlen,
139 (unsigned long)declen
140 );
141 fail = true;
142 continue;
143 }
144 if (strncmp(&chr[i], dec, declen) != 0) {
145 printf("FAIL: roundtrip @ %d: decoded output not same as input\n", i);
146 fail = true;
147 }
148 }
149
150 return fail;
151 }
152
153 static int
test_streaming(int flags)154 test_streaming (int flags)
155 {
156 bool fail = false;
157 char chr[256];
158 char ref[400], enc[400];
159 size_t reflen;
160 struct base64_state state;
161
162 // Fill array with all characters 0..255:
163 for (int i = 0; i < 256; i++)
164 chr[i] = (unsigned char)i;
165
166 // Create reference base64 encoding:
167 base64_encode(chr, 256, ref, &reflen, BASE64_FORCE_PLAIN);
168
169 // Encode the table with various block sizes and compare to reference:
170 for (size_t bs = 1; bs < 255; bs++)
171 {
172 size_t inpos = 0;
173 size_t partlen = 0;
174 size_t enclen = 0;
175
176 base64_stream_encode_init(&state, flags);
177 memset(enc, 0, 400);
178 for (;;) {
179 base64_stream_encode(&state, &chr[inpos], (inpos + bs > 256) ? 256 - inpos : bs, &enc[enclen], &partlen);
180 enclen += partlen;
181 if (inpos + bs > 256) {
182 break;
183 }
184 inpos += bs;
185 }
186 base64_stream_encode_final(&state, &enc[enclen], &partlen);
187 enclen += partlen;
188
189 if (enclen != reflen) {
190 printf("FAIL: stream encoding gave incorrect size: "
191 "%lu instead of %lu\n",
192 (unsigned long)enclen,
193 (unsigned long)reflen
194 );
195 fail = true;
196 }
197 if (strncmp(ref, enc, reflen) != 0) {
198 printf("FAIL: stream encoding with blocksize %lu failed\n",
199 (unsigned long)bs
200 );
201 fail = true;
202 }
203 }
204
205 // Decode the reference encoding with various block sizes and
206 // compare to input char table:
207 for (size_t bs = 1; bs < 255; bs++)
208 {
209 size_t inpos = 0;
210 size_t partlen = 0;
211 size_t enclen = 0;
212
213 base64_stream_decode_init(&state, flags);
214 memset(enc, 0, 400);
215 while (base64_stream_decode(&state, &ref[inpos], (inpos + bs > reflen) ? reflen - inpos : bs, &enc[enclen], &partlen)) {
216 enclen += partlen;
217 inpos += bs;
218
219 // Has the entire buffer been consumed?
220 if (inpos >= 400) {
221 break;
222 }
223 }
224 if (enclen != 256) {
225 printf("FAIL: stream decoding gave incorrect size: "
226 "%lu instead of 255\n",
227 (unsigned long)enclen
228 );
229 fail = true;
230 }
231 if (strncmp(chr, enc, 256) != 0) {
232 printf("FAIL: stream decoding with blocksize %lu failed\n",
233 (unsigned long)bs
234 );
235 fail = true;
236 }
237 }
238
239 return fail;
240 }
241
242 static int
test_invalid_dec_input(int flags)243 test_invalid_dec_input (int flags)
244 {
245 // Subset of invalid characters to cover all ranges
246 static const char invalid_set[] = { '\0', -1, '!', '-', ';', '_', '|' };
247 static const char* invalid_strings[] = {
248 "Zm9vYg=",
249 "Zm9vYg",
250 "Zm9vY",
251 "Zm9vYmF=Zm9v"
252 };
253
254 bool fail = false;
255 char chr[256];
256 char enc[400], dec[400];
257 size_t enclen, declen;
258
259 // Fill array with all characters 0..255:
260 for (int i = 0; i < 256; i++)
261 chr[i] = (unsigned char)i;
262
263 // Create reference base64 encoding:
264 base64_encode(chr, 256, enc, &enclen, BASE64_FORCE_PLAIN);
265
266 // Test invalid strings returns error.
267 for (size_t i = 0U; i < sizeof(invalid_strings) / sizeof(invalid_strings[0]); ++i) {
268 if (base64_decode(invalid_strings[i], strlen(invalid_strings[i]), dec, &declen, flags)) {
269 printf("FAIL: decoding invalid input \"%s\": no decoding error\n", invalid_strings[i]);
270 fail = true;
271 }
272 }
273
274 // Loop, corrupting each char to increase test coverage:
275 for (size_t c = 0U; c < sizeof(invalid_set); ++c) {
276 for (size_t i = 0U; i < enclen; i++) {
277 char backup = enc[i];
278
279 enc[i] = invalid_set[c];
280
281 if (base64_decode(enc, enclen, dec, &declen, flags)) {
282 printf("FAIL: decoding invalid input @ %d: no decoding error\n", (int)i);
283 fail = true;
284 enc[i] = backup;
285 continue;
286 }
287 enc[i] = backup;
288 }
289 }
290
291 // Loop, corrupting two chars to increase test coverage:
292 for (size_t c = 0U; c < sizeof(invalid_set); ++c) {
293 for (size_t i = 0U; i < enclen - 2U; i++) {
294 char backup = enc[i+0];
295 char backup2 = enc[i+2];
296
297 enc[i+0] = invalid_set[c];
298 enc[i+2] = invalid_set[c];
299
300 if (base64_decode(enc, enclen, dec, &declen, flags)) {
301 printf("FAIL: decoding invalid input @ %d: no decoding error\n", (int)i);
302 fail = true;
303 enc[i+0] = backup;
304 enc[i+2] = backup2;
305 continue;
306 }
307 enc[i+0] = backup;
308 enc[i+2] = backup2;
309 }
310 }
311
312 return fail;
313 }
314
315 static int
test_one_codec(const char * codec,int flags)316 test_one_codec (const char *codec, int flags)
317 {
318 bool fail = false;
319
320 printf("Codec %s:\n", codec);
321
322 // Skip if this codec is not supported:
323 if (!codec_supported(flags)) {
324 puts(" skipping");
325 return false;
326 }
327
328 // Test vectors:
329 struct {
330 const char *in;
331 const char *out;
332 } vec[] = {
333
334 // These are the test vectors from RFC4648:
335 { "", "" },
336 { "f", "Zg==" },
337 { "fo", "Zm8=" },
338 { "foo", "Zm9v" },
339 { "foob", "Zm9vYg==" },
340 { "fooba", "Zm9vYmE=" },
341 { "foobar", "Zm9vYmFy" },
342
343 // The first paragraph from Moby Dick,
344 // to test the SIMD codecs with larger blocksize:
345 { moby_dick_plain, moby_dick_base64 },
346 };
347
348 for (size_t i = 0; i < sizeof(vec) / sizeof(vec[0]); i++) {
349
350 // Encode plain string, check against output:
351 fail |= assert_enc(flags, vec[i].in, vec[i].out);
352
353 // Decode the output string, check if we get the input:
354 fail |= assert_dec(flags, vec[i].out, vec[i].in);
355
356 // Do a roundtrip on the inputs and the outputs:
357 fail |= assert_roundtrip(flags, vec[i].in);
358 fail |= assert_roundtrip(flags, vec[i].out);
359 }
360
361 fail |= test_char_table(flags, false); /* test with unaligned input buffer */
362 fail |= test_char_table(flags, true); /* test for out-of-bound input read */
363 fail |= test_streaming(flags);
364 fail |= test_invalid_dec_input(flags);
365
366 if (!fail)
367 puts(" all tests passed.");
368
369 return fail;
370 }
371
372 int
main()373 main ()
374 {
375 bool fail = false;
376
377 // Loop over all codecs:
378 for (size_t i = 0; codecs[i]; i++) {
379
380 // Flags to invoke this codec:
381 int codec_flags = (1 << i);
382
383 // Test this codec, merge the results:
384 fail |= test_one_codec(codecs[i], codec_flags);
385 }
386
387 return (fail) ? 1 : 0;
388 }
389