rename SymmetricWormhole to just "Wormhole"
Update docs too. Now both blocking/ and twisted/ use "Wormhole".
This commit is contained in:
parent
d8ca850d1a
commit
efd6d27cc6
|
@ -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
|
||||
|
|
83
docs/api.md
83
docs/api.md
|
@ -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*
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -43,7 +43,7 @@ class DataProducer:
|
|||
pass
|
||||
|
||||
|
||||
class SymmetricWormhole:
|
||||
class Wormhole:
|
||||
motd_displayed = False
|
||||
version_warning_displayed = False
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user