Skip to content
This repository has been archived by the owner on Mar 28, 2023. It is now read-only.

IPFS secio handshake patch likely not forward compatible #1626

Closed
cpacia opened this issue Jun 6, 2019 · 3 comments · Fixed by #1637
Closed

IPFS secio handshake patch likely not forward compatible #1626

cpacia opened this issue Jun 6, 2019 · 3 comments · Fixed by #1637
Assignees

Comments

@cpacia
Copy link
Member

cpacia commented Jun 6, 2019

In the code segment below when establishing a new outgoing connection s.remotePeer is set to the peerID of the node we're trying to connect to. Ultimately this will either be an old style (hashed) peerID or a new style inline key depending on the actual ID that is used when we try to make a connection.

So far so good. Also peer.IDFromPublicKey is currently programmed to return an old style (hashed) peerID since we set AdvancedEnableInlining to false.

OK . Now take a look at the secio handshake... this is their raw code without any modifications:

// get peer id
    actualRemotePeer, err := peer.IDFromPublicKey(s.remote.permanentPubKey)
    if err != nil {
        return err
    }
    switch s.remotePeer {
    case actualRemotePeer:
        // All good.
    case "":
        // No peer set. We're accepting a remote connection.
        s.remotePeer = actualRemotePeer
    default:
        // Peer mismatch. Bail.
        s.insecure.Close()
        log.Debugf("expected peer %s, got peer %s", s.remotePeer, actualRemotePeer)
        return ErrWrongPeer
    }

If we ever enable inline keys, old nodes which have not upgraded, when trying to connect to a new style peerID, will pass in a new style ID into s.remotePeer but actualRemotePeer will still be an old style key. Hence s.remotePeer != actualRemotePeer and the handshake will fail.

If an upgraded node tries to connect to a non-upgraded peerID then peer.IDFromPublicKey will return a new style peerID while s.remotePeer is an old style key. Hence it will fail to connect.

So to make our current release forward compatible with a future release using inline keys I've done:

// get peer id
    actualRemotePeer, err := peer.IDFromPublicKey(s.remote.permanentPubKey)
    if err != nil {
        return err
    }
    switch s.remotePeer {
    case actualRemotePeer:
        // All good.
    case "":
        // No peer set. We're accepting a remote connection.
        s.remotePeer = actualRemotePeer
    default:
        pubkeyBytes, err := s.remote.permanentPubKey.Bytes()
        if err != nil {
            return err
        }
        oldMultihash, err := mh.Sum(pubkeyBytes, mh.SHA2_256, 32)
        if err != nil {
            return err
        }
        oldStylePeer, err := peer.IDB58Decode(oldMultihash.B58String())
        if err != nil {
            return err
        }
        if s.remotePeer != oldStylePeer {
            // Peer mismatch. Bail.
            s.insecure.Close()
            log.Debugf("expected peer %s, got peer %s", s.remotePeer, actualRemotePeer)
            return ErrWrongPeer
        }
    }

But looking at it, s.remotePeer would be an inline peerID and oldStylePeer would be an old style key, so I think this is screwed up and would prevent old nodes from connecting to new nodes. I think our current release should be a newStylePeer and compare it to s.remotePeer rather than an oldStylePeer.

And then a subsequent release with inline keys should use the code snippet above. (edited)

@cpacia cpacia self-assigned this Jun 6, 2019
@cpacia
Copy link
Member Author

cpacia commented Jun 6, 2019

This will likely mean we need to fix this for next release and then push out the timeline for updating to the inline keys until at least enough people upgrade to this next release.

@placer14
Copy link
Member

Thinking through this problem with annotations around the code:

    primaryTest, err := peer.IDFromPublicKey(s.remote.permanentPubKey)
    // *snip*
    switch s.remotePeer {
    case primaryTest:
        // (Primary Case) All good.
    case "":
        // Ignore
    default:
        // (Backup Case)
        // preparation and checking *snipped*
        if s.remotePeer != oldStylePeer {
          // Failure Case *snip*
        }
    }

Scenarios:

  1. OldNode (hashed) connecting to OldNode (hashed, inline off):
    remote (hashed) == primaryTest (hashed)... success in Primary Case
  2. OldNode (hashed) connecting to NewNode (inline, inline on):
    remote (hashed) != primaryTest (inline)... remote (hashed) == oldStyle (hashed) success in Backup
  3. NewNode (inline) connecting to OldNode (hashed, inline off):
    remote (inline) != primaryTest (hashed)... remote (inline) != oldStyle (hashed) ...always fails
  4. NewNode (inline) connecting to NewNode (inline, inline on)
    remote (inline) == primaryTest (inline)... success in Primary Case

I see the problem you're describing... it seems the backup test is always stuck producing the hashed peerID when we seem to want the backupTest to use the other algo opposite from the primaryTest.

I think the way forward for us will be a function ipfs.AlternativeIDFromPublicKey(crypto.PubKey) (peer.ID, error) which looks at the state of AdvancedEnableInlining and applies the opposite algorithm than what IDFromPublicKey applies. And then we can use that function inside of default until we've completed our inline key migration completely. Does that seem reasonable, @cpacia?

@cpacia
Copy link
Member Author

cpacia commented Jun 14, 2019

That seems like a good approach. Better than when I was suggesting.

placer14 added a commit that referenced this issue Jun 21, 2019
OpenBazaar currently uses hashed ID but intends to later switch to the
default inline IDs. During this transition, this commit ensures that nodes
of one type should always interop with the other.
placer14 added a commit that referenced this issue Jun 21, 2019
OpenBazaar currently uses hashed ID but intends to later switch to the
default inline IDs. During this transition, this commit ensures that nodes
of one type should always interop with the other.
@placer14 placer14 assigned placer14 and unassigned cpacia Jul 10, 2019
anchaj added a commit to phoreproject/pm-go that referenced this issue Aug 1, 2019
* commit 'a68e0e617eaa1a3ffa134bc08250b27341e847b4':
  Bump version to 0.13.4
  [OpenBazaar#1626] Fix secio handshake to properly fallback to alt ID generation
  [OpenBazaar#1634] Apply patch from go-libp2p-secio v0.0.3 release
  [OpenBazaar#1593] Add Start subcommand option to force key purge from IPNS cache
  Use separate namespace for IPNS persistent cache
  Best effort delete ipns record if unmarshaling fails
anchaj added a commit to phoreproject/pm-go that referenced this issue Aug 8, 2019
* development: (77 commits)
  Fix imports.
  Remove unused gx code.
  Delete unused code.
  Purge unused packages.
  Add missing gx packages.
  Handle error when calculating SHA peer id
  [OpenBazaar#1645] Prevent notifications produced on MODERATOR_ADD and REMOVE
  Bump version to 0.13.4
  [OpenBazaar#1626] Fix secio handshake to properly fallback to alt ID generation
  [OpenBazaar#1634] Apply patch from go-libp2p-secio v0.0.3 release
  [OpenBazaar#1593] Add Start subcommand option to force key purge from IPNS cache
  Use separate namespace for IPNS persistent cache
  Best effort delete ipns record if unmarshaling fails
  Don't cache records for our own peerID
  Add validator in mobile package
  Add record validator to APIRouter
  [OpenBazaar#1557] Make CachingRouter implement routing.PubKeyFetcher
  [OpenBazaar#1557] CachingRouter network lookup fixes
  Cleanup function naming, lint failures
  Update mobile/node to use extracted config methods
  ...
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants