mirror of
https://git.rwth-aachen.de/acs/public/villas/node/
synced 2025-03-09 00:00:00 +01:00
use custom base64 implementation and add unit tests
This commit is contained in:
parent
e4a602cd5e
commit
827e526917
6 changed files with 187 additions and 56 deletions
|
@ -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(
|
||||
|
|
131
common/lib/base64.cpp
Normal file
131
common/lib/base64.cpp
Normal file
|
@ -0,0 +1,131 @@
|
|||
/** 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")
|
||||
|
|
51
common/tests/unit/base64.cpp
Normal file
51
common/tests/unit/base64.cpp
Normal file
|
@ -0,0 +1,51 @@
|
|||
/** 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"));
|
||||
}
|
Loading…
Add table
Reference in a new issue