keep-network / keep-core

The smart contracts and reference client behind the Keep network
https://keep.network
MIT License
124 stars 75 forks source link

libp2p authentication #9

Closed Shadowfiend closed 5 years ago

Shadowfiend commented 7 years ago

This will be done in a bootstrap node.

Proving stake here can start with sending the staking address to the bootstrap node, signed by the staking key, perhaps… This should allow the bootstrap node to verify that the signature on the staking address is correct for that address, and then check the network to ensure the address in question is staked.

We need to figure out whether the above approach is correct and implementable, or if we need a different one instead.

Let's use this issue to discuss implementation, and then spin up separate issues for proving stake and blocking unproven clients when we're ready to start working on them.

pdyraga commented 6 years ago

sending the staking address to the bootstrap node, signed by the staking key, perhaps…

Shouldn't we send some random challenge instead of expecting the new node to sign always the same thing (address is constant, right?). If I were an adversary able to sniff the network, I'd use the same signature to prove my (false) identity.

pschlump commented 6 years ago

I think that we should generate a random message (cryptographically strong)

After we know in the Go code that we have a real address we can ask the Geth server if the person is staked.

Am I understanding this - or - did I goof it up?

On Mon, Aug 20, 2018 at 8:19 AM pdyraga notifications@github.com wrote:

sending the staking address to the bootstrap node, signed by the staking key, perhaps…

Shouldn't we send some random challenge instead of expecting the new node to sign always the same thing (address is constant, right?). If I were an adversary able to sniff the network, I'd use the same signature to prove my (false) identity.

— You are receiving this because you were assigned. Reply to this email directly, view it on GitHub https://github.com/keep-network/keep-core/issues/9#issuecomment-414332610, or mute the thread https://github.com/notifications/unsubscribe-auth/AAhMQa2Cbn-1xPqeU8vFr3G6MVGbe2qgks5uSsV3gaJpZM4Qg8FG .

-- Philip Schlump

pdyraga commented 6 years ago

If the signed data is not generated by the verifier and signature is not checked against the verifier-generated data, then the adversary may replicate some historical message.

Shadowfiend commented 6 years ago

Yah, replay attack is definitely a problem here, excellent point. Challenge seems like a reasonable solution, but the downside is it requires the bootstrap nodes to challenge all nodes that join, which feels like a potential bottleneck. We could have the connector sign their own multiaddr. The recipient should be able to compare the multiaddr in the message with the multiaddr that is connecting to verify that it's correct. Only question there is whether multiaddrs can be spoofed by a sender.

mhluongo commented 6 years ago

Is there a sequence number, timestamp, or other incrementing piece of data that can be used to prevent replays?

pdyraga commented 6 years ago

but the downside is it requires the bootstrap nodes to challenge all nodes that join, which feels like a potential bottleneck

I think it's even worse - the joining nodes should challenge the bootstrap one as well.

Is there a sequence number, timestamp, or other incrementing piece of data that can be used to prevent replays?

I've been thinking about using some data from the current block, but it's possible we'll have multiple threshold relay requests within a single block and then, an adversary can prepare attack under concurrent composition.

We could have the connector sign their own multiaddr.

But the connector we'll use the same multiaddr in several groups it participates in, right? So it's still vulnerable for a replay attack.

Shadowfiend commented 6 years ago

This isn't about authenticating during group join; that's handled by the group formation protocol we're working on for the yellowpaper (and does have an on-chain component). This is about proving a given node is allowed to access the Keep p2p network at all; it should ideally be one time per connection.

mhluongo commented 6 years ago

I've been thinking about using some data from the current block, but it's possible we'll have multiple threshold relay requests within a single block and then, an adversary can prepare attack under concurrent composition.

I was thinking about each message including a commitment to the last sent or received message, rather than relying on on-chain consensus. As long as we validate that commitment we'll have a replay mitigation.

If we need an initial signature, maybe the first message if always a response to a node's challenge?

pschlump commented 6 years ago

by verifying a randomly generated message it will not be possible to have a re-play attack.

On Tue, Aug 21, 2018 at 8:33 AM Matt Luongo notifications@github.com wrote:

I've been thinking about using some data from the current block, but it's possible we'll have multiple threshold relay requests within a single block and then, an adversary can prepare attack under concurrent composition.

I was thinking about each message including a commitment to the last sent or received message, rather than relying on on-chain consensus.

If we need an initial signature, maybe the first message if always a response to a bootstrap node's challenge?

— You are receiving this because you were assigned. Reply to this email directly, view it on GitHub https://github.com/keep-network/keep-core/issues/9#issuecomment-414695408, or mute the thread https://github.com/notifications/unsubscribe-auth/AAhMQWS-AxiKUKZ0d9M0hoFrv1bXgU05ks5uTBosgaJpZM4Qg8FG .

-- Philip Schlump

pschlump commented 6 years ago

My understanding of how this is all going to work

A new node (NN) makes a request to a bootstrap node (BB) to participate.

NN sends to BB - Hi - I want to talk to you :

  1. my signed random message
  2. my signature
  3. my address

BB verifies the signature to check that the address is correct

BB call HasMinimumStake ( address ) to verify that they are staked and can participate

If BB finds that the signature is valid and that they are staked then BB sends back to NN

1. Ok - I like you.
2. a signed random message using my key
3. my signature of the message
4. my address

NN then validates the signature from BB.

NN checks that they got back the expected address.

They are now connected and validated.

pschlump commented 6 years ago

To prevent replaying the message BB saves the random message - and disallows the use of that random message in the future.

Shadowfiend commented 6 years ago

Couple of thoughts on this approach:

pschlump commented 6 years ago

My understanding of how this is all going to work (v2)

(I have made some adjustments to eliminate the possibility of a replay attack)

A new node (NN) makes a request to the bootstrap (BB). It gets back a random token. BB saves the random token for 1 minute or until the token has been used once. The one minute is the time window when the token is valid.

NN sends to BB - Hi - I want to talk to you :

0. The keccak256 hash of the random token and the message
1. my signed random message
2. my signature
3. my address
3. my message

BB deletes it copy of the random token that matches what was sent. The random token can never be used again.

BB verifies the signature to check that the address is correct

BB call HasMinimumStake ( address ) to verify that they are staked and can participate

If BB finds that the signature is valid and that they are staked then BB sends back to NN

1. Ok - I like you.
2. a signed random message using my key
3. my signature of the message
4. my address

NN then validates the signature from BB.

NN checks that they got back the expected address.

They are now connected and validated.

Shadowfiend commented 6 years ago

This is closer. Again, need a better understanding of how secure multiaddrs are (that is, given a message M signed with signature S from a multiaddr A that includes a peer ID P, can I verify that signature S was indeed signed by the key associated with peer ID P, and therefore that it was in fact multiaddr A that generated that signature?). If they're secure, I think the rest of this becomes a bit simpler.

rargulati commented 6 years ago

@Shadowfiend It’s secure in the sense that if you tell me you’re at /ip4/127.0.0.1/udp/9090/quic/peerID, and I can’t dial + challenge you back at that location, that’s a no go.

A peerID has a 1:1 correspondence to a public key THOUGH we should explore all of the different encodings offered here.

To your example, given a message M signed with signature S from a multiaddr A that includes a peer ID P, I verify that signature S was signed by the key associated with peer ID P by calling the method that goes from peer.ID -> publicKey and then pubKey.Verify(message, sig). Lastly, I ensure that multiAddr A is indeed a valid location by either confirming that I did indeed receive a message M from that multiaddr A OR pinging multiaddr A. Is this what you were looking for?

The signing of the multiaddrs + nonce are great ideas, and adds layers where an adversary may try to mitm a challenge - response cycle.

pdyraga commented 5 years ago

This is covered in #302, #304, #311 . Closing to avoid duplicates.