From c499fce9f55bf9e707034314776bf55f43f100d5 Mon Sep 17 00:00:00 2001 From: Brian Warner Date: Fri, 3 Mar 2017 23:19:48 +0100 Subject: [PATCH] change API (wormhole.create), start on serialization --- src/wormhole/_boss.py | 3 ++ src/wormhole/cli/cmd_receive.py | 15 +++++----- src/wormhole/cli/cmd_send.py | 24 ++++++++------- src/wormhole/wormhole.py | 53 ++++++++++++++++++++------------- 4 files changed, 56 insertions(+), 39 deletions(-) diff --git a/src/wormhole/_boss.py b/src/wormhole/_boss.py index f89b964..9aa73fd 100644 --- a/src/wormhole/_boss.py +++ b/src/wormhole/_boss.py @@ -80,6 +80,9 @@ class Boss(object): old_state, input, new_state)) names[machine].set_trace(tracer) + def serialize(self): + raise NotImplemented + # and these are the state-machine transition functions, which don't take # args @m.state(initial=True) diff --git a/src/wormhole/cli/cmd_receive.py b/src/wormhole/cli/cmd_receive.py index c262b9a..0c2c438 100644 --- a/src/wormhole/cli/cmd_receive.py +++ b/src/wormhole/cli/cmd_receive.py @@ -5,7 +5,7 @@ from humanize import naturalsize from twisted.internet import reactor from twisted.internet.defer import inlineCallbacks, returnValue from twisted.python import log -from ..wormhole import wormhole +from .. import wormhole from ..transit import TransitReceiver from ..errors import TransferError, WormholeClosedError, NoTorError from ..util import (dict_to_bytes, bytes_to_dict, bytes_to_hexstr, @@ -61,8 +61,10 @@ class TwistedReceiver: # with the user handing off the wormhole code yield self._tor_manager.start() - w = wormhole(self.args.appid or APPID, self.args.relay_url, - self._reactor, self._tor_manager, timing=self.args.timing) + w = wormhole.create(self.args.appid or APPID, self.args.relay_url, + self._reactor, + tor_manager=self._tor_manager, + timing=self.args.timing) # I wanted to do this instead: # # try: @@ -80,13 +82,12 @@ class TwistedReceiver: @inlineCallbacks def _go(self, w): yield self._handle_code(w) - yield w.establish_key() def on_slow_connection(): print(u"Key established, waiting for confirmation...", file=self.args.stderr) notify = self._reactor.callLater(VERIFY_TIMER, on_slow_connection) try: - verifier = yield w.verify() + verifier = yield w.when_verifier() finally: if not notify.called: notify.cancel() @@ -127,7 +128,7 @@ class TwistedReceiver: @inlineCallbacks def _get_data(self, w): # this may raise WrongPasswordError - them_bytes = yield w.get() + them_bytes = yield w.when_received() them_d = bytes_to_dict(them_bytes) if "error" in them_d: raise TransferError(them_d["error"]) @@ -142,7 +143,7 @@ class TwistedReceiver: if code: w.set_code(code) else: - yield w.input_code("Enter receive wormhole code: ", + yield w.input_code("Enter receive wormhole code: ", # TODO self.args.code_length) def _show_verifier(self, verifier): diff --git a/src/wormhole/cli/cmd_send.py b/src/wormhole/cli/cmd_send.py index 12dd3bb..30436b8 100644 --- a/src/wormhole/cli/cmd_send.py +++ b/src/wormhole/cli/cmd_send.py @@ -7,7 +7,7 @@ from twisted.protocols import basic from twisted.internet import reactor from twisted.internet.defer import inlineCallbacks, returnValue from ..errors import TransferError, WormholeClosedError, NoTorError -from ..wormhole import wormhole +from .. import wormhole from ..transit import TransitSender from ..util import dict_to_bytes, bytes_to_dict, bytes_to_hexstr @@ -52,9 +52,10 @@ class Sender: # with the user handing off the wormhole code yield self._tor_manager.start() - w = wormhole(self._args.appid or APPID, self._args.relay_url, - self._reactor, self._tor_manager, - timing=self._timing) + w = wormhole.create(self._args.appid or APPID, self._args.relay_url, + self._reactor, + tor_manager=self._tor_manager, + timing=self._timing) d = self._go(w) d.addBoth(w.close) # must wait for ack from close() yield d @@ -83,25 +84,25 @@ class Sender: if args.code: w.set_code(args.code) - code = args.code else: - code = yield w.get_code(args.code_length) + w.allocate_code(args.code_length) + code = yield w.when_code() if not args.zeromode: print(u"Wormhole code is: %s" % code, file=args.stderr) # flush stderr so the code is displayed immediately args.stderr.flush() print(u"", file=args.stderr) - yield w.establish_key() def on_slow_connection(): print(u"Key established, waiting for confirmation...", file=args.stderr) notify = self._reactor.callLater(VERIFY_TIMER, on_slow_connection) - # TODO: don't stall on w.verify() unless they want it + # TODO: maybe don't stall on verifier unless they want it try: - verifier_bytes = yield w.verify() # this may raise WrongPasswordError + # this may raise WrongPasswordError + verifier_bytes = yield w.when_verifier() finally: if not notify.called: notify.cancel() @@ -146,12 +147,13 @@ class Sender: while True: try: - them_d_bytes = yield w.get() + them_d_bytes = yield w.when_received() except WormholeClosedError: if done: returnValue(None) raise TransferError("unexpected close") - # TODO: get() fired, so now it's safe to use w.derive_key() + # TODO: when_received() fired, so now it's safe to use + # w.derive_key() them_d = bytes_to_dict(them_d_bytes) #print("GOT", them_d) recognized = False diff --git a/src/wormhole/wormhole.py b/src/wormhole/wormhole.py index 42cab86..15449c6 100644 --- a/src/wormhole/wormhole.py +++ b/src/wormhole/wormhole.py @@ -51,6 +51,12 @@ class _DelegatedWormhole(object): def set_code(self, code): self._boss.set_code(code) + def serialize(self): + s = {"serialized_wormhole_version": 1, + "boss": self._boss.serialize(), + } + return s + def send(self, plaintext): self._boss.send(plaintext) def close(self): @@ -116,6 +122,7 @@ class _DeferredWormhole(object): def set_code(self, code): self._boss.set_code(code) + # no .serialize in Deferred-mode def send(self, plaintext): self._boss.send(plaintext) def close(self): @@ -165,11 +172,8 @@ class _DeferredWormhole(object): for d in self._closed_observers: d.callback(close_result) -def _wormhole(appid, relay_url, reactor, delegate=None, - tor_manager=None, timing=None, - journal=None, - stderr=sys.stderr, - ): +def create(appid, relay_url, reactor, delegate=None, journal=None, + tor_manager=None, timing=None, stderr=sys.stderr): timing = timing or DebugTiming() side = bytes_to_hexstr(os.urandom(5)) journal = journal or ImmediateJournal() @@ -179,23 +183,30 @@ def _wormhole(appid, relay_url, reactor, delegate=None, w = _DeferredWormhole() b = Boss(w, side, relay_url, appid, reactor, journal, timing) w._set_boss(b) - # force allocate for now b.start() return w -def delegated_wormhole(appid, relay_url, reactor, delegate, - tor_manager=None, timing=None, - journal=None, - stderr=sys.stderr, - ): - assert delegate - return _wormhole(appid, relay_url, reactor, delegate, - tor_manager, timing, journal, stderr) +def from_serialized(serialized, reactor, delegate, + journal=None, tor_manager=None, + timing=None, stderr=sys.stderr): + assert serialized["serialized_wormhole_version"] == 1 + timing = timing or DebugTiming() + w = _DelegatedWormhole(delegate) + # now unpack state machines, including the SPAKE2 in Key + b = Boss.from_serialized(w, serialized["boss"], reactor, journal, timing) + w._set_boss(b) + b.start() # ?? + raise NotImplemented + # should the new Wormhole call got_code? only if it wasn't called before. + +# after creating the wormhole object, app must call exactly one of: +# set_code(code), generate_code(), helper=type_code(), and then (if they need +# to know the code) wait for delegate.got_code() or d=w.when_code() + +# the helper for type_code() can be asked for completions: +# d=helper.get_completions(text_so_far), which will fire with a list of +# strings that could usefully be appended to text_so_far. + +# wormhole.type_code_readline(w) is a wrapper that knows how to use +# w.type_code() to drive rlcompleter -def deferred_wormhole(appid, relay_url, reactor, - tor_manager=None, timing=None, - journal=None, - stderr=sys.stderr, - ): - return _wormhole(appid, relay_url, reactor, None, - tor_manager, timing, journal, stderr)