Repository: libp2p/go-libp2p
Pull request #1460 (2022-04-26 12:23:46)

move go-conn-security-multistream here
Part of #1187.


Patch:
diff --git a/config/security.go b/config/security.go
index 0f6c7887..bac3eb21 100644
--- a/config/security.go
+++ b/config/security.go
@@ -3,13 +3,13 @@ package config
 import (
 	"fmt"
 
+	csms "github.com/libp2p/go-libp2p/p2p/net/conn-security-multistream"
+
 	"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/sec"
 	"github.com/libp2p/go-libp2p-core/sec/insecure"
-
-	csms "github.com/libp2p/go-conn-security-multistream"
 )
 
 // SecC is a security transport constructor.
diff --git a/go.mod b/go.mod
index fed40f9e..a3539c73 100644
--- a/go.mod
+++ b/go.mod
@@ -15,7 +15,6 @@ require (
 	github.com/ipfs/go-log/v2 v2.5.1
 	github.com/klauspost/compress v1.15.1
 	github.com/libp2p/go-buffer-pool v0.0.2
-	github.com/libp2p/go-conn-security-multistream v0.3.0
 	github.com/libp2p/go-eventbus v0.2.1
 	github.com/libp2p/go-libp2p-asn-util v0.1.0
 	github.com/libp2p/go-libp2p-circuit v0.6.0
diff --git a/p2p/net/conn-security-multistream/ssms.go b/p2p/net/conn-security-multistream/ssms.go
new file mode 100644
index 00000000..46bb334a
--- /dev/null
+++ b/p2p/net/conn-security-multistream/ssms.go
@@ -0,0 +1,109 @@
+package csms
+
+import (
+	"context"
+	"fmt"
+	"log"
+	"net"
+
+	"github.com/libp2p/go-libp2p-core/peer"
+	"github.com/libp2p/go-libp2p-core/sec"
+	mss "github.com/multiformats/go-multistream"
+)
+
+// SSMuxer is a multistream stream security transport multiplexer.
+//
+// SSMuxer is safe to use without initialization. However, it's not safe to move
+// after use.
+type SSMuxer struct {
+	mux             mss.MultistreamMuxer
+	tpts            map[string]sec.SecureTransport
+	OrderPreference []string
+}
+
+var _ sec.SecureMuxer = (*SSMuxer)(nil)
+
+// AddTransport adds a stream security transport to this multistream muxer.
+//
+// This method is *not* thread-safe. It should be called only when initializing
+// the SSMuxer.
+func (sm *SSMuxer) AddTransport(path string, transport sec.SecureTransport) {
+	if sm.tpts == nil {
+		sm.tpts = make(map[string]sec.SecureTransport, 1)
+	}
+
+	sm.mux.AddHandler(path, nil)
+	sm.tpts[path] = transport
+	sm.OrderPreference = append(sm.OrderPreference, path)
+}
+
+// SecureInbound secures an inbound connection using this multistream
+// multiplexed stream security transport.
+func (sm *SSMuxer) SecureInbound(ctx context.Context, insecure net.Conn, p peer.ID) (sec.SecureConn, bool, error) {
+	tpt, _, err := sm.selectProto(ctx, insecure, true)
+	if err != nil {
+		return nil, false, err
+	}
+	sconn, err := tpt.SecureInbound(ctx, insecure, p)
+	return sconn, true, err
+}
+
+// SecureOutbound secures an outbound connection using this multistream
+// multiplexed stream security transport.
+func (sm *SSMuxer) SecureOutbound(ctx context.Context, insecure net.Conn, p peer.ID) (sec.SecureConn, bool, error) {
+	tpt, server, err := sm.selectProto(ctx, insecure, false)
+	if err != nil {
+		return nil, false, err
+	}
+
+	var sconn sec.SecureConn
+	if server {
+		sconn, err = tpt.SecureInbound(ctx, insecure, p)
+		if err != nil {
+			return nil, false, fmt.Errorf("failed to secure inbound connection: %s", err)
+		}
+		// ensure the correct peer connected to us
+		if sconn.RemotePeer() != p {
+			sconn.Close()
+			log.Printf("Handshake failed to properly authenticate peer. Authenticated %s, expected %s.", sconn.RemotePeer(), p)
+			return nil, false, fmt.Errorf("unexpected peer")
+		}
+	} else {
+		sconn, err = tpt.SecureOutbound(ctx, insecure, p)
+	}
+
+	return sconn, server, err
+}
+
+func (sm *SSMuxer) selectProto(ctx context.Context, insecure net.Conn, server bool) (sec.SecureTransport, bool, error) {
+	var proto string
+	var err error
+	var iamserver bool
+	done := make(chan struct{})
+	go func() {
+		defer close(done)
+		if server {
+			iamserver = true
+			proto, _, err = sm.mux.Negotiate(insecure)
+		} else {
+			proto, iamserver, err = mss.SelectWithSimopenOrFail(sm.OrderPreference, insecure)
+		}
+	}()
+
+	select {
+	case <-done:
+		if err != nil {
+			return nil, false, err
+		}
+		if tpt, ok := sm.tpts[proto]; ok {
+			return tpt, iamserver, nil
+		}
+		return nil, false, fmt.Errorf("selected unknown security transport")
+	case <-ctx.Done():
+		// We *must* do this. We have outstanding work on the connection
+		// and it's no longer safe to use.
+		insecure.Close()
+		<-done // wait to stop using the connection.
+		return nil, false, ctx.Err()
+	}
+}


Test patch:
diff --git a/p2p/net/conn-security-multistream/ssms_test.go b/p2p/net/conn-security-multistream/ssms_test.go
new file mode 100644
index 00000000..cee925a3
--- /dev/null
+++ b/p2p/net/conn-security-multistream/ssms_test.go
@@ -0,0 +1,79 @@
+package csms
+
+import (
+	"context"
+	"net"
+	"sync"
+	"testing"
+
+	"github.com/libp2p/go-libp2p-core/peer"
+	"github.com/libp2p/go-libp2p-core/sec"
+	"github.com/libp2p/go-libp2p-core/sec/insecure"
+	tnet "github.com/libp2p/go-libp2p-testing/net"
+	sst "github.com/libp2p/go-libp2p-testing/suites/sec"
+)
+
+type TransportAdapter struct {
+	mux *SSMuxer
+}
+
+func (sm *TransportAdapter) SecureInbound(ctx context.Context, insecure net.Conn, p peer.ID) (sec.SecureConn, error) {
+	sconn, _, err := sm.mux.SecureInbound(ctx, insecure, p)
+	return sconn, err
+}
+
+func (sm *TransportAdapter) SecureOutbound(ctx context.Context, insecure net.Conn, p peer.ID) (sec.SecureConn, error) {
+	sconn, _, err := sm.mux.SecureOutbound(ctx, insecure, p)
+	return sconn, err
+}
+
+func TestCommonProto(t *testing.T) {
+	idA := tnet.RandIdentityOrFatal(t)
+	idB := tnet.RandIdentityOrFatal(t)
+
+	var at, bt SSMuxer
+
+	atInsecure := insecure.NewWithIdentity(idA.ID(), idA.PrivateKey())
+	btInsecure := insecure.NewWithIdentity(idB.ID(), idB.PrivateKey())
+	at.AddTransport("/plaintext/1.0.0", atInsecure)
+	bt.AddTransport("/plaintext/1.1.0", btInsecure)
+	bt.AddTransport("/plaintext/1.0.0", btInsecure)
+	sst.SubtestRW(t, &TransportAdapter{mux: &at}, &TransportAdapter{mux: &bt}, idA.ID(), idB.ID())
+}
+
+func TestNoCommonProto(t *testing.T) {
+	idA := tnet.RandIdentityOrFatal(t)
+	idB := tnet.RandIdentityOrFatal(t)
+
+	var at, bt SSMuxer
+	atInsecure := insecure.NewWithIdentity(idA.ID(), idA.PrivateKey())
+	btInsecure := insecure.NewWithIdentity(idB.ID(), idB.PrivateKey())
+
+	at.AddTransport("/plaintext/1.0.0", atInsecure)
+	bt.AddTransport("/plaintext/1.1.0", btInsecure)
+
+	ctx, cancel := context.WithCancel(context.Background())
+	defer cancel()
+	a, b := net.Pipe()
+
+	var wg sync.WaitGroup
+	wg.Add(2)
+	go func() {
+		defer wg.Done()
+		defer a.Close()
+		_, _, err := at.SecureInbound(ctx, a, "")
+		if err == nil {
+			t.Error("connection should have failed")
+		}
+	}()
+
+	go func() {
+		defer wg.Done()
+		defer b.Close()
+		_, _, err := bt.SecureOutbound(ctx, b, "peerA")
+		if err == nil {
+			t.Error("connection should have failed")
+		}
+	}()
+	wg.Wait()
+}
diff --git a/p2p/net/swarm/dial_worker_test.go b/p2p/net/swarm/dial_worker_test.go
index a7cc1e8d..0219a63a 100644
--- a/p2p/net/swarm/dial_worker_test.go
+++ b/p2p/net/swarm/dial_worker_test.go
@@ -9,6 +9,7 @@ import (
 	"time"
 
 	"github.com/libp2p/go-libp2p/p2p/muxer/yamux"
+	csms "github.com/libp2p/go-libp2p/p2p/net/conn-security-multistream"
 	quic "github.com/libp2p/go-libp2p/p2p/transport/quic"
 	"github.com/libp2p/go-libp2p/p2p/transport/tcp"
 
@@ -16,7 +17,6 @@ import (
 	"github.com/libp2p/go-libp2p-core/sec/insecure"
 	"github.com/libp2p/go-libp2p-core/transport"
 
-	csms "github.com/libp2p/go-conn-security-multistream"
 	"github.com/libp2p/go-libp2p-peerstore/pstoremem"
 	tnet "github.com/libp2p/go-libp2p-testing/net"
 	tptu "github.com/libp2p/go-libp2p-transport-upgrader"
diff --git a/p2p/net/swarm/testing/testing.go b/p2p/net/swarm/testing/testing.go
index 0b75205e..6e56112f 100644
--- a/p2p/net/swarm/testing/testing.go
+++ b/p2p/net/swarm/testing/testing.go
@@ -5,6 +5,7 @@ import (
 	"time"
 
 	"github.com/libp2p/go-libp2p/p2p/muxer/yamux"
+	csms "github.com/libp2p/go-libp2p/p2p/net/conn-security-multistream"
 	"github.com/libp2p/go-libp2p/p2p/net/swarm"
 	quic "github.com/libp2p/go-libp2p/p2p/transport/quic"
 	"github.com/libp2p/go-libp2p/p2p/transport/tcp"
@@ -19,7 +20,6 @@ import (
 	"github.com/libp2p/go-libp2p-core/sec/insecure"
 	"github.com/libp2p/go-libp2p-core/transport"
 
-	csms "github.com/libp2p/go-conn-security-multistream"
 	"github.com/libp2p/go-libp2p-peerstore/pstoremem"
 	tnet "github.com/libp2p/go-libp2p-testing/net"
 	tptu "github.com/libp2p/go-libp2p-transport-upgrader"
diff --git a/p2p/transport/tcp/tcp_test.go b/p2p/transport/tcp/tcp_test.go
index 8e48a991..3976d76f 100644
--- a/p2p/transport/tcp/tcp_test.go
+++ b/p2p/transport/tcp/tcp_test.go
@@ -6,6 +6,7 @@ import (
 	"testing"
 
 	"github.com/libp2p/go-libp2p/p2p/muxer/yamux"
+	csms "github.com/libp2p/go-libp2p/p2p/net/conn-security-multistream"
 
 	"github.com/libp2p/go-libp2p-core/crypto"
 	"github.com/libp2p/go-libp2p-core/network"
@@ -14,7 +15,6 @@ import (
 	"github.com/libp2p/go-libp2p-core/sec/insecure"
 	"github.com/libp2p/go-libp2p-core/transport"
 
-	csms "github.com/libp2p/go-conn-security-multistream"
 	mocknetwork "github.com/libp2p/go-libp2p-testing/mocks/network"
 	ttransport "github.com/libp2p/go-libp2p-testing/suites/transport"
 	tptu "github.com/libp2p/go-libp2p-transport-upgrader"
diff --git a/p2p/transport/websocket/websocket_test.go b/p2p/transport/websocket/websocket_test.go
index 01ced160..db80277a 100644
--- a/p2p/transport/websocket/websocket_test.go
+++ b/p2p/transport/websocket/websocket_test.go
@@ -15,6 +15,8 @@ import (
 	"testing"
 	"time"
 
+	csms "github.com/libp2p/go-libp2p/p2p/net/conn-security-multistream"
+
 	"github.com/libp2p/go-libp2p-core/crypto"
 	"github.com/libp2p/go-libp2p-core/network"
 	"github.com/libp2p/go-libp2p-core/peer"
@@ -24,7 +26,6 @@ import (
 	"github.com/libp2p/go-libp2p-core/transport"
 	"github.com/libp2p/go-libp2p/p2p/muxer/yamux"
 
-	csms "github.com/libp2p/go-conn-security-multistream"
 	ttransport "github.com/libp2p/go-libp2p-testing/suites/transport"
 	tptu "github.com/libp2p/go-libp2p-transport-upgrader"