From 4e67a6216bf3633320950117aba3566cbfd5ffda Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Mon, 25 Jul 2011 15:13:55 -0700 Subject: [PATCH 1/8] Faster Base64 decoder. --- src/bitcoinrpc.cpp | 18 -------------- src/util.cpp | 59 ++++++++++++++++++++++++++++++++++++++++++++++ src/util.h | 1 + 3 files changed, 60 insertions(+), 18 deletions(-) diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 5a1fab694..db2e610fa 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -1830,24 +1830,6 @@ string EncodeBase64(string s) return result; } -string DecodeBase64(string s) -{ - BIO *b64, *bmem; - - char* buffer = static_cast(calloc(s.size(), sizeof(char))); - - b64 = BIO_new(BIO_f_base64()); - BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); - bmem = BIO_new_mem_buf(const_cast(s.c_str()), s.size()); - bmem = BIO_push(b64, bmem); - BIO_read(bmem, buffer, s.size()); - BIO_free_all(bmem); - - string result(buffer); - free(buffer); - return result; -} - bool HTTPAuthorized(map& mapHeaders) { string strAuth = mapHeaders["authorization"]; diff --git a/src/util.cpp b/src/util.cpp index a5e3d3099..5b6d26bb8 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -470,6 +470,65 @@ void ParseParameters(int argc, char* argv[]) } } +static const int decode64_table[256]= +{ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, + -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 +}; + +std::string DecodeBase64(const std::string &s) +{ + char buf[1024]; + if(s.length()>512) return ""; + char *optr=buf; + + int dec, mode=0, left=0; + size_t index=0; + for (int i=0; i>4); + left = dec & 15; + mode = 2; + break; + + case 2: // we have 4 bits and get 6, we keep 2 + *optr++ = (left<<4) | (dec>>2); + left = dec & 3; + mode = 3; + break; + + case 3: // we have 2 bits and get 6 + *optr++ = (left<<6) | dec; + mode=0; + break; + } + } + + *optr=0; + return buf; +} + bool WildcardMatch(const char* psz, const char* mask) diff --git a/src/util.h b/src/util.h index 33013a2f8..92a2b839c 100644 --- a/src/util.h +++ b/src/util.h @@ -201,6 +201,7 @@ void SetMockTime(int64 nMockTimeIn); int64 GetAdjustedTime(); void AddTimeData(unsigned int ip, int64 nTime); std::string FormatFullVersion(); +std::string DecodeBase64(const std::string &s); From 4b603f1cd6a0b8480c15555389077a4b401c2c7b Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 20 Sep 2011 15:38:29 +0200 Subject: [PATCH 2/8] Inline base64 encoder/decoder This replaces the openssl-based base64 encoder and decoder with a more efficient internal one. Tested against the rfc4648 test vectors. Decoder is based on JoelKatz' version. --- src/bitcoinrpc.cpp | 19 ------ src/util.cpp | 151 ++++++++++++++++++++++++++++++++++----------- src/util.h | 5 +- 3 files changed, 119 insertions(+), 56 deletions(-) diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index db2e610fa..23d97db89 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -1811,25 +1811,6 @@ int ReadHTTP(std::basic_istream& stream, map& mapHeadersRe return nStatus; } -string EncodeBase64(string s) -{ - BIO *b64, *bmem; - BUF_MEM *bptr; - - b64 = BIO_new(BIO_f_base64()); - BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); - bmem = BIO_new(BIO_s_mem()); - b64 = BIO_push(b64, bmem); - BIO_write(b64, s.c_str(), s.size()); - BIO_flush(b64); - BIO_get_mem_ptr(b64, &bptr); - - string result(bptr->data, bptr->length); - BIO_free_all(b64); - - return result; -} - bool HTTPAuthorized(map& mapHeaders) { string strAuth = mapHeaders["authorization"]; diff --git a/src/util.cpp b/src/util.cpp index 5b6d26bb8..fb648a972 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -443,7 +443,6 @@ vector ParseHex(const string& str) return ParseHex(str.c_str()); } - void ParseParameters(int argc, char* argv[]) { mapArgs.clear(); @@ -470,36 +469,91 @@ void ParseParameters(int argc, char* argv[]) } } -static const int decode64_table[256]= +string EncodeBase64(const unsigned char* pch, size_t len) { - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, - -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, - 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, - 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 -}; + static const char *pbase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; -std::string DecodeBase64(const std::string &s) -{ - char buf[1024]; - if(s.length()>512) return ""; - char *optr=buf; + string strRet=""; + strRet.reserve((len+2)/3*4); - int dec, mode=0, left=0; - size_t index=0; - for (int i=0; i> 2]; + left = (enc & 3) << 4; + mode = 1; + break; + + case 1: // we have two bits + strRet += pbase64[left | (enc >> 4)]; + left = (enc & 15) << 2; + mode = 2; + break; + + case 2: // we have four bits + strRet += pbase64[left | (enc >> 6)]; + strRet += pbase64[enc & 63]; + mode = 0; + break; + } + } + + if (mode) + { + strRet += pbase64[left]; + strRet += '='; + if (mode == 1) + strRet += '='; + } + + return strRet; +} + +string EncodeBase64(const string& str) +{ + return EncodeBase64((const unsigned char*)str.c_str(), str.size()); +} + +vector DecodeBase64(const char* p, bool* pfInvalid) +{ + static const int decode64_table[256] = + { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, + -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 + }; + + if (pfInvalid) + *pfInvalid = false; + + vector vchRet; + vchRet.reserve(strlen(p)*3/4); + + int mode = 0; + int left = 0; + + while (1) + { + int dec = decode64_table[*p]; + if (dec == -1) break; + p++; + switch (mode) { case 0: // we have no bits and get 6 left = dec; @@ -507,28 +561,53 @@ std::string DecodeBase64(const std::string &s) break; case 1: // we have 6 bits and keep 4 - *optr++ = (left<<2) | (dec>>4); + vchRet.push_back((left<<2) | (dec>>4)); left = dec & 15; mode = 2; break; - - case 2: // we have 4 bits and get 6, we keep 2 - *optr++ = (left<<4) | (dec>>2); + + case 2: // we have 4 bits and get 6, we keep 2 + vchRet.push_back((left<<4) | (dec>>2)); left = dec & 3; mode = 3; break; - + case 3: // we have 2 bits and get 6 - *optr++ = (left<<6) | dec; - mode=0; + vchRet.push_back((left<<6) | dec); + mode = 0; break; } } - *optr=0; - return buf; + if (pfInvalid) + switch (mode) + { + case 0: // 4n base64 characters processed: ok + break; + + case 1: // 4n+1 base64 character processed: impossible + *pfInvalid = true; + break; + + case 2: // 4n+2 base64 characters processed: require '==' + if (left || p[0] != '=' || p[1] != '=' || decode64_table[p[2]] != -1) + *pfInvalid = true; + break; + + case 3: // 4n+3 base64 characters processed: require '=' + if (left || p[0] != '=' || decode64_table[p[1]] != -1) + *pfInvalid = true; + break; + } + + return vchRet; } +string DecodeBase64(const string& str) +{ + vector vchRet = DecodeBase64(str.c_str()); + return string((const char*)&vchRet[0], vchRet.size()); +} bool WildcardMatch(const char* psz, const char* mask) diff --git a/src/util.h b/src/util.h index 92a2b839c..1fdfdf119 100644 --- a/src/util.h +++ b/src/util.h @@ -178,6 +178,10 @@ bool ParseMoney(const std::string& str, int64& nRet); bool ParseMoney(const char* pszIn, int64& nRet); std::vector ParseHex(const char* psz); std::vector ParseHex(const std::string& str); +std::vector DecodeBase64(const char* p, bool* pfInvalid = NULL); +std::string DecodeBase64(const std::string& str); +std::string EncodeBase64(const unsigned char* pch, size_t len); +std::string EncodeBase64(const std::string& str); void ParseParameters(int argc, char* argv[]); const char* wxGetTranslation(const char* psz); bool WildcardMatch(const char* psz, const char* mask); @@ -201,7 +205,6 @@ void SetMockTime(int64 nMockTimeIn); int64 GetAdjustedTime(); void AddTimeData(unsigned int ip, int64 nTime); std::string FormatFullVersion(); -std::string DecodeBase64(const std::string &s); From e93bf37e86488e481136ca66cd0b6b17b2c2e05e Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 27 Sep 2011 19:46:57 +0200 Subject: [PATCH 3/8] Test case for base64 encode/decode --- src/test/base64_tests.cpp | 20 ++++++++++++++++++++ src/test/test_bitcoin.cpp | 1 + 2 files changed, 21 insertions(+) create mode 100644 src/test/base64_tests.cpp diff --git a/src/test/base64_tests.cpp b/src/test/base64_tests.cpp new file mode 100644 index 000000000..f30f7f893 --- /dev/null +++ b/src/test/base64_tests.cpp @@ -0,0 +1,20 @@ +#include + +#include "../util.h" + +BOOST_AUTO_TEST_SUITE(base64_tests) + +BOOST_AUTO_TEST_CASE(base64_testvectors) +{ + static const string vstrIn[] = {"","f","fo","foo","foob","fooba","foobar"}; + static const string vstrOut[] = {"","Zg==","Zm8=","Zm9v","Zm9vYg==","Zm9vYmE=","Zm9vYmFy"}; + for (int i=0; i Date: Sun, 24 Apr 2011 14:27:52 +0200 Subject: [PATCH 4/8] Sign and verify message with bitcoin address and public key Add padding to input (fixed string + address) before hashing --- src/bitcoinrpc.cpp | 66 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 23d97db89..1b2a5b5f7 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -526,6 +526,70 @@ Value sendtoaddress(const Array& params, bool fHelp) return wtx.GetHash().GetHex(); } +Value signmessage(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 2) + throw runtime_error( + "signmessage \n" + "Sign a message with the private key of an address"); + + string strAddress = params[0].get_str(); + string strMessage = params[1].get_str(); + strMessage.insert(0, "Padding text - "); + + uint160 hash160; + if(!AddressToHash160(strAddress, hash160)) + throw JSONRPCError(-3, "Invalid address"); + + vector& vchPubKey = mapPubKeys[hash160]; + CKey key; + if(!key.SetPubKey(vchPubKey)) + throw JSONRPCError(-3, "Public key not found"); + strMessage.insert(0, HexStr(vchPubKey.begin(), vchPubKey.end()).c_str()); + + vector vchMsg(strMessage.begin(), strMessage.end()); + vector vchSig; + if (!CKey::Sign(mapKeys[vchPubKey], Hash(vchMsg.begin(), vchMsg.end()), vchSig)) + throw JSONRPCError(-3, "Sign failed"); + + Object obj; + obj.push_back(Pair("address", strAddress)); + obj.push_back(Pair("pubkey", HexStr(vchPubKey.begin(), vchPubKey.end()).c_str())); + obj.push_back(Pair("sign", HexStr(vchSig.begin(), vchSig.end()).c_str())); + + return obj; +} + +Value verifymessage(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 3) + throw runtime_error( + "verifymessage \n" + "Verify a signed message with the public key"); + + string strPubKey = params[0].get_str(); + string strSign = params[1].get_str(); + string strMessage = params[2].get_str(); + strMessage.insert(0, "Padding text - "); + strMessage.insert(0, strPubKey.c_str()); + + vector vchPubKey = ParseHex(strPubKey); + vector vchSig = ParseHex(strSign); + vector vchMsg(strMessage.begin(), strMessage.end()); + + CKey key; + if(!key.SetPubKey(vchPubKey)) + throw JSONRPCError(-3, "Invalid pubkey"); + + if (!key.Verify(Hash(vchMsg.begin(), vchMsg.end()), vchSig)) + throw JSONRPCError(-3, "Verify failed"); + + Object obj; + obj.push_back(Pair("address", PubKeyToAddress(vchPubKey))); + obj.push_back(Pair("pubkey", strPubKey.c_str())); + return obj; +} + Value getreceivedbyaddress(const Array& params, bool fHelp) { @@ -1636,6 +1700,8 @@ pair pCallTable[] = make_pair("sendmany", &sendmany), make_pair("gettransaction", &gettransaction), make_pair("listtransactions", &listtransactions), + make_pair("signmessage", &signmessage), + make_pair("verifymessage", &verifymessage), make_pair("getwork", &getwork), make_pair("listaccounts", &listaccounts), make_pair("settxfee", &settxfee), From b53d6284eb5b2833ed1bde46dbce5b5a4284804a Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Mon, 19 Sep 2011 20:03:03 +0200 Subject: [PATCH 5/8] Incorporate pubkey in signature, check based on address Include the public key in the signature string, to allow verification based on address. --- src/bitcoinrpc.cpp | 86 ++++++++++++++++++++++++++-------------------- 1 file changed, 49 insertions(+), 37 deletions(-) diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 1b2a5b5f7..3638adac3 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -526,6 +526,8 @@ Value sendtoaddress(const Array& params, bool fHelp) return wtx.GetHash().GetHex(); } +static const string strMessageMagic = "Bitcoin Signed Message:\n"; + Value signmessage(const Array& params, bool fHelp) { if (fHelp || params.size() != 2) @@ -533,61 +535,71 @@ Value signmessage(const Array& params, bool fHelp) "signmessage \n" "Sign a message with the private key of an address"); + if (pwalletMain->IsLocked()) + throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first."); + string strAddress = params[0].get_str(); string strMessage = params[1].get_str(); - strMessage.insert(0, "Padding text - "); - - uint160 hash160; - if(!AddressToHash160(strAddress, hash160)) + + CBitcoinAddress addr(strAddress); + if (!addr.IsValid()) throw JSONRPCError(-3, "Invalid address"); - - vector& vchPubKey = mapPubKeys[hash160]; + CKey key; - if(!key.SetPubKey(vchPubKey)) - throw JSONRPCError(-3, "Public key not found"); - strMessage.insert(0, HexStr(vchPubKey.begin(), vchPubKey.end()).c_str()); + if (!pwalletMain->GetKey(addr, key)) + throw JSONRPCError(-4, "Private key not available"); + + CDataStream ss(SER_GETHASH); + ss << strMessageMagic; + ss << strMessage; - vector vchMsg(strMessage.begin(), strMessage.end()); vector vchSig; - if (!CKey::Sign(mapKeys[vchPubKey], Hash(vchMsg.begin(), vchMsg.end()), vchSig)) - throw JSONRPCError(-3, "Sign failed"); + if (!key.Sign(Hash(ss.begin(), ss.end()), vchSig)) + throw JSONRPCError(-5, "Sign failed"); - Object obj; - obj.push_back(Pair("address", strAddress)); - obj.push_back(Pair("pubkey", HexStr(vchPubKey.begin(), vchPubKey.end()).c_str())); - obj.push_back(Pair("sign", HexStr(vchSig.begin(), vchSig.end()).c_str())); + CDataStream sres(SER_NETWORK); + sres << key.GetPubKey(); // public key + sres << vchSig; // signature; - return obj; + return HexStr(sres.begin(), sres.end()); } Value verifymessage(const Array& params, bool fHelp) { if (fHelp || params.size() != 3) throw runtime_error( - "verifymessage \n" - "Verify a signed message with the public key"); + "verifymessage \n" + "Verify a signed message"); - string strPubKey = params[0].get_str(); - string strSign = params[1].get_str(); - string strMessage = params[2].get_str(); - strMessage.insert(0, "Padding text - "); - strMessage.insert(0, strPubKey.c_str()); + string strAddress = params[0].get_str(); + string strSign = params[1].get_str(); + string strMessage = params[2].get_str(); - vector vchPubKey = ParseHex(strPubKey); - vector vchSig = ParseHex(strSign); - vector vchMsg(strMessage.begin(), strMessage.end()); + CBitcoinAddress addr(strAddress); + if (!addr.IsValid()) + throw JSONRPCError(-3, "Invalid address"); + + vector vchResult = ParseHex(strSign); + CDataStream sres(vchResult); + + std::vector vchPubKey; + sres >> vchPubKey; + std::vector vchSig; + sres >> vchSig; CKey key; - if(!key.SetPubKey(vchPubKey)) - throw JSONRPCError(-3, "Invalid pubkey"); - - if (!key.Verify(Hash(vchMsg.begin(), vchMsg.end()), vchSig)) - throw JSONRPCError(-3, "Verify failed"); - - Object obj; - obj.push_back(Pair("address", PubKeyToAddress(vchPubKey))); - obj.push_back(Pair("pubkey", strPubKey.c_str())); - return obj; + if (!key.SetPubKey(vchPubKey)) + throw JSONRPCError(-5, "Invalid public key in signature"); + + if (key.GetAddress() == addr) + { + CDataStream ss(SER_GETHASH); + ss << strMessageMagic; + ss << strMessage; + return key.Verify(Hash(ss.begin(), ss.end()), vchSig); + } + else + return false; } From d9867551fc58ebb62eddef3dfbdb1dc711110577 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 20 Sep 2011 15:42:36 +0200 Subject: [PATCH 6/8] base64-based sign/verify --- src/bitcoinrpc.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 3638adac3..c8a0076bf 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -561,7 +561,8 @@ Value signmessage(const Array& params, bool fHelp) sres << key.GetPubKey(); // public key sres << vchSig; // signature; - return HexStr(sres.begin(), sres.end()); + vector vchRet(sres.begin(), sres.end()); + return EncodeBase64(&vchRet[0], vchRet.size()); } Value verifymessage(const Array& params, bool fHelp) @@ -579,7 +580,12 @@ Value verifymessage(const Array& params, bool fHelp) if (!addr.IsValid()) throw JSONRPCError(-3, "Invalid address"); - vector vchResult = ParseHex(strSign); + bool fInvalid = false; + vector vchResult = DecodeBase64(strSign.c_str(), &fInvalid); + + if (fInvalid) + throw JSONRPCError(-5, "Malformed base64 encoding"); + CDataStream sres(vchResult); std::vector vchPubKey; From 01cc526318ff9f0ee6fb675ffe3be4ca7f3cda7a Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Mon, 25 Apr 2011 13:23:29 +0200 Subject: [PATCH 7/8] Compact signatures and key recovery Introduce a new type of signatures that are only 65 bytes long, and allow reconstruction of the public key that was used to create the signature. --- src/key.h | 140 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) diff --git a/src/key.h b/src/key.h index d2e668945..5ffd7b9cc 100644 --- a/src/key.h +++ b/src/key.h @@ -75,6 +75,76 @@ err: return(ok); } +int static inline ECDSA_SIG_recover_key_GFp(EC_KEY *eckey, ECDSA_SIG *ecsig, const unsigned char *msg, int msglen, int recid, int check) +{ + if (!eckey) return 0; + + int ret = 0; + BN_CTX *ctx = NULL; + + BIGNUM *x = NULL; + BIGNUM *e = NULL; + BIGNUM *order = NULL; + BIGNUM *sor = NULL; + BIGNUM *eor = NULL; + BIGNUM *field = NULL; + EC_POINT *R = NULL; + EC_POINT *O = NULL; + EC_POINT *Q = NULL; + BIGNUM *rr = NULL; + BIGNUM *zero = NULL; + int n = 0; + int i = recid / 2; + + const EC_GROUP *group = EC_KEY_get0_group(eckey); + if ((ctx = BN_CTX_new()) == NULL) { ret = -1; goto err; } + BN_CTX_start(ctx); + order = BN_CTX_get(ctx); + if (!EC_GROUP_get_order(group, order, ctx)) { ret = -2; goto err; } + x = BN_CTX_get(ctx); + if (!BN_copy(x, order)) { ret=-1; goto err; } + if (!BN_mul_word(x, i)) { ret=-1; goto err; } + if (!BN_add(x, x, ecsig->r)) { ret=-1; goto err; } + field = BN_CTX_get(ctx); + if (!EC_GROUP_get_curve_GFp(group, field, NULL, NULL, ctx)) { ret=-2; goto err; } + if (BN_cmp(x, field) >= 0) { ret=0; goto err; } + if ((R = EC_POINT_new(group)) == NULL) { ret = -2; goto err; } + if (!EC_POINT_set_compressed_coordinates_GFp(group, R, x, recid % 2, ctx)) { ret=0; goto err; } + if (check) + { + if ((O = EC_POINT_new(group)) == NULL) { ret = -2; goto err; } + if (!EC_POINT_mul(group, O, NULL, R, order, ctx)) { ret=-2; goto err; } + if (!EC_POINT_is_at_infinity(group, O)) { ret = 0; goto err; } + } + if ((Q = EC_POINT_new(group)) == NULL) { ret = -2; goto err; } + n = EC_GROUP_get_degree(group); + e = BN_CTX_get(ctx); + if (!BN_bin2bn(msg, msglen, e)) { ret=-1; goto err; } + if (8*msglen > n) BN_rshift(e, e, 8-(n & 7)); + zero = BN_CTX_get(ctx); + if (!BN_zero(zero)) { ret=-1; goto err; } + if (!BN_mod_sub(e, zero, e, order, ctx)) { ret=-1; goto err; } + rr = BN_CTX_get(ctx); + if (!BN_mod_inverse(rr, ecsig->r, order, ctx)) { ret=-1; goto err; } + sor = BN_CTX_get(ctx); + if (!BN_mod_mul(sor, ecsig->s, rr, order, ctx)) { ret=-1; goto err; } + eor = BN_CTX_get(ctx); + if (!BN_mod_mul(eor, e, rr, order, ctx)) { ret=-1; goto err; } + if (!EC_POINT_mul(group, Q, eor, R, sor, ctx)) { ret=-2; goto err; } + if (!EC_KEY_set_public_key(eckey, Q)) { ret=-2; goto err; } + + ret = 1; + +err: + if (ctx) { + BN_CTX_end(ctx); + BN_CTX_free(ctx); + } + if (R != NULL) EC_POINT_free(R); + if (O != NULL) EC_POINT_free(O); + if (Q != NULL) EC_POINT_free(Q); + return ret; +} class key_error : public std::runtime_error { @@ -221,6 +291,66 @@ public: return true; } + // create a compact signature (65 bytes), which allows reconstructing the used public key + bool SignCompact(uint256 hash, std::vector& vchSig) + { + bool fOk = false; + ECDSA_SIG *sig = ECDSA_do_sign((unsigned char*)&hash, sizeof(hash), pkey); + if (sig==NULL) + return false; + vchSig.clear(); + vchSig.resize(65,0); + int nBitsR = BN_num_bits(sig->r); + int nBitsS = BN_num_bits(sig->s); + if (nBitsR <= 256 && nBitsS <= 256) + { + int nRecId = -1; + for (int i=0; i<4; i++) + { + CKey keyRec; + keyRec.fSet = true; + if (ECDSA_SIG_recover_key_GFp(keyRec.pkey, sig, (unsigned char*)&hash, sizeof(hash), i, 1) == 1) + if (keyRec.GetPubKey() == this->GetPubKey()) + { + nRecId = i; + break; + } + } + + if (nRecId == -1) + throw key_error("CKEy::SignCompact() : unable to construct recoverable key"); + + vchSig[0] = nRecId+27; + BN_bn2bin(sig->r,&vchSig[33-(nBitsR+7)/8]); + BN_bn2bin(sig->s,&vchSig[65-(nBitsS+7)/8]); + fOk = true; + } + ECDSA_SIG_free(sig); + return fOk; + } + + // reconstruct public key from a compact signature + bool SetCompactSignature(uint256 hash, const std::vector& vchSig) + { + if (vchSig.size() != 65) + return false; + if (vchSig[0]<27 || vchSig[0]>=31) + return false; + ECDSA_SIG *sig = ECDSA_SIG_new(); + BN_bin2bn(&vchSig[1],32,sig->r); + BN_bin2bn(&vchSig[33],32,sig->s); + + EC_KEY_free(pkey); + pkey = EC_KEY_new_by_curve_name(NID_secp256k1); + if (ECDSA_SIG_recover_key_GFp(pkey, sig, (unsigned char*)&hash, sizeof(hash), vchSig[0] - 27, 0) == 1) + { + fSet = true; + ECDSA_SIG_free(sig); + return true; + } + return false; + } + bool Verify(uint256 hash, const std::vector& vchSig) { // -1 = error, 0 = bad sig, 1 = good @@ -229,6 +359,16 @@ public: return true; } + bool VerifyCompact(uint256 hash, const std::vector& vchSig) + { + CKey key; + if (!key.SetCompactSignature(hash, vchSig)) + return false; + if (GetPubKey() != key.GetPubKey()) + return false; + return true; + } + CBitcoinAddress GetAddress() const { return CBitcoinAddress(GetPubKey()); From 3a570dc80a7391b7f3943cb83849055318e0217a Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Wed, 21 Sep 2011 17:03:28 +0200 Subject: [PATCH 8/8] Use key recovery for message signatures Instead of encoding the public key inside the signature string, use key recovery to do verification. This allows 88-character base64-encoded signature strings instead of 188-character ones. --- src/bitcoinrpc.cpp | 34 +++++++++------------------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index c8a0076bf..8468e5610 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -554,15 +554,10 @@ Value signmessage(const Array& params, bool fHelp) ss << strMessage; vector vchSig; - if (!key.Sign(Hash(ss.begin(), ss.end()), vchSig)) + if (!key.SignCompact(Hash(ss.begin(), ss.end()), vchSig)) throw JSONRPCError(-5, "Sign failed"); - CDataStream sres(SER_NETWORK); - sres << key.GetPubKey(); // public key - sres << vchSig; // signature; - - vector vchRet(sres.begin(), sres.end()); - return EncodeBase64(&vchRet[0], vchRet.size()); + return EncodeBase64(&vchSig[0], vchSig.size()); } Value verifymessage(const Array& params, bool fHelp) @@ -581,31 +576,20 @@ Value verifymessage(const Array& params, bool fHelp) throw JSONRPCError(-3, "Invalid address"); bool fInvalid = false; - vector vchResult = DecodeBase64(strSign.c_str(), &fInvalid); + vector vchSig = DecodeBase64(strSign.c_str(), &fInvalid); if (fInvalid) throw JSONRPCError(-5, "Malformed base64 encoding"); - CDataStream sres(vchResult); - - std::vector vchPubKey; - sres >> vchPubKey; - std::vector vchSig; - sres >> vchSig; + CDataStream ss(SER_GETHASH); + ss << strMessageMagic; + ss << strMessage; CKey key; - if (!key.SetPubKey(vchPubKey)) - throw JSONRPCError(-5, "Invalid public key in signature"); - - if (key.GetAddress() == addr) - { - CDataStream ss(SER_GETHASH); - ss << strMessageMagic; - ss << strMessage; - return key.Verify(Hash(ss.begin(), ss.end()), vchSig); - } - else + if (!key.SetCompactSignature(Hash(ss.begin(), ss.end()), vchSig)) return false; + + return (key.GetAddress() == addr); }