From d8ca850d1a0bb52f55e669756cfec883e8779454 Mon Sep 17 00:00:00 2001 From: Brian Warner Date: Fri, 24 Jul 2015 17:28:55 -0700 Subject: [PATCH] blocking: finish alignment --- src/wormhole/blocking/transcribe.py | 55 +++++++++++++++-------------- 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/src/wormhole/blocking/transcribe.py b/src/wormhole/blocking/transcribe.py index 776660a..d61558b 100644 --- a/src/wormhole/blocking/transcribe.py +++ b/src/wormhole/blocking/transcribe.py @@ -97,13 +97,6 @@ class Wormhole: r.raise_for_status() return r.json() - def _post_message(self, url, msg): - # TODO: retry on failure, with exponential backoff. We're guarding - # against the rendezvous server being temporarily offline. - if not isinstance(msg, type(b"")): raise UsageError(type(msg)) - resp = self._post_json(url, {"message": hexlify(msg).decode("ascii")}) - return resp["messages"] # other_msgs - def _allocate_channel(self): r = requests.post(self.relay + "allocate/%s" % self.side) r.raise_for_status() @@ -155,6 +148,13 @@ class Wormhole: idB=self.appid+":SymmetricB") self.msg1 = self.sp.start() + def _post_message(self, url, msg): + # TODO: retry on failure, with exponential backoff. We're guarding + # against the rendezvous server being temporarily offline. + if not isinstance(msg, type(b"")): raise UsageError(type(msg)) + resp = self._post_json(url, {"message": hexlify(msg).decode("ascii")}) + return resp["messages"] # other_msgs + def _get_message(self, old_msgs, verb, msgnum): # For now, server errors cause the client to fail. TODO: don't. This # will require changing the client to re-post messages when the @@ -208,31 +208,34 @@ class Wormhole: return self.verifier def get_data(self, outbound_data): + # only call this once if self.code is None: raise UsageError if self.channel_id is None: raise UsageError + try: + self._get_key() + return self._get_data2(outbound_data) + finally: + self._deallocate() - self._get_key() + def _get_data2(self, outbound_data): # Without predefined roles, we can't derive predictably unique keys # for each side, so we use the same key for both. We use random # nonces to keep the messages distinct, and check for reflection. + data_key = self.derive_key(b"data-key") + + outbound_encrypted = self._encrypt_data(data_key, outbound_data) + msgs = self._post_message(self._url("post", "data"), outbound_encrypted) + + inbound_encrypted = self._get_message(msgs, "poll", "data") + if inbound_encrypted == outbound_encrypted: + raise ReflectionAttack try: - data_key = self.derive_key(b"data-key") - - outbound_encrypted = self._encrypt_data(data_key, outbound_data) - old_msgs = self._post_message(self._url("post", "data"), - outbound_encrypted) - - inbound_encrypted = self._get_message(old_msgs, "poll", "data") - if inbound_encrypted == outbound_encrypted: - raise ReflectionAttack - try: - inbound_data = self._decrypt_data(data_key, inbound_encrypted) - except CryptoError: - raise WrongPasswordError - finally: - self._deallocate() - return inbound_data + inbound_data = self._decrypt_data(data_key, inbound_encrypted) + return inbound_data + except CryptoError: + raise WrongPasswordError def _deallocate(self): - r = requests.post(self._url("deallocate")) - r.raise_for_status() + # only try once, no retries + requests.post(self._url("deallocate")) + # ignore POST failure, don't call r.raise_for_status()