1 /* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "test-lib.h"
4 #include "str.h"
5 #include "base64.h"
6 
test_base64_encode(void)7 static void test_base64_encode(void)
8 {
9 	const struct {
10 		const char *input;
11 		const char *output;
12 	} tests[] = {
13 		{ "hello world", "aGVsbG8gd29ybGQ=" },
14 		{ "foo barits", "Zm9vIGJhcml0cw==" },
15 		{ "just niin", "anVzdCBuaWlu" },
16 		{ "\xe7\x8c\xbf\xe3\x82\x82\xe6\x9c\xa8\xe3\x81\x8b"
17 		  "\xe3\x82\x89\xe8\x90\xbd\xe3\x81\xa1\xe3\x82\x8b",
18 		  "54y/44KC5pyo44GL44KJ6JC944Gh44KL" },
19 		{ "\xe8\xa7\x92\xe3\x82\x92\xe7\x9f\xaf\xe3\x82\x81\xe3\x81"
20 		  "\xa6\xe7\x89\x9b\xe3\x82\x92\xe6\xae\xba\xe3\x81\x99",
21 		  "6KeS44KS55+v44KB44Gm54mb44KS5q6644GZ" },
22 	};
23 	string_t *str;
24 	unsigned int i;
25 
26 	test_begin("base64_encode()");
27 	str = t_str_new(256);
28 	for (i = 0; i < N_ELEMENTS(tests); i++) {
29 		str_truncate(str, 0);
30 		base64_encode(tests[i].input, strlen(tests[i].input), str);
31 		test_assert_idx(strcmp(tests[i].output, str_c(str)) == 0, i);
32 		test_assert_idx(
33 			str_len(str) ==	MAX_BASE64_ENCODED_SIZE(
34 				strlen(tests[i].input)), i);
35 	}
36 	test_end();
37 }
38 
39 struct test_base64_decode {
40 	const char *input;
41 	const char *output;
42 	int ret;
43 };
44 
test_base64_decode(void)45 static void test_base64_decode(void)
46 {
47 	static const struct test_base64_decode tests[] = {
48 		{ "", "", 0 },
49 		{ "\taGVsbG8gd29ybGQ=",
50 		  "hello world", 0 },
51 		{ "\nZm9v\n \tIGJh  \t\ncml0cw==",
52 		  "foo barits", 0 },
53 		{ "  anVzdCBuaWlu  \n",
54 		  "just niin", 0 },
55 		{ "aGVsb",
56 		  "hel", -1 },
57 		{ "aGVsb!!!!!",
58 		  "hel", -1 },
59 		{ "aGVs!!!!!",
60 		  "hel", -1 },
61 		{ "0JPQvtCy0L7RgNGPzIHRgiwg0YfRgt"
62 		  "C+INC60YPRgCDQtNC+0Y/MgdGCLg==",
63 		  "\xd0\x93\xd0\xbe\xd0\xb2\xd0\xbe\xd1\x80\xd1\x8f\xcc"
64 		  "\x81\xd1\x82\x2c\x20\xd1\x87\xd1\x82\xd0\xbe\x20\xd0"
65 		  "\xba\xd1\x83\xd1\x80\x20\xd0\xb4\xd0\xbe\xd1\x8f\xcc"
66 		  "\x81\xd1\x82\x2e", 0 },
67 	};
68 	string_t *str;
69 	buffer_t buf;
70 	unsigned int i;
71 	int ret;
72 
73 	test_begin("base64_decode()");
74 	for (i = 0; i < N_ELEMENTS(tests); i++) {
75 		/* Some of the base64_decode() callers use fixed size buffers.
76 		   Use a fixed size buffer here as well to test that
77 		   base64_decode() can't allocate any extra space even
78 		   temporarily. */
79 		size_t max_decoded_size =
80 			MAX_BASE64_DECODED_SIZE(strlen(tests[i].input));
81 
82 		buffer_create_from_data(&buf,
83 					(max_decoded_size == 0 ? "" :
84 					 t_malloc0(max_decoded_size)),
85 					max_decoded_size);
86 		str = &buf;
87 		ret = base64_decode(tests[i].input, strlen(tests[i].input),
88 				    NULL, str);
89 
90 		test_assert_idx(tests[i].ret == ret, i);
91 		test_assert_idx(strlen(tests[i].output) == str_len(str) &&
92 				memcmp(tests[i].output, str_data(str),
93 				       str_len(str)) == 0, i);
94 		if (ret >= 0) {
95 			test_assert_idx(
96 				str_len(str) <= MAX_BASE64_DECODED_SIZE(
97 					strlen(tests[i].input)), i);
98 		}
99 	}
100 	test_end();
101 }
102 
test_base64_random(void)103 static void test_base64_random(void)
104 {
105 	string_t *str, *dest;
106 	unsigned char buf[10];
107 	unsigned int i, j, max;
108 
109 	str = t_str_new(256);
110 	dest = t_str_new(256);
111 
112 	test_begin("base64 encode/decode with random input");
113 	for (i = 0; i < 1000; i++) {
114 		max = i_rand_limit(sizeof(buf));
115 		for (j = 0; j < max; j++)
116 			buf[j] = i_rand_uchar();
117 
118 		str_truncate(str, 0);
119 		str_truncate(dest, 0);
120 		base64_encode(buf, max, str);
121 		test_assert_idx(base64_decode(str_data(str), str_len(str),
122 					      NULL, dest) >= 0, i);
123 		test_assert_idx(str_len(dest) == max &&
124 				memcmp(buf, str_data(dest), max) == 0, i);
125 	}
126 	test_end();
127 }
128 
test_base64url_encode(void)129 static void test_base64url_encode(void)
130 {
131 	const struct {
132 		const char *input;
133 		const char *output;
134 	} tests[] = {
135 		{ "", "" },
136 		{ "hello world", "aGVsbG8gd29ybGQ=" },
137 		{ "foo barits", "Zm9vIGJhcml0cw==" },
138 		{ "just niin", "anVzdCBuaWlu" },
139 		{ "\xe7\x8c\xbf\xe3\x82\x82\xe6\x9c\xa8\xe3\x81\x8b"
140 		  "\xe3\x82\x89\xe8\x90\xbd\xe3\x81\xa1\xe3\x82\x8b",
141 		  "54y_44KC5pyo44GL44KJ6JC944Gh44KL" },
142 		{ "\xe8\xa7\x92\xe3\x82\x92\xe7\x9f\xaf\xe3\x82\x81\xe3\x81"
143 		  "\xa6\xe7\x89\x9b\xe3\x82\x92\xe6\xae\xba\xe3\x81\x99",
144 		  "6KeS44KS55-v44KB44Gm54mb44KS5q6644GZ" },
145 	};
146 	string_t *str;
147 	unsigned int i;
148 
149 	test_begin("base64url_encode()");
150 	str = t_str_new(256);
151 	for (i = 0; i < N_ELEMENTS(tests); i++) {
152 		str_truncate(str, 0);
153 		base64url_encode(0, 0, tests[i].input, strlen(tests[i].input),
154 				 str);
155 		test_assert_idx(strcmp(tests[i].output, str_c(str)) == 0, i);
156 		test_assert_idx(
157 			str_len(str) ==	MAX_BASE64_ENCODED_SIZE(
158 				strlen(tests[i].input)), i);
159 	}
160 	test_end();
161 }
162 
163 struct test_base64url_decode {
164 	const char *input;
165 	const char *output;
166 	int ret;
167 };
168 
test_base64url_decode(void)169 static void test_base64url_decode(void)
170 {
171 	static const struct test_base64url_decode tests[] = {
172 		{ "", "", 0 },
173 		{ "\taGVsbG8gd29ybGQ=",
174 		  "hello world", 0 },
175 		{ "\nZm9v\n \tIGJh  \t\ncml0cw==",
176 		  "foo barits", 0 },
177 		{ "  anVzdCBuaWlu  \n",
178 		  "just niin", 0 },
179 		{ "aGVsb",
180 		  "hel", -1 },
181 		{ "aGVsb!!!!!",
182 		  "hel", -1 },
183 		{ "aGVs!!!!!",
184 		  "hel", -1 },
185 		{ "0JPQvtCy0L7RgNGPzIHRgiwg0YfRgt"
186 		  "C-INC60YPRgCDQtNC-0Y_MgdGCLg==",
187 		  "\xd0\x93\xd0\xbe\xd0\xb2\xd0\xbe\xd1\x80\xd1\x8f\xcc"
188 		  "\x81\xd1\x82\x2c\x20\xd1\x87\xd1\x82\xd0\xbe\x20\xd0"
189 		  "\xba\xd1\x83\xd1\x80\x20\xd0\xb4\xd0\xbe\xd1\x8f\xcc"
190 		  "\x81\xd1\x82\x2e", 0 },
191 	};
192 	string_t *str;
193 	buffer_t buf;
194 	unsigned int i;
195 	int ret;
196 
197 	test_begin("base64url_decode()");
198 	for (i = 0; i < N_ELEMENTS(tests); i++) {
199 		/* Some of the base64_decode() callers use fixed size buffers.
200 		   Use a fixed size buffer here as well to test that
201 		   base64_decode() can't allocate any extra space even
202 		   temporarily. */
203 		size_t max_decoded_size =
204 			MAX_BASE64_DECODED_SIZE(strlen(tests[i].input));
205 
206 		buffer_create_from_data(&buf,
207 					(max_decoded_size == 0 ? "" :
208 					 t_malloc0(max_decoded_size)),
209 					max_decoded_size);
210 		str = &buf;
211 		ret = base64url_decode(0, tests[i].input,
212 				       strlen(tests[i].input), str);
213 
214 		test_assert_idx(tests[i].ret == ret, i);
215 		test_assert_idx(strlen(tests[i].output) == str_len(str) &&
216 				memcmp(tests[i].output, str_data(str),
217 				       str_len(str)) == 0, i);
218 		if (ret >= 0) {
219 			test_assert_idx(
220 				str_len(str) <= MAX_BASE64_DECODED_SIZE(
221 					strlen(tests[i].input)), i);
222 		}
223 	}
224 	test_end();
225 }
226 
test_base64url_random(void)227 static void test_base64url_random(void)
228 {
229 	string_t *str, *dest;
230 	unsigned char buf[10];
231 	unsigned int i, j, max;
232 
233 	str = t_str_new(256);
234 	dest = t_str_new(256);
235 
236 	test_begin("base64url encode/decode with random input");
237 	for (i = 0; i < 1000; i++) {
238 		max = i_rand_limit(sizeof(buf));
239 		for (j = 0; j < max; j++)
240 			buf[j] = i_rand_uchar();
241 
242 		str_truncate(str, 0);
243 		str_truncate(dest, 0);
244 		base64url_encode(0, 0, buf, max, str);
245 		test_assert_idx(base64url_decode(0, str_data(str), str_len(str),
246 						 dest) >= 0, i);
247 		test_assert_idx(str_len(dest) == max &&
248 				memcmp(buf, str_data(dest), max) == 0, i);
249 	}
250 	test_end();
251 }
252 
253 struct test_base64_encode_lowlevel {
254 	const struct base64_scheme *scheme;
255 	enum base64_encode_flags flags;
256 	size_t max_line_len;
257 
258 	const char *input;
259 	const char *output;
260 };
261 
262 static const struct test_base64_encode_lowlevel
263 tests_base64_encode_lowlevel[] = {
264 	{
265 		.scheme = &base64_scheme,
266 		.input = "",
267 		.output = "",
268 	},
269 	{
270 		.scheme = &base64_scheme,
271 		.max_line_len = 2,
272 		.input = "",
273 		.output = "",
274 	},
275 	{
276 		.scheme = &base64_scheme,
277 		.flags = BASE64_ENCODE_FLAG_CRLF,
278 		.max_line_len = 2,
279 		.input = "",
280 		.output = "",
281 	},
282 	{
283 		.scheme = &base64_scheme,
284 		.flags = BASE64_ENCODE_FLAG_NO_PADDING,
285 		.input = "",
286 		.output = "",
287 	},
288 	{
289 		.scheme = &base64_scheme,
290 		.input = "hello world",
291 		.output = "aGVsbG8gd29ybGQ=",
292 	},
293 	{
294 		.scheme = &base64url_scheme,
295 		.input = "hello world",
296 		.output = "aGVsbG8gd29ybGQ=",
297 	},
298 	{
299 		.scheme = &base64_scheme,
300 		.flags = BASE64_ENCODE_FLAG_NO_PADDING,
301 		.input = "hello world",
302 		.output = "aGVsbG8gd29ybGQ",
303 	},
304 	{
305 		.scheme = &base64_scheme,
306 		.input = "foo barits",
307 		.output = "Zm9vIGJhcml0cw==",
308 	},
309 	{
310 		.scheme = &base64url_scheme,
311 		.input = "foo barits",
312 		.output = "Zm9vIGJhcml0cw==",
313 	},
314 	{
315 		.scheme = &base64_scheme,
316 		.flags = BASE64_ENCODE_FLAG_NO_PADDING,
317 		.input = "foo barits",
318 		.output = "Zm9vIGJhcml0cw",
319 	},
320 	{
321 		.scheme = &base64_scheme,
322 		.input = "just niin",
323 		.output = "anVzdCBuaWlu",
324 	},
325 	{
326 		.scheme = &base64url_scheme,
327 		.input = "just niin",
328 		.output = "anVzdCBuaWlu",
329 	},
330 	{
331 		.scheme = &base64_scheme,
332 		.flags = BASE64_ENCODE_FLAG_NO_PADDING,
333 		.input = "just niin",
334 		.output = "anVzdCBuaWlu",
335 	},
336 	{
337 		.scheme = &base64_scheme,
338 		.input =
339 			"\xe7\x8c\xbf\xe3\x82\x82\xe6\x9c\xa8\xe3\x81\x8b"
340 			"\xe3\x82\x89\xe8\x90\xbd\xe3\x81\xa1\xe3\x82\x8b",
341 		.output = "54y/44KC5pyo44GL44KJ6JC944Gh44KL",
342 	},
343 	{
344 		.scheme = &base64url_scheme,
345 		.input =
346 			"\xe7\x8c\xbf\xe3\x82\x82\xe6\x9c\xa8\xe3\x81\x8b"
347 			"\xe3\x82\x89\xe8\x90\xbd\xe3\x81\xa1\xe3\x82\x8b",
348 		.output = "54y_44KC5pyo44GL44KJ6JC944Gh44KL",
349 	},
350 	{
351 		.scheme = &base64_scheme,
352 		.input =
353 			"\xe8\xa7\x92\xe3\x82\x92\xe7\x9f\xaf\xe3\x82\x81\xe3"
354 			"\x81\xa6\xe7\x89\x9b\xe3\x82\x92\xe6\xae\xba\xe3\x81"
355 			"\x99",
356 		.output = "6KeS44KS55+v44KB44Gm54mb44KS5q6644GZ",
357 	},
358 	{
359 		.scheme = &base64url_scheme,
360 		.input =
361 			"\xe8\xa7\x92\xe3\x82\x92\xe7\x9f\xaf\xe3\x82\x81\xe3"
362 			"\x81\xa6\xe7\x89\x9b\xe3\x82\x92\xe6\xae\xba\xe3\x81"
363 			"\x99",
364 		.output = "6KeS44KS55-v44KB44Gm54mb44KS5q6644GZ",
365 	},
366 	{
367 		.scheme = &base64_scheme,
368 		.flags = BASE64_ENCODE_FLAG_CRLF,
369 		.input = "just niin",
370 		.output = "anVzdCBuaWlu",
371 	},
372 	{
373 		.scheme = &base64_scheme,
374 		.flags = BASE64_ENCODE_FLAG_CRLF,
375 		.max_line_len = 80,
376 		.input = "just niin",
377 		.output = "anVzdCBuaWlu",
378 	},
379 	{
380 		.scheme = &base64_scheme,
381 		.flags = BASE64_ENCODE_FLAG_CRLF,
382 		.max_line_len = 48,
383 		.input =
384 			"Passer, deliciae meae puellae,\n"
385 			"quicum ludere, quem in sinu tenere,\n"
386 			"cui primum digitum dare appetenti\n"
387 			"et acris solet incitare morsus,\n"
388 			"cum desiderio meo nitenti\n"
389 			"carum nescio quid lubet iocari,\n"
390 			"credo ut, cum gravis acquiescet ardor,\n"
391 			"sit solaciolum sui doloris,\n"
392 			"tecum ludere sicut ipsa possem\n"
393 			"et tristis animi levare curas!\n",
394 		.output =
395 			"UGFzc2VyLCBkZWxpY2lhZSBtZWFlIHB1ZWxsYWUsCnF1aWN1\r\n"
396 			"bSBsdWRlcmUsIHF1ZW0gaW4gc2ludSB0ZW5lcmUsCmN1aSBw\r\n"
397 			"cmltdW0gZGlnaXR1bSBkYXJlIGFwcGV0ZW50aQpldCBhY3Jp\r\n"
398 			"cyBzb2xldCBpbmNpdGFyZSBtb3JzdXMsCmN1bSBkZXNpZGVy\r\n"
399 			"aW8gbWVvIG5pdGVudGkKY2FydW0gbmVzY2lvIHF1aWQgbHVi\r\n"
400 			"ZXQgaW9jYXJpLApjcmVkbyB1dCwgY3VtIGdyYXZpcyBhY3F1\r\n"
401 			"aWVzY2V0IGFyZG9yLApzaXQgc29sYWNpb2x1bSBzdWkgZG9s\r\n"
402 			"b3JpcywKdGVjdW0gbHVkZXJlIHNpY3V0IGlwc2EgcG9zc2Vt\r\n"
403 			"CmV0IHRyaXN0aXMgYW5pbWkgbGV2YXJlIGN1cmFzIQo=",
404 	},
405 	{
406 		.scheme = &base64_scheme,
407 		.max_line_len = 48,
408 		.input =
409 			"Lugete, o Veneres Cupidinesque,\n"
410 			"et quantum est hominum venustiorum:\n"
411 			"passer mortuus est meae puellae, \n"
412 			"passer, deliciae meae puellae\n"
413 			"quem plus amat illa oculis suis amabat.\n"
414 			"Nam mellitus erat suamque norat\n"
415 			"ipsam tam bene quam puella matrem,\n"
416 			"nec sese a gremio illius movebat,\n"
417 			"sed circumsiliens modo huc modo illuc\n"
418 			"ad solam dominam usque pipiabat;\n"
419 			"qui nunc it per iter tenebricosum\n"
420 			"illuc, unde negant redire quemquam.\n"
421 			"At vobis male sint, malae tenebrae\n"
422 			"Orci, quae omnia bella devoratis:\n"
423 			"tam bellum mihi passerem abstulistis.\n"
424 			"O factum male! O miselle passer!\n"
425 			"Tua nunc opera meae puellae\n"
426 			"flendo turgiduli rubent ocelli.\n",
427 		.output =
428 			"THVnZXRlLCBvIFZlbmVyZXMgQ3VwaWRpbmVzcXVlLApldCBx\n"
429 			"dWFudHVtIGVzdCBob21pbnVtIHZlbnVzdGlvcnVtOgpwYXNz\n"
430 			"ZXIgbW9ydHV1cyBlc3QgbWVhZSBwdWVsbGFlLCAKcGFzc2Vy\n"
431 			"LCBkZWxpY2lhZSBtZWFlIHB1ZWxsYWUKcXVlbSBwbHVzIGFt\n"
432 			"YXQgaWxsYSBvY3VsaXMgc3VpcyBhbWFiYXQuCk5hbSBtZWxs\n"
433 			"aXR1cyBlcmF0IHN1YW1xdWUgbm9yYXQKaXBzYW0gdGFtIGJl\n"
434 			"bmUgcXVhbSBwdWVsbGEgbWF0cmVtLApuZWMgc2VzZSBhIGdy\n"
435 			"ZW1pbyBpbGxpdXMgbW92ZWJhdCwKc2VkIGNpcmN1bXNpbGll\n"
436 			"bnMgbW9kbyBodWMgbW9kbyBpbGx1YwphZCBzb2xhbSBkb21p\n"
437 			"bmFtIHVzcXVlIHBpcGlhYmF0OwpxdWkgbnVuYyBpdCBwZXIg\n"
438 			"aXRlciB0ZW5lYnJpY29zdW0KaWxsdWMsIHVuZGUgbmVnYW50\n"
439 			"IHJlZGlyZSBxdWVtcXVhbS4KQXQgdm9iaXMgbWFsZSBzaW50\n"
440 			"LCBtYWxhZSB0ZW5lYnJhZQpPcmNpLCBxdWFlIG9tbmlhIGJl\n"
441 			"bGxhIGRldm9yYXRpczoKdGFtIGJlbGx1bSBtaWhpIHBhc3Nl\n"
442 			"cmVtIGFic3R1bGlzdGlzLgpPIGZhY3R1bSBtYWxlISBPIG1p\n"
443 			"c2VsbGUgcGFzc2VyIQpUdWEgbnVuYyBvcGVyYSBtZWFlIHB1\n"
444 			"ZWxsYWUKZmxlbmRvIHR1cmdpZHVsaSBydWJlbnQgb2NlbGxp\n"
445 			"Lgo=",
446 	},
447 };
448 
test_base64_encode_lowlevel(void)449 static void test_base64_encode_lowlevel(void)
450 {
451 	string_t *str;
452 	unsigned int i;
453 
454 	test_begin("base64 encode low-level");
455 	str = t_str_new(256);
456 	for (i = 0; i < N_ELEMENTS(tests_base64_encode_lowlevel); i++) {
457 		const struct test_base64_encode_lowlevel *test =
458 			&tests_base64_encode_lowlevel[i];
459 		struct base64_encoder enc;
460 		uoff_t out_size;
461 
462 		str_truncate(str, 0);
463 
464 		base64_encode_init(&enc, test->scheme, test->flags,
465 				   test->max_line_len);
466 		out_size = base64_get_full_encoded_size(
467 			&enc, strlen(test->input));
468 		test_assert_idx(base64_encode_more(&enc, test->input,
469 						   strlen(test->input),
470 						   NULL, str), i);
471 		test_assert_idx(base64_encode_finish(&enc, str), i);
472 
473 		test_assert_idx(strcmp(test->output, str_c(str)) == 0, i);
474 		test_assert_idx(test->flags != 0 || test->max_line_len != 0 ||
475 			str_len(str) == MAX_BASE64_ENCODED_SIZE(
476 				strlen(test->input)), i);
477 		test_assert_idx(str_len(str) == out_size, i);
478 	}
479 	test_end();
480 }
481 
482 struct test_base64_decode_lowlevel {
483 	const struct base64_scheme *scheme;
484 	enum base64_decode_flags flags;
485 
486 	const char *input;
487 	const char *output;
488 	int ret;
489 	unsigned int src_pos;
490 };
491 
492 static const struct test_base64_decode_lowlevel
493 tests_base64_decode_lowlevel[] = {
494 	{
495 		.scheme = &base64_scheme,
496 		.input = "",
497 		.output = "",
498 		.ret = 0,
499 		.src_pos = UINT_MAX,
500 	},
501 	{
502 		.scheme = &base64_scheme,
503 		.input = " ",
504 		.output = "",
505 		.ret = 0,
506 		.src_pos = UINT_MAX,
507 	},
508 	{
509 		.scheme = &base64_scheme,
510 		.input = "",
511 		.output = "",
512 		.ret = 0,
513 		.src_pos = UINT_MAX,
514 		.flags = BASE64_DECODE_FLAG_EXPECT_BOUNDARY,
515 	},
516 	{
517 		.scheme = &base64_scheme,
518 		.input = "",
519 		.output = "",
520 		.ret = 0,
521 		.src_pos = UINT_MAX,
522 		.flags = BASE64_DECODE_FLAG_NO_WHITESPACE,
523 	},
524 	{
525 		.scheme = &base64_scheme,
526 		.input = " ",
527 		.output = "",
528 		.ret = -1,
529 		.src_pos = 0,
530 		.flags = BASE64_DECODE_FLAG_NO_WHITESPACE,
531 	},
532 	{
533 		.scheme = &base64_scheme,
534 		.input = "",
535 		.output = "",
536 		.ret = 0,
537 		.src_pos = UINT_MAX,
538 		.flags = BASE64_DECODE_FLAG_NO_PADDING,
539 	},
540 	{
541 		.scheme = &base64_scheme,
542 		.input = "",
543 		.output = "",
544 		.ret = 0,
545 		.src_pos = UINT_MAX,
546 		.flags = BASE64_DECODE_FLAG_IGNORE_PADDING,
547 	},
548 	{
549 		.scheme = &base64_scheme,
550 		.input = "\taGVsbG8gd29ybGQ=",
551 		.output = "hello world",
552 		.ret = 0,
553 		.src_pos = UINT_MAX,
554 	},
555 	{
556 		.scheme = &base64url_scheme,
557 		.input = "\taGVsbG8gd29ybGQ=",
558 		.output = "hello world",
559 		.ret = 0,
560 		.src_pos = UINT_MAX,
561 	},
562 	{
563 		.scheme = &base64_scheme,
564 		.input = "aGVsbG8gd29ybGQ=\t",
565 		.output = "hello world",
566 		.ret = 0,
567 		.src_pos = UINT_MAX,
568 	},
569 	{
570 		.scheme = &base64_scheme,
571 		.input = "\taGVsbG8gd29ybGQ=\t",
572 		.output = "hello world",
573 		.ret = 0,
574 		.src_pos = UINT_MAX,
575 	},
576 	{
577 		.scheme = &base64_scheme,
578 		.input = "aGVsbG8gd29ybGQ=:frop",
579 		.output = "hello world",
580 		.ret = 0,
581 		.src_pos = 16,
582 		.flags = BASE64_DECODE_FLAG_EXPECT_BOUNDARY,
583 	},
584 	{
585 		.scheme = &base64_scheme,
586 		.input = "\taGVsbG8gd29ybGQ=\t:frop",
587 		.output = "hello world",
588 		.ret = 0,
589 		.src_pos = 18,
590 		.flags = BASE64_DECODE_FLAG_EXPECT_BOUNDARY,
591 	},
592 	{
593 		.scheme = &base64_scheme,
594 		.input = "aGVsbG8gd29ybGQ=\t",
595 		.output = "hello world",
596 		.ret = -1,
597 		.src_pos = 16,
598 		.flags = BASE64_DECODE_FLAG_NO_WHITESPACE,
599 	},
600 	{
601 		.scheme = &base64_scheme,
602 		.input = "\taGVsbG8gd29ybGQ=\t",
603 		.output = "",
604 		.ret = -1,
605 		.src_pos = 0,
606 		.flags = BASE64_DECODE_FLAG_NO_WHITESPACE,
607 	},
608 	{
609 		.scheme = &base64_scheme,
610 		.flags = BASE64_DECODE_FLAG_NO_PADDING,
611 		.input = "\taGVsbG8gd29ybGQ=",
612 		.output = "hello world",
613 		.ret = -1,
614 		.src_pos = 16,
615 	},
616 	{
617 		.scheme = &base64_scheme,
618 		.flags = BASE64_DECODE_FLAG_NO_PADDING,
619 		.input = "\taGVsbG8gd29ybGQ",
620 		.output = "hello world",
621 		.ret = 0,
622 		.src_pos = 16,
623 	},
624 	{
625 		.scheme = &base64_scheme,
626 		.flags = BASE64_DECODE_FLAG_IGNORE_PADDING,
627 		.input = "\taGVsbG8gd29ybGQ=",
628 		.output = "hello world",
629 		.ret = 0,
630 		.src_pos = 17,
631 	},
632 	{
633 		.scheme = &base64_scheme,
634 		.flags = BASE64_DECODE_FLAG_IGNORE_PADDING,
635 		.input = "\taGVsbG8gd29ybGQ",
636 		.output = "hello world",
637 		.ret = 0,
638 		.src_pos = 16,
639 	},
640 	{
641 		.scheme = &base64_scheme,
642 		.input = "\nZm9v\n \tIGJh  \t\ncml0cw==",
643 		.output = "foo barits",
644 		.ret = 0,
645 		.src_pos = UINT_MAX,
646 	},
647 	{
648 		.scheme = &base64url_scheme,
649 		.input = "\nZm9v\n \tIGJh  \t\ncml0cw==",
650 		.output = "foo barits",
651 		.ret = 0,
652 		.src_pos = UINT_MAX,
653 	},
654 	{
655 		.scheme = &base64_scheme,
656 		.input = "\nZm9v\n \tIGJh  \t\ncml0cw==\n  ",
657 		.output = "foo barits",
658 		.ret = 0,
659 		.src_pos = UINT_MAX,
660 	},
661 	{
662 		.scheme = &base64_scheme,
663 		.input = "\nZm9v\n \tIGJh  \t\ncml0cw= =\n  ",
664 		.output = "foo barits",
665 		.ret = 0,
666 		.src_pos = UINT_MAX,
667 	},
668 	{
669 		.scheme = &base64_scheme,
670 		.input = "\nZm9v\n \tIGJh  \t\ncml0cw\n= =\n  ",
671 		.output = "foo barits",
672 		.ret = 0,
673 		.src_pos = UINT_MAX,
674 	},
675 	{
676 		.scheme = &base64_scheme,
677 		.flags = BASE64_DECODE_FLAG_NO_PADDING,
678 		.input = "\nZm9v\n \tIGJh  \t\ncml0cw==",
679 		.output = "foo barits",
680 		.ret = -1,
681 		.src_pos = 22,
682 	},
683 	{
684 		.scheme = &base64_scheme,
685 		.flags = BASE64_DECODE_FLAG_NO_PADDING,
686 		.input = "\nZm9v\n \tIGJh  \t\ncml0cw",
687 		.output = "foo barits",
688 		.ret = 0,
689 		.src_pos = 22,
690 	},
691 	{
692 		.scheme = &base64_scheme,
693 		.flags = BASE64_DECODE_FLAG_IGNORE_PADDING,
694 		.input = "\nZm9v\n \tIGJh  \t\ncml0cw==",
695 		.output = "foo barits",
696 		.ret = 0,
697 		.src_pos = 24,
698 	},
699 	{
700 		.scheme = &base64_scheme,
701 		.flags = BASE64_DECODE_FLAG_IGNORE_PADDING,
702 		.input = "\nZm9v\n \tIGJh  \t\ncml0cw",
703 		.output = "foo barits",
704 		.ret = 0,
705 		.src_pos = 22,
706 	},
707 	{
708 		.scheme = &base64_scheme,
709 		.input = "  anVzdCBuaWlu  \n",
710 		.output = "just niin",
711 		.ret = 0,
712 		.src_pos = UINT_MAX,
713 	},
714 	{
715 		.scheme = &base64url_scheme,
716 		.input = "  anVzdCBuaWlu  \n",
717 		.output = "just niin",
718 		.ret = 0,
719 		.src_pos = UINT_MAX,
720 	},
721 	{
722 		.scheme = &base64_scheme,
723 		.flags = BASE64_DECODE_FLAG_NO_PADDING,
724 		.input = "  anVzdCBuaWlu  \n",
725 		.output = "just niin",
726 		.ret = 0,
727 		.src_pos = UINT_MAX,
728 	},
729 	{
730 		.scheme = &base64_scheme,
731 		.flags = BASE64_DECODE_FLAG_IGNORE_PADDING,
732 		.input = "  anVzdCBuaWlu  \n",
733 		.output = "just niin",
734 		.ret = 0,
735 		.src_pos = UINT_MAX,
736 	},
737 	{
738 		.scheme = &base64_scheme,
739 		.input = "aGVsb",
740 		.output = "hel",
741 		.ret = -1,
742 		.src_pos = 5,
743 	},
744 	{
745 		.scheme = &base64url_scheme,
746 		.input = "aGVsb",
747 		.output = "hel",
748 		.ret = -1,
749 		.src_pos = 5,
750 	},
751 	{
752 		.scheme = &base64_scheme,
753 		.flags = BASE64_DECODE_FLAG_NO_PADDING,
754 		.input = "aGVsb",
755 		.output = "hel",
756 		.ret = 0,
757 		.src_pos = 5,
758 	},
759 	{
760 		.scheme = &base64_scheme,
761 		.flags = BASE64_DECODE_FLAG_IGNORE_PADDING,
762 		.input = "aGVsb",
763 		.output = "hel",
764 		.ret = 0,
765 		.src_pos = 5,
766 	},
767 	{
768 		.scheme = &base64_scheme,
769 		.input = "aGVsb!!!!!",
770 		.output = "hel",
771 		.ret = -1,
772 		.src_pos = 5,
773 	},
774 	{
775 		.scheme = &base64url_scheme,
776 		.input = "aGVsb!!!!!",
777 		.output = "hel",
778 		.ret = -1,
779 		.src_pos = 5,
780 	},
781 	{
782 		.scheme = &base64_scheme,
783 		.input = "aGVs!!!!!",
784 		.output = "hel",
785 		.ret = -1,
786 		.src_pos = 4,
787 	},
788 	{
789 		.scheme = &base64url_scheme,
790 		.input = "aGVs!!!!!",
791 		.output = "hel",
792 		.ret = -1,
793 		.src_pos = 4,
794 	},
795 	{
796 		.scheme = &base64_scheme,
797 		.input =
798 			"0JPQvtCy0L7RgNGPzIHRgiwg0YfRgt"
799 			"C+INC60YPRgCDQtNC+0Y/MgdGCLg==",
800 		.output =
801 			"\xd0\x93\xd0\xbe\xd0\xb2\xd0\xbe\xd1\x80\xd1\x8f\xcc"
802 			"\x81\xd1\x82\x2c\x20\xd1\x87\xd1\x82\xd0\xbe\x20\xd0"
803 			"\xba\xd1\x83\xd1\x80\x20\xd0\xb4\xd0\xbe\xd1\x8f\xcc"
804 			"\x81\xd1\x82\x2e",
805 		.ret = 0,
806 		.src_pos = UINT_MAX,
807 	},
808 	{
809 		.scheme = &base64url_scheme,
810 		.input =
811 			"0JPQvtCy0L7RgNGPzIHRgiwg0YfRgt"
812 			"C-INC60YPRgCDQtNC-0Y_MgdGCLg==",
813 		.output =
814 			"\xd0\x93\xd0\xbe\xd0\xb2\xd0\xbe\xd1\x80\xd1\x8f\xcc"
815 			"\x81\xd1\x82\x2c\x20\xd1\x87\xd1\x82\xd0\xbe\x20\xd0"
816 			"\xba\xd1\x83\xd1\x80\x20\xd0\xb4\xd0\xbe\xd1\x8f\xcc"
817 			"\x81\xd1\x82\x2e",
818 		.ret = 0,
819 		.src_pos = UINT_MAX,
820 	},
821 	{
822 		.scheme = &base64_scheme,
823 		.flags = BASE64_DECODE_FLAG_NO_PADDING,
824 		.input =
825 			"0JPQvtCy0L7RgNGPzIHRgiwg0YfRgt"
826 			"C+INC60YPRgCDQtNC+0Y/MgdGCLg",
827 		.output =
828 			"\xd0\x93\xd0\xbe\xd0\xb2\xd0\xbe\xd1\x80\xd1\x8f\xcc"
829 			"\x81\xd1\x82\x2c\x20\xd1\x87\xd1\x82\xd0\xbe\x20\xd0"
830 			"\xba\xd1\x83\xd1\x80\x20\xd0\xb4\xd0\xbe\xd1\x8f\xcc"
831 			"\x81\xd1\x82\x2e",
832 		.ret = 0,
833 		.src_pos = UINT_MAX,
834 	},
835 	{
836 		.scheme = &base64_scheme,
837 		.flags = BASE64_DECODE_FLAG_IGNORE_PADDING,
838 		.input =
839 			"0JPQvtCy0L7RgNGPzIHRgiwg0YfRgt"
840 			"C+INC60YPRgCDQtNC+0Y/MgdGCLg",
841 		.output =
842 			"\xd0\x93\xd0\xbe\xd0\xb2\xd0\xbe\xd1\x80\xd1\x8f\xcc"
843 			"\x81\xd1\x82\x2c\x20\xd1\x87\xd1\x82\xd0\xbe\x20\xd0"
844 			"\xba\xd1\x83\xd1\x80\x20\xd0\xb4\xd0\xbe\xd1\x8f\xcc"
845 			"\x81\xd1\x82\x2e",
846 		.ret = 0,
847 		.src_pos = UINT_MAX,
848 	},
849 	{
850 		.scheme = &base64_scheme,
851 		.flags = BASE64_DECODE_FLAG_IGNORE_PADDING,
852 		.input =
853 			"0JPQvtCy0L7RgNGPzIHRgiwg0YfRgt"
854 			"C+INC60YPRgCDQtNC+0Y/MgdGCLg==",
855 		.output =
856 			"\xd0\x93\xd0\xbe\xd0\xb2\xd0\xbe\xd1\x80\xd1\x8f\xcc"
857 			"\x81\xd1\x82\x2c\x20\xd1\x87\xd1\x82\xd0\xbe\x20\xd0"
858 			"\xba\xd1\x83\xd1\x80\x20\xd0\xb4\xd0\xbe\xd1\x8f\xcc"
859 			"\x81\xd1\x82\x2e",
860 		.ret = 0,
861 		.src_pos = UINT_MAX,
862 	},
863 };
864 
test_base64_decode_lowlevel(void)865 static void test_base64_decode_lowlevel(void)
866 {
867 	string_t *str;
868 	buffer_t buf;
869 	unsigned int i;
870 
871 	test_begin("base64 decode low-level");
872 	for (i = 0; i < N_ELEMENTS(tests_base64_decode_lowlevel); i++) {
873 		const struct test_base64_decode_lowlevel *test =
874 			&tests_base64_decode_lowlevel[i];
875 		struct base64_decoder dec;
876 		size_t src_pos;
877 		int ret;
878 
879 		/* Some of the base64_decode() callers use fixed size buffers.
880 		   Use a fixed size buffer here as well to test that
881 		   base64_decode() can't allocate any extra space even
882 		   temporarily. */
883 		size_t max_decoded_size =
884 			MAX_BASE64_DECODED_SIZE(strlen(test->input));
885 
886 		buffer_create_from_data(&buf,
887 					(max_decoded_size == 0 ? "" :
888 					 t_malloc0(max_decoded_size)),
889 					max_decoded_size);
890 		str = &buf;
891 		base64_decode_init(&dec, test->scheme, test->flags);
892 		ret = base64_decode_more(&dec, test->input, strlen(test->input),
893 					 &src_pos, str);
894 		if (ret >= 0)
895 			ret = base64_decode_finish(&dec);
896 
897 		test_assert_idx(ret == test->ret, i);
898 		test_assert_idx(strlen(test->output) == str_len(str) &&
899 				memcmp(test->output, str_data(str),
900 				       str_len(str)) == 0, i);
901 		test_assert_idx(src_pos == test->src_pos ||
902 				(test->src_pos == UINT_MAX &&
903 				src_pos == strlen(test->input)), i);
904 		if (ret >= 0) {
905 			test_assert_idx(
906 				str_len(str) <= MAX_BASE64_DECODED_SIZE(
907 					strlen(test->input)), i);
908 		}
909 	}
910 	test_end();
911 }
912 
913 static void
test_base64_random_lowlevel_one_block(const struct base64_scheme * b64,enum base64_encode_flags enc_flags,enum base64_decode_flags dec_flags,size_t max_line_len,unsigned int test_idx,const unsigned char * in_buf,size_t in_buf_size,buffer_t * buf1,buffer_t * buf2)914 test_base64_random_lowlevel_one_block(const struct base64_scheme *b64,
915 				      enum base64_encode_flags enc_flags,
916 				      enum base64_decode_flags dec_flags,
917 				      size_t max_line_len,
918 				      unsigned int test_idx,
919 				      const unsigned char *in_buf,
920 				      size_t in_buf_size,
921 				      buffer_t *buf1, buffer_t *buf2)
922 {
923 	struct base64_encoder enc;
924 	struct base64_decoder dec;
925 	void *space;
926 	size_t enc_size;
927 	buffer_t buf;
928 	int ret;
929 
930 	buffer_set_used_size(buf1, 0);
931 	buffer_set_used_size(buf2, 0);
932 
933 	base64_encode_init(&enc, b64, enc_flags, max_line_len);
934 	enc_size = base64_get_full_encoded_size(&enc, in_buf_size);
935 	space = buffer_append_space_unsafe(buf1, enc_size);
936 	buffer_create_from_data(&buf, space, enc_size);
937 
938 	if (!base64_encode_more(&enc, in_buf, in_buf_size, NULL, &buf))
939 		test_assert_idx(FALSE, test_idx);
940 	if (!base64_encode_finish(&enc, &buf))
941 		test_assert_idx(FALSE, test_idx);
942 
943 	test_assert(base64_get_full_encoded_size(&enc, in_buf_size) ==
944 		    buf1->used);
945 
946 	base64_decode_init(&dec, b64, dec_flags);
947 	space = buffer_append_space_unsafe(buf2, in_buf_size);
948 	buffer_create_from_data(&buf, space, in_buf_size);
949 
950 	ret = base64_decode_more(&dec, buf1->data, buf1->used, NULL, &buf);
951 	if (ret >= 0)
952 		ret = base64_decode_finish(&dec);
953 
954 	test_assert_idx(ret >= 0, test_idx);
955 	test_assert_idx(buf2->used == in_buf_size &&
956 			memcmp(in_buf, buf2->data, in_buf_size) == 0, test_idx);
957 }
958 
959 static void
test_base64_random_lowlevel_stream(const struct base64_scheme * b64,enum base64_encode_flags enc_flags,enum base64_decode_flags dec_flags,size_t max_line_len,unsigned int test_idx,const unsigned char * in_buf,size_t in_buf_size,buffer_t * buf1,buffer_t * buf2,size_t chunk_size)960 test_base64_random_lowlevel_stream(const struct base64_scheme *b64,
961 				   enum base64_encode_flags enc_flags,
962 				   enum base64_decode_flags dec_flags,
963 				   size_t max_line_len, unsigned int test_idx,
964 				   const unsigned char *in_buf,
965 				   size_t in_buf_size,
966 				   buffer_t *buf1, buffer_t *buf2,
967 				   size_t chunk_size)
968 {
969 	struct base64_encoder enc;
970 	struct base64_decoder dec;
971 	const unsigned char *buf_p, *buf_begin, *buf_end;
972 	int ret;
973 	size_t out_space, out_full_size;
974 	void *out_data;
975 	buffer_t out;
976 
977 	/* Encode */
978 
979 	buffer_set_used_size(buf1, 0);
980 
981 	buf_begin = in_buf;
982 	buf_end = buf_begin + in_buf_size;
983 
984 	base64_encode_init(&enc, b64, enc_flags, max_line_len);
985 	out_full_size = base64_get_full_encoded_size(&enc, in_buf_size);
986 	out_space = 0;
987 	for (buf_p = buf_begin; buf_p < buf_end; ) {
988 		size_t buf_ch, out_ch;
989 		size_t left = (buf_end - buf_p);
990 		size_t used = buf1->used;
991 		size_t src_pos, out_size, src_full_space;
992 		bool eres;
993 
994 		if (chunk_size == 0) {
995 			buf_ch = i_rand_limit(32);
996 			out_ch = i_rand_limit(32);
997 		} else {
998 			buf_ch = chunk_size;
999 			out_ch = chunk_size;
1000 		}
1001 
1002 		out_space += out_ch;
1003 		out_data = buffer_append_space_unsafe(buf1, out_space);
1004 		buffer_create_from_data(&out, out_data, out_space);
1005 
1006 		if (buf_ch > left)
1007 			buf_ch = left;
1008 
1009 		src_full_space = base64_encode_get_full_space(
1010 			&enc, out_full_size - used);
1011 		test_assert_idx(src_full_space >= (size_t)(buf_end - buf_p),
1012 				test_idx);
1013 
1014 		out_size = base64_encode_get_size(&enc, buf_ch);
1015 
1016 		eres = base64_encode_more(&enc, buf_p, buf_ch, &src_pos, &out);
1017 		test_assert_idx((eres && src_pos == buf_ch) ||
1018 				(!eres && src_pos < buf_ch), test_idx);
1019 		test_assert_idx(out.used <= out_size, test_idx);
1020 		buf_p += src_pos;
1021 		i_assert(out_space >= out.used);
1022 		out_space -= out.used;
1023 		buffer_set_used_size(buf1, used + out.used);
1024 	}
1025 	test_assert_idx(base64_encode_finish(&enc, buf1), test_idx);
1026 
1027 	/* Verify encode */
1028 
1029 	test_assert(out_full_size == buf1->used);
1030 
1031 	buffer_set_used_size(buf2, 0);
1032 	base64_encode_init(&enc, b64, enc_flags, max_line_len);
1033 	test_assert_idx(base64_encode_more(&enc, in_buf, in_buf_size,
1034 					   NULL, buf2), test_idx);
1035 	test_assert_idx(base64_encode_finish(&enc, buf2), test_idx);
1036 	test_assert_idx(buffer_cmp(buf1, buf2), test_idx);
1037 
1038 	/* Decode */
1039 
1040 	buffer_set_used_size(buf2, 0);
1041 
1042 	buf_begin = buf1->data;
1043 	buf_end = buf_begin + buf1->used;
1044 
1045 	base64_decode_init(&dec, b64, dec_flags);
1046 	ret = 1;
1047 	out_space = 0;
1048 	for (buf_p = buf_begin; buf_p < buf_end; ) {
1049 		size_t buf_ch, out_ch;
1050 		size_t left = (buf_end - buf_p);
1051 		size_t used = buf2->used;
1052 		size_t src_pos;
1053 
1054 		if (chunk_size == 0) {
1055 			buf_ch = i_rand_limit(32);
1056 			out_ch = i_rand_limit(32);
1057 		} else {
1058 			buf_ch = chunk_size;
1059 			out_ch = chunk_size;
1060 		}
1061 
1062 		out_space += out_ch;
1063 		out_data = buffer_append_space_unsafe(buf2, out_space);
1064 		buffer_create_from_data(&out, out_data, out_space);
1065 
1066 		if (buf_ch > left)
1067 			buf_ch = left;
1068 		ret = base64_decode_more(&dec, buf_p, buf_ch,
1069 					 &src_pos, &out);
1070 		test_assert_idx(ret >= 0, test_idx);
1071 		if (ret < 0) {
1072 			break;
1073 		}
1074 		buf_p += src_pos;
1075 		i_assert(out_space >= out.used);
1076 		out_space -= out.used;
1077 		buffer_set_used_size(buf2, used + out.used);
1078 	}
1079 	test_assert_idx(ret >= 0, test_idx);
1080 
1081 	/* Verify decode */
1082 	if (ret > 0) {
1083 		ret = base64_decode_finish(&dec);
1084 		test_assert_idx(ret == 0, test_idx);
1085 		test_assert_idx(buf2->used == in_buf_size &&
1086 				memcmp(in_buf, buf2->data, in_buf_size) == 0,
1087 				test_idx);
1088 	}
1089 }
1090 
1091 static void
test_base64_random_lowlevel_case(const struct base64_scheme * b64,enum base64_encode_flags enc_flags,enum base64_decode_flags dec_flags,size_t max_line_len)1092 test_base64_random_lowlevel_case(const struct base64_scheme *b64,
1093 				 enum base64_encode_flags enc_flags,
1094 				 enum base64_decode_flags dec_flags,
1095 				 size_t max_line_len)
1096 {
1097 	unsigned char in_buf[512];
1098 	size_t in_buf_size;
1099 	buffer_t *buf1, *buf2;
1100 	unsigned int i, j;
1101 
1102 	if (test_has_failed())
1103 		return;
1104 
1105 	buf1 = t_buffer_create(MAX_BASE64_ENCODED_SIZE(sizeof(in_buf)));
1106 	buf2 = t_buffer_create(MAX_BASE64_ENCODED_SIZE(sizeof(in_buf)));
1107 
1108 	/* one block */
1109 	for (i = 0; i < 1000; i++) {
1110 		in_buf_size = i_rand_limit(sizeof(in_buf));
1111 		for (j = 0; j < in_buf_size; j++)
1112 			in_buf[j] = i_rand_uchar();
1113 
1114 		test_base64_random_lowlevel_one_block(b64, enc_flags, dec_flags,
1115 						      max_line_len, i,
1116 						      in_buf, in_buf_size,
1117 						      buf1, buf2);
1118 
1119 		if (test_has_failed()) {
1120 			i_info("One block test failed ("
1121 			       "enc_flags=%02x dec_flags=%02x "
1122 			       "max_line_len=%zu size=%zu)",
1123 				enc_flags, dec_flags, max_line_len,
1124 				in_buf_size);
1125 			return;
1126 		}
1127 	}
1128 
1129 	/* streaming; single-byte trickle */
1130 	for (i = 0; i < 1000; i++) {
1131 		in_buf_size = i_rand_limit(sizeof(in_buf));
1132 		for (j = 0; j < in_buf_size; j++)
1133 			in_buf[j] = i_rand_uchar();
1134 
1135 		test_base64_random_lowlevel_stream(b64, enc_flags, dec_flags,
1136 						   max_line_len, i,
1137 						   in_buf, in_buf_size,
1138 						   buf1, buf2, 1);
1139 
1140 		if (test_has_failed()) {
1141 			i_info("Streaming single-byte trickle test failed ("
1142 			       "enc_flags=%02x dec_flags=%02x "
1143 			       "max_line_len=%zu size=%zu)",
1144 				enc_flags, dec_flags, max_line_len,
1145 				in_buf_size);
1146 			return;
1147 		}
1148 	}
1149 
1150 	/* streaming; random chunks */
1151 	for (i = 0; i < 1000; i++) {
1152 		in_buf_size = i_rand_limit(sizeof(in_buf));
1153 		for (j = 0; j < in_buf_size; j++)
1154 			in_buf[j] = i_rand_uchar();
1155 
1156 		test_base64_random_lowlevel_stream(b64, enc_flags, dec_flags,
1157 						   max_line_len, i,
1158 						   in_buf, in_buf_size,
1159 						   buf1, buf2, 0);
1160 		if (test_has_failed()) {
1161 			i_info("Streaming random chunks test failed ("
1162 			       "enc_flags=%02x dec_flags=%02x "
1163 			       "max_line_len=%zu size=%zu)",
1164 				enc_flags, dec_flags, max_line_len,
1165 				in_buf_size);
1166 			return;
1167 		}
1168 	}
1169 }
1170 
1171 static void
test_base64_random_lowlevel(void)1172 test_base64_random_lowlevel(void)
1173 {
1174 	test_begin("base64 encode/decode low-level with random input");
1175 	test_base64_random_lowlevel_case(&base64_scheme, 0, 0, 0);
1176 	test_base64_random_lowlevel_case(&base64url_scheme, 0, 0, 0);
1177 	test_base64_random_lowlevel_case(&base64_scheme, 0,
1178 					 BASE64_DECODE_FLAG_EXPECT_BOUNDARY, 0);
1179 	test_base64_random_lowlevel_case(&base64url_scheme, 0,
1180 					 BASE64_DECODE_FLAG_EXPECT_BOUNDARY, 0);
1181 	test_base64_random_lowlevel_case(&base64_scheme, 0,
1182 					 BASE64_DECODE_FLAG_NO_WHITESPACE, 0);
1183 	test_base64_random_lowlevel_case(&base64url_scheme, 0,
1184 					 BASE64_DECODE_FLAG_NO_WHITESPACE, 0);
1185 	test_base64_random_lowlevel_case(&base64_scheme, 0, 0, 10);
1186 	test_base64_random_lowlevel_case(&base64url_scheme, 0, 0, 10);
1187 	test_base64_random_lowlevel_case(&base64_scheme,
1188 					 BASE64_ENCODE_FLAG_CRLF, 0, 10);
1189 	test_base64_random_lowlevel_case(&base64url_scheme,
1190 					 BASE64_ENCODE_FLAG_CRLF, 0, 10);
1191 	test_base64_random_lowlevel_case(&base64_scheme,
1192 					 BASE64_ENCODE_FLAG_NO_PADDING,
1193 					 BASE64_DECODE_FLAG_NO_PADDING, 0);
1194 	test_base64_random_lowlevel_case(&base64url_scheme,
1195 					 BASE64_ENCODE_FLAG_NO_PADDING,
1196 					 BASE64_DECODE_FLAG_NO_PADDING, 0);
1197 	test_base64_random_lowlevel_case(&base64_scheme,
1198 					 BASE64_ENCODE_FLAG_NO_PADDING |
1199 					 BASE64_ENCODE_FLAG_CRLF,
1200 					 BASE64_DECODE_FLAG_NO_PADDING, 15);
1201 	test_base64_random_lowlevel_case(&base64url_scheme,
1202 					 BASE64_ENCODE_FLAG_NO_PADDING |
1203 					 BASE64_ENCODE_FLAG_CRLF,
1204 					 BASE64_DECODE_FLAG_NO_PADDING, 15);
1205 	test_base64_random_lowlevel_case(&base64_scheme,
1206 					 BASE64_ENCODE_FLAG_NO_PADDING |
1207 					 BASE64_ENCODE_FLAG_CRLF,
1208 					 BASE64_DECODE_FLAG_NO_PADDING, 1);
1209 	test_end();
1210 }
1211 
1212 static void
_add_lines(const char * in,size_t max_line_len,bool crlf,string_t * out)1213 _add_lines(const char *in, size_t max_line_len, bool crlf, string_t *out)
1214 {
1215 	size_t in_len = strlen(in);
1216 
1217 	while (max_line_len > 0 && in_len > max_line_len) {
1218 		str_append_data(out, in, max_line_len);
1219 		if (crlf)
1220 			str_append(out, "\r\n");
1221 		else
1222 			str_append_c(out, '\n');
1223 		in += max_line_len;
1224 		in_len -= max_line_len;
1225 	}
1226 
1227 	str_append_data(out, in, in_len);
1228 }
1229 
test_base64_encode_lines(void)1230 static void test_base64_encode_lines(void)
1231 {
1232 	static const char *input[] = {
1233 		"Passer, deliciae meae puellae,\n"
1234 		"quicum ludere, quem in sinu tenere,\n"
1235 		"cui primum digitum dare appetenti\n"
1236 		"et acris solet incitare morsus,\n"
1237 		"cum desiderio meo nitenti\n"
1238 		"carum nescio quid lubet iocari,\n"
1239 		"credo ut, cum gravis acquiescet ardor,\n"
1240 		"sit solaciolum sui doloris,\n"
1241 		"tecum ludere sicut ipsa possem\n"
1242 		"et tristis animi levare curas!\n"
1243 	};
1244 	static const char *output[] = {
1245 		"UGFzc2VyLCBkZWxpY2lhZSBtZWFlIHB1ZWxsYWUsCnF1aWN1"
1246 		"bSBsdWRlcmUsIHF1ZW0gaW4gc2ludSB0ZW5lcmUsCmN1aSBw"
1247 		"cmltdW0gZGlnaXR1bSBkYXJlIGFwcGV0ZW50aQpldCBhY3Jp"
1248 		"cyBzb2xldCBpbmNpdGFyZSBtb3JzdXMsCmN1bSBkZXNpZGVy"
1249 		"aW8gbWVvIG5pdGVudGkKY2FydW0gbmVzY2lvIHF1aWQgbHVi"
1250 		"ZXQgaW9jYXJpLApjcmVkbyB1dCwgY3VtIGdyYXZpcyBhY3F1"
1251 		"aWVzY2V0IGFyZG9yLApzaXQgc29sYWNpb2x1bSBzdWkgZG9s"
1252 		"b3JpcywKdGVjdW0gbHVkZXJlIHNpY3V0IGlwc2EgcG9zc2Vt"
1253 		"CmV0IHRyaXN0aXMgYW5pbWkgbGV2YXJlIGN1cmFzIQo=",
1254 	};
1255 	string_t *out_test, *out_ref;
1256 	unsigned int i, n;
1257 
1258 	out_test = t_str_new(256);
1259 	out_ref = t_str_new(256);
1260 
1261 	test_begin("base64 encode lines (LF)");
1262 	for (i = 0; i < N_ELEMENTS(input); i++) {
1263 		struct base64_encoder b64enc;
1264 
1265 		for (n = 0; n <= 80; n++) {
1266 			str_truncate(out_test, 0);
1267 			base64_encode_init(&b64enc, &base64_scheme, 0, n);
1268 			base64_encode_more(&b64enc, input[i], strlen(input[i]),
1269 					   NULL, out_test);
1270 			base64_encode_finish(&b64enc, out_test);
1271 
1272 			str_truncate(out_ref, 0);
1273 			_add_lines(output[i], n, FALSE, out_ref);
1274 
1275 			test_assert(strcmp(str_c(out_ref),
1276 					   str_c(out_test)) == 0);
1277 
1278 		}
1279 	}
1280 	test_end();
1281 
1282 	test_begin("base64 encode lines (CRLF)");
1283 	for (i = 0; i < N_ELEMENTS(input); i++) {
1284 		struct base64_encoder b64enc;
1285 
1286 		for (n = 0; n <= 80; n++) {
1287 			str_truncate(out_test, 0);
1288 			base64_encode_init(&b64enc, &base64_scheme,
1289 					   BASE64_ENCODE_FLAG_CRLF, n);
1290 			base64_encode_more(&b64enc, input[i], strlen(input[i]),
1291 					   NULL, out_test);
1292 			base64_encode_finish(&b64enc, out_test);
1293 
1294 			str_truncate(out_ref, 0);
1295 			_add_lines(output[i], n, TRUE, out_ref);
1296 
1297 			test_assert(strcmp(str_c(out_ref),
1298 					   str_c(out_test)) == 0);
1299 
1300 		}
1301 	}
1302 	test_end();
1303 }
1304 
test_base64(void)1305 void test_base64(void)
1306 {
1307 	test_base64_encode();
1308 	test_base64_decode();
1309 	test_base64_random();
1310 	test_base64url_encode();
1311 	test_base64url_decode();
1312 	test_base64url_random();
1313 	test_base64_encode_lowlevel();
1314 	test_base64_decode_lowlevel();
1315 	test_base64_random_lowlevel();
1316 	test_base64_encode_lines();
1317 }
1318