-
Notifications
You must be signed in to change notification settings - Fork 166
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[network] Check the OriginID of a libp2p message corresponds to its authenticated source #1163
Changes from 2 commits
4453009
f15333d
63261ad
38f8a5e
3d91f99
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,6 +28,11 @@ import ( | |
|
||
const testChannel = "test-channel" | ||
|
||
// libp2p emits a call to `Protect` with a topic-specific tag upon establishing each peering connection in a GossipSUb mesh, see: | ||
// https://github.com/libp2p/go-libp2p-pubsub/blob/master/tag_tracer.go | ||
// One way to make sure such a mesh has formed, asynchronously, in unit tests, is to wait for libp2p.GossipSubD such calls, | ||
// and that's what we do with tagsObserver. | ||
// | ||
type tagsObserver struct { | ||
tags chan string | ||
log zerolog.Logger | ||
|
@@ -268,6 +273,54 @@ func (m *MiddlewareTestSuite) TestEcho() { | |
} | ||
} | ||
|
||
// TestSpoofedPubSubHello evaluates checking the originID of the message w.r.t. its libp2p network ID on PubSub | ||
// we check a pubsub message with a spoofed OriginID does not get delivered | ||
// This would be doubled with cryptographic verification of the libp2p network ID in production (see message signing options in pubSub initialization) | ||
func (m *MiddlewareTestSuite) TestSpoofedPubSubHello() { | ||
first := 0 | ||
last := m.size - 1 | ||
lastNode := m.ids[last].NodeID | ||
|
||
// initially subscribe the nodes to the channel | ||
for _, mw := range m.mws { | ||
err := mw.Subscribe(testChannel) | ||
require.NoError(m.Suite.T(), err) | ||
} | ||
|
||
// set up waiting for m.size pubsub tags indicating a mesh has formed | ||
for i := 0; i < m.size; i++ { | ||
select { | ||
case <-m.obs: | ||
case <-time.After(2 * time.Second): | ||
assert.FailNow(m.T(), "could not receive pubsub tag indicating mesh formed") | ||
} | ||
} | ||
Comment on lines
+291
to
+297
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you explain when a pubsub tag is emmitted? Thanks There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Andy why getting There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Libp2p emits You would usually wait for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd like to understand this a bit more. From what I understand, for each connection both peers would get a So if there are 5 peers in total, then we should expect to see 8 tags before we can be confident that a mesh has formed, correct? (5 peers, 5 - 1 = 4 edges required for a connected graph, 4 * 2 = 8 tags). Here, you are only checking for 5 tags. Maybe I'm not understanding how this works? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, are we sure that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So here's where the calls to This is where they originate at the libp2p level, in reaction to a join. I don't think you're incorrect in your analysis. However:
I'm happy to update to a value you'd find more suitable, but for count = 2 (in the test we're commenting on), I note we have, practically and theoretically, 2 graft messages, every time.
Yep, the Protect calls otherwise made by our engines are all tagless. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for the explanation :) |
||
|
||
spoofedID := unittest.IdentifierFixture() | ||
|
||
message1 := createMessage(spoofedID, lastNode, "hello1") | ||
|
||
err := m.mws[first].Publish(message1, testChannel) | ||
assert.NoError(m.T(), err) | ||
|
||
// assert that the spoofed message is not received by the target node | ||
assert.Never(m.T(), func() bool { | ||
return !m.ov[last].AssertNumberOfCalls(m.T(), "Receive", 0) | ||
}, 2*time.Second, 100*time.Millisecond) | ||
|
||
// invalid message sent by firstNode claims to be from lastNode | ||
message2 := createMessage(lastNode, lastNode, "hello1") | ||
|
||
err = m.mws[first].Publish(message2, testChannel) | ||
assert.NoError(m.T(), err) | ||
|
||
// assert that the invalid message is not received by the target node | ||
assert.Never(m.T(), func() bool { | ||
return !m.ov[last].AssertNumberOfCalls(m.T(), "Receive", 0) | ||
}, 2*time.Second, 100*time.Millisecond) | ||
|
||
} | ||
|
||
// TestMaxMessageSize_SendDirect evaluates that invoking SendDirect method of the middleware on a message | ||
// size beyond the permissible unicast message size returns an error. | ||
func (m *MiddlewareTestSuite) TestMaxMessageSize_SendDirect() { | ||
|
@@ -424,7 +477,7 @@ func (m *MiddlewareTestSuite) TestUnsubscribe() { | |
// assert that the new message is not received by the target node | ||
assert.Never(m.T(), func() bool { | ||
return !m.ov[last].AssertNumberOfCalls(m.T(), "Receive", 1) | ||
}, 2*time.Second, time.Millisecond) | ||
}, 2*time.Second, 100*time.Millisecond) | ||
} | ||
|
||
func createMessage(originID flow.Identifier, targetID flow.Identifier, msg ...string) *message.Message { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I found still a bit confusing.
It sounds like the message has been authenticated, but actually we are going to check the authentication, right?
If so, would
checkOriginAndProcess
be better?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It has, the message has a clear network origin, which is the signer of its envelope, which signature has been checked.
No, we are going to check the protocol-level authorship claim against the prior network-level authentication.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I see. would be great to add these knowledges into comments