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

@ -166,6 +166,7 @@ var rpcHandlersBeforeInit = map[string]commandHandler{
"searchrawtransactions": handleSearchRawTransactions, "searchrawtransactions": handleSearchRawTransactions,
"sendrawtransaction": handleSendRawTransaction, "sendrawtransaction": handleSendRawTransaction,
"setgenerate": handleSetGenerate, "setgenerate": handleSetGenerate,
"signmessagewithprivkey": handleSignMessageWithPrivKey,
"stop": handleStop, "stop": handleStop,
"submitblock": handleSubmitBlock, "submitblock": handleSubmitBlock,
"uptime": handleUptime, "uptime": handleUptime,
@ -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.'",
@ -730,6 +736,7 @@ var rpcResultTypes = map[string][]interface{}{
"searchrawtransactions": {(*string)(nil), (*[]btcjson.SearchRawTransactionsResult)(nil)}, "searchrawtransactions": {(*string)(nil), (*[]btcjson.SearchRawTransactionsResult)(nil)},
"sendrawtransaction": {(*string)(nil)}, "sendrawtransaction": {(*string)(nil)},
"setgenerate": nil, "setgenerate": nil,
"signmessagewithprivkey": {(*string)(nil)},
"stop": {(*string)(nil)}, "stop": {(*string)(nil)},
"submitblock": {nil, (*string)(nil)}, "submitblock": {nil, (*string)(nil)},
"uptime": {(*int64)(nil)}, "uptime": {(*int64)(nil)},