Skip to content

Commit

Permalink
tls: use ALPN to negotiate the stream multiplexer (#1772)
Browse files Browse the repository at this point in the history
* Muxer selection in TLS handshake first cut

* Clean up some part of the code

* Change earlydata to ConnectionState for security connection.

* resolve merging conflicts

* Add stubs for noise

* clean up code

* Switch over to passing muxers to security transport constructors

* Address feedback points

* Update p2p/net/upgrader/upgrader.go

Co-authored-by: Marten Seemann <martenseemann@gmail.com>

* clean up accidental checked file.

* Review points round 2

* Address some go nit points

* Update tls transport test to address review points

Co-authored-by: Marten Seemann <martenseemann@gmail.com>
  • Loading branch information
julian88110 and marten-seemann committed Oct 7, 2022
1 parent 69a574c commit ebd000d
Show file tree
Hide file tree
Showing 20 changed files with 227 additions and 46 deletions.
2 changes: 1 addition & 1 deletion config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ func (cfg *Config) addTransports(h host.Host) error {
secure = makeInsecureTransport(h.ID(), cfg.PeerKey)
} else {
var err error
secure, err = makeSecurityMuxer(h, cfg.SecurityTransports)
secure, err = makeSecurityMuxer(h, cfg.SecurityTransports, cfg.Muxers)
if err != nil {
return err
}
Expand Down
27 changes: 16 additions & 11 deletions config/constructor_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/core/peerstore"
"github.com/libp2p/go-libp2p/core/pnet"
"github.com/libp2p/go-libp2p/core/protocol"
"github.com/libp2p/go-libp2p/core/sec"
"github.com/libp2p/go-libp2p/core/transport"

Expand All @@ -35,42 +36,46 @@ var (
peerIDType = reflect.TypeOf((peer.ID)(""))
pskType = reflect.TypeOf((pnet.PSK)(nil))
resolverType = reflect.TypeOf((*madns.Resolver)(nil))
muxersType = reflect.TypeOf(([]protocol.ID)(nil))
)

var argTypes = map[reflect.Type]constructor{
upgraderType: func(_ host.Host, u transport.Upgrader, _ pnet.PSK, _ connmgr.ConnectionGater, _ network.ResourceManager, _ *madns.Resolver) interface{} {
upgraderType: func(_ host.Host, u transport.Upgrader, _ pnet.PSK, _ connmgr.ConnectionGater, _ network.ResourceManager, _ *madns.Resolver, _ []protocol.ID) interface{} {
return u
},
hostType: func(h host.Host, _ transport.Upgrader, _ pnet.PSK, _ connmgr.ConnectionGater, _ network.ResourceManager, _ *madns.Resolver) interface{} {
hostType: func(h host.Host, _ transport.Upgrader, _ pnet.PSK, _ connmgr.ConnectionGater, _ network.ResourceManager, _ *madns.Resolver, _ []protocol.ID) interface{} {
return h
},
networkType: func(h host.Host, _ transport.Upgrader, _ pnet.PSK, _ connmgr.ConnectionGater, _ network.ResourceManager, _ *madns.Resolver) interface{} {
networkType: func(h host.Host, _ transport.Upgrader, _ pnet.PSK, _ connmgr.ConnectionGater, _ network.ResourceManager, _ *madns.Resolver, _ []protocol.ID) interface{} {
return h.Network()
},
pskType: func(_ host.Host, _ transport.Upgrader, psk pnet.PSK, _ connmgr.ConnectionGater, _ network.ResourceManager, _ *madns.Resolver) interface{} {
pskType: func(_ host.Host, _ transport.Upgrader, psk pnet.PSK, _ connmgr.ConnectionGater, _ network.ResourceManager, _ *madns.Resolver, _ []protocol.ID) interface{} {
return psk
},
connGaterType: func(_ host.Host, _ transport.Upgrader, _ pnet.PSK, cg connmgr.ConnectionGater, _ network.ResourceManager, _ *madns.Resolver) interface{} {
connGaterType: func(_ host.Host, _ transport.Upgrader, _ pnet.PSK, cg connmgr.ConnectionGater, _ network.ResourceManager, _ *madns.Resolver, _ []protocol.ID) interface{} {
return cg
},
peerIDType: func(h host.Host, _ transport.Upgrader, _ pnet.PSK, _ connmgr.ConnectionGater, _ network.ResourceManager, _ *madns.Resolver) interface{} {
peerIDType: func(h host.Host, _ transport.Upgrader, _ pnet.PSK, _ connmgr.ConnectionGater, _ network.ResourceManager, _ *madns.Resolver, _ []protocol.ID) interface{} {
return h.ID()
},
privKeyType: func(h host.Host, _ transport.Upgrader, _ pnet.PSK, _ connmgr.ConnectionGater, _ network.ResourceManager, _ *madns.Resolver) interface{} {
privKeyType: func(h host.Host, _ transport.Upgrader, _ pnet.PSK, _ connmgr.ConnectionGater, _ network.ResourceManager, _ *madns.Resolver, _ []protocol.ID) interface{} {
return h.Peerstore().PrivKey(h.ID())
},
pubKeyType: func(h host.Host, _ transport.Upgrader, _ pnet.PSK, _ connmgr.ConnectionGater, _ network.ResourceManager, _ *madns.Resolver) interface{} {
pubKeyType: func(h host.Host, _ transport.Upgrader, _ pnet.PSK, _ connmgr.ConnectionGater, _ network.ResourceManager, _ *madns.Resolver, _ []protocol.ID) interface{} {
return h.Peerstore().PubKey(h.ID())
},
pstoreType: func(h host.Host, _ transport.Upgrader, _ pnet.PSK, _ connmgr.ConnectionGater, _ network.ResourceManager, _ *madns.Resolver) interface{} {
pstoreType: func(h host.Host, _ transport.Upgrader, _ pnet.PSK, _ connmgr.ConnectionGater, _ network.ResourceManager, _ *madns.Resolver, _ []protocol.ID) interface{} {
return h.Peerstore()
},
rcmgrType: func(_ host.Host, _ transport.Upgrader, _ pnet.PSK, _ connmgr.ConnectionGater, rcmgr network.ResourceManager, _ *madns.Resolver) interface{} {
rcmgrType: func(_ host.Host, _ transport.Upgrader, _ pnet.PSK, _ connmgr.ConnectionGater, rcmgr network.ResourceManager, _ *madns.Resolver, _ []protocol.ID) interface{} {
return rcmgr
},
resolverType: func(_ host.Host, _ transport.Upgrader, _ pnet.PSK, _ connmgr.ConnectionGater, _ network.ResourceManager, r *madns.Resolver) interface{} {
resolverType: func(_ host.Host, _ transport.Upgrader, _ pnet.PSK, _ connmgr.ConnectionGater, _ network.ResourceManager, r *madns.Resolver, _ []protocol.ID) interface{} {
return r
},
muxersType: func(_ host.Host, _ transport.Upgrader, _ pnet.PSK, _ connmgr.ConnectionGater, _ network.ResourceManager, _ *madns.Resolver, muxers []protocol.ID) interface{} {
return muxers
},
}

func newArgTypeSet(types ...reflect.Type) map[reflect.Type]constructor {
Expand Down
2 changes: 1 addition & 1 deletion config/muxer.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func MuxerConstructor(m interface{}) (MuxC, error) {
return nil, err
}
return func(h host.Host) (network.Multiplexer, error) {
t, err := ctor(h, nil, nil, nil, nil, nil)
t, err := ctor(h, nil, nil, nil, nil, nil, nil)
if err != nil {
return nil, err
}
Expand Down
9 changes: 5 additions & 4 deletions config/reflection_magic.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/libp2p/go-libp2p/core/host"
"github.com/libp2p/go-libp2p/core/network"
"github.com/libp2p/go-libp2p/core/pnet"
"github.com/libp2p/go-libp2p/core/protocol"
"github.com/libp2p/go-libp2p/core/transport"

madns "github.com/multiformats/go-multiaddr-dns"
Expand Down Expand Up @@ -82,7 +83,7 @@ func callConstructor(c reflect.Value, args []reflect.Value) (interface{}, error)
return val, err
}

type constructor func(host.Host, transport.Upgrader, pnet.PSK, connmgr.ConnectionGater, network.ResourceManager, *madns.Resolver) interface{}
type constructor func(host.Host, transport.Upgrader, pnet.PSK, connmgr.ConnectionGater, network.ResourceManager, *madns.Resolver, []protocol.ID) interface{}

func makeArgumentConstructors(fnType reflect.Type, argTypes map[reflect.Type]constructor) ([]constructor, error) {
params := fnType.NumIn()
Expand Down Expand Up @@ -133,7 +134,7 @@ func makeConstructor(
tptType reflect.Type,
argTypes map[reflect.Type]constructor,
opts ...interface{},
) (func(host.Host, transport.Upgrader, pnet.PSK, connmgr.ConnectionGater, network.ResourceManager, *madns.Resolver) (interface{}, error), error) {
) (func(host.Host, transport.Upgrader, pnet.PSK, connmgr.ConnectionGater, network.ResourceManager, *madns.Resolver, []protocol.ID) (interface{}, error), error) {
v := reflect.ValueOf(tpt)
// avoid panicing on nil/zero value.
if v == (reflect.Value{}) {
Expand All @@ -157,10 +158,10 @@ func makeConstructor(
return nil, err
}

return func(h host.Host, u transport.Upgrader, psk pnet.PSK, cg connmgr.ConnectionGater, rcmgr network.ResourceManager, resolver *madns.Resolver) (interface{}, error) {
return func(h host.Host, u transport.Upgrader, psk pnet.PSK, cg connmgr.ConnectionGater, rcmgr network.ResourceManager, resolver *madns.Resolver, muxers []protocol.ID) (interface{}, error) {
arguments := make([]reflect.Value, 0, len(argConstructors)+len(opts))
for i, makeArg := range argConstructors {
if arg := makeArg(h, u, psk, cg, rcmgr, resolver); arg != nil {
if arg := makeArg(h, u, psk, cg, rcmgr, resolver, muxers); arg != nil {
arguments = append(arguments, reflect.ValueOf(arg))
} else {
// ValueOf an un-typed nil yields a zero reflect
Expand Down
18 changes: 12 additions & 6 deletions config/security.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ import (
"github.com/libp2p/go-libp2p/core/crypto"
"github.com/libp2p/go-libp2p/core/host"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/core/protocol"
"github.com/libp2p/go-libp2p/core/sec"
"github.com/libp2p/go-libp2p/core/sec/insecure"
csms "github.com/libp2p/go-libp2p/p2p/net/conn-security-multistream"
)

// SecC is a security transport constructor.
type SecC func(h host.Host) (sec.SecureTransport, error)
type SecC func(h host.Host, muxers []protocol.ID) (sec.SecureTransport, error)

// MsSecC is a tuple containing a security transport constructor and a protocol
// ID.
Expand All @@ -24,14 +25,15 @@ type MsSecC struct {
var securityArgTypes = newArgTypeSet(
hostType, networkType, peerIDType,
privKeyType, pubKeyType, pstoreType,
muxersType,
)

// SecurityConstructor creates a security constructor from the passed parameter
// using reflection.
func SecurityConstructor(security interface{}) (SecC, error) {
// Already constructed?
if t, ok := security.(sec.SecureTransport); ok {
return func(_ host.Host) (sec.SecureTransport, error) {
return func(_ host.Host, _ []protocol.ID) (sec.SecureTransport, error) {
return t, nil
}, nil
}
Expand All @@ -40,8 +42,8 @@ func SecurityConstructor(security interface{}) (SecC, error) {
if err != nil {
return nil, err
}
return func(h host.Host) (sec.SecureTransport, error) {
t, err := ctor(h, nil, nil, nil, nil, nil)
return func(h host.Host, muxers []protocol.ID) (sec.SecureTransport, error) {
t, err := ctor(h, nil, nil, nil, nil, nil, muxers)
if err != nil {
return nil, err
}
Expand All @@ -55,7 +57,7 @@ func makeInsecureTransport(id peer.ID, privKey crypto.PrivKey) sec.SecureMuxer {
return secMuxer
}

func makeSecurityMuxer(h host.Host, tpts []MsSecC) (sec.SecureMuxer, error) {
func makeSecurityMuxer(h host.Host, tpts []MsSecC, muxers []MsMuxC) (sec.SecureMuxer, error) {
secMuxer := new(csms.SSMuxer)
transportSet := make(map[string]struct{}, len(tpts))
for _, tptC := range tpts {
Expand All @@ -64,8 +66,12 @@ func makeSecurityMuxer(h host.Host, tpts []MsSecC) (sec.SecureMuxer, error) {
}
transportSet[tptC.ID] = struct{}{}
}
muxIds := make([]protocol.ID, 0, len(muxers))
for _, muxc := range muxers {
muxIds = append(muxIds, protocol.ID(muxc.ID))
}
for _, tptC := range tpts {
tpt, err := tptC.SecC(h)
tpt, err := tptC.SecC(h, muxIds)
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion config/transport.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func TransportConstructor(tpt interface{}, opts ...interface{}) (TptC, error) {
return nil, err
}
return func(h host.Host, u transport.Upgrader, psk pnet.PSK, cg connmgr.ConnectionGater, rcmgr network.ResourceManager, resolver *madns.Resolver) (transport.Transport, error) {
t, err := ctor(h, u, psk, cg, rcmgr, resolver)
t, err := ctor(h, u, psk, cg, rcmgr, resolver, nil)
if err != nil {
return nil, err
}
Expand Down
11 changes: 11 additions & 0 deletions core/network/conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ type Conn interface {
GetStreams() []Stream
}

// ConnectionState holds extra information releated to the ConnSecurity entity.
type ConnectionState struct {
// The next protocol used for stream muxer selection. This is derived from
// security protocol handshake, for example, Noise handshake payload or
// TLS/ALPN negotiation.
NextProto string
}

// ConnSecurity is the interface that one can mix into a connection interface to
// give it the security methods.
type ConnSecurity interface {
Expand All @@ -48,6 +56,9 @@ type ConnSecurity interface {

// RemotePublicKey returns the public key of the remote peer.
RemotePublicKey() ic.PubKey

// Connection state info of the secured connection.
ConnState() ConnectionState
}

// ConnMultiaddrs is an interface mixin for connection types that provide multiaddr
Expand Down
6 changes: 6 additions & 0 deletions core/sec/insecure/insecure.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"net"

ci "github.com/libp2p/go-libp2p/core/crypto"
"github.com/libp2p/go-libp2p/core/network"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/core/sec"
pb "github.com/libp2p/go-libp2p/core/sec/insecure/pb"
Expand Down Expand Up @@ -230,5 +231,10 @@ func (ic *Conn) LocalPrivateKey() ci.PrivKey {
return ic.localPrivKey
}

// ConnState returns the security connection's state information.
func (ic *Conn) ConnState() network.ConnectionState {
return network.ConnectionState{}
}

var _ sec.SecureTransport = (*Transport)(nil)
var _ sec.SecureConn = (*Conn)(nil)
5 changes: 5 additions & 0 deletions p2p/muxer/muxer-multistream/multistream.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,8 @@ func (t *Transport) NewConn(nc net.Conn, isServer bool, scope network.PeerScope)

return tpt.NewConn(nc, isServer, scope)
}

func (t *Transport) GetTransportByKey(key string) (network.Multiplexer, bool) {
val, ok := t.tpts[key]
return val, ok
}
1 change: 1 addition & 0 deletions p2p/net/connmgr/connmgr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -806,6 +806,7 @@ func (m mockConn) ID() string { panic
func (m mockConn) NewStream(ctx context.Context) (network.Stream, error) { panic("implement me") }
func (m mockConn) GetStreams() []network.Stream { panic("implement me") }
func (m mockConn) Scope() network.ConnScope { panic("implement me") }
func (m mockConn) ConnState() network.ConnectionState { return network.ConnectionState{} }

func TestPeerInfoSorting(t *testing.T) {
t.Run("starts with temporary connections", func(t *testing.T) {
Expand Down
5 changes: 5 additions & 0 deletions p2p/net/mock/mock_conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,11 @@ func (c *conn) RemotePublicKey() ic.PubKey {
return c.remotePubKey
}

// ConnState of security connection. Empty if not supported.
func (c *conn) ConnState() network.ConnectionState {
return network.ConnectionState{}
}

// Stat returns metadata about the connection
func (c *conn) Stat() network.ConnStats {
return c.stat
Expand Down
6 changes: 6 additions & 0 deletions p2p/net/swarm/swarm_conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,12 @@ func (c *Conn) RemotePublicKey() ic.PubKey {
return c.conn.RemotePublicKey()
}

// ConnState is the security connection state. including early data result.
// Empty if not supported.
func (c *Conn) ConnState() network.ConnectionState {
return c.conn.ConnState()
}

// Stat returns metadata pertaining to this connection
func (c *Conn) Stat() network.ConnStats {
c.streams.Lock()
Expand Down
17 changes: 15 additions & 2 deletions p2p/net/upgrader/upgrader.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
ipnet "github.com/libp2p/go-libp2p/core/pnet"
"github.com/libp2p/go-libp2p/core/sec"
"github.com/libp2p/go-libp2p/core/transport"
msmux "github.com/libp2p/go-libp2p/p2p/muxer/muxer-multistream"
"github.com/libp2p/go-libp2p/p2p/net/pnet"

manet "github.com/multiformats/go-multiaddr/net"
Expand Down Expand Up @@ -194,12 +195,24 @@ func (u *upgrader) setupSecurity(ctx context.Context, conn net.Conn, p peer.ID,
return u.secure.SecureOutbound(ctx, conn, p)
}

func (u *upgrader) setupMuxer(ctx context.Context, conn net.Conn, server bool, scope network.PeerScope) (network.MuxedConn, error) {
// TODO: The muxer should take a context.
func (u *upgrader) setupMuxer(ctx context.Context, conn sec.SecureConn, server bool, scope network.PeerScope) (network.MuxedConn, error) {
msmuxer, ok := u.muxer.(*msmux.Transport)
muxerSelected := conn.ConnState().NextProto
// Use muxer selected from security handshake if available. Otherwise fall back to multistream-selection.
if ok && len(muxerSelected) > 0 {
tpt, ok := msmuxer.GetTransportByKey(muxerSelected)
if !ok {
return nil, fmt.Errorf("selected a muxer we don't know: %s", muxerSelected)
}

return tpt.NewConn(conn, server, scope)
}

done := make(chan struct{})

var smconn network.MuxedConn
var err error
// TODO: The muxer should take a context.
go func() {
defer close(done)
smconn, err = u.muxer.NewConn(conn, server, scope)
Expand Down
5 changes: 5 additions & 0 deletions p2p/security/noise/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/flynn/noise"

"github.com/libp2p/go-libp2p/core/crypto"
"github.com/libp2p/go-libp2p/core/network"
"github.com/libp2p/go-libp2p/core/peer"
)

Expand Down Expand Up @@ -108,6 +109,10 @@ func (s *secureSession) RemotePublicKey() crypto.PubKey {
return s.remoteKey
}

func (s *secureSession) ConnState() network.ConnectionState {
return network.ConnectionState{}
}

func (s *secureSession) SetDeadline(t time.Time) error {
return s.insecureConn.SetDeadline(t)
}
Expand Down
2 changes: 1 addition & 1 deletion p2p/security/tls/cmd/tlsdiag/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func StartClient() error {
return err
}
fmt.Printf(" Peer ID: %s\n", id.Pretty())
tp, err := libp2ptls.New(priv)
tp, err := libp2ptls.New(priv, nil)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion p2p/security/tls/cmd/tlsdiag/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func StartServer() error {
return err
}
fmt.Printf(" Peer ID: %s\n", id.Pretty())
tp, err := libp2ptls.New(priv)
tp, err := libp2ptls.New(priv, nil)
if err != nil {
return err
}
Expand Down
10 changes: 8 additions & 2 deletions p2p/security/tls/conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"crypto/tls"

ci "github.com/libp2p/go-libp2p/core/crypto"
"github.com/libp2p/go-libp2p/core/network"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/core/sec"
)
Expand All @@ -14,8 +15,9 @@ type conn struct {
localPeer peer.ID
privKey ci.PrivKey

remotePeer peer.ID
remotePubKey ci.PubKey
remotePeer peer.ID
remotePubKey ci.PubKey
connectionState network.ConnectionState
}

var _ sec.SecureConn = &conn{}
Expand All @@ -35,3 +37,7 @@ func (c *conn) RemotePeer() peer.ID {
func (c *conn) RemotePublicKey() ci.PubKey {
return c.remotePubKey
}

func (c *conn) ConnState() network.ConnectionState {
return c.connectionState
}
Loading

0 comments on commit ebd000d

Please sign in to comment.