Previously, w.when_verified() was documented to fire only after a valid encrypted message was received, but in fact it fired as soon as the shared key was derived (before any encrypted messages are seen, so no actual "verification" could occur yet). This fixes that, and also adds a new w.when_key() API call which fires at the earlier point. Having something which fires early is useful for the CLI commands that want to print a pacifier message when the peer is responding slowly. In particular it helps detect the case where 'wormhole send' has quit early (after depositing the PAKE message on the server, but before the receiver has started). In this case, the receiver will compute the shared key, but then wait forever hoping for a VERSION that will never come. By starting a timer when w.when_key() fires, and cancelling it when w.when_verified() fires, we have a good place to tell the user that something is taking longer than it should have. This shifts responsibility for notifying Boss.got_verifier, out of Key and into Receive, since Receive is what notices the first valid encrypted message. It also shifts the Boss's ordering expectations: it now receives B.happy() before B.got_verifier(), and consequently got_verifier ought to arrive in the S2_happy state rather than S1_lonely.
90 lines
3.2 KiB
Python
90 lines
3.2 KiB
Python
from __future__ import print_function, absolute_import, unicode_literals
|
|
from zope.interface import implementer
|
|
from attr import attrs, attrib
|
|
from attr.validators import provides, instance_of
|
|
from automat import MethodicalMachine
|
|
from . import _interfaces
|
|
from ._key import derive_key, derive_phase_key, decrypt_data, CryptoError
|
|
|
|
@attrs
|
|
@implementer(_interfaces.IReceive)
|
|
class Receive(object):
|
|
_side = attrib(validator=instance_of(type(u"")))
|
|
_timing = attrib(validator=provides(_interfaces.ITiming))
|
|
m = MethodicalMachine()
|
|
set_trace = getattr(m, "setTrace", lambda self, f: None)
|
|
|
|
def __attrs_post_init__(self):
|
|
self._key = None
|
|
|
|
def wire(self, boss, send):
|
|
self._B = _interfaces.IBoss(boss)
|
|
self._S = _interfaces.ISend(send)
|
|
|
|
@m.state(initial=True)
|
|
def S0_unknown_key(self): pass # pragma: no cover
|
|
@m.state()
|
|
def S1_unverified_key(self): pass # pragma: no cover
|
|
@m.state()
|
|
def S2_verified_key(self): pass # pragma: no cover
|
|
@m.state(terminal=True)
|
|
def S3_scared(self): pass # pragma: no cover
|
|
|
|
# from Ordering
|
|
def got_message(self, side, phase, body):
|
|
assert isinstance(side, type("")), type(phase)
|
|
assert isinstance(phase, type("")), type(phase)
|
|
assert isinstance(body, type(b"")), type(body)
|
|
assert self._key
|
|
data_key = derive_phase_key(self._key, side, phase)
|
|
try:
|
|
plaintext = decrypt_data(data_key, body)
|
|
except CryptoError:
|
|
self.got_message_bad()
|
|
return
|
|
self.got_message_good(phase, plaintext)
|
|
@m.input()
|
|
def got_message_good(self, phase, plaintext): pass
|
|
@m.input()
|
|
def got_message_bad(self): pass
|
|
|
|
# from Key
|
|
@m.input()
|
|
def got_key(self, key): pass
|
|
|
|
@m.output()
|
|
def record_key(self, key):
|
|
self._key = key
|
|
@m.output()
|
|
def S_got_verified_key(self, phase, plaintext):
|
|
assert self._key
|
|
self._S.got_verified_key(self._key)
|
|
@m.output()
|
|
def W_happy(self, phase, plaintext):
|
|
self._B.happy()
|
|
@m.output()
|
|
def W_got_verifier(self, phase, plaintext):
|
|
self._B.got_verifier(derive_key(self._key, b"wormhole:verifier"))
|
|
@m.output()
|
|
def W_got_message(self, phase, plaintext):
|
|
assert isinstance(phase, type("")), type(phase)
|
|
assert isinstance(plaintext, type(b"")), type(plaintext)
|
|
self._B.got_message(phase, plaintext)
|
|
@m.output()
|
|
def W_scared(self):
|
|
self._B.scared()
|
|
|
|
S0_unknown_key.upon(got_key, enter=S1_unverified_key, outputs=[record_key])
|
|
S1_unverified_key.upon(got_message_good, enter=S2_verified_key,
|
|
outputs=[S_got_verified_key,
|
|
W_happy, W_got_verifier, W_got_message])
|
|
S1_unverified_key.upon(got_message_bad, enter=S3_scared,
|
|
outputs=[W_scared])
|
|
S2_verified_key.upon(got_message_bad, enter=S3_scared,
|
|
outputs=[W_scared])
|
|
S2_verified_key.upon(got_message_good, enter=S2_verified_key,
|
|
outputs=[W_got_message])
|
|
S3_scared.upon(got_message_good, enter=S3_scared, outputs=[])
|
|
S3_scared.upon(got_message_bad, enter=S3_scared, outputs=[])
|
|
|