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