xref: /openbsd-src/regress/lib/libcrypto/base64/base64test.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*
2  * Copyright (c) 2014 Joel Sing <jsing@openbsd.org>
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16 
17 #include <openssl/bio.h>
18 #include <openssl/evp.h>
19 
20 #include <err.h>
21 #include <stdio.h>
22 #include <string.h>
23 #include <sys/types.h>
24 
25 #define BUF_SIZE 128
26 
27 struct base64_test {
28 	const unsigned char in[BUF_SIZE];
29 	const ssize_t in_len;
30 	const unsigned char out[BUF_SIZE];
31 	const ssize_t out_len;
32 	const ssize_t valid_len;
33 };
34 
35 /*
36  * Many of these tests are based on those found in Go's encoding/base64 tests.
37  */
38 struct base64_test base64_tests[] = {
39 
40 	/* RFC3548 examples. */
41 	{ "\x14\xfb\x9c\x03\xd9\x7e", 6, "FPucA9l+", 8, 6, },
42 	{ "\x14\xfb\x9c\x03\xd9", 5, "FPucA9k=", 8, 5, },
43 	{ "\x14\xfb\x9c\x03", 4, "FPucAw==", 8, 4, },
44 
45 	/* RFC4648 examples. */
46 	{ "", 0, "", 0, 0, },
47 	{ "f", 1, "Zg==", 4, 1, },
48 	{ "fo", 2, "Zm8=", 4, 2, },
49 	{ "foo", 3, "Zm9v", 4, 3, },
50 	{ "foob", 4, "Zm9vYg==", 8, 4, },
51 	{ "fooba", 5, "Zm9vYmE=", 8, 5, },
52 	{ "foobar", 6, "Zm9vYmFy", 8, 6, },
53 
54 	/* Wikipedia examples. */
55 	{ "sure.", 5, "c3VyZS4=", 8, 5, },
56 	{ "sure", 4, "c3VyZQ==", 8, 4, },
57 	{ "sur", 3, "c3Vy", 4, 3, },
58 	{ "su", 2, "c3U=", 4, 2, },
59 	{ "leasure.", 8, "bGVhc3VyZS4=", 12, 8, },
60 	{ "easure.", 7, "ZWFzdXJlLg==", 12, 7, },
61 	{ "asure.", 6, "YXN1cmUu", 8, 6, },
62 
63 	{ "abcd", 4, "YWJjZA==", 8, 4, },
64 
65 	{
66 		"Twas brillig, and the slithy toves",
67 		34,
68 		"VHdhcyBicmlsbGlnLCBhbmQgdGhlIHNsaXRoeSB0b3Zlcw==",
69 		48,
70 		34,
71 	},
72 };
73 
74 #define N_TESTS (sizeof(base64_tests) / sizeof(*base64_tests))
75 
76 struct base64_test base64_nl_tests[] = {
77 
78 	/* Corrupt/invalid encodings. */
79 	{ "", -1, "", 0, 0, },
80 	{ "", -1, "!!!!", 4, 0, },
81 	{ "", -1, "====", 4, 0, },
82 	{ "", -1, "x===", 4, 0, },
83 	{ "", -1, "=AAA", 4, 0, },
84 	{ "", -1, "A=AA", 4, 0, },
85 	{ "", -1, "AA=A", 4, 0, },
86 	{ "", -1, "AA==A", 5, 0, },
87 	{ "", -1, "AAA=AAAA", 8, 0, },
88 	{ "", -1, "AAAAA", 5, 0, },
89 	{ "", -1, "AAAAAA", 6, 0, },
90 	{ "", -1, "A=", 2, 0, },
91 	{ "", -1, "A==", 3, 0, },
92 	{ "", -1, "AA=", 3, 0, },
93 	{ "", -1, "AA==", 4, 1, },		/* XXX - output ix 0x0. */
94 	{ "", -1, "AAA=", 4, 2, },		/* XXX - output ix 2x 0x0. */
95 	{ "", -1, "AAAA", 4, 3, },		/* XXX - output ix 3x 0x0. */
96 	{ "", -1, "AAAAAA=", 7, 0, },
97 	{ "", -1, "YWJjZA=====", 11, 0, },
98 
99 
100 	/* Encodings with embedded CR/LF. */
101 	{ "sure", 4, "c3VyZQ==", 8, 4, },
102 	{ "sure", 4, "c3VyZQ==\r", 9, 4, },
103 	{ "sure", 4, "c3VyZQ==\n", 9, 4, },
104 	{ "sure", 4, "c3VyZQ==\r\n", 10, 4, },
105 	{ "sure", 4, "c3VyZ\r\nQ==", 10, 4, },
106 	{ "sure", 4, "c3V\ryZ\nQ==", 10, 4, },
107 	{ "sure", 4, "c3V\nyZ\rQ==", 10, 4, },
108 	{ "sure", 4, "c3VyZ\nQ==", 9, 4, },
109 	{ "sure", 4, "c3VyZQ\n==", 9, 4, },
110 	{ "sure", 4, "c3VyZQ=\n=", 9, 4, },
111 	{ "sure", 4, "c3VyZQ=\r\n\r\n=", 12, 4, },
112 
113 	{
114 		"",
115 		-1,
116 		"YWJjZA======================================================"
117 		"============",
118 		74,
119 		0,
120 	},
121 };
122 
123 #define N_NL_TESTS (sizeof(base64_nl_tests) / sizeof(*base64_nl_tests))
124 
125 struct base64_test base64_no_nl_tests[] = {
126 
127 	/*
128 	 * In non-newline mode, the output resulting from corrupt/invalid
129 	 * encodings is completely crazy. A number of zero bytes is returned
130 	 * rather than nothing.
131 	 */
132 
133 	/* Corrupt/invalid encodings. */
134 	{ "", -1, "", 0, 0, },
135 	{ "", -1, "!!!!", 4, 0, },
136 	{ "", -1, "====", 4, 1, },
137 	{ "", -1, "x===", 4, 1, },
138 	{ "", -1, "=AAA", 4, 3, },
139 	{ "", -1, "A=AA", 4, 3, },
140 	{ "", -1, "AA=A", 4, 3, },
141 	{ "", -1, "AA==A", 5, 1, },
142 	{ "", -1, "AAA=AAAA", 8, 6, },
143 	{ "", -1, "AAAAA", 5, 3, },
144 	{ "", -1, "AAAAAA", 6, 3, },
145 	{ "", -1, "A=", 2, 0, },
146 	{ "", -1, "A==", 3, 0, },
147 	{ "", -1, "AA=", 3, 0, },
148 	{ "", -1, "AA==", 4, 1, },
149 	{ "", -1, "AAA=", 4, 2, },
150 	{ "", -1, "AAAA", 4, 3, },
151 	{ "", -1, "AAAAAA=", 7, 3, },
152 	{ "", -1, "YWJjZA=====", 11, 4, },
153 
154 	/* Encodings with embedded CR/LF. */
155 	{ "sure", 4, "c3VyZQ==", 8, 4, },
156 	{ "sure", 4, "c3VyZQ==\r", 9, 4, },
157 	{ "sure", 4, "c3VyZQ==\n", 9, 4, },
158 	{ "sure", 4, "c3VyZQ==\r\n", 10, 4, },
159 	{ "sure", -1, "c3VyZ\r\nQ==", 10, 0, },
160 	{ "sure", -1, "c3V\ryZ\nQ==", 10, 0, },
161 	{ "sure", -1, "c3V\nyZ\rQ==", 10, 0, },
162 	{ "sure", -1, "c3VyZ\nQ==", 9, 0, },
163 	{ "sure", -1, "c3VyZQ\n==", 9, 0, },
164 	{ "sure", -1, "c3VyZQ=\n=", 9, 0, },
165 	{ "sure", -1, "c3VyZQ=\r\n\r\n=", 12, 0, },
166 
167 	/*
168 	 * This is invalid, yet results in 'abcd' followed by a stream of
169 	 * zero value bytes.
170 	 */
171 	{
172 		"",
173 		-1,
174 		"YWJjZA======================================================"
175 		"============",
176 		74,
177 		52,
178 	},
179 };
180 
181 #define N_NO_NL_TESTS (sizeof(base64_no_nl_tests) / sizeof(*base64_no_nl_tests))
182 
183 static int
184 base64_encoding_test(int test_no, struct base64_test *bt, int test_nl)
185 {
186 	BIO *bio_b64, *bio_mem;
187 	unsigned char *buf, *out;
188 	ssize_t i, len, b64len;
189 	int failure = 0;
190 
191 	buf = malloc(BUF_SIZE);
192 	if (buf == NULL)
193 		errx(1, "malloc");
194 
195 	bio_b64 = BIO_new(BIO_f_base64());
196 	if (bio_b64 == NULL)
197 		errx(1, "BIO_new failed for BIO_f_base64");
198 
199 	bio_mem = BIO_new(BIO_s_mem());
200 	if (bio_mem == NULL)
201 		errx(1, "BIO_new failed for BIO_s_mem");
202 
203 	bio_mem = BIO_push(bio_b64, bio_mem);
204 
205 	if (!test_nl)
206 		BIO_set_flags(bio_b64, BIO_FLAGS_BASE64_NO_NL);
207 
208 	len = BIO_write(bio_mem, bt->in, bt->in_len);
209 	if (len != bt->in_len) {
210 		fprintf(stderr, "FAIL: test %i - only wrote %zi out of %zi "
211 		    "characters\n", test_no, len, bt->in_len);
212 		failure = 1;
213 		goto done;
214 	}
215 	if (BIO_flush(bio_mem) < 0) {
216 		fprintf(stderr, "FAIL: test %i - flush failed\n", test_no);
217 		failure = 1;
218 		goto done;
219 	}
220 
221 	b64len = 0;
222 	for (i = 0; i < bt->out_len; i++) {
223 		if (bt->out[i] == '\r' || bt->out[i] == '\n')
224 			continue;
225 		buf[b64len++] = bt->out[i];
226 	}
227 	if (test_nl)
228 		buf[b64len++] = '\n';
229 
230 	len = BIO_get_mem_data(bio_mem, &out);
231 
232 	/* An empty string with NL results in no output, rather than '\n'. */
233 	if (test_nl && b64len == 1 && len == 0)
234 		goto done;
235 
236 	if (len != b64len) {
237 		fprintf(stderr, "FAIL: test %i - encoding resulted in %zi "
238 		    "characters instead of %zi\n", test_no, len, b64len);
239 		failure = 1;
240 		goto done;
241 	}
242 
243 	if (memcmp(buf, out, b64len) != 0) {
244 		fprintf(stderr, "FAIL: test %i - encoding differs:\n", test_no);
245 		fprintf(stderr, "  encoding: ");
246 		for (i = 0; i < len; i++)
247 			fprintf(stderr, "%c", out[i]);
248 		fprintf(stderr, "\n");
249 		fprintf(stderr, " test data: ");
250 		for (i = 0; i < bt->out_len; i++)
251 			fprintf(stderr, "%c", buf[i]);
252 		fprintf(stderr, "\n");
253 		failure = 1;
254 	}
255 
256 done:
257 	BIO_free_all(bio_mem);
258 	free(buf);
259 
260 	return failure;
261 }
262 
263 static int
264 base64_decoding_test(int test_no, struct base64_test *bt, int test_nl)
265 {
266 	BIO *bio_b64, *bio_mem;
267 	char *buf, *input;
268 	ssize_t i, inlen, len;
269 	int failure = 0;
270 
271 	buf = malloc(BUF_SIZE);
272 	if (buf == NULL)
273 		errx(1, "malloc");
274 
275 	input = (char *)bt->out;
276 	inlen = bt->out_len;
277 
278 	if (test_nl)
279 		inlen = asprintf(&input, "%s\r\n", bt->out);
280 
281 	bio_mem = BIO_new_mem_buf(input, inlen);
282 	if (bio_mem == NULL)
283 		errx(1, "BIO_new_mem_buf failed");
284 
285 	bio_b64 = BIO_new(BIO_f_base64());
286 	if (bio_b64 == NULL)
287 		errx(1, "BIO_new failed for BIO_f_base64");
288 
289 	if (!test_nl)
290 		BIO_set_flags(bio_b64, BIO_FLAGS_BASE64_NO_NL);
291 
292 	bio_mem = BIO_push(bio_b64, bio_mem);
293 
294 	/*
295 	 * If we wrote zero characters then a BIO_read will result in a return
296 	 * value of -1, hence we need to handle this case.
297 	 */
298 	len = BIO_read(bio_mem, buf, BUF_SIZE);
299 	if (len != bt->valid_len && (bt->in_len != 0 || len != -1)) {
300 		fprintf(stderr, "FAIL: test %i - decoding resulted in %zi "
301 		    "characters instead of %zi\n", test_no, len, bt->valid_len);
302 		fprintf(stderr, "  input: ");
303 		for (i = 0; i < inlen; i++)
304 			fprintf(stderr, "%c", input[i]);
305 		fprintf(stderr, "\n");
306 		fprintf(stderr, "  decoding: ");
307 		for (i = 0; i < len; i++)
308 			fprintf(stderr, "0x%x ", buf[i]);
309 		fprintf(stderr, "\n");
310 		failure = 1;
311 		goto done;
312 	}
313 
314 	/* See if we expect this to fail decoding. */
315 	if (bt->in_len == -1)
316 		goto done;
317 
318 	if (memcmp(bt->in, buf, bt->in_len) != 0) {
319 		fprintf(stderr, "FAIL: test %i - decoding differs:\n", test_no);
320 		fprintf(stderr, "  decoding: ");
321 		for (i = 0; i < len; i++)
322 			fprintf(stderr, "0x%x ", buf[i]);
323 		fprintf(stderr, "\n");
324 		fprintf(stderr, " test data: ");
325 		for (i = 0; i < inlen; i++)
326 			fprintf(stderr, "0x%x ", input[i]);
327 		fprintf(stderr, "\n");
328 		failure = 1;
329 	}
330 
331 done:
332 	BIO_free_all(bio_mem);
333 	free(buf);
334 	if (test_nl)
335 		free(input);
336 
337 	return failure;
338 }
339 
340 int
341 main(int argc, char **argv)
342 {
343 	struct base64_test *bt;
344 	int failed = 0;
345 	size_t i;
346 
347 	fprintf(stderr, "Starting combined tests...\n");
348 
349 	for (i = 0; i < N_TESTS; i++) {
350 		bt = &base64_tests[i];
351 		if (bt->in_len != -1)
352 			failed += base64_encoding_test(i, bt, 0);
353 		if (bt->out_len != -1)
354 			failed += base64_decoding_test(i, bt, 0);
355 		if (bt->in_len != -1)
356 			failed += base64_encoding_test(i, bt, 1);
357 		if (bt->out_len != -1)
358 			failed += base64_decoding_test(i, bt, 1);
359 	}
360 
361 	fprintf(stderr, "Starting NL tests...\n");
362 
363 	for (i = 0; i < N_NL_TESTS; i++) {
364 		bt = &base64_nl_tests[i];
365 
366 		if (bt->in_len != -1)
367 			failed += base64_encoding_test(i, bt, 1);
368 		if (bt->out_len != -1)
369 			failed += base64_decoding_test(i, bt, 1);
370 	}
371 
372 	fprintf(stderr, "Starting NO NL tests...\n");
373 
374 	for (i = 0; i < N_NO_NL_TESTS; i++) {
375 		bt = &base64_no_nl_tests[i];
376 
377 		if (bt->in_len != -1)
378 			failed += base64_encoding_test(i, bt, 0);
379 		if (bt->out_len != -1)
380 			failed += base64_decoding_test(i, bt, 0);
381 	}
382 
383 	return failed;
384 }
385