来自 go-ethereum 和 ethereumjs.utils 的不同签名结果

different signing results from go-ethereum and ethereumjs.utils

我在 go-ethereum 和 ethereumjs.utils 中使用相同的私钥签署相同的字符串得到不同的结果。 ts中使用的键前面有“0x”,因为该功能需要十六进制键才能具有该指示符。

crytpo 库来自 github.com/go-ethereum/crypto,而非标准库 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))
}

输出为

hexed sig message 0xea0b35bf34b27ce98736cd68ce6203743ac84c31bac0ac228e9c8d3dd8381c845aa3c2a32a7cc591e28fca1f9e9e51d7da9a6820d39f48bf3df439714934f81c00

和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)
}

输出为

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

我认为这可能与散列消息有关,所以我查看了两个单独的散列函数,但无法弄清楚。

ethereumjs/utils.hashPersonalMessage https://github.com/ethereumjs/ethereumjs-util/blob/9396416913125711e526a05591e3ce8471c3a528/index.js#L369-L372

go-ethereum/crypto.签名 https://github.com/ethereum/go-ethereum/blob/master/crypto/crypto.go#L55

如评论中smarx所述,ethereumjs中的hashPersonalMessage函数为签名数据添加了前缀。

所以上面的哈希函数需要改成

func prefixHash(data []byte) common.Hash {
    msg := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(data), data)
    return crypto.Keccak256Hash([]byte(msg))
}

添加前缀。弄了好久才弄明白,js函数的前缀中找到的两个0不要加上,而是把前缀加到bytes上,而不是直接加到string上。

输出现在相同

 //golang 
0x8d2e693aea627c05ce49d4b11108f493e8176b1262a39233947ce6979764042e55314992ecdac94c445a24d18eea476c80161dea61112b5df2c8f29e61e5e7061c
 //ts 
0x8d2e693aea627c05ce49d4b11108f493e8176b1262a39233947ce6979764042e55314992ecdac94c445a24d18eea476c80161dea61112b5df2c8f29e61e5e7061c