crypto: add SignatureLength constant and use it everywhere (#19996)

Original change by @jpeletier
This commit is contained in:
Felix Lange 2019-08-22 15:14:06 +02:00 committed by GitHub
parent b90cdbaa79
commit 54b271a86d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 46 additions and 33 deletions

View File

@ -32,6 +32,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
) )
@ -341,7 +342,7 @@ func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction
op = ledgerP1ContTransactionData op = ledgerP1ContTransactionData
} }
// Extract the Ethereum signature and do a sanity validation // 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") return common.Address{}, nil, errors.New("reply lacks signature")
} }
signature := append(reply[1:], reply[0]) signature := append(reply[1:], reply[0])

View File

@ -55,8 +55,8 @@ const (
var ( var (
epochLength = uint64(30000) // Default number of blocks after which to checkpoint and reset the pending votes 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 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 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 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. 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.GasLimit,
header.GasUsed, header.GasUsed,
header.Time, 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.MixDigest,
header.Nonce, header.Nonce,
}) })

View File

@ -31,6 +31,7 @@ import (
"github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params" "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 // Assemble and return the genesis with the precompiles and faucet pre-funded
return &Genesis{ return &Genesis{
Config: &config, 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, GasLimit: 6283185,
Difficulty: big.NewInt(1), Difficulty: big.NewInt(1),
Alloc: map[common.Address]GenesisAccount{ Alloc: map[common.Address]GenesisAccount{

View File

@ -193,8 +193,8 @@ func (s FrontierSigner) Equal(s2 Signer) bool {
// SignatureValues returns signature values. This signature // SignatureValues returns signature values. This signature
// needs to be in the [R || S || V] format where V is 0 or 1. // 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) { func (fs FrontierSigner) SignatureValues(tx *Transaction, sig []byte) (r, s, v *big.Int, err error) {
if len(sig) != 65 { if len(sig) != crypto.SignatureLength {
panic(fmt.Sprintf("wrong size for signature: got %d, want 65", len(sig))) panic(fmt.Sprintf("wrong size for signature: got %d, want %d", len(sig), crypto.SignatureLength))
} }
r = new(big.Int).SetBytes(sig[:32]) r = new(big.Int).SetBytes(sig[:32])
s = new(big.Int).SetBytes(sig[32:64]) 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 // encode the signature in uncompressed format
r, s := R.Bytes(), S.Bytes() r, s := R.Bytes(), S.Bytes()
sig := make([]byte, 65) sig := make([]byte, crypto.SignatureLength)
copy(sig[32-len(r):32], r) copy(sig[32-len(r):32], r)
copy(sig[64-len(s):64], s) copy(sig[64-len(s):64], s)
sig[64] = V sig[64] = V

View File

@ -34,6 +34,15 @@ import (
"golang.org/x/crypto/sha3" "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 ( var (
secp256k1N, _ = new(big.Int).SetString("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", 16) secp256k1N, _ = new(big.Int).SetString("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", 16)
secp256k1halfN = new(big.Int).Div(secp256k1N, big.NewInt(2)) secp256k1halfN = new(big.Int).Div(secp256k1N, big.NewInt(2))

View File

@ -47,24 +47,24 @@ func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) {
// //
// This function is susceptible to chosen plaintext attacks that can leak // This function is susceptible to chosen plaintext attacks that can leak
// information about the private key that is used for signing. Callers must // 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. // 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. // 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) { func Sign(digestHash []byte, prv *ecdsa.PrivateKey) (sig []byte, err error) {
if len(hash) != 32 { if len(digestHash) != DigestLength {
return nil, fmt.Errorf("hash is required to be exactly 32 bytes (%d)", len(hash)) 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) seckey := math.PaddedBigBytes(prv.D, prv.Params().BitSize/8)
defer zeroBytes(seckey) 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 public key should be in compressed (33 bytes) or uncompressed (65 bytes) format.
// The signature should have the 64 byte [R || S] format. // The signature should have the 64 byte [R || S] format.
func VerifySignature(pubkey, hash, signature []byte) bool { func VerifySignature(pubkey, digestHash, signature []byte) bool {
return secp256k1.VerifySignature(pubkey, hash, signature) return secp256k1.VerifySignature(pubkey, digestHash, signature)
} }
// DecompressPubkey parses a public key in the 33-byte compressed format. // DecompressPubkey parses a public key in the 33-byte compressed format.

View File

@ -41,7 +41,7 @@ func Ecrecover(hash, sig []byte) ([]byte, error) {
// SigToPub returns the public key that created the given signature. // SigToPub returns the public key that created the given signature.
func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) { func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) {
// Convert to btcec input format with 'recovery id' v at the beginning. // 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 btcsig[0] = sig[64] + 27
copy(btcsig[1:], sig) copy(btcsig[1:], sig)

View File

@ -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) log.Warn("Failed data sign attempt", "address", addr, "err", err)
return nil, 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 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 // 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) { func (s *PrivateAccountAPI) EcRecover(ctx context.Context, data, sig hexutil.Bytes) (common.Address, error) {
if len(sig) != 65 { if len(sig) != crypto.SignatureLength {
return common.Address{}, fmt.Errorf("signature must be 65 bytes long") 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)") 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) rpk, err := crypto.SigToPub(accounts.TextHash(data), sig)
if err != nil { if err != nil {

View File

@ -95,7 +95,7 @@ func newTestWorkerBackend(t *testing.T, chainConfig *params.ChainConfig, engine
switch engine.(type) { switch engine.(type) {
case *clique.Clique: 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[:]) copy(gspec.ExtraData[32:], testBankAddress[:])
case *ethash.Ethash: case *ethash.Ethash:
default: default:

View File

@ -46,10 +46,10 @@ import (
const ( const (
maxUint24 = ^uint32(0) >> 8 maxUint24 = ^uint32(0) >> 8
sskLen = 16 // ecies.MaxSharedKeyLength(pubKey) / 2 sskLen = 16 // ecies.MaxSharedKeyLength(pubKey) / 2
sigLen = 65 // elliptic S256 sigLen = crypto.SignatureLength // elliptic S256
pubLen = 64 // 512 bit pubkey in uncompressed representation without format byte pubLen = 64 // 512 bit pubkey in uncompressed representation without format byte
shaLen = 32 // hash length (for nonce etc) shaLen = 32 // hash length (for nonce etc)
authMsgLen = sigLen + shaLen + pubLen + shaLen + 1 authMsgLen = sigLen + shaLen + pubLen + shaLen + 1
authRespLen = pubLen + shaLen + 1 authRespLen = pubLen + shaLen + 1

View File

@ -34,6 +34,8 @@ package whisperv6
import ( import (
"time" "time"
"github.com/ethereum/go-ethereum/crypto"
) )
// Whisper protocol parameters // Whisper protocol parameters
@ -54,12 +56,12 @@ const (
SizeMask = byte(3) // mask used to extract the size of payload size field from the flags SizeMask = byte(3) // mask used to extract the size of payload size field from the flags
signatureFlag = byte(4) signatureFlag = byte(4)
TopicLength = 4 // in bytes TopicLength = 4 // in bytes
signatureLength = 65 // in bytes signatureLength = crypto.SignatureLength // in bytes
aesKeyLength = 32 // in bytes aesKeyLength = 32 // in bytes
aesNonceLength = 12 // in bytes; for more info please see cipher.gcmStandardNonceSize & aesgcm.NonceSize() aesNonceLength = 12 // in bytes; for more info please see cipher.gcmStandardNonceSize & aesgcm.NonceSize()
keyIDSize = 32 // in bytes keyIDSize = 32 // in bytes
BloomFilterSize = 64 // in bytes BloomFilterSize = 64 // in bytes
flagsLength = 1 flagsLength = 1
EnvelopeHeaderLength = 20 EnvelopeHeaderLength = 20