github.com/ava-labs/avalanchego@v1.11.11/network/peer/ip.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package peer
     5  
     6  import (
     7  	"crypto"
     8  	"crypto/rand"
     9  	"errors"
    10  	"fmt"
    11  	"net"
    12  	"net/netip"
    13  	"time"
    14  
    15  	"github.com/ava-labs/avalanchego/staking"
    16  	"github.com/ava-labs/avalanchego/utils/crypto/bls"
    17  	"github.com/ava-labs/avalanchego/utils/hashing"
    18  	"github.com/ava-labs/avalanchego/utils/wrappers"
    19  )
    20  
    21  var (
    22  	errTimestampTooFarInFuture = errors.New("timestamp too far in the future")
    23  	errInvalidTLSSignature     = errors.New("invalid TLS signature")
    24  )
    25  
    26  // UnsignedIP is used for a validator to claim an IP. The [Timestamp] is used to
    27  // ensure that the most updated IP claim is tracked by peers for a given
    28  // validator.
    29  type UnsignedIP struct {
    30  	AddrPort  netip.AddrPort
    31  	Timestamp uint64
    32  }
    33  
    34  // Sign this IP with the provided signer and return the signed IP.
    35  func (ip *UnsignedIP) Sign(tlsSigner crypto.Signer, blsSigner *bls.SecretKey) (*SignedIP, error) {
    36  	ipBytes := ip.bytes()
    37  	tlsSignature, err := tlsSigner.Sign(
    38  		rand.Reader,
    39  		hashing.ComputeHash256(ipBytes),
    40  		crypto.SHA256,
    41  	)
    42  	blsSignature := bls.SignProofOfPossession(blsSigner, ipBytes)
    43  	return &SignedIP{
    44  		UnsignedIP:        *ip,
    45  		TLSSignature:      tlsSignature,
    46  		BLSSignature:      blsSignature,
    47  		BLSSignatureBytes: bls.SignatureToBytes(blsSignature),
    48  	}, err
    49  }
    50  
    51  func (ip *UnsignedIP) bytes() []byte {
    52  	p := wrappers.Packer{
    53  		Bytes: make([]byte, net.IPv6len+wrappers.ShortLen+wrappers.LongLen),
    54  	}
    55  	addrBytes := ip.AddrPort.Addr().As16()
    56  	p.PackFixedBytes(addrBytes[:])
    57  	p.PackShort(ip.AddrPort.Port())
    58  	p.PackLong(ip.Timestamp)
    59  	return p.Bytes
    60  }
    61  
    62  // SignedIP is a wrapper of an UnsignedIP with the signature from a signer.
    63  type SignedIP struct {
    64  	UnsignedIP
    65  	TLSSignature      []byte
    66  	BLSSignature      *bls.Signature
    67  	BLSSignatureBytes []byte
    68  }
    69  
    70  // Returns nil if:
    71  // * [ip.Timestamp] is not after [maxTimestamp].
    72  // * [ip.TLSSignature] is a valid signature over [ip.UnsignedIP] from [cert].
    73  func (ip *SignedIP) Verify(
    74  	cert *staking.Certificate,
    75  	maxTimestamp time.Time,
    76  ) error {
    77  	maxUnixTimestamp := uint64(maxTimestamp.Unix())
    78  	if ip.Timestamp > maxUnixTimestamp {
    79  		return fmt.Errorf("%w: timestamp %d > maxTimestamp %d", errTimestampTooFarInFuture, ip.Timestamp, maxUnixTimestamp)
    80  	}
    81  
    82  	if err := staking.CheckSignature(
    83  		cert,
    84  		ip.UnsignedIP.bytes(),
    85  		ip.TLSSignature,
    86  	); err != nil {
    87  		return fmt.Errorf("%w: %w", errInvalidTLSSignature, err)
    88  	}
    89  	return nil
    90  }