/
eth_signer.go
61 lines (50 loc) · 2.01 KB
/
eth_signer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package util
import (
"crypto/ecdsa"
"github.com/ethereum/go-ethereum/crypto"
"github.com/pkg/errors"
)
const (
signaturePrefix = "\x19Ethereum Signed Message:\n32"
)
// NewEthereumSignature creates a new signuature over a given byte array
func NewEthereumSignature(hash []byte, privateKey *ecdsa.PrivateKey) ([]byte, error) {
if privateKey == nil {
return nil, errors.New("empty private key")
}
protectedHash := crypto.Keccak256Hash(append([]uint8(signaturePrefix), hash...))
return crypto.Sign(protectedHash.Bytes(), privateKey)
}
// ValidateEthereumSignature takes a message, an associated signature and public key and
// returns an error if the signature isn't valid
func ValidateEthereumSignature(hash []byte, signature []byte, ethAddress string) error {
if len(signature) < 65 {
return errors.New("signature too short")
}
// To verify signature
// - use crypto.SigToPub to get the public key
// - use crypto.PubkeyToAddress to get the address
// - compare this to the address given.
// for backwards compatibility reasons the V value of an Ethereum sig is presented
// as 27 or 28, internally though it should be a 0-3 value due to changed formats.
// It seems that go-ethereum expects this to be done before sigs actually reach it's
// internal validation functions. In order to comply with this requirement we check
// the sig an dif it's in standard format we correct it. If it's in go-ethereum's expected
// format already we make no changes.
//
// We could attempt to break or otherwise exit early on obviously invalid values for this
// byte, but that's a task best left to go-ethereum
if signature[64] == 27 || signature[64] == 28 {
signature[64] -= 27
}
protectedHash := crypto.Keccak256Hash(append([]uint8(signaturePrefix), hash...))
pubkey, err := crypto.SigToPub(protectedHash.Bytes(), signature)
if err != nil {
return errors.Wrap(err, "signature to public key")
}
addr := crypto.PubkeyToAddress(*pubkey)
if addr.Hex() != ethAddress {
return errors.New("signature mismatch")
}
return nil
}