1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
|
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
#pragma allow_unsafe_buffers
#endif
#include "base/base64.h"
#include <string_view>
#include "base/numerics/checked_math.h"
#include "base/strings/escape.h"
#include "base/test/gtest_util.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/modp_b64/modp_b64.h"
namespace base {
TEST(Base64Test, Basic) {
const std::string kText = "hello world";
const std::string kBase64Text = "aGVsbG8gd29ybGQ=";
std::string decoded;
bool ok;
std::string encoded = Base64Encode(kText);
EXPECT_EQ(kBase64Text, encoded);
ok = Base64Decode(encoded, &decoded);
EXPECT_TRUE(ok);
EXPECT_EQ(kText, decoded);
}
TEST(Base64Test, ForgivingAndStrictDecode) {
struct {
const char* in;
// nullptr indicates a decode failure.
const char* expected_out_forgiving;
const char* expected_out_strict;
} kTestCases[] = {
// Failures that should apply in all decoding modes:
//
// - Characters not in the base64 alphabet
{"abc&", nullptr, nullptr},
{"ab-d", nullptr, nullptr},
// - input len % 4 == 1
{"abcde", nullptr, nullptr},
{"a", nullptr, nullptr},
// Invalid padding causes failure if kForgiving is set.
{"abcd=", nullptr, nullptr},
{"abcd==", nullptr, nullptr},
{"abcd===", nullptr, nullptr},
{"abcd====", nullptr, nullptr},
{"abcd==============", nullptr, nullptr},
{"abcde===", nullptr, nullptr},
{"=", nullptr, nullptr},
{"====", nullptr, nullptr},
// Otherwise, inputs that are multiples of 4 always succeed, this matches
// kStrict mode.
{"abcd", "i\xB7\x1D", "i\xB7\x1D"},
{"abc=", "i\xB7", "i\xB7"},
{"abcdefgh", "i\xB7\x1Dy\xF8!", "i\xB7\x1Dy\xF8!"},
// kForgiving mode allows for omitting padding (to a multiple of 4) if
// len % 4 != 1.
{"abcdef", "i\xB7\x1Dy", nullptr},
{"abc", "i\xB7", nullptr},
{"ab", "i", nullptr},
// Whitespace should be allowed if kForgiving is set, matching
// https://infra.spec.whatwg.org/#ascii-whitespace:
// ASCII whitespace is U+0009 TAB '\t', U+000A LF '\n', U+000C FF '\f',
// U+000D CR '\r', or U+0020 SPACE ' '.
{" a bcd", "i\xB7\x1D", nullptr},
{"ab\t\tc=", "i\xB7", nullptr},
{"ab c\ndefgh", "i\xB7\x1Dy\xF8!", nullptr},
{"a\tb\nc\f d\r", "i\xB7\x1D", nullptr},
{"this should fail", "\xB6\x18\xAC\xB2\x1A.\x95\xD7\xDA\x8A", nullptr},
// U+000B VT '\v' is _not_ valid whitespace to be stripped.
{"ab\vcd", nullptr, nullptr},
// Empty string should yield an empty result.
{"", "", ""},
};
for (const auto& test_case : kTestCases) {
SCOPED_TRACE(::testing::Message()
<< "Forgiving: " << EscapeAllExceptUnreserved(test_case.in));
std::string output;
bool success =
Base64Decode(test_case.in, &output, Base64DecodePolicy::kForgiving);
bool expected_success = test_case.expected_out_forgiving != nullptr;
EXPECT_EQ(success, expected_success);
if (expected_success) {
EXPECT_EQ(output, test_case.expected_out_forgiving);
}
}
for (const auto& test_case : kTestCases) {
SCOPED_TRACE(::testing::Message()
<< "Strict: " << EscapeAllExceptUnreserved(test_case.in));
std::string output;
bool success =
Base64Decode(test_case.in, &output, Base64DecodePolicy::kStrict);
bool expected_success = test_case.expected_out_strict != nullptr;
EXPECT_EQ(success, expected_success);
if (expected_success) {
EXPECT_EQ(output, test_case.expected_out_strict);
}
}
}
TEST(Base64Test, Binary) {
const uint8_t kData[] = {0x00, 0x01, 0xFE, 0xFF};
std::string binary_encoded = Base64Encode(kData);
// Check that encoding the same data through the std::string_view interface
// gives the same results.
std::string string_piece_encoded = Base64Encode(
std::string_view(reinterpret_cast<const char*>(kData), sizeof(kData)));
EXPECT_EQ(binary_encoded, string_piece_encoded);
EXPECT_THAT(Base64Decode(binary_encoded),
testing::Optional(testing::ElementsAreArray(kData)));
EXPECT_FALSE(Base64Decode("invalid base64!"));
std::string encoded_with_prefix = "PREFIX";
Base64EncodeAppend(kData, &encoded_with_prefix);
EXPECT_EQ(encoded_with_prefix, "PREFIX" + binary_encoded);
}
TEST(Base64Test, InPlace) {
const std::string kText = "hello world";
const std::string kBase64Text = "aGVsbG8gd29ybGQ=";
std::string text = Base64Encode(kText);
EXPECT_EQ(kBase64Text, text);
bool ok = Base64Decode(text, &text);
EXPECT_TRUE(ok);
EXPECT_EQ(text, kText);
}
TEST(Base64Test, Overflow) {
// `Base64Encode` makes the input larger, which means inputs whose base64
// output overflows `size_t`. Actually allocating a span of this size will
// likely fail, but we test it with a fake span and assume a correct
// implementation will check for overflow before touching the input.
//
// Note that, with or without an overflow check, the function will still
// crash. This test is only meaningful because `EXPECT_CHECK_DEATH` looks for
// a `CHECK`-based failure.
uint8_t b;
auto large_span = span(&b, MODP_B64_MAX_INPUT_LEN + 1);
EXPECT_CHECK_DEATH(Base64Encode(large_span));
std::string output = "PREFIX";
EXPECT_CHECK_DEATH(Base64EncodeAppend(large_span, &output));
// `modp_b64_encode_data_len` is a macro, so check `MODP_B64_MAX_INPUT_LEN` is
// correct be verifying the computation doesn't overflow.
base::CheckedNumeric<size_t> max_len = MODP_B64_MAX_INPUT_LEN;
EXPECT_TRUE(modp_b64_encode_data_len(max_len).IsValid());
}
} // namespace base
|