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. // StopCmd defines the stop JSON-RPC command.
type StopCmd struct{} type StopCmd struct{}
@ -971,6 +989,7 @@ func init() {
MustRegisterCmd("searchrawtransactions", (*SearchRawTransactionsCmd)(nil), flags) MustRegisterCmd("searchrawtransactions", (*SearchRawTransactionsCmd)(nil), flags)
MustRegisterCmd("sendrawtransaction", (*SendRawTransactionCmd)(nil), flags) MustRegisterCmd("sendrawtransaction", (*SendRawTransactionCmd)(nil), flags)
MustRegisterCmd("setgenerate", (*SetGenerateCmd)(nil), flags) MustRegisterCmd("setgenerate", (*SetGenerateCmd)(nil), flags)
MustRegisterCmd("signmessagewithprivkey", (*SignMessageWithPrivKeyCmd)(nil), flags)
MustRegisterCmd("stop", (*StopCmd)(nil), flags) MustRegisterCmd("stop", (*StopCmd)(nil), flags)
MustRegisterCmd("submitblock", (*SubmitBlockCmd)(nil), flags) MustRegisterCmd("submitblock", (*SubmitBlockCmd)(nil), flags)
MustRegisterCmd("uptime", (*UptimeCmd)(nil), flags) MustRegisterCmd("uptime", (*UptimeCmd)(nil), flags)

View file

@ -1186,6 +1186,20 @@ func TestChainSvrCmds(t *testing.T) {
GenProcLimit: btcjson.Int(6), 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", name: "stop",
newCmd: func() (interface{}, error) { newCmd: func() (interface{}, error) {

View file

@ -127,52 +127,53 @@ type commandHandler func(*rpcServer, interface{}, <-chan struct{}) (interface{},
// a dependency loop. // a dependency loop.
var rpcHandlers map[string]commandHandler var rpcHandlers map[string]commandHandler
var rpcHandlersBeforeInit = map[string]commandHandler{ var rpcHandlersBeforeInit = map[string]commandHandler{
"addnode": handleAddNode, "addnode": handleAddNode,
"createrawtransaction": handleCreateRawTransaction, "createrawtransaction": handleCreateRawTransaction,
"debuglevel": handleDebugLevel, "debuglevel": handleDebugLevel,
"decoderawtransaction": handleDecodeRawTransaction, "decoderawtransaction": handleDecodeRawTransaction,
"decodescript": handleDecodeScript, "decodescript": handleDecodeScript,
"estimatefee": handleEstimateFee, "estimatefee": handleEstimateFee,
"generate": handleGenerate, "generate": handleGenerate,
"getaddednodeinfo": handleGetAddedNodeInfo, "getaddednodeinfo": handleGetAddedNodeInfo,
"getbestblock": handleGetBestBlock, "getbestblock": handleGetBestBlock,
"getbestblockhash": handleGetBestBlockHash, "getbestblockhash": handleGetBestBlockHash,
"getblock": handleGetBlock, "getblock": handleGetBlock,
"getblockchaininfo": handleGetBlockChainInfo, "getblockchaininfo": handleGetBlockChainInfo,
"getblockcount": handleGetBlockCount, "getblockcount": handleGetBlockCount,
"getblockhash": handleGetBlockHash, "getblockhash": handleGetBlockHash,
"getblockheader": handleGetBlockHeader, "getblockheader": handleGetBlockHeader,
"getblocktemplate": handleGetBlockTemplate, "getblocktemplate": handleGetBlockTemplate,
"getcfilter": handleGetCFilter, "getcfilter": handleGetCFilter,
"getcfilterheader": handleGetCFilterHeader, "getcfilterheader": handleGetCFilterHeader,
"getconnectioncount": handleGetConnectionCount, "getconnectioncount": handleGetConnectionCount,
"getcurrentnet": handleGetCurrentNet, "getcurrentnet": handleGetCurrentNet,
"getdifficulty": handleGetDifficulty, "getdifficulty": handleGetDifficulty,
"getgenerate": handleGetGenerate, "getgenerate": handleGetGenerate,
"gethashespersec": handleGetHashesPerSec, "gethashespersec": handleGetHashesPerSec,
"getheaders": handleGetHeaders, "getheaders": handleGetHeaders,
"getinfo": handleGetInfo, "getinfo": handleGetInfo,
"getmempoolinfo": handleGetMempoolInfo, "getmempoolinfo": handleGetMempoolInfo,
"getmininginfo": handleGetMiningInfo, "getmininginfo": handleGetMiningInfo,
"getnettotals": handleGetNetTotals, "getnettotals": handleGetNetTotals,
"getnetworkhashps": handleGetNetworkHashPS, "getnetworkhashps": handleGetNetworkHashPS,
"getpeerinfo": handleGetPeerInfo, "getpeerinfo": handleGetPeerInfo,
"getrawmempool": handleGetRawMempool, "getrawmempool": handleGetRawMempool,
"getrawtransaction": handleGetRawTransaction, "getrawtransaction": handleGetRawTransaction,
"gettxout": handleGetTxOut, "gettxout": handleGetTxOut,
"help": handleHelp, "help": handleHelp,
"node": handleNode, "node": handleNode,
"ping": handlePing, "ping": handlePing,
"searchrawtransactions": handleSearchRawTransactions, "searchrawtransactions": handleSearchRawTransactions,
"sendrawtransaction": handleSendRawTransaction, "sendrawtransaction": handleSendRawTransaction,
"setgenerate": handleSetGenerate, "setgenerate": handleSetGenerate,
"stop": handleStop, "signmessagewithprivkey": handleSignMessageWithPrivKey,
"submitblock": handleSubmitBlock, "stop": handleStop,
"uptime": handleUptime, "submitblock": handleSubmitBlock,
"validateaddress": handleValidateAddress, "uptime": handleUptime,
"verifychain": handleVerifyChain, "validateaddress": handleValidateAddress,
"verifymessage": handleVerifyMessage, "verifychain": handleVerifyChain,
"version": handleVersion, "verifymessage": handleVerifyMessage,
"version": handleVersion,
} }
// list of commands that we recognize, but for which btcd has no support because // 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 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. // handleStop implements the stop command.
func handleStop(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { func handleStop(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
select { 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. // Validate the signature - this just shows that it was valid at all.
// we will compare it with the key next. // we will compare it with the key next.
var buf bytes.Buffer var buf bytes.Buffer
wire.WriteVarString(&buf, 0, "Bitcoin Signed Message:\n") wire.WriteVarString(&buf, 0, messageSignatureHeader)
wire.WriteVarString(&buf, 0, c.Message) wire.WriteVarString(&buf, 0, c.Message)
expectedMessageHash := chainhash.DoubleHashB(buf.Bytes()) expectedMessageHash := chainhash.DoubleHashB(buf.Bytes())
pk, wasCompressed, err := btcec.RecoverCompact(btcec.S256(), sig, 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-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", "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. // StopCmd help.
"stop--synopsis": "Shutdown btcd.", "stop--synopsis": "Shutdown btcd.",
"stop--result0": "The string 'btcd stopping.'", "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 // 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). // pointer to the type (or nil to indicate no return value).
var rpcResultTypes = map[string][]interface{}{ var rpcResultTypes = map[string][]interface{}{
"addnode": nil, "addnode": nil,
"createrawtransaction": {(*string)(nil)}, "createrawtransaction": {(*string)(nil)},
"debuglevel": {(*string)(nil), (*string)(nil)}, "debuglevel": {(*string)(nil), (*string)(nil)},
"decoderawtransaction": {(*btcjson.TxRawDecodeResult)(nil)}, "decoderawtransaction": {(*btcjson.TxRawDecodeResult)(nil)},
"decodescript": {(*btcjson.DecodeScriptResult)(nil)}, "decodescript": {(*btcjson.DecodeScriptResult)(nil)},
"estimatefee": {(*float64)(nil)}, "estimatefee": {(*float64)(nil)},
"generate": {(*[]string)(nil)}, "generate": {(*[]string)(nil)},
"getaddednodeinfo": {(*[]string)(nil), (*[]btcjson.GetAddedNodeInfoResult)(nil)}, "getaddednodeinfo": {(*[]string)(nil), (*[]btcjson.GetAddedNodeInfoResult)(nil)},
"getbestblock": {(*btcjson.GetBestBlockResult)(nil)}, "getbestblock": {(*btcjson.GetBestBlockResult)(nil)},
"getbestblockhash": {(*string)(nil)}, "getbestblockhash": {(*string)(nil)},
"getblock": {(*string)(nil), (*btcjson.GetBlockVerboseResult)(nil)}, "getblock": {(*string)(nil), (*btcjson.GetBlockVerboseResult)(nil)},
"getblockcount": {(*int64)(nil)}, "getblockcount": {(*int64)(nil)},
"getblockhash": {(*string)(nil)}, "getblockhash": {(*string)(nil)},
"getblockheader": {(*string)(nil), (*btcjson.GetBlockHeaderVerboseResult)(nil)}, "getblockheader": {(*string)(nil), (*btcjson.GetBlockHeaderVerboseResult)(nil)},
"getblocktemplate": {(*btcjson.GetBlockTemplateResult)(nil), (*string)(nil), nil}, "getblocktemplate": {(*btcjson.GetBlockTemplateResult)(nil), (*string)(nil), nil},
"getblockchaininfo": {(*btcjson.GetBlockChainInfoResult)(nil)}, "getblockchaininfo": {(*btcjson.GetBlockChainInfoResult)(nil)},
"getcfilter": {(*string)(nil)}, "getcfilter": {(*string)(nil)},
"getcfilterheader": {(*string)(nil)}, "getcfilterheader": {(*string)(nil)},
"getconnectioncount": {(*int32)(nil)}, "getconnectioncount": {(*int32)(nil)},
"getcurrentnet": {(*uint32)(nil)}, "getcurrentnet": {(*uint32)(nil)},
"getdifficulty": {(*float64)(nil)}, "getdifficulty": {(*float64)(nil)},
"getgenerate": {(*bool)(nil)}, "getgenerate": {(*bool)(nil)},
"gethashespersec": {(*float64)(nil)}, "gethashespersec": {(*float64)(nil)},
"getheaders": {(*[]string)(nil)}, "getheaders": {(*[]string)(nil)},
"getinfo": {(*btcjson.InfoChainResult)(nil)}, "getinfo": {(*btcjson.InfoChainResult)(nil)},
"getmempoolinfo": {(*btcjson.GetMempoolInfoResult)(nil)}, "getmempoolinfo": {(*btcjson.GetMempoolInfoResult)(nil)},
"getmininginfo": {(*btcjson.GetMiningInfoResult)(nil)}, "getmininginfo": {(*btcjson.GetMiningInfoResult)(nil)},
"getnettotals": {(*btcjson.GetNetTotalsResult)(nil)}, "getnettotals": {(*btcjson.GetNetTotalsResult)(nil)},
"getnetworkhashps": {(*int64)(nil)}, "getnetworkhashps": {(*int64)(nil)},
"getpeerinfo": {(*[]btcjson.GetPeerInfoResult)(nil)}, "getpeerinfo": {(*[]btcjson.GetPeerInfoResult)(nil)},
"getrawmempool": {(*[]string)(nil), (*btcjson.GetRawMempoolVerboseResult)(nil)}, "getrawmempool": {(*[]string)(nil), (*btcjson.GetRawMempoolVerboseResult)(nil)},
"getrawtransaction": {(*string)(nil), (*btcjson.TxRawResult)(nil)}, "getrawtransaction": {(*string)(nil), (*btcjson.TxRawResult)(nil)},
"gettxout": {(*btcjson.GetTxOutResult)(nil)}, "gettxout": {(*btcjson.GetTxOutResult)(nil)},
"node": nil, "node": nil,
"help": {(*string)(nil), (*string)(nil)}, "help": {(*string)(nil), (*string)(nil)},
"ping": nil, "ping": nil,
"searchrawtransactions": {(*string)(nil), (*[]btcjson.SearchRawTransactionsResult)(nil)}, "searchrawtransactions": {(*string)(nil), (*[]btcjson.SearchRawTransactionsResult)(nil)},
"sendrawtransaction": {(*string)(nil)}, "sendrawtransaction": {(*string)(nil)},
"setgenerate": nil, "setgenerate": nil,
"stop": {(*string)(nil)}, "signmessagewithprivkey": {(*string)(nil)},
"submitblock": {nil, (*string)(nil)}, "stop": {(*string)(nil)},
"uptime": {(*int64)(nil)}, "submitblock": {nil, (*string)(nil)},
"validateaddress": {(*btcjson.ValidateAddressChainResult)(nil)}, "uptime": {(*int64)(nil)},
"verifychain": {(*bool)(nil)}, "validateaddress": {(*btcjson.ValidateAddressChainResult)(nil)},
"verifymessage": {(*bool)(nil)}, "verifychain": {(*bool)(nil)},
"version": {(*map[string]btcjson.VersionResult)(nil)}, "verifymessage": {(*bool)(nil)},
"version": {(*map[string]btcjson.VersionResult)(nil)},
// Websocket commands. // Websocket commands.
"loadtxfilter": nil, "loadtxfilter": nil,