github.com/luckypickle/go-ethereum-vet@v1.14.2/cmd/ethkey/message.go (about) 1 // Copyright 2017 The go-ethereum Authors 2 // This file is part of go-ethereum. 3 // 4 // go-ethereum is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // go-ethereum is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. 16 17 package main 18 19 import ( 20 "encoding/hex" 21 "fmt" 22 "io/ioutil" 23 24 "github.com/luckypickle/go-ethereum-vet/accounts/keystore" 25 "github.com/luckypickle/go-ethereum-vet/cmd/utils" 26 "github.com/luckypickle/go-ethereum-vet/common" 27 "github.com/luckypickle/go-ethereum-vet/crypto" 28 "gopkg.in/urfave/cli.v1" 29 ) 30 31 type outputSign struct { 32 Signature string 33 } 34 35 var msgfileFlag = cli.StringFlag{ 36 Name: "msgfile", 37 Usage: "file containing the message to sign/verify", 38 } 39 40 var commandSignMessage = cli.Command{ 41 Name: "signmessage", 42 Usage: "sign a message", 43 ArgsUsage: "<keyfile> <message>", 44 Description: ` 45 Sign the message with a keyfile. 46 47 To sign a message contained in a file, use the --msgfile flag. 48 `, 49 Flags: []cli.Flag{ 50 passphraseFlag, 51 jsonFlag, 52 msgfileFlag, 53 }, 54 Action: func(ctx *cli.Context) error { 55 message := getMessage(ctx, 1) 56 57 // Load the keyfile. 58 keyfilepath := ctx.Args().First() 59 keyjson, err := ioutil.ReadFile(keyfilepath) 60 if err != nil { 61 utils.Fatalf("Failed to read the keyfile at '%s': %v", keyfilepath, err) 62 } 63 64 // Decrypt key with passphrase. 65 passphrase := getPassphrase(ctx) 66 key, err := keystore.DecryptKey(keyjson, passphrase) 67 if err != nil { 68 utils.Fatalf("Error decrypting key: %v", err) 69 } 70 71 signature, err := crypto.Sign(signHash(message), key.PrivateKey) 72 if err != nil { 73 utils.Fatalf("Failed to sign message: %v", err) 74 } 75 out := outputSign{Signature: hex.EncodeToString(signature)} 76 if ctx.Bool(jsonFlag.Name) { 77 mustPrintJSON(out) 78 } else { 79 fmt.Println("Signature:", out.Signature) 80 } 81 return nil 82 }, 83 } 84 85 type outputVerify struct { 86 Success bool 87 RecoveredAddress string 88 RecoveredPublicKey string 89 } 90 91 var commandVerifyMessage = cli.Command{ 92 Name: "verifymessage", 93 Usage: "verify the signature of a signed message", 94 ArgsUsage: "<address> <signature> <message>", 95 Description: ` 96 Verify the signature of the message. 97 It is possible to refer to a file containing the message.`, 98 Flags: []cli.Flag{ 99 jsonFlag, 100 msgfileFlag, 101 }, 102 Action: func(ctx *cli.Context) error { 103 addressStr := ctx.Args().First() 104 signatureHex := ctx.Args().Get(1) 105 message := getMessage(ctx, 2) 106 107 if !common.IsHexAddress(addressStr) { 108 utils.Fatalf("Invalid address: %s", addressStr) 109 } 110 address := common.HexToAddress(addressStr) 111 signature, err := hex.DecodeString(signatureHex) 112 if err != nil { 113 utils.Fatalf("Signature encoding is not hexadecimal: %v", err) 114 } 115 116 recoveredPubkey, err := crypto.SigToPub(signHash(message), signature) 117 if err != nil || recoveredPubkey == nil { 118 utils.Fatalf("Signature verification failed: %v", err) 119 } 120 recoveredPubkeyBytes := crypto.FromECDSAPub(recoveredPubkey) 121 recoveredAddress := crypto.PubkeyToAddress(*recoveredPubkey) 122 success := address == recoveredAddress 123 124 out := outputVerify{ 125 Success: success, 126 RecoveredPublicKey: hex.EncodeToString(recoveredPubkeyBytes), 127 RecoveredAddress: recoveredAddress.Hex(), 128 } 129 if ctx.Bool(jsonFlag.Name) { 130 mustPrintJSON(out) 131 } else { 132 if out.Success { 133 fmt.Println("Signature verification successful!") 134 } else { 135 fmt.Println("Signature verification failed!") 136 } 137 fmt.Println("Recovered public key:", out.RecoveredPublicKey) 138 fmt.Println("Recovered address:", out.RecoveredAddress) 139 } 140 return nil 141 }, 142 } 143 144 func getMessage(ctx *cli.Context, msgarg int) []byte { 145 if file := ctx.String("msgfile"); file != "" { 146 if len(ctx.Args()) > msgarg { 147 utils.Fatalf("Can't use --msgfile and message argument at the same time.") 148 } 149 msg, err := ioutil.ReadFile(file) 150 if err != nil { 151 utils.Fatalf("Can't read message file: %v", err) 152 } 153 return msg 154 } else if len(ctx.Args()) == msgarg+1 { 155 return []byte(ctx.Args().Get(msgarg)) 156 } 157 utils.Fatalf("Invalid number of arguments: want %d, got %d", msgarg+1, len(ctx.Args())) 158 return nil 159 }