// util/base64.cpp
/* Copyright 2009 10gen Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the GNU Affero General Public License in all respects
* for all of the code used other than as permitted herein. If you modify
* file(s) with this exception, you may extend this exception to your
* version of the file(s), but you are not obligated to do so. If you do not
* wish to do so, delete this exception statement from your version. If you
* delete this exception statement from all source files in the program,
* then also delete it in the license file.
*/
#include "mongo/platform/basic.h"
#include "mongo/util/base64.h"
#include "mongo/util/assert_util.h"
#include
namespace mongo {
using std::begin;
using std::end;
using std::string;
using std::stringstream;
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(40537, "Invalid base64 character", c != kInvalid);
return c;
}
bool valid(unsigned char x) const {
return decode[x] != kInvalid;
}
private:
StringData encode{
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/"};
std::array decode;
} alphabet;
} // namespace
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;
// byte 0
ss << alphabet.e(start[0] >> 2);
// byte 1
unsigned char temp = (start[0] << 4);
if (left == 1) {
ss << alphabet.e(temp);
break;
}
temp |= ((start[1] >> 4) & 0xF);
ss << alphabet.e(temp);
// byte 2
temp = (start[1] & 0xF) << 2;
if (left == 2) {
ss << alphabet.e(temp);
break;
}
temp |= ((start[2] >> 6) & 0x3);
ss << alphabet.e(temp);
// byte 3
ss << alphabet.e(start[2] & 0x3f);
}
int mod = size % 3;
if (mod == 1) {
ss << "==";
} else if (mod == 2) {
ss << "=";
}
}
string base64::encode(const char* data, int size) {
stringstream ss;
encode(ss, data, size);
return ss.str();
}
string base64::encode(const string& s) {
return encode(s.c_str(), s.size());
}
void base64::decode(stringstream& ss, const string& s) {
uassert(10270, "invalid base64", s.size() % 4 == 0);
auto const data = reinterpret_cast(s.c_str());
auto const size = s.size();
bool done = false;
for (size_t i = 0; i < size; i += 4) {
uassert(
40538, "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)));
}
}
}
}
string base64::decode(const string& s) {
stringstream ss;
decode(ss, s);
return ss.str();
}
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