From 54b271a86dd748f3b0bcebeaf678dc34e0d6177a Mon Sep 17 00:00:00 2001
From: Felix Lange <fjl@twurst.com>
Date: Thu, 22 Aug 2019 15:14:06 +0200
Subject: [PATCH] crypto: add SignatureLength constant and use it everywhere
 (#19996)

Original change by @jpeletier
---
 accounts/usbwallet/ledger.go      |  3 ++-
 consensus/clique/clique.go        |  6 +++---
 core/genesis.go                   |  3 ++-
 core/types/transaction_signing.go |  6 +++---
 crypto/crypto.go                  |  9 +++++++++
 crypto/signature_cgo.go           | 16 ++++++++--------
 crypto/signature_nocgo.go         |  2 +-
 internal/ethapi/api.go            | 10 +++++-----
 miner/worker_test.go              |  2 +-
 p2p/rlpx.go                       |  8 ++++----
 whisper/whisperv6/doc.go          | 14 ++++++++------
 11 files changed, 46 insertions(+), 33 deletions(-)

diff --git a/accounts/usbwallet/ledger.go b/accounts/usbwallet/ledger.go
index c30903b5b..17ca9223f 100644
--- a/accounts/usbwallet/ledger.go
+++ b/accounts/usbwallet/ledger.go
@@ -32,6 +32,7 @@ import (
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/common/hexutil"
 	"github.com/ethereum/go-ethereum/core/types"
+	"github.com/ethereum/go-ethereum/crypto"
 	"github.com/ethereum/go-ethereum/log"
 	"github.com/ethereum/go-ethereum/rlp"
 )
@@ -341,7 +342,7 @@ func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction
 		op = ledgerP1ContTransactionData
 	}
 	// Extract the Ethereum signature and do a sanity validation
-	if len(reply) != 65 {
+	if len(reply) != crypto.SignatureLength {
 		return common.Address{}, nil, errors.New("reply lacks signature")
 	}
 	signature := append(reply[1:], reply[0])
diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go
index 084009a06..02b6da35b 100644
--- a/consensus/clique/clique.go
+++ b/consensus/clique/clique.go
@@ -55,8 +55,8 @@ const (
 var (
 	epochLength = uint64(30000) // Default number of blocks after which to checkpoint and reset the pending votes
 
-	extraVanity = 32 // Fixed number of extra-data prefix bytes reserved for signer vanity
-	extraSeal   = 65 // Fixed number of extra-data suffix bytes reserved for signer seal
+	extraVanity = 32                     // Fixed number of extra-data prefix bytes reserved for signer vanity
+	extraSeal   = crypto.SignatureLength // Fixed number of extra-data suffix bytes reserved for signer seal
 
 	nonceAuthVote = hexutil.MustDecode("0xffffffffffffffff") // Magic nonce number to vote on adding a new signer
 	nonceDropVote = hexutil.MustDecode("0x0000000000000000") // Magic nonce number to vote on removing a signer.
@@ -728,7 +728,7 @@ func encodeSigHeader(w io.Writer, header *types.Header) {
 		header.GasLimit,
 		header.GasUsed,
 		header.Time,
-		header.Extra[:len(header.Extra)-65], // Yes, this will panic if extra is too short
+		header.Extra[:len(header.Extra)-crypto.SignatureLength], // Yes, this will panic if extra is too short
 		header.MixDigest,
 		header.Nonce,
 	})
diff --git a/core/genesis.go b/core/genesis.go
index 87bab2520..5d6311b32 100644
--- a/core/genesis.go
+++ b/core/genesis.go
@@ -31,6 +31,7 @@ import (
 	"github.com/ethereum/go-ethereum/core/rawdb"
 	"github.com/ethereum/go-ethereum/core/state"
 	"github.com/ethereum/go-ethereum/core/types"
+	"github.com/ethereum/go-ethereum/crypto"
 	"github.com/ethereum/go-ethereum/ethdb"
 	"github.com/ethereum/go-ethereum/log"
 	"github.com/ethereum/go-ethereum/params"
@@ -377,7 +378,7 @@ func DeveloperGenesisBlock(period uint64, faucet common.Address) *Genesis {
 	// Assemble and return the genesis with the precompiles and faucet pre-funded
 	return &Genesis{
 		Config:     &config,
-		ExtraData:  append(append(make([]byte, 32), faucet[:]...), make([]byte, 65)...),
+		ExtraData:  append(append(make([]byte, 32), faucet[:]...), make([]byte, crypto.SignatureLength)...),
 		GasLimit:   6283185,
 		Difficulty: big.NewInt(1),
 		Alloc: map[common.Address]GenesisAccount{
diff --git a/core/types/transaction_signing.go b/core/types/transaction_signing.go
index 63132048e..842fedbd0 100644
--- a/core/types/transaction_signing.go
+++ b/core/types/transaction_signing.go
@@ -193,8 +193,8 @@ func (s FrontierSigner) Equal(s2 Signer) bool {
 // SignatureValues returns signature values. This signature
 // needs to be in the [R || S || V] format where V is 0 or 1.
 func (fs FrontierSigner) SignatureValues(tx *Transaction, sig []byte) (r, s, v *big.Int, err error) {
-	if len(sig) != 65 {
-		panic(fmt.Sprintf("wrong size for signature: got %d, want 65", len(sig)))
+	if len(sig) != crypto.SignatureLength {
+		panic(fmt.Sprintf("wrong size for signature: got %d, want %d", len(sig), crypto.SignatureLength))
 	}
 	r = new(big.Int).SetBytes(sig[:32])
 	s = new(big.Int).SetBytes(sig[32:64])
@@ -229,7 +229,7 @@ func recoverPlain(sighash common.Hash, R, S, Vb *big.Int, homestead bool) (commo
 	}
 	// encode the signature in uncompressed format
 	r, s := R.Bytes(), S.Bytes()
-	sig := make([]byte, 65)
+	sig := make([]byte, crypto.SignatureLength)
 	copy(sig[32-len(r):32], r)
 	copy(sig[64-len(s):64], s)
 	sig[64] = V
diff --git a/crypto/crypto.go b/crypto/crypto.go
index 4567fafc7..2869b4c19 100644
--- a/crypto/crypto.go
+++ b/crypto/crypto.go
@@ -34,6 +34,15 @@ import (
 	"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))
diff --git a/crypto/signature_cgo.go b/crypto/signature_cgo.go
index aadf028d2..1fe84509e 100644
--- a/crypto/signature_cgo.go
+++ b/crypto/signature_cgo.go
@@ -47,24 +47,24 @@ func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) {
 //
 // This function is susceptible to chosen plaintext attacks that can leak
 // information about the private key that is used for signing. Callers must
-// be aware that the given hash cannot be chosen by an adversery. Common
+// be aware that the given digest cannot be chosen by an adversery. Common
 // solution is to hash any input before calculating the signature.
 //
 // The produced signature is in the [R || S || V] format where V is 0 or 1.
-func Sign(hash []byte, prv *ecdsa.PrivateKey) (sig []byte, err error) {
-	if len(hash) != 32 {
-		return nil, fmt.Errorf("hash is required to be exactly 32 bytes (%d)", len(hash))
+func Sign(digestHash []byte, prv *ecdsa.PrivateKey) (sig []byte, err error) {
+	if len(digestHash) != DigestLength {
+		return nil, fmt.Errorf("hash is required to be exactly %d bytes (%d)", DigestLength, len(digestHash))
 	}
 	seckey := math.PaddedBigBytes(prv.D, prv.Params().BitSize/8)
 	defer zeroBytes(seckey)
-	return secp256k1.Sign(hash, seckey)
+	return secp256k1.Sign(digestHash, seckey)
 }
 
-// VerifySignature checks that the given public key created signature over hash.
+// VerifySignature checks that the given public key created signature over digest.
 // The public key should be in compressed (33 bytes) or uncompressed (65 bytes) format.
 // The signature should have the 64 byte [R || S] format.
-func VerifySignature(pubkey, hash, signature []byte) bool {
-	return secp256k1.VerifySignature(pubkey, hash, signature)
+func VerifySignature(pubkey, digestHash, signature []byte) bool {
+	return secp256k1.VerifySignature(pubkey, digestHash, signature)
 }
 
 // DecompressPubkey parses a public key in the 33-byte compressed format.
diff --git a/crypto/signature_nocgo.go b/crypto/signature_nocgo.go
index 90d072cda..067d32e13 100644
--- a/crypto/signature_nocgo.go
+++ b/crypto/signature_nocgo.go
@@ -41,7 +41,7 @@ func Ecrecover(hash, sig []byte) ([]byte, error) {
 // SigToPub returns the public key that created the given signature.
 func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) {
 	// Convert to btcec input format with 'recovery id' v at the beginning.
-	btcsig := make([]byte, 65)
+	btcsig := make([]byte, SignatureLength)
 	btcsig[0] = sig[64] + 27
 	copy(btcsig[1:], sig)
 
diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go
index 05204e547..d18700e25 100644
--- a/internal/ethapi/api.go
+++ b/internal/ethapi/api.go
@@ -427,7 +427,7 @@ func (s *PrivateAccountAPI) Sign(ctx context.Context, data hexutil.Bytes, addr c
 		log.Warn("Failed data sign attempt", "address", addr, "err", err)
 		return nil, err
 	}
-	signature[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper
+	signature[crypto.RecoveryIDOffset] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper
 	return signature, nil
 }
 
@@ -442,13 +442,13 @@ func (s *PrivateAccountAPI) Sign(ctx context.Context, data hexutil.Bytes, addr c
 //
 // https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_ecRecover
 func (s *PrivateAccountAPI) EcRecover(ctx context.Context, data, sig hexutil.Bytes) (common.Address, error) {
-	if len(sig) != 65 {
-		return common.Address{}, fmt.Errorf("signature must be 65 bytes long")
+	if len(sig) != crypto.SignatureLength {
+		return common.Address{}, fmt.Errorf("signature must be %d bytes long", crypto.SignatureLength)
 	}
-	if sig[64] != 27 && sig[64] != 28 {
+	if sig[crypto.RecoveryIDOffset] != 27 && sig[crypto.RecoveryIDOffset] != 28 {
 		return common.Address{}, fmt.Errorf("invalid Ethereum signature (V is not 27 or 28)")
 	}
-	sig[64] -= 27 // Transform yellow paper V from 27/28 to 0/1
+	sig[crypto.RecoveryIDOffset] -= 27 // Transform yellow paper V from 27/28 to 0/1
 
 	rpk, err := crypto.SigToPub(accounts.TextHash(data), sig)
 	if err != nil {
diff --git a/miner/worker_test.go b/miner/worker_test.go
index 7fa9a5f98..1604e988d 100644
--- a/miner/worker_test.go
+++ b/miner/worker_test.go
@@ -95,7 +95,7 @@ func newTestWorkerBackend(t *testing.T, chainConfig *params.ChainConfig, engine
 
 	switch engine.(type) {
 	case *clique.Clique:
-		gspec.ExtraData = make([]byte, 32+common.AddressLength+65)
+		gspec.ExtraData = make([]byte, 32+common.AddressLength+crypto.SignatureLength)
 		copy(gspec.ExtraData[32:], testBankAddress[:])
 	case *ethash.Ethash:
 	default:
diff --git a/p2p/rlpx.go b/p2p/rlpx.go
index 0636431f5..52e1eb8a4 100644
--- a/p2p/rlpx.go
+++ b/p2p/rlpx.go
@@ -46,10 +46,10 @@ import (
 const (
 	maxUint24 = ^uint32(0) >> 8
 
-	sskLen = 16 // ecies.MaxSharedKeyLength(pubKey) / 2
-	sigLen = 65 // elliptic S256
-	pubLen = 64 // 512 bit pubkey in uncompressed representation without format byte
-	shaLen = 32 // hash length (for nonce etc)
+	sskLen = 16                     // ecies.MaxSharedKeyLength(pubKey) / 2
+	sigLen = crypto.SignatureLength // elliptic S256
+	pubLen = 64                     // 512 bit pubkey in uncompressed representation without format byte
+	shaLen = 32                     // hash length (for nonce etc)
 
 	authMsgLen  = sigLen + shaLen + pubLen + shaLen + 1
 	authRespLen = pubLen + shaLen + 1
diff --git a/whisper/whisperv6/doc.go b/whisper/whisperv6/doc.go
index 529bf3d2d..44c0c3271 100644
--- a/whisper/whisperv6/doc.go
+++ b/whisper/whisperv6/doc.go
@@ -34,6 +34,8 @@ package whisperv6
 
 import (
 	"time"
+
+	"github.com/ethereum/go-ethereum/crypto"
 )
 
 // Whisper protocol parameters
@@ -54,12 +56,12 @@ const (
 	SizeMask      = byte(3) // mask used to extract the size of payload size field from the flags
 	signatureFlag = byte(4)
 
-	TopicLength     = 4  // in bytes
-	signatureLength = 65 // in bytes
-	aesKeyLength    = 32 // in bytes
-	aesNonceLength  = 12 // in bytes; for more info please see cipher.gcmStandardNonceSize & aesgcm.NonceSize()
-	keyIDSize       = 32 // in bytes
-	BloomFilterSize = 64 // in bytes
+	TopicLength     = 4                      // in bytes
+	signatureLength = crypto.SignatureLength // in bytes
+	aesKeyLength    = 32                     // in bytes
+	aesNonceLength  = 12                     // in bytes; for more info please see cipher.gcmStandardNonceSize & aesgcm.NonceSize()
+	keyIDSize       = 32                     // in bytes
+	BloomFilterSize = 64                     // in bytes
 	flagsLength     = 1
 
 	EnvelopeHeaderLength = 20
-- 
GitLab