diff options
Diffstat (limited to 'cmd/ethkey/message.go')
-rw-r--r-- | cmd/ethkey/message.go | 159 |
1 files changed, 159 insertions, 0 deletions
diff --git a/cmd/ethkey/message.go b/cmd/ethkey/message.go new file mode 100644 index 000000000..531a931c8 --- /dev/null +++ b/cmd/ethkey/message.go @@ -0,0 +1,159 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. + +package main + +import ( + "encoding/hex" + "fmt" + "io/ioutil" + + "github.com/ethereum/go-ethereum/accounts/keystore" + "github.com/ethereum/go-ethereum/cmd/utils" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "gopkg.in/urfave/cli.v1" +) + +type outputSign struct { + Signature string +} + +var msgfileFlag = cli.StringFlag{ + Name: "msgfile", + Usage: "file containing the message to sign/verify", +} + +var commandSignMessage = cli.Command{ + Name: "signmessage", + Usage: "sign a message", + ArgsUsage: "<keyfile> <message>", + Description: ` +Sign the message with a keyfile. + +To sign a message contained in a file, use the --msgfile flag. +`, + Flags: []cli.Flag{ + passphraseFlag, + jsonFlag, + msgfileFlag, + }, + Action: func(ctx *cli.Context) error { + message := getMessage(ctx, 1) + + // Load the keyfile. + keyfilepath := ctx.Args().First() + keyjson, err := ioutil.ReadFile(keyfilepath) + if err != nil { + utils.Fatalf("Failed to read the keyfile at '%s': %v", keyfilepath, err) + } + + // Decrypt key with passphrase. + passphrase := getPassPhrase(ctx, false) + key, err := keystore.DecryptKey(keyjson, passphrase) + if err != nil { + utils.Fatalf("Error decrypting key: %v", err) + } + + signature, err := crypto.Sign(signHash(message), key.PrivateKey) + if err != nil { + utils.Fatalf("Failed to sign message: %v", err) + } + out := outputSign{Signature: hex.EncodeToString(signature)} + if ctx.Bool(jsonFlag.Name) { + mustPrintJSON(out) + } else { + fmt.Println("Signature:", out.Signature) + } + return nil + }, +} + +type outputVerify struct { + Success bool + RecoveredAddress string + RecoveredPublicKey string +} + +var commandVerifyMessage = cli.Command{ + Name: "verifymessage", + Usage: "verify the signature of a signed message", + ArgsUsage: "<address> <signature> <message>", + Description: ` +Verify the signature of the message. +It is possible to refer to a file containing the message.`, + Flags: []cli.Flag{ + jsonFlag, + msgfileFlag, + }, + Action: func(ctx *cli.Context) error { + addressStr := ctx.Args().First() + signatureHex := ctx.Args().Get(1) + message := getMessage(ctx, 2) + + if !common.IsHexAddress(addressStr) { + utils.Fatalf("Invalid address: %s", addressStr) + } + address := common.HexToAddress(addressStr) + signature, err := hex.DecodeString(signatureHex) + if err != nil { + utils.Fatalf("Signature encoding is not hexadecimal: %v", err) + } + + recoveredPubkey, err := crypto.SigToPub(signHash(message), signature) + if err != nil || recoveredPubkey == nil { + utils.Fatalf("Signature verification failed: %v", err) + } + recoveredPubkeyBytes := crypto.FromECDSAPub(recoveredPubkey) + recoveredAddress := crypto.PubkeyToAddress(*recoveredPubkey) + success := address == recoveredAddress + + out := outputVerify{ + Success: success, + RecoveredPublicKey: hex.EncodeToString(recoveredPubkeyBytes), + RecoveredAddress: recoveredAddress.Hex(), + } + if ctx.Bool(jsonFlag.Name) { + mustPrintJSON(out) + } else { + if out.Success { + fmt.Println("Signature verification successful!") + } else { + fmt.Println("Signature verification failed!") + } + fmt.Println("Recovered public key:", out.RecoveredPublicKey) + fmt.Println("Recovered address:", out.RecoveredAddress) + } + return nil + }, +} + +func getMessage(ctx *cli.Context, msgarg int) []byte { + if file := ctx.String("msgfile"); file != "" { + if len(ctx.Args()) > msgarg { + utils.Fatalf("Can't use --msgfile and message argument at the same time.") + } + msg, err := ioutil.ReadFile(file) + if err != nil { + utils.Fatalf("Can't read message file: %v", err) + } + return msg + } else if len(ctx.Args()) == msgarg+1 { + return []byte(ctx.Args().Get(msgarg)) + } + utils.Fatalf("Invalid number of arguments: want %d, got %d", msgarg+1, len(ctx.Args())) + return nil +} |