From d6726a3a997364aa3ca7c349ea9915065ea97a88 Mon Sep 17 00:00:00 2001 From: Chinmay Kousik Date: Mon, 1 Aug 2022 11:21:07 +0530 Subject: [PATCH 1/5] Allow adding prologue to noise connections --- p2p/security/noise/crypto_test.go | 2 +- p2p/security/noise/handshake.go | 1 + p2p/security/noise/intermediate_transport.go | 50 ++++++++++++++++++++ p2p/security/noise/session.go | 6 ++- p2p/security/noise/transport.go | 4 +- p2p/security/noise/transport_test.go | 49 +++++++++++++++++++ 6 files changed, 108 insertions(+), 4 deletions(-) create mode 100644 p2p/security/noise/intermediate_transport.go diff --git a/p2p/security/noise/crypto_test.go b/p2p/security/noise/crypto_test.go index ca5125cd70..87efb8487f 100644 --- a/p2p/security/noise/crypto_test.go +++ b/p2p/security/noise/crypto_test.go @@ -93,7 +93,7 @@ func TestCryptoFailsIfHandshakeIncomplete(t *testing.T) { init, resp := net.Pipe() _ = resp.Close() - session, _ := newSecureSession(initTransport, context.TODO(), init, "remote-peer", true) + session, _ := newSecureSession(initTransport, context.TODO(), init, "remote-peer", nil, true) _, err := session.encrypt(nil, []byte("hi")) if err == nil { t.Error("expected encryption error when handshake incomplete") diff --git a/p2p/security/noise/handshake.go b/p2p/security/noise/handshake.go index 65fe294bda..504a2b155c 100644 --- a/p2p/security/noise/handshake.go +++ b/p2p/security/noise/handshake.go @@ -57,6 +57,7 @@ func (s *secureSession) runHandshake(ctx context.Context) (err error) { Pattern: noise.HandshakeXX, Initiator: s.initiator, StaticKeypair: kp, + Prologue: s.prologue, } hs, err := noise.NewHandshakeState(cfg) diff --git a/p2p/security/noise/intermediate_transport.go b/p2p/security/noise/intermediate_transport.go new file mode 100644 index 0000000000..9c989ddaf5 --- /dev/null +++ b/p2p/security/noise/intermediate_transport.go @@ -0,0 +1,50 @@ +package noise + +import ( + "context" + "net" + + "github.com/libp2p/go-libp2p-core/canonicallog" + "github.com/libp2p/go-libp2p-core/peer" + "github.com/libp2p/go-libp2p-core/sec" + manet "github.com/multiformats/go-multiaddr/net" +) + +type NoiseOptions struct { + Prologue []byte +} + +var _ sec.SecureTransport = &intermediateTransport{} + +// intermediate transport can be used +// to provide per-connection options +type intermediateTransport struct { + t *Transport + // options + prologue []byte +} + +// SecureInbound runs the Noise handshake as the responder. +// If p is empty, connections from any peer are accepted. +func (i *intermediateTransport) SecureInbound(ctx context.Context, insecure net.Conn, p peer.ID) (sec.SecureConn, error) { + c, err := newSecureSession(i.t, ctx, insecure, p, i.prologue, false) + if err != nil { + addr, maErr := manet.FromNetAddr(insecure.RemoteAddr()) + if maErr == nil { + canonicallog.LogPeerStatus(100, p, addr, "handshake_failure", "noise", "err", err.Error()) + } + } + return c, err +} + +// SecureOutbound runs the Noise handshake as the initiator. +func (i *intermediateTransport) SecureOutbound(ctx context.Context, insecure net.Conn, p peer.ID) (sec.SecureConn, error) { + return newSecureSession(i.t, ctx, insecure, p, i.prologue, true) +} + +func (t *Transport) WithNoiseOptions(opts NoiseOptions) sec.SecureTransport { + return &intermediateTransport{ + t: t, + prologue: opts.Prologue, + } +} diff --git a/p2p/security/noise/session.go b/p2p/security/noise/session.go index 9675cc03bb..9770178a5c 100644 --- a/p2p/security/noise/session.go +++ b/p2p/security/noise/session.go @@ -34,11 +34,14 @@ type secureSession struct { enc *noise.CipherState dec *noise.CipherState + + // noise prologue + prologue []byte } // newSecureSession creates a Noise session over the given insecureConn Conn, using // the libp2p identity keypair from the given Transport. -func newSecureSession(tpt *Transport, ctx context.Context, insecure net.Conn, remote peer.ID, initiator bool) (*secureSession, error) { +func newSecureSession(tpt *Transport, ctx context.Context, insecure net.Conn, remote peer.ID, prologue []byte, initiator bool) (*secureSession, error) { s := &secureSession{ insecureConn: insecure, insecureReader: bufio.NewReader(insecure), @@ -46,6 +49,7 @@ func newSecureSession(tpt *Transport, ctx context.Context, insecure net.Conn, re localID: tpt.localID, localKey: tpt.privateKey, remoteID: remote, + prologue: prologue, } // the go-routine we create to run the handshake will diff --git a/p2p/security/noise/transport.go b/p2p/security/noise/transport.go index c39da8697b..689a5e123c 100644 --- a/p2p/security/noise/transport.go +++ b/p2p/security/noise/transport.go @@ -40,7 +40,7 @@ func New(privkey crypto.PrivKey) (*Transport, error) { // SecureInbound runs the Noise handshake as the responder. // If p is empty, connections from any peer are accepted. func (t *Transport) SecureInbound(ctx context.Context, insecure net.Conn, p peer.ID) (sec.SecureConn, error) { - c, err := newSecureSession(t, ctx, insecure, p, false) + c, err := newSecureSession(t, ctx, insecure, p, nil, false) if err != nil { addr, maErr := manet.FromNetAddr(insecure.RemoteAddr()) if maErr == nil { @@ -52,5 +52,5 @@ func (t *Transport) SecureInbound(ctx context.Context, insecure net.Conn, p peer // SecureOutbound runs the Noise handshake as the initiator. func (t *Transport) SecureOutbound(ctx context.Context, insecure net.Conn, p peer.ID) (sec.SecureConn, error) { - return newSecureSession(t, ctx, insecure, p, true) + return newSecureSession(t, ctx, insecure, p, nil, true) } diff --git a/p2p/security/noise/transport_test.go b/p2p/security/noise/transport_test.go index e88fbe5943..ac33011252 100644 --- a/p2p/security/noise/transport_test.go +++ b/p2p/security/noise/transport_test.go @@ -372,3 +372,52 @@ func TestReadUnencryptedFails(t *testing.T) { require.Error(t, err) require.Equal(t, 0, afterLen) } + +func TestPrologueMatches(t *testing.T) { + commonPrologue := []byte("test") + initTransport := newTestTransport(t, crypto.Ed25519, 2048) + respTransport := newTestTransport(t, crypto.Ed25519, 2048) + + initConn, respConn := newConnPair(t) + + done := make(chan struct{}) + + go func() { + defer close(done) + conn, err := initTransport. + WithNoiseOptions(NoiseOptions{Prologue: commonPrologue}). + SecureOutbound(context.TODO(), initConn, respTransport.localID) + require.NoError(t, err) + defer conn.Close() + }() + + conn, err := respTransport. + WithNoiseOptions(NoiseOptions{Prologue: commonPrologue}). + SecureInbound(context.TODO(), respConn, "") + require.NoError(t, err) + defer conn.Close() + <-done +} + +func TestPrologueDoesNotMatchFailsHandshake(t *testing.T) { + initTransport := newTestTransport(t, crypto.Ed25519, 2048) + respTransport := newTestTransport(t, crypto.Ed25519, 2048) + + initConn, respConn := newConnPair(t) + + done := make(chan struct{}) + + go func() { + defer close(done) + _, err := initTransport. + WithNoiseOptions(NoiseOptions{Prologue: []byte("testtesttest")}). + SecureOutbound(context.TODO(), initConn, respTransport.localID) + require.Error(t, err) + }() + + _, err := respTransport. + WithNoiseOptions(NoiseOptions{Prologue: []byte("testtesttesttest")}). + SecureInbound(context.TODO(), respConn, "") + require.Error(t, err) + <-done +} From 66889f33d3441cbbdb1a10c52128442a9c470e1b Mon Sep 17 00:00:00 2001 From: Chinmay Kousik Date: Mon, 1 Aug 2022 19:58:50 +0530 Subject: [PATCH 2/5] Expose session transport --- ...iate_transport.go => session_transport.go} | 32 ++++++++++++------- p2p/security/noise/transport_test.go | 29 ++++++++++------- 2 files changed, 37 insertions(+), 24 deletions(-) rename p2p/security/noise/{intermediate_transport.go => session_transport.go} (52%) diff --git a/p2p/security/noise/intermediate_transport.go b/p2p/security/noise/session_transport.go similarity index 52% rename from p2p/security/noise/intermediate_transport.go rename to p2p/security/noise/session_transport.go index 9c989ddaf5..fe5273b34d 100644 --- a/p2p/security/noise/intermediate_transport.go +++ b/p2p/security/noise/session_transport.go @@ -10,15 +10,13 @@ import ( manet "github.com/multiformats/go-multiaddr/net" ) -type NoiseOptions struct { - Prologue []byte -} +type SessionOption = func (*SessionTransport) error -var _ sec.SecureTransport = &intermediateTransport{} +var _ sec.SecureTransport = &SessionTransport{} -// intermediate transport can be used +// SessionTransport can be used // to provide per-connection options -type intermediateTransport struct { +type SessionTransport struct { t *Transport // options prologue []byte @@ -26,7 +24,7 @@ type intermediateTransport struct { // SecureInbound runs the Noise handshake as the responder. // If p is empty, connections from any peer are accepted. -func (i *intermediateTransport) SecureInbound(ctx context.Context, insecure net.Conn, p peer.ID) (sec.SecureConn, error) { +func (i *SessionTransport) SecureInbound(ctx context.Context, insecure net.Conn, p peer.ID) (sec.SecureConn, error) { c, err := newSecureSession(i.t, ctx, insecure, p, i.prologue, false) if err != nil { addr, maErr := manet.FromNetAddr(insecure.RemoteAddr()) @@ -38,13 +36,23 @@ func (i *intermediateTransport) SecureInbound(ctx context.Context, insecure net. } // SecureOutbound runs the Noise handshake as the initiator. -func (i *intermediateTransport) SecureOutbound(ctx context.Context, insecure net.Conn, p peer.ID) (sec.SecureConn, error) { +func (i *SessionTransport) SecureOutbound(ctx context.Context, insecure net.Conn, p peer.ID) (sec.SecureConn, error) { return newSecureSession(i.t, ctx, insecure, p, i.prologue, true) } -func (t *Transport) WithNoiseOptions(opts NoiseOptions) sec.SecureTransport { - return &intermediateTransport{ - t: t, - prologue: opts.Prologue, +func (t *Transport) WithSessionOptions(opts ...SessionOption) (sec.SecureTransport, error) { + st := &SessionTransport{ t: t} + for _, opt := range opts { + if err := opt(st); err != nil { + return nil, err + } + } + return st, nil +} + +func Prologue(prologue []byte) SessionOption { + return func (s *SessionTransport) error { + s.prologue = prologue + return nil } } diff --git a/p2p/security/noise/transport_test.go b/p2p/security/noise/transport_test.go index ac33011252..71c23e2c4f 100644 --- a/p2p/security/noise/transport_test.go +++ b/p2p/security/noise/transport_test.go @@ -384,18 +384,20 @@ func TestPrologueMatches(t *testing.T) { go func() { defer close(done) - conn, err := initTransport. - WithNoiseOptions(NoiseOptions{Prologue: commonPrologue}). - SecureOutbound(context.TODO(), initConn, respTransport.localID) + tpt, err := initTransport. + WithSessionOptions(Prologue(commonPrologue)) require.NoError(t, err) + conn, err := tpt.SecureOutbound(context.TODO(), initConn, respTransport.localID) defer conn.Close() + require.NoError(t, err) }() - conn, err := respTransport. - WithNoiseOptions(NoiseOptions{Prologue: commonPrologue}). - SecureInbound(context.TODO(), respConn, "") + tpt, err := respTransport. + WithSessionOptions(Prologue(commonPrologue)) require.NoError(t, err) + conn, err:= tpt.SecureInbound(context.TODO(), respConn, "") defer conn.Close() + require.NoError(t, err) <-done } @@ -409,15 +411,18 @@ func TestPrologueDoesNotMatchFailsHandshake(t *testing.T) { go func() { defer close(done) - _, err := initTransport. - WithNoiseOptions(NoiseOptions{Prologue: []byte("testtesttest")}). - SecureOutbound(context.TODO(), initConn, respTransport.localID) + tpt, err := initTransport. + WithSessionOptions(Prologue([]byte("testtesttest"))) + require.NoError(t, err) + _, err = tpt.SecureOutbound(context.TODO(), initConn, respTransport.localID) require.Error(t, err) }() - _, err := respTransport. - WithNoiseOptions(NoiseOptions{Prologue: []byte("testtesttesttest")}). - SecureInbound(context.TODO(), respConn, "") + tpt, err := respTransport. + WithSessionOptions(Prologue([]byte("testtesttesttest"))) + require.NoError(t, err) + + _, err = tpt.SecureInbound(context.TODO(), respConn, "") require.Error(t, err) <-done } From faaa3e15dfd0aab1557666c3ccb854d757f4227d Mon Sep 17 00:00:00 2001 From: Chinmay Kousik Date: Mon, 1 Aug 2022 19:58:59 +0530 Subject: [PATCH 3/5] Expose session transport --- p2p/security/noise/session_transport.go | 6 +++--- p2p/security/noise/transport_test.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/p2p/security/noise/session_transport.go b/p2p/security/noise/session_transport.go index fe5273b34d..b996d95b16 100644 --- a/p2p/security/noise/session_transport.go +++ b/p2p/security/noise/session_transport.go @@ -10,7 +10,7 @@ import ( manet "github.com/multiformats/go-multiaddr/net" ) -type SessionOption = func (*SessionTransport) error +type SessionOption = func(*SessionTransport) error var _ sec.SecureTransport = &SessionTransport{} @@ -41,7 +41,7 @@ func (i *SessionTransport) SecureOutbound(ctx context.Context, insecure net.Conn } func (t *Transport) WithSessionOptions(opts ...SessionOption) (sec.SecureTransport, error) { - st := &SessionTransport{ t: t} + st := &SessionTransport{t: t} for _, opt := range opts { if err := opt(st); err != nil { return nil, err @@ -51,7 +51,7 @@ func (t *Transport) WithSessionOptions(opts ...SessionOption) (sec.SecureTranspo } func Prologue(prologue []byte) SessionOption { - return func (s *SessionTransport) error { + return func(s *SessionTransport) error { s.prologue = prologue return nil } diff --git a/p2p/security/noise/transport_test.go b/p2p/security/noise/transport_test.go index 71c23e2c4f..c14808b2ac 100644 --- a/p2p/security/noise/transport_test.go +++ b/p2p/security/noise/transport_test.go @@ -395,7 +395,7 @@ func TestPrologueMatches(t *testing.T) { tpt, err := respTransport. WithSessionOptions(Prologue(commonPrologue)) require.NoError(t, err) - conn, err:= tpt.SecureInbound(context.TODO(), respConn, "") + conn, err := tpt.SecureInbound(context.TODO(), respConn, "") defer conn.Close() require.NoError(t, err) <-done From 6c48ebc941dcc13fa8573d4fb4017d814566ec9a Mon Sep 17 00:00:00 2001 From: Chinmay Kousik Date: Wed, 3 Aug 2022 18:46:42 +0530 Subject: [PATCH 4/5] cleanup noise prologue test --- p2p/security/noise/transport_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/p2p/security/noise/transport_test.go b/p2p/security/noise/transport_test.go index c14808b2ac..9447586e86 100644 --- a/p2p/security/noise/transport_test.go +++ b/p2p/security/noise/transport_test.go @@ -402,6 +402,7 @@ func TestPrologueMatches(t *testing.T) { } func TestPrologueDoesNotMatchFailsHandshake(t *testing.T) { + initPrologue, respPrologue := []byte("initPrologue"), []byte("respPrologue") initTransport := newTestTransport(t, crypto.Ed25519, 2048) respTransport := newTestTransport(t, crypto.Ed25519, 2048) @@ -412,14 +413,13 @@ func TestPrologueDoesNotMatchFailsHandshake(t *testing.T) { go func() { defer close(done) tpt, err := initTransport. - WithSessionOptions(Prologue([]byte("testtesttest"))) + WithSessionOptions(Prologue(initPrologue)) require.NoError(t, err) _, err = tpt.SecureOutbound(context.TODO(), initConn, respTransport.localID) require.Error(t, err) }() - tpt, err := respTransport. - WithSessionOptions(Prologue([]byte("testtesttesttest"))) + tpt, err := respTransport.WithSessionOptions(Prologue(respPrologue)) require.NoError(t, err) _, err = tpt.SecureInbound(context.TODO(), respConn, "") From cabd6b50ceb4b8c9ffc8fa12f5c78b42464cc199 Mon Sep 17 00:00:00 2001 From: Chinmay Kousik Date: Wed, 3 Aug 2022 20:43:46 +0530 Subject: [PATCH 5/5] fix staticheck error --- p2p/security/noise/transport_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/p2p/security/noise/transport_test.go b/p2p/security/noise/transport_test.go index 9447586e86..4ce476139c 100644 --- a/p2p/security/noise/transport_test.go +++ b/p2p/security/noise/transport_test.go @@ -388,16 +388,16 @@ func TestPrologueMatches(t *testing.T) { WithSessionOptions(Prologue(commonPrologue)) require.NoError(t, err) conn, err := tpt.SecureOutbound(context.TODO(), initConn, respTransport.localID) - defer conn.Close() require.NoError(t, err) + defer conn.Close() }() tpt, err := respTransport. WithSessionOptions(Prologue(commonPrologue)) require.NoError(t, err) conn, err := tpt.SecureInbound(context.TODO(), respConn, "") - defer conn.Close() require.NoError(t, err) + defer conn.Close() <-done }