forked from cerc-io/plugeth
b628d72766
This changes the CI / release builds to use the latest Go version. It also upgrades golangci-lint to a newer version compatible with Go 1.19. In Go 1.19, godoc has gained official support for links and lists. The syntax for code blocks in doc comments has changed and now requires a leading tab character. gofmt adapts comments to the new syntax automatically, so there are a lot of comment re-formatting changes in this PR. We need to apply the new format in order to pass the CI lint stage with Go 1.19. With the linter upgrade, I have decided to disable 'gosec' - it produces too many false-positive warnings. The 'deadcode' and 'varcheck' linters have also been removed because golangci-lint warns about them being unmaintained. 'unused' provides similar coverage and we already have it enabled, so we don't lose much with this change.
285 lines
8.5 KiB
Go
285 lines
8.5 KiB
Go
// Copyright 2014 The go-ethereum Authors
|
|
// This file is part of the go-ethereum library.
|
|
//
|
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Lesser General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// The go-ethereum library 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 Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public License
|
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
package crypto
|
|
|
|
import (
|
|
"bufio"
|
|
"crypto/ecdsa"
|
|
"crypto/elliptic"
|
|
"crypto/rand"
|
|
"encoding/hex"
|
|
"errors"
|
|
"fmt"
|
|
"hash"
|
|
"io"
|
|
"math/big"
|
|
"os"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/ethereum/go-ethereum/common/math"
|
|
"github.com/ethereum/go-ethereum/rlp"
|
|
"golang.org/x/crypto/sha3"
|
|
)
|
|
|
|
// SignatureLength indicates the byte length required to carry a signature with recovery id.
|
|
const SignatureLength = 64 + 1 // 64 bytes ECDSA signature + 1 byte recovery id
|
|
|
|
// RecoveryIDOffset points to the byte offset within the signature that contains the recovery id.
|
|
const RecoveryIDOffset = 64
|
|
|
|
// DigestLength sets the signature digest exact length
|
|
const DigestLength = 32
|
|
|
|
var (
|
|
secp256k1N, _ = new(big.Int).SetString("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", 16)
|
|
secp256k1halfN = new(big.Int).Div(secp256k1N, big.NewInt(2))
|
|
)
|
|
|
|
var errInvalidPubkey = errors.New("invalid secp256k1 public key")
|
|
|
|
// KeccakState wraps sha3.state. In addition to the usual hash methods, it also supports
|
|
// Read to get a variable amount of data from the hash state. Read is faster than Sum
|
|
// because it doesn't copy the internal state, but also modifies the internal state.
|
|
type KeccakState interface {
|
|
hash.Hash
|
|
Read([]byte) (int, error)
|
|
}
|
|
|
|
// NewKeccakState creates a new KeccakState
|
|
func NewKeccakState() KeccakState {
|
|
return sha3.NewLegacyKeccak256().(KeccakState)
|
|
}
|
|
|
|
// HashData hashes the provided data using the KeccakState and returns a 32 byte hash
|
|
func HashData(kh KeccakState, data []byte) (h common.Hash) {
|
|
kh.Reset()
|
|
kh.Write(data)
|
|
kh.Read(h[:])
|
|
return h
|
|
}
|
|
|
|
// Keccak256 calculates and returns the Keccak256 hash of the input data.
|
|
func Keccak256(data ...[]byte) []byte {
|
|
b := make([]byte, 32)
|
|
d := NewKeccakState()
|
|
for _, b := range data {
|
|
d.Write(b)
|
|
}
|
|
d.Read(b)
|
|
return b
|
|
}
|
|
|
|
// Keccak256Hash calculates and returns the Keccak256 hash of the input data,
|
|
// converting it to an internal Hash data structure.
|
|
func Keccak256Hash(data ...[]byte) (h common.Hash) {
|
|
d := NewKeccakState()
|
|
for _, b := range data {
|
|
d.Write(b)
|
|
}
|
|
d.Read(h[:])
|
|
return h
|
|
}
|
|
|
|
// Keccak512 calculates and returns the Keccak512 hash of the input data.
|
|
func Keccak512(data ...[]byte) []byte {
|
|
d := sha3.NewLegacyKeccak512()
|
|
for _, b := range data {
|
|
d.Write(b)
|
|
}
|
|
return d.Sum(nil)
|
|
}
|
|
|
|
// CreateAddress creates an ethereum address given the bytes and the nonce
|
|
func CreateAddress(b common.Address, nonce uint64) common.Address {
|
|
data, _ := rlp.EncodeToBytes([]interface{}{b, nonce})
|
|
return common.BytesToAddress(Keccak256(data)[12:])
|
|
}
|
|
|
|
// CreateAddress2 creates an ethereum address given the address bytes, initial
|
|
// contract code hash and a salt.
|
|
func CreateAddress2(b common.Address, salt [32]byte, inithash []byte) common.Address {
|
|
return common.BytesToAddress(Keccak256([]byte{0xff}, b.Bytes(), salt[:], inithash)[12:])
|
|
}
|
|
|
|
// ToECDSA creates a private key with the given D value.
|
|
func ToECDSA(d []byte) (*ecdsa.PrivateKey, error) {
|
|
return toECDSA(d, true)
|
|
}
|
|
|
|
// ToECDSAUnsafe blindly converts a binary blob to a private key. It should almost
|
|
// never be used unless you are sure the input is valid and want to avoid hitting
|
|
// errors due to bad origin encoding (0 prefixes cut off).
|
|
func ToECDSAUnsafe(d []byte) *ecdsa.PrivateKey {
|
|
priv, _ := toECDSA(d, false)
|
|
return priv
|
|
}
|
|
|
|
// toECDSA creates a private key with the given D value. The strict parameter
|
|
// controls whether the key's length should be enforced at the curve size or
|
|
// it can also accept legacy encodings (0 prefixes).
|
|
func toECDSA(d []byte, strict bool) (*ecdsa.PrivateKey, error) {
|
|
priv := new(ecdsa.PrivateKey)
|
|
priv.PublicKey.Curve = S256()
|
|
if strict && 8*len(d) != priv.Params().BitSize {
|
|
return nil, fmt.Errorf("invalid length, need %d bits", priv.Params().BitSize)
|
|
}
|
|
priv.D = new(big.Int).SetBytes(d)
|
|
|
|
// The priv.D must < N
|
|
if priv.D.Cmp(secp256k1N) >= 0 {
|
|
return nil, fmt.Errorf("invalid private key, >=N")
|
|
}
|
|
// The priv.D must not be zero or negative.
|
|
if priv.D.Sign() <= 0 {
|
|
return nil, fmt.Errorf("invalid private key, zero or negative")
|
|
}
|
|
|
|
priv.PublicKey.X, priv.PublicKey.Y = priv.PublicKey.Curve.ScalarBaseMult(d)
|
|
if priv.PublicKey.X == nil {
|
|
return nil, errors.New("invalid private key")
|
|
}
|
|
return priv, nil
|
|
}
|
|
|
|
// FromECDSA exports a private key into a binary dump.
|
|
func FromECDSA(priv *ecdsa.PrivateKey) []byte {
|
|
if priv == nil {
|
|
return nil
|
|
}
|
|
return math.PaddedBigBytes(priv.D, priv.Params().BitSize/8)
|
|
}
|
|
|
|
// UnmarshalPubkey converts bytes to a secp256k1 public key.
|
|
func UnmarshalPubkey(pub []byte) (*ecdsa.PublicKey, error) {
|
|
x, y := elliptic.Unmarshal(S256(), pub)
|
|
if x == nil {
|
|
return nil, errInvalidPubkey
|
|
}
|
|
return &ecdsa.PublicKey{Curve: S256(), X: x, Y: y}, nil
|
|
}
|
|
|
|
func FromECDSAPub(pub *ecdsa.PublicKey) []byte {
|
|
if pub == nil || pub.X == nil || pub.Y == nil {
|
|
return nil
|
|
}
|
|
return elliptic.Marshal(S256(), pub.X, pub.Y)
|
|
}
|
|
|
|
// HexToECDSA parses a secp256k1 private key.
|
|
func HexToECDSA(hexkey string) (*ecdsa.PrivateKey, error) {
|
|
b, err := hex.DecodeString(hexkey)
|
|
if byteErr, ok := err.(hex.InvalidByteError); ok {
|
|
return nil, fmt.Errorf("invalid hex character %q in private key", byte(byteErr))
|
|
} else if err != nil {
|
|
return nil, errors.New("invalid hex data for private key")
|
|
}
|
|
return ToECDSA(b)
|
|
}
|
|
|
|
// LoadECDSA loads a secp256k1 private key from the given file.
|
|
func LoadECDSA(file string) (*ecdsa.PrivateKey, error) {
|
|
fd, err := os.Open(file)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer fd.Close()
|
|
|
|
r := bufio.NewReader(fd)
|
|
buf := make([]byte, 64)
|
|
n, err := readASCII(buf, r)
|
|
if err != nil {
|
|
return nil, err
|
|
} else if n != len(buf) {
|
|
return nil, fmt.Errorf("key file too short, want 64 hex characters")
|
|
}
|
|
if err := checkKeyFileEnd(r); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return HexToECDSA(string(buf))
|
|
}
|
|
|
|
// readASCII reads into 'buf', stopping when the buffer is full or
|
|
// when a non-printable control character is encountered.
|
|
func readASCII(buf []byte, r *bufio.Reader) (n int, err error) {
|
|
for ; n < len(buf); n++ {
|
|
buf[n], err = r.ReadByte()
|
|
switch {
|
|
case err == io.EOF || buf[n] < '!':
|
|
return n, nil
|
|
case err != nil:
|
|
return n, err
|
|
}
|
|
}
|
|
return n, nil
|
|
}
|
|
|
|
// checkKeyFileEnd skips over additional newlines at the end of a key file.
|
|
func checkKeyFileEnd(r *bufio.Reader) error {
|
|
for i := 0; ; i++ {
|
|
b, err := r.ReadByte()
|
|
switch {
|
|
case err == io.EOF:
|
|
return nil
|
|
case err != nil:
|
|
return err
|
|
case b != '\n' && b != '\r':
|
|
return fmt.Errorf("invalid character %q at end of key file", b)
|
|
case i >= 2:
|
|
return errors.New("key file too long, want 64 hex characters")
|
|
}
|
|
}
|
|
}
|
|
|
|
// SaveECDSA saves a secp256k1 private key to the given file with
|
|
// restrictive permissions. The key data is saved hex-encoded.
|
|
func SaveECDSA(file string, key *ecdsa.PrivateKey) error {
|
|
k := hex.EncodeToString(FromECDSA(key))
|
|
return os.WriteFile(file, []byte(k), 0600)
|
|
}
|
|
|
|
// GenerateKey generates a new private key.
|
|
func GenerateKey() (*ecdsa.PrivateKey, error) {
|
|
return ecdsa.GenerateKey(S256(), rand.Reader)
|
|
}
|
|
|
|
// ValidateSignatureValues verifies whether the signature values are valid with
|
|
// the given chain rules. The v value is assumed to be either 0 or 1.
|
|
func ValidateSignatureValues(v byte, r, s *big.Int, homestead bool) bool {
|
|
if r.Cmp(common.Big1) < 0 || s.Cmp(common.Big1) < 0 {
|
|
return false
|
|
}
|
|
// reject upper range of s values (ECDSA malleability)
|
|
// see discussion in secp256k1/libsecp256k1/include/secp256k1.h
|
|
if homestead && s.Cmp(secp256k1halfN) > 0 {
|
|
return false
|
|
}
|
|
// Frontier: allow s to be in full N range
|
|
return r.Cmp(secp256k1N) < 0 && s.Cmp(secp256k1N) < 0 && (v == 0 || v == 1)
|
|
}
|
|
|
|
func PubkeyToAddress(p ecdsa.PublicKey) common.Address {
|
|
pubBytes := FromECDSAPub(&p)
|
|
return common.BytesToAddress(Keccak256(pubBytes[1:])[12:])
|
|
}
|
|
|
|
func zeroBytes(bytes []byte) {
|
|
for i := range bytes {
|
|
bytes[i] = 0
|
|
}
|
|
}
|