/*** * ==++== * * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ==--== * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * base64.cpp * * Tests for base64-related utility functions and classes. * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #include "stdafx.h" using namespace utility; namespace tests { namespace functional { namespace utils_tests { SUITE(base64) { // Note: base64 works by encoding any 3 bytes as a four-byte string. Each triple is encoded independently of // previous and subsequent triples. If, for a given set of input bytes, the number is not an even multiple of 3, // the remaining 1 or two bytes are encoded and padded using '=' characters at the end. The encoding format is // defined by IETF RFC 4648. Such padding is only allowed at the end of a encoded string, which makes it impossible // to generally concatenate encoded strings and wind up with a string that is a valid base64 encoding. // // Since each triple of bytes is independent of others, we don't have to test particularly large sets if input data, // validating that the algorithm can process at least two triples should be sufficient. // TEST(rfc_4648_tests_encode) { // These tests are what base64 RFC 4648 proposes. { std::vector str1; VERIFY_ARE_EQUAL(string_t(_XPLATSTR("")), utility::conversions::to_base64(str1)); } { std::vector str1; str1.push_back('f'); VERIFY_ARE_EQUAL(string_t(_XPLATSTR("Zg==")), utility::conversions::to_base64(str1)); } { std::vector str1; str1.push_back('f'); str1.push_back('o'); VERIFY_ARE_EQUAL(string_t(_XPLATSTR("Zm8=")), utility::conversions::to_base64(str1)); } { std::vector str1; str1.push_back('f'); str1.push_back('o'); str1.push_back('o'); VERIFY_ARE_EQUAL(string_t(_XPLATSTR("Zm9v")), utility::conversions::to_base64(str1)); } { std::vector str1; str1.push_back('f'); str1.push_back('o'); str1.push_back('o'); str1.push_back('b'); VERIFY_ARE_EQUAL(string_t(_XPLATSTR("Zm9vYg==")), utility::conversions::to_base64(str1)); } { std::vector str1; str1.push_back('f'); str1.push_back('o'); str1.push_back('o'); str1.push_back('b'); str1.push_back('a'); VERIFY_ARE_EQUAL(string_t(_XPLATSTR("Zm9vYmE=")), utility::conversions::to_base64(str1)); } { std::vector str1; str1.push_back('f'); str1.push_back('o'); str1.push_back('o'); str1.push_back('b'); str1.push_back('a'); str1.push_back('r'); VERIFY_ARE_EQUAL(string_t(_XPLATSTR("Zm9vYmFy")), utility::conversions::to_base64(str1)); } } TEST(rfc_4648_tests_decode) { // These tests are what base64 RFC 4648 proposes. { std::vector str1 = utility::conversions::from_base64(_XPLATSTR("")); VERIFY_ARE_EQUAL(0u, str1.size()); } { std::vector str1 = utility::conversions::from_base64(_XPLATSTR("Zg==")); VERIFY_ARE_EQUAL(1u, str1.size()); VERIFY_ARE_EQUAL('f', str1[0]); } { std::vector str1 = utility::conversions::from_base64(_XPLATSTR("Zm8=")); VERIFY_ARE_EQUAL(2u, str1.size()); VERIFY_ARE_EQUAL('f', str1[0]); VERIFY_ARE_EQUAL('o', str1[1]); } { std::vector str1 = utility::conversions::from_base64(_XPLATSTR("Zm9v")); VERIFY_ARE_EQUAL(3u, str1.size()); VERIFY_ARE_EQUAL('f', str1[0]); VERIFY_ARE_EQUAL('o', str1[1]); VERIFY_ARE_EQUAL('o', str1[2]); } { std::vector str1 = utility::conversions::from_base64(_XPLATSTR("Zm9vYg==")); VERIFY_ARE_EQUAL(4u, str1.size()); VERIFY_ARE_EQUAL('f', str1[0]); VERIFY_ARE_EQUAL('o', str1[1]); VERIFY_ARE_EQUAL('o', str1[2]); VERIFY_ARE_EQUAL('b', str1[3]); } { std::vector str1 = utility::conversions::from_base64(_XPLATSTR("Zm9vYmE=")); VERIFY_ARE_EQUAL(5u, str1.size()); VERIFY_ARE_EQUAL('f', str1[0]); VERIFY_ARE_EQUAL('o', str1[1]); VERIFY_ARE_EQUAL('o', str1[2]); VERIFY_ARE_EQUAL('b', str1[3]); VERIFY_ARE_EQUAL('a', str1[4]); } { std::vector str1 = utility::conversions::from_base64(_XPLATSTR("Zm9vYmFy")); VERIFY_ARE_EQUAL(6u, str1.size()); VERIFY_ARE_EQUAL('f', str1[0]); VERIFY_ARE_EQUAL('o', str1[1]); VERIFY_ARE_EQUAL('o', str1[2]); VERIFY_ARE_EQUAL('b', str1[3]); VERIFY_ARE_EQUAL('a', str1[4]); VERIFY_ARE_EQUAL('r', str1[5]); } } TEST(additional_encode) { { // Check '/' encoding std::vector str1; str1.push_back(254); VERIFY_ARE_EQUAL(string_t(_XPLATSTR("/g==")), utility::conversions::to_base64(str1)); } { // Check '+' encoding std::vector str1; str1.push_back(250); VERIFY_ARE_EQUAL(string_t(_XPLATSTR("+g==")), utility::conversions::to_base64(str1)); } { std::vector str1; str1.push_back('f'); str1.push_back('o'); str1.push_back(239); str1.push_back('b'); VERIFY_ARE_EQUAL(string_t(_XPLATSTR("Zm/vYg==")), utility::conversions::to_base64(str1)); } { std::vector str1; str1.push_back('g'); str1.push_back(239); str1.push_back('o'); str1.push_back('b'); VERIFY_ARE_EQUAL(string_t(_XPLATSTR("Z+9vYg==")), utility::conversions::to_base64(str1)); } } TEST(additional_decode) { // Tests beyond what the RFC recommends. { // Check '/' decoding std::vector str1 = utility::conversions::from_base64(_XPLATSTR("/g==")); VERIFY_ARE_EQUAL(1u, str1.size()); VERIFY_ARE_EQUAL(254u, str1[0]); } { // Check '+' decoding std::vector str1 = utility::conversions::from_base64(_XPLATSTR("+g==")); VERIFY_ARE_EQUAL(1u, str1.size()); VERIFY_ARE_EQUAL(250u, str1[0]); } { // Check '/' decoding std::vector str1 = utility::conversions::from_base64(_XPLATSTR("Zm/vYg==")); VERIFY_ARE_EQUAL(4u, str1.size()); VERIFY_ARE_EQUAL('f', str1[0]); VERIFY_ARE_EQUAL('o', str1[1]); VERIFY_ARE_EQUAL(239, str1[2]); VERIFY_ARE_EQUAL('b', str1[3]); } { // Check '+' decoding std::vector str1 = utility::conversions::from_base64(_XPLATSTR("Z+9vYg==")); VERIFY_ARE_EQUAL(4u, str1.size()); VERIFY_ARE_EQUAL('g', str1[0]); VERIFY_ARE_EQUAL(239, str1[1]); VERIFY_ARE_EQUAL('o', str1[2]); VERIFY_ARE_EQUAL('b', str1[3]); } { // Check the whole base64 alphabet std::vector str1 = utility::conversions::from_base64(_XPLATSTR("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")); VERIFY_ARE_EQUAL(48u, str1.size()); } } TEST(bad_decode) { // These tests are for input that should be disallowed by a very strict decoder, but // the available APIs on Windows accept them, as does glib, which is used on Linux. // Invalid character before padding, unused ones. VERIFY_THROWS(utility::conversions::from_base64(_XPLATSTR("/q==")), std::runtime_error); VERIFY_THROWS(utility::conversions::from_base64(_XPLATSTR("Zm9vYmD=")), std::runtime_error); // CRLF in the middle. VERIFY_THROWS(utility::conversions::from_base64(_XPLATSTR("ABCDEFGHIJKLMNOPQRSTUVWXYZ\r\nabcdefghijklmnopqrstuvwxyz\r\n0123456789+/")), std::runtime_error); // Not the right length. VERIFY_THROWS(utility::conversions::from_base64(_XPLATSTR("/q")), std::runtime_error); // Characters not in the alphabet VERIFY_THROWS(utility::conversions::from_base64(_XPLATSTR("$%#@")), std::runtime_error); // Too much padding at the end. VERIFY_THROWS(utility::conversions::from_base64(_XPLATSTR("/q=========")), std::runtime_error); // Valid strings, concatenated VERIFY_THROWS(utility::conversions::from_base64(_XPLATSTR("Z+9vYg==Z+9vYg==")), std::runtime_error); } TEST(large_string) { const size_t size = 64*1024; std::vector data(size); for (auto i = 0u; i < size; ++i) { data[i] = (unsigned char)(rand() & 0xFF); } auto string = utility::conversions::to_base64(data); auto data2 = utility::conversions::from_base64(string); VERIFY_ARE_EQUAL(data, data2); } } // SUITE(base64) }}}