INCOMPATIBILITY: change "confirm" message to include version dict
This gives the two Wormholes a way to signal capabilities to each other, before the applications start sending their own messages.
This commit is contained in:
parent
e1c488247f
commit
7f43561a50
|
@ -236,10 +236,10 @@ class Basic(unittest.TestCase):
|
|||
self.assertNoResult(v)
|
||||
|
||||
# hearing a valid confirmation message doesn't throw an error
|
||||
confkey = w.derive_key(u"wormhole:confirmation", SecretBox.KEY_SIZE)
|
||||
nonce = os.urandom(wormhole.CONFMSG_NONCE_LENGTH)
|
||||
confirm2 = wormhole.make_confmsg(confkey, nonce)
|
||||
confirm2_hex = hexlify(confirm2).decode("ascii")
|
||||
plaintext = json.dumps({}).encode("utf-8")
|
||||
data_key = w._derive_phase_key(side2, u"confirm")
|
||||
confmsg = w._encrypt_data(data_key, plaintext)
|
||||
confirm2_hex = hexlify(confmsg).decode("ascii")
|
||||
response(w, type=u"message", phase=u"confirm", body=confirm2_hex,
|
||||
side=side2)
|
||||
|
||||
|
@ -524,15 +524,16 @@ class Basic(unittest.TestCase):
|
|||
w._drop_connection = mock.Mock()
|
||||
w._ws_send_command = mock.Mock()
|
||||
w._mailbox_state = wormhole.OPEN
|
||||
side2 = u"side2"
|
||||
d = None
|
||||
|
||||
if success:
|
||||
w._key = b"key"
|
||||
else:
|
||||
w._key = b"wrongkey"
|
||||
confkey = w._derive_confirmation_key()
|
||||
nonce = os.urandom(wormhole.CONFMSG_NONCE_LENGTH)
|
||||
confmsg = wormhole.make_confmsg(confkey, nonce)
|
||||
plaintext = json.dumps({}).encode("utf-8")
|
||||
data_key = w._derive_phase_key(side2, u"confirm")
|
||||
confmsg = w._encrypt_data(data_key, plaintext)
|
||||
w._key = None
|
||||
|
||||
if when == "early":
|
||||
|
@ -543,7 +544,7 @@ class Basic(unittest.TestCase):
|
|||
w._key = b"key"
|
||||
w._event_established_key()
|
||||
else:
|
||||
w._event_received_confirm(confmsg)
|
||||
w._event_received_confirm(side2, confmsg)
|
||||
|
||||
if when == "middle":
|
||||
d = w.verify()
|
||||
|
@ -554,7 +555,7 @@ class Basic(unittest.TestCase):
|
|||
w._key = b"key"
|
||||
w._event_established_key()
|
||||
else:
|
||||
w._event_received_confirm(confmsg)
|
||||
w._event_received_confirm(side2, confmsg)
|
||||
|
||||
if when == "late":
|
||||
d = w.verify()
|
||||
|
@ -824,6 +825,22 @@ class Wormholes(ServerBase, unittest.TestCase):
|
|||
yield w1.close()
|
||||
yield w2.close()
|
||||
|
||||
@inlineCallbacks
|
||||
def test_versions(self):
|
||||
# there's no API for this yet, but make sure the internals work
|
||||
w1 = wormhole.wormhole(APPID, self.relayurl, reactor)
|
||||
w1._my_versions = {u"w1": 123}
|
||||
w2 = wormhole.wormhole(APPID, self.relayurl, reactor)
|
||||
w2._my_versions = {u"w2": 456}
|
||||
code = yield w1.get_code()
|
||||
w2.set_code(code)
|
||||
yield w1.verify()
|
||||
self.assertEqual(w1._their_versions, {u"w2": 456})
|
||||
yield w2.verify()
|
||||
self.assertEqual(w2._their_versions, {u"w1": 123})
|
||||
yield w1.close()
|
||||
yield w2.close()
|
||||
|
||||
class Errors(ServerBase, unittest.TestCase):
|
||||
@inlineCallbacks
|
||||
def test_codes_1(self):
|
||||
|
|
|
@ -245,6 +245,9 @@ class _Wormhole:
|
|||
self._verify_result = None # bytes or a Failure
|
||||
self._verifier_waiter = None
|
||||
|
||||
self._my_versions = {} # sent
|
||||
self._their_versions = {} # received
|
||||
|
||||
self._close_called = False # the close() API has been called
|
||||
self._closing = False # we've started shutdown
|
||||
self._disconnect_waiter = defer.Deferred()
|
||||
|
@ -545,10 +548,7 @@ class _Wormhole:
|
|||
self._timing.add("key established")
|
||||
|
||||
# both sides send different (random) confirmation messages
|
||||
confkey = self._derive_confirmation_key()
|
||||
nonce = os.urandom(CONFMSG_NONCE_LENGTH)
|
||||
confmsg = make_confmsg(confkey, nonce)
|
||||
self._msg_send(u"confirm", confmsg)
|
||||
self._send_confirmation_message()
|
||||
|
||||
verifier = self._derive_key(b"wormhole:verifier")
|
||||
self._event_computed_verifier(verifier)
|
||||
|
@ -556,6 +556,16 @@ class _Wormhole:
|
|||
self._maybe_check_confirmation()
|
||||
self._maybe_send_phase_messages()
|
||||
|
||||
def _send_confirmation_message(self):
|
||||
# this is encrypted like a normal phase message, and includes a
|
||||
# dictionary of version flags to let the other Wormhole know what
|
||||
# we're capable of (for future expansion)
|
||||
plaintext = json.dumps(self._my_versions).encode("utf-8")
|
||||
phase = u"confirm"
|
||||
data_key = self._derive_phase_key(self._side, phase)
|
||||
encrypted = self._encrypt_data(data_key, plaintext)
|
||||
self._msg_send(phase, encrypted)
|
||||
|
||||
def _API_verify(self):
|
||||
if self._error: return defer.fail(self._error)
|
||||
if self._get_verifier_called: raise UsageError
|
||||
|
@ -579,13 +589,13 @@ class _Wormhole:
|
|||
if self._verifier_waiter and not self._verifier_waiter.called:
|
||||
self._verifier_waiter.callback(self._verify_result)
|
||||
|
||||
def _event_received_confirm(self, body):
|
||||
def _event_received_confirm(self, side, body):
|
||||
# We ought to have the master key by now, because sensible peers
|
||||
# should always send "pake" before sending "confirm". It might be
|
||||
# nice to relax this requirement, which means storing the received
|
||||
# confirmation message, and having _event_established_key call
|
||||
# _check_confirmation()
|
||||
self._confirmation_message = body
|
||||
self._confirmation_message = (side, body)
|
||||
self._maybe_check_confirmation()
|
||||
|
||||
def _maybe_check_confirmation(self):
|
||||
|
@ -593,16 +603,24 @@ class _Wormhole:
|
|||
return
|
||||
if self._confirmation_checked:
|
||||
return
|
||||
confkey = self._derive_confirmation_key()
|
||||
body = self._confirmation_message
|
||||
nonce = body[:CONFMSG_NONCE_LENGTH]
|
||||
if body != make_confmsg(confkey, nonce):
|
||||
self._confirmation_checked = True
|
||||
|
||||
side, body = self._confirmation_message
|
||||
data_key = self._derive_phase_key(side, u"confirm")
|
||||
try:
|
||||
plaintext = self._decrypt_data(data_key, body)
|
||||
except CryptoError:
|
||||
# this makes all API calls fail
|
||||
if self.DEBUG: print("CONFIRM FAILED")
|
||||
self._signal_error(WrongPasswordError(), u"scary")
|
||||
self._confirmation_checked = True
|
||||
return
|
||||
msg = json.loads(plaintext.decode("utf-8"))
|
||||
self._version_received(msg)
|
||||
|
||||
self._maybe_notify_verify()
|
||||
|
||||
def _version_received(self, msg):
|
||||
self._their_versions = msg
|
||||
|
||||
def _API_send(self, outbound_data):
|
||||
if self._error: raise self._error
|
||||
|
@ -703,7 +721,7 @@ class _Wormhole:
|
|||
if phase == u"pake":
|
||||
return self._event_received_pake(body)
|
||||
if phase == u"confirm":
|
||||
return self._event_received_confirm(body)
|
||||
return self._event_received_confirm(side, body)
|
||||
if re.search(r'^\d+$', phase):
|
||||
return self._event_received_phase_message(side, phase, body)
|
||||
# ignore unrecognized phases, for forwards-compatibility
|
||||
|
|
Loading…
Reference in New Issue
Block a user