rename SymmetricWormhole to just "Wormhole"

Update docs too. Now both blocking/ and twisted/ use "Wormhole".
This commit is contained in:
Brian Warner 2015-07-24 17:47:46 -07:00
parent d8ca850d1a
commit efd6d27cc6
4 changed files with 67 additions and 47 deletions

View File

@ -113,14 +113,11 @@ All four commands accept:
## Library
The `wormhole` module makes it possible for other applications to use these
code-protected channels. This includes blocking/synchronous support (for an
asymmetric pair of "initiator" and "receiver" endpoints), and async/Twisted
support (for a symmetric scheme). The main module is named
code-protected channels. This includes blocking/synchronous support and
async/Twisted support, both for a symmetric scheme. The main module is named
`wormhole.blocking.transcribe`, to reflect that it is for
synchronous/blocking code, and uses a PAKE mode whereby one user transcribes
their code to the other. (internal names may change in the future). The
synchronous support uses distinctive sides: one `Initiator`, and one
`Receiver`.
their code to the other. (internal names may change in the future).
The file-transfer tools use a second module named
`wormhole.blocking.transit`, which provides an encrypted record-pipe. It

View File

@ -12,26 +12,48 @@ server" that relays information from one machine to the other.
## Modes
This library will eventually offer multiple modes.
This library will eventually offer multiple modes. For now, only "transcribe
mode" is available.
The first mode provided is "transcribe" mode. In this mode, one machine goes
first, and is called the "initiator". The initiator contacts the rendezvous
server and allocates a "channel ID", which is a small integer. The initiator
then displays the "invitation code", which is the channel-ID plus a few
secret words. The user copies the invitation code to the second machine,
called the "receiver". The receiver connects to the rendezvous server, and
uses the invitation code to contact the initiator. They agree upon an
encryption key, and exchange a small encrypted+authenticated data message.
Transcribe mode has two variants. In the "machine-generated" variant, the
"initiator" machine creates the invitation code, displays it to the first
user, they convey it (somehow) to the second user, who transcribes it into
the second ("receiver") machine. In the "human-generated" variant, the two
humans come up with the code (possibly without computers), then later
transcribe it into both machines.
When the initator machine generates the invitation code, the initiator
contacts the rendezvous server and allocates a "channel ID", which is a small
integer. The initiator then displays the invitation code, which is the
channel-ID plus a few secret words. The user copies the code to the second
machine. The receiver machine connects to the rendezvous server, and uses the
invitation code to contact the initiator. They agree upon an encryption key,
and exchange a small encrypted+authenticated data message.
When the humans create an invitation code out-of-band, they are responsible
for choosing an unused channel-ID (simply picking a random 3-or-more digit
number is probably enough), and some random words. The invitation code uses
the same format in either variant: channel-ID, a hyphen, and an arbitrary
string.
The two machines participating in the wormhole setup are not distinguished:
it doesn't matter which one goes first, and both use the same Wormhole class.
In the first variant, one side calls `get_code()` while the other calls
`set_code()`. In the second variant, both sides call `set_code()`. Note that
this is not true for the "Transit" protocol used for bulk data-transfer: the
Transit class currently distinguishes "Sender" from "Receiver", so the
programs on each side must have some way to decide (ahead of time) which is
which.
## Examples
The synchronous+blocking flow looks like this:
```python
from wormhole.transcribe import Initiator
from wormhole.transcribe import Wormhole
from wormhole.public_relay import RENDEZVOUS_RELAY
mydata = b"initiator's data"
i = Initiator("appid", RENDEZVOUS_RELAY)
i = Wormhole("appid", RENDEZVOUS_RELAY)
code = i.get_code()
print("Invitation Code: %s" % code)
theirdata = i.get_data(mydata)
@ -40,11 +62,11 @@ print("Their data: %s" % theirdata.decode("ascii"))
```python
import sys
from wormhole.transcribe import Receiver
from wormhole.transcribe import Wormhole
from wormhole.public_relay import RENDEZVOUS_RELAY
mydata = b"receiver's data"
code = sys.argv[1]
r = Receiver("appid", RENDEZVOUS_RELAY)
r = Wormhole("appid", RENDEZVOUS_RELAY)
r.set_code(code)
theirdata = r.get_data(mydata)
print("Their data: %s" % theirdata.decode("ascii"))
@ -57,9 +79,9 @@ The Twisted-friendly flow looks like this:
```python
from twisted.internet import reactor
from wormhole.public_relay import RENDEZVOUS_RELAY
from wormhole.twisted.transcribe import SymmetricWormhole
from wormhole.twisted.transcribe import Wormhole
outbound_message = b"outbound data"
w1 = SymmetricWormhole("appid", RENDEZVOUS_RELAY)
w1 = Wormhole("appid", RENDEZVOUS_RELAY)
d = w1.get_code()
def _got_code(code):
print "Invitation Code:", code
@ -75,9 +97,10 @@ reactor.run()
On the other side, you call `set_code()` instead of waiting for `get_code()`:
```python
w2 = SymmetricWormhole("appid", RENDEZVOUS_RELAY)
w2 = Wormhole("appid", RENDEZVOUS_RELAY)
w2.set_code(code)
d = w2.get_data(my_message)
...
```
You can call `d=w.get_verifier()` before `get_data()`: this will perform the
@ -90,14 +113,14 @@ pausing.
## Generating the Invitation Code
In most situations, the Initiator will call `i.get_code()` to generate the
invitation code. This returns a string in the form `NNN-code-words`. The
numeric "NNN" prefix is the "channel id", and is a short integer allocated by
talking to the rendezvous server. The rest is a randomly-generated selection
from the PGP wordlist, providing a default of 16 bits of entropy. The
initiating program should display this code to the user, who should
transcribe it to the receiving user, who gives it to the Receiver object by
calling `r.set_code()`. The receiving program can also use
In most situations, the "sending" or "initiating" side will call
`i.get_code()` to generate the invitation code. This returns a string in the
form `NNN-code-words`. The numeric "NNN" prefix is the "channel id", and is a
short integer allocated by talking to the rendezvous server. The rest is a
randomly-generated selection from the PGP wordlist, providing a default of 16
bits of entropy. The initiating program should display this code to the user,
who should transcribe it to the receiving user, who gives it to the Receiver
object by calling `r.set_code()`. The receiving program can also use
`input_code_with_completion()` to use a readline-based input function: this
offers tab completion of allocated channel-ids and known codewords.
@ -168,12 +191,12 @@ Both have defaults suitable for face-to-face realtime setup environments.
TODO: only the Twisted form supports serialization so far
You may not be able to hold the Initiator/Receiver object in memory for the
whole sync process: maybe you allow it to wait for several days, but the
program will be restarted during that time. To support this, you can persist
the state of the object by calling `data = w.serialize()`, which will return
a printable bytestring (the JSON-encoding of a small dictionary). To restore,
use the `from_serialized(data)` classmethod (e.g. `w =
You may not be able to hold the Wormhole object in memory for the whole sync
process: maybe you allow it to wait for several days, but the program will be
restarted during that time. To support this, you can persist the state of the
object by calling `data = w.serialize()`, which will return a printable
bytestring (the JSON-encoding of a small dictionary). To restore, use the
`from_serialized(data)` classmethod (e.g. `w =
SymmetricWormhole.from_serialized(data)`).
There is exactly one point at which you can serialize the wormhole: *after*

View File

@ -3,7 +3,7 @@ from twisted.trial import unittest
from twisted.internet import defer
from twisted.application import service
from ..servers.relay import RelayServer
from ..twisted.transcribe import SymmetricWormhole, UsageError
from ..twisted.transcribe import Wormhole, UsageError
from ..twisted.util import allocate_ports
from .. import __version__
#from twisted.python import log
@ -31,8 +31,8 @@ class Basic(unittest.TestCase):
def test_basic(self):
appid = "appid"
w1 = SymmetricWormhole(appid, self.relayurl)
w2 = SymmetricWormhole(appid, self.relayurl)
w1 = Wormhole(appid, self.relayurl)
w2 = Wormhole(appid, self.relayurl)
d = w1.get_code()
def _got_code(code):
w2.set_code(code)
@ -52,8 +52,8 @@ class Basic(unittest.TestCase):
def test_fixed_code(self):
appid = "appid"
w1 = SymmetricWormhole(appid, self.relayurl)
w2 = SymmetricWormhole(appid, self.relayurl)
w1 = Wormhole(appid, self.relayurl)
w2 = Wormhole(appid, self.relayurl)
w1.set_code("123-purple-elephant")
w2.set_code("123-purple-elephant")
d1 = w1.get_data("data1")
@ -71,22 +71,22 @@ class Basic(unittest.TestCase):
def test_errors(self):
appid = "appid"
w1 = SymmetricWormhole(appid, self.relayurl)
w1 = Wormhole(appid, self.relayurl)
self.assertRaises(UsageError, w1.get_verifier)
self.assertRaises(UsageError, w1.get_data, "data")
w1.set_code("123-purple-elephant")
self.assertRaises(UsageError, w1.set_code, "123-nope")
self.assertRaises(UsageError, w1.get_code)
w2 = SymmetricWormhole(appid, self.relayurl)
w2 = Wormhole(appid, self.relayurl)
d = w2.get_code()
self.assertRaises(UsageError, w2.get_code)
return d
def test_serialize(self):
appid = "appid"
w1 = SymmetricWormhole(appid, self.relayurl)
w1 = Wormhole(appid, self.relayurl)
self.assertRaises(UsageError, w1.serialize) # too early
w2 = SymmetricWormhole(appid, self.relayurl)
w2 = Wormhole(appid, self.relayurl)
d = w1.get_code()
def _got_code(code):
self.assertRaises(UsageError, w2.serialize) # too early
@ -96,7 +96,7 @@ class Basic(unittest.TestCase):
self.assertEqual(type(s), type(""))
unpacked = json.loads(s) # this is supposed to be JSON
self.assertEqual(type(unpacked), dict)
new_w1 = SymmetricWormhole.from_serialized(s)
new_w1 = Wormhole.from_serialized(s)
d1 = new_w1.get_data("data1")
d2 = w2.get_data("data2")
return defer.DeferredList([d1,d2], fireOnOneErrback=False)

View File

@ -43,7 +43,7 @@ class DataProducer:
pass
class SymmetricWormhole:
class Wormhole:
motd_displayed = False
version_warning_displayed = False