Skip to content

Commit

Permalink
Provide BLS signature in Handshake message (#2730)
Browse files Browse the repository at this point in the history
  • Loading branch information
StephenButtolph committed Feb 15, 2024
1 parent 2bfa6e8 commit 6e0837e
Show file tree
Hide file tree
Showing 18 changed files with 339 additions and 263 deletions.
6 changes: 4 additions & 2 deletions message/messages_benchmark_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,9 @@ func BenchmarkMarshalHandshake(b *testing.B) {
IpPort: 0,
MyVersion: "v1.2.3",
IpSigningTime: uint64(time.Now().Unix()),
Sig: []byte{'y', 'e', 'e', 't'},
IpNodeIdSig: []byte{'y', 'e', 'e', 't'},
TrackedSubnets: [][]byte{id[:]},
IpBlsSig: []byte{'y', 'e', 'e', 't', '2'},
},
},
}
Expand Down Expand Up @@ -109,8 +110,9 @@ func BenchmarkUnmarshalHandshake(b *testing.B) {
IpPort: 0,
MyVersion: "v1.2.3",
IpSigningTime: uint64(time.Now().Unix()),
Sig: []byte{'y', 'e', 'e', 't'},
IpNodeIdSig: []byte{'y', 'e', 'e', 't'},
TrackedSubnets: [][]byte{id[:]},
IpBlsSig: []byte{'y', 'e', 'e', 't', '2'},
},
},
}
Expand Down
3 changes: 2 additions & 1 deletion message/messages_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,9 @@ func TestMessage(t *testing.T) {
IpPort: 9651,
MyVersion: "v1.2.3",
IpSigningTime: uint64(nowUnix),
Sig: []byte{'y', 'e', 'e', 't'},
IpNodeIdSig: []byte{'y', 'e', 'e', 't'},
TrackedSubnets: [][]byte{testID[:]},
IpBlsSig: []byte{'y', 'e', 'e', 't', '2'},
},
},
},
Expand Down
8 changes: 4 additions & 4 deletions message/mock_outbound_message_builder.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 6 additions & 3 deletions message/outbound_msg_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ type OutboundMsgBuilder interface {
minor uint32,
patch uint32,
ipSigningTime uint64,
sig []byte,
ipNodeIDSig []byte,
ipBLSSig []byte,
trackedSubnets []ids.ID,
supportedACPs []uint32,
objectedACPs []uint32,
Expand Down Expand Up @@ -243,7 +244,8 @@ func (b *outMsgBuilder) Handshake(
minor uint32,
patch uint32,
ipSigningTime uint64,
sig []byte,
ipNodeIDSig []byte,
ipBLSSig []byte,
trackedSubnets []ids.ID,
supportedACPs []uint32,
objectedACPs []uint32,
Expand All @@ -262,7 +264,7 @@ func (b *outMsgBuilder) Handshake(
IpPort: uint32(ip.Port),
MyVersion: myVersion,
IpSigningTime: ipSigningTime,
Sig: sig,
IpNodeIdSig: ipNodeIDSig,
TrackedSubnets: subnetIDBytes,
Client: &p2p.Client{
Name: client,
Expand All @@ -276,6 +278,7 @@ func (b *outMsgBuilder) Handshake(
Filter: knownPeersFilter,
Salt: knownPeersSalt,
},
IpBlsSig: ipBLSSig,
},
},
},
Expand Down
3 changes: 3 additions & 0 deletions network/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/ava-labs/avalanchego/snow/uptime"
"github.com/ava-labs/avalanchego/snow/validators"
"github.com/ava-labs/avalanchego/utils/compression"
"github.com/ava-labs/avalanchego/utils/crypto/bls"
"github.com/ava-labs/avalanchego/utils/ips"
"github.com/ava-labs/avalanchego/utils/set"
)
Expand Down Expand Up @@ -142,6 +143,8 @@ type Config struct {

// TLSKey is this node's TLS key that is used to sign IPs.
TLSKey crypto.Signer `json:"-"`
// BLSKey is this node's BLS key that is used to sign IPs.
BLSKey *bls.SecretKey `json:"-"`

// TrackedSubnets of the node.
TrackedSubnets set.Set[ids.ID] `json:"-"`
Expand Down
6 changes: 3 additions & 3 deletions network/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ func NewNetwork(
ObjectedACPs: config.ObjectedACPs.List(),
ResourceTracker: config.ResourceTracker,
UptimeCalculator: config.UptimeCalculator,
IPSigner: peer.NewIPSigner(config.MyIPPort, config.TLSKey),
IPSigner: peer.NewIPSigner(config.MyIPPort, config.TLSKey, config.BLSKey),
}

// Invariant: We delay the activation of durango during the TLS handshake to
Expand Down Expand Up @@ -435,7 +435,7 @@ func (n *network) Connected(nodeID ids.NodeID) {
peer.Cert(),
peerIP.IPPort,
peerIP.Timestamp,
peerIP.Signature,
peerIP.TLSSignature,
)
n.ipTracker.Connected(newIP)

Expand Down Expand Up @@ -621,7 +621,7 @@ func (n *network) track(ip *ips.ClaimedIPPort) error {
IPPort: ip.IPPort,
Timestamp: ip.Timestamp,
},
Signature: ip.Signature,
TLSSignature: ip.Signature,
}
maxTimestamp := n.peerConfig.Clock.Time().Add(n.peerConfig.MaxClockDifference)
if err := signedIP.Verify(ip.Cert, maxTimestamp); err != nil {
Expand Down
9 changes: 7 additions & 2 deletions network/network_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/ava-labs/avalanchego/staking"
"github.com/ava-labs/avalanchego/subnets"
"github.com/ava-labs/avalanchego/utils/constants"
"github.com/ava-labs/avalanchego/utils/crypto/bls"
"github.com/ava-labs/avalanchego/utils/ips"
"github.com/ava-labs/avalanchego/utils/logging"
"github.com/ava-labs/avalanchego/utils/math/meter"
Expand Down Expand Up @@ -171,11 +172,15 @@ func newTestNetwork(t *testing.T, count int) (*testDialer, []*testListener, []id
ip, listener := dialer.NewListener()
nodeID, tlsCert, tlsConfig := getTLS(t, i)

blsKey, err := bls.NewSecretKey()
require.NoError(t, err)

config := defaultConfig
config.TLSConfig = tlsConfig
config.MyNodeID = nodeID
config.MyIPPort = ip
config.TLSKey = tlsCert.PrivateKey.(crypto.Signer)
config.BLSKey = blsKey

listeners[i] = listener
nodeIDs[i] = nodeID
Expand Down Expand Up @@ -542,7 +547,7 @@ func TestDialDeletesNonValidators(t *testing.T) {
}

config := configs[0]
signer := peer.NewIPSigner(config.MyIPPort, config.TLSKey)
signer := peer.NewIPSigner(config.MyIPPort, config.TLSKey, config.BLSKey)
ip, err := signer.GetSignedIP()
require.NoError(err)

Expand All @@ -555,7 +560,7 @@ func TestDialDeletesNonValidators(t *testing.T) {
staking.CertificateFromX509(config.TLSConfig.Certificates[0].Leaf),
ip.IPPort,
ip.Timestamp,
ip.Signature,
ip.TLSSignature,
),
})
require.NoError(err)
Expand Down
27 changes: 17 additions & 10 deletions network/peer/ip.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@ import (
"time"

"github.com/ava-labs/avalanchego/staking"
"github.com/ava-labs/avalanchego/utils/crypto/bls"
"github.com/ava-labs/avalanchego/utils/hashing"
"github.com/ava-labs/avalanchego/utils/ips"
"github.com/ava-labs/avalanchego/utils/wrappers"
)

var (
errTimestampTooFarInFuture = errors.New("timestamp too far in the future")
errInvalidSignature = errors.New("invalid signature")
errInvalidTLSSignature = errors.New("invalid TLS signature")
)

// UnsignedIP is used for a validator to claim an IP. The [Timestamp] is used to
Expand All @@ -30,15 +31,19 @@ type UnsignedIP struct {
}

// Sign this IP with the provided signer and return the signed IP.
func (ip *UnsignedIP) Sign(signer crypto.Signer) (*SignedIP, error) {
sig, err := signer.Sign(
func (ip *UnsignedIP) Sign(tlsSigner crypto.Signer, blsSigner *bls.SecretKey) (*SignedIP, error) {
ipBytes := ip.bytes()
tlsSignature, err := tlsSigner.Sign(
rand.Reader,
hashing.ComputeHash256(ip.bytes()),
hashing.ComputeHash256(ipBytes),
crypto.SHA256,
)
blsSignature := bls.SignProofOfPossession(blsSigner, ipBytes)
return &SignedIP{
UnsignedIP: *ip,
Signature: sig,
UnsignedIP: *ip,
TLSSignature: tlsSignature,
BLSSignature: blsSignature,
BLSSignatureBytes: bls.SignatureToBytes(blsSignature),
}, err
}

Expand All @@ -54,12 +59,14 @@ func (ip *UnsignedIP) bytes() []byte {
// SignedIP is a wrapper of an UnsignedIP with the signature from a signer.
type SignedIP struct {
UnsignedIP
Signature []byte
TLSSignature []byte
BLSSignature *bls.Signature
BLSSignatureBytes []byte
}

// Returns nil if:
// * [ip.Timestamp] is not after [maxTimestamp].
// * [ip.Signature] is a valid signature over [ip.UnsignedIP] from [cert].
// * [ip.TLSSignature] is a valid signature over [ip.UnsignedIP] from [cert].
func (ip *SignedIP) Verify(
cert *staking.Certificate,
maxTimestamp time.Time,
Expand All @@ -72,9 +79,9 @@ func (ip *SignedIP) Verify(
if err := staking.CheckSignature(
cert,
ip.UnsignedIP.bytes(),
ip.Signature,
ip.TLSSignature,
); err != nil {
return fmt.Errorf("%w: %w", errInvalidSignature, err)
return fmt.Errorf("%w: %w", errInvalidTLSSignature, err)
}
return nil
}
18 changes: 11 additions & 7 deletions network/peer/ip_signer.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,17 @@ import (
"crypto"
"sync"

"github.com/ava-labs/avalanchego/utils/crypto/bls"
"github.com/ava-labs/avalanchego/utils/ips"
"github.com/ava-labs/avalanchego/utils/timer/mockable"
)

// IPSigner will return a signedIP for the current value of our dynamic IP.
type IPSigner struct {
ip ips.DynamicIPPort
clock mockable.Clock
signer crypto.Signer
ip ips.DynamicIPPort
clock mockable.Clock
tlsSigner crypto.Signer
blsSigner *bls.SecretKey

// Must be held while accessing [signedIP]
signedIPLock sync.RWMutex
Expand All @@ -26,11 +28,13 @@ type IPSigner struct {

func NewIPSigner(
ip ips.DynamicIPPort,
signer crypto.Signer,
tlsSigner crypto.Signer,
blsSigner *bls.SecretKey,
) *IPSigner {
return &IPSigner{
ip: ip,
signer: signer,
ip: ip,
tlsSigner: tlsSigner,
blsSigner: blsSigner,
}
}

Expand Down Expand Up @@ -67,7 +71,7 @@ func (s *IPSigner) GetSignedIP() (*SignedIP, error) {
IPPort: ip,
Timestamp: s.clock.Unix(),
}
signedIP, err := unsignedIP.Sign(s.signer)
signedIP, err := unsignedIP.Sign(s.tlsSigner, s.blsSigner)
if err != nil {
return nil, err
}
Expand Down
11 changes: 7 additions & 4 deletions network/peer/ip_signer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/require"

"github.com/ava-labs/avalanchego/staking"
"github.com/ava-labs/avalanchego/utils/crypto/bls"
"github.com/ava-labs/avalanchego/utils/ips"
)

Expand All @@ -26,9 +27,11 @@ func TestIPSigner(t *testing.T) {
tlsCert, err := staking.NewTLSCert()
require.NoError(err)

key := tlsCert.PrivateKey.(crypto.Signer)
tlsKey := tlsCert.PrivateKey.(crypto.Signer)
blsKey, err := bls.NewSecretKey()
require.NoError(err)

s := NewIPSigner(dynIP, key)
s := NewIPSigner(dynIP, tlsKey, blsKey)

s.clock.Set(time.Unix(10, 0))

Expand All @@ -43,13 +46,13 @@ func TestIPSigner(t *testing.T) {
require.NoError(err)
require.Equal(dynIP.IPPort(), signedIP2.IPPort)
require.Equal(uint64(10), signedIP2.Timestamp)
require.Equal(signedIP1.Signature, signedIP2.Signature)
require.Equal(signedIP1.TLSSignature, signedIP2.TLSSignature)

dynIP.SetIP(net.IPv4(1, 2, 3, 4))

signedIP3, err := s.GetSignedIP()
require.NoError(err)
require.Equal(dynIP.IPPort(), signedIP3.IPPort)
require.Equal(uint64(11), signedIP3.Timestamp)
require.NotEqual(signedIP2.Signature, signedIP3.Signature)
require.NotEqual(signedIP2.TLSSignature, signedIP3.TLSSignature)
}
Loading

0 comments on commit 6e0837e

Please sign in to comment.