Commit 001966f8 authored by Steffen Vogel's avatar Steffen Vogel 🎅🏼

use custom base64 implementation and add unit tests

parent 4b89121f
......@@ -210,11 +210,10 @@ int sha1sum(FILE *f, unsigned char *sha1);
namespace base64 {
std::string encode(const std::string &str);
std::string encode(const unsigned char *input, size_t len);
using byte = std::uint8_t;
std::string decode(const std::string &str);
std::string decode(unsigned char *input, size_t len);
std::string encode(const std::vector<byte> &input);
std::vector<byte> decode(const std::string &input);
} /* namespace base64 */
} /* namespace utils */
......
......@@ -45,6 +45,7 @@ add_library(villas-common SHARED
version.cpp
common.cpp
tool.cpp
base64.cpp
)
execute_process(
......
/** Base64 encoding/decoding
*
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2014-2019, Institute for Automation of Complex Power Systems, EONERC
* @license GNU General Public License (version 3)
*
* VILLAScommon
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
#include <string>
#include <vector>
#include <stdexcept>
#include <cstdint>
#include <villas/utils.hpp>
namespace villas {
namespace utils {
namespace base64 {
static const char kEncodeLookup[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static const char kPadCharacter = '=';
std::string encode(const std::vector<byte>& input)
{
std::string encoded;
encoded.reserve(((input.size() / 3) + (input.size() % 3 > 0)) * 4);
std::uint32_t temp{};
auto it = input.begin();
for (std::size_t i = 0; i < input.size() / 3; ++i) {
temp = (*it++) << 16;
temp += (*it++) << 8;
temp += (*it++);
encoded.append(1, kEncodeLookup[(temp & 0x00FC0000) >> 18]);
encoded.append(1, kEncodeLookup[(temp & 0x0003F000) >> 12]);
encoded.append(1, kEncodeLookup[(temp & 0x00000FC0) >> 6 ]);
encoded.append(1, kEncodeLookup[(temp & 0x0000003F) ]);
}
switch (input.size() % 3) {
case 1:
temp = (*it++) << 16;
encoded.append(1, kEncodeLookup[(temp & 0x00FC0000) >> 18]);
encoded.append(1, kEncodeLookup[(temp & 0x0003F000) >> 12]);
encoded.append(2, kPadCharacter);
break;
case 2:
temp = (*it++) << 16;
temp += (*it++) << 8;
encoded.append(1, kEncodeLookup[(temp & 0x00FC0000) >> 18]);
encoded.append(1, kEncodeLookup[(temp & 0x0003F000) >> 12]);
encoded.append(1, kEncodeLookup[(temp & 0x00000FC0) >> 6 ]);
encoded.append(1, kPadCharacter);
break;
}
return encoded;
}
std::vector<byte> decode(const std::string& input)
{
if (input.length() % 4)
throw std::runtime_error("Invalid base64 length!");
std::size_t padding{};
if (input.length()) {
if(input[input.length() - 1] == kPadCharacter) padding++;
if(input[input.length() - 2] == kPadCharacter) padding++;
}
std::vector<byte> decoded;
decoded.reserve(((input.length() / 4) * 3) - padding);
std::uint32_t temp{};
auto it = input.begin();
while (it < input.end()) {
for (std::size_t i = 0; i < 4; ++i) {
temp <<= 6;
if (*it >= 0x41 && *it <= 0x5A) temp |= *it - 0x41;
else if (*it >= 0x61 && *it <= 0x7A) temp |= *it - 0x47;
else if (*it >= 0x30 && *it <= 0x39) temp |= *it + 0x04;
else if (*it == 0x2B) temp |= 0x3E;
else if (*it == 0x2F) temp |= 0x3F;
else if (*it == kPadCharacter) {
switch(input.end() - it) {
case 1:
decoded.push_back((temp >> 16) & 0x000000FF);
decoded.push_back((temp >> 8 ) & 0x000000FF);
return decoded;
case 2:
decoded.push_back((temp >> 10) & 0x000000FF);
return decoded;
default:
throw std::runtime_error("Invalid padding in base64!");
}
}
else
throw std::runtime_error("Invalid character in base64!");
++it;
}
decoded.push_back((temp >> 16) & 0x000000FF);
decoded.push_back((temp >> 8 ) & 0x000000FF);
decoded.push_back((temp ) & 0x000000FF);
}
return decoded;
}
} /* namespace base64 */
} /* namespace utils */
} /* namespace villas */
......@@ -356,57 +356,5 @@ int sha1sum(FILE *f, unsigned char *sha1)
return 0;
}
namespace base64 {
std::string encode(const std::string &str)
{
return encode((unsigned char *) str.data(), str.size());
}
std::string decode(const std::string &str)
{
return decode((unsigned char *) str.data(), str.size());
}
std::string encode(const unsigned char *input, size_t len)
{
BIO *bmem, *b64;
BUF_MEM *bptr;
b64 = BIO_new(BIO_f_base64());
bmem = BIO_new(BIO_s_mem());
b64 = BIO_push(b64, bmem);
BIO_write(b64, input, len);
BIO_flush(b64);
BIO_get_mem_ptr(b64, &bptr);
std::string str(bptr->data, bptr->length);
BIO_free_all(b64);
return str;
}
std::string decode(unsigned char *input, size_t len)
{
BIO *b64, *bmem;
std::string str(len, 0);
char *buffer = (char *) malloc(len);
memset(buffer, 0, len);
b64 = BIO_new(BIO_f_base64());
bmem = BIO_new_mem_buf(input, len);
bmem = BIO_push(b64, bmem);
BIO_read(bmem, const_cast<std::string::value_type *>(str.data()), str.capacity());
BIO_free_all(bmem);
return buffer;
}
} /* namespace base64 */
} /* namespace utils */
} /* namespace villas */
......@@ -30,6 +30,7 @@ add_executable(unit-tests-common
task.cpp
timing.cpp
utils.cpp
base64.cpp
)
if(ARCH STREQUAL "x86_64")
......
/** Unit tests for base64 encoding/decoding
*
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2014-2019, Institute for Automation of Complex Power Systems, EONERC
* @license GNU General Public License (version 3)
*
* VILLAScommon
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
#include <criterion/criterion.h>
#include <iostream>
#include <villas/utils.hpp>
using namespace villas::utils::base64;
TestSuite(base64, .description = "Base64 En/decoder");
static std::vector<byte> vec(const char *str)
{
return std::vector<byte>((byte *) str, (byte *) str + strlen(str));
}
static std::string str(const std::vector<byte> &vec)
{
return std::string((char *) vec.data(), vec.size());
}
Test(base64, encoding)
{
cr_assert(encode(vec("pohy0Aiy1ZaVa5aik2yaiy3ifoh3oole")) == "cG9oeTBBaXkxWmFWYTVhaWsyeWFpeTNpZm9oM29vbGU=");
}
Test(base64, decoding)
{
cr_assert(decode("cG9oeTBBaXkxWmFWYTVhaWsyeWFpeTNpZm9oM29vbGU=") == vec("pohy0Aiy1ZaVa5aik2yaiy3ifoh3oole"));
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment