From f5741f9a520187c11a27d2ed0a785c2bb5cb94a1 Mon Sep 17 00:00:00 2001 From: Brian Warner Date: Fri, 20 Feb 2015 00:32:48 -0800 Subject: [PATCH] offer a wrong-password error message --- bin/receive_file.py | 8 ++++-- bin/receive_text.py | 10 +++++--- bin/send_file.py | 8 ++++-- bin/send_text.py | 8 ++++-- src/wormhole/blocking/transcribe.py | 38 ++++++++++++++++++++++++++--- 5 files changed, 60 insertions(+), 12 deletions(-) diff --git a/bin/receive_file.py b/bin/receive_file.py index 16dcfa8..455df9f 100644 --- a/bin/receive_file.py +++ b/bin/receive_file.py @@ -1,7 +1,7 @@ from __future__ import print_function import sys, os, json from nacl.secret import SecretBox -from wormhole.blocking.transcribe import Receiver +from wormhole.blocking.transcribe import Receiver, WrongPasswordError from wormhole.blocking.transit import TransitReceiver APPID = "lothar.com/wormhole/file-xfer" @@ -18,7 +18,11 @@ mydata = json.dumps({ r = Receiver(APPID, mydata) r.set_code(r.input_code("Enter receive-file wormhole code: ")) -data = json.loads(r.get_data().decode("utf-8")) +try: + data = json.loads(r.get_data().decode("utf-8")) +except WrongPasswordError as e: + print("ERROR: " + e.explain(), file=sys.stderr) + sys.exit(1) #print("their data: %r" % (data,)) file_data = data["file"] diff --git a/bin/receive_text.py b/bin/receive_text.py index 4090190..e4a1e6f 100644 --- a/bin/receive_text.py +++ b/bin/receive_text.py @@ -1,6 +1,6 @@ from __future__ import print_function -import time, json -from wormhole.blocking.transcribe import Receiver +import sys, time, json +from wormhole.blocking.transcribe import Receiver, WrongPasswordError APPID = "lothar.com/wormhole/text-xfer" @@ -9,7 +9,11 @@ data = json.dumps({"message": "ok"}).encode("utf-8") r = Receiver(APPID, data) r.set_code(r.input_code("Enter receive-text wormhole code: ")) start = time.time() -them_bytes = r.get_data() +try: + them_bytes = r.get_data() +except WrongPasswordError as e: + print("ERROR: " + e.explain(), file=sys.stderr) + sys.exit(1) them_d = json.loads(them_bytes.decode("utf-8")) print(them_d["message"]) print("elapsed time: %.2f" % (time.time() - start)) diff --git a/bin/send_file.py b/bin/send_file.py index c21ea3e..7aafc21 100644 --- a/bin/send_file.py +++ b/bin/send_file.py @@ -1,7 +1,7 @@ from __future__ import print_function import os, sys, json from nacl.secret import SecretBox -from wormhole.blocking.transcribe import Initiator +from wormhole.blocking.transcribe import Initiator, WrongPasswordError from wormhole.blocking.transit import TransitSender APPID = "lothar.com/wormhole/file-xfer" @@ -28,7 +28,11 @@ code = i.get_code() print("On the other computer, please run: receive_file") print("Wormhole code is '%s'" % code) print("") -them_bytes = i.get_data() +try: + them_bytes = i.get_data() +except WrongPasswordError as e: + print("ERROR: " + e.explain(), file=sys.stderr) + sys.exit(1) them_d = json.loads(them_bytes.decode("utf-8")) #print("them: %r" % (them_d,)) xfer_key = i.derive_key(APPID+"/xfer-key", SecretBox.KEY_SIZE) diff --git a/bin/send_text.py b/bin/send_text.py index 891fa1f..a975ff3 100644 --- a/bin/send_text.py +++ b/bin/send_text.py @@ -1,6 +1,6 @@ from __future__ import print_function import sys, json -from wormhole.blocking.transcribe import Initiator +from wormhole.blocking.transcribe import Initiator, WrongPasswordError APPID = "lothar.com/wormhole/text-xfer" @@ -13,6 +13,10 @@ code = i.get_code() print("On the other computer, please run: receive_text") print("Wormhole code is: %s" % code) print("") -them_bytes = i.get_data() +try: + them_bytes = i.get_data() +except WrongPasswordError as e: + print("ERROR: " + e.explain(), file=sys.stderr) + sys.exit(1) them_d = json.loads(them_bytes.decode("utf-8")) print("them: %r" % (them_d,)) diff --git a/src/wormhole/blocking/transcribe.py b/src/wormhole/blocking/transcribe.py index 20efdba..f0cab4c 100644 --- a/src/wormhole/blocking/transcribe.py +++ b/src/wormhole/blocking/transcribe.py @@ -1,7 +1,8 @@ -import time, requests, json +import time, requests, json, textwrap from binascii import hexlify, unhexlify from spake2 import SPAKE2_A, SPAKE2_B from nacl.secret import SecretBox +from nacl.exceptions import CryptoError from nacl import utils from .. import codes from ..util.hkdf import HKDF @@ -13,6 +14,29 @@ MINUTE = 60*SECOND class Timeout(Exception): pass +class WrongPasswordError(Exception): + """ + Key confirmation failed. + """ + # or the data blob was corrupted, and that's why decrypt failed + def explain(self): + return textwrap.dedent(self.__doc__) + +class InitiatorWrongPasswordError(WrongPasswordError): + """ + Key confirmation failed. Either your correspondent typed the code wrong, + or a would-be man-in-the-middle attacker guessed incorrectly. You could + try again, giving both your correspondent and the attacker another + chance. + """ + +class ReceiverWrongPasswordError(WrongPasswordError): + """ + Key confirmation failed. Either you typed the code wrong, or a would-be + man-in-the-middle attacker guessed incorrectly. You could try again, + giving both you and the attacker another chance. + """ + # POST /allocate -> {channel-id: INT} # POST /CHANNEL-ID/SIDE/pake/post {message: STR} -> {messages: [STR..]} # POST /CHANNEL-ID/SIDE/pake/poll -> {messages: [STR..]} @@ -116,7 +140,11 @@ class Initiator(Common): inbound_encrypted = self._poll_data(other_msgs) inbound_key = self.derive_key(b"receiver") - inbound_data = self._decrypt_data(inbound_key, inbound_encrypted) + try: + inbound_data = self._decrypt_data(inbound_key, + inbound_encrypted) + except CryptoError: + raise InitiatorWrongPasswordError finally: self._deallocate() return inbound_data @@ -169,7 +197,11 @@ class Receiver(Common): inbound_encrypted = self._poll_data(other_msgs) inbound_key = self.derive_key(b"sender") - inbound_data = self._decrypt_data(inbound_key, inbound_encrypted) + try: + inbound_data = self._decrypt_data(inbound_key, + inbound_encrypted) + except CryptoError: + raise ReceiverWrongPasswordError finally: self._deallocate() return inbound_data