Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[5.0 -> main] Fix base64 encoding - take 2 #1893

Merged
merged 7 commits into from
Nov 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions libraries/chain/include/eosio/chain/database_utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,8 @@ namespace fc {

inline
void from_variant( const variant& v, eosio::chain::shared_blob& b ) {
std::string _s = base64_decode(v.as_string());
b = eosio::chain::shared_blob(_s.begin(), _s.end(), b.get_allocator());
std::vector<char> b64 = base64_decode(v.as_string());
b = eosio::chain::shared_blob(b64.begin(), b64.end(), b.get_allocator());
}

template<typename T>
Expand Down
6 changes: 4 additions & 2 deletions libraries/libfc/include/fc/crypto/base64.hpp
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
#pragma once
#include <string>
#include <string_view>
#include <vector>

namespace fc {
std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len);
inline std::string base64_encode(char const* bytes_to_encode, unsigned int in_len) { return base64_encode( (unsigned char const*)bytes_to_encode, in_len); }
std::string base64_encode( const std::string& enc );
std::string base64_decode( const std::string& encoded_string);
std::vector<char> base64_decode( std::string_view encoded_string);

std::string base64url_encode(unsigned char const* bytes_to_encode, unsigned int in_len);
inline std::string base64url_encode(char const* bytes_to_encode, unsigned int in_len) { return base64url_encode( (unsigned char const*)bytes_to_encode, in_len); }
std::string base64url_encode( const std::string& enc );
std::string base64url_decode( const std::string& encoded_string);
std::vector<char> base64url_decode( std::string_view encoded_string);
} // namespace fc
13 changes: 7 additions & 6 deletions libraries/libfc/src/crypto/base64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,13 +107,14 @@ std::string base64url_encode( const std::string& enc ) {
return base64url_encode( (unsigned char const*)s, enc.size() );
}

std::string base64_decode_impl(std::string const& encoded_string, const char* const b64_chars) {
std::vector<char> base64_decode_impl(std::string_view encoded_string, const char* const b64_chars) {
int in_len = encoded_string.size();
int i = 0;
int j = 0;
int in_ = 0;
unsigned char char_array_4[4], char_array_3[3];
std::string ret;
std::vector<char> ret;
ret.reserve(in_len / 4 * 3);

while (in_len-- && encoded_string[in_] != '=') {
throw_on_nonbase64(encoded_string[in_], b64_chars);
Expand All @@ -127,7 +128,7 @@ std::string base64_decode_impl(std::string const& encoded_string, const char* co
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];

for (i = 0; (i < 3); i++)
ret += char_array_3[i];
ret.push_back(char_array_3[i]);
i = 0;
}
}
Expand All @@ -143,17 +144,17 @@ std::string base64_decode_impl(std::string const& encoded_string, const char* co
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];

for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
for (j = 0; (j < i - 1); j++) ret.push_back(char_array_3[j]);
}

return ret;
}

std::string base64_decode(std::string const& encoded_string) {
std::vector<char> base64_decode(std::string_view encoded_string) {
return base64_decode_impl(encoded_string, base64_chars);
}

std::string base64url_decode(std::string const& encoded_string) {
std::vector<char> base64url_decode(std::string_view encoded_string) {
return base64_decode_impl(encoded_string, base64url_chars);
}

Expand Down
4 changes: 2 additions & 2 deletions libraries/libfc/src/crypto/bigint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -221,8 +221,8 @@ namespace fc {
else
{
std::string b64 = v.as_string();
std::string bin = base64_decode(b64);
bi = bigint(bin.c_str(), bin.size() );
std::vector<char> bin = base64_decode(b64);
bi = bigint(bin.data(), bin.size() );
}
}

Expand Down
2 changes: 1 addition & 1 deletion libraries/libfc/src/crypto/elliptic_webauthn.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ public_key::public_key(const signature& c, const fc::sha256& digest, bool) {

FC_ASSERT(handler.found_type == "webauthn.get", "webauthn signature type not an assertion");

std::string challenge_bytes = fc::base64url_decode(handler.found_challenge);
std::vector<char> challenge_bytes = fc::base64url_decode(handler.found_challenge);
FC_ASSERT(fc::sha256(challenge_bytes.data(), challenge_bytes.size()) == digest, "Wrong webauthn challenge");

char required_origin_scheme[] = "https://";
Expand Down
17 changes: 10 additions & 7 deletions libraries/libfc/src/variant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -490,7 +490,7 @@ std::string variant::as_string()const
return *reinterpret_cast<const bool*>(this) ? "true" : "false";
case blob_type:
if( get_blob().data.size() )
return base64_encode( get_blob().data.data(), get_blob().data.size() ) + "=";
return base64_encode( get_blob().data.data(), get_blob().data.size() );
return std::string();
case null_type:
return std::string();
Expand Down Expand Up @@ -533,10 +533,14 @@ blob variant::as_blob()const
{
const std::string& str = get_string();
if( str.size() == 0 ) return blob();
if( str.back() == '=' )
{
std::string b64 = base64_decode( get_string() );
return blob( { std::vector<char>( b64.begin(), b64.end() ) } );
try {
// pre-5.0 versions of variant added `=` to end of base64 encoded string in as_string() above.
// fc version of base64_decode allows for extra `=` at the end of the string.
// Other base64 decoders will not accept the extra `=`.
std::vector<char> b64 = base64_decode( str );
return { std::move(b64) };
} catch(const std::exception&) {
// unable to decode, return the raw chars
}
return blob( { std::vector<char>( str.begin(), str.end() ) } );
}
Expand Down Expand Up @@ -758,8 +762,7 @@ void to_variant( const blob& b, variant& v ) {
}

void from_variant( const variant& v, blob& b ) {
std::string _s = base64_decode(v.as_string());
b.data = std::vector<char>(_s.begin(), _s.end());
b.data = base64_decode(v.as_string());
}

void to_variant( const UInt<8>& n, variant& v ) { v = uint64_t(n); }
Expand Down
9 changes: 6 additions & 3 deletions libraries/libfc/test/test_base64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,24 @@ BOOST_AUTO_TEST_CASE(base64dec) try {
auto input = "YWJjMTIzJCYoKSc/tPUB+n5h"s;
auto expected_output = "abc123$&()'?\xb4\xf5\x01\xfa~a"s;

BOOST_CHECK_EQUAL(expected_output, base64_decode(input));
std::vector<char> b64 = base64_decode(input);
BOOST_CHECK_EQUAL(expected_output, std::string_view(b64.begin(), b64.end()));
} FC_LOG_AND_RETHROW();

BOOST_AUTO_TEST_CASE(base64urldec) try {
auto input = "YWJjMTIzJCYoKSc_tPUB-n5h"s;
auto expected_output = "abc123$&()'?\xb4\xf5\x01\xfa~a"s;

BOOST_CHECK_EQUAL(expected_output, base64url_decode(input));
std::vector<char> b64 = base64url_decode(input);
BOOST_CHECK_EQUAL(expected_output, std::string_view(b64.begin(), b64.end()));
} FC_LOG_AND_RETHROW();

BOOST_AUTO_TEST_CASE(base64dec_extraequals) try {
auto input = "YWJjMTIzJCYoKSc/tPUB+n5h========="s;
auto expected_output = "abc123$&()'?\xb4\xf5\x01\xfa~a"s;

BOOST_CHECK_EQUAL(expected_output, base64_decode(input));
std::vector<char> b64 = base64_decode(input);
BOOST_CHECK_EQUAL(expected_output, std::string_view(b64.begin(), b64.end()));
} FC_LOG_AND_RETHROW();

BOOST_AUTO_TEST_CASE(base64dec_bad_stuff) try {
Expand Down
78 changes: 77 additions & 1 deletion libraries/libfc/test/variant/test_variant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,87 @@ BOOST_AUTO_TEST_CASE(variant_format_string_limited)
const string target_result = format_prefix + a_short_list + " " +
"{" + "\"b\":\"" + b_short_list + "\",\"c\":\"" + c_short_list + "\"}" + " " +
"[\"" + d_short_list + "\",\"" + e_short_list + "\"]" + " " +
base64_encode( a_blob.data.data(), a_blob.data.size() ) + "=" + " " +
base64_encode( a_blob.data.data(), a_blob.data.size() ) + " " +
g_short_list;

BOOST_CHECK_EQUAL( result, target_result);
BOOST_CHECK_LT(result.size(), 1024 + 3 * mu.size());
}
}

BOOST_AUTO_TEST_CASE(variant_blob)
{
// Some test cases from https://github.com/ReneNyffenegger/cpp-base64
{
std::string a17_orig = "aaaaaaaaaaaaaaaaa";
std::string a17_encoded = "YWFhYWFhYWFhYWFhYWFhYWE=";
fc::mutable_variant_object mu;
mu("blob", blob{{a17_orig.begin(), a17_orig.end()}});
mu("str", a17_encoded);

BOOST_CHECK_EQUAL(mu["blob"].as_string(), a17_encoded);
std::vector<char> b64 = mu["str"].as_blob().data;
std::string_view b64_str(b64.data(), b64.size());
BOOST_CHECK_EQUAL(b64_str, a17_orig);
}
{
std::string s_6364 = "\x03" "\xef" "\xff" "\xf9";
std::string s_6364_encoded = "A+//+Q==";
fc::mutable_variant_object mu;
mu("blob", blob{{s_6364.begin(), s_6364.end()}});
mu("str", s_6364_encoded);

BOOST_CHECK_EQUAL(mu["blob"].as_string(), s_6364_encoded);
std::vector<char> b64 = mu["str"].as_blob().data;
std::string_view b64_str(b64.data(), b64.size());
BOOST_CHECK_EQUAL(b64_str, s_6364);
}
{
std::string org = "abc";
std::string encoded = "YWJj";

fc::mutable_variant_object mu;
mu("blob", blob{{org.begin(), org.end()}});
mu("str", encoded);

BOOST_CHECK_EQUAL(mu["blob"].as_string(), encoded);
std::vector<char> b64 = mu["str"].as_blob().data;
std::string_view b64_str(b64.data(), b64.size());
BOOST_CHECK_EQUAL(b64_str, org);
}
}

BOOST_AUTO_TEST_CASE(variant_blob_backwards_compatibility)
{
// pre-5.0 variant would add an additional `=` as a flag that the blob data was base64 encoded
// verify variant can process encoded data with the extra `=`
{
std::string a17_orig = "aaaaaaaaaaaaaaaaa";
std::string a17_encoded = "YWFhYWFhYWFhYWFhYWFhYWE=";
std::string a17_encoded_old = a17_encoded + '=';
fc::mutable_variant_object mu;
mu("blob", blob{{a17_orig.begin(), a17_orig.end()}});
mu("str", a17_encoded_old);

BOOST_CHECK_EQUAL(mu["blob"].as_string(), a17_encoded);
std::vector<char> b64 = mu["str"].as_blob().data;
std::string_view b64_str(b64.data(), b64.size());
BOOST_CHECK_EQUAL(b64_str, a17_orig);
}
{
std::string org = "abc";
std::string encoded = "YWJj";
std::string encoded_old = encoded + '=';

fc::mutable_variant_object mu;
mu("blob", blob{{org.begin(), org.end()}});
mu("str", encoded_old);

BOOST_CHECK_EQUAL(mu["blob"].as_string(), encoded);
std::vector<char> b64 = mu["str"].as_blob().data;
std::string_view b64_str(b64.data(), b64.size());
BOOST_CHECK_EQUAL(b64_str, org);
}
}

BOOST_AUTO_TEST_SUITE_END()
2 changes: 1 addition & 1 deletion tests/nodeos_run_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
errFileName=f"{cluster.nodeosLogPath}/node_00/stderr.txt"
if args.error_log_path:
errFileName=args.error_log_path
walletMgr=WalletMgr(True, port=walletPort)
walletMgr=WalletMgr(True, port=walletPort, keepRunning=args.leave_running, keepLogs=args.keep_logs)
testSuccessful=False
dontBootstrap=sanityTest # intent is to limit the scope of the sanity test to just verifying that nodes can be started

Expand Down