From 2b68d7ec327be7da8998ffc65a5c7c246f3df964 Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Thu, 23 Mar 2023 14:19:37 +0100 Subject: [PATCH 01/14] src/WIF.cpp: move _calculate_sig_checksum() to top of file, so we dont need to add a function declaration. --- src/WIF.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/WIF.cpp b/src/WIF.cpp index 7a6f5c8..2c2ca4b 100644 --- a/src/WIF.cpp +++ b/src/WIF.cpp @@ -31,6 +31,20 @@ namespace libeosio { #define PRIV_KEY_PREFIX 0x80 /* 0x80 for "Bitcoin mainnet". Always used by EOS. */ +// Just to make it "harder" the calculated checksum for a signature +// has a "K1" suffix that is not present in the WIF encoded string. +// So this function is a quick hack to calculate it. +// +// Should implement and use Init/Update/Finalize hash functions to do it inplace. +checksum_t _calculate_sig_checksum(const unsigned char *in) { + unsigned char buf[EC_SIGNATURE_SIZE + 2]; + + memcpy(buf, in, EC_SIGNATURE_SIZE); + memcpy(buf + EC_SIGNATURE_SIZE, "K1", 2); + + return checksum_ripemd160(buf, EC_SIGNATURE_SIZE + 2); +} + std::string wif_priv_encode(const ec_privkey_t& priv) { checksum_t check; @@ -112,20 +126,6 @@ void wif_print_key(const struct ec_keypair *key, const std::string& prefix) { std::cout << "Private: " << wif_priv_encode(key->secret) << std::endl; } -// Just to make it "harder" the calculated checksum for a signature -// has a "K1" suffix that is not present in the WIF encoded string. -// So this function is a quick hack to calculate it. -// -// Should implement and use Init/Update/Finalize hash functions to do it inplace. -checksum_t _calculate_sig_checksum(const unsigned char *in) { - unsigned char buf[EC_SIGNATURE_SIZE + 2]; - - memcpy(buf, in, EC_SIGNATURE_SIZE); - memcpy(buf + EC_SIGNATURE_SIZE, "K1", 2); - - return checksum_ripemd160(buf, EC_SIGNATURE_SIZE + 2); -} - bool wif_sig_decode(ec_signature_t& sig, const std::string& data) { checksum_t checksum; From f8630de09812df63b70adaed7444d3f4f1050232 Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Thu, 23 Mar 2023 18:26:55 +0100 Subject: [PATCH 02/14] tests/WIF/pub_decode.cpp: fix test name. --- tests/WIF/pub_decode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/WIF/pub_decode.cpp b/tests/WIF/pub_decode.cpp index 7898918..8004407 100644 --- a/tests/WIF/pub_decode.cpp +++ b/tests/WIF/pub_decode.cpp @@ -4,7 +4,7 @@ #include #include -TEST_CASE("WIF::wif_pub_encode") { +TEST_CASE("WIF::wif_pub_decode [legacy]") { struct testcase { const char* name; std::string key; From 9114c173150bdf07c16623c0479f435566e8b1e2 Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Thu, 23 Mar 2023 19:14:51 +0100 Subject: [PATCH 03/14] include/libeosio/WIF.hpp: adding prefixes constants. --- include/libeosio/WIF.hpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/libeosio/WIF.hpp b/include/libeosio/WIF.hpp index 9eca7fc..a402607 100644 --- a/include/libeosio/WIF.hpp +++ b/include/libeosio/WIF.hpp @@ -29,6 +29,14 @@ namespace libeosio { +/** + * Key prefixes. (strings that is not equal to these prefixes are treated as legacy format.) + */ +const std::string WIF_PUB_LEG = "EOS"; +const std::string WIF_PUB_K1 = "PUB_K1_"; +const std::string WIF_PVT_K1 = "PVT_K1_"; + + /** * Encode an EC private key to WIF String. */ From ebb421902464f127f0be18f7d38d60104019643b Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Thu, 23 Mar 2023 19:15:25 +0100 Subject: [PATCH 04/14] WIF: Support PUB_K1 format. --- include/libeosio/WIF.hpp | 6 ++--- src/WIF.cpp | 56 +++++++++++++++++++++++++++++----------- tests/WIF/pub_decode.cpp | 28 ++++++++++++++++++++ tests/WIF/pub_encode.cpp | 36 +++++++++++++++++++++----- 4 files changed, 101 insertions(+), 25 deletions(-) diff --git a/include/libeosio/WIF.hpp b/include/libeosio/WIF.hpp index a402607..dffee92 100644 --- a/include/libeosio/WIF.hpp +++ b/include/libeosio/WIF.hpp @@ -50,17 +50,17 @@ bool wif_priv_decode(ec_privkey_t& priv, const std::string& data); /** * Encode an EC public key to WIF String. */ -std::string wif_pub_encode(const ec_pubkey_t& pub, const std::string& prefix = "EOS"); +std::string wif_pub_encode(const ec_pubkey_t& pub, const std::string& prefix = WIF_PUB_LEG); /** * Decode an WIF String to EC public key */ -bool wif_pub_decode(ec_pubkey_t& pub, const std::string& data, size_t prefix_length = 3); +bool wif_pub_decode(ec_pubkey_t& pub, const std::string& data); /** * Prints an EC keypair in WIF format to standard out. */ -void wif_print_key(const struct ec_keypair *key, const std::string& prefix = "EOS"); +void wif_print_key(const struct ec_keypair *key, const std::string& prefix = WIF_PUB_LEG); /** * Signatures diff --git a/src/WIF.cpp b/src/WIF.cpp index 2c2ca4b..0d8fd2a 100644 --- a/src/WIF.cpp +++ b/src/WIF.cpp @@ -31,18 +31,18 @@ namespace libeosio { #define PRIV_KEY_PREFIX 0x80 /* 0x80 for "Bitcoin mainnet". Always used by EOS. */ -// Just to make it "harder" the calculated checksum for a signature -// has a "K1" suffix that is not present in the WIF encoded string. +// Just to make it "harder" the calculated checksum for a signature (k1) and pub/priv keys in k1/r1 format. +// has a suffix that is not present in the WIF encoded string. // So this function is a quick hack to calculate it. // // Should implement and use Init/Update/Finalize hash functions to do it inplace. -checksum_t _calculate_sig_checksum(const unsigned char *in) { - unsigned char buf[EC_SIGNATURE_SIZE + 2]; +checksum_t _checksum_suffix(const unsigned char *in, size_t size, const char *suffix) { + unsigned char buf[size + 2]; - memcpy(buf, in, EC_SIGNATURE_SIZE); - memcpy(buf + EC_SIGNATURE_SIZE, "K1", 2); + memcpy(buf, in, size); + memcpy(buf + size, suffix, 2); - return checksum_ripemd160(buf, EC_SIGNATURE_SIZE + 2); + return checksum_ripemd160(buf, size + 2); } std::string wif_priv_encode(const ec_privkey_t& priv) { @@ -89,20 +89,42 @@ bool wif_priv_decode(ec_privkey_t& priv, const std::string& data) { std::string wif_pub_encode(const ec_pubkey_t& pub, const std::string& prefix) { - checksum_t check = checksum_ripemd160(pub.data(), pub.size()); + checksum_t check; unsigned char buf[EC_PUBKEY_SIZE + CHECKSUM_SIZE]; memcpy(buf, pub.data(), pub.size()); + + + if (prefix == WIF_PUB_K1) { + check = _checksum_suffix(buf, EC_PUBKEY_SIZE, "K1"); + } + // Legacy + else { + check = checksum_ripemd160(pub.data(), pub.size()); + } + memcpy(buf + EC_PUBKEY_SIZE, check.data(), check.size()); - return prefix.substr(0, 3) + base58_encode(buf, buf + sizeof(buf)); + return prefix + base58_encode(buf, buf + sizeof(buf)); } -bool wif_pub_decode(ec_pubkey_t& pub, const std::string& data, size_t prefix_length) { +bool wif_pub_decode(ec_pubkey_t& pub, const std::string& data) { + const char *suffix; + int offset; std::vector buf; - if (!base58_decode(data.c_str() + prefix_length, buf)) { + // Check prefix + if (data.substr(0, WIF_PUB_K1.size()) == WIF_PUB_K1) { + suffix = "K1"; + offset = WIF_PUB_K1.size(); + } else { + // Legacy + suffix = ""; + offset = 3; + } + + if (!base58_decode(data.c_str() + offset, buf)) { return false; } @@ -110,8 +132,12 @@ bool wif_pub_decode(ec_pubkey_t& pub, const std::string& data, size_t prefix_len return false; } - // Calculate and validate checksum - if (!checksum_validate(buf.data(), buf.size())) { + if (suffix[0] != '\0') { + checksum_t check = _checksum_suffix(buf.data(), EC_PUBKEY_SIZE, suffix); + if (memcmp(buf.data() + EC_PUBKEY_SIZE, check.data(), CHECKSUM_SIZE)) { + return false; + } + } else if (!checksum_validate(buf.data(), buf.size())) { return false; } @@ -145,7 +171,7 @@ bool wif_sig_decode(ec_signature_t& sig, const std::string& data) { } // Calculate checksum - checksum = _calculate_sig_checksum(buf.data()); + checksum = _checksum_suffix(buf.data(), EC_SIGNATURE_SIZE, "K1"); // And validate if (memcmp(buf.data() + EC_SIGNATURE_SIZE, checksum.data(), CHECKSUM_SIZE)) { @@ -160,7 +186,7 @@ bool wif_sig_decode(ec_signature_t& sig, const std::string& data) { std::string wif_sig_encode(const ec_signature_t& sig) { unsigned char buf[EC_SIGNATURE_SIZE + CHECKSUM_SIZE]; - checksum_t check = _calculate_sig_checksum(sig.data()); + checksum_t check = _checksum_suffix(sig.data(), EC_SIGNATURE_SIZE, "K1"); memcpy(buf, sig.data(), sig.size()); memcpy(buf + EC_SIGNATURE_SIZE, check.data(), check.size()); diff --git a/tests/WIF/pub_decode.cpp b/tests/WIF/pub_decode.cpp index 8004407..36ccad1 100644 --- a/tests/WIF/pub_decode.cpp +++ b/tests/WIF/pub_decode.cpp @@ -31,3 +31,31 @@ TEST_CASE("WIF::wif_pub_decode [legacy]") { } } } + +TEST_CASE("WIF::wif_pub_decode [k1]") { + struct testcase { + const char* name; + std::string key; + libeosio::ec_pubkey_t expected; + bool expectedRet; + }; + + std::vector tests { + { "one", "PUB_K1_7kzJ5iFBmQWWT1LiWgAiocESD7TTNuuPCdYREUQysruq7AxzWu", { 0x03, 0x7a, 0x0e, 0x6b, 0xfd, 0xe4, 0xf1, 0xad, 0x36, 0x3f, 0x3a, 0xf9, 0xe0, 0x93, 0x63, 0x5a, 0xa9, 0x99, 0x21, 0x15, 0xbc, 0x23, 0x35, 0x75, 0x13, 0x69, 0x55, 0xee, 0x3f, 0xf8, 0xfd, 0x97, 0xec }, true }, + { "two", "PUB_K1_5c9HkNCJLDebe2Wvapp8bpB38Pf1QWNpkrsFy3mshg7DViSUUa", { 0x02, 0x5e, 0x94, 0xa5, 0xe7, 0x9f, 0x66, 0x37, 0x55, 0x7e, 0xc2, 0x28, 0x30, 0x40, 0x82, 0x9a, 0x38, 0x72, 0x10, 0x96, 0x6e, 0x15, 0xb7, 0xa5, 0x8a, 0x27, 0x9a, 0x71, 0x06, 0xa7, 0x64, 0x23, 0x30 }, true }, + { "three", "PUB_K1_8SwZMY8DChbbmRKS3wdHCAbv1VWgTRmQEDSaLyJk8pG4wKBXpw", { 0x03, 0xd4, 0xc6, 0x2a, 0xdc, 0x11, 0x1c, 0x65, 0x7a, 0x9f, 0x5b, 0xba, 0x96, 0x3f, 0xbb, 0x2a, 0x69, 0x2e, 0xc5, 0x4a, 0x48, 0x3b, 0xa3, 0x5f, 0x2a, 0x37, 0x6c, 0x59, 0x95, 0xb1, 0x95, 0x1c, 0xc9 }, true }, + { "wrong_checksum", "PUB_K1_8SwZMY8DChbbmRKS3wdHCAbv1VWgTRmQEDSaLyJk8pG4wKBXgE", { 0x0 }, false }, + { "wrong_length", "PUB_K1_7kzJ5iFBmQWWT1LiWgAiocESD7TT", { 0x0 }, false }, + { "not_base58", "PUB_K1_7IIIIIOOOO", { 0x0 }, false } + }; + + for(auto it = tests.begin(); it != tests.end(); it++) { + + SUBCASE(it->name) { + libeosio::ec_pubkey_t result = { 0x0 }; + + CHECK( libeosio::wif_pub_decode(result, it->key) == it->expectedRet ); + CHECK( result == it->expected ); + } + } +} diff --git a/tests/WIF/pub_encode.cpp b/tests/WIF/pub_encode.cpp index cb6d627..d3e548f 100644 --- a/tests/WIF/pub_encode.cpp +++ b/tests/WIF/pub_encode.cpp @@ -4,29 +4,29 @@ #include #include -TEST_CASE("WIF::wif_pub_encode") { +TEST_CASE("WIF::wif_pub_encode [legacy]") { struct testcase { std::string name; + const std::string prefix; libeosio::ec_pubkey_t key; std::string expected; }; std::vector tests { - { "one", { 0x03, 0x7a, 0x0e, 0x6b, 0xfd, 0xe4, 0xf1, 0xad, 0x36, 0x3f, 0x3a, 0xf9, 0xe0, 0x93, 0x63, 0x5a, 0xa9, 0x99, 0x21, 0x15, 0xbc, 0x23, 0x35, 0x75, 0x13, 0x69, 0x55, 0xee, 0x3f, 0xf8, 0xfd, 0x97, 0xec }, "EOS7kzJ5iFBmQWWT1LiWgAiocESD7TTNuuPCdYREUQysruq8VeFKy" }, - { "two", { 0x02, 0x5e, 0x94, 0xa5, 0xe7, 0x9f, 0x66, 0x37, 0x55, 0x7e, 0xc2, 0x28, 0x30, 0x40, 0x82, 0x9a, 0x38, 0x72, 0x10, 0x96, 0x6e, 0x15, 0xb7, 0xa5, 0x8a, 0x27, 0x9a, 0x71, 0x06, 0xa7, 0x64, 0x23, 0x30 }, "EOS5c9HkNCJLDebe2Wvapp8bpB38Pf1QWNpkrsFy3mshg7DZfPNeA" }, - { "three", { 0x03, 0xd4, 0xc6, 0x2a, 0xdc, 0x11, 0x1c, 0x65, 0x7a, 0x9f, 0x5b, 0xba, 0x96, 0x3f, 0xbb, 0x2a, 0x69, 0x2e, 0xc5, 0x4a, 0x48, 0x3b, 0xa3, 0x5f, 0x2a, 0x37, 0x6c, 0x59, 0x95, 0xb1, 0x95, 0x1c, 0xc9 }, "EOS8SwZMY8DChbbmRKS3wdHCAbv1VWgTRmQEDSaLyJk8pG4wm8BJF" } + { "one", libeosio::WIF_PUB_LEG, { 0x03, 0x7a, 0x0e, 0x6b, 0xfd, 0xe4, 0xf1, 0xad, 0x36, 0x3f, 0x3a, 0xf9, 0xe0, 0x93, 0x63, 0x5a, 0xa9, 0x99, 0x21, 0x15, 0xbc, 0x23, 0x35, 0x75, 0x13, 0x69, 0x55, 0xee, 0x3f, 0xf8, 0xfd, 0x97, 0xec }, "EOS7kzJ5iFBmQWWT1LiWgAiocESD7TTNuuPCdYREUQysruq8VeFKy" }, + { "two", libeosio::WIF_PUB_LEG, { 0x02, 0x5e, 0x94, 0xa5, 0xe7, 0x9f, 0x66, 0x37, 0x55, 0x7e, 0xc2, 0x28, 0x30, 0x40, 0x82, 0x9a, 0x38, 0x72, 0x10, 0x96, 0x6e, 0x15, 0xb7, 0xa5, 0x8a, 0x27, 0x9a, 0x71, 0x06, 0xa7, 0x64, 0x23, 0x30 }, "EOS5c9HkNCJLDebe2Wvapp8bpB38Pf1QWNpkrsFy3mshg7DZfPNeA" }, + { "three", libeosio::WIF_PUB_LEG, { 0x03, 0xd4, 0xc6, 0x2a, 0xdc, 0x11, 0x1c, 0x65, 0x7a, 0x9f, 0x5b, 0xba, 0x96, 0x3f, 0xbb, 0x2a, 0x69, 0x2e, 0xc5, 0x4a, 0x48, 0x3b, 0xa3, 0x5f, 0x2a, 0x37, 0x6c, 0x59, 0x95, 0xb1, 0x95, 0x1c, 0xc9 }, "EOS8SwZMY8DChbbmRKS3wdHCAbv1VWgTRmQEDSaLyJk8pG4wm8BJF" } }; for(auto it = tests.begin(); it != tests.end(); it++) { SUBCASE(it->name.c_str()) { - CHECK( libeosio::wif_pub_encode(it->key) == it->expected ); + CHECK( libeosio::wif_pub_encode(it->key, it->prefix) == it->expected ); } } } - -TEST_CASE("WIF::wif_pub_encode [prefix]") { +TEST_CASE("WIF::wif_pub_encode [custom prefix]") { struct testcase { std::string name; @@ -47,3 +47,25 @@ TEST_CASE("WIF::wif_pub_encode [prefix]") { } } } + +TEST_CASE("WIF::wif_pub_encode [k1]") { + struct testcase { + std::string name; + const std::string prefix; + libeosio::ec_pubkey_t key; + std::string expected; + }; + + std::vector tests { + { "one", libeosio::WIF_PUB_K1, { 0x03, 0x7a, 0x0e, 0x6b, 0xfd, 0xe4, 0xf1, 0xad, 0x36, 0x3f, 0x3a, 0xf9, 0xe0, 0x93, 0x63, 0x5a, 0xa9, 0x99, 0x21, 0x15, 0xbc, 0x23, 0x35, 0x75, 0x13, 0x69, 0x55, 0xee, 0x3f, 0xf8, 0xfd, 0x97, 0xec }, "PUB_K1_7kzJ5iFBmQWWT1LiWgAiocESD7TTNuuPCdYREUQysruq7AxzWu" }, + { "two", libeosio::WIF_PUB_K1, { 0x02, 0x5e, 0x94, 0xa5, 0xe7, 0x9f, 0x66, 0x37, 0x55, 0x7e, 0xc2, 0x28, 0x30, 0x40, 0x82, 0x9a, 0x38, 0x72, 0x10, 0x96, 0x6e, 0x15, 0xb7, 0xa5, 0x8a, 0x27, 0x9a, 0x71, 0x06, 0xa7, 0x64, 0x23, 0x30 }, "PUB_K1_5c9HkNCJLDebe2Wvapp8bpB38Pf1QWNpkrsFy3mshg7DViSUUa" }, + { "three", libeosio::WIF_PUB_K1, { 0x03, 0xd4, 0xc6, 0x2a, 0xdc, 0x11, 0x1c, 0x65, 0x7a, 0x9f, 0x5b, 0xba, 0x96, 0x3f, 0xbb, 0x2a, 0x69, 0x2e, 0xc5, 0x4a, 0x48, 0x3b, 0xa3, 0x5f, 0x2a, 0x37, 0x6c, 0x59, 0x95, 0xb1, 0x95, 0x1c, 0xc9 }, "PUB_K1_8SwZMY8DChbbmRKS3wdHCAbv1VWgTRmQEDSaLyJk8pG4wKBXpw" } + }; + + for(auto it = tests.begin(); it != tests.end(); it++) { + + SUBCASE(it->name.c_str()) { + CHECK( libeosio::wif_pub_encode(it->key, it->prefix) == it->expected ); + } + } +} \ No newline at end of file From 3abfc488e8ed4a5c3d875351dc64ac6b0589dac1 Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Sat, 25 Mar 2023 15:24:05 +0100 Subject: [PATCH 05/14] Adding src/wif/codec.hpp --- src/wif/codec.hpp | 52 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 src/wif/codec.hpp diff --git a/src/wif/codec.hpp b/src/wif/codec.hpp new file mode 100644 index 0000000..2c38d44 --- /dev/null +++ b/src/wif/codec.hpp @@ -0,0 +1,52 @@ +/** + * MIT License + * + * Copyright (c) 2019-2021 EOS Sw/eden + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef LIBEOSIO_CODEC_H +#define LIBEOSIO_CODEC_H + +#include +#include + +namespace libeosio { namespace internal { + +/** + * Public-key encoders + */ +typedef void (*pub_encoder_t)(const ec_pubkey_t& key, unsigned char *buf); + +void pub_encoder_legacy(const ec_pubkey_t& key, unsigned char *buf); + +void pub_encoder_k1(const ec_pubkey_t& key, unsigned char *buf); + +/** + * Public-key decoders + */ +typedef bool (*pub_decoder_t)(const std::vector& buf, ec_pubkey_t& key); + +bool pub_decoder_legacy(const std::vector& buf, ec_pubkey_t& key); + +bool pub_decoder_k1(const std::vector& buf, ec_pubkey_t& key); + +}} // namespace libeosio::internal + +#endif /* LIBEOSIO_CODEC_H */ From 6793762fbb91e8e5a8ad72326ef1d9c380e2dd49 Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Sat, 25 Mar 2023 15:24:49 +0100 Subject: [PATCH 06/14] Adding src/wif/legacy.cpp --- src/wif/legacy.cpp | 48 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 src/wif/legacy.cpp diff --git a/src/wif/legacy.cpp b/src/wif/legacy.cpp new file mode 100644 index 0000000..47a8251 --- /dev/null +++ b/src/wif/legacy.cpp @@ -0,0 +1,48 @@ +/** + * MIT License + * + * Copyright (c) 2019-2021 EOS Sw/eden + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include "codec.hpp" + +namespace libeosio { namespace internal { + +void pub_encoder_legacy(const ec_pubkey_t& key, unsigned char *buf) { + + checksum_t check = checksum_ripemd160(key.data(), EC_PUBKEY_SIZE); + + memcpy(buf, key.data(), EC_PUBKEY_SIZE); + memcpy(buf + EC_PUBKEY_SIZE, check.data(), check.size()); +} + +bool pub_decoder_legacy(const std::vector& buf, ec_pubkey_t& key) { + + if (!checksum_validate(buf.data(), buf.size())) { + return false; + } + + memcpy(key.data(), buf.data(), EC_PUBKEY_SIZE); + return true; +} + +}} // namespace libeosio::internal \ No newline at end of file From 053f91c74b86123904dd9dec72097ec8c9e68525 Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Sat, 25 Mar 2023 15:24:57 +0100 Subject: [PATCH 07/14] Adding src/wif/k1.cpp --- src/wif/k1.cpp | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 src/wif/k1.cpp diff --git a/src/wif/k1.cpp b/src/wif/k1.cpp new file mode 100644 index 0000000..ea29f4e --- /dev/null +++ b/src/wif/k1.cpp @@ -0,0 +1,64 @@ +/** + * MIT License + * + * Copyright (c) 2019-2021 EOS Sw/eden + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include "codec.hpp" + +namespace libeosio { namespace internal { + +// Just to make it "harder" the calculated checksum for a signature (k1) and pub/priv keys in k1/r1 format. +// has a suffix that is not present in the WIF encoded string. +// So this function is a quick hack to calculate it. +// +// Should implement and use Init/Update/Finalize hash functions to do it inplace. +checksum_t _checksum_suffix(const unsigned char *in, size_t size, const char *suffix) { + unsigned char buf[size + 2]; + + memcpy(buf, in, size); + memcpy(buf + size, suffix, 2); + + return checksum_ripemd160(buf, size + 2); +} + +void pub_encoder_k1(const ec_pubkey_t& key, unsigned char *buf) { + + checksum_t check = _checksum_suffix(key.data(), EC_PUBKEY_SIZE, "K1"); + + memcpy(buf, key.data(), EC_PUBKEY_SIZE); + memcpy(buf + EC_PUBKEY_SIZE, check.data(), check.size()); +} + +bool pub_decoder_k1(const std::vector& buf, ec_pubkey_t& key) { + + checksum_t check = _checksum_suffix(buf.data(), EC_PUBKEY_SIZE, "K1"); + + if (memcmp(buf.data() + EC_PUBKEY_SIZE, check.data(), CHECKSUM_SIZE)) { + return false; + } + + memcpy(key.data(), buf.data(), EC_PUBKEY_SIZE); + return true; +} + +}} // namespace libeosio::internal \ No newline at end of file From ea411793a219d611d7dd0d808b6c8e09d2b1a1e4 Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Sat, 25 Mar 2023 16:02:09 +0100 Subject: [PATCH 08/14] src/WIF.cpp: use wif/codec.hpp --- CMakeLists.txt | 2 ++ src/WIF.cpp | 31 +++++++++---------------------- 2 files changed, 11 insertions(+), 22 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 46dec36..990af40 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,6 +53,8 @@ add_library( ${LIB_NAME} STATIC src/base58.cpp src/ec.cpp src/WIF.cpp + src/wif/k1.cpp + src/wif/legacy.cpp src/openssl/hash.cpp ) diff --git a/src/WIF.cpp b/src/WIF.cpp index 0d8fd2a..9e904c6 100644 --- a/src/WIF.cpp +++ b/src/WIF.cpp @@ -26,6 +26,7 @@ #include #include #include +#include "wif/codec.hpp" namespace libeosio { @@ -91,36 +92,33 @@ std::string wif_pub_encode(const ec_pubkey_t& pub, const std::string& prefix) { checksum_t check; unsigned char buf[EC_PUBKEY_SIZE + CHECKSUM_SIZE]; - - memcpy(buf, pub.data(), pub.size()); - + internal::pub_encoder_t encoder; if (prefix == WIF_PUB_K1) { - check = _checksum_suffix(buf, EC_PUBKEY_SIZE, "K1"); + encoder = internal::pub_encoder_k1; } // Legacy else { - check = checksum_ripemd160(pub.data(), pub.size()); + encoder = internal::pub_encoder_legacy; } - memcpy(buf + EC_PUBKEY_SIZE, check.data(), check.size()); + encoder(pub, buf); return prefix + base58_encode(buf, buf + sizeof(buf)); } bool wif_pub_decode(ec_pubkey_t& pub, const std::string& data) { - const char *suffix; + internal::pub_decoder_t decoder = internal::pub_decoder_legacy; int offset; std::vector buf; // Check prefix if (data.substr(0, WIF_PUB_K1.size()) == WIF_PUB_K1) { - suffix = "K1"; - offset = WIF_PUB_K1.size(); + decoder = internal::pub_decoder_k1; + offset = WIF_PUB_K1.size(); } else { // Legacy - suffix = ""; offset = 3; } @@ -132,18 +130,7 @@ bool wif_pub_decode(ec_pubkey_t& pub, const std::string& data) { return false; } - if (suffix[0] != '\0') { - checksum_t check = _checksum_suffix(buf.data(), EC_PUBKEY_SIZE, suffix); - if (memcmp(buf.data() + EC_PUBKEY_SIZE, check.data(), CHECKSUM_SIZE)) { - return false; - } - } else if (!checksum_validate(buf.data(), buf.size())) { - return false; - } - - // Copy data to output - memcpy(pub.data(), buf.data(), pub.size()); - return true; + return decoder(buf, pub); } void wif_print_key(const struct ec_keypair *key, const std::string& prefix) { From 1aa6906ba24a04ab0ab9cdb88927ceb9d4797ebb Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Sat, 25 Mar 2023 16:25:02 +0100 Subject: [PATCH 09/14] WIF: Support PVT_K1 format. --- include/libeosio/WIF.hpp | 3 ++- src/WIF.cpp | 57 ++++++++++++++++++--------------------- src/wif/codec.hpp | 18 +++++++++++++ src/wif/k1.cpp | 24 +++++++++++++++++ src/wif/legacy.cpp | 30 +++++++++++++++++++++ tests/WIF/priv_decode.cpp | 29 +++++++++++++++++++- tests/WIF/priv_encode.cpp | 34 +++++++++++++++++++---- 7 files changed, 157 insertions(+), 38 deletions(-) diff --git a/include/libeosio/WIF.hpp b/include/libeosio/WIF.hpp index dffee92..07f2a94 100644 --- a/include/libeosio/WIF.hpp +++ b/include/libeosio/WIF.hpp @@ -34,13 +34,14 @@ namespace libeosio { */ const std::string WIF_PUB_LEG = "EOS"; const std::string WIF_PUB_K1 = "PUB_K1_"; +const std::string WIF_PVT_LEG = ""; const std::string WIF_PVT_K1 = "PVT_K1_"; /** * Encode an EC private key to WIF String. */ -std::string wif_priv_encode(const ec_privkey_t& priv); +std::string wif_priv_encode(const ec_privkey_t& priv, const std::string& prefix = WIF_PVT_LEG); /** * Decode an WIF String to EC private key diff --git a/src/WIF.cpp b/src/WIF.cpp index 9e904c6..0ace329 100644 --- a/src/WIF.cpp +++ b/src/WIF.cpp @@ -30,8 +30,6 @@ namespace libeosio { -#define PRIV_KEY_PREFIX 0x80 /* 0x80 for "Bitcoin mainnet". Always used by EOS. */ - // Just to make it "harder" the calculated checksum for a signature (k1) and pub/priv keys in k1/r1 format. // has a suffix that is not present in the WIF encoded string. // So this function is a quick hack to calculate it. @@ -46,51 +44,48 @@ checksum_t _checksum_suffix(const unsigned char *in, size_t size, const char *su return checksum_ripemd160(buf, size + 2); } -std::string wif_priv_encode(const ec_privkey_t& priv) { +std::string wif_priv_encode(const ec_privkey_t& priv, const std::string& prefix) { checksum_t check; - // 1 byte extra for prefix. - unsigned char buf[1 + EC_PRIVKEY_SIZE + CHECKSUM_SIZE] = { PRIV_KEY_PREFIX }; + // 1 byte extra for legacy prefix prefix. + unsigned char buf[1 + EC_PRIVKEY_SIZE + CHECKSUM_SIZE] = { 0 }; + size_t len; - memcpy(buf + 1, priv.data(), priv.size()); + if (prefix == WIF_PVT_K1) { + len = internal::priv_encoder_k1(priv, buf); + } else if (prefix == WIF_PVT_LEG) { + len = internal::priv_encoder_legacy(priv, buf); + } else { + return ""; + } - // Checksum - check = checksum_sha256d(buf, 1 + EC_PRIVKEY_SIZE); - memcpy(buf + 1 + EC_PRIVKEY_SIZE, check.data(), check.size()); - - return base58_encode(buf, buf + sizeof(buf)); + return prefix + base58_encode(buf, buf + len); } bool wif_priv_decode(ec_privkey_t& priv, const std::string& data) { + uint8_t offset; std::vector buf; + internal::priv_decoder_t decoder = internal::priv_decoder_legacy; - if (!base58_decode(data, buf)) { + // Check prefix + if (data.substr(0, WIF_PVT_K1.size()) == WIF_PVT_K1) { + offset = WIF_PVT_K1.size(); + decoder = internal::priv_decoder_k1; + } else { + // Legacy + offset = 0; + } + + if (!base58_decode(data.c_str() + offset, buf)) { return false; } - if (buf.size() != 1 + EC_PRIVKEY_SIZE + CHECKSUM_SIZE) { - return false; - } - - // First byte is the prefix - if (buf[0] != PRIV_KEY_PREFIX) { - return false; - } - - // Calculate and validate checksum - if (!checksum_validate(buf.data(), buf.size())) { - return false; - } - - // Copy data to output - memcpy(priv.data(), buf.data() + 1, priv.size()); - return true; + return decoder(buf, priv); } std::string wif_pub_encode(const ec_pubkey_t& pub, const std::string& prefix) { - checksum_t check; unsigned char buf[EC_PUBKEY_SIZE + CHECKSUM_SIZE]; internal::pub_encoder_t encoder; @@ -136,7 +131,7 @@ bool wif_pub_decode(ec_pubkey_t& pub, const std::string& data) { void wif_print_key(const struct ec_keypair *key, const std::string& prefix) { std::cout << "Public: " << wif_pub_encode(key->pub, prefix) << std::endl; - std::cout << "Private: " << wif_priv_encode(key->secret) << std::endl; + std::cout << "Private: " << wif_priv_encode(key->secret, prefix) << std::endl; } bool wif_sig_decode(ec_signature_t& sig, const std::string& data) { diff --git a/src/wif/codec.hpp b/src/wif/codec.hpp index 2c38d44..3e235d3 100644 --- a/src/wif/codec.hpp +++ b/src/wif/codec.hpp @@ -47,6 +47,24 @@ bool pub_decoder_legacy(const std::vector& buf, ec_pubkey_t& key) bool pub_decoder_k1(const std::vector& buf, ec_pubkey_t& key); +/** + * Private-key encoders + */ +typedef size_t (*priv_encoder_t)(const ec_privkey_t&, unsigned char *); + +size_t priv_encoder_legacy(const ec_privkey_t& priv, unsigned char *buf); + +size_t priv_encoder_k1(const ec_privkey_t& priv, unsigned char *buf); + +/** + * Private-key decoders + */ +typedef bool (*priv_decoder_t)(const std::vector&, ec_privkey_t&); + +bool priv_decoder_legacy(const std::vector& buf, ec_privkey_t& priv); + +bool priv_decoder_k1(const std::vector& buf, ec_privkey_t& priv); + }} // namespace libeosio::internal #endif /* LIBEOSIO_CODEC_H */ diff --git a/src/wif/k1.cpp b/src/wif/k1.cpp index ea29f4e..3792dd1 100644 --- a/src/wif/k1.cpp +++ b/src/wif/k1.cpp @@ -61,4 +61,28 @@ bool pub_decoder_k1(const std::vector& buf, ec_pubkey_t& key) { return true; } +size_t priv_encoder_k1(const ec_privkey_t& priv, unsigned char *buf) { + checksum_t check = _checksum_suffix(priv.data(), EC_PRIVKEY_SIZE, "K1"); + + memcpy(buf, priv.data(), priv.size()); + memcpy(buf + EC_PRIVKEY_SIZE, check.data(), check.size()); + + return EC_PRIVKEY_SIZE + CHECKSUM_SIZE; +} + +bool priv_decoder_k1(const std::vector& buf, ec_privkey_t& priv) { + + if (buf.size() != EC_PRIVKEY_SIZE + CHECKSUM_SIZE) { + return false; + } + + checksum_t check = _checksum_suffix(buf.data(), EC_PRIVKEY_SIZE, "K1"); + if (memcmp(buf.data() + EC_PRIVKEY_SIZE, check.data(), CHECKSUM_SIZE)) { + return false; + } + + memcpy(priv.data(), buf.data(), priv.size()); + return true; +} + }} // namespace libeosio::internal \ No newline at end of file diff --git a/src/wif/legacy.cpp b/src/wif/legacy.cpp index 47a8251..96578e6 100644 --- a/src/wif/legacy.cpp +++ b/src/wif/legacy.cpp @@ -27,6 +27,8 @@ namespace libeosio { namespace internal { +#define PRIV_KEY_PREFIX 0x80 /* 0x80 for "Bitcoin mainnet". Always used by EOS. */ + void pub_encoder_legacy(const ec_pubkey_t& key, unsigned char *buf) { checksum_t check = checksum_ripemd160(key.data(), EC_PUBKEY_SIZE); @@ -45,4 +47,32 @@ bool pub_decoder_legacy(const std::vector& buf, ec_pubkey_t& key) return true; } +size_t priv_encoder_legacy(const ec_privkey_t& priv, unsigned char *buf) { + checksum_t check; + + buf[0] = PRIV_KEY_PREFIX; + memcpy(buf + 1, priv.data(), EC_PRIVKEY_SIZE); + check = checksum_sha256d(buf, 1 + EC_PRIVKEY_SIZE); + memcpy(buf + 1 + EC_PRIVKEY_SIZE, check.data(), check.size()); + + return 1 + EC_PRIVKEY_SIZE + CHECKSUM_SIZE; +} + +bool priv_decoder_legacy(const std::vector& buf, ec_privkey_t& priv) { + if (buf[0] != PRIV_KEY_PREFIX) { + return false; + } + + if (buf.size() != 1 + EC_PRIVKEY_SIZE + CHECKSUM_SIZE) { + return false; + } + + if (!checksum_validate(buf.data(), buf.size())) { + return false; + } + + memcpy(priv.data(), buf.data() + 1, priv.size()); + return true; +} + }} // namespace libeosio::internal \ No newline at end of file diff --git a/tests/WIF/priv_decode.cpp b/tests/WIF/priv_decode.cpp index d3d8c6a..5105e66 100644 --- a/tests/WIF/priv_decode.cpp +++ b/tests/WIF/priv_decode.cpp @@ -4,7 +4,7 @@ #include #include -TEST_CASE("WIF::wif_priv_decode") { +TEST_CASE("WIF::wif_priv_decode [legacy]") { struct testcase { std::string name; std::string key; @@ -29,4 +29,31 @@ TEST_CASE("WIF::wif_priv_decode") { } } +} + +TEST_CASE("WIF::wif_priv_decode [K1]") { + struct testcase { + std::string name; + std::string key; + libeosio::ec_privkey_t expected; + }; + + + std::vector tests { + { "one", "PVT_K1_6Mcb23muAxyXaSMhmB6B1mqkvLdWhtuFZmnZsxDczHRvQdp32", { 0x0C, 0x28, 0xFC, 0xA3, 0x86, 0xC7, 0xA2, 0x27, 0x60, 0x0B, 0x2F, 0xE5, 0x0B, 0x7C, 0xAE, 0x11, 0xEC, 0x86, 0xD3, 0xBF, 0x1F, 0xBE, 0x47, 0x1B, 0xE8, 0x98, 0x27, 0xE1, 0x9D, 0x72, 0xAA, 0x1D } }, + { "two", "PVT_K1_2DRBT8jmXT8k9ywNSSbufvhk1hLFhPzWJBpsE2jo12CDoFhcc1", { 0x9F, 0xE3, 0xE3, 0x2B, 0x3C, 0x4B, 0x6B, 0x91, 0x6E, 0x20, 0x6C, 0xB0, 0x91, 0xDF, 0x1F, 0x5E, 0x34, 0x32, 0x88, 0x0B, 0x41, 0x33, 0x86, 0xBD, 0xF2, 0x92, 0xFF, 0x23, 0x06, 0x43, 0xF2, 0x8C } }, + { "three", "PVT_K1_gJCsP4CwMv4gTkDXiZT8QFhs3NrSB7Sv22ANGrc8Svun9uC9C", { 0x59, 0x3A, 0x51, 0xB5, 0x5D, 0x56, 0xAA, 0xF0, 0x5B, 0xD9, 0xD1, 0x0E, 0x6B, 0x88, 0x6D, 0xF9, 0xC4, 0x37, 0x09, 0xB2, 0x4C, 0xEC, 0xBB, 0x63, 0x68, 0x92, 0xC2, 0x94, 0x31, 0x48, 0x71, 0x8C } } + }; + + for(auto it = tests.begin(); it != tests.end(); it++) { + + SUBCASE(it->name.c_str()) { + + libeosio::ec_privkey_t result; + + CHECK( libeosio::wif_priv_decode(result, it->key) ); + CHECK( result == it->expected ); + } + } + } \ No newline at end of file diff --git a/tests/WIF/priv_encode.cpp b/tests/WIF/priv_encode.cpp index 57a7173..bb08bd1 100644 --- a/tests/WIF/priv_encode.cpp +++ b/tests/WIF/priv_encode.cpp @@ -4,24 +4,48 @@ #include #include -TEST_CASE("WIF::wif_priv_encode") { +TEST_CASE("WIF::wif_priv_encode [Legacy]") { struct testcase { std::string name; + const std::string prefix; libeosio::ec_privkey_t key; std::string expected; }; std::vector tests { - { "one", { 0x0C, 0x28, 0xFC, 0xA3, 0x86, 0xC7, 0xA2, 0x27, 0x60, 0x0B, 0x2F, 0xE5, 0x0B, 0x7C, 0xAE, 0x11, 0xEC, 0x86, 0xD3, 0xBF, 0x1F, 0xBE, 0x47, 0x1B, 0xE8, 0x98, 0x27, 0xE1, 0x9D,0x72,0xAA,0x1D}, "5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ" }, - { "two", { 0x9F, 0xE3, 0xE3, 0x2B, 0x3C, 0x4B, 0x6B, 0x91, 0x6E, 0x20, 0x6C, 0xB0, 0x91, 0xDF, 0x1F, 0x5E, 0x34, 0x32, 0x88, 0x0B, 0x41, 0x33, 0x86, 0xBD, 0xF2, 0x92, 0xFF, 0x23, 0x06, 0x43, 0xF2, 0x8C}, "5K2hm8apqz281ANDQdtVzifpxcXFTqG5E7Fc6Q5V2ssqPRQ3urJ" }, - { "three", { 0x59, 0x3A, 0x51, 0xB5, 0x5D, 0x56, 0xAA, 0xF0, 0x5B, 0xD9, 0xD1, 0x0E, 0x6B, 0x88, 0x6D, 0xF9, 0xC4, 0x37, 0x09, 0xB2, 0x4C, 0xEC, 0xBB, 0x63, 0x68, 0x92, 0xC2, 0x94, 0x31, 0x48, 0x71, 0x8C}, "5JVanYq9HPvuKgr2FjATYB9NvTsJ4a3CAj5oPYKbr1Ja5MRLsZX" } + { "one", libeosio::WIF_PVT_LEG, { 0x0C, 0x28, 0xFC, 0xA3, 0x86, 0xC7, 0xA2, 0x27, 0x60, 0x0B, 0x2F, 0xE5, 0x0B, 0x7C, 0xAE, 0x11, 0xEC, 0x86, 0xD3, 0xBF, 0x1F, 0xBE, 0x47, 0x1B, 0xE8, 0x98, 0x27, 0xE1, 0x9D,0x72,0xAA,0x1D}, "5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ" }, + { "two", libeosio::WIF_PVT_LEG, { 0x9F, 0xE3, 0xE3, 0x2B, 0x3C, 0x4B, 0x6B, 0x91, 0x6E, 0x20, 0x6C, 0xB0, 0x91, 0xDF, 0x1F, 0x5E, 0x34, 0x32, 0x88, 0x0B, 0x41, 0x33, 0x86, 0xBD, 0xF2, 0x92, 0xFF, 0x23, 0x06, 0x43, 0xF2, 0x8C}, "5K2hm8apqz281ANDQdtVzifpxcXFTqG5E7Fc6Q5V2ssqPRQ3urJ" }, + { "three", libeosio::WIF_PVT_LEG, { 0x59, 0x3A, 0x51, 0xB5, 0x5D, 0x56, 0xAA, 0xF0, 0x5B, 0xD9, 0xD1, 0x0E, 0x6B, 0x88, 0x6D, 0xF9, 0xC4, 0x37, 0x09, 0xB2, 0x4C, 0xEC, 0xBB, 0x63, 0x68, 0x92, 0xC2, 0x94, 0x31, 0x48, 0x71, 0x8C}, "5JVanYq9HPvuKgr2FjATYB9NvTsJ4a3CAj5oPYKbr1Ja5MRLsZX" } }; for(auto it = tests.begin(); it != tests.end(); it++) { SUBCASE(it->name.c_str()) { - CHECK( libeosio::wif_priv_encode(it->key) == it->expected ); + CHECK( libeosio::wif_priv_encode(it->key, it->prefix) == it->expected ); + } + } +} + +TEST_CASE("WIF::wif_priv_encode [K1]") { + + struct testcase { + std::string name; + const std::string prefix; + libeosio::ec_privkey_t key; + std::string expected; + }; + + std::vector tests { + { "one", libeosio::WIF_PVT_K1, { 0x0C, 0x28, 0xFC, 0xA3, 0x86, 0xC7, 0xA2, 0x27, 0x60, 0x0B, 0x2F, 0xE5, 0x0B, 0x7C, 0xAE, 0x11, 0xEC, 0x86, 0xD3, 0xBF, 0x1F, 0xBE, 0x47, 0x1B, 0xE8, 0x98, 0x27, 0xE1, 0x9D,0x72,0xAA,0x1D}, "PVT_K1_6Mcb23muAxyXaSMhmB6B1mqkvLdWhtuFZmnZsxDczHRvQdp32" }, + { "two", libeosio::WIF_PVT_K1, { 0x9F, 0xE3, 0xE3, 0x2B, 0x3C, 0x4B, 0x6B, 0x91, 0x6E, 0x20, 0x6C, 0xB0, 0x91, 0xDF, 0x1F, 0x5E, 0x34, 0x32, 0x88, 0x0B, 0x41, 0x33, 0x86, 0xBD, 0xF2, 0x92, 0xFF, 0x23, 0x06, 0x43, 0xF2, 0x8C}, "PVT_K1_2DRBT8jmXT8k9ywNSSbufvhk1hLFhPzWJBpsE2jo12CDoFhcc1" }, + { "three", libeosio::WIF_PVT_K1, { 0x59, 0x3A, 0x51, 0xB5, 0x5D, 0x56, 0xAA, 0xF0, 0x5B, 0xD9, 0xD1, 0x0E, 0x6B, 0x88, 0x6D, 0xF9, 0xC4, 0x37, 0x09, 0xB2, 0x4C, 0xEC, 0xBB, 0x63, 0x68, 0x92, 0xC2, 0x94, 0x31, 0x48, 0x71, 0x8C}, "PVT_K1_gJCsP4CwMv4gTkDXiZT8QFhs3NrSB7Sv22ANGrc8Svun9uC9C" } + }; + + for(auto it = tests.begin(); it != tests.end(); it++) { + + SUBCASE(it->name.c_str()) { + CHECK( libeosio::wif_priv_encode(it->key, it->prefix) == it->expected ); } } } From 171db63de40e8916541066f612361d4af9fbc118 Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Sat, 25 Mar 2023 18:14:51 +0100 Subject: [PATCH 10/14] include/libeosio/WIF.hpp: Adding WIF_SIG_K1 constant. --- include/libeosio/WIF.hpp | 2 +- src/WIF.cpp | 10 +++------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/include/libeosio/WIF.hpp b/include/libeosio/WIF.hpp index 07f2a94..040c392 100644 --- a/include/libeosio/WIF.hpp +++ b/include/libeosio/WIF.hpp @@ -36,7 +36,7 @@ const std::string WIF_PUB_LEG = "EOS"; const std::string WIF_PUB_K1 = "PUB_K1_"; const std::string WIF_PVT_LEG = ""; const std::string WIF_PVT_K1 = "PVT_K1_"; - +const std::string WIF_SIG_K1 = "SIG_K1_"; /** * Encode an EC private key to WIF String. diff --git a/src/WIF.cpp b/src/WIF.cpp index 0ace329..aa07c3b 100644 --- a/src/WIF.cpp +++ b/src/WIF.cpp @@ -139,16 +139,12 @@ bool wif_sig_decode(ec_signature_t& sig, const std::string& data) { checksum_t checksum; std::vector buf; - if (data.substr(0, 7) != "SIG_K1_") { + if (data.substr(0, WIF_SIG_K1.length()) != WIF_SIG_K1) { // Invalid prefix return false; } - if (!base58_decode(data.c_str() + 7, buf)) { - return false; - } - - if (buf.size() != EC_SIGNATURE_SIZE + CHECKSUM_SIZE) { + if (!base58_decode(data.c_str() + WIF_SIG_K1.length(), buf)) { return false; } @@ -173,7 +169,7 @@ std::string wif_sig_encode(const ec_signature_t& sig) { memcpy(buf, sig.data(), sig.size()); memcpy(buf + EC_SIGNATURE_SIZE, check.data(), check.size()); - return "SIG_K1_" + base58_encode(buf, buf + sizeof(buf)); + return WIF_SIG_K1_" + base58_encode(buf, buf + sizeof(buf)); } } // namespace libeosio From abdd84f257326856ccbcf173726d4e5f955b4c70 Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Sat, 25 Mar 2023 18:15:44 +0100 Subject: [PATCH 11/14] src/wif/codec.hpp: Adding signature encoders/decoders --- src/wif/codec.hpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/wif/codec.hpp b/src/wif/codec.hpp index 3e235d3..eb3e5ca 100644 --- a/src/wif/codec.hpp +++ b/src/wif/codec.hpp @@ -65,6 +65,20 @@ bool priv_decoder_legacy(const std::vector& buf, ec_privkey_t& pr bool priv_decoder_k1(const std::vector& buf, ec_privkey_t& priv); +/** + * Signature encoders + */ +typedef void (*sig_encoder_t)(const ec_signature_t& sig, unsigned char *buf); + +void sig_encoder_k1(const ec_signature_t& sig, unsigned char *buf); + +/** + * Signature decoders + */ +typedef bool (*sig_decoder_t)(const std::vector& buf, ec_signature_t& sig); + +bool sig_decoder_k1(const std::vector& buf, ec_signature_t& sig); + }} // namespace libeosio::internal #endif /* LIBEOSIO_CODEC_H */ From be6c98f1bd5574cc174f8d15a77a2e26dd4fd949 Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Sat, 25 Mar 2023 18:16:10 +0100 Subject: [PATCH 12/14] src/wif/k1.cpp: implement sig_encoder_k1 and sig_decoder_k1 --- src/wif/k1.cpp | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/wif/k1.cpp b/src/wif/k1.cpp index 3792dd1..507ce99 100644 --- a/src/wif/k1.cpp +++ b/src/wif/k1.cpp @@ -85,4 +85,34 @@ bool priv_decoder_k1(const std::vector& buf, ec_privkey_t& priv) return true; } +void sig_encoder_k1(const ec_signature_t& sig, unsigned char *buf) { + + checksum_t check = _checksum_suffix(sig.data(), EC_SIGNATURE_SIZE, "K1"); + + memcpy(buf, sig.data(), sig.size()); + memcpy(buf + EC_SIGNATURE_SIZE, check.data(), check.size()); +} + +bool sig_decoder_k1(const std::vector& buf, ec_signature_t& sig) { + + checksum_t check; + + if (buf.size() != EC_SIGNATURE_SIZE + CHECKSUM_SIZE) { + return false; + } + + // Calculate checksum + check = _checksum_suffix(buf.data(), EC_SIGNATURE_SIZE, "K1"); + + // And validate + if (memcmp(buf.data() + EC_SIGNATURE_SIZE, check.data(), CHECKSUM_SIZE)) { + return false; + } + + // Copy data to output + memcpy(sig.data(), buf.data(), sig.size()); + return true; +} + + }} // namespace libeosio::internal \ No newline at end of file From 9819b2b94df9bc069bc76b5209c1ee6759004f47 Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Sat, 25 Mar 2023 18:16:32 +0100 Subject: [PATCH 13/14] src/WIF.cpp: use signature encoder/decoder. --- src/WIF.cpp | 33 +++------------------------------ 1 file changed, 3 insertions(+), 30 deletions(-) diff --git a/src/WIF.cpp b/src/WIF.cpp index aa07c3b..44869f2 100644 --- a/src/WIF.cpp +++ b/src/WIF.cpp @@ -30,20 +30,6 @@ namespace libeosio { -// Just to make it "harder" the calculated checksum for a signature (k1) and pub/priv keys in k1/r1 format. -// has a suffix that is not present in the WIF encoded string. -// So this function is a quick hack to calculate it. -// -// Should implement and use Init/Update/Finalize hash functions to do it inplace. -checksum_t _checksum_suffix(const unsigned char *in, size_t size, const char *suffix) { - unsigned char buf[size + 2]; - - memcpy(buf, in, size); - memcpy(buf + size, suffix, 2); - - return checksum_ripemd160(buf, size + 2); -} - std::string wif_priv_encode(const ec_privkey_t& priv, const std::string& prefix) { checksum_t check; @@ -148,28 +134,15 @@ bool wif_sig_decode(ec_signature_t& sig, const std::string& data) { return false; } - // Calculate checksum - checksum = _checksum_suffix(buf.data(), EC_SIGNATURE_SIZE, "K1"); - - // And validate - if (memcmp(buf.data() + EC_SIGNATURE_SIZE, checksum.data(), CHECKSUM_SIZE)) { - return false; - } - - // Copy data to output - memcpy(sig.data(), buf.data(), sig.size()); - return true; + return internal::sig_decoder_k1(buf, sig); } std::string wif_sig_encode(const ec_signature_t& sig) { unsigned char buf[EC_SIGNATURE_SIZE + CHECKSUM_SIZE]; - checksum_t check = _checksum_suffix(sig.data(), EC_SIGNATURE_SIZE, "K1"); + internal::sig_encoder_k1(sig, buf); - memcpy(buf, sig.data(), sig.size()); - memcpy(buf + EC_SIGNATURE_SIZE, check.data(), check.size()); - - return WIF_SIG_K1_" + base58_encode(buf, buf + sizeof(buf)); + return WIF_SIG_K1 + base58_encode(buf, buf + sizeof(buf)); } } // namespace libeosio From 815ab2569f39e7e780b7f087ece1dd2fc8c9618f Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Sat, 25 Mar 2023 19:54:05 +0100 Subject: [PATCH 14/14] src/wif/k1.cpp: in _checksum_suffix() change array to std::vector as MSVC does not like variable size c-arrays. --- src/wif/k1.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/wif/k1.cpp b/src/wif/k1.cpp index 507ce99..cb32283 100644 --- a/src/wif/k1.cpp +++ b/src/wif/k1.cpp @@ -23,6 +23,7 @@ */ #include +#include #include "codec.hpp" namespace libeosio { namespace internal { @@ -33,12 +34,12 @@ namespace libeosio { namespace internal { // // Should implement and use Init/Update/Finalize hash functions to do it inplace. checksum_t _checksum_suffix(const unsigned char *in, size_t size, const char *suffix) { - unsigned char buf[size + 2]; + std::vector buf(size + 2); - memcpy(buf, in, size); - memcpy(buf + size, suffix, 2); + memcpy(buf.data(), in, size); + memcpy(buf.data() + size, suffix, 2); - return checksum_ripemd160(buf, size + 2); + return checksum_ripemd160(buf.data(), buf.size()); } void pub_encoder_k1(const ec_pubkey_t& key, unsigned char *buf) {