From efd6d27cc63b173f3369fafb9e5edccd48d2a892 Mon Sep 17 00:00:00 2001 From: Brian Warner Date: Fri, 24 Jul 2015 17:47:46 -0700 Subject: [PATCH] rename SymmetricWormhole to just "Wormhole" Update docs too. Now both blocking/ and twisted/ use "Wormhole". --- README.md | 9 ++-- docs/api.md | 83 +++++++++++++++++++----------- src/wormhole/test/test_twisted.py | 20 +++---- src/wormhole/twisted/transcribe.py | 2 +- 4 files changed, 67 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index 527e5f0..0799cbd 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/docs/api.md b/docs/api.md index 130566d..123184d 100644 --- a/docs/api.md +++ b/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* diff --git a/src/wormhole/test/test_twisted.py b/src/wormhole/test/test_twisted.py index 60f4ee2..815facc 100644 --- a/src/wormhole/test/test_twisted.py +++ b/src/wormhole/test/test_twisted.py @@ -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) diff --git a/src/wormhole/twisted/transcribe.py b/src/wormhole/twisted/transcribe.py index 22b19e5..1758029 100644 --- a/src/wormhole/twisted/transcribe.py +++ b/src/wormhole/twisted/transcribe.py @@ -43,7 +43,7 @@ class DataProducer: pass -class SymmetricWormhole: +class Wormhole: motd_displayed = False version_warning_displayed = False