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
base / base64_unittest.cc [blame]
// 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