3 ms·
(I'm the author of magic-wormhole) Nice! I'm glad to see libp2p getting more use. I would love to have it as a connection protocol in magic-wormhole someday. Y
by lotharrr 6y ago
(I'm the author of magic-wormhole)
Nice! I'm glad to see libp2p getting more use. I would love to have it as a connection protocol in magic-wormhole someday. Your implementation looks really slick.
I'll echo mintplant's question.. I'm interested in more details of the protocol. From the README (which is really good, thank you), I gather that it creates a libp2p keypair, uses the first 44 bits of the pubkey as the transfer code, use the first 8 bits plus a rounded timestamp as a DHT channel ID, then.. publishes its libp2p contact information to a DHT slot under the channel ID and waits for the peer to connect? I guess I need to learn more about libp2p and the difference between publishing information to a key, and accepting connections. Like mintplant, my big question is what gets used as the PAKE secret.
The PAKE step in magic-wormhole is there to make sure there's some secret value that is known only to the two correspondents, not anything in the middle, and use that to negotiate a full session key. It's the only piece of knowledge that distinguishes your intended recipient from an attacker.
If you're using the 44 bit transfer code as the PAKE "secret", and that's trivially derivable from the pubkey, and the pubkey is observable to anyone watching the network (or the DHT entries), then it's not actually a secret. An attacker could monitor the DHT for new keys in the "/pcp/" namespace, read their contents, connect to the indicated sender, note the public key they used for the connection, rebuild the transfer code that you created, run the protocol in the same way your intended recipient would, and steal the file. The sender would see the program complete earlier than they expected (before they even told anyone the transfer code), and the recipient would see some sort of error.
A particularly clever attacker would read the sender's data from the DHT, create a keypair with the same first 44 bits (requires 2^44 keypairs, but that's not comfortably impossible, and all of the work could be done ahead of time), flood the DHT with their own details (so the recipient connects to the attacker's host instead of yours), and then man-in-the-middle the connection, allowing them to both steal your file and substitute an alternate one to your recipient, with minimal evidence left behind.
But there's an easy fix: have it create four random words, use the first one or two as the channel ID (perhaps combined with the quantized timestamp), and the remainder as the secret PAKE input, which would completely solve that problem. The channel ID could be derived from a randomly-generated libp2p pubkey, or it could just be random. The important thing is that the "password" input to the PAKE step is uniformly random and unrelated to anything outside of the transfer code, and that it never gets revealed to anyone but the recipient (so it can't be used for any network purposes).
Cool stuff.. thank you for building it!
- sodality2 6y agoI spent many hours reading the magic-wormhole docs to study for my p2p IB project, thought I'd say thanks. :-)
- dennis-tra 6y agoThanks for your feedback and the kind words as I have great respect for magic-wormhole as well! I watched your talk at PyCon 2016 several times as I was building pcp. > [...] it creates a libp2p keypair, uses the first 44 bits of the pubkey as the transfer code, use the first 8 bits plus a rounded timestamp as a DHT channel ID, then.. publishes its libp2p contact information to a DHT slot under the channel ID and waits for the peer to connect? That summarizes it really well. There are actually three types of records you can store in the DHT of IPFS: Provider, IPNS and Peer records [0]. What pcp stores in the DHT are: - cid("/pcp/{unixts}/chanID") -> peerID (provider record) - peerID -> multiaddresses (peer record) where cid stands for the content ID [1] of the given string. So the receiving peer first searches for cid("/pcp/{unixts}/chanID"), finds the peerID and then searches for its associated addresses. These addresses can contain the public address of your home router (if NAT traversal is possible) or relay addresses. All of this complexity is pretty much hidden away by libp2p, which is amazing! Regarding your points about using PAKE/Public Key etc: I thought of making it even harder by tying the random code to the identity - but this is indeed flawed. I'll change the logic to your suggestion! Honestly, thanks for your write-up and the valuable input! [0] https://docs.ipfs.io/concepts/dht/ https://docs.ipfs.io/concepts/dht/ [1] https://docs.ipfs.io/concepts/content-addressing/ https://docs.ipfs.io/concepts/content-addressing/