Skip to content

Commit

Permalink
adding base64 encoding
Browse files Browse the repository at this point in the history
  • Loading branch information
Greg Hewett committed Jul 26, 2023
1 parent 491050a commit e1c598e
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 0 deletions.
6 changes: 6 additions & 0 deletions lib/bytes/include/bytes/bytes.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,4 +115,10 @@ to_hex(const bytes& data);
bytes
from_hex(const std::string& hex);

std::string
to_base64(const bytes& data);

std::string
to_base64url(const bytes& data);

} // namespace bytes_ns
65 changes: 65 additions & 0 deletions lib/bytes/src/bytes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <openssl/evp.h>
#include <openssl/bio.h>

namespace bytes_ns {

Expand Down Expand Up @@ -137,4 +139,67 @@ operator!=(const std::vector<uint8_t>& lhs, const bytes_ns::bytes& rhs)
return rhs != lhs;
}

std::string
to_base64(const bytes& data)
{
bool done = false;
int result = 0;

if (data.empty()) {
return "";
}

BIO* b64 = BIO_new(BIO_f_base64());
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
BIO* out = BIO_new(BIO_s_mem());
BIO_push(b64, out);

while (!done) {
result = BIO_write(b64, data.data(), static_cast<int>(data.size()));

if (result <= 0) {
if (BIO_should_retry(b64)) {
continue;
}
throw std::runtime_error("base64 encode failed");
}
done = true;
}
BIO_flush(b64);
char* string_ptr = nullptr;
// long string_len = BIO_get_mem_data(out, &string_ptr);
// BIO_get_mem_data failed clang-tidy
long string_len = BIO_ctrl(out, BIO_CTRL_INFO, 0, &string_ptr);
auto return_value = std::string(string_ptr, string_len);

BIO_set_close(out, BIO_NOCLOSE);
BIO_free(b64);
BIO_free(out);
return return_value;
}

std::string
to_base64url(const bytes& data)
{
if (data.empty()) {
return "";
}

std::string return_value = to_base64(data);

// remove the end padding
auto sz = return_value.find_first_of('=');

if (sz != std::string::npos) {
return_value = return_value.substr(0, sz);
}

// replace plus with hyphen
std::replace(return_value.begin(), return_value.end(), '+', '-');

// replace slash with underscore
std::replace(return_value.begin(), return_value.end(), '/', '_');
return return_value;
}

} // namespace bytes_ns
26 changes: 26 additions & 0 deletions lib/bytes/test/bytes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include <doctest/doctest.h>
#include <memory>
#include <sstream>
#include <vector>

using namespace bytes_ns;
using namespace std::literals::string_literals;
Expand Down Expand Up @@ -40,6 +41,31 @@ TEST_CASE("To/from hex/ASCII")
REQUIRE(from_ascii(str) == ascii);
}

TEST_CASE("To Base64 / To Base64Url")
{
struct KnownAnswerTest
{
bytes data;
std::string base64;
std::string base64u;
};

const std::vector<KnownAnswerTest> cases{
{ from_ascii("hello there"), "aGVsbG8gdGhlcmU=", "aGVsbG8gdGhlcmU" },
{ from_ascii("A B C D E F "), "QSBCIEMgRCBFIEYg", "QSBCIEMgRCBFIEYg" },
{ from_ascii("hello\xfethere"), "aGVsbG/+dGhlcmU=", "aGVsbG_-dGhlcmU" },
{ from_ascii("\xfe"), "/g==", "_g" },
{ from_ascii("\x01\x02"), "AQI=", "AQI" },
{ from_ascii("\x01"), "AQ==", "AQ" },
{ from_ascii(""), "", "" },
};

for (const auto& tc : cases) {
REQUIRE(to_base64(tc.data) == tc.base64);
REQUIRE(to_base64url(tc.data) == tc.base64u);
}
}

TEST_CASE("Operators")
{
const auto lhs = from_hex("00010203");
Expand Down

0 comments on commit e1c598e

Please sign in to comment.