From 25fb9d2587033761033616d5fda76333299ef2ec Mon Sep 17 00:00:00 2001 From: Chinmay Kousik Date: Wed, 12 Oct 2022 20:41:41 +0530 Subject: [PATCH 01/10] Allow unknown peer ID in noise handshake of initiator --- p2p/security/noise/handshake.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/p2p/security/noise/handshake.go b/p2p/security/noise/handshake.go index 6e119cb594..a6b9009da2 100644 --- a/p2p/security/noise/handshake.go +++ b/p2p/security/noise/handshake.go @@ -273,9 +273,8 @@ func (s *secureSession) handleRemoteHandshakePayload(payload []byte, remoteStati } // check the peer ID for: - // * all outbound connection - // * inbound connections, if we know which peer we want to connect to (SecureInbound called with a peer ID) - if (s.initiator && s.remoteID != id) || (!s.initiator && s.remoteID != "" && s.remoteID != id) { + // * all connections, if we know the peer ID to which we want to connect. + if s.remoteID != "" && s.remoteID != id { // use Pretty() as it produces the full b58-encoded string, rather than abbreviated forms. return nil, fmt.Errorf("peer id mismatch: expected %s, but remote key matches %s", s.remoteID.Pretty(), id.Pretty()) } From dabddb94b2b5c4de11f194b829bedb840d2f68c1 Mon Sep 17 00:00:00 2001 From: Chinmay Kousik Date: Fri, 14 Oct 2022 21:31:10 +0530 Subject: [PATCH 02/10] add session transport option --- p2p/security/noise/crypto_test.go | 2 +- p2p/security/noise/handshake.go | 10 ++++++---- p2p/security/noise/session.go | 6 ++++-- p2p/security/noise/session_transport.go | 14 +++++++++++--- p2p/security/noise/transport.go | 10 +++++++--- p2p/security/noise/transport_test.go | 9 +++++---- 6 files changed, 34 insertions(+), 17 deletions(-) diff --git a/p2p/security/noise/crypto_test.go b/p2p/security/noise/crypto_test.go index 1ca11476f4..5c40a5ef61 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", nil, nil, nil, true) + session, _ := newSecureSession(initTransport, context.TODO(), init, "remote-peer", nil, nil, nil, true, 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 a6b9009da2..7d3d6337d3 100644 --- a/p2p/security/noise/handshake.go +++ b/p2p/security/noise/handshake.go @@ -272,12 +272,14 @@ func (s *secureSession) handleRemoteHandshakePayload(payload []byte, remoteStati return nil, err } - // check the peer ID for: - // * all connections, if we know the peer ID to which we want to connect. - if s.remoteID != "" && s.remoteID != id { - // use Pretty() as it produces the full b58-encoded string, rather than abbreviated forms. + // check the peer ID if enabled + if s.checkPeerID && s.remoteID != id { return nil, fmt.Errorf("peer id mismatch: expected %s, but remote key matches %s", s.remoteID.Pretty(), id.Pretty()) } + // if (s.initiator && s.remoteID != id) || (!s.initiator && s.remoteID != "" && s.remoteID != id) { + // // use Pretty() as it produces the full b58-encoded string, rather than abbreviated forms. + // return nil, fmt.Errorf("peer id mismatch: expected %s, but remote key matches %s", s.remoteID.Pretty(), id.Pretty()) + // } // verify payload is signed by asserted remote libp2p key. sig := nhp.GetIdentitySig() diff --git a/p2p/security/noise/session.go b/p2p/security/noise/session.go index ce8d97cdc8..f7e75e64ae 100644 --- a/p2p/security/noise/session.go +++ b/p2p/security/noise/session.go @@ -15,7 +15,8 @@ import ( ) type secureSession struct { - initiator bool + initiator bool + checkPeerID bool localID peer.ID localKey crypto.PrivKey @@ -47,7 +48,7 @@ type secureSession struct { // 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, prologue []byte, initiatorEDH, responderEDH EarlyDataHandler, initiator bool) (*secureSession, error) { +func newSecureSession(tpt *Transport, ctx context.Context, insecure net.Conn, remote peer.ID, prologue []byte, initiatorEDH, responderEDH EarlyDataHandler, initiator, checkPeerID bool) (*secureSession, error) { s := &secureSession{ insecureConn: insecure, insecureReader: bufio.NewReader(insecure), @@ -58,6 +59,7 @@ func newSecureSession(tpt *Transport, ctx context.Context, insecure net.Conn, re prologue: prologue, initiatorEarlyDataHandler: initiatorEDH, responderEarlyDataHandler: responderEDH, + checkPeerID: checkPeerID, } // the go-routine we create to run the handshake will diff --git a/p2p/security/noise/session_transport.go b/p2p/security/noise/session_transport.go index 7b468edbb4..05e288b9bf 100644 --- a/p2p/security/noise/session_transport.go +++ b/p2p/security/noise/session_transport.go @@ -50,6 +50,13 @@ func EarlyData(initiator, responder EarlyDataHandler) SessionOption { } } +func CheckPeerID(enable bool) SessionOption { + return func(s *SessionTransport) error { + s.checkPeerID = enable + return nil + } +} + var _ sec.SecureTransport = &SessionTransport{} // SessionTransport can be used @@ -57,7 +64,8 @@ var _ sec.SecureTransport = &SessionTransport{} type SessionTransport struct { t *Transport // options - prologue []byte + prologue []byte + checkPeerID bool initiatorEarlyDataHandler, responderEarlyDataHandler EarlyDataHandler } @@ -65,7 +73,7 @@ type SessionTransport struct { // SecureInbound runs the Noise handshake as the responder. // If p is empty, connections from any peer are accepted. 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, i.initiatorEarlyDataHandler, i.responderEarlyDataHandler, false) + c, err := newSecureSession(i.t, ctx, insecure, p, i.prologue, i.initiatorEarlyDataHandler, i.responderEarlyDataHandler, false, i.checkPeerID) if err != nil { addr, maErr := manet.FromNetAddr(insecure.RemoteAddr()) if maErr == nil { @@ -77,5 +85,5 @@ func (i *SessionTransport) SecureInbound(ctx context.Context, insecure net.Conn, // SecureOutbound runs the Noise handshake as the initiator. 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, i.initiatorEarlyDataHandler, i.responderEarlyDataHandler, true) + return newSecureSession(i.t, ctx, insecure, p, i.prologue, i.initiatorEarlyDataHandler, i.responderEarlyDataHandler, true, i.checkPeerID) } diff --git a/p2p/security/noise/transport.go b/p2p/security/noise/transport.go index 7ae4d7deb1..3b318a1900 100644 --- a/p2p/security/noise/transport.go +++ b/p2p/security/noise/transport.go @@ -51,10 +51,14 @@ func New(privkey crypto.PrivKey, muxers []protocol.ID) (*Transport, error) { } // SecureInbound runs the Noise handshake as the responder. -// If p is empty, connections from any peer are accepted. +// if p is empty accept any peer ID. func (t *Transport) SecureInbound(ctx context.Context, insecure net.Conn, p peer.ID) (sec.SecureConn, error) { responderEDH := newTransportEDH(t) - c, err := newSecureSession(t, ctx, insecure, p, nil, nil, responderEDH, false) + checkPeerID := true + if p == "" { + checkPeerID = false + } + c, err := newSecureSession(t, ctx, insecure, p, nil, nil, responderEDH, checkPeerID) if err != nil { addr, maErr := manet.FromNetAddr(insecure.RemoteAddr()) if maErr == nil { @@ -74,7 +78,7 @@ func (t *Transport) SecureOutbound(ctx context.Context, insecure net.Conn, p pee return SessionWithConnState(c, initiatorEDH.MatchMuxers(true)), err } -func (t *Transport) WithSessionOptions(opts ...SessionOption) (sec.SecureTransport, error) { +func (t *Transport) WithSessionOptions(opts ...SessionOption) (*SessionTransport, error) { st := &SessionTransport{t: t} for _, opt := range opts { if err := opt(st); err != nil { diff --git a/p2p/security/noise/transport_test.go b/p2p/security/noise/transport_test.go index c3b180470e..ad2464012f 100644 --- a/p2p/security/noise/transport_test.go +++ b/p2p/security/noise/transport_test.go @@ -88,7 +88,8 @@ func connect(t *testing.T, initTransport, respTransport *Transport) (*secureSess initConn, initErr = initTransport.SecureOutbound(context.Background(), init, respTransport.localID) }() - respConn, respErr := respTransport.SecureInbound(context.Background(), resp, "") + receiverTpt, _ := respTransport.WithSessionOptions(CheckPeerID(false)) + respConn, respErr := receiverTpt.SecureInbound(context.Background(), resp, "") <-done if initErr != nil { @@ -196,7 +197,7 @@ func TestPeerIDMatch(t *testing.T) { func TestPeerIDMismatchOutboundFailsHandshake(t *testing.T) { initTransport := newTestTransport(t, crypto.Ed25519, 2048) - respTransport := newTestTransport(t, crypto.Ed25519, 2048) + respTransport, _ := newTestTransport(t, crypto.Ed25519, 2048).WithSessionOptions(CheckPeerID(false)) init, resp := newConnPair(t) errChan := make(chan error) @@ -571,7 +572,7 @@ func TestEarlyfffDataAcceptedWithNoHandler(t *testing.T) { } initTransport, err := newTestTransport(t, crypto.Ed25519, 2048).WithSessionOptions(EarlyData(clientEDH, nil)) require.NoError(t, err) - respTransport := newTestTransport(t, crypto.Ed25519, 2048) + respTransport, _ := newTestTransport(t, crypto.Ed25519, 2048).WithSessionOptions(CheckPeerID(false)) initConn, respConn := newConnPair(t) @@ -581,7 +582,7 @@ func TestEarlyfffDataAcceptedWithNoHandler(t *testing.T) { errChan <- err }() - conn, err := initTransport.SecureOutbound(context.Background(), respConn, respTransport.localID) + conn, err := initTransport.SecureOutbound(context.Background(), respConn, respTransport.t.localID) require.NoError(t, err) defer conn.Close() From 1f7b15baedc1c05595b17d26c2d690ba708f9b44 Mon Sep 17 00:00:00 2001 From: Chinmay Kousik Date: Mon, 17 Oct 2022 16:06:49 +0530 Subject: [PATCH 03/10] add test --- p2p/security/noise/transport_test.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/p2p/security/noise/transport_test.go b/p2p/security/noise/transport_test.go index ad2464012f..b352533197 100644 --- a/p2p/security/noise/transport_test.go +++ b/p2p/security/noise/transport_test.go @@ -233,6 +233,24 @@ func TestPeerIDMismatchInboundFailsHandshake(t *testing.T) { <-done } +func TestPeerIDOutboundNoCheck(t *testing.T) { + initTransport, err := newTestTransport(t, crypto.Ed25519, 2048).WithSessionOptions(CheckPeerID(false)) + require.NoError(t, err, "could not initiate session transport") + respTransport := newTestTransport(t, crypto.Ed25519, 2048) + init, resp := newConnPair(t) + + errChan := make(chan error) + go func() { + _, err := initTransport.SecureOutbound(context.Background(), init, "") + errChan <- err + }() + + _, err = respTransport.SecureInbound(context.Background(), resp, "") + require.NoError(t, err) + initErr := <-errChan + require.NoError(t, initErr) +} + func makeLargePlaintext(size int) []byte { buf := make([]byte, size) rand.Read(buf) From 9a5a2d553aef3d6ca4e886251a4a12ecf67138c6 Mon Sep 17 00:00:00 2001 From: Chinmay Kousik Date: Tue, 18 Oct 2022 20:00:14 +0530 Subject: [PATCH 04/10] make explicit method --- p2p/security/noise/handshake.go | 4 ---- p2p/security/noise/session_transport.go | 21 ++++++++++----------- p2p/security/noise/transport.go | 7 +++++++ p2p/security/noise/transport_test.go | 16 +++++++--------- 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/p2p/security/noise/handshake.go b/p2p/security/noise/handshake.go index 7d3d6337d3..4dd481d67f 100644 --- a/p2p/security/noise/handshake.go +++ b/p2p/security/noise/handshake.go @@ -276,10 +276,6 @@ func (s *secureSession) handleRemoteHandshakePayload(payload []byte, remoteStati if s.checkPeerID && s.remoteID != id { return nil, fmt.Errorf("peer id mismatch: expected %s, but remote key matches %s", s.remoteID.Pretty(), id.Pretty()) } - // if (s.initiator && s.remoteID != id) || (!s.initiator && s.remoteID != "" && s.remoteID != id) { - // // use Pretty() as it produces the full b58-encoded string, rather than abbreviated forms. - // return nil, fmt.Errorf("peer id mismatch: expected %s, but remote key matches %s", s.remoteID.Pretty(), id.Pretty()) - // } // verify payload is signed by asserted remote libp2p key. sig := nhp.GetIdentitySig() diff --git a/p2p/security/noise/session_transport.go b/p2p/security/noise/session_transport.go index 05e288b9bf..4814933cbb 100644 --- a/p2p/security/noise/session_transport.go +++ b/p2p/security/noise/session_transport.go @@ -50,13 +50,6 @@ func EarlyData(initiator, responder EarlyDataHandler) SessionOption { } } -func CheckPeerID(enable bool) SessionOption { - return func(s *SessionTransport) error { - s.checkPeerID = enable - return nil - } -} - var _ sec.SecureTransport = &SessionTransport{} // SessionTransport can be used @@ -64,8 +57,7 @@ var _ sec.SecureTransport = &SessionTransport{} type SessionTransport struct { t *Transport // options - prologue []byte - checkPeerID bool + prologue []byte initiatorEarlyDataHandler, responderEarlyDataHandler EarlyDataHandler } @@ -73,7 +65,7 @@ type SessionTransport struct { // SecureInbound runs the Noise handshake as the responder. // If p is empty, connections from any peer are accepted. 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, i.initiatorEarlyDataHandler, i.responderEarlyDataHandler, false, i.checkPeerID) + c, err := newSecureSession(i.t, ctx, insecure, p, i.prologue, i.initiatorEarlyDataHandler, i.responderEarlyDataHandler, false, p != "") if err != nil { addr, maErr := manet.FromNetAddr(insecure.RemoteAddr()) if maErr == nil { @@ -85,5 +77,12 @@ func (i *SessionTransport) SecureInbound(ctx context.Context, insecure net.Conn, // SecureOutbound runs the Noise handshake as the initiator. 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, i.initiatorEarlyDataHandler, i.responderEarlyDataHandler, true, i.checkPeerID) + return newSecureSession(i.t, ctx, insecure, p, i.prologue, i.initiatorEarlyDataHandler, i.responderEarlyDataHandler, true, true) +} + +// SecureOutboundForAnyPeerID runs the Noise handshake as the initiator but does not check +// the remote's peer ID. This is the outbound equivalent of calling `SecureInbound` with an empty +// peer ID. +func (i *SessionTransport) SecureOutboundForAnyPeerID(ctx context.Context, insecure net.Conn) (sec.SecureConn, error) { + return newSecureSession(i.t, ctx, insecure, "", i.prologue, i.initiatorEarlyDataHandler, i.responderEarlyDataHandler, true, false) } diff --git a/p2p/security/noise/transport.go b/p2p/security/noise/transport.go index 3b318a1900..cd0731e947 100644 --- a/p2p/security/noise/transport.go +++ b/p2p/security/noise/transport.go @@ -78,6 +78,13 @@ func (t *Transport) SecureOutbound(ctx context.Context, insecure net.Conn, p pee return SessionWithConnState(c, initiatorEDH.MatchMuxers(true)), err } +// SecureOutboundForAnyPeerID runs the Noise handshake as the initiator but does not check +// the remote's peer ID. This is the outbound equivalent of calling `SecureInbound` with an empty +// peer ID. +func (t *Transport) SecureOutboundForAnyPeerID(ctx context.Context, insecure net.Conn) (sec.SecureConn, error) { + return newSecureSession(t, ctx, insecure, "", nil, nil, nil, true, false) +} + func (t *Transport) WithSessionOptions(opts ...SessionOption) (*SessionTransport, error) { st := &SessionTransport{t: t} for _, opt := range opts { diff --git a/p2p/security/noise/transport_test.go b/p2p/security/noise/transport_test.go index b352533197..87e1c49c65 100644 --- a/p2p/security/noise/transport_test.go +++ b/p2p/security/noise/transport_test.go @@ -88,8 +88,7 @@ func connect(t *testing.T, initTransport, respTransport *Transport) (*secureSess initConn, initErr = initTransport.SecureOutbound(context.Background(), init, respTransport.localID) }() - receiverTpt, _ := respTransport.WithSessionOptions(CheckPeerID(false)) - respConn, respErr := receiverTpt.SecureInbound(context.Background(), resp, "") + respConn, respErr := respTransport.SecureInbound(context.Background(), resp, "") <-done if initErr != nil { @@ -197,7 +196,7 @@ func TestPeerIDMatch(t *testing.T) { func TestPeerIDMismatchOutboundFailsHandshake(t *testing.T) { initTransport := newTestTransport(t, crypto.Ed25519, 2048) - respTransport, _ := newTestTransport(t, crypto.Ed25519, 2048).WithSessionOptions(CheckPeerID(false)) + respTransport := newTestTransport(t, crypto.Ed25519, 2048) init, resp := newConnPair(t) errChan := make(chan error) @@ -234,18 +233,17 @@ func TestPeerIDMismatchInboundFailsHandshake(t *testing.T) { } func TestPeerIDOutboundNoCheck(t *testing.T) { - initTransport, err := newTestTransport(t, crypto.Ed25519, 2048).WithSessionOptions(CheckPeerID(false)) - require.NoError(t, err, "could not initiate session transport") + initTransport := newTestTransport(t, crypto.Ed25519, 2048) respTransport := newTestTransport(t, crypto.Ed25519, 2048) init, resp := newConnPair(t) errChan := make(chan error) go func() { - _, err := initTransport.SecureOutbound(context.Background(), init, "") + _, err := initTransport.SecureOutboundForAnyPeerID(context.Background(), init) errChan <- err }() - _, err = respTransport.SecureInbound(context.Background(), resp, "") + _, err := respTransport.SecureInbound(context.Background(), resp, "") require.NoError(t, err) initErr := <-errChan require.NoError(t, initErr) @@ -590,7 +588,7 @@ func TestEarlyfffDataAcceptedWithNoHandler(t *testing.T) { } initTransport, err := newTestTransport(t, crypto.Ed25519, 2048).WithSessionOptions(EarlyData(clientEDH, nil)) require.NoError(t, err) - respTransport, _ := newTestTransport(t, crypto.Ed25519, 2048).WithSessionOptions(CheckPeerID(false)) + respTransport := newTestTransport(t, crypto.Ed25519, 2048) initConn, respConn := newConnPair(t) @@ -600,7 +598,7 @@ func TestEarlyfffDataAcceptedWithNoHandler(t *testing.T) { errChan <- err }() - conn, err := initTransport.SecureOutbound(context.Background(), respConn, respTransport.t.localID) + conn, err := initTransport.SecureOutbound(context.Background(), respConn, respTransport.localID) require.NoError(t, err) defer conn.Close() From 8e117a9200b6feca5fc8effae2f7cd685522fd07 Mon Sep 17 00:00:00 2001 From: Chinmay Kousik Date: Thu, 27 Oct 2022 20:22:40 +0530 Subject: [PATCH 05/10] fix comment --- p2p/security/noise/transport.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/p2p/security/noise/transport.go b/p2p/security/noise/transport.go index cd0731e947..d49ff3ae83 100644 --- a/p2p/security/noise/transport.go +++ b/p2p/security/noise/transport.go @@ -51,7 +51,7 @@ func New(privkey crypto.PrivKey, muxers []protocol.ID) (*Transport, error) { } // SecureInbound runs the Noise handshake as the responder. -// if p is empty accept any peer ID. +// 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) { responderEDH := newTransportEDH(t) checkPeerID := true From a2913e60f6246024715f2502072a05e359e08e0d Mon Sep 17 00:00:00 2001 From: Chinmay Kousik Date: Thu, 27 Oct 2022 21:02:34 +0530 Subject: [PATCH 06/10] fix --- p2p/security/noise/transport.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/p2p/security/noise/transport.go b/p2p/security/noise/transport.go index d49ff3ae83..a29021c0af 100644 --- a/p2p/security/noise/transport.go +++ b/p2p/security/noise/transport.go @@ -54,11 +54,7 @@ func New(privkey crypto.PrivKey, muxers []protocol.ID) (*Transport, error) { // 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) { responderEDH := newTransportEDH(t) - checkPeerID := true - if p == "" { - checkPeerID = false - } - c, err := newSecureSession(t, ctx, insecure, p, nil, nil, responderEDH, checkPeerID) + c, err := newSecureSession(t, ctx, insecure, p, nil, nil, responderEDH, false, p != "") if err != nil { addr, maErr := manet.FromNetAddr(insecure.RemoteAddr()) if maErr == nil { @@ -71,7 +67,7 @@ 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) { initiatorEDH := newTransportEDH(t) - c, err := newSecureSession(t, ctx, insecure, p, nil, initiatorEDH, nil, true) + c, err := newSecureSession(t, ctx, insecure, p, nil, initiatorEDH, nil, true, true) if err != nil { return c, err } From eeb8d4d27744c62654abc78d77ba43402053fea3 Mon Sep 17 00:00:00 2001 From: Chinmay Kousik Date: Thu, 27 Oct 2022 21:25:12 +0530 Subject: [PATCH 07/10] fix comment --- p2p/security/noise/session_transport.go | 3 ++- p2p/security/noise/transport.go | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/p2p/security/noise/session_transport.go b/p2p/security/noise/session_transport.go index 4814933cbb..4d5eef3f59 100644 --- a/p2p/security/noise/session_transport.go +++ b/p2p/security/noise/session_transport.go @@ -82,7 +82,8 @@ func (i *SessionTransport) SecureOutbound(ctx context.Context, insecure net.Conn // SecureOutboundForAnyPeerID runs the Noise handshake as the initiator but does not check // the remote's peer ID. This is the outbound equivalent of calling `SecureInbound` with an empty -// peer ID. +// peer ID. This is susceptible to MITM attacks since we do not verify the identity of the remote +// peer. func (i *SessionTransport) SecureOutboundForAnyPeerID(ctx context.Context, insecure net.Conn) (sec.SecureConn, error) { return newSecureSession(i.t, ctx, insecure, "", i.prologue, i.initiatorEarlyDataHandler, i.responderEarlyDataHandler, true, false) } diff --git a/p2p/security/noise/transport.go b/p2p/security/noise/transport.go index a29021c0af..087735d7df 100644 --- a/p2p/security/noise/transport.go +++ b/p2p/security/noise/transport.go @@ -76,7 +76,8 @@ func (t *Transport) SecureOutbound(ctx context.Context, insecure net.Conn, p pee // SecureOutboundForAnyPeerID runs the Noise handshake as the initiator but does not check // the remote's peer ID. This is the outbound equivalent of calling `SecureInbound` with an empty -// peer ID. +// peer ID. This is susceptible to MITM attacks since we do not verify the identity of the remote +// peer. func (t *Transport) SecureOutboundForAnyPeerID(ctx context.Context, insecure net.Conn) (sec.SecureConn, error) { return newSecureSession(t, ctx, insecure, "", nil, nil, nil, true, false) } From 09d0d63c8aca836c1baa166df103f3c7bb29dd45 Mon Sep 17 00:00:00 2001 From: Chinmay Kousik Date: Wed, 2 Nov 2022 20:33:28 +0530 Subject: [PATCH 08/10] use option --- p2p/security/noise/session_transport.go | 26 ++++++++++++++----------- p2p/security/noise/transport.go | 10 +--------- p2p/security/noise/transport_test.go | 7 +++++-- 3 files changed, 21 insertions(+), 22 deletions(-) diff --git a/p2p/security/noise/session_transport.go b/p2p/security/noise/session_transport.go index 4d5eef3f59..b80ca9ffd8 100644 --- a/p2p/security/noise/session_transport.go +++ b/p2p/security/noise/session_transport.go @@ -50,6 +50,17 @@ func EarlyData(initiator, responder EarlyDataHandler) SessionOption { } } +// DisablePeerIDCheck disables checking the remote peer ID for a noise connection. +// For outbound connections, this is the equivalent of calling `SecureInbound` with an empty +// peer ID. This is susceptible to MITM attacks since we do not verify the identity of the remote +// peer. +func DisablePeerIDCheck() SessionOption { + return func(s *SessionTransport) error { + s.disablePeerIdCheck = true + return nil + } +} + var _ sec.SecureTransport = &SessionTransport{} // SessionTransport can be used @@ -57,7 +68,8 @@ var _ sec.SecureTransport = &SessionTransport{} type SessionTransport struct { t *Transport // options - prologue []byte + prologue []byte + disablePeerIdCheck bool initiatorEarlyDataHandler, responderEarlyDataHandler EarlyDataHandler } @@ -65,7 +77,7 @@ type SessionTransport struct { // SecureInbound runs the Noise handshake as the responder. // If p is empty, connections from any peer are accepted. 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, i.initiatorEarlyDataHandler, i.responderEarlyDataHandler, false, p != "") + c, err := newSecureSession(i.t, ctx, insecure, p, i.prologue, i.initiatorEarlyDataHandler, i.responderEarlyDataHandler, false, p != "" || i.disablePeerIdCheck) if err != nil { addr, maErr := manet.FromNetAddr(insecure.RemoteAddr()) if maErr == nil { @@ -77,13 +89,5 @@ func (i *SessionTransport) SecureInbound(ctx context.Context, insecure net.Conn, // SecureOutbound runs the Noise handshake as the initiator. 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, i.initiatorEarlyDataHandler, i.responderEarlyDataHandler, true, true) -} - -// SecureOutboundForAnyPeerID runs the Noise handshake as the initiator but does not check -// the remote's peer ID. This is the outbound equivalent of calling `SecureInbound` with an empty -// peer ID. This is susceptible to MITM attacks since we do not verify the identity of the remote -// peer. -func (i *SessionTransport) SecureOutboundForAnyPeerID(ctx context.Context, insecure net.Conn) (sec.SecureConn, error) { - return newSecureSession(i.t, ctx, insecure, "", i.prologue, i.initiatorEarlyDataHandler, i.responderEarlyDataHandler, true, false) + return newSecureSession(i.t, ctx, insecure, p, i.prologue, i.initiatorEarlyDataHandler, i.responderEarlyDataHandler, true, !i.disablePeerIdCheck) } diff --git a/p2p/security/noise/transport.go b/p2p/security/noise/transport.go index 087735d7df..5d7b8c026c 100644 --- a/p2p/security/noise/transport.go +++ b/p2p/security/noise/transport.go @@ -74,16 +74,8 @@ func (t *Transport) SecureOutbound(ctx context.Context, insecure net.Conn, p pee return SessionWithConnState(c, initiatorEDH.MatchMuxers(true)), err } -// SecureOutboundForAnyPeerID runs the Noise handshake as the initiator but does not check -// the remote's peer ID. This is the outbound equivalent of calling `SecureInbound` with an empty -// peer ID. This is susceptible to MITM attacks since we do not verify the identity of the remote -// peer. -func (t *Transport) SecureOutboundForAnyPeerID(ctx context.Context, insecure net.Conn) (sec.SecureConn, error) { - return newSecureSession(t, ctx, insecure, "", nil, nil, nil, true, false) -} - func (t *Transport) WithSessionOptions(opts ...SessionOption) (*SessionTransport, error) { - st := &SessionTransport{t: t} + st := &SessionTransport{t: t, disablePeerIdCheck: false} for _, opt := range opts { if err := opt(st); err != nil { return nil, err diff --git a/p2p/security/noise/transport_test.go b/p2p/security/noise/transport_test.go index 87e1c49c65..453f642fd4 100644 --- a/p2p/security/noise/transport_test.go +++ b/p2p/security/noise/transport_test.go @@ -237,13 +237,16 @@ func TestPeerIDOutboundNoCheck(t *testing.T) { respTransport := newTestTransport(t, crypto.Ed25519, 2048) init, resp := newConnPair(t) + initSessionTransport, err := initTransport.WithSessionOptions(DisablePeerIDCheck()) + require.NoError(t, err) + errChan := make(chan error) go func() { - _, err := initTransport.SecureOutboundForAnyPeerID(context.Background(), init) + _, err := initSessionTransport.SecureOutbound(context.Background(), init, "test") errChan <- err }() - _, err := respTransport.SecureInbound(context.Background(), resp, "") + _, err = respTransport.SecureInbound(context.Background(), resp, "") require.NoError(t, err) initErr := <-errChan require.NoError(t, initErr) From bbc2cb43a48afd0f2762959fd2fbe25960ef3c2b Mon Sep 17 00:00:00 2001 From: Chinmay Kousik Date: Wed, 9 Nov 2022 17:03:12 +0530 Subject: [PATCH 09/10] address review --- p2p/security/noise/session_transport.go | 8 ++++---- p2p/security/noise/transport.go | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/p2p/security/noise/session_transport.go b/p2p/security/noise/session_transport.go index b80ca9ffd8..86b304b503 100644 --- a/p2p/security/noise/session_transport.go +++ b/p2p/security/noise/session_transport.go @@ -56,7 +56,7 @@ func EarlyData(initiator, responder EarlyDataHandler) SessionOption { // peer. func DisablePeerIDCheck() SessionOption { return func(s *SessionTransport) error { - s.disablePeerIdCheck = true + s.disablePeerIDCheck = true return nil } } @@ -69,7 +69,7 @@ type SessionTransport struct { t *Transport // options prologue []byte - disablePeerIdCheck bool + disablePeerIDCheck bool initiatorEarlyDataHandler, responderEarlyDataHandler EarlyDataHandler } @@ -77,7 +77,7 @@ type SessionTransport struct { // SecureInbound runs the Noise handshake as the responder. // If p is empty, connections from any peer are accepted. 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, i.initiatorEarlyDataHandler, i.responderEarlyDataHandler, false, p != "" || i.disablePeerIdCheck) + c, err := newSecureSession(i.t, ctx, insecure, p, i.prologue, i.initiatorEarlyDataHandler, i.responderEarlyDataHandler, false, p != "" || i.disablePeerIDCheck) if err != nil { addr, maErr := manet.FromNetAddr(insecure.RemoteAddr()) if maErr == nil { @@ -89,5 +89,5 @@ func (i *SessionTransport) SecureInbound(ctx context.Context, insecure net.Conn, // SecureOutbound runs the Noise handshake as the initiator. 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, i.initiatorEarlyDataHandler, i.responderEarlyDataHandler, true, !i.disablePeerIdCheck) + return newSecureSession(i.t, ctx, insecure, p, i.prologue, i.initiatorEarlyDataHandler, i.responderEarlyDataHandler, true, !i.disablePeerIDCheck) } diff --git a/p2p/security/noise/transport.go b/p2p/security/noise/transport.go index 5d7b8c026c..6878929505 100644 --- a/p2p/security/noise/transport.go +++ b/p2p/security/noise/transport.go @@ -75,7 +75,7 @@ func (t *Transport) SecureOutbound(ctx context.Context, insecure net.Conn, p pee } func (t *Transport) WithSessionOptions(opts ...SessionOption) (*SessionTransport, error) { - st := &SessionTransport{t: t, disablePeerIdCheck: false} + st := &SessionTransport{t: t} for _, opt := range opts { if err := opt(st); err != nil { return nil, err From f1a924db00d39d92083a19254ab5c72f7b4adb98 Mon Sep 17 00:00:00 2001 From: Chinmay Kousik Date: Wed, 9 Nov 2022 17:14:13 +0530 Subject: [PATCH 10/10] fix peer ID conditional --- p2p/security/noise/session_transport.go | 3 ++- p2p/security/noise/transport_test.go | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/p2p/security/noise/session_transport.go b/p2p/security/noise/session_transport.go index 86b304b503..c42271bd91 100644 --- a/p2p/security/noise/session_transport.go +++ b/p2p/security/noise/session_transport.go @@ -77,7 +77,8 @@ type SessionTransport struct { // SecureInbound runs the Noise handshake as the responder. // If p is empty, connections from any peer are accepted. 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, i.initiatorEarlyDataHandler, i.responderEarlyDataHandler, false, p != "" || i.disablePeerIDCheck) + checkPeerID := !i.disablePeerIDCheck && p != "" + c, err := newSecureSession(i.t, ctx, insecure, p, i.prologue, i.initiatorEarlyDataHandler, i.responderEarlyDataHandler, false, checkPeerID) if err != nil { addr, maErr := manet.FromNetAddr(insecure.RemoteAddr()) if maErr == nil { diff --git a/p2p/security/noise/transport_test.go b/p2p/security/noise/transport_test.go index 453f642fd4..15b46bc969 100644 --- a/p2p/security/noise/transport_test.go +++ b/p2p/security/noise/transport_test.go @@ -232,6 +232,24 @@ func TestPeerIDMismatchInboundFailsHandshake(t *testing.T) { <-done } +func TestPeerIDInboundCheckDisabled(t *testing.T) { + initTransport := newTestTransport(t, crypto.Ed25519, 2048) + respTransport := newTestTransport(t, crypto.Ed25519, 2048) + init, resp := newConnPair(t) + + initSessionTransport, err := initTransport.WithSessionOptions(DisablePeerIDCheck()) + require.NoError(t, err) + errChan := make(chan error) + go func() { + _, err := initSessionTransport.SecureInbound(context.Background(), init, "test") + errChan <- err + }() + _, err = respTransport.SecureOutbound(context.Background(), resp, initTransport.localID) + require.NoError(t, err) + initErr := <-errChan + require.NoError(t, initErr) +} + func TestPeerIDOutboundNoCheck(t *testing.T) { initTransport := newTestTransport(t, crypto.Ed25519, 2048) respTransport := newTestTransport(t, crypto.Ed25519, 2048)