1 /* $OpenBSD: base64test.c,v 1.10 2022/09/05 21:06:31 tb Exp $ */
2 /*
3 * Copyright (c) 2014 Joel Sing <jsing@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #include <openssl/bio.h>
19 #include <openssl/evp.h>
20
21 #include <err.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <sys/types.h>
25
26 #define BUF_SIZE 128
27
28 struct base64_test {
29 const unsigned char in[BUF_SIZE];
30 const ssize_t in_len;
31 const unsigned char out[BUF_SIZE];
32 const ssize_t out_len;
33 const ssize_t valid_len;
34 };
35
36 /*
37 * Many of these tests are based on those found in Go's encoding/base64 tests.
38 */
39 struct base64_test base64_tests[] = {
40
41 /* RFC3548 examples. */
42 { "\x14\xfb\x9c\x03\xd9\x7e", 6, "FPucA9l+", 8, 6, },
43 { "\x14\xfb\x9c\x03\xd9", 5, "FPucA9k=", 8, 5, },
44 { "\x14\xfb\x9c\x03", 4, "FPucAw==", 8, 4, },
45
46 /* RFC4648 examples. */
47 { "", 0, "", 0, 0, },
48 { "f", 1, "Zg==", 4, 1, },
49 { "fo", 2, "Zm8=", 4, 2, },
50 { "foo", 3, "Zm9v", 4, 3, },
51 { "foob", 4, "Zm9vYg==", 8, 4, },
52 { "fooba", 5, "Zm9vYmE=", 8, 5, },
53 { "foobar", 6, "Zm9vYmFy", 8, 6, },
54
55 /* Wikipedia examples. */
56 { "sure.", 5, "c3VyZS4=", 8, 5, },
57 { "sure", 4, "c3VyZQ==", 8, 4, },
58 { "sur", 3, "c3Vy", 4, 3, },
59 { "su", 2, "c3U=", 4, 2, },
60 { "leasure.", 8, "bGVhc3VyZS4=", 12, 8, },
61 { "easure.", 7, "ZWFzdXJlLg==", 12, 7, },
62 { "asure.", 6, "YXN1cmUu", 8, 6, },
63
64 { "abcd", 4, "YWJjZA==", 8, 4, },
65
66 {
67 "Twas brillig, and the slithy toves",
68 34,
69 "VHdhcyBicmlsbGlnLCBhbmQgdGhlIHNsaXRoeSB0b3Zlcw==",
70 48,
71 34,
72 },
73 };
74
75 #define N_TESTS (sizeof(base64_tests) / sizeof(*base64_tests))
76
77 struct base64_test base64_nl_tests[] = {
78
79 /* Corrupt/invalid encodings. */
80 { "", -1, "", 0, 0, },
81 { "", -1, "!!!!", 4, 0, },
82 { "", -1, "====", 4, 0, },
83 { "", -1, "x===", 4, 0, },
84 { "", -1, "=AAA", 4, 0, },
85 { "", -1, "A=AA", 4, 0, },
86 { "", -1, "AA=A", 4, 0, },
87 { "", -1, "AA==A", 5, 0, },
88 { "", -1, "AAA=AAAA", 8, 0, },
89 { "", -1, "AAAAA", 5, 0, },
90 { "", -1, "AAAAAA", 6, 0, },
91 { "", -1, "A=", 2, 0, },
92 { "", -1, "A==", 3, 0, },
93 { "", -1, "AA=", 3, 0, },
94 { "", -1, "AA==", 4, 1, }, /* XXX - output ix 0x0. */
95 { "", -1, "AAA=", 4, 2, }, /* XXX - output ix 2x 0x0. */
96 { "", -1, "AAAA", 4, 3, }, /* XXX - output ix 3x 0x0. */
97 { "", -1, "AAAAAA=", 7, 0, },
98 { "", -1, "YWJjZA=====", 11, 0, },
99
100
101 /* Encodings with embedded CR/LF. */
102 { "sure", 4, "c3VyZQ==", 8, 4, },
103 { "sure", 4, "c3VyZQ==\r", 9, 4, },
104 { "sure", 4, "c3VyZQ==\n", 9, 4, },
105 { "sure", 4, "c3VyZQ==\r\n", 10, 4, },
106 { "sure", 4, "c3VyZ\r\nQ==", 10, 4, },
107 { "sure", 4, "c3V\ryZ\nQ==", 10, 4, },
108 { "sure", 4, "c3V\nyZ\rQ==", 10, 4, },
109 { "sure", 4, "c3VyZ\nQ==", 9, 4, },
110 { "sure", 4, "c3VyZQ\n==", 9, 4, },
111 { "sure", 4, "c3VyZQ=\n=", 9, 4, },
112 { "sure", 4, "c3VyZQ=\r\n\r\n=", 12, 4, },
113
114 {
115 "",
116 -1,
117 "YWJjZA======================================================"
118 "============",
119 74,
120 0,
121 },
122
123 /* OpenSSL-1.1.1d test */
124 /* canonical */
125 { "", 0, "", 0, 0, },
126 /* canonical */
127 { "h", 1, "aA==\n", 5, 1, },
128 /* canonical */
129 { "hello", 5, "aGVsbG8=\n", 9, 5, },
130 /* canonical */
131 { "hello world!", 12, "aGVsbG8gd29ybGQh\n", 17, 12, },
132 /* canonical */
133 { "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\xa0\xb0\xc0\xd0\xe0\xf0\x00", 17, "AAECAwQFBgcICaCwwNDg8AA=\n", 25, 17, },
134 /* invalid # Missing padding */
135 { "", -1, "aGVsbG8", 7, 0, },
136 /* invalid */
137 { "", -1, "aGVsbG8\n", 8, 0, },
138 /* valid # Tolerate missing newline */
139 { "hello", -1, "aGVsbG8=", 8, 5, },
140 /* invalid # Don't tolerate extra trailing '=' */
141 { "", -1, "aGVsbG8==\n", 10, 0, },
142 /* invalid */
143 { "", -1, "aGVsbG8===\n", 11, 0, },
144 /* invalid # Don't tolerate data after '=' */
145 { "", -1, "aGV=sbG8=\n", 10, 0, },
146 /* valid # Newlines are ignored */
147 { "hello", -1, "aGV\nsbG8=\n", 10, 5, },
148 /* canonical */
149 { "hello", 5, "\x61\x47\x56\x73\x62\x47\x38\x3d\x0a", 9, 5, },
150 /* invalid # Invalid characters */
151 { "", -1, "\x61\x47\x56\x73\x62\x47\x38\x3d\x0a\x00", 10, 0, },
152 /* invalid */
153 { "", -1, "\x61\x47\x56\x00\x73\x62\x47\x38\x3d\x0a", 10, 0, },
154 /* invalid */
155 { "", -1, "\x61\x47\x56\x01\x73\x62\x47\x38\x3d\x0a", 10, 0, },
156 /* invalid */
157 { "", -1, "\x61\x47\x56\x80\x73\x62\x47\x38\x3d\x0a", 10, 0, },
158 /* invalid */
159 { "", -1, "\xe1\x47\x56\x73\x62\x47\x38\x3d\x0a", 9, 0, },
160 /* canonical */
161 { "OpenSSLOpenSSL\n", 15, "T3BlblNTTE9wZW5TU0wK\n", 21, 15, },
162 /* valid */
163 { "OpenSSLOpenSSL\n", -1, "T3BlblNTTE9wZW5TU0wK", 20, 15, },
164 /* invalid # Truncate 1-3 chars */
165 { "", -1, "T3BlblNTTE9wZW5TU0w", 19, 0, },
166 /* invalid */
167 { "", -1, "T3BlblNTTE9wZW5TU0", 18, 0, },
168 /* invalid */
169 { "", -1, "T3BlblNTTE9wZW5TU", 17, 0, },
170 /* invalid */
171 { "", -1, "T3BlblNTTE9wZW5TU0wK====", 24, 0, },
172 /* invalid */
173 { "", -1, "T3BlblNTTE9wZW5TU0wK============================================\n", 65, 0, },
174 /* invalid */
175 { "", -1, "YQ==YQ==YQ==\n", 13, 0, },
176 /* invalid */
177 { "", -1, "A", 1, 0, },
178 /* invalid */
179 { "", -1, "A\n", 2, 0, },
180 /* invalid */
181 { "", -1, "A=", 2, 0, },
182 /* invalid */
183 { "", -1, "A==\n", 4, 0, },
184 /* invalid */
185 { "", -1, "A===\n", 5, 0, },
186 /* invalid */
187 { "", -1, "A====\n", 6, 0, },
188 /* valid */
189 { "OpenSSLOpenSSL\n", -1, "T3BlblNTTE9wZW5TU0wK\n\n", 22, 15, },
190 /* valid */
191 { "OpenSSLOpenSSL\n", -1, "T3BlblNTTE\n9wZW5TU0wK", 21, 15, },
192 /* invalid # CVE 2015-0292 */
193 { "", -1, "ZW5jb2RlIG1lCg==================================================================\n", 81, 0, },
194 /* canonical */
195 { "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", 46, "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eA==\n", 65, 46, },
196 /* valid */
197 { "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", -1, "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eA\n==\n", 66, 46, },
198 /* valid */
199 { "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", -1, "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eA=\n=\n", 66, 46, },
200 /* invalid */
201 { "", -1, "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eA====\n", 67, 0, },
202 /* canonical # Multiline output without padding */
203 { "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", 60, "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4\neHh4eHh4eHh4eHh4\n", 82, 60, },
204 /* canonical # Multiline output with padding */
205 { "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", 64, "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4\neHh4eHh4eHh4eHh4eHh4eA==\n", 90, 64, },
206 /* valid # Multiline output with line break in the middle of a b64 block is accepted */
207 { "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", -1, "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh\n4eHh4eHh4eHh4eHh4eHh4eA==\n", 90, 64, },
208 /* valid # Long lines are accepted */
209 { "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", -1, "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eA==\n", 89, 64, },
210 /* invalid # Multiline input with data after '='. */
211 { "", -1, "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eA==\neHh4eHh4eHh4eHh4eHh4eHh4\n", 90, 0, },
212 /* invalid */
213 { "", -1, "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4\neA==eHh4eHh4eHh4eHh4eHh4\n", 90, 0, },
214 /* valid # B64_EOF ('-') terminates input and trailing bytes are ignored */
215 { "OpenSSLOpenSSL\n", -1, "T3BlblNTTE9wZW5TU0wK\n-abcd", 26, 15, },
216 /* valid */
217 { "OpenSSLOpenSSL\n", -1, "T3BlblNTTE9wZW5TU0wK-abcd", 25, 15, },
218 };
219
220 #define N_NL_TESTS (sizeof(base64_nl_tests) / sizeof(*base64_nl_tests))
221
222 struct base64_test base64_no_nl_tests[] = {
223
224 /*
225 * In non-newline mode, the output resulting from corrupt/invalid
226 * encodings is completely crazy. A number of zero bytes is returned
227 * rather than nothing.
228 */
229
230 /* Corrupt/invalid encodings. */
231 { "", -1, "", 0, 0, },
232 { "", -1, "!!!!", 4, 0, },
233 { "", -1, "====", 4, 1, },
234 { "", -1, "x===", 4, 1, },
235 { "", -1, "=AAA", 4, 3, },
236 { "", -1, "A=AA", 4, 3, },
237 { "", -1, "AA=A", 4, 3, },
238 { "", -1, "AA==A", 5, 1, },
239 { "", -1, "AAA=AAAA", 8, 6, },
240 { "", -1, "AAAAA", 5, 3, },
241 { "", -1, "AAAAAA", 6, 3, },
242 { "", -1, "A=", 2, 0, },
243 { "", -1, "A==", 3, 0, },
244 { "", -1, "AA=", 3, 0, },
245 { "", -1, "AA==", 4, 1, },
246 { "", -1, "AAA=", 4, 2, },
247 { "", -1, "AAAA", 4, 3, },
248 { "", -1, "AAAAAA=", 7, 3, },
249 { "", -1, "YWJjZA=====", 11, 4, },
250
251 /* Encodings with embedded CR/LF. */
252 { "sure", 4, "c3VyZQ==", 8, 4, },
253 { "sure", 4, "c3VyZQ==\r", 9, 4, },
254 { "sure", 4, "c3VyZQ==\n", 9, 4, },
255 { "sure", 4, "c3VyZQ==\r\n", 10, 4, },
256 { "sure", -1, "c3VyZ\r\nQ==", 10, 0, },
257 { "sure", -1, "c3V\ryZ\nQ==", 10, 0, },
258 { "sure", -1, "c3V\nyZ\rQ==", 10, 0, },
259 { "sure", -1, "c3VyZ\nQ==", 9, 0, },
260 { "sure", -1, "c3VyZQ\n==", 9, 0, },
261 { "sure", -1, "c3VyZQ=\n=", 9, 0, },
262 { "sure", -1, "c3VyZQ=\r\n\r\n=", 12, 0, },
263
264 /*
265 * This is invalid, yet results in 'abcd' followed by a stream of
266 * zero value bytes.
267 */
268 {
269 "",
270 -1,
271 "YWJjZA======================================================"
272 "============",
273 74,
274 52,
275 },
276 };
277
278 #define N_NO_NL_TESTS (sizeof(base64_no_nl_tests) / sizeof(*base64_no_nl_tests))
279
280 static int
base64_encoding_test(int test_no,struct base64_test * bt,int test_nl)281 base64_encoding_test(int test_no, struct base64_test *bt, int test_nl)
282 {
283 BIO *bio_b64, *bio_mem;
284 unsigned char *buf, *out;
285 ssize_t i, len, b64len;
286 int failure = 0;
287
288 buf = malloc(BUF_SIZE);
289 if (buf == NULL)
290 errx(1, "malloc");
291
292 bio_b64 = BIO_new(BIO_f_base64());
293 if (bio_b64 == NULL)
294 errx(1, "BIO_new failed for BIO_f_base64");
295
296 bio_mem = BIO_new(BIO_s_mem());
297 if (bio_mem == NULL)
298 errx(1, "BIO_new failed for BIO_s_mem");
299
300 bio_mem = BIO_push(bio_b64, bio_mem);
301
302 if (!test_nl)
303 BIO_set_flags(bio_b64, BIO_FLAGS_BASE64_NO_NL);
304
305 len = BIO_write(bio_mem, bt->in, bt->in_len);
306 if (len != bt->in_len) {
307 fprintf(stderr, "FAIL: test %d - only wrote %zd out of %zd "
308 "characters\n", test_no, len, bt->in_len);
309 failure = 1;
310 goto done;
311 }
312 if (BIO_flush(bio_mem) < 0) {
313 fprintf(stderr, "FAIL: test %d - flush failed\n", test_no);
314 failure = 1;
315 goto done;
316 }
317
318 b64len = 0;
319 for (i = 0; i < bt->out_len; i++) {
320 if ((!test_nl ||
321 (test_nl && (i % 64 != 0 || i == bt->out_len - 1))) &&
322 (bt->out[i] == '\r' || bt->out[i] == '\n'))
323 continue;
324 buf[b64len++] = bt->out[i];
325 }
326 if (test_nl)
327 buf[b64len++] = '\n';
328
329 len = BIO_get_mem_data(bio_mem, &out);
330
331 /* An empty string with NL results in no output, rather than '\n'. */
332 if (test_nl && b64len == 1 && len == 0)
333 goto done;
334
335 if (len != b64len) {
336 fprintf(stderr, "FAIL: test %d - encoding resulted in %zd "
337 "characters instead of %zd\n", test_no, len, b64len);
338 failure = 1;
339 goto done;
340 }
341
342 if (memcmp(buf, out, b64len) != 0) {
343 fprintf(stderr, "FAIL: test %d - encoding differs:\n", test_no);
344 fprintf(stderr, " encoding: ");
345 for (i = 0; i < len; i++)
346 fprintf(stderr, "%c", out[i]);
347 fprintf(stderr, "\n");
348 fprintf(stderr, " test data: ");
349 for (i = 0; i < bt->out_len; i++)
350 fprintf(stderr, "%c", buf[i]);
351 fprintf(stderr, "\n");
352 failure = 1;
353 }
354
355 done:
356 BIO_free_all(bio_mem);
357 free(buf);
358
359 return failure;
360 }
361
362 static int
base64_decoding_test(int test_no,struct base64_test * bt,int test_nl)363 base64_decoding_test(int test_no, struct base64_test *bt, int test_nl)
364 {
365 BIO *bio_b64, *bio_mem;
366 char *buf, *input;
367 ssize_t i, inlen, len;
368 int failure = 0;
369
370 buf = malloc(BUF_SIZE);
371 if (buf == NULL)
372 errx(1, "malloc");
373
374 if ((input = malloc(BUF_SIZE)) == NULL)
375 errx(1, "malloc");
376
377 memcpy(input, bt->out, bt->out_len);
378 inlen = bt->out_len;
379 if (test_nl) {
380 memcpy(&input[bt->out_len], "\r\n", 2);
381 inlen += 2;
382 }
383
384 bio_mem = BIO_new_mem_buf(input, inlen);
385 if (bio_mem == NULL)
386 errx(1, "BIO_new_mem_buf failed");
387
388 bio_b64 = BIO_new(BIO_f_base64());
389 if (bio_b64 == NULL)
390 errx(1, "BIO_new failed for BIO_f_base64");
391
392 if (!test_nl)
393 BIO_set_flags(bio_b64, BIO_FLAGS_BASE64_NO_NL);
394
395 bio_mem = BIO_push(bio_b64, bio_mem);
396
397 /*
398 * If we wrote zero characters then a BIO_read will result in a return
399 * value of -1, hence we need to handle this case.
400 */
401 len = BIO_read(bio_mem, buf, BUF_SIZE);
402 if (len != bt->valid_len && (bt->in_len != 0 || len != -1)) {
403 fprintf(stderr, "FAIL: test %d - decoding resulted in %zd "
404 "characters instead of %zd\n", test_no, len, bt->valid_len);
405 fprintf(stderr, " input: ");
406 for (i = 0; i < inlen; i++)
407 fprintf(stderr, "%c", input[i]);
408 fprintf(stderr, "\n");
409 fprintf(stderr, " decoding: ");
410 for (i = 0; i < len; i++)
411 fprintf(stderr, "0x%x ", buf[i]);
412 fprintf(stderr, "\n");
413 failure = 1;
414 goto done;
415 }
416
417 /* See if we expect this to fail decoding. */
418 if (bt->in_len == -1)
419 goto done;
420
421 if (memcmp(bt->in, buf, bt->in_len) != 0) {
422 fprintf(stderr, "FAIL: test %d - decoding differs:\n", test_no);
423 fprintf(stderr, " decoding: ");
424 for (i = 0; i < len; i++)
425 fprintf(stderr, "0x%x ", buf[i]);
426 fprintf(stderr, "\n");
427 fprintf(stderr, " test data: ");
428 for (i = 0; i < bt->in_len; i++)
429 fprintf(stderr, "0x%x ", bt->in[i]);
430 fprintf(stderr, "\n");
431 failure = 1;
432 }
433
434 done:
435 BIO_free_all(bio_mem);
436 free(buf);
437 free(input);
438
439 return failure;
440 }
441
442 int
main(int argc,char ** argv)443 main(int argc, char **argv)
444 {
445 struct base64_test *bt;
446 int failed = 0;
447 size_t i;
448
449 fprintf(stderr, "Starting combined tests...\n");
450
451 for (i = 0; i < N_TESTS; i++) {
452 bt = &base64_tests[i];
453 if (bt->in_len != -1)
454 failed += base64_encoding_test(i, bt, 0);
455 if (bt->out_len != -1)
456 failed += base64_decoding_test(i, bt, 0);
457 if (bt->in_len != -1)
458 failed += base64_encoding_test(i, bt, 1);
459 if (bt->out_len != -1)
460 failed += base64_decoding_test(i, bt, 1);
461 }
462
463 fprintf(stderr, "Starting NL tests...\n");
464
465 for (i = 0; i < N_NL_TESTS; i++) {
466 bt = &base64_nl_tests[i];
467
468 if (bt->in_len != -1)
469 failed += base64_encoding_test(i, bt, 1);
470 if (bt->out_len != -1)
471 failed += base64_decoding_test(i, bt, 1);
472 }
473
474 fprintf(stderr, "Starting NO NL tests...\n");
475
476 for (i = 0; i < N_NO_NL_TESTS; i++) {
477 bt = &base64_no_nl_tests[i];
478
479 if (bt->in_len != -1)
480 failed += base64_encoding_test(i, bt, 0);
481 if (bt->out_len != -1)
482 failed += base64_decoding_test(i, bt, 0);
483 }
484
485 return failed;
486 }
487