summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVarun Ravichandran <varun.ravichandran@mongodb.com>2022-08-23 15:43:11 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2022-09-20 20:53:22 +0000
commitcf6bde6ffdfb5a6310c68c9ef297b5518e307ca4 (patch)
tree2e456a17e6e96a99f2b0c92ad37db1b26b157744
parent597ebc2163ad48f5674c2d8625458af6a46dd23d (diff)
downloadmongo-cf6bde6ffdfb5a6310c68c9ef297b5518e307ca4.tar.gz
Revert "SERVER-67464 Add base64url encode/decode"
This reverts commit ef8c3a1546b72894360dab8e3121434ba306c884. (cherry picked from commit b97bcd00e8b8b4ebf3988feb6eea288b705bf00c)
-rw-r--r--src/mongo/idl/basic_types.idl14
-rw-r--r--src/mongo/idl/idl_test.cpp54
-rw-r--r--src/mongo/idl/unittest.idl6
-rw-r--r--src/mongo/util/base64.cpp163
-rw-r--r--src/mongo/util/base64.h31
-rw-r--r--src/mongo/util/base64_test.cpp100
6 files changed, 65 insertions, 303 deletions
diff --git a/src/mongo/idl/basic_types.idl b/src/mongo/idl/basic_types.idl
index f0c7611993e..ab35193de7a 100644
--- a/src/mongo/idl/basic_types.idl
+++ b/src/mongo/idl/basic_types.idl
@@ -234,20 +234,6 @@ types:
deserializer: mongo::NamespaceString
deserialize_with_tenant: true
- base64string:
- bson_serialization_type: string
- description: "A binary-safe base64 encoded string"
- cpp_type: "std::string"
- serializer: "::mongo::base64::encode"
- deserializer: "mongo::base64::decode"
-
- base64urlstring:
- bson_serialization_type: string
- description: "A binary-safe base64url encoded string"
- cpp_type: "std::string"
- serializer: "::mongo::base64url::encode"
- deserializer: "mongo::base64url::decode"
-
connection_string:
bson_serialization_type: string
description: "A MongoDB ConnectionString"
diff --git a/src/mongo/idl/idl_test.cpp b/src/mongo/idl/idl_test.cpp
index 32467123b21..64671e12b8c 100644
--- a/src/mongo/idl/idl_test.cpp
+++ b/src/mongo/idl/idl_test.cpp
@@ -387,60 +387,6 @@ TEST(IDLOneTypeTests, TestNamespaceString) {
}
}
-// Positive: Test base64 encoded strings.
-TEST(IDLOneTypeTests, TestBase64StringPositive) {
- auto doc = BSON("basic"
- << "ABCD+/0="
- << "url"
- << "1234-_0");
- auto parsed = Two_base64string::parse(IDLParserContext{"base64"}, doc);
- ASSERT_EQ(parsed.getBasic(), "\x00\x10\x83\xFB\xFD"_sd);
- ASSERT_EQ(parsed.getUrl(), "\xD7m\xF8\xFB\xFD"_sd);
-
- BSONObjBuilder builder;
- parsed.serialize(&builder);
- ASSERT_BSONOBJ_EQ(doc, builder.obj());
-}
-
-// Negative: Test base64 encoded strings.
-TEST(IDLOneTypeTests, TestBase64StringNegative) {
- {
- // No terminator on basic.
- auto doc = BSON("basic"
- << "ABCD+/0"
- << "url"
- << "1234-_0");
- ASSERT_THROWS_CODE_AND_WHAT(Two_base64string::parse(IDLParserContext{"base64"}, doc),
- AssertionException,
- 10270,
- "invalid base64");
- }
-
- {
- // Invalid chars in basic.
- auto doc = BSON("basic"
- << "ABCD+_0="
- << "url"
- << "1234-_0");
- ASSERT_THROWS_CODE_AND_WHAT(Two_base64string::parse(IDLParserContext{"base64"}, doc),
- AssertionException,
- 40537,
- "Invalid base64 character");
- }
-
- {
- // Invalid chars in url
- auto doc = BSON("basic"
- << "ABCD+/0="
- << "url"
- << "1234-/0");
- ASSERT_THROWS_CODE_AND_WHAT(Two_base64string::parse(IDLParserContext{"base64"}, doc),
- AssertionException,
- 40537,
- "Invalid base64 character");
- }
-}
-
// Postive: Test any type
TEST(IDLOneTypeTests, TestAnyType) {
IDLParserContext ctxt("root");
diff --git a/src/mongo/idl/unittest.idl b/src/mongo/idl/unittest.idl
index c165f7b9602..0b06c0ad5a2 100644
--- a/src/mongo/idl/unittest.idl
+++ b/src/mongo/idl/unittest.idl
@@ -288,12 +288,6 @@ structs:
fields:
value: namespacestring
- two_base64string:
- description: UnitTest for a single base64string
- fields:
- basic: base64string
- url: base64urlstring
-
##################################################################################################
#
# Test a custom non-BSONElement deserialization and serialization methods for a bindata type
diff --git a/src/mongo/util/base64.cpp b/src/mongo/util/base64.cpp
index 048864dbbdc..fefe3160bde 100644
--- a/src/mongo/util/base64.cpp
+++ b/src/mongo/util/base64.cpp
@@ -37,7 +37,7 @@
#include <cstdint>
#include <iostream>
-namespace mongo {
+namespace mongo::base64 {
namespace {
constexpr unsigned char kInvalid = ~0;
@@ -55,33 +55,21 @@ constexpr auto invertTable(StringData table, std::index_sequence<Cs...>) {
{static_cast<unsigned char>(search(table, Cs))...}};
}
-struct Base64 {
- static constexpr auto kEncodeTable =
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"_sd;
- static constexpr auto kDecodeTable = invertTable(kEncodeTable, std::make_index_sequence<256>{});
- static constexpr bool kTerminatorRequired = true;
-};
-
-struct Base64URL {
- static constexpr auto kEncodeTable =
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"_sd;
- static constexpr auto kDecodeTable = invertTable(kEncodeTable, std::make_index_sequence<256>{});
- static constexpr bool kTerminatorRequired = false;
-};
-
-template <typename Mode>
+constexpr StringData kEncodeTable =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"_sd;
+
+constexpr auto kDecodeTable = invertTable(kEncodeTable, std::make_index_sequence<256>{});
+
bool valid(unsigned char x) {
- static_assert(Mode::kDecodeTable.size() == 256, "Invalid decode table");
- return Mode::kDecodeTable[x] != kInvalid;
+ return kDecodeTable[x] != kInvalid;
}
-template <typename Mode, typename Writer>
+template <typename Writer>
void encodeImpl(Writer&& write, StringData in) {
- static_assert(Mode::kEncodeTable.size() == 64, "Invalid encoding table");
const char* data = in.rawData();
std::size_t size = in.size();
auto readOctet = [&data] { return static_cast<std::uint8_t>(*data++); };
- auto encodeSextet = [](unsigned x) { return Mode::kEncodeTable[x & 0b11'1111]; };
+ auto encodeSextet = [](unsigned x) { return kEncodeTable[x & 0b11'1111]; };
std::array<char, 512> buf;
std::array<char, 512>::iterator p;
@@ -114,9 +102,7 @@ void encodeImpl(Writer&& write, StringData in) {
*p++ = encodeSextet(accum >> (6 * (3 - 0)));
*p++ = encodeSextet(accum >> (6 * (3 - 1)));
*p++ = encodeSextet(accum >> (6 * (3 - 2)));
- if (Mode::kTerminatorRequired) {
- *p++ = '=';
- }
+ *p++ = '=';
write(buf.data(), p - buf.begin());
break;
case 1:
@@ -125,10 +111,8 @@ void encodeImpl(Writer&& write, StringData in) {
accum |= readOctet() << (8 * (2 - 0));
*p++ = encodeSextet(accum >> (6 * (3 - 0)));
*p++ = encodeSextet(accum >> (6 * (3 - 1)));
- if (Mode::kTerminatorRequired) {
- *p++ = '=';
- *p++ = '=';
- }
+ *p++ = '=';
+ *p++ = '=';
write(buf.data(), p - buf.begin());
break;
case 0:
@@ -136,25 +120,16 @@ void encodeImpl(Writer&& write, StringData in) {
}
}
-template <typename Mode, typename Writer>
+template <typename Writer>
void decodeImpl(const Writer& write, StringData in) {
- static_assert(Mode::kDecodeTable.size() == 256, "Invalid decode table");
const char* data = in.rawData();
std::size_t size = in.size();
- if (size == 0) {
+ if (size == 0)
return;
- }
-
- const std::size_t lastBlockSize = (size % 4) ? (size % 4) : 4;
- constexpr std::size_t kMinLastBlockSize = Mode::kTerminatorRequired ? 4 : 2;
- uassert(10270, "invalid base64", lastBlockSize >= kMinLastBlockSize);
+ uassert(10270, "invalid base64", size % 4 == 0);
auto decodeSextet = [](char x) {
- static_assert(std::numeric_limits<unsigned char>::min() == 0,
- "Unexpected range for unsigned char");
- static_assert(std::numeric_limits<unsigned char>::max() == 255,
- "Unexpected range for unsigned char");
- auto c = Mode::kDecodeTable[static_cast<unsigned char>(x)];
+ auto c = kDecodeTable[static_cast<unsigned char>(x)];
uassert(40537, "Invalid base64 character", c != kInvalid);
return c;
};
@@ -164,7 +139,7 @@ void decodeImpl(const Writer& write, StringData in) {
std::uint32_t accum;
// All but the final group to avoid '='-related conditionals in the bulk path.
- for (std::size_t groups = (size - lastBlockSize) / 4; groups;) {
+ for (std::size_t groups = size / 4 - 1; groups;) {
std::size_t chunkGroups = std::min(groups, buf.size() / 3);
groups -= chunkGroups;
p = buf.begin();
@@ -184,11 +159,10 @@ void decodeImpl(const Writer& write, StringData in) {
{
// Final group might have some equal signs
std::size_t nbits = 24;
- if ((lastBlockSize < 4) || (data[3] == '=')) {
+ if (data[3] == '=') {
nbits -= 8;
- if ((lastBlockSize < 3) || (data[2] == '=')) {
+ if (data[2] == '=')
nbits -= 8;
- }
}
accum = 0;
accum |= decodeSextet(*data++) << (6 * (3 - 0));
@@ -211,42 +185,40 @@ void decodeImpl(const Writer& write, StringData in) {
} // namespace
-// Base64
-
-std::string base64::encode(StringData in) {
+std::string encode(StringData in) {
std::string r;
r.reserve(encodedLength(in.size()));
- encodeImpl<Base64>([&](const char* s, std::size_t n) { r.append(s, s + n); }, in);
+ encodeImpl([&](const char* s, std::size_t n) { r.append(s, s + n); }, in);
return r;
}
-std::string base64::decode(StringData in) {
+std::string decode(StringData in) {
std::string r;
r.reserve(in.size() / 4 * 3);
- decodeImpl<Base64>([&](const char* s, std::size_t n) { r.append(s, s + n); }, in);
+ decodeImpl([&](const char* s, std::size_t n) { r.append(s, s + n); }, in);
return r;
}
-void base64::encode(std::stringstream& ss, StringData in) {
- encodeImpl<Base64>([&](const char* s, std::size_t n) { ss.write(s, n); }, in);
+void encode(std::stringstream& ss, StringData in) {
+ encodeImpl([&](const char* s, std::size_t n) { ss.write(s, n); }, in);
}
-void base64::decode(std::stringstream& ss, StringData in) {
- decodeImpl<Base64>([&](const char* s, std::size_t n) { ss.write(s, n); }, in);
+void decode(std::stringstream& ss, StringData in) {
+ decodeImpl([&](const char* s, std::size_t n) { ss.write(s, n); }, in);
}
-void base64::encode(fmt::memory_buffer& buffer, StringData in) {
+void encode(fmt::memory_buffer& buffer, StringData in) {
buffer.reserve(buffer.size() + encodedLength(in.size()));
- encodeImpl<Base64>([&](const char* s, std::size_t n) { buffer.append(s, s + n); }, in);
+ encodeImpl([&](const char* s, std::size_t n) { buffer.append(s, s + n); }, in);
}
-void base64::decode(fmt::memory_buffer& buffer, StringData in) {
+void decode(fmt::memory_buffer& buffer, StringData in) {
buffer.reserve(buffer.size() + in.size() / 4 * 3);
- decodeImpl<Base64>([&](const char* s, std::size_t n) { buffer.append(s, s + n); }, in);
+ decodeImpl([&](const char* s, std::size_t n) { buffer.append(s, s + n); }, in);
}
-bool base64::validate(StringData s) {
+bool validate(StringData s) {
if (s.size() % 4) {
return false;
}
@@ -254,74 +226,13 @@ bool base64::validate(StringData s) {
return true;
}
- auto const unwindTerminator = [](auto it) { return (*(it - 1) == '=') ? (it - 1) : it; };
- auto const e = unwindTerminator(unwindTerminator(std::end(s)));
-
- return e == std::find_if(std::begin(s), e, [](const char ch) { return !valid<Base64>(ch); });
-}
-
-// Base64URL
-
-std::string base64url::encode(StringData in) {
- std::string r;
- r.reserve(encodedLength(in.size()));
- encodeImpl<Base64URL>([&](const char* s, std::size_t n) { r.append(s, s + n); }, in);
- return r;
-}
-
-std::string base64url::decode(StringData in) {
- std::string r;
- // effectively ceil(in.size() / 4) * 3
- r.reserve(((in.size() + 3) / 4) * 3);
- decodeImpl<Base64URL>([&](const char* s, std::size_t n) { r.append(s, s + n); }, in);
- return r;
-}
-
-void base64url::encode(std::stringstream& ss, StringData in) {
- encodeImpl<Base64URL>([&](const char* s, std::size_t n) { ss.write(s, n); }, in);
-}
-
-void base64url::decode(std::stringstream& ss, StringData in) {
- decodeImpl<Base64URL>([&](const char* s, std::size_t n) { ss.write(s, n); }, in);
-}
-
-void base64url::encode(fmt::memory_buffer& buffer, StringData in) {
- buffer.reserve(buffer.size() + encodedLength(in.size()));
- encodeImpl<Base64URL>([&](const char* s, std::size_t n) { buffer.append(s, s + n); }, in);
-}
-
-void base64url::decode(fmt::memory_buffer& buffer, StringData in) {
- buffer.reserve(buffer.size() + in.size() / 4 * 3);
- decodeImpl<Base64URL>([&](const char* s, std::size_t n) { buffer.append(s, s + n); }, in);
-}
-
-
-bool base64url::validate(StringData s) {
- if (s.empty()) {
- return true;
- }
+ using std::begin;
+ using std::end;
auto const unwindTerminator = [](auto it) { return (*(it - 1) == '=') ? (it - 1) : it; };
- auto e = std::end(s);
-
- switch (s.size() % 4) {
- case 1:
- // Invalid length for a Base64URL block.
- return false;
- case 2:
- // Valid length when no terminators present.
- break;
- case 3:
- // Valid with one optional terminator.
- e = unwindTerminator(e);
- break;
- case 0:
- // Valid with up to two optional terminators.
- e = unwindTerminator(unwindTerminator(e));
- break;
- }
+ auto const e = unwindTerminator(unwindTerminator(end(s)));
- return e == std::find_if(std::begin(s), e, [](const char ch) { return !valid<Base64URL>(ch); });
+ return e == std::find_if(begin(s), e, [](const char ch) { return !valid(ch); });
}
-} // namespace mongo
+} // namespace mongo::base64
diff --git a/src/mongo/util/base64.h b/src/mongo/util/base64.h
index 28ef7a553f7..7a2627338d2 100644
--- a/src/mongo/util/base64.h
+++ b/src/mongo/util/base64.h
@@ -35,8 +35,7 @@
#include "mongo/base/string_data.h"
-namespace mongo {
-namespace base64 {
+namespace mongo::base64 {
std::string encode(StringData in);
std::string decode(StringData in);
@@ -60,31 +59,5 @@ bool validate(StringData s);
constexpr std::size_t encodedLength(std::size_t inLen) {
return (inLen + 2) / 3 * 4;
}
-} // namespace base64
-// base64url encoding is a "url safe" variant of base64.
-// '+' is replaced with '-'
-// '/' is replaced with '_'
-// '=' at the end of the string are optional
-namespace base64url {
-
-std::string encode(StringData in);
-std::string decode(StringData out);
-
-void encode(std::stringstream& ss, StringData in);
-void decode(std::stringstream& ss, StringData in);
-
-void encode(fmt::memory_buffer& buffer, StringData in);
-void decode(fmt::memory_buffer& buffer, StringData in);
-
-inline std::string encode(const void* data, std::size_t len) {
- return encode(StringData(reinterpret_cast<const char*>(data), len));
-}
-
-bool validate(StringData s);
-
-constexpr std::size_t encodedLength(std::size_t inLen) {
- return base64::encodedLength(inLen);
-}
-} // namespace base64url
-} // namespace mongo
+} // namespace mongo::base64
diff --git a/src/mongo/util/base64_test.cpp b/src/mongo/util/base64_test.cpp
index 296f831bdab..a4a3575c185 100644
--- a/src/mongo/util/base64_test.cpp
+++ b/src/mongo/util/base64_test.cpp
@@ -48,40 +48,30 @@ TEST(Base64Test, transcode) {
int line;
StringData plain;
StringData encoded;
- StringData encodedUrl;
} const tests[] = {
- {__LINE__, ""_sd, ""_sd},
- {__LINE__, "a"_sd, "YQ=="_sd, "YQ"_sd},
- {__LINE__, "aa"_sd, "YWE="_sd, "YWE"_sd},
- {__LINE__, "aaa"_sd, "YWFh"_sd, "YWFh"_sd},
- {__LINE__, "aaaa"_sd, "YWFhYQ=="_sd, "YWFhYQ"_sd},
+ {__LINE__, "", ""},
+ {__LINE__, "a", "YQ=="},
+ {__LINE__, "aa", "YWE="},
+ {__LINE__, "aaa", "YWFh"},
+ {__LINE__, "aaaa", "YWFhYQ=="},
- {__LINE__, "A"_sd, "QQ=="_sd, "QQ"_sd},
- {__LINE__, "AA"_sd, "QUE="_sd, "QUE"_sd},
- {__LINE__, "AAA"_sd, "QUFB"_sd, "QUFB"_sd},
- {__LINE__, "AAAA"_sd, "QUFBQQ=="_sd, "QUFBQQ"_sd},
+ {__LINE__, "A", "QQ=="},
+ {__LINE__, "AA", "QUE="},
+ {__LINE__, "AAA", "QUFB"},
+ {__LINE__, "AAAA", "QUFBQQ=="},
{__LINE__,
- "The quick brown fox jumped over the lazy dog."_sd,
- "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wZWQgb3ZlciB0aGUgbGF6eSBkb2cu"_sd,
- "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wZWQgb3ZlciB0aGUgbGF6eSBkb2cu"_sd},
- {__LINE__, "\0\1\2\3\4\5\6\7"_sd, "AAECAwQFBgc="_sd, "AAECAwQFBgc"_sd},
- {__LINE__, "\0\277\1\276\2\275"_sd, "AL8BvgK9"_sd, "AL8BvgK9"_sd},
-
- {__LINE__, "\x7E\x8A\x3E\xFD\xB6\xAB"_sd, "foo+/bar"_sd, "foo-_bar"_sd},
+ "The quick brown fox jumped over the lazy dog.",
+ "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wZWQgb3ZlciB0aGUgbGF6eSBkb2cu"},
+ {__LINE__, "\0\1\2\3\4\5\6\7"_sd, "AAECAwQFBgc="},
+ {__LINE__, "\0\277\1\276\2\275"_sd, "AL8BvgK9"},
};
for (const auto& t : tests) {
- ASSERT_TRUE(base64::validate(t.encoded)) << t.line;
- ASSERT_EQUALS(base64::encode(t.plain), t.encoded)
- << "line: " << t.line << ", plain: '" << t.plain << "'";
- ASSERT_EQUALS(base64::decode(t.encoded), t.plain)
- << "line: " << t.line << ", encoded: '" << t.encoded << "'";
-
- ASSERT_TRUE(base64url::validate(t.encodedUrl)) << t.line;
- ASSERT_EQUALS(base64url::encode(t.plain), t.encodedUrl)
+ ASSERT_TRUE(base64::validate(std::string{t.encoded})) << t.line;
+ ASSERT_EQUALS(base64::encode(std::string{t.plain}), t.encoded)
<< "line: " << t.line << ", plain: '" << t.plain << "'";
- ASSERT_EQUALS(base64url::decode(t.encodedUrl), t.plain)
+ ASSERT_EQUALS(base64::decode(std::string{t.encoded}), t.plain)
<< "line: " << t.line << ", encoded: '" << t.encoded << "'";
}
}
@@ -99,19 +89,13 @@ TEST(Base64Test, encodeAllPossibleGroups) {
std::string s = base64::encode(buf);
ASSERT_EQ(s.size(), 4);
if (kSuperVerbose) {
- LOGV2(23509, "buffer", "buf"_attr = mongo::hexblob::encode(buf), "s"_attr = s);
+ LOGV2(23509,
+ "buf=[{buf}] s=`{s}`",
+ "buf"_attr = mongo::hexblob::encode(buf),
+ "s"_attr = s);
}
std::string recovered = base64::decode(s);
ASSERT_EQ(buf, recovered);
-
- s = base64url::encode(buf);
- ASSERT_GTE(s.size(), 2);
- ASSERT_LTE(s.size(), 4);
- if (kSuperVerbose) {
- LOGV2(6746400, "buffer", "buf"_attr = mongo::hexblob::encode(buf), "s"_attr = s);
- }
- recovered = base64url::decode(s);
- ASSERT_EQ(buf, recovered);
}
}
}
@@ -122,14 +106,12 @@ TEST(Base64Test, parseFail) {
StringData encoded;
boost::optional<int> code;
} const tests[] = {
- {__LINE__, "BadLength"_sd, 10270},
- {__LINE__, "Has Whitespace=="_sd, 40537},
- {__LINE__, "Hasbadchar$="_sd, 40537},
- {__LINE__, "Hasbadchar\xFF="_sd, 40537},
- {__LINE__, "Hasbadchar\t="_sd, 40537},
- {__LINE__, "Has-dash"_sd, 40537},
- {__LINE__, "Has_Underscore=="_sd, 40537},
- {__LINE__, "too=soon"_sd, {}}, // fail, don't care how
+ {__LINE__, "BadLength", 10270},
+ {__LINE__, "Has Whitespace==", 40537},
+ {__LINE__, "Hasbadchar$=", 40537},
+ {__LINE__, "Hasbadchar\xFF=", 40537},
+ {__LINE__, "Hasbadcahr\t=", 40537},
+ {__LINE__, "too=soon", {}}, // fail, don't care how
};
for (const auto& t : tests) {
@@ -146,35 +128,5 @@ TEST(Base64Test, parseFail) {
}
}
-TEST(Base64UrlTest, parseFail) {
- struct {
- int line;
- StringData encoded;
- boost::optional<int> code;
- } const tests[] = {
- {__LINE__, "BadLength"_sd, 10270},
- {__LINE__, "Has Whitespace=="_sd, 40537},
- {__LINE__, "Hasbadchar$="_sd, 40537},
- {__LINE__, "Hasbadchar\xFF="_sd, 40537},
- {__LINE__, "Hasbadchar\t="_sd, 40537},
- {__LINE__, "Has+plus"_sd, 40537},
- {__LINE__, "Has/Solidus="_sd, 40537},
- {__LINE__, "too=soon"_sd, {}}, // fail, don't care how
- };
-
- for (const auto& t : tests) {
- ASSERT_FALSE(base64url::validate(t.encoded)) << t.line;
-
- try {
- base64url::decode(t.encoded);
- ASSERT_TRUE(false) << t.line;
- } catch (const AssertionException& e) {
- if (t.code) {
- ASSERT_EQ(e.code(), *t.code) << t.line << " e: " << e.toString();
- }
- }
- }
-}
-
} // namespace
} // namespace mongo