Make get_verifier() wait for _confirm to arrive
This improves the error behavior when --verify is used but there's a WrongPasswordError: the mismatch is detected before the verifiers are displayed or confirmation is requested. It requires that the far end sends a "_confirm" message, which was introduced in release 0.6.0. Use with older versions (if it doesn't break for other reasons) will cause a hang. This patch also deletes test_twisted.Basic.test_verifier_mismatch, since both sides now detect this on their own. It changes test_wrong_password() too, since we might now notice the error during send_data (previously we'd only see it in get_data).
This commit is contained in:
parent
c017de5e4b
commit
b70c2f8868
|
@ -109,11 +109,20 @@ class Basic(ServerBase, unittest.TestCase):
|
||||||
# * w2 will send w2.PAKE, wait for (and get) w1.PAKE, compute a key,
|
# * w2 will send w2.PAKE, wait for (and get) w1.PAKE, compute a key,
|
||||||
# send w2.CONFIRM, then wait for w1.DATA.
|
# send w2.CONFIRM, then wait for w1.DATA.
|
||||||
# * w1 will get w2.PAKE, compute a key, send w1.CONFIRM.
|
# * w1 will get w2.PAKE, compute a key, send w1.CONFIRM.
|
||||||
|
# * w1 might also get w2.CONFIRM, and may notice the error before it
|
||||||
|
# sends w1.CONFIRM, in which case the wait=True will signal an
|
||||||
|
# error inside _get_master_key() (inside send_data), and d1 will
|
||||||
|
# errback.
|
||||||
|
# * but w1 might not see w2.CONFIRM yet, in which case it won't
|
||||||
|
# errback until we do w1.get_data()
|
||||||
# * w2 gets w1.CONFIRM, notices the error, records it.
|
# * w2 gets w1.CONFIRM, notices the error, records it.
|
||||||
# * w2 (waiting for w1.DATA) wakes up, sees the error, throws
|
# * w2 (waiting for w1.DATA) wakes up, sees the error, throws
|
||||||
# * meanwhile w1 finishes sending its data. w2.CONFIRM may or may not
|
# * meanwhile w1 finishes sending its data. w2.CONFIRM may or may not
|
||||||
# have arrived by then
|
# have arrived by then
|
||||||
yield d1
|
try:
|
||||||
|
yield d1
|
||||||
|
except WrongPasswordError:
|
||||||
|
pass
|
||||||
|
|
||||||
# When we ask w1 to get_data(), one of two things might happen:
|
# When we ask w1 to get_data(), one of two things might happen:
|
||||||
# * if w2.CONFIRM arrived already, it will have recorded the error.
|
# * if w2.CONFIRM arrived already, it will have recorded the error.
|
||||||
|
@ -167,21 +176,6 @@ class Basic(ServerBase, unittest.TestCase):
|
||||||
self.assertEqual(dataY, b"data1")
|
self.assertEqual(dataY, b"data1")
|
||||||
yield self.doBoth(w1.close(), w2.close())
|
yield self.doBoth(w1.close(), w2.close())
|
||||||
|
|
||||||
@inlineCallbacks
|
|
||||||
def test_verifier_mismatch(self):
|
|
||||||
w1 = Wormhole(APPID, self.relayurl)
|
|
||||||
w2 = Wormhole(APPID, self.relayurl)
|
|
||||||
# we must disable confirmation messages, else the wormholes will
|
|
||||||
# figure out the mismatch by themselves and throw WrongPasswordError.
|
|
||||||
w1._send_confirm = w2._send_confirm = False
|
|
||||||
code = yield w1.get_code()
|
|
||||||
w2.set_code(code+"not")
|
|
||||||
res = yield self.doBoth(w1.get_verifier(), w2.get_verifier())
|
|
||||||
v1, v2 = res
|
|
||||||
self.failUnlessEqual(type(v1), type(b""))
|
|
||||||
self.failIfEqual(v1, v2)
|
|
||||||
yield self.doBoth(w1.close(), w2.close())
|
|
||||||
|
|
||||||
@inlineCallbacks
|
@inlineCallbacks
|
||||||
def test_errors(self):
|
def test_errors(self):
|
||||||
w1 = Wormhole(APPID, self.relayurl)
|
w1 = Wormhole(APPID, self.relayurl)
|
||||||
|
|
|
@ -350,6 +350,15 @@ class Wormhole:
|
||||||
if self._closed: raise UsageError
|
if self._closed: raise UsageError
|
||||||
if self._code is None: raise UsageError
|
if self._code is None: raise UsageError
|
||||||
yield self._get_master_key()
|
yield self._get_master_key()
|
||||||
|
# If the caller cares about the verifier, then they'll probably also
|
||||||
|
# willing to wait a moment to see the _confirm message. Each side
|
||||||
|
# sends this as soon as it sees the other's PAKE message. So the
|
||||||
|
# sender should see this hot on the heels of the inbound PAKE message
|
||||||
|
# (a moment after _get_master_key() returns). The receiver will see
|
||||||
|
# this a round-trip after they send their PAKE (because the sender is
|
||||||
|
# using wait=True inside _get_master_key, below: otherwise the sender
|
||||||
|
# might go do some blocking call).
|
||||||
|
yield self._msg_get(u"_confirm")
|
||||||
returnValue(self._verifier)
|
returnValue(self._verifier)
|
||||||
|
|
||||||
@inlineCallbacks
|
@inlineCallbacks
|
||||||
|
@ -369,7 +378,7 @@ class Wormhole:
|
||||||
confkey = self.derive_key(u"wormhole:confirmation")
|
confkey = self.derive_key(u"wormhole:confirmation")
|
||||||
nonce = os.urandom(CONFMSG_NONCE_LENGTH)
|
nonce = os.urandom(CONFMSG_NONCE_LENGTH)
|
||||||
confmsg = make_confmsg(confkey, nonce)
|
confmsg = make_confmsg(confkey, nonce)
|
||||||
yield self._msg_send(u"_confirm", confmsg)
|
yield self._msg_send(u"_confirm", confmsg, wait=True)
|
||||||
|
|
||||||
@inlineCallbacks
|
@inlineCallbacks
|
||||||
def _msg_send(self, phase, body, wait=False):
|
def _msg_send(self, phase, body, wait=False):
|
||||||
|
@ -396,6 +405,9 @@ class Wormhole:
|
||||||
return self._signal_error(err)
|
return self._signal_error(err)
|
||||||
self._received_messages[phase] = body
|
self._received_messages[phase] = body
|
||||||
if phase == u"_confirm":
|
if phase == u"_confirm":
|
||||||
|
# TODO: we might not have a master key yet, if the caller wasn't
|
||||||
|
# waiting in _get_master_key() when a back-to-back pake+_confirm
|
||||||
|
# message pair arrived.
|
||||||
confkey = self.derive_key(u"wormhole:confirmation")
|
confkey = self.derive_key(u"wormhole:confirmation")
|
||||||
nonce = body[:CONFMSG_NONCE_LENGTH]
|
nonce = body[:CONFMSG_NONCE_LENGTH]
|
||||||
if body != make_confmsg(confkey, nonce):
|
if body != make_confmsg(confkey, nonce):
|
||||||
|
|
Loading…
Reference in New Issue
Block a user