I get different results from signing the same string with the same private key in go-ethereum and ethereumjs.utils. The key used in ts has the "0x" in front because that function requires hexed keys to have that indicator.
the crytpo library is from github.com/go-ethereum/crypto, not the standard library crypto
package main
import (
"bytes"
"crypto/ecdsa"
"fmt"
"log"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
)
func hash(data []byte) common.Hash {
return crypto.Keccak256Hash(data)
}
func signMsg(msg, privKeyHex string) ([]byte, ecdsa.PublicKey) {
privKeyECDSA, err := crypto.HexToECDSA(privKeyHex)
if err != nil {
log.Fatal(err)
}
msgHash := hash([]byte(msg))
signature, err := crypto.Sign(msgHash.Bytes(), privKeyECDSA)
if err != nil {
log.Fatal(err)
}
return signature, privKeyECDSA.PublicKey
}
func verifyMsg(msg string, sig []byte, publicKey ecdsa.PublicKey) bool {
msgHash := hash([]byte(msg))
publicKeyBytes := crypto.FromECDSAPub(&publicKey)
sigPubKey, err := crypto.Ecrecover(msgHash.Bytes(), sig)
if err != nil {
log.Fatal(err)
}
matches := bytes.Equal(sigPubKey, publicKeyBytes)
return matches
}
var fakeP = "3a1076bf45ab87712ad64ccb3b10217737f7faacbf2872e88fdd9a537d8fe266"
func main() {
sig, pubKey := signMsg("Hiya!", fakeP)
fmt.Println(verifyMsg("Hiya!", sig, pubKey))
fmt.Println("signature", hexutil.Encode(sig))
}
with the output being
hexed sig message 0xea0b35bf34b27ce98736cd68ce6203743ac84c31bac0ac228e9c8d3dd8381c845aa3c2a32a7cc591e28fca1f9e9e51d7da9a6820d39f48bf3df439714934f81c00
and the ts/js
function Sign(message , hexKey) {
// hash the message
let hash = utils.hashPersonalMessage(Buffer.from(message));
console.log("hashed message:", hash)
// signing the message
let signature = utils.ecsign(hash, utils.toBuffer(hexKey));
console.log("signature", signature)
// Hexing the signature
let hexedSig = utils.toRpcSig(signature.v, signature.r, signature.s);
console.log(hexedSig)
}
with the output being
hashed message: <Buffer 9f 7a b3 b9 75 da 1f 2c 64 67 2a 05 47 26 ec d4 01 51 48 13 5c 5d 60 77 e8 19 61 ed 2f 1e 0d e6>
signature { r:
<Buffer 8d 2e 69 3a ea 62 7c 05 ce 49 d4 b1 11 08 f4 93 e8 17 6b 12 62 a3 92 33 94 7c e6 97 97 64 04 2e>,
s:
<Buffer 55 31 49 92 ec da c9 4c 44 5a 24 d1 8e ea 47 6c 80 16 1d ea 61 11 2b 5d f2 c8 f2 9e 61 e5 e7 06>,
v: 28 }
0x8d2e693aea627c05ce49d4b11108f493e8176b1262a39233947ce6979764042e55314992ecdac94c445a24d18eea476c80161dea61112b5df2c8f29e61e5e7061c
I think it might have something to do with hashing the message, so I've looked at the two separate hashing functions, but can't figure it out.
ethereumjs/utils.hashPersonalMessage https://github.com/ethereumjs/ethereumjs-util/blob/9396416913125711e526a05591e3ce8471c3a528/index.js#L369-L372
go-ethereum/crypto.Sign https://github.com/ethereum/go-ethereum/blob/master/crypto/crypto.go#L55
as smarx said in the comments, the hashPersonalMessage function in ethereumjs adds a prefix to the signed data.
so the hash function above needs to be changed to
func prefixHash(data []byte) common.Hash {
msg := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(data), data)
return crypto.Keccak256Hash([]byte(msg))
}
to add the prefix. Took me a while to figure out not to add the two 0s found in the prefix of the js function, and to add the prefix to the bytes, rather than adding it directly to the string.
the outputs are now the same
//golang
0x8d2e693aea627c05ce49d4b11108f493e8176b1262a39233947ce6979764042e55314992ecdac94c445a24d18eea476c80161dea61112b5df2c8f29e61e5e7061c
//ts
0x8d2e693aea627c05ce49d4b11108f493e8176b1262a39233947ce6979764042e55314992ecdac94c445a24d18eea476c80161dea61112b5df2c8f29e61e5e7061c