diff --git a/src/wormhole/blocking/transcribe.py b/src/wormhole/blocking/transcribe.py index f3edfb7..a79f4ed 100644 --- a/src/wormhole/blocking/transcribe.py +++ b/src/wormhole/blocking/transcribe.py @@ -175,6 +175,8 @@ class Initiator(Common): self.wait = 0.5*SECOND self.timeout = 3*MINUTE self.side = "initiator" + self.key = None + self.verifier = None def get_code(self, code_length=2): self.channel_id = self._allocate() # allocate channel @@ -185,9 +187,18 @@ class Initiator(Common): self._post_pake() return self.code + def _wait_for_key(self): + if not self.key: + key = self._get_pake([]) + self.key = key + self.verifier = self.derive_key(self.appid+b":Verifier") + + def get_verifier(self): + self._wait_for_key() + return self.verifier + def get_data(self, outbound_data): - key = self._get_pake([]) - self.key = key + self._wait_for_key() try: outbound_key = self.derive_key(b"sender") outbound_encrypted = self._encrypt_data(outbound_key, outbound_data) @@ -216,6 +227,8 @@ class Receiver(Common): self.side = "receiver" self.code = None self.channel_id = None + self.key = None + self.verifier = None def list_channels(self): r = requests.get(self.relay + "list") @@ -237,12 +250,21 @@ class Receiver(Common): idA=self.appid+":Initiator", idB=self.appid+":Receiver") + def _wait_for_key(self): + if not self.key: + other_msgs = self._post_pake() + key = self._get_pake(other_msgs) + self.key = key + self.verifier = self.derive_key(self.appid+b":Verifier") + + def get_verifier(self): + self._wait_for_key() + return self.verifier + def get_data(self, outbound_data): assert self.code is not None assert self.channel_id is not None - other_msgs = self._post_pake() - key = self._get_pake(other_msgs) - self.key = key + self._wait_for_key() try: outbound_key = self.derive_key(b"receiver") diff --git a/src/wormhole/scripts/cmd_receive_file.py b/src/wormhole/scripts/cmd_receive_file.py index bf1f74d..d7ae9ef 100644 --- a/src/wormhole/scripts/cmd_receive_file.py +++ b/src/wormhole/scripts/cmd_receive_file.py @@ -1,5 +1,5 @@ from __future__ import print_function -import sys, os, json +import sys, os, json, binascii from wormhole.blocking.transcribe import Receiver, WrongPasswordError from wormhole.blocking.transit import TransitReceiver, TransitError from .progress import start_progress, update_progress, finish_progress @@ -17,6 +17,10 @@ def receive_file(args): args.code_length) r.set_code(code) + if args.verify: + verifier = binascii.hexlify(r.get_verifier()) + print("Verifier %s." % verifier) + mydata = json.dumps({ "transit": { "direct_connection_hints": transit_receiver.get_direct_hints(), @@ -30,6 +34,10 @@ def receive_file(args): return 1 #print("their data: %r" % (data,)) + if "error" in data: + print("ERROR: " + data["error"], file=sys.stderr) + return 1 + file_data = data["file"] filename = os.path.basename(file_data["filename"]) # unicode filesize = file_data["filesize"] diff --git a/src/wormhole/scripts/cmd_receive_text.py b/src/wormhole/scripts/cmd_receive_text.py index d5b0e29..7d3b566 100644 --- a/src/wormhole/scripts/cmd_receive_text.py +++ b/src/wormhole/scripts/cmd_receive_text.py @@ -1,5 +1,5 @@ from __future__ import print_function -import sys, json +import sys, json, binascii from wormhole.blocking.transcribe import Receiver, WrongPasswordError APPID = "lothar.com/wormhole/text-xfer" @@ -12,6 +12,11 @@ def receive_text(args): code = r.input_code("Enter receive-text wormhole code: ", args.code_length) r.set_code(code) + + if args.verify: + verifier = binascii.hexlify(r.get_verifier()) + print("Verifier %s." % verifier) + data = json.dumps({"message": "ok"}).encode("utf-8") try: them_bytes = r.get_data(data) @@ -19,4 +24,7 @@ def receive_text(args): print("ERROR: " + e.explain(), file=sys.stderr) return 1 them_d = json.loads(them_bytes.decode("utf-8")) + if "error" in them_d: + print("ERROR: " + them_d["error"], file=sys.stderr) + return 1 print(them_d["message"]) diff --git a/src/wormhole/scripts/cmd_send_file.py b/src/wormhole/scripts/cmd_send_file.py index bc79527..8384295 100644 --- a/src/wormhole/scripts/cmd_send_file.py +++ b/src/wormhole/scripts/cmd_send_file.py @@ -1,5 +1,5 @@ from __future__ import print_function -import os, sys, json +import os, sys, json, binascii from wormhole.blocking.transcribe import Initiator, WrongPasswordError from wormhole.blocking.transit import TransitSender from .progress import start_progress, update_progress, finish_progress @@ -18,6 +18,20 @@ def send_file(args): print("Wormhole code is '%s'" % code) print() + if args.verify: + verifier = binascii.hexlify(i.get_verifier()) + while True: + ok = raw_input("Verifier %s. ok? (yes/no): " % verifier) + if ok.lower() == "yes": + break + if ok.lower() == "no": + print("verification rejected, abandoning transfer", + file=sys.stderr) + reject_data = json.dumps({"error": "verification rejected", + }).encode("utf-8") + i.get_data(reject_data) + return 1 + filesize = os.stat(filename).st_size data = json.dumps({ "file": { diff --git a/src/wormhole/scripts/cmd_send_text.py b/src/wormhole/scripts/cmd_send_text.py index 9cc8795..4f865ad 100644 --- a/src/wormhole/scripts/cmd_send_text.py +++ b/src/wormhole/scripts/cmd_send_text.py @@ -1,19 +1,34 @@ from __future__ import print_function -import sys, json +import sys, json, binascii from wormhole.blocking.transcribe import Initiator, WrongPasswordError APPID = "lothar.com/wormhole/text-xfer" def send_text(args): # we're sending - message = args.text - data = json.dumps({"message": message, - }).encode("utf-8") i = Initiator(APPID, args.relay_url) code = i.get_code(args.code_length) print("On the other computer, please run: wormhole receive-text") print("Wormhole code is: %s" % code) print("") + + if args.verify: + verifier = binascii.hexlify(i.get_verifier()) + while True: + ok = raw_input("Verifier %s. ok? (yes/no): " % verifier) + if ok.lower() == "yes": + break + if ok.lower() == "no": + print("verification rejected, abandoning transfer", + file=sys.stderr) + reject_data = json.dumps({"error": "verification rejected", + }).encode("utf-8") + i.get_data(reject_data) + return 1 + + message = args.text + data = json.dumps({"message": message, + }).encode("utf-8") try: them_bytes = i.get_data(data) except WrongPasswordError as e: diff --git a/src/wormhole/scripts/runner.py b/src/wormhole/scripts/runner.py index 1ce7f57..0bfa39d 100644 --- a/src/wormhole/scripts/runner.py +++ b/src/wormhole/scripts/runner.py @@ -20,6 +20,8 @@ g.add_argument("--transit-helper", default=public_relay.TRANSIT_RELAY, metavar="tcp:HOST:PORT", help="transit relay to use") g.add_argument("-c", "--code-length", type=int, default=2, metavar="WORDS", help="length of code (in bytes/words)") +g.add_argument("-v", "--verify", action="store_true", + help="display (and wait for acceptance of) verification string") subparsers = parser.add_subparsers(title="subcommands", dest="subcommand")