Implement signmessagewithprivkey JSON-RPC command

Reuse the Bitcoin message signature header const
also in verifymessage.
This commit is contained in:
Mikael Lindlof 2020-05-23 19:54:49 +01:00 committed by John C. Vernaleo
parent b68c50e33c
commit d2c0123bef
4 changed files with 180 additions and 93 deletions

View File

@ -806,6 +806,24 @@ func NewSetGenerateCmd(generate bool, genProcLimit *int) *SetGenerateCmd {
}
}
// SignMessageWithPrivKeyCmd defines the signmessagewithprivkey JSON-RPC command.
type SignMessageWithPrivKeyCmd struct {
PrivKey string // base 58 Wallet Import format private key
Message string // Message to sign
}
// NewSignMessageWithPrivKey returns a new instance which can be used to issue a
// signmessagewithprivkey JSON-RPC command.
//
// The first parameter is a private key in base 58 Wallet Import format.
// The second parameter is the message to sign.
func NewSignMessageWithPrivKey(privKey, message string) *SignMessageWithPrivKeyCmd {
return &SignMessageWithPrivKeyCmd{
PrivKey: privKey,
Message: message,
}
}
// StopCmd defines the stop JSON-RPC command.
type StopCmd struct{}
@ -971,6 +989,7 @@ func init() {
MustRegisterCmd("searchrawtransactions", (*SearchRawTransactionsCmd)(nil), flags)
MustRegisterCmd("sendrawtransaction", (*SendRawTransactionCmd)(nil), flags)
MustRegisterCmd("setgenerate", (*SetGenerateCmd)(nil), flags)
MustRegisterCmd("signmessagewithprivkey", (*SignMessageWithPrivKeyCmd)(nil), flags)
MustRegisterCmd("stop", (*StopCmd)(nil), flags)
MustRegisterCmd("submitblock", (*SubmitBlockCmd)(nil), flags)
MustRegisterCmd("uptime", (*UptimeCmd)(nil), flags)

View File

@ -1186,6 +1186,20 @@ func TestChainSvrCmds(t *testing.T) {
GenProcLimit: btcjson.Int(6),
},
},
{
name: "signmessagewithprivkey",
newCmd: func() (interface{}, error) {
return btcjson.NewCmd("signmessagewithprivkey", "5Hue", "Hey")
},
staticCmd: func() interface{} {
return btcjson.NewSignMessageWithPrivKey("5Hue", "Hey")
},
marshalled: `{"jsonrpc":"1.0","method":"signmessagewithprivkey","params":["5Hue","Hey"],"id":1}`,
unmarshalled: &btcjson.SignMessageWithPrivKeyCmd{
PrivKey: "5Hue",
Message: "Hey",
},
},
{
name: "stop",
newCmd: func() (interface{}, error) {

View File

@ -127,52 +127,53 @@ type commandHandler func(*rpcServer, interface{}, <-chan struct{}) (interface{},
// a dependency loop.
var rpcHandlers map[string]commandHandler
var rpcHandlersBeforeInit = map[string]commandHandler{
"addnode": handleAddNode,
"createrawtransaction": handleCreateRawTransaction,
"debuglevel": handleDebugLevel,
"decoderawtransaction": handleDecodeRawTransaction,
"decodescript": handleDecodeScript,
"estimatefee": handleEstimateFee,
"generate": handleGenerate,
"getaddednodeinfo": handleGetAddedNodeInfo,
"getbestblock": handleGetBestBlock,
"getbestblockhash": handleGetBestBlockHash,
"getblock": handleGetBlock,
"getblockchaininfo": handleGetBlockChainInfo,
"getblockcount": handleGetBlockCount,
"getblockhash": handleGetBlockHash,
"getblockheader": handleGetBlockHeader,
"getblocktemplate": handleGetBlockTemplate,
"getcfilter": handleGetCFilter,
"getcfilterheader": handleGetCFilterHeader,
"getconnectioncount": handleGetConnectionCount,
"getcurrentnet": handleGetCurrentNet,
"getdifficulty": handleGetDifficulty,
"getgenerate": handleGetGenerate,
"gethashespersec": handleGetHashesPerSec,
"getheaders": handleGetHeaders,
"getinfo": handleGetInfo,
"getmempoolinfo": handleGetMempoolInfo,
"getmininginfo": handleGetMiningInfo,
"getnettotals": handleGetNetTotals,
"getnetworkhashps": handleGetNetworkHashPS,
"getpeerinfo": handleGetPeerInfo,
"getrawmempool": handleGetRawMempool,
"getrawtransaction": handleGetRawTransaction,
"gettxout": handleGetTxOut,
"help": handleHelp,
"node": handleNode,
"ping": handlePing,
"searchrawtransactions": handleSearchRawTransactions,
"sendrawtransaction": handleSendRawTransaction,
"setgenerate": handleSetGenerate,
"stop": handleStop,
"submitblock": handleSubmitBlock,
"uptime": handleUptime,
"validateaddress": handleValidateAddress,
"verifychain": handleVerifyChain,
"verifymessage": handleVerifyMessage,
"version": handleVersion,
"addnode": handleAddNode,
"createrawtransaction": handleCreateRawTransaction,
"debuglevel": handleDebugLevel,
"decoderawtransaction": handleDecodeRawTransaction,
"decodescript": handleDecodeScript,
"estimatefee": handleEstimateFee,
"generate": handleGenerate,
"getaddednodeinfo": handleGetAddedNodeInfo,
"getbestblock": handleGetBestBlock,
"getbestblockhash": handleGetBestBlockHash,
"getblock": handleGetBlock,
"getblockchaininfo": handleGetBlockChainInfo,
"getblockcount": handleGetBlockCount,
"getblockhash": handleGetBlockHash,
"getblockheader": handleGetBlockHeader,
"getblocktemplate": handleGetBlockTemplate,
"getcfilter": handleGetCFilter,
"getcfilterheader": handleGetCFilterHeader,
"getconnectioncount": handleGetConnectionCount,
"getcurrentnet": handleGetCurrentNet,
"getdifficulty": handleGetDifficulty,
"getgenerate": handleGetGenerate,
"gethashespersec": handleGetHashesPerSec,
"getheaders": handleGetHeaders,
"getinfo": handleGetInfo,
"getmempoolinfo": handleGetMempoolInfo,
"getmininginfo": handleGetMiningInfo,
"getnettotals": handleGetNetTotals,
"getnetworkhashps": handleGetNetworkHashPS,
"getpeerinfo": handleGetPeerInfo,
"getrawmempool": handleGetRawMempool,
"getrawtransaction": handleGetRawTransaction,
"gettxout": handleGetTxOut,
"help": handleHelp,
"node": handleNode,
"ping": handlePing,
"searchrawtransactions": handleSearchRawTransactions,
"sendrawtransaction": handleSendRawTransaction,
"setgenerate": handleSetGenerate,
"signmessagewithprivkey": handleSignMessageWithPrivKey,
"stop": handleStop,
"submitblock": handleSubmitBlock,
"uptime": handleUptime,
"validateaddress": handleValidateAddress,
"verifychain": handleVerifyChain,
"verifymessage": handleVerifyMessage,
"version": handleVersion,
}
// list of commands that we recognize, but for which btcd has no support because
@ -3435,6 +3436,52 @@ func handleSetGenerate(s *rpcServer, cmd interface{}, closeChan <-chan struct{})
return nil, nil
}
// Text used to signify that a signed message follows and to prevent
// inadvertently signing a transaction.
const messageSignatureHeader = "Bitcoin Signed Message:\n"
// handleSignMessageWithPrivKey implements the signmessagewithprivkey command.
func handleSignMessageWithPrivKey(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
c := cmd.(*btcjson.SignMessageWithPrivKeyCmd)
wif, err := btcutil.DecodeWIF(c.PrivKey)
if err != nil {
message := "Invalid private key"
switch err {
case btcutil.ErrMalformedPrivateKey:
message = "Malformed private key"
case btcutil.ErrChecksumMismatch:
message = "Private key checksum mismatch"
}
return nil, &btcjson.RPCError{
Code: btcjson.ErrRPCInvalidAddressOrKey,
Message: message,
}
}
if !wif.IsForNet(s.cfg.ChainParams) {
return nil, &btcjson.RPCError{
Code: btcjson.ErrRPCInvalidAddressOrKey,
Message: "Private key for wrong network",
}
}
var buf bytes.Buffer
wire.WriteVarString(&buf, 0, messageSignatureHeader)
wire.WriteVarString(&buf, 0, c.Message)
messageHash := chainhash.DoubleHashB(buf.Bytes())
sig, err := btcec.SignCompact(btcec.S256(), wif.PrivKey,
messageHash, wif.CompressPubKey)
if err != nil {
return nil, &btcjson.RPCError{
Code: btcjson.ErrRPCInvalidAddressOrKey,
Message: "Sign failed",
}
}
return base64.StdEncoding.EncodeToString(sig), nil
}
// handleStop implements the stop command.
func handleStop(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
select {
@ -3615,7 +3662,7 @@ func handleVerifyMessage(s *rpcServer, cmd interface{}, closeChan <-chan struct{
// Validate the signature - this just shows that it was valid at all.
// we will compare it with the key next.
var buf bytes.Buffer
wire.WriteVarString(&buf, 0, "Bitcoin Signed Message:\n")
wire.WriteVarString(&buf, 0, messageSignatureHeader)
wire.WriteVarString(&buf, 0, c.Message)
expectedMessageHash := chainhash.DoubleHashB(buf.Bytes())
pk, wasCompressed, err := btcec.RecoverCompact(btcec.S256(), sig,

View File

@ -559,6 +559,12 @@ var helpDescsEnUS = map[string]string{
"setgenerate-generate": "Use true to enable generation, false to disable it",
"setgenerate-genproclimit": "The number of processors (cores) to limit generation to or -1 for default",
// SignMessageWithPrivKeyCmd help.
"signmessagewithprivkey--synopsis": "Sign a message with the private key of an address",
"signmessagewithprivkey-privkey": "The private key to sign the message with",
"signmessagewithprivkey-message": "The message to create a signature of",
"signmessagewithprivkey--result0": "The signature of the message encoded in base 64",
// StopCmd help.
"stop--synopsis": "Shutdown btcd.",
"stop--result0": "The string 'btcd stopping.'",
@ -691,52 +697,53 @@ var helpDescsEnUS = map[string]string{
// This information is used to generate the help. Each result type must be a
// pointer to the type (or nil to indicate no return value).
var rpcResultTypes = map[string][]interface{}{
"addnode": nil,
"createrawtransaction": {(*string)(nil)},
"debuglevel": {(*string)(nil), (*string)(nil)},
"decoderawtransaction": {(*btcjson.TxRawDecodeResult)(nil)},
"decodescript": {(*btcjson.DecodeScriptResult)(nil)},
"estimatefee": {(*float64)(nil)},
"generate": {(*[]string)(nil)},
"getaddednodeinfo": {(*[]string)(nil), (*[]btcjson.GetAddedNodeInfoResult)(nil)},
"getbestblock": {(*btcjson.GetBestBlockResult)(nil)},
"getbestblockhash": {(*string)(nil)},
"getblock": {(*string)(nil), (*btcjson.GetBlockVerboseResult)(nil)},
"getblockcount": {(*int64)(nil)},
"getblockhash": {(*string)(nil)},
"getblockheader": {(*string)(nil), (*btcjson.GetBlockHeaderVerboseResult)(nil)},
"getblocktemplate": {(*btcjson.GetBlockTemplateResult)(nil), (*string)(nil), nil},
"getblockchaininfo": {(*btcjson.GetBlockChainInfoResult)(nil)},
"getcfilter": {(*string)(nil)},
"getcfilterheader": {(*string)(nil)},
"getconnectioncount": {(*int32)(nil)},
"getcurrentnet": {(*uint32)(nil)},
"getdifficulty": {(*float64)(nil)},
"getgenerate": {(*bool)(nil)},
"gethashespersec": {(*float64)(nil)},
"getheaders": {(*[]string)(nil)},
"getinfo": {(*btcjson.InfoChainResult)(nil)},
"getmempoolinfo": {(*btcjson.GetMempoolInfoResult)(nil)},
"getmininginfo": {(*btcjson.GetMiningInfoResult)(nil)},
"getnettotals": {(*btcjson.GetNetTotalsResult)(nil)},
"getnetworkhashps": {(*int64)(nil)},
"getpeerinfo": {(*[]btcjson.GetPeerInfoResult)(nil)},
"getrawmempool": {(*[]string)(nil), (*btcjson.GetRawMempoolVerboseResult)(nil)},
"getrawtransaction": {(*string)(nil), (*btcjson.TxRawResult)(nil)},
"gettxout": {(*btcjson.GetTxOutResult)(nil)},
"node": nil,
"help": {(*string)(nil), (*string)(nil)},
"ping": nil,
"searchrawtransactions": {(*string)(nil), (*[]btcjson.SearchRawTransactionsResult)(nil)},
"sendrawtransaction": {(*string)(nil)},
"setgenerate": nil,
"stop": {(*string)(nil)},
"submitblock": {nil, (*string)(nil)},
"uptime": {(*int64)(nil)},
"validateaddress": {(*btcjson.ValidateAddressChainResult)(nil)},
"verifychain": {(*bool)(nil)},
"verifymessage": {(*bool)(nil)},
"version": {(*map[string]btcjson.VersionResult)(nil)},
"addnode": nil,
"createrawtransaction": {(*string)(nil)},
"debuglevel": {(*string)(nil), (*string)(nil)},
"decoderawtransaction": {(*btcjson.TxRawDecodeResult)(nil)},
"decodescript": {(*btcjson.DecodeScriptResult)(nil)},
"estimatefee": {(*float64)(nil)},
"generate": {(*[]string)(nil)},
"getaddednodeinfo": {(*[]string)(nil), (*[]btcjson.GetAddedNodeInfoResult)(nil)},
"getbestblock": {(*btcjson.GetBestBlockResult)(nil)},
"getbestblockhash": {(*string)(nil)},
"getblock": {(*string)(nil), (*btcjson.GetBlockVerboseResult)(nil)},
"getblockcount": {(*int64)(nil)},
"getblockhash": {(*string)(nil)},
"getblockheader": {(*string)(nil), (*btcjson.GetBlockHeaderVerboseResult)(nil)},
"getblocktemplate": {(*btcjson.GetBlockTemplateResult)(nil), (*string)(nil), nil},
"getblockchaininfo": {(*btcjson.GetBlockChainInfoResult)(nil)},
"getcfilter": {(*string)(nil)},
"getcfilterheader": {(*string)(nil)},
"getconnectioncount": {(*int32)(nil)},
"getcurrentnet": {(*uint32)(nil)},
"getdifficulty": {(*float64)(nil)},
"getgenerate": {(*bool)(nil)},
"gethashespersec": {(*float64)(nil)},
"getheaders": {(*[]string)(nil)},
"getinfo": {(*btcjson.InfoChainResult)(nil)},
"getmempoolinfo": {(*btcjson.GetMempoolInfoResult)(nil)},
"getmininginfo": {(*btcjson.GetMiningInfoResult)(nil)},
"getnettotals": {(*btcjson.GetNetTotalsResult)(nil)},
"getnetworkhashps": {(*int64)(nil)},
"getpeerinfo": {(*[]btcjson.GetPeerInfoResult)(nil)},
"getrawmempool": {(*[]string)(nil), (*btcjson.GetRawMempoolVerboseResult)(nil)},
"getrawtransaction": {(*string)(nil), (*btcjson.TxRawResult)(nil)},
"gettxout": {(*btcjson.GetTxOutResult)(nil)},
"node": nil,
"help": {(*string)(nil), (*string)(nil)},
"ping": nil,
"searchrawtransactions": {(*string)(nil), (*[]btcjson.SearchRawTransactionsResult)(nil)},
"sendrawtransaction": {(*string)(nil)},
"setgenerate": nil,
"signmessagewithprivkey": {(*string)(nil)},
"stop": {(*string)(nil)},
"submitblock": {nil, (*string)(nil)},
"uptime": {(*int64)(nil)},
"validateaddress": {(*btcjson.ValidateAddressChainResult)(nil)},
"verifychain": {(*bool)(nil)},
"verifymessage": {(*bool)(nil)},
"version": {(*map[string]btcjson.VersionResult)(nil)},
// Websocket commands.
"loadtxfilter": nil,