From 298ee24f0e6550176abc17492d9dd898aa5c8442 Mon Sep 17 00:00:00 2001 From: Erwin Coumans Date: Wed, 6 May 2020 22:49:09 +0000 Subject: [PATCH] add cpp_base64 --- .../ThirdPartyLibs/cpp_base64/CMakeLists.txt | 6 + examples/ThirdPartyLibs/cpp_base64/LICENSE | 19 ++ examples/ThirdPartyLibs/cpp_base64/base64.cpp | 195 ++++++++++++++++++ .../cpp_base64/include/cpp_base64/base64.h | 18 ++ examples/ThirdPartyLibs/cpp_base64/test.cpp | 91 ++++++++ 5 files changed, 329 insertions(+) create mode 100644 examples/ThirdPartyLibs/cpp_base64/CMakeLists.txt create mode 100644 examples/ThirdPartyLibs/cpp_base64/LICENSE create mode 100644 examples/ThirdPartyLibs/cpp_base64/base64.cpp create mode 100644 examples/ThirdPartyLibs/cpp_base64/include/cpp_base64/base64.h create mode 100644 examples/ThirdPartyLibs/cpp_base64/test.cpp diff --git a/examples/ThirdPartyLibs/cpp_base64/CMakeLists.txt b/examples/ThirdPartyLibs/cpp_base64/CMakeLists.txt new file mode 100644 index 000000000..7e8cd1f87 --- /dev/null +++ b/examples/ThirdPartyLibs/cpp_base64/CMakeLists.txt @@ -0,0 +1,6 @@ + +ADD_LIBRARY(cpp_base64 base64.cpp) + +target_include_directories(cpp_base64 PUBLIC include PRIVATE include/cpp_base64) + + diff --git a/examples/ThirdPartyLibs/cpp_base64/LICENSE b/examples/ThirdPartyLibs/cpp_base64/LICENSE new file mode 100644 index 000000000..f258136d8 --- /dev/null +++ b/examples/ThirdPartyLibs/cpp_base64/LICENSE @@ -0,0 +1,19 @@ +Copyright © 2004-2017 by René Nyffenegger + +This source code is provided 'as-is', without any express or implied +warranty. In no event will the author be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this source code must not be misrepresented; you must not + claim that you wrote the original source code. If you use this source code + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original source code. + +3. This notice may not be removed or altered from any source distribution. diff --git a/examples/ThirdPartyLibs/cpp_base64/base64.cpp b/examples/ThirdPartyLibs/cpp_base64/base64.cpp new file mode 100644 index 000000000..40b555c44 --- /dev/null +++ b/examples/ThirdPartyLibs/cpp_base64/base64.cpp @@ -0,0 +1,195 @@ +/* + base64.cpp and base64.h + + base64 encoding and decoding with C++. + More information at + https://renenyffenegger.ch/notes/development/Base64/Encoding-and-decoding-base-64-with-cpp + + Version: 2.rc.00 (release candidate) + + Copyright (C) 2004-2017, 2020 René Nyffenegger + + This source code is provided 'as-is', without any express or implied + warranty. In no event will the author be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this source code must not be misrepresented; you must not + claim that you wrote the original source code. If you use this source code + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original source code. + + 3. This notice may not be removed or altered from any source distribution. + + René Nyffenegger rene.nyffenegger@adp-gmbh.ch + +*/ + +#include "base64.h" + +static std::string base64_chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789" + "??"; // These two question marks will be replaced based on the value of url in base64_encode + + +static std::size_t pos_of_char(const unsigned char chr) { + // + // Return the position of chr within base64_chars. + // + + if (chr >= 'A' && chr <= 'Z') return chr - 'A'; + else if (chr >= 'a' && chr <= 'z') return chr - 'a' + ('Z' - 'A') + 1; + else if (chr >= '0' && chr <= '9') return chr - '0' + ('Z' - 'A') + ('z' - 'a') + 2; + else if (chr == '+' || chr == '-') return 62; // Be liberal with input and accept both url ('-') and non-url ('+') base 64 characters ( + else if (chr == '/' || chr == '_') return 63; // Ditto for '/' and '_' + + //throw "If input is correct, this line should never be reached."; + return 0; +} + +static std::string insert_linebreaks(std::string str, size_t distance) { + // + // Provided by https://github.com/JomaCorpFX, adapted by me. + // + if (!str.length()) { + return ""; + } + + size_t pos = distance; + + while (pos < str.size()) { + str.insert(pos, "\n"); + pos += distance + 1; + } + + return str; +} + +std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len, bool url) { + // + // Replace question marks in base64_chars: + // + if (url) { + base64_chars[62] = '-'; + base64_chars[63] = '_'; + } + else { + base64_chars[62] = '+'; + base64_chars[63] = '/'; + } + + unsigned int len_encoded = (in_len +2) / 3 * 4; + + unsigned char trailing_char = url ? '.' : '='; + + std::string ret; + ret.reserve(len_encoded); + + unsigned int pos = 0; + + while (pos < in_len) { + ret.push_back(base64_chars[(bytes_to_encode[pos + 0] & 0xfc) >> 2]); + + if (pos+1 < in_len) { + ret.push_back(base64_chars[((bytes_to_encode[pos + 0] & 0x03) << 4) + ((bytes_to_encode[pos + 1] & 0xf0) >> 4)]); + + if (pos+2 < in_len) { + ret.push_back(base64_chars[((bytes_to_encode[pos + 1] & 0x0f) << 2) + ((bytes_to_encode[pos + 2] & 0xc0) >> 6)]); + ret.push_back(base64_chars[ bytes_to_encode[pos + 2] & 0x3f]); + } + else { + ret.push_back(base64_chars[(bytes_to_encode[pos + 1] & 0x0f) << 2]); + ret.push_back(trailing_char); + } + } + else { + + ret.push_back(base64_chars[(bytes_to_encode[pos + 0] & 0x03) << 4]); + ret.push_back(trailing_char); + ret.push_back(trailing_char); + } + + pos += 3; + } + + + return ret; +} + + +std::string base64_decode(std::string const& encoded_string, bool remove_linebreaks) { + + if (remove_linebreaks) { + + if (! encoded_string.length() ) { + return ""; + } + + std::string copy(encoded_string); + + size_t pos=0; + while ((pos = copy.find("\n", pos)) != std::string::npos) { + copy.erase(pos, 1); + } + + return base64_decode(copy, false); + + } + + int length_of_string = encoded_string.length(); + if (!length_of_string) return std::string(""); + + size_t in_len = length_of_string; + size_t pos = 0; + + // + // The approximate length (bytes) of the decoded string might be one ore + // two bytes smaller, depending on the amount of trailing equal signs + // in the encoded string. This approximation is needed to reserve + // enough space in the string to be returned. + // + size_t approx_length_of_decoded_string = length_of_string / 4 * 3; + std::string ret; + ret.reserve(approx_length_of_decoded_string); + + while (pos < in_len) { + + unsigned int pos_of_char_1 = pos_of_char(encoded_string[pos+1] ); + + ret.push_back( ( (pos_of_char(encoded_string[pos+0]) ) << 2 ) + ( (pos_of_char_1 & 0x30 ) >> 4)); + + if (encoded_string[pos+2] != '=' && encoded_string[pos+2] != '.') { // accept URL-safe base 64 strings, too, so check for '.' also. + + unsigned int pos_of_char_2 = pos_of_char(encoded_string[pos+2] ); + ret.push_back( (( pos_of_char_1 & 0x0f) << 4) + (( pos_of_char_2 & 0x3c) >> 2)); + + if (encoded_string[pos+3] != '=') { + ret.push_back( ( (pos_of_char_2 & 0x03 ) << 6 ) + pos_of_char(encoded_string[pos+3]) ); + } + } + + pos += 4; + } + + return ret; +} + +std::string base64_encode(std::string const& s, bool url) { + return base64_encode(reinterpret_cast(s.c_str()), s.length(), url); +} + +std::string base64_encode_pem (std::string const& s) { + return insert_linebreaks(base64_encode(s, false), 64); +} + +std::string base64_encode_mime(std::string const& s) { + return insert_linebreaks(base64_encode(s, false), 76); +} diff --git a/examples/ThirdPartyLibs/cpp_base64/include/cpp_base64/base64.h b/examples/ThirdPartyLibs/cpp_base64/include/cpp_base64/base64.h new file mode 100644 index 000000000..54695ce8c --- /dev/null +++ b/examples/ThirdPartyLibs/cpp_base64/include/cpp_base64/base64.h @@ -0,0 +1,18 @@ +// +// base64 encoding and decoding with C++. +// Version: 2.rc.00 (release candidate) +// + +#ifndef BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A +#define BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A + +#include + +std::string base64_encode (std::string const& s, bool url = false); +std::string base64_encode_pem (std::string const& s); +std::string base64_encode_mime(std::string const& s); + +std::string base64_decode(std::string const& encoded_string, bool remove_linebreaks = false) +std::string base64_encode(unsigned char const*, unsigned int len, bool url = false); + +#endif /* BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A */ diff --git a/examples/ThirdPartyLibs/cpp_base64/test.cpp b/examples/ThirdPartyLibs/cpp_base64/test.cpp new file mode 100644 index 000000000..0665691e4 --- /dev/null +++ b/examples/ThirdPartyLibs/cpp_base64/test.cpp @@ -0,0 +1,91 @@ +#include "base64.h" +#include + +int main() { + + bool all_tests_passed = true; + + const std::string orig = + "René Nyffenegger\n" + "http://www.renenyffenegger.ch\n" + "passion for data\n"; + + std::string encoded = base64_encode(reinterpret_cast(orig.c_str()), orig.length()); + std::string decoded = base64_decode(encoded); + + if (encoded != "UmVuw6kgTnlmZmVuZWdnZXIKaHR0cDovL3d3dy5yZW5lbnlmZmVuZWdnZXIuY2gKcGFzc2lvbiBmb3IgZGF0YQo=") { + std::cout << "Encoding is wrong" << std::endl; + all_tests_passed = false; + } + + if (decoded != orig) { + std::cout << "decoded != orig" << std::endl; + all_tests_passed = false; + } + + // Test all possibilites of fill bytes (none, one =, two ==) + // References calculated with: https://www.base64encode.org/ + + std::string rest0_original = "abc"; + std::string rest0_reference = "YWJj"; + + std::string rest0_encoded = base64_encode(reinterpret_cast(rest0_original.c_str()), + rest0_original.length()); + std::string rest0_decoded = base64_decode(rest0_encoded); + + if (rest0_decoded != rest0_original) { + std::cout << "rest0_decoded != rest0_original" << std::endl; + all_tests_passed = false; + } + if (rest0_reference != rest0_encoded) { + std::cout << "rest0_reference != rest0_encoded" << std::endl; + all_tests_passed = false; + } + + // std::cout << "encoded: " << rest0_encoded << std::endl; + // std::cout << "reference: " << rest0_reference << std::endl; + // std::cout << "decoded: " << rest0_decoded << std::endl << std::endl; + + std::string rest1_original = "abcd"; + std::string rest1_reference = "YWJjZA=="; + + std::string rest1_encoded = base64_encode(reinterpret_cast(rest1_original.c_str()), + rest1_original.length()); + std::string rest1_decoded = base64_decode(rest1_encoded); + + if (rest1_decoded != rest1_original) { + std::cout << "rest1_decoded != rest1_original" << std::endl; + all_tests_passed = false; + } + if (rest1_reference != rest1_encoded) { + std::cout << "rest1_reference != rest1_encoded" << std::endl; + all_tests_passed = false; + } + + // std::cout << "encoded: " << rest1_encoded << std::endl; + // std::cout << "reference: " << rest1_reference << std::endl; + // std::cout << "decoded: " << rest1_decoded << std::endl << std::endl; + + std::string rest2_original = "abcde"; + std::string rest2_reference = "YWJjZGU="; + + std::string rest2_encoded = base64_encode(reinterpret_cast(rest2_original.c_str()), + rest2_original.length()); + std::string rest2_decoded = base64_decode(rest2_encoded); + + if (rest2_decoded != rest2_original) { + std::cout << "rest2_decoded != rest2_original" << std::endl; + all_tests_passed = false; + } + if (rest2_reference != rest2_encoded) { + std::cout << "rest2_reference != rest2_encoded" << std::endl; + all_tests_passed = false; + } + + // std::cout << "encoded: " << rest2_encoded << std::endl; + // std::cout << "reference: " << rest2_reference << std::endl; + // std::cout << "decoded: " << rest2_decoded << std::endl << std::endl; + + if (all_tests_passed) return 0; + return 1; +}