build out all state machines
still early: automat is happy (they're syntactically valid), but the Outputs are not implemented, and there are plenty of type mismatches
This commit is contained in:
parent
9ae8091ec3
commit
80661392b6
|
@ -73,6 +73,8 @@ digraph {
|
|||
]
|
||||
Code -> Wormhole [style="dashed"
|
||||
label="set_code"]
|
||||
App -> Code [style="dashed"
|
||||
label="allocate\ninput\nset"]
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -38,5 +38,5 @@ digraph {
|
|||
|
||||
{rank=same; foo foo2 legend}
|
||||
legend [shape="box" style="dotted"
|
||||
label="refresh: NL.refresh_nameplates()\nrx: NL.got_nameplates()"]
|
||||
label="refresh: NL.refresh_nameplates()\nrx: NL.rx_nameplates()"]
|
||||
}
|
||||
|
|
|
@ -40,6 +40,8 @@ digraph {
|
|||
P2_got_message [shape="box" label="A.received"]
|
||||
P2_got_message -> S2
|
||||
|
||||
S2 -> P_close_scary [label="scared" color="red"]
|
||||
|
||||
S_closing [label="closing"]
|
||||
S_closing -> P_closed [label="closed"]
|
||||
S_closing -> S_closing [label="got_message\nhappy\nscared\nclose"]
|
||||
|
|
112
src/wormhole/_code.py
Normal file
112
src/wormhole/_code.py
Normal file
|
@ -0,0 +1,112 @@
|
|||
import os
|
||||
from zope.interface import implementer
|
||||
from automat import MethodicalMachine
|
||||
from . import _interfaces
|
||||
from .wordlist import (byte_to_even_word, byte_to_odd_word,
|
||||
#even_words_lowercase, odd_words_lowercase,
|
||||
)
|
||||
|
||||
def make_code(nameplate, code_length):
|
||||
assert isinstance(nameplate, type("")), type(nameplate)
|
||||
words = []
|
||||
for i in range(code_length):
|
||||
# we start with an "odd word"
|
||||
if i % 2 == 0:
|
||||
words.append(byte_to_odd_word[os.urandom(1)].lower())
|
||||
else:
|
||||
words.append(byte_to_even_word[os.urandom(1)].lower())
|
||||
return "%s-%s" % (nameplate, "-".join(words))
|
||||
|
||||
@implementer(_interfaces.ICode)
|
||||
class Code(object):
|
||||
m = MethodicalMachine()
|
||||
def __init__(self, code_length, timing):
|
||||
self._code_length = code_length
|
||||
self._timing = timing
|
||||
def wire(self, wormhole, rendezvous_connector, nameplate_lister):
|
||||
self._W = _interfaces.IWormhole(wormhole)
|
||||
self._RC = _interfaces.IRendezvousConnector(rendezvous_connector)
|
||||
self._NL = _interfaces.INameplateListing(nameplate_lister)
|
||||
|
||||
@m.state(initial=True)
|
||||
def S0_unknown(self): pass
|
||||
@m.state()
|
||||
def S1_allocating(self): pass
|
||||
@m.state()
|
||||
def S2_typing_nameplate(self): pass
|
||||
@m.state()
|
||||
def S3_typing_code(self): pass
|
||||
@m.state()
|
||||
def S4_known(self): pass
|
||||
|
||||
# from App
|
||||
@m.input()
|
||||
def allocate(self): pass
|
||||
@m.input()
|
||||
def input(self): pass
|
||||
@m.input()
|
||||
def set(self, code): pass
|
||||
|
||||
# from RendezvousConnector
|
||||
@m.input()
|
||||
def rx_allocated(self, nameplate): pass
|
||||
|
||||
# from NameplateLister
|
||||
@m.input()
|
||||
def got_nameplates(self, nameplates): pass
|
||||
|
||||
# from stdin/readline/???
|
||||
@m.input()
|
||||
def tab(self): pass
|
||||
@m.input()
|
||||
def hyphen(self): pass
|
||||
@m.input()
|
||||
def RETURN(self, code): pass
|
||||
|
||||
@m.output()
|
||||
def NL_refresh_nameplates(self):
|
||||
self._NL.refresh_nameplates()
|
||||
@m.output()
|
||||
def RC_tx_allocate(self):
|
||||
self._RC.tx_allocate()
|
||||
@m.output()
|
||||
def do_completion_nameplates(self):
|
||||
pass
|
||||
@m.output()
|
||||
def stash_nameplates(self, nameplates):
|
||||
self._known_nameplates = nameplates
|
||||
pass
|
||||
@m.output()
|
||||
def lookup_wordlist(self):
|
||||
pass
|
||||
@m.output()
|
||||
def do_completion_code(self):
|
||||
pass
|
||||
@m.output()
|
||||
def generate_and_set(self, nameplate):
|
||||
self._code = make_code(nameplate, self._code_length)
|
||||
self._W_set_code()
|
||||
|
||||
@m.output()
|
||||
def W_set_code(self, code):
|
||||
self._code = code
|
||||
self._W_set_code()
|
||||
|
||||
def _W_set_code(self):
|
||||
self._W.set_code(self._code)
|
||||
|
||||
S0_unknown.upon(allocate, enter=S1_allocating, outputs=[RC_tx_allocate])
|
||||
S1_allocating.upon(rx_allocated, enter=S4_known, outputs=[generate_and_set])
|
||||
|
||||
S0_unknown.upon(set, enter=S4_known, outputs=[W_set_code])
|
||||
|
||||
S0_unknown.upon(input, enter=S2_typing_nameplate,
|
||||
outputs=[NL_refresh_nameplates])
|
||||
S2_typing_nameplate.upon(tab, enter=S2_typing_nameplate,
|
||||
outputs=[do_completion_nameplates])
|
||||
S2_typing_nameplate.upon(got_nameplates, enter=S2_typing_nameplate,
|
||||
outputs=[stash_nameplates])
|
||||
S2_typing_nameplate.upon(hyphen, enter=S3_typing_code,
|
||||
outputs=[lookup_wordlist])
|
||||
S3_typing_code.upon(tab, enter=S3_typing_code, outputs=[do_completion_code])
|
||||
S3_typing_code.upon(RETURN, enter=S4_known, outputs=[W_set_code])
|
|
@ -1,8 +1,8 @@
|
|||
|
||||
from zope.interface import Interface
|
||||
from six.moves.urllib_parse import urlparse
|
||||
from attr import attrs, attrib
|
||||
from twisted.internet import defer, endpoints #, error
|
||||
from twisted.application import internet
|
||||
from twisted.application import internet, service
|
||||
from autobahn.twisted import websocket
|
||||
from automat import MethodicalMachine
|
||||
|
||||
|
|
20
src/wormhole/_interfaces.py
Normal file
20
src/wormhole/_interfaces.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
from zope.interface import Interface
|
||||
|
||||
class IWormhole(Interface):
|
||||
pass
|
||||
class IMailbox(Interface):
|
||||
pass
|
||||
class ISend(Interface):
|
||||
pass
|
||||
class IOrder(Interface):
|
||||
pass
|
||||
class IKey(Interface):
|
||||
pass
|
||||
class IReceive(Interface):
|
||||
pass
|
||||
class IRendezvousConnector(Interface):
|
||||
pass
|
||||
class INameplateListing(Interface):
|
||||
pass
|
||||
class ICode(Interface):
|
||||
pass
|
|
@ -1,8 +1,15 @@
|
|||
from automat import MethodicalMachine
|
||||
from hashlib import sha256
|
||||
from zope.interface import implementer
|
||||
from spake2 import SPAKE2_Symmetric
|
||||
from hkdf import Hkdf
|
||||
from nacl.secret import SecretBox
|
||||
from nacl.exceptions import CryptoError
|
||||
from automat import MethodicalMachine
|
||||
from .util import (to_bytes, bytes_to_hexstr, hexstr_to_bytes)
|
||||
from . import _interfaces
|
||||
CryptoError
|
||||
__all__ = ["derive_key", "derive_phase_key", "CryptoError",
|
||||
"Key"]
|
||||
|
||||
def HKDF(skm, outlen, salt=None, CTXinfo=b""):
|
||||
return Hkdf(salt, skm).expand(CTXinfo, outlen)
|
||||
|
@ -13,15 +20,33 @@ def derive_key(key, purpose, length=SecretBox.KEY_SIZE):
|
|||
if not isinstance(length, int): raise TypeError(type(length))
|
||||
return HKDF(key, length, CTXinfo=purpose)
|
||||
|
||||
class KeyMachine(object):
|
||||
def derive_phase_key(side, phase):
|
||||
assert isinstance(side, type("")), type(side)
|
||||
assert isinstance(phase, type("")), type(phase)
|
||||
side_bytes = side.encode("ascii")
|
||||
phase_bytes = phase.encode("ascii")
|
||||
purpose = (b"wormhole:phase:"
|
||||
+ sha256(side_bytes).digest()
|
||||
+ sha256(phase_bytes).digest())
|
||||
return derive_key(purpose)
|
||||
|
||||
def decrypt_data(key, encrypted):
|
||||
assert isinstance(key, type(b"")), type(key)
|
||||
assert isinstance(encrypted, type(b"")), type(encrypted)
|
||||
assert len(key) == SecretBox.KEY_SIZE, len(key)
|
||||
box = SecretBox(key)
|
||||
data = box.decrypt(encrypted)
|
||||
return data
|
||||
|
||||
@implementer(_interfaces.IKey)
|
||||
class Key(object):
|
||||
m = MethodicalMachine()
|
||||
def __init__(self, wormhole, timing):
|
||||
self._wormhole = wormhole
|
||||
def __init__(self, timing):
|
||||
self._timing = timing
|
||||
def set_mailbox(self, mailbox):
|
||||
self._mailbox = mailbox
|
||||
def set_receive(self, receive):
|
||||
self._receive = receive
|
||||
def wire(self, wormhole, mailbox, receive):
|
||||
self._W = _interfaces.IWormhole(wormhole)
|
||||
self._M = _interfaces.IMailbox(mailbox)
|
||||
self._R = _interfaces.IReceive(receive)
|
||||
|
||||
@m.state(initial=True)
|
||||
def S0_know_nothing(self): pass
|
||||
|
@ -29,7 +54,7 @@ class KeyMachine(object):
|
|||
def S1_know_code(self): pass
|
||||
@m.state()
|
||||
def S2_know_key(self): pass
|
||||
@m.state()
|
||||
@m.state(terminal=True)
|
||||
def S3_scared(self): pass
|
||||
|
||||
def got_pake(self, payload):
|
||||
|
@ -51,20 +76,20 @@ class KeyMachine(object):
|
|||
self._sp = SPAKE2_Symmetric(to_bytes(code),
|
||||
idSymmetric=to_bytes(self._appid))
|
||||
msg1 = self._sp.start()
|
||||
self._mailbox.add_message("pake", {"pake_v1": bytes_to_hexstr(msg1)})
|
||||
self._M.add_message("pake", {"pake_v1": bytes_to_hexstr(msg1)})
|
||||
|
||||
@m.output()
|
||||
def scared(self):
|
||||
self._wormhole.scared()
|
||||
self._W.scared()
|
||||
@m.output()
|
||||
def compute_key(self, msg2):
|
||||
assert isinstance(msg2, type(b""))
|
||||
with self._timing.add("pake2", waiting="crypto"):
|
||||
key = self._sp.finish(msg2)
|
||||
self._my_versions = {}
|
||||
self._mailbox.add_message("version", self._my_versions)
|
||||
self._wormhole.got_verifier(derive_key(key, b"wormhole:verifier"))
|
||||
self._receive.got_key(key)
|
||||
self._M.add_message("version", self._my_versions)
|
||||
self._W.got_verifier(derive_key(key, b"wormhole:verifier"))
|
||||
self._R.got_key(key)
|
||||
|
||||
S0_know_nothing.upon(set_code, enter=S1_know_code, outputs=[build_pake])
|
||||
S1_know_code.upon(got_pake_good, enter=S2_know_key, outputs=[compute_key])
|
||||
|
|
|
@ -1,12 +1,21 @@
|
|||
from attr import attrs, attrib
|
||||
from zope.interface import implementer
|
||||
from automat import MethodicalMachine
|
||||
from . import _interfaces
|
||||
|
||||
@attrs
|
||||
class _Mailbox_Machine(object):
|
||||
_connection_machine = attrib()
|
||||
_m = attrib()
|
||||
@implementer(_interfaces.IMailbox)
|
||||
class Mailbox(object):
|
||||
m = MethodicalMachine()
|
||||
|
||||
def __init__(self, side):
|
||||
self._side = side
|
||||
self._mood = None
|
||||
self._nameplate = None
|
||||
|
||||
def wire(self, wormhole, rendezvous_connector, ordering):
|
||||
self._W = _interfaces.IWormhole(wormhole)
|
||||
self._RC = _interfaces.IRendezvousConnector(rendezvous_connector)
|
||||
self._O = _interfaces.IOrder(ordering)
|
||||
|
||||
@m.state(initial=True)
|
||||
def initial(self): pass
|
||||
|
||||
|
@ -68,136 +77,201 @@ class _Mailbox_Machine(object):
|
|||
# Ss: closed and released, waiting for stop
|
||||
@m.state()
|
||||
def SsB(self): pass
|
||||
@m.state()
|
||||
def Ss(self): pass # terminal
|
||||
@m.state(terminal=True)
|
||||
def Ss(self): pass
|
||||
|
||||
|
||||
def connected(self, ws):
|
||||
self._ws = ws
|
||||
self.M_connected()
|
||||
|
||||
@m.input()
|
||||
def M_start_unconnected(self): pass
|
||||
def start_unconnected(self): pass
|
||||
@m.input()
|
||||
def M_start_connected(self): pass
|
||||
def start_connected(self): pass
|
||||
|
||||
# from Wormhole
|
||||
@m.input()
|
||||
def M_set_nameplate(self): pass
|
||||
def set_nameplate(self, nameplate): pass
|
||||
@m.input()
|
||||
def M_connected(self): pass
|
||||
def close(self, mood): pass
|
||||
|
||||
# from RendezvousConnector
|
||||
@m.input()
|
||||
def M_lost(self): pass
|
||||
def connected(self): pass
|
||||
@m.input()
|
||||
def M_send(self, msg): pass
|
||||
def lost(self): pass
|
||||
|
||||
@m.input()
|
||||
def M_rx_claimed(self): pass
|
||||
def rx_claimed(self, mailbox): pass
|
||||
|
||||
def rx_message(self, side, phase, msg):
|
||||
if side == self._side:
|
||||
self.rx_message_ours(phase, msg)
|
||||
else:
|
||||
self.rx_message_theirs(phase, msg)
|
||||
@m.input()
|
||||
def M_rx_msg_from_me(self, msg): pass
|
||||
def rx_message_ours(self, phase, msg): pass
|
||||
@m.input()
|
||||
def M_rx_msg_from_them(self, msg): pass
|
||||
def rx_message_theirs(self, phase, msg): pass
|
||||
@m.input()
|
||||
def M_rx_released(self): pass
|
||||
def rx_released(self): pass
|
||||
@m.input()
|
||||
def M_rx_closed(self): pass
|
||||
def rx_closed(self): pass
|
||||
@m.input()
|
||||
def M_stop(self): pass
|
||||
def stopped(self): pass
|
||||
|
||||
# from Send or Key
|
||||
@m.input()
|
||||
def M_stopped(self): pass
|
||||
def add_message(self, phase, msg): pass
|
||||
|
||||
|
||||
@m.output()
|
||||
def tx_claim(self):
|
||||
self._c.send_command("claim", nameplate=self._nameplate)
|
||||
def record_nameplate(self, nameplate):
|
||||
self._nameplate = nameplate
|
||||
@m.output()
|
||||
def tx_open(self): pass
|
||||
def record_nameplate_and_RC_tx_claim(self, nameplate):
|
||||
self._nameplate = nameplate
|
||||
self._RX.tx_claim(self._nameplate)
|
||||
@m.output()
|
||||
def queue(self, msg): pass
|
||||
def RC_tx_claim(self):
|
||||
# when invoked via M.connected(), we must use the stored nameplate
|
||||
self._RC.tx_claim(self._nameplate)
|
||||
@m.output()
|
||||
def store_mailbox(self): pass # trouble(mb)
|
||||
def RC_tx_open(self):
|
||||
assert self._mailbox
|
||||
self._RC.tx_open(self._mailbox)
|
||||
@m.output()
|
||||
def tx_add(self, msg): pass
|
||||
def queue(self, phase, msg):
|
||||
self._pending_outbound[phase] = msg
|
||||
@m.output()
|
||||
def tx_add_queued(self): pass
|
||||
def store_mailbox_and_RC_tx_open_and_drain(self, mailbox):
|
||||
self._mailbox = mailbox
|
||||
self._RC.tx_open(mailbox)
|
||||
self._drain()
|
||||
@m.output()
|
||||
def tx_release(self): pass
|
||||
def drain(self):
|
||||
self._drain()
|
||||
def _drain(self):
|
||||
for phase, msg in self._pending_outbound.items():
|
||||
self._RC.tx_add(phase, msg)
|
||||
@m.output()
|
||||
def tx_close(self): pass
|
||||
def RC_tx_add(self, phase, msg):
|
||||
self._RC.tx_add(phase, msg)
|
||||
@m.output()
|
||||
def process_first_msg_from_them(self, msg):
|
||||
self.tx_release()
|
||||
self.process_msg_from_them(msg)
|
||||
def RC_tx_release(self):
|
||||
self._RC.tx_release()
|
||||
@m.output()
|
||||
def process_msg_from_them(self, msg): pass
|
||||
def RC_tx_release_and_accept(self, phase, msg):
|
||||
self._RC.tx_release()
|
||||
self._accept(phase, msg)
|
||||
@m.output()
|
||||
def dequeue(self, msg): pass
|
||||
def record_mood_and_RC_tx_release(self, mood):
|
||||
self._mood = mood
|
||||
self._RC.tx_release()
|
||||
@m.output()
|
||||
def C_stop(self): pass
|
||||
def record_mood_and_RC_tx_release_and_RC_tx_close(self, mood):
|
||||
self._mood = mood
|
||||
self._RC.tx_release()
|
||||
self._RC.tx_close(self._mood)
|
||||
@m.output()
|
||||
def WM_stopped(self): pass
|
||||
def RC_tx_close(self):
|
||||
assert self._mood
|
||||
self._RC.tx_close(self._mood)
|
||||
@m.output()
|
||||
def record_mood_and_RC_tx_close(self, mood):
|
||||
self._mood = mood
|
||||
self._RC.tx_close(self._mood)
|
||||
@m.output()
|
||||
def accept(self, phase, msg):
|
||||
self._accept(phase, msg)
|
||||
def _accept(self, phase, msg):
|
||||
if phase not in self._processed:
|
||||
self._O.got_message(phase, msg)
|
||||
self._processed.add(phase)
|
||||
@m.output()
|
||||
def dequeue(self, phase, msg):
|
||||
self._pending_outbound.pop(phase)
|
||||
@m.output()
|
||||
def record_mood(self, mood):
|
||||
self._mood = mood
|
||||
@m.output()
|
||||
def record_mood_and_RC_stop(self, mood):
|
||||
self._mood = mood
|
||||
self._RC_stop()
|
||||
@m.output()
|
||||
def RC_stop(self):
|
||||
self._RC_stop()
|
||||
@m.output()
|
||||
def W_closed(self):
|
||||
self._W.closed()
|
||||
|
||||
initial.upon(M_start_unconnected, enter=S0A, outputs=[])
|
||||
initial.upon(M_start_connected, enter=S0B, outputs=[])
|
||||
S0A.upon(M_connected, enter=S0B, outputs=[])
|
||||
S0A.upon(M_set_nameplate, enter=S1A, outputs=[])
|
||||
S0A.upon(M_stop, enter=SsB, outputs=[C_stop])
|
||||
S0B.upon(M_lost, enter=S0A, outputs=[])
|
||||
S0B.upon(M_set_nameplate, enter=S2B, outputs=[tx_claim])
|
||||
S0B.upon(M_stop, enter=SsB, outputs=[C_stop])
|
||||
initial.upon(start_unconnected, enter=S0A, outputs=[])
|
||||
initial.upon(start_connected, enter=S0B, outputs=[])
|
||||
S0A.upon(connected, enter=S0B, outputs=[])
|
||||
S0A.upon(set_nameplate, enter=S1A, outputs=[record_nameplate])
|
||||
S0A.upon(add_message, enter=S0A, outputs=[queue])
|
||||
S0B.upon(lost, enter=S0A, outputs=[])
|
||||
S0B.upon(set_nameplate, enter=S2B, outputs=[record_nameplate_and_RC_tx_claim])
|
||||
S0B.upon(add_message, enter=S0B, outputs=[queue])
|
||||
|
||||
S1A.upon(M_connected, enter=S2B, outputs=[tx_claim])
|
||||
S1A.upon(M_send, enter=S1A, outputs=[queue])
|
||||
S1A.upon(M_stop, enter=SsB, outputs=[C_stop])
|
||||
S1A.upon(connected, enter=S2B, outputs=[RC_tx_claim])
|
||||
S1A.upon(add_message, enter=S1A, outputs=[queue])
|
||||
|
||||
S2A.upon(M_connected, enter=S2B, outputs=[tx_claim])
|
||||
S2A.upon(M_stop, enter=SrA, outputs=[])
|
||||
S2A.upon(M_send, enter=S2A, outputs=[queue])
|
||||
S2B.upon(M_lost, enter=S2A, outputs=[])
|
||||
S2B.upon(M_send, enter=S2B, outputs=[queue])
|
||||
S2B.upon(M_stop, enter=SrB, outputs=[tx_release])
|
||||
S2B.upon(M_rx_claimed, enter=S3B, outputs=[store_mailbox, tx_open,
|
||||
tx_add_queued])
|
||||
S2A.upon(connected, enter=S2B, outputs=[RC_tx_claim])
|
||||
S2A.upon(add_message, enter=S2A, outputs=[queue])
|
||||
S2B.upon(lost, enter=S2A, outputs=[])
|
||||
S2B.upon(add_message, enter=S2B, outputs=[queue])
|
||||
S2B.upon(rx_claimed, enter=S3B,
|
||||
outputs=[store_mailbox_and_RC_tx_open_and_drain])
|
||||
|
||||
S3A.upon(M_connected, enter=S3B, outputs=[tx_open, tx_add_queued])
|
||||
S3A.upon(M_send, enter=S3A, outputs=[queue])
|
||||
S3A.upon(M_stop, enter=SrcA, outputs=[])
|
||||
S3B.upon(M_lost, enter=S3A, outputs=[])
|
||||
S3B.upon(M_rx_msg_from_them, enter=S4B,
|
||||
outputs=[process_first_msg_from_them])
|
||||
S3B.upon(M_rx_msg_from_me, enter=S3B, outputs=[dequeue])
|
||||
S3B.upon(M_rx_claimed, enter=S3B, outputs=[])
|
||||
S3B.upon(M_send, enter=S3B, outputs=[queue, tx_add])
|
||||
S3B.upon(M_stop, enter=SrcB, outputs=[tx_release, tx_close])
|
||||
S3A.upon(connected, enter=S3B, outputs=[RC_tx_open, drain])
|
||||
S3A.upon(add_message, enter=S3A, outputs=[queue])
|
||||
S3B.upon(lost, enter=S3A, outputs=[])
|
||||
S3B.upon(rx_message_theirs, enter=S4B, outputs=[RC_tx_release_and_accept])
|
||||
S3B.upon(rx_message_ours, enter=S3B, outputs=[dequeue])
|
||||
S3B.upon(rx_claimed, enter=S3B, outputs=[])
|
||||
S3B.upon(add_message, enter=S3B, outputs=[queue, RC_tx_add])
|
||||
|
||||
S4A.upon(M_connected, enter=S4B,
|
||||
outputs=[tx_open, tx_add_queued, tx_release])
|
||||
S4A.upon(M_send, enter=S4A, outputs=[queue])
|
||||
S4A.upon(M_stop, enter=SrcA, outputs=[])
|
||||
S4B.upon(M_lost, enter=S4A, outputs=[])
|
||||
S4B.upon(M_send, enter=S4B, outputs=[queue, tx_add])
|
||||
S4B.upon(M_rx_msg_from_them, enter=S4B, outputs=[process_msg_from_them])
|
||||
S4B.upon(M_rx_msg_from_me, enter=S4B, outputs=[dequeue])
|
||||
S4B.upon(M_rx_released, enter=S5B, outputs=[])
|
||||
S4B.upon(M_stop, enter=SrcB, outputs=[tx_release, tx_close])
|
||||
S4A.upon(connected, enter=S4B,
|
||||
outputs=[RC_tx_open, drain, RC_tx_release])
|
||||
S4A.upon(add_message, enter=S4A, outputs=[queue])
|
||||
S4B.upon(lost, enter=S4A, outputs=[])
|
||||
S4B.upon(add_message, enter=S4B, outputs=[queue, RC_tx_add])
|
||||
S4B.upon(rx_message_theirs, enter=S4B, outputs=[accept])
|
||||
S4B.upon(rx_message_ours, enter=S4B, outputs=[dequeue])
|
||||
S4B.upon(rx_released, enter=S5B, outputs=[])
|
||||
|
||||
S5A.upon(M_connected, enter=S5B, outputs=[tx_open, tx_add_queued])
|
||||
S5A.upon(M_send, enter=S5A, outputs=[queue])
|
||||
S5A.upon(M_stop, enter=ScA, outputs=[])
|
||||
S5B.upon(M_lost, enter=S5A, outputs=[])
|
||||
S5B.upon(M_send, enter=S5B, outputs=[queue, tx_add])
|
||||
S5B.upon(M_rx_msg_from_them, enter=S5B, outputs=[process_msg_from_them])
|
||||
S5B.upon(M_rx_msg_from_me, enter=S5B, outputs=[dequeue])
|
||||
S5B.upon(M_stop, enter=ScB, outputs=[tx_close])
|
||||
S5A.upon(connected, enter=S5B, outputs=[RC_tx_open, drain])
|
||||
S5A.upon(add_message, enter=S5A, outputs=[queue])
|
||||
S5B.upon(lost, enter=S5A, outputs=[])
|
||||
S5B.upon(add_message, enter=S5B, outputs=[queue, RC_tx_add])
|
||||
S5B.upon(rx_message_theirs, enter=S5B, outputs=[accept])
|
||||
S5B.upon(rx_message_ours, enter=S5B, outputs=[dequeue])
|
||||
|
||||
SrcA.upon(M_connected, enter=SrcB, outputs=[tx_release, tx_close])
|
||||
SrcB.upon(M_lost, enter=SrcA, outputs=[])
|
||||
SrcB.upon(M_rx_closed, enter=SrB, outputs=[])
|
||||
SrcB.upon(M_rx_released, enter=ScB, outputs=[])
|
||||
if True:
|
||||
S0A.upon(close, enter=SsB, outputs=[record_mood_and_RC_stop])
|
||||
S0B.upon(close, enter=SsB, outputs=[record_mood_and_RC_stop])
|
||||
S1A.upon(close, enter=SsB, outputs=[record_mood_and_RC_stop])
|
||||
S2A.upon(close, enter=SrA, outputs=[record_mood])
|
||||
S2B.upon(close, enter=SrB, outputs=[record_mood_and_RC_tx_release])
|
||||
S3A.upon(close, enter=SrcA, outputs=[record_mood])
|
||||
S3B.upon(close, enter=SrcB,
|
||||
outputs=[record_mood_and_RC_tx_release_and_RC_tx_close])
|
||||
S4A.upon(close, enter=SrcA, outputs=[record_mood])
|
||||
S4B.upon(close, enter=SrcB,
|
||||
outputs=[record_mood_and_RC_tx_release_and_RC_tx_close])
|
||||
S5A.upon(close, enter=ScA, outputs=[record_mood])
|
||||
S5B.upon(close, enter=ScB, outputs=[record_mood_and_RC_tx_close])
|
||||
|
||||
SrB.upon(M_lost, enter=SrA, outputs=[])
|
||||
SrA.upon(M_connected, enter=SrB, outputs=[tx_release])
|
||||
SrB.upon(M_rx_released, enter=SsB, outputs=[C_stop])
|
||||
SrcA.upon(connected, enter=SrcB, outputs=[RC_tx_release, RC_tx_close])
|
||||
SrcB.upon(lost, enter=SrcA, outputs=[])
|
||||
SrcB.upon(rx_closed, enter=SrB, outputs=[])
|
||||
SrcB.upon(rx_released, enter=ScB, outputs=[])
|
||||
|
||||
ScB.upon(M_lost, enter=ScA, outputs=[])
|
||||
ScB.upon(M_rx_closed, enter=SsB, outputs=[C_stop])
|
||||
ScA.upon(M_connected, enter=ScB, outputs=[tx_close])
|
||||
SrB.upon(lost, enter=SrA, outputs=[])
|
||||
SrA.upon(connected, enter=SrB, outputs=[RC_tx_release])
|
||||
SrB.upon(rx_released, enter=SsB, outputs=[RC_stop])
|
||||
|
||||
SsB.upon(M_stopped, enter=Ss, outputs=[WM_stopped])
|
||||
ScB.upon(lost, enter=ScA, outputs=[])
|
||||
ScB.upon(rx_closed, enter=SsB, outputs=[RC_stop])
|
||||
ScA.upon(connected, enter=ScB, outputs=[RC_tx_close])
|
||||
|
||||
SsB.upon(stopped, enter=Ss, outputs=[W_closed])
|
||||
|
||||
|
|
|
@ -1,8 +1,15 @@
|
|||
from zope.interface import implementer
|
||||
from automat import MethodicalMachine
|
||||
from . import _interfaces
|
||||
|
||||
class NameplateListingMachine(object):
|
||||
@implementer(_interfaces.INameplateListing)
|
||||
class NameplateListing(object):
|
||||
m = MethodicalMachine()
|
||||
def __init__(self):
|
||||
self._list_nameplate_waiters = []
|
||||
pass
|
||||
def wire(self, rendezvous_connector, code):
|
||||
self._RC = _interfaces.IRendezvousConnector(rendezvous_connector)
|
||||
self._C = _interfaces.ICode(code)
|
||||
|
||||
# Ideally, each API request would spawn a new "list_nameplates" message
|
||||
# to the server, so the response would be maximally fresh, but that would
|
||||
|
@ -14,40 +21,45 @@ class NameplateListingMachine(object):
|
|||
# request arrives, both requests will be satisfied by the same response.
|
||||
|
||||
@m.state(initial=True)
|
||||
def idle(self): pass
|
||||
def S0A_idle_disconnected(self): pass
|
||||
@m.state()
|
||||
def requesting(self): pass
|
||||
def S1A_wanting_disconnected(self): pass
|
||||
@m.state()
|
||||
def S0B_idle_connected(self): pass
|
||||
@m.state()
|
||||
def S1B_wanting_connected(self): pass
|
||||
|
||||
@m.input()
|
||||
def list_nameplates(self): pass # returns Deferred
|
||||
def connected(self): pass
|
||||
@m.input()
|
||||
def response(self, message): pass
|
||||
def lost(self): pass
|
||||
@m.input()
|
||||
def refresh_nameplates(self): pass
|
||||
@m.input()
|
||||
def rx_nameplates(self, message): pass
|
||||
|
||||
@m.output()
|
||||
def add_deferred(self):
|
||||
d = defer.Deferred()
|
||||
self._list_nameplate_waiters.append(d)
|
||||
return d
|
||||
def RC_tx_list(self):
|
||||
self._RC.tx_list()
|
||||
@m.output()
|
||||
def send_request(self):
|
||||
self._connection.send_command("list")
|
||||
@m.output()
|
||||
def distribute_response(self, message):
|
||||
nameplates = parse(message)
|
||||
waiters = self._list_nameplate_waiters
|
||||
self._list_nameplate_waiters = []
|
||||
for d in waiters:
|
||||
d.callback(nameplates)
|
||||
def C_got_nameplates(self, message):
|
||||
self._C.got_nameplates(message["nameplates"])
|
||||
|
||||
idle.upon(list_nameplates, enter=requesting,
|
||||
outputs=[add_deferred, send_request],
|
||||
collector=lambda outs: outs[0])
|
||||
idle.upon(response, enter=idle, outputs=[])
|
||||
requesting.upon(list_nameplates, enter=requesting,
|
||||
outputs=[add_deferred],
|
||||
collector=lambda outs: outs[0])
|
||||
requesting.upon(response, enter=idle, outputs=[distribute_response])
|
||||
S0A_idle_disconnected.upon(connected, enter=S0B_idle_connected, outputs=[])
|
||||
S0B_idle_connected.upon(lost, enter=S0A_idle_disconnected, outputs=[])
|
||||
|
||||
# nlm._connection = c = Connection(ws)
|
||||
# nlm.list_nameplates().addCallback(display_completions)
|
||||
# c.register_dispatch("nameplates", nlm.response)
|
||||
S0A_idle_disconnected.upon(refresh_nameplates,
|
||||
enter=S1A_wanting_disconnected, outputs=[])
|
||||
S1A_wanting_disconnected.upon(refresh_nameplates,
|
||||
enter=S1A_wanting_disconnected, outputs=[])
|
||||
S1A_wanting_disconnected.upon(connected, enter=S1B_wanting_connected,
|
||||
outputs=[RC_tx_list])
|
||||
S0B_idle_connected.upon(refresh_nameplates, enter=S1B_wanting_connected,
|
||||
outputs=[RC_tx_list])
|
||||
S0B_idle_connected.upon(rx_nameplates, enter=S0B_idle_connected,
|
||||
outputs=[C_got_nameplates])
|
||||
S1B_wanting_connected.upon(lost, enter=S1A_wanting_disconnected, outputs=[])
|
||||
S1B_wanting_connected.upon(refresh_nameplates, enter=S1B_wanting_connected,
|
||||
outputs=[RC_tx_list])
|
||||
S1B_wanting_connected.upon(rx_nameplates, enter=S0B_idle_connected,
|
||||
outputs=[C_got_nameplates])
|
||||
|
|
55
src/wormhole/_order.py
Normal file
55
src/wormhole/_order.py
Normal file
|
@ -0,0 +1,55 @@
|
|||
from zope.interface import implementer
|
||||
from automat import MethodicalMachine
|
||||
from . import _interfaces
|
||||
|
||||
@implementer(_interfaces.IOrder)
|
||||
class Order(object):
|
||||
m = MethodicalMachine()
|
||||
def __init__(self, side, timing):
|
||||
self._side = side
|
||||
self._timing = timing
|
||||
self._key = None
|
||||
self._queue = []
|
||||
def wire(self, key, receive):
|
||||
self._K = _interfaces.IKey(key)
|
||||
self._R = _interfaces.IReceive(receive)
|
||||
|
||||
@m.state(initial=True)
|
||||
def S0_no_pake(self): pass
|
||||
@m.state(terminal=True)
|
||||
def S1_yes_pake(self): pass
|
||||
|
||||
def got_message(self, phase, payload):
|
||||
if phase == "pake":
|
||||
self.got_pake(phase, payload)
|
||||
else:
|
||||
self.got_non_pake(phase, payload)
|
||||
|
||||
@m.input()
|
||||
def got_pake(self, phase, payload): pass
|
||||
@m.input()
|
||||
def got_non_pake(self, phase, payload): pass
|
||||
|
||||
@m.output()
|
||||
def queue(self, phase, payload):
|
||||
self._queue.append((phase, payload))
|
||||
@m.output()
|
||||
def notify_key(self, phase, payload):
|
||||
self._K.got_pake(payload)
|
||||
@m.output()
|
||||
def drain(self, phase, payload):
|
||||
del phase
|
||||
del payload
|
||||
for (phase, payload) in self._queue:
|
||||
self._deliver(phase, payload)
|
||||
self._queue[:] = []
|
||||
@m.output()
|
||||
def deliver(self, phase, payload):
|
||||
self._deliver(phase, payload)
|
||||
|
||||
def _deliver(self, phase, payload):
|
||||
self._R.got_message(phase, payload)
|
||||
|
||||
S0_no_pake.upon(got_non_pake, enter=S0_no_pake, outputs=[queue])
|
||||
S0_no_pake.upon(got_pake, enter=S1_yes_pake, outputs=[notify_key, drain])
|
||||
S1_yes_pake.upon(got_non_pake, enter=S1_yes_pake, outputs=[deliver])
|
72
src/wormhole/_receive.py
Normal file
72
src/wormhole/_receive.py
Normal file
|
@ -0,0 +1,72 @@
|
|||
from zope.interface import implementer
|
||||
from automat import MethodicalMachine
|
||||
from . import _interfaces
|
||||
from ._key import derive_phase_key, decrypt_data, CryptoError
|
||||
|
||||
@implementer(_interfaces.IReceive)
|
||||
class Receive(object):
|
||||
m = MethodicalMachine()
|
||||
def __init__(self, side, timing):
|
||||
self._side = side
|
||||
self._timing = timing
|
||||
self._key = None
|
||||
def wire(self, wormhole, key, send):
|
||||
self._W = _interfaces.IWormhole(wormhole)
|
||||
self._K = _interfaces.IKey(key)
|
||||
self._S = _interfaces.ISend(send)
|
||||
|
||||
@m.state(initial=True)
|
||||
def S0_unknown_key(self): pass
|
||||
@m.state()
|
||||
def S1_unverified_key(self): pass
|
||||
@m.state()
|
||||
def S2_verified_key(self): pass
|
||||
@m.state(terminal=True)
|
||||
def S3_scared(self): pass
|
||||
|
||||
def got_message(self, phase, payload):
|
||||
assert self._key
|
||||
data_key = derive_phase_key(self._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_key(self, key): pass
|
||||
@m.input()
|
||||
def got_message_good(self, phase, plaintext): pass
|
||||
@m.input()
|
||||
def got_message_bad(self): 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._W.happy()
|
||||
@m.output()
|
||||
def W_got_message(self, phase, plaintext):
|
||||
self._W.got_message(phase, plaintext)
|
||||
@m.output()
|
||||
def W_scared(self):
|
||||
self._W.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_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=[])
|
||||
|
39
src/wormhole/_rendezvous.py
Normal file
39
src/wormhole/_rendezvous.py
Normal file
|
@ -0,0 +1,39 @@
|
|||
from zope.interface import implementer
|
||||
from twisted.application import service
|
||||
from . import _interfaces
|
||||
|
||||
@implementer(_interfaces.IRendezvousConnector)
|
||||
class RendezvousConnector(service.MultiService, object):
|
||||
def __init__(self, journal, timing):
|
||||
self._journal = journal
|
||||
self._timing = timing
|
||||
|
||||
def wire(self, mailbox, code, nameplate_lister):
|
||||
self._M = _interfaces.IMailbox(mailbox)
|
||||
self._C = _interfaces.ICode(code)
|
||||
self._NL = _interfaces.INameplateListing(nameplate_lister)
|
||||
|
||||
|
||||
# from Mailbox
|
||||
def tx_claim(self):
|
||||
pass
|
||||
def tx_open(self):
|
||||
pass
|
||||
def tx_add(self, x):
|
||||
pass
|
||||
def tx_release(self):
|
||||
pass
|
||||
def tx_close(self, mood):
|
||||
pass
|
||||
def stop(self):
|
||||
pass
|
||||
|
||||
# from NameplateLister
|
||||
def tx_list(self):
|
||||
pass
|
||||
|
||||
# from Code
|
||||
def tx_allocate(self):
|
||||
pass
|
||||
|
||||
# record, message, payload, packet, bundle, ciphertext, plaintext
|
|
@ -1,28 +1,20 @@
|
|||
from zope.interface import implementer
|
||||
from automat import MethodicalMachine
|
||||
from spake2 import SPAKE2_Symmetric
|
||||
from hkdf import Hkdf
|
||||
from nacl.secret import SecretBox
|
||||
from .util import (to_bytes, bytes_to_hexstr, hexstr_to_bytes)
|
||||
from . import _interfaces
|
||||
from .util import hexstr_to_bytes
|
||||
|
||||
def HKDF(skm, outlen, salt=None, CTXinfo=b""):
|
||||
return Hkdf(salt, skm).expand(CTXinfo, outlen)
|
||||
|
||||
def derive_key(key, purpose, length=SecretBox.KEY_SIZE):
|
||||
if not isinstance(key, type(b"")): raise TypeError(type(key))
|
||||
if not isinstance(purpose, type(b"")): raise TypeError(type(purpose))
|
||||
if not isinstance(length, int): raise TypeError(type(length))
|
||||
return HKDF(key, length, CTXinfo=purpose)
|
||||
|
||||
class SendMachine(object):
|
||||
@implementer(_interfaces.ISend)
|
||||
class Send(object):
|
||||
m = MethodicalMachine()
|
||||
def __init__(self, timing):
|
||||
def __init__(self, side, timing):
|
||||
self._side = side
|
||||
self._timing = timing
|
||||
def set_mailbox(self, mailbox):
|
||||
self._mailbox = mailbox
|
||||
def wire(self, mailbox):
|
||||
self._M = _interfaces.IMailbox(mailbox)
|
||||
|
||||
@m.state(initial=True)
|
||||
def S0_no_key(self): pass
|
||||
@m.state()
|
||||
@m.state(terminal=True)
|
||||
def S1_verified_key(self): pass
|
||||
|
||||
def got_pake(self, payload):
|
||||
|
@ -47,6 +39,7 @@ class SendMachine(object):
|
|||
del key
|
||||
for (phase, payload) in self._queue:
|
||||
self._encrypt_and_send(phase, payload)
|
||||
self._queue[:] = []
|
||||
@m.output()
|
||||
def deliver(self, phase, payload):
|
||||
self._encrypt_and_send(phase, payload)
|
||||
|
@ -54,7 +47,7 @@ class SendMachine(object):
|
|||
def _encrypt_and_send(self, phase, payload):
|
||||
data_key = self._derive_phase_key(self._side, phase)
|
||||
encrypted = self._encrypt_data(data_key, plaintext)
|
||||
self._mailbox.add_message(phase, encrypted)
|
||||
self._M.add_message(phase, encrypted)
|
||||
|
||||
S0_no_key.upon(send, enter=S0_no_key, outputs=[queue])
|
||||
S0_no_key.upon(got_verified_key, enter=S1_verified_key,
|
||||
|
|
|
@ -1,14 +1,39 @@
|
|||
from zope.interface import implementer
|
||||
from automat import MethodicalMachine
|
||||
from . import _interfaces
|
||||
from ._mailbox import Mailbox
|
||||
from ._send import Send
|
||||
from ._order import Order
|
||||
from ._key import Key
|
||||
from ._receive import Receive
|
||||
from ._rendezvous import RendezvousConnector
|
||||
from ._nameplate import NameplateListing
|
||||
from ._code import Code
|
||||
|
||||
@implementer(_interfaces.IWormhole)
|
||||
class Wormhole:
|
||||
m = MethodicalMachine()
|
||||
|
||||
def __init__(self, ws_url, reactor):
|
||||
self._relay_client = WSRelayClient(self, ws_url, reactor)
|
||||
# This records all the messages we want the relay to have. Each time
|
||||
# we establish a connection, we'll send them all (and the relay
|
||||
# server will filter out duplicates). If we add any while a
|
||||
# connection is established, we'll send the new ones.
|
||||
self._outbound_messages = []
|
||||
def __init__(self, side, reactor, timing):
|
||||
self._reactor = reactor
|
||||
|
||||
self._M = Mailbox(side)
|
||||
self._S = Send(side, timing)
|
||||
self._O = Order(side, timing)
|
||||
self._K = Key(timing)
|
||||
self._R = Receive(side, timing)
|
||||
self._RC = RendezvousConnector(side, timing, reactor)
|
||||
self._NL = NameplateListing()
|
||||
self._C = Code(timing)
|
||||
|
||||
self._M.wire(self, self._RC, self._O)
|
||||
self._S.wire(self._M)
|
||||
self._O.wire(self._K, self._R)
|
||||
self._K.wire(self, self._M, self._R)
|
||||
self._R.wire(self, self._K, self._S)
|
||||
self._RC.wire(self._M, self._C, self._NL)
|
||||
self._NL.wire(self._RC, self._C)
|
||||
self._C.wire(self, self._RC, self._NL)
|
||||
|
||||
# these methods are called from outside
|
||||
def start(self):
|
||||
|
@ -16,130 +41,110 @@ class Wormhole:
|
|||
|
||||
# and these are the state-machine transition functions, which don't take
|
||||
# args
|
||||
@m.state(initial=True)
|
||||
def S0_empty(self): pass
|
||||
@m.state()
|
||||
def closed(initial=True): pass
|
||||
def S1_lonely(self): pass
|
||||
@m.state()
|
||||
def know_code_not_mailbox(): pass
|
||||
def S2_happy(self): pass
|
||||
@m.state()
|
||||
def know_code_and_mailbox(): pass # no longer need nameplate
|
||||
@m.state()
|
||||
def waiting_first_msg(): pass # key is established, want any message
|
||||
@m.state()
|
||||
def processing_version(): pass
|
||||
@m.state()
|
||||
def processing_phase(): pass
|
||||
@m.state()
|
||||
def open(): pass # key is verified, can post app messages
|
||||
def S3_closing(self): pass
|
||||
@m.state(terminal=True)
|
||||
def failed(): pass
|
||||
def S4_closed(self): pass
|
||||
|
||||
# from the Application, or some sort of top-level shim
|
||||
@m.input()
|
||||
def deliver_message(self, message): pass
|
||||
def send(self, phase, message): pass
|
||||
@m.input()
|
||||
def close(self): pass
|
||||
|
||||
def w_set_seed(self, code, mailbox):
|
||||
"""Call w_set_seed when we sprout a Wormhole Seed, which
|
||||
contains both the code and the mailbox"""
|
||||
self.w_set_code(code)
|
||||
self.w_set_mailbox(mailbox)
|
||||
# from Code (which may be provoked by the Application)
|
||||
@m.input()
|
||||
def set_code(self, code): pass
|
||||
|
||||
# Key sends (got_verifier, scared)
|
||||
# Receive sends (got_message, happy, scared)
|
||||
@m.input()
|
||||
def w_set_code(self, code):
|
||||
"""Call w_set_code when you learn the code, probably because the user
|
||||
typed it in."""
|
||||
def happy(self): pass
|
||||
@m.input()
|
||||
def w_set_mailbox(self, mailbox):
|
||||
"""Call w_set_mailbox() when you learn the mailbox id, from the
|
||||
response to claim_nameplate"""
|
||||
pass
|
||||
def scared(self): pass
|
||||
def got_message(self, phase, plaintext):
|
||||
if phase == "version":
|
||||
self.got_version(plaintext)
|
||||
else:
|
||||
self.got_phase(phase, plaintext)
|
||||
@m.input()
|
||||
def got_version(self, version): pass
|
||||
@m.input()
|
||||
def got_phase(self, phase, plaintext): pass
|
||||
@m.input()
|
||||
def got_verifier(self, verifier): pass
|
||||
|
||||
# Mailbox sends closed
|
||||
@m.input()
|
||||
def closed(self): pass
|
||||
|
||||
@m.input()
|
||||
def rx_pake(self, pake): pass # reponse["message"][phase=pake]
|
||||
|
||||
@m.input()
|
||||
def rx_version(self, version): # response["message"][phase=version]
|
||||
pass
|
||||
@m.input()
|
||||
def verify_good(self, verifier): pass
|
||||
@m.input()
|
||||
def verify_bad(self, f): pass
|
||||
|
||||
@m.input()
|
||||
def rx_phase(self, message): pass
|
||||
@m.input()
|
||||
def phase_good(self, message): pass
|
||||
@m.input()
|
||||
def phase_bad(self, f): pass
|
||||
|
||||
@m.output()
|
||||
def compute_and_post_pake(self, code):
|
||||
self._code = code
|
||||
self._pake = compute(code)
|
||||
self._post(pake=self._pake)
|
||||
self._ws_send_command("add", phase="pake", body=XXX(pake))
|
||||
@m.output()
|
||||
def set_mailbox(self, mailbox):
|
||||
self._mailbox = mailbox
|
||||
@m.output()
|
||||
def set_seed(self, code, mailbox):
|
||||
self._code = code
|
||||
self._mailbox = mailbox
|
||||
|
||||
def got_code(self, code):
|
||||
nameplate = code.split("-")[0]
|
||||
self._M.set_nameplate(nameplate)
|
||||
self._K.set_code(code)
|
||||
@m.output()
|
||||
def process_version(self, version): # response["message"][phase=version]
|
||||
their_verifier = com
|
||||
if OK:
|
||||
self.verify_good(verifier)
|
||||
else:
|
||||
self.verify_bad(f)
|
||||
pass
|
||||
|
||||
@m.output()
|
||||
def notify_verified(self, verifier):
|
||||
for d in self._verify_waiters:
|
||||
d.callback(verifier)
|
||||
@m.output()
|
||||
def notify_failed(self, f):
|
||||
for d in self._verify_waiters:
|
||||
d.errback(f)
|
||||
def S_send(self, phase, message):
|
||||
self._S.send(phase, message)
|
||||
|
||||
@m.output()
|
||||
def process_phase(self, message): # response["message"][phase=version]
|
||||
their_verifier = com
|
||||
if OK:
|
||||
self.verify_good(verifier)
|
||||
else:
|
||||
self.verify_bad(f)
|
||||
pass
|
||||
def close_scared(self):
|
||||
self._M.close("scary")
|
||||
@m.output()
|
||||
def close_lonely(self):
|
||||
self._M.close("lonely")
|
||||
@m.output()
|
||||
def close_happy(self):
|
||||
self._M.close("happy")
|
||||
|
||||
@m.output()
|
||||
def post_inbound(self, message):
|
||||
pass
|
||||
def A_received(self, phase, plaintext):
|
||||
self._A.received(phase, plaintext)
|
||||
@m.output()
|
||||
def A_got_verifier(self, verifier):
|
||||
self._A.got_verifier(verifier)
|
||||
|
||||
@m.output()
|
||||
def deliver_message(self, message):
|
||||
self._qc.deliver_message(message)
|
||||
def A_closed(self):
|
||||
result = "???"
|
||||
self._A.closed(result)
|
||||
|
||||
@m.output()
|
||||
def compute_key_and_post_version(self, pake):
|
||||
self._key = x
|
||||
self._verifier = x
|
||||
plaintext = dict_to_bytes(self._my_versions)
|
||||
phase = "version"
|
||||
data_key = self._derive_phase_key(self._side, phase)
|
||||
encrypted = self._encrypt_data(data_key, plaintext)
|
||||
self._msg_send(phase, encrypted)
|
||||
S0_empty.upon(send, enter=S0_empty, outputs=[S_send])
|
||||
S0_empty.upon(set_code, enter=S1_lonely, outputs=[got_code])
|
||||
S1_lonely.upon(happy, enter=S2_happy, outputs=[])
|
||||
S1_lonely.upon(scared, enter=S3_closing, outputs=[close_scared])
|
||||
S1_lonely.upon(close, enter=S3_closing, outputs=[close_lonely])
|
||||
S1_lonely.upon(send, enter=S1_lonely, outputs=[S_send])
|
||||
S1_lonely.upon(got_verifier, enter=S1_lonely, outputs=[A_got_verifier])
|
||||
S2_happy.upon(got_phase, enter=S2_happy, outputs=[A_received])
|
||||
S2_happy.upon(got_version, enter=S2_happy, outputs=[process_version])
|
||||
S2_happy.upon(scared, enter=S3_closing, outputs=[close_scared])
|
||||
S2_happy.upon(close, enter=S3_closing, outputs=[close_happy])
|
||||
S2_happy.upon(send, enter=S2_happy, outputs=[S_send])
|
||||
|
||||
S3_closing.upon(got_phase, enter=S3_closing, outputs=[])
|
||||
S3_closing.upon(got_version, enter=S3_closing, outputs=[])
|
||||
S3_closing.upon(happy, enter=S3_closing, outputs=[])
|
||||
S3_closing.upon(scared, enter=S3_closing, outputs=[])
|
||||
S3_closing.upon(close, enter=S3_closing, outputs=[])
|
||||
S3_closing.upon(send, enter=S3_closing, outputs=[])
|
||||
S3_closing.upon(closed, enter=S4_closed, outputs=[A_closed])
|
||||
|
||||
S4_closed.upon(got_phase, enter=S4_closed, outputs=[])
|
||||
S4_closed.upon(got_version, enter=S4_closed, outputs=[])
|
||||
S4_closed.upon(happy, enter=S4_closed, outputs=[])
|
||||
S4_closed.upon(scared, enter=S4_closed, outputs=[])
|
||||
S4_closed.upon(close, enter=S4_closed, outputs=[])
|
||||
S4_closed.upon(send, enter=S4_closed, outputs=[])
|
||||
|
||||
closed.upon(w_set_code, enter=know_code_not_mailbox,
|
||||
outputs=[compute_and_post_pake])
|
||||
know_code_not_mailbox.upon(w_set_mailbox, enter=know_code_and_mailbox,
|
||||
outputs=[set_mailbox])
|
||||
know_code_and_mailbox.upon(rx_pake, enter=waiting_first_msg,
|
||||
outputs=[compute_key_and_post_version])
|
||||
waiting_first_msg.upon(rx_version, enter=processing_version,
|
||||
outputs=[process_version])
|
||||
processing_version.upon(verify_good, enter=open, outputs=[notify_verified])
|
||||
processing_version.upon(verify_bad, enter=failed, outputs=[notify_failed])
|
||||
open.upon(rx_phase, enter=processing_phase, outputs=[process_phase])
|
||||
processing_phase.upon(phase_good, enter=open, outputs=[post_inbound])
|
||||
processing_phase.upon(phase_bad, enter=failed, outputs=[notify_failed])
|
||||
|
|
Loading…
Reference in New Issue
Block a user