Files

343 lines
11 KiB
C

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <open62541/types.h>
#include <check.h>
#include "base64.h"
#include <stdlib.h>
#include <string.h>
/* RFC 4648 test vectors */
START_TEST(encodeEmpty) {
size_t out_len = 42;
unsigned char *out = UA_base64((const unsigned char *)"", 0, &out_len);
ck_assert_uint_eq(out_len, 0);
ck_assert_ptr_eq(out, NULL);
} END_TEST
START_TEST(encodeF) {
size_t out_len = 0;
unsigned char *out = UA_base64((const unsigned char *)"f", 1, &out_len);
ck_assert_uint_eq(out_len, 4);
ck_assert(memcmp(out, "Zg==", 4) == 0);
UA_free(out);
} END_TEST
START_TEST(encodeFo) {
size_t out_len = 0;
unsigned char *out = UA_base64((const unsigned char *)"fo", 2, &out_len);
ck_assert_uint_eq(out_len, 4);
ck_assert(memcmp(out, "Zm8=", 4) == 0);
UA_free(out);
} END_TEST
START_TEST(encodeFoo) {
size_t out_len = 0;
unsigned char *out = UA_base64((const unsigned char *)"foo", 3, &out_len);
ck_assert_uint_eq(out_len, 4);
ck_assert(memcmp(out, "Zm9v", 4) == 0);
UA_free(out);
} END_TEST
START_TEST(encodeFoob) {
size_t out_len = 0;
unsigned char *out = UA_base64((const unsigned char *)"foob", 4, &out_len);
ck_assert_uint_eq(out_len, 8);
ck_assert(memcmp(out, "Zm9vYg==", 8) == 0);
UA_free(out);
} END_TEST
START_TEST(encodeFooba) {
size_t out_len = 0;
unsigned char *out = UA_base64((const unsigned char *)"fooba", 5, &out_len);
ck_assert_uint_eq(out_len, 8);
ck_assert(memcmp(out, "Zm9vYmE=", 8) == 0);
UA_free(out);
} END_TEST
START_TEST(encodeFoobar) {
size_t out_len = 0;
unsigned char *out = UA_base64((const unsigned char *)"foobar", 6, &out_len);
ck_assert_uint_eq(out_len, 8);
ck_assert(memcmp(out, "Zm9vYmFy", 8) == 0);
UA_free(out);
} END_TEST
START_TEST(encodeBuf) {
/* Test UA_base64_buf with pre-allocated buffer */
unsigned char buf[8];
size_t len = UA_base64_buf((const unsigned char *)"foo", 3, buf);
ck_assert_uint_eq(len, 4);
ck_assert(memcmp(buf, "Zm9v", 4) == 0);
} END_TEST
START_TEST(encodeBufPadding1) {
unsigned char buf[8];
size_t len = UA_base64_buf((const unsigned char *)"fo", 2, buf);
ck_assert_uint_eq(len, 4);
ck_assert(memcmp(buf, "Zm8=", 4) == 0);
} END_TEST
START_TEST(encodeBufPadding2) {
unsigned char buf[8];
size_t len = UA_base64_buf((const unsigned char *)"f", 1, buf);
ck_assert_uint_eq(len, 4);
ck_assert(memcmp(buf, "Zg==", 4) == 0);
} END_TEST
START_TEST(encodeBinaryData) {
/* Test encoding binary data with all byte values 0x00-0x02 */
unsigned char data[] = {0x00, 0x01, 0x02};
size_t out_len = 0;
unsigned char *out = UA_base64(data, 3, &out_len);
ck_assert_uint_eq(out_len, 4);
ck_assert(memcmp(out, "AAEC", 4) == 0);
UA_free(out);
} END_TEST
/* Decode tests */
START_TEST(decodeEmpty) {
size_t out_len = 42;
unsigned char *out = UA_unbase64((const unsigned char *)"", 0, &out_len);
ck_assert_uint_eq(out_len, 0);
ck_assert_ptr_eq(out, UA_EMPTY_ARRAY_SENTINEL);
} END_TEST
START_TEST(decodeF) {
size_t out_len = 0;
unsigned char *out = UA_unbase64((const unsigned char *)"Zg==", 4, &out_len);
ck_assert_ptr_ne(out, NULL);
ck_assert_uint_eq(out_len, 1);
ck_assert(out[0] == 'f');
UA_free(out);
} END_TEST
START_TEST(decodeFo) {
size_t out_len = 0;
unsigned char *out = UA_unbase64((const unsigned char *)"Zm8=", 4, &out_len);
ck_assert_ptr_ne(out, NULL);
ck_assert_uint_eq(out_len, 2);
ck_assert(memcmp(out, "fo", 2) == 0);
UA_free(out);
} END_TEST
START_TEST(decodeFoo) {
size_t out_len = 0;
unsigned char *out = UA_unbase64((const unsigned char *)"Zm9v", 4, &out_len);
ck_assert_ptr_ne(out, NULL);
ck_assert_uint_eq(out_len, 3);
ck_assert(memcmp(out, "foo", 3) == 0);
UA_free(out);
} END_TEST
START_TEST(decodeFoobar) {
size_t out_len = 0;
unsigned char *out = UA_unbase64((const unsigned char *)"Zm9vYmFy", 8, &out_len);
ck_assert_ptr_ne(out, NULL);
ck_assert_uint_eq(out_len, 6);
ck_assert(memcmp(out, "foobar", 6) == 0);
UA_free(out);
} END_TEST
START_TEST(decodeWithWhitespace) {
/* RFC 2045 allows whitespace (tabs, newlines, spaces) */
size_t out_len = 0;
unsigned char *out = UA_unbase64((const unsigned char *)"Zm9v\nYmFy", 9, &out_len);
ck_assert_ptr_ne(out, NULL);
ck_assert_uint_eq(out_len, 6);
ck_assert(memcmp(out, "foobar", 6) == 0);
UA_free(out);
} END_TEST
START_TEST(decodeWithTab) {
size_t out_len = 0;
unsigned char *out = UA_unbase64((const unsigned char *)"Zm9v\tYmFy", 9, &out_len);
ck_assert_ptr_ne(out, NULL);
ck_assert_uint_eq(out_len, 6);
ck_assert(memcmp(out, "foobar", 6) == 0);
UA_free(out);
} END_TEST
START_TEST(decodeInvalidChar) {
/* '!' is not a valid base64 character */
size_t out_len = 0;
unsigned char *out = UA_unbase64((const unsigned char *)"Zm9!YmFy", 8, &out_len);
ck_assert_ptr_eq(out, NULL);
} END_TEST
START_TEST(decodeNonAscii) {
/* Byte with high bit set */
unsigned char input[] = {'Z', 'm', 0x80, 'v'};
size_t out_len = 0;
unsigned char *out = UA_unbase64(input, 4, &out_len);
ck_assert_ptr_eq(out, NULL);
} END_TEST
START_TEST(decodePaddingTooEarly) {
/* Padding as first byte in a block */
size_t out_len = 0;
unsigned char *out = UA_unbase64((const unsigned char *)"=m9v", 4, &out_len);
ck_assert_ptr_eq(out, NULL);
} END_TEST
START_TEST(decodePaddingSecond) {
/* Padding as second byte in a block */
size_t out_len = 0;
unsigned char *out = UA_unbase64((const unsigned char *)"Z===", 4, &out_len);
ck_assert_ptr_eq(out, NULL);
} END_TEST
START_TEST(decodePaddingThree) {
/* Three padding chars is invalid */
size_t out_len = 0;
unsigned char *out = UA_unbase64((const unsigned char *)"Z===", 4, &out_len);
ck_assert_ptr_eq(out, NULL);
} END_TEST
START_TEST(decodeNotMultipleOfFour) {
/* A final block of one character cannot encode a byte. */
size_t out_len = 0;
unsigned char *out = UA_unbase64((const unsigned char *)"Zm9vY", 5, &out_len);
ck_assert_ptr_eq(out, NULL);
} END_TEST
START_TEST(decodeUnpaddedBase64Url) {
const char *inputs[] = {"-w", "-_8", "-___", "-w \n", "- _8\t"};
const unsigned char expected[] = {0xfb, 0xff, 0xff};
const size_t lengths[] = {1, 2, 3, 1, 2};
for(size_t i = 0; i < sizeof(inputs) / sizeof(inputs[0]); i++) {
size_t out_len = 0;
unsigned char *out = UA_unbase64((const unsigned char *)inputs[i],
strlen(inputs[i]), &out_len);
ck_assert_ptr_ne(out, NULL);
ck_assert_uint_eq(out_len, lengths[i]);
ck_assert_int_eq(memcmp(out, expected, out_len), 0);
UA_free(out);
}
} END_TEST
START_TEST(decodeIncompletePadding) {
size_t out_len = 0;
unsigned char *out = UA_unbase64((const unsigned char *)"Zg=", 3, &out_len);
ck_assert_ptr_eq(out, NULL);
} END_TEST
START_TEST(decodePaddingNotTerminated) {
/* Padding followed by non-padding within a block */
size_t out_len = 0;
unsigned char *out = UA_unbase64((const unsigned char *)"Zm=v", 4, &out_len);
ck_assert_ptr_eq(out, NULL);
} END_TEST
/* Roundtrip tests */
START_TEST(roundtripEmpty) {
size_t enc_len = 0;
unsigned char *enc = UA_base64((const unsigned char *)"", 0, &enc_len);
ck_assert_uint_eq(enc_len, 0);
size_t dec_len = 0;
unsigned char *dec = UA_unbase64(enc, enc_len, &dec_len);
ck_assert(dec != NULL);
ck_assert_uint_eq(dec_len, 0);
} END_TEST
START_TEST(roundtripBinary) {
/* Roundtrip with all bytes 0x00..0xFF */
unsigned char data[256];
for(int i = 0; i < 256; i++)
data[i] = (unsigned char)i;
size_t enc_len = 0;
unsigned char *enc = UA_base64(data, 256, &enc_len);
ck_assert_ptr_ne(enc, NULL);
ck_assert_uint_gt(enc_len, 0);
size_t dec_len = 0;
unsigned char *dec = UA_unbase64(enc, enc_len, &dec_len);
ck_assert_ptr_ne(dec, NULL);
ck_assert_uint_eq(dec_len, 256);
ck_assert(memcmp(data, dec, 256) == 0);
UA_free(enc);
UA_free(dec);
} END_TEST
START_TEST(roundtripVariousLengths) {
/* Test roundtrip for lengths 1..20 */
for(size_t len = 1; len <= 20; len++) {
unsigned char data[20];
for(size_t i = 0; i < len; i++)
data[i] = (unsigned char)(i * 7 + 13);
size_t enc_len = 0;
unsigned char *enc = UA_base64(data, len, &enc_len);
ck_assert_ptr_ne(enc, NULL);
size_t dec_len = 0;
unsigned char *dec = UA_unbase64(enc, enc_len, &dec_len);
ck_assert_ptr_ne(dec, NULL);
ck_assert_uint_eq(dec_len, len);
ck_assert(memcmp(data, dec, len) == 0);
UA_free(enc);
UA_free(dec);
}
} END_TEST
static Suite *testSuite_base64(void) {
TCase *tc_encode = tcase_create("base64_encode");
tcase_add_test(tc_encode, encodeEmpty);
tcase_add_test(tc_encode, encodeF);
tcase_add_test(tc_encode, encodeFo);
tcase_add_test(tc_encode, encodeFoo);
tcase_add_test(tc_encode, encodeFoob);
tcase_add_test(tc_encode, encodeFooba);
tcase_add_test(tc_encode, encodeFoobar);
tcase_add_test(tc_encode, encodeBuf);
tcase_add_test(tc_encode, encodeBufPadding1);
tcase_add_test(tc_encode, encodeBufPadding2);
tcase_add_test(tc_encode, encodeBinaryData);
TCase *tc_decode = tcase_create("base64_decode");
tcase_add_test(tc_decode, decodeEmpty);
tcase_add_test(tc_decode, decodeF);
tcase_add_test(tc_decode, decodeFo);
tcase_add_test(tc_decode, decodeFoo);
tcase_add_test(tc_decode, decodeFoobar);
tcase_add_test(tc_decode, decodeWithWhitespace);
tcase_add_test(tc_decode, decodeWithTab);
tcase_add_test(tc_decode, decodeInvalidChar);
tcase_add_test(tc_decode, decodeNonAscii);
tcase_add_test(tc_decode, decodePaddingTooEarly);
tcase_add_test(tc_decode, decodePaddingSecond);
tcase_add_test(tc_decode, decodePaddingThree);
tcase_add_test(tc_decode, decodeNotMultipleOfFour);
tcase_add_test(tc_decode, decodeUnpaddedBase64Url);
tcase_add_test(tc_decode, decodeIncompletePadding);
tcase_add_test(tc_decode, decodePaddingNotTerminated);
TCase *tc_roundtrip = tcase_create("base64_roundtrip");
tcase_add_test(tc_roundtrip, roundtripEmpty);
tcase_add_test(tc_roundtrip, roundtripBinary);
tcase_add_test(tc_roundtrip, roundtripVariousLengths);
Suite *s = suite_create("Test Base64 encoding/decoding");
suite_add_tcase(s, tc_encode);
suite_add_tcase(s, tc_decode);
suite_add_tcase(s, tc_roundtrip);
return s;
}
int main(void) {
int number_failed = 0;
Suite *s = testSuite_base64();
SRunner *sr = srunner_create(s);
srunner_set_fork_status(sr, CK_NOFORK);
srunner_run_all(sr, CK_NORMAL);
number_failed += srunner_ntests_failed(sr);
srunner_free(sr);
return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}