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