Add --verify (display/check key-verifier). Not entirely usable yet.

To be useful, both sides must add -v. If the sender uses -v but the
receiver doesn't, the receiver won't show the verification string, so
the sender can't compare it to anything (and must either abort the
transfer or accept it blindly). Maybe the receiver should show the
verification string unconditionally. Maybe the sender should
indicate (in unprotected plaintext, along with the PAKE message) whether
the receiver should show it or not.
This commit is contained in:
Brian Warner 2015-03-24 00:28:02 -07:00
parent ed1809d521
commit fae14ebe6a
6 changed files with 81 additions and 12 deletions

View File

@ -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")

View File

@ -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"]

View File

@ -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"])

View File

@ -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": {

View File

@ -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:

View File

@ -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")