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.wait = 0.5*SECOND
self.timeout = 3*MINUTE self.timeout = 3*MINUTE
self.side = "initiator" self.side = "initiator"
self.key = None
self.verifier = None
def get_code(self, code_length=2): def get_code(self, code_length=2):
self.channel_id = self._allocate() # allocate channel self.channel_id = self._allocate() # allocate channel
@ -185,9 +187,18 @@ class Initiator(Common):
self._post_pake() self._post_pake()
return self.code 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): def get_data(self, outbound_data):
key = self._get_pake([]) self._wait_for_key()
self.key = key
try: try:
outbound_key = self.derive_key(b"sender") outbound_key = self.derive_key(b"sender")
outbound_encrypted = self._encrypt_data(outbound_key, outbound_data) outbound_encrypted = self._encrypt_data(outbound_key, outbound_data)
@ -216,6 +227,8 @@ class Receiver(Common):
self.side = "receiver" self.side = "receiver"
self.code = None self.code = None
self.channel_id = None self.channel_id = None
self.key = None
self.verifier = None
def list_channels(self): def list_channels(self):
r = requests.get(self.relay + "list") r = requests.get(self.relay + "list")
@ -237,12 +250,21 @@ class Receiver(Common):
idA=self.appid+":Initiator", idA=self.appid+":Initiator",
idB=self.appid+":Receiver") 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): def get_data(self, outbound_data):
assert self.code is not None assert self.code is not None
assert self.channel_id is not None assert self.channel_id is not None
other_msgs = self._post_pake() self._wait_for_key()
key = self._get_pake(other_msgs)
self.key = key
try: try:
outbound_key = self.derive_key(b"receiver") outbound_key = self.derive_key(b"receiver")

View File

@ -1,5 +1,5 @@
from __future__ import print_function from __future__ import print_function
import sys, os, json import sys, os, json, binascii
from wormhole.blocking.transcribe import Receiver, WrongPasswordError from wormhole.blocking.transcribe import Receiver, WrongPasswordError
from wormhole.blocking.transit import TransitReceiver, TransitError from wormhole.blocking.transit import TransitReceiver, TransitError
from .progress import start_progress, update_progress, finish_progress from .progress import start_progress, update_progress, finish_progress
@ -17,6 +17,10 @@ def receive_file(args):
args.code_length) args.code_length)
r.set_code(code) r.set_code(code)
if args.verify:
verifier = binascii.hexlify(r.get_verifier())
print("Verifier %s." % verifier)
mydata = json.dumps({ mydata = json.dumps({
"transit": { "transit": {
"direct_connection_hints": transit_receiver.get_direct_hints(), "direct_connection_hints": transit_receiver.get_direct_hints(),
@ -30,6 +34,10 @@ def receive_file(args):
return 1 return 1
#print("their data: %r" % (data,)) #print("their data: %r" % (data,))
if "error" in data:
print("ERROR: " + data["error"], file=sys.stderr)
return 1
file_data = data["file"] file_data = data["file"]
filename = os.path.basename(file_data["filename"]) # unicode filename = os.path.basename(file_data["filename"]) # unicode
filesize = file_data["filesize"] filesize = file_data["filesize"]

View File

@ -1,5 +1,5 @@
from __future__ import print_function from __future__ import print_function
import sys, json import sys, json, binascii
from wormhole.blocking.transcribe import Receiver, WrongPasswordError from wormhole.blocking.transcribe import Receiver, WrongPasswordError
APPID = "lothar.com/wormhole/text-xfer" APPID = "lothar.com/wormhole/text-xfer"
@ -12,6 +12,11 @@ def receive_text(args):
code = r.input_code("Enter receive-text wormhole code: ", code = r.input_code("Enter receive-text wormhole code: ",
args.code_length) args.code_length)
r.set_code(code) 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") data = json.dumps({"message": "ok"}).encode("utf-8")
try: try:
them_bytes = r.get_data(data) them_bytes = r.get_data(data)
@ -19,4 +24,7 @@ def receive_text(args):
print("ERROR: " + e.explain(), file=sys.stderr) print("ERROR: " + e.explain(), file=sys.stderr)
return 1 return 1
them_d = json.loads(them_bytes.decode("utf-8")) 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"]) print(them_d["message"])

View File

@ -1,5 +1,5 @@
from __future__ import print_function from __future__ import print_function
import os, sys, json import os, sys, json, binascii
from wormhole.blocking.transcribe import Initiator, WrongPasswordError from wormhole.blocking.transcribe import Initiator, WrongPasswordError
from wormhole.blocking.transit import TransitSender from wormhole.blocking.transit import TransitSender
from .progress import start_progress, update_progress, finish_progress from .progress import start_progress, update_progress, finish_progress
@ -18,6 +18,20 @@ def send_file(args):
print("Wormhole code is '%s'" % code) print("Wormhole code is '%s'" % code)
print() 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 filesize = os.stat(filename).st_size
data = json.dumps({ data = json.dumps({
"file": { "file": {

View File

@ -1,19 +1,34 @@
from __future__ import print_function from __future__ import print_function
import sys, json import sys, json, binascii
from wormhole.blocking.transcribe import Initiator, WrongPasswordError from wormhole.blocking.transcribe import Initiator, WrongPasswordError
APPID = "lothar.com/wormhole/text-xfer" APPID = "lothar.com/wormhole/text-xfer"
def send_text(args): def send_text(args):
# we're sending # we're sending
message = args.text
data = json.dumps({"message": message,
}).encode("utf-8")
i = Initiator(APPID, args.relay_url) i = Initiator(APPID, args.relay_url)
code = i.get_code(args.code_length) code = i.get_code(args.code_length)
print("On the other computer, please run: wormhole receive-text") print("On the other computer, please run: wormhole receive-text")
print("Wormhole code is: %s" % code) print("Wormhole code is: %s" % code)
print("") 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: try:
them_bytes = i.get_data(data) them_bytes = i.get_data(data)
except WrongPasswordError as e: 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") metavar="tcp:HOST:PORT", help="transit relay to use")
g.add_argument("-c", "--code-length", type=int, default=2, g.add_argument("-c", "--code-length", type=int, default=2,
metavar="WORDS", help="length of code (in bytes/words)") 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", subparsers = parser.add_subparsers(title="subcommands",
dest="subcommand") dest="subcommand")