diff options
author | Sara Golemon <sara.golemon@mongodb.com> | 2017-06-27 10:00:42 -0400 |
---|---|---|
committer | Sara Golemon <sara.golemon@mongodb.com> | 2017-07-06 16:36:35 -0400 |
commit | 4b222edf455a34667cfaf7b67e7f8dfdca42bd9c (patch) | |
tree | 5f468e532fa02650803ea20036c7b9a9161ce96f /src/mongo/util/base64.cpp | |
parent | f0b95cc0c48fe242edbc9c7958f9df0a34813e78 (diff) | |
download | mongo-4b222edf455a34667cfaf7b67e7f8dfdca42bd9c.tar.gz |
SERVER-15194 Refactor base64::decode Implementation
* Existing check for length as multiple of 4 as-is
* Added check for non-base64 characters on input
* Added check for terminators ('=') midstream
Implicitly in positions 0 and 1 via non-base64 check
Explicitly in positions 2 and 3 via "done" check.
Moved "Alphabet" class into cpp file in anon namespace
as it's an implementation detail and shouldn't be used
by outside classes.
Added base64::validate() method to accomodate BSON's
isBase64String() check.
Diffstat (limited to 'src/mongo/util/base64.cpp')
-rw-r--r-- | src/mongo/util/base64.cpp | 105 |
1 files changed, 74 insertions, 31 deletions
diff --git a/src/mongo/util/base64.cpp b/src/mongo/util/base64.cpp index 59fdfb63872..2f1f28bfda2 100644 --- a/src/mongo/util/base64.cpp +++ b/src/mongo/util/base64.cpp @@ -32,18 +32,53 @@ #include "mongo/util/base64.h" -#include <sstream> +#include "mongo/util/assert_util.h" + +#include <array> namespace mongo { +using std::begin; +using std::end; using std::string; using std::stringstream; -namespace base64 { +namespace { +constexpr unsigned char kInvalid = -1; + +const class Alphabet { +public: + Alphabet() { + decode.fill(kInvalid); + for (size_t i = 0; i < encode.size(); ++i) { + decode[encode[i]] = i; + } + } + + unsigned char e(std::uint8_t x) const { + return encode[x & 0x3f]; + } + + std::uint8_t d(unsigned char x) const { + auto const c = decode[x]; + uassert(40533, "Invalid base64 character", c != kInvalid); + return c; + } + + bool valid(unsigned char x) const { + return decode[x] != kInvalid; + } -Alphabet alphabet; +private: + StringData encode{ + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"}; + std::array<unsigned char, 256> decode; +} alphabet; +} // namespace -void encode(stringstream& ss, const char* data, int size) { +void base64::encode(stringstream& ss, const char* data, int size) { for (int i = 0; i < size; i += 3) { int left = size - i; const unsigned char* start = (const unsigned char*)data + i; @@ -82,51 +117,59 @@ void encode(stringstream& ss, const char* data, int size) { } -string encode(const char* data, int size) { +string base64::encode(const char* data, int size) { stringstream ss; encode(ss, data, size); return ss.str(); } -string encode(const string& s) { +string base64::encode(const string& s) { return encode(s.c_str(), s.size()); } -void decode(stringstream& ss, const string& s) { +void base64::decode(stringstream& ss, const string& s) { uassert(10270, "invalid base64", s.size() % 4 == 0); - const unsigned char* data = (const unsigned char*)s.c_str(); - int size = s.size(); - - unsigned char buf[3]; - for (int i = 0; i < size; i += 4) { - const unsigned char* start = data + i; - buf[0] = - ((alphabet.decode[start[0]] << 2) & 0xFC) | ((alphabet.decode[start[1]] >> 4) & 0x3); - buf[1] = - ((alphabet.decode[start[1]] << 4) & 0xF0) | ((alphabet.decode[start[2]] >> 2) & 0xF); - buf[2] = ((alphabet.decode[start[2]] << 6) & 0xC0) | ((alphabet.decode[start[3]] & 0x3F)); - - int len = 3; - if (start[3] == '=') { - len = 2; - if (start[2] == '=') { - len = 1; + auto const data = reinterpret_cast<const unsigned char*>(s.c_str()); + auto const size = s.size(); + bool done = false; + + for (size_t i = 0; i < size; i += 4) { + uassert( + 40534, "Invalid Base64 stream. Additional data following terminating sequence.", !done); + auto const start = data + i; + done = (start[2] == '=') || (start[3] == '='); + + ss << (char)(((alphabet.d(start[0]) << 2) & 0xFC) | ((alphabet.d(start[1]) >> 4) & 0x3)); + if (start[2] != '=') { + ss << (char)(((alphabet.d(start[1]) << 4) & 0xF0) | + ((alphabet.d(start[2]) >> 2) & 0xF)); + if (!done) { + ss << (char)(((alphabet.d(start[2]) << 6) & 0xC0) | + ((alphabet.d(start[3]) & 0x3F))); } } - ss.write((const char*)buf, len); } } -string decode(const string& s) { +string base64::decode(const string& s) { stringstream ss; decode(ss, s); return ss.str(); } -const char* chars = - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789+/="; -} +bool base64::validate(const StringData s) { + if (s.size() % 4) { + return false; + } + if (s.empty()) { + return true; + } + + auto const unwindTerminator = [](auto it) { return (*(it - 1) == '=') ? (it - 1) : it; }; + auto const e = unwindTerminator(unwindTerminator(end(s))); + + return e == std::find_if(begin(s), e, [](const char ch) { return !alphabet.valid(ch); }); } + +} // namespace mongo |