mirror of
https://github.com/eosswedenorg/libantelope
synced 2026-06-16 03:34:56 +02:00
WIF: Support PVT_K1 format.
This commit is contained in:
parent
ea411793a2
commit
1aa6906ba2
7 changed files with 157 additions and 38 deletions
|
|
@ -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
|
||||
|
|
|
|||
57
src/WIF.cpp
57
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<unsigned char> 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<checksum_sha256d>(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) {
|
||||
|
|
|
|||
|
|
@ -47,6 +47,24 @@ bool pub_decoder_legacy(const std::vector<unsigned char>& buf, ec_pubkey_t& key)
|
|||
|
||||
bool pub_decoder_k1(const std::vector<unsigned char>& 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<unsigned char>&, ec_privkey_t&);
|
||||
|
||||
bool priv_decoder_legacy(const std::vector<unsigned char>& buf, ec_privkey_t& priv);
|
||||
|
||||
bool priv_decoder_k1(const std::vector<unsigned char>& buf, ec_privkey_t& priv);
|
||||
|
||||
}} // namespace libeosio::internal
|
||||
|
||||
#endif /* LIBEOSIO_CODEC_H */
|
||||
|
|
|
|||
|
|
@ -61,4 +61,28 @@ bool pub_decoder_k1(const std::vector<unsigned char>& 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<unsigned char>& 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
|
||||
|
|
@ -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<unsigned char>& 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<unsigned char>& 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<checksum_sha256d>(buf.data(), buf.size())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(priv.data(), buf.data() + 1, priv.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
}} // namespace libeosio::internal
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
#include <vector>
|
||||
#include <doctest.h>
|
||||
|
||||
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<struct testcase> 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 );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -4,24 +4,48 @@
|
|||
#include <vector>
|
||||
#include <doctest.h>
|
||||
|
||||
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<struct testcase> 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<struct testcase> 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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue