github.com/MetalBlockchain/metalgo@v1.11.9/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/MetalBlockchain/metalgo/staking" 16 "github.com/MetalBlockchain/metalgo/utils/crypto/bls" 17 "github.com/MetalBlockchain/metalgo/utils/hashing" 18 "github.com/MetalBlockchain/metalgo/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 }