• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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