summaryrefslogtreecommitdiff
path: root/src/mongo/util/base64.cpp
diff options
context:
space:
mode:
authorSara Golemon <sara.golemon@mongodb.com>2017-06-27 10:00:42 -0400
committerSara Golemon <sara.golemon@mongodb.com>2017-07-06 16:36:35 -0400
commit4b222edf455a34667cfaf7b67e7f8dfdca42bd9c (patch)
tree5f468e532fa02650803ea20036c7b9a9161ce96f /src/mongo/util/base64.cpp
parentf0b95cc0c48fe242edbc9c7958f9df0a34813e78 (diff)
downloadmongo-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.cpp105
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