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 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 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 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