working on fixes
This commit is contained in:
parent
b7df5e21eb
commit
41b7bcfed5
|
@ -17,6 +17,9 @@ digraph {
|
||||||
S0 [label="S0: empty"]
|
S0 [label="S0: empty"]
|
||||||
S0 -> P0_build [label="set_code"]
|
S0 -> P0_build [label="set_code"]
|
||||||
|
|
||||||
|
S0 -> P_close_error [label="rx_error"]
|
||||||
|
P_close_error [shape="box" label="M.close(errory)"]
|
||||||
|
P_close_error -> S_closing
|
||||||
S0 -> P_close_lonely [label="close"]
|
S0 -> P_close_lonely [label="close"]
|
||||||
|
|
||||||
P0_build [shape="box" label="W.got_code\nM.set_nameplate\nK.got_code"]
|
P0_build [shape="box" label="W.got_code\nM.set_nameplate\nK.got_code"]
|
||||||
|
@ -25,6 +28,7 @@ digraph {
|
||||||
|
|
||||||
S1 -> S2 [label="happy"]
|
S1 -> S2 [label="happy"]
|
||||||
|
|
||||||
|
S1 -> P_close_error [label="rx_error"]
|
||||||
S1 -> P_close_scary [label="scared" color="red"]
|
S1 -> P_close_scary [label="scared" color="red"]
|
||||||
S1 -> P_close_lonely [label="close"]
|
S1 -> P_close_lonely [label="close"]
|
||||||
P_close_lonely [shape="box" label="M.close(lonely)"]
|
P_close_lonely [shape="box" label="M.close(lonely)"]
|
||||||
|
@ -39,22 +43,23 @@ digraph {
|
||||||
P2_close -> S_closing
|
P2_close -> S_closing
|
||||||
|
|
||||||
S2 -> P2_got_message [label="got_message"]
|
S2 -> P2_got_message [label="got_message"]
|
||||||
P2_got_message [shape="box" label="A.received"]
|
P2_got_message [shape="box" label="W.received"]
|
||||||
P2_got_message -> S2
|
P2_got_message -> S2
|
||||||
|
|
||||||
|
S2 -> P_close_error [label="rx_error"]
|
||||||
S2 -> P_close_scary [label="scared" color="red"]
|
S2 -> P_close_scary [label="scared" color="red"]
|
||||||
|
|
||||||
S_closing [label="closing"]
|
S_closing [label="closing"]
|
||||||
S_closing -> P_closed [label="closed"]
|
S_closing -> P_closed [label="closed"]
|
||||||
S_closing -> S_closing [label="got_message\nhappy\nscared\nclose"]
|
S_closing -> S_closing [label="got_message\nhappy\nscared\nclose"]
|
||||||
|
|
||||||
P_closed [shape="box" label="A.closed(reason)"]
|
P_closed [shape="box" label="W.closed(reason)"]
|
||||||
P_closed -> S_closed
|
P_closed -> S_closed
|
||||||
S_closed [label="closed"]
|
S_closed [label="closed"]
|
||||||
|
|
||||||
{rank=same; Other S_closed}
|
{rank=same; Other S_closed}
|
||||||
Other [shape="box" style="dashed"
|
Other [shape="box" style="dashed"
|
||||||
label="rx_welcome -> process\nsend -> S.send\ngot_verifier -> A.got_verifier\nallocate -> C.allocate\ninput -> C.input\nset_code -> C.set_code"
|
label="rx_welcome -> process\nsend -> S.send\ngot_verifier -> W.got_verifier\nallocate -> C.allocate\ninput -> C.input\nset_code -> C.set_code"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ digraph {
|
||||||
|
|
||||||
#Boss -> Connection [color="blue"]
|
#Boss -> Connection [color="blue"]
|
||||||
Boss -> Connection [style="dashed" label="start"]
|
Boss -> Connection [style="dashed" label="start"]
|
||||||
Connection -> Boss [style="dashed" label="rx_welcome"]
|
Connection -> Boss [style="dashed" label="rx_welcome\nrx_error"]
|
||||||
|
|
||||||
Boss -> Send [style="dashed" label="send"]
|
Boss -> Send [style="dashed" label="send"]
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
from __future__ import print_function, absolute_import, unicode_literals
|
from __future__ import print_function, absolute_import, unicode_literals
|
||||||
|
import re
|
||||||
|
import six
|
||||||
from zope.interface import implementer
|
from zope.interface import implementer
|
||||||
from attr import attrs, attrib
|
from attr import attrs, attrib
|
||||||
from attr.validators import provides, instance_of
|
from attr.validators import provides, instance_of
|
||||||
|
from twisted.python import log
|
||||||
from automat import MethodicalMachine
|
from automat import MethodicalMachine
|
||||||
from . import _interfaces
|
from . import _interfaces
|
||||||
from ._mailbox import Mailbox
|
from ._mailbox import Mailbox
|
||||||
|
@ -12,8 +15,12 @@ from ._receive import Receive
|
||||||
from ._rendezvous import RendezvousConnector
|
from ._rendezvous import RendezvousConnector
|
||||||
from ._nameplate import NameplateListing
|
from ._nameplate import NameplateListing
|
||||||
from ._code import Code
|
from ._code import Code
|
||||||
|
from .errors import WrongPasswordError
|
||||||
from .util import bytes_to_dict
|
from .util import bytes_to_dict
|
||||||
|
|
||||||
|
class WormholeError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
@attrs
|
@attrs
|
||||||
@implementer(_interfaces.IBoss)
|
@implementer(_interfaces.IBoss)
|
||||||
class Boss(object):
|
class Boss(object):
|
||||||
|
@ -30,7 +37,7 @@ class Boss(object):
|
||||||
self._M = Mailbox(self._side)
|
self._M = Mailbox(self._side)
|
||||||
self._S = Send(self._side, self._timing)
|
self._S = Send(self._side, self._timing)
|
||||||
self._O = Order(self._side, self._timing)
|
self._O = Order(self._side, self._timing)
|
||||||
self._K = Key(self._appid, self._timing)
|
self._K = Key(self._appid, self._side, self._timing)
|
||||||
self._R = Receive(self._side, self._timing)
|
self._R = Receive(self._side, self._timing)
|
||||||
self._RC = RendezvousConnector(self._url, self._appid, self._side,
|
self._RC = RendezvousConnector(self._url, self._appid, self._side,
|
||||||
self._reactor, self._journal,
|
self._reactor, self._journal,
|
||||||
|
@ -51,6 +58,8 @@ class Boss(object):
|
||||||
self._next_rx_phase = 0
|
self._next_rx_phase = 0
|
||||||
self._rx_phases = {} # phase -> plaintext
|
self._rx_phases = {} # phase -> plaintext
|
||||||
|
|
||||||
|
self._result = "empty"
|
||||||
|
|
||||||
# these methods are called from outside
|
# these methods are called from outside
|
||||||
def start(self):
|
def start(self):
|
||||||
self._RC.start()
|
self._RC.start()
|
||||||
|
@ -107,13 +116,20 @@ class Boss(object):
|
||||||
def happy(self): pass
|
def happy(self): pass
|
||||||
@m.input()
|
@m.input()
|
||||||
def scared(self): pass
|
def scared(self): pass
|
||||||
|
@m.input()
|
||||||
|
def rx_error(self, err, orig): pass
|
||||||
|
|
||||||
def got_message(self, phase, plaintext):
|
def got_message(self, phase, plaintext):
|
||||||
assert isinstance(phase, type("")), type(phase)
|
assert isinstance(phase, type("")), type(phase)
|
||||||
assert isinstance(plaintext, type(b"")), type(plaintext)
|
assert isinstance(plaintext, type(b"")), type(plaintext)
|
||||||
if phase == "version":
|
if phase == "version":
|
||||||
self.got_version(plaintext)
|
self.got_version(plaintext)
|
||||||
|
elif re.search(r'^\d+$', phase):
|
||||||
|
self.got_phase(int(phase), plaintext)
|
||||||
else:
|
else:
|
||||||
self.got_phase(phase, plaintext)
|
# Ignore unrecognized phases, for forwards-compatibility. Use
|
||||||
|
# log.err so tests will catch surprises.
|
||||||
|
log.err("received unknown phase '%s'" % phase)
|
||||||
@m.input()
|
@m.input()
|
||||||
def got_version(self, plaintext): pass
|
def got_version(self, plaintext): pass
|
||||||
@m.input()
|
@m.input()
|
||||||
|
@ -143,18 +159,26 @@ class Boss(object):
|
||||||
|
|
||||||
@m.output()
|
@m.output()
|
||||||
def S_send(self, plaintext):
|
def S_send(self, plaintext):
|
||||||
|
assert isinstance(plaintext, type(b"")), type(plaintext)
|
||||||
phase = self._next_tx_phase
|
phase = self._next_tx_phase
|
||||||
self._next_tx_phase += 1
|
self._next_tx_phase += 1
|
||||||
self._S.send(phase, plaintext)
|
self._S.send("%d" % phase, plaintext)
|
||||||
|
|
||||||
@m.output()
|
@m.output()
|
||||||
|
def close_error(self, err, orig):
|
||||||
|
self._result = WormholeError(err)
|
||||||
|
self._M.close("errory")
|
||||||
|
@m.output()
|
||||||
def close_scared(self):
|
def close_scared(self):
|
||||||
|
self._result = WrongPasswordError()
|
||||||
self._M.close("scary")
|
self._M.close("scary")
|
||||||
@m.output()
|
@m.output()
|
||||||
def close_lonely(self):
|
def close_lonely(self):
|
||||||
|
self._result = WormholeError("lonely")
|
||||||
self._M.close("lonely")
|
self._M.close("lonely")
|
||||||
@m.output()
|
@m.output()
|
||||||
def close_happy(self):
|
def close_happy(self):
|
||||||
|
self._result = "happy"
|
||||||
self._M.close("happy")
|
self._M.close("happy")
|
||||||
|
|
||||||
@m.output()
|
@m.output()
|
||||||
|
@ -162,6 +186,7 @@ class Boss(object):
|
||||||
self._W.got_verifier(verifier)
|
self._W.got_verifier(verifier)
|
||||||
@m.output()
|
@m.output()
|
||||||
def W_received(self, phase, plaintext):
|
def W_received(self, phase, plaintext):
|
||||||
|
assert isinstance(phase, six.integer_types), type(phase)
|
||||||
# we call Wormhole.received() in strict phase order, with no gaps
|
# we call Wormhole.received() in strict phase order, with no gaps
|
||||||
self._rx_phases[phase] = plaintext
|
self._rx_phases[phase] = plaintext
|
||||||
while self._next_rx_phase in self._rx_phases:
|
while self._next_rx_phase in self._rx_phases:
|
||||||
|
@ -170,27 +195,30 @@ class Boss(object):
|
||||||
|
|
||||||
@m.output()
|
@m.output()
|
||||||
def W_closed(self):
|
def W_closed(self):
|
||||||
result = "???"
|
self._W.closed(self._result)
|
||||||
self._W.closed(result)
|
|
||||||
|
|
||||||
S0_empty.upon(close, enter=S3_closing, outputs=[close_lonely])
|
S0_empty.upon(close, enter=S3_closing, outputs=[close_lonely])
|
||||||
S0_empty.upon(send, enter=S0_empty, outputs=[S_send])
|
S0_empty.upon(send, enter=S0_empty, outputs=[S_send])
|
||||||
S0_empty.upon(rx_welcome, enter=S0_empty, outputs=[process_welcome])
|
S0_empty.upon(rx_welcome, enter=S0_empty, outputs=[process_welcome])
|
||||||
S0_empty.upon(got_code, enter=S1_lonely, outputs=[do_got_code])
|
S0_empty.upon(got_code, enter=S1_lonely, outputs=[do_got_code])
|
||||||
|
S0_empty.upon(rx_error, enter=S3_closing, outputs=[close_error])
|
||||||
S1_lonely.upon(rx_welcome, enter=S1_lonely, outputs=[process_welcome])
|
S1_lonely.upon(rx_welcome, enter=S1_lonely, outputs=[process_welcome])
|
||||||
S1_lonely.upon(happy, enter=S2_happy, outputs=[])
|
S1_lonely.upon(happy, enter=S2_happy, outputs=[])
|
||||||
S1_lonely.upon(scared, enter=S3_closing, outputs=[close_scared])
|
S1_lonely.upon(scared, enter=S3_closing, outputs=[close_scared])
|
||||||
S1_lonely.upon(close, enter=S3_closing, outputs=[close_lonely])
|
S1_lonely.upon(close, enter=S3_closing, outputs=[close_lonely])
|
||||||
S1_lonely.upon(send, enter=S1_lonely, outputs=[S_send])
|
S1_lonely.upon(send, enter=S1_lonely, outputs=[S_send])
|
||||||
S1_lonely.upon(got_verifier, enter=S1_lonely, outputs=[W_got_verifier])
|
S1_lonely.upon(got_verifier, enter=S1_lonely, outputs=[W_got_verifier])
|
||||||
|
S1_lonely.upon(rx_error, enter=S3_closing, outputs=[close_error])
|
||||||
S2_happy.upon(rx_welcome, enter=S2_happy, outputs=[process_welcome])
|
S2_happy.upon(rx_welcome, enter=S2_happy, outputs=[process_welcome])
|
||||||
S2_happy.upon(got_phase, enter=S2_happy, outputs=[W_received])
|
S2_happy.upon(got_phase, enter=S2_happy, outputs=[W_received])
|
||||||
S2_happy.upon(got_version, enter=S2_happy, outputs=[process_version])
|
S2_happy.upon(got_version, enter=S2_happy, outputs=[process_version])
|
||||||
S2_happy.upon(scared, enter=S3_closing, outputs=[close_scared])
|
S2_happy.upon(scared, enter=S3_closing, outputs=[close_scared])
|
||||||
S2_happy.upon(close, enter=S3_closing, outputs=[close_happy])
|
S2_happy.upon(close, enter=S3_closing, outputs=[close_happy])
|
||||||
S2_happy.upon(send, enter=S2_happy, outputs=[S_send])
|
S2_happy.upon(send, enter=S2_happy, outputs=[S_send])
|
||||||
|
S2_happy.upon(rx_error, enter=S3_closing, outputs=[close_error])
|
||||||
|
|
||||||
S3_closing.upon(rx_welcome, enter=S3_closing, outputs=[])
|
S3_closing.upon(rx_welcome, enter=S3_closing, outputs=[])
|
||||||
|
S3_closing.upon(rx_error, enter=S3_closing, outputs=[])
|
||||||
S3_closing.upon(got_phase, enter=S3_closing, outputs=[])
|
S3_closing.upon(got_phase, enter=S3_closing, outputs=[])
|
||||||
S3_closing.upon(got_version, enter=S3_closing, outputs=[])
|
S3_closing.upon(got_version, enter=S3_closing, outputs=[])
|
||||||
S3_closing.upon(happy, enter=S3_closing, outputs=[])
|
S3_closing.upon(happy, enter=S3_closing, outputs=[])
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from __future__ import print_function, absolute_import, unicode_literals
|
from __future__ import print_function, absolute_import, unicode_literals
|
||||||
from hashlib import sha256
|
from hashlib import sha256
|
||||||
|
import six
|
||||||
from zope.interface import implementer
|
from zope.interface import implementer
|
||||||
from attr import attrs, attrib
|
from attr import attrs, attrib
|
||||||
from attr.validators import provides, instance_of
|
from attr.validators import provides, instance_of
|
||||||
|
@ -22,10 +23,10 @@ def HKDF(skm, outlen, salt=None, CTXinfo=b""):
|
||||||
def derive_key(key, purpose, length=SecretBox.KEY_SIZE):
|
def derive_key(key, purpose, length=SecretBox.KEY_SIZE):
|
||||||
if not isinstance(key, type(b"")): raise TypeError(type(key))
|
if not isinstance(key, type(b"")): raise TypeError(type(key))
|
||||||
if not isinstance(purpose, type(b"")): raise TypeError(type(purpose))
|
if not isinstance(purpose, type(b"")): raise TypeError(type(purpose))
|
||||||
if not isinstance(length, int): raise TypeError(type(length))
|
if not isinstance(length, six.integer_types): raise TypeError(type(length))
|
||||||
return HKDF(key, length, CTXinfo=purpose)
|
return HKDF(key, length, CTXinfo=purpose)
|
||||||
|
|
||||||
def derive_phase_key(side, phase):
|
def derive_phase_key(key, side, phase):
|
||||||
assert isinstance(side, type("")), type(side)
|
assert isinstance(side, type("")), type(side)
|
||||||
assert isinstance(phase, type("")), type(phase)
|
assert isinstance(phase, type("")), type(phase)
|
||||||
side_bytes = side.encode("ascii")
|
side_bytes = side.encode("ascii")
|
||||||
|
@ -33,7 +34,7 @@ def derive_phase_key(side, phase):
|
||||||
purpose = (b"wormhole:phase:"
|
purpose = (b"wormhole:phase:"
|
||||||
+ sha256(side_bytes).digest()
|
+ sha256(side_bytes).digest()
|
||||||
+ sha256(phase_bytes).digest())
|
+ sha256(phase_bytes).digest())
|
||||||
return derive_key(purpose)
|
return derive_key(key, purpose)
|
||||||
|
|
||||||
def decrypt_data(key, encrypted):
|
def decrypt_data(key, encrypted):
|
||||||
assert isinstance(key, type(b"")), type(key)
|
assert isinstance(key, type(b"")), type(key)
|
||||||
|
@ -55,6 +56,7 @@ def encrypt_data(key, plaintext):
|
||||||
@implementer(_interfaces.IKey)
|
@implementer(_interfaces.IKey)
|
||||||
class Key(object):
|
class Key(object):
|
||||||
_appid = attrib(validator=instance_of(type(u"")))
|
_appid = attrib(validator=instance_of(type(u"")))
|
||||||
|
_side = attrib(validator=instance_of(type(u"")))
|
||||||
_timing = attrib(validator=provides(_interfaces.ITiming))
|
_timing = attrib(validator=provides(_interfaces.ITiming))
|
||||||
m = MethodicalMachine()
|
m = MethodicalMachine()
|
||||||
|
|
||||||
|
@ -106,9 +108,13 @@ class Key(object):
|
||||||
assert isinstance(msg2, type(b""))
|
assert isinstance(msg2, type(b""))
|
||||||
with self._timing.add("pake2", waiting="crypto"):
|
with self._timing.add("pake2", waiting="crypto"):
|
||||||
key = self._sp.finish(msg2)
|
key = self._sp.finish(msg2)
|
||||||
self._my_versions = {}
|
|
||||||
self._M.add_message("version", self._my_versions)
|
|
||||||
self._B.got_verifier(derive_key(key, b"wormhole:verifier"))
|
self._B.got_verifier(derive_key(key, b"wormhole:verifier"))
|
||||||
|
phase = "version"
|
||||||
|
data_key = derive_phase_key(key, self._side, phase)
|
||||||
|
my_versions = {} # TODO: get from Wormhole?
|
||||||
|
plaintext = dict_to_bytes(my_versions)
|
||||||
|
encrypted = encrypt_data(data_key, plaintext)
|
||||||
|
self._M.add_message(phase, encrypted)
|
||||||
self._R.got_key(key)
|
self._R.got_key(key)
|
||||||
|
|
||||||
S0_know_nothing.upon(got_code, enter=S1_know_code, outputs=[build_pake])
|
S0_know_nothing.upon(got_code, enter=S1_know_code, outputs=[build_pake])
|
||||||
|
|
|
@ -121,7 +121,10 @@ class Mailbox(object):
|
||||||
|
|
||||||
# from Send or Key
|
# from Send or Key
|
||||||
@m.input()
|
@m.input()
|
||||||
def add_message(self, phase, body): pass
|
def add_message(self, phase, body):
|
||||||
|
assert isinstance(body, type(b"")), type(body)
|
||||||
|
#print("ADD_MESSAGE", phase, len(body))
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
@m.output()
|
@m.output()
|
||||||
|
@ -142,7 +145,7 @@ class Mailbox(object):
|
||||||
@m.output()
|
@m.output()
|
||||||
def queue(self, phase, body):
|
def queue(self, phase, body):
|
||||||
assert isinstance(phase, type("")), type(phase)
|
assert isinstance(phase, type("")), type(phase)
|
||||||
assert isinstance(body, type(b"")), type(body)
|
assert isinstance(body, type(b"")), (type(body), phase, body)
|
||||||
self._pending_outbound[phase] = body
|
self._pending_outbound[phase] = body
|
||||||
@m.output()
|
@m.output()
|
||||||
def store_mailbox_and_RC_tx_open_and_drain(self, mailbox):
|
def store_mailbox_and_RC_tx_open_and_drain(self, mailbox):
|
||||||
|
@ -189,11 +192,11 @@ class Mailbox(object):
|
||||||
self._accept(phase, body)
|
self._accept(phase, body)
|
||||||
def _accept(self, phase, body):
|
def _accept(self, phase, body):
|
||||||
if phase not in self._processed:
|
if phase not in self._processed:
|
||||||
self._O.got_message(phase, body)
|
|
||||||
self._processed.add(phase)
|
self._processed.add(phase)
|
||||||
|
self._O.got_message(phase, body)
|
||||||
@m.output()
|
@m.output()
|
||||||
def dequeue(self, phase, body):
|
def dequeue(self, phase, body):
|
||||||
self._pending_outbound.pop(phase)
|
self._pending_outbound.pop(phase, None)
|
||||||
@m.output()
|
@m.output()
|
||||||
def record_mood(self, mood):
|
def record_mood(self, mood):
|
||||||
self._mood = mood
|
self._mood = mood
|
||||||
|
@ -235,8 +238,7 @@ class Mailbox(object):
|
||||||
S3B.upon(rx_claimed, enter=S3B, outputs=[])
|
S3B.upon(rx_claimed, enter=S3B, outputs=[])
|
||||||
S3B.upon(add_message, enter=S3B, outputs=[queue, RC_tx_add])
|
S3B.upon(add_message, enter=S3B, outputs=[queue, RC_tx_add])
|
||||||
|
|
||||||
S4A.upon(connected, enter=S4B,
|
S4A.upon(connected, enter=S4B, outputs=[RC_tx_open, drain, RC_tx_release])
|
||||||
outputs=[RC_tx_open, drain, RC_tx_release])
|
|
||||||
S4A.upon(add_message, enter=S4A, outputs=[queue])
|
S4A.upon(add_message, enter=S4A, outputs=[queue])
|
||||||
S4B.upon(lost, enter=S4A, outputs=[])
|
S4B.upon(lost, enter=S4A, outputs=[])
|
||||||
S4B.upon(add_message, enter=S4B, outputs=[queue, RC_tx_add])
|
S4B.upon(add_message, enter=S4B, outputs=[queue, RC_tx_add])
|
||||||
|
|
|
@ -25,6 +25,7 @@ class Order(object):
|
||||||
def S1_yes_pake(self): pass
|
def S1_yes_pake(self): pass
|
||||||
|
|
||||||
def got_message(self, phase, body):
|
def got_message(self, phase, body):
|
||||||
|
#print("ORDER[%s].got_message(%s)" % (self._side, phase))
|
||||||
assert isinstance(phase, type("")), type(phase)
|
assert isinstance(phase, type("")), type(phase)
|
||||||
assert isinstance(body, type(b"")), type(body)
|
assert isinstance(body, type(b"")), type(body)
|
||||||
if phase == "pake":
|
if phase == "pake":
|
||||||
|
|
|
@ -35,7 +35,7 @@ class Receive(object):
|
||||||
assert isinstance(phase, type("")), type(phase)
|
assert isinstance(phase, type("")), type(phase)
|
||||||
assert isinstance(body, type(b"")), type(body)
|
assert isinstance(body, type(b"")), type(body)
|
||||||
assert self._key
|
assert self._key
|
||||||
data_key = derive_phase_key(self._side, phase)
|
data_key = derive_phase_key(self._key, self._side, phase)
|
||||||
try:
|
try:
|
||||||
plaintext = decrypt_data(data_key, body)
|
plaintext = decrypt_data(data_key, body)
|
||||||
except CryptoError:
|
except CryptoError:
|
||||||
|
|
|
@ -26,12 +26,12 @@ class WSClient(websocket.WebSocketClientProtocol):
|
||||||
self._RC.ws_open(self)
|
self._RC.ws_open(self)
|
||||||
|
|
||||||
def onMessage(self, payload, isBinary):
|
def onMessage(self, payload, isBinary):
|
||||||
#print("onMessage")
|
|
||||||
assert not isBinary
|
assert not isBinary
|
||||||
try:
|
try:
|
||||||
self._RC.ws_message(payload)
|
self._RC.ws_message(payload)
|
||||||
except:
|
except:
|
||||||
print("LOGGING")
|
from twisted.python.failure import Failure
|
||||||
|
print("LOGGING", Failure())
|
||||||
log.err()
|
log.err()
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
@ -57,6 +57,10 @@ class WSFactory(websocket.WebSocketClientFactory):
|
||||||
#proto.wormhole_open = False
|
#proto.wormhole_open = False
|
||||||
return proto
|
return proto
|
||||||
|
|
||||||
|
def dmsg(side, text):
|
||||||
|
offset = int(side, 16) % 20
|
||||||
|
print(" "*offset, text)
|
||||||
|
|
||||||
@attrs
|
@attrs
|
||||||
@implementer(_interfaces.IRendezvousConnector)
|
@implementer(_interfaces.IRendezvousConnector)
|
||||||
class RendezvousConnector(object):
|
class RendezvousConnector(object):
|
||||||
|
@ -66,7 +70,7 @@ class RendezvousConnector(object):
|
||||||
_reactor = attrib()
|
_reactor = attrib()
|
||||||
_journal = attrib(validator=provides(_interfaces.IJournal))
|
_journal = attrib(validator=provides(_interfaces.IJournal))
|
||||||
_timing = attrib(validator=provides(_interfaces.ITiming))
|
_timing = attrib(validator=provides(_interfaces.ITiming))
|
||||||
DEBUG = False
|
DEBUG = True
|
||||||
|
|
||||||
def __attrs_post_init__(self):
|
def __attrs_post_init__(self):
|
||||||
self._ws = None
|
self._ws = None
|
||||||
|
@ -124,6 +128,8 @@ class RendezvousConnector(object):
|
||||||
|
|
||||||
# from our WSClient (the WebSocket protocol)
|
# from our WSClient (the WebSocket protocol)
|
||||||
def ws_open(self, proto):
|
def ws_open(self, proto):
|
||||||
|
if self.DEBUG:
|
||||||
|
dmsg(self._side, "R.connected")
|
||||||
self._ws = proto
|
self._ws = proto
|
||||||
self._tx("bind", appid=self._appid, side=self._side)
|
self._tx("bind", appid=self._appid, side=self._side)
|
||||||
self._C.connected()
|
self._C.connected()
|
||||||
|
@ -132,7 +138,11 @@ class RendezvousConnector(object):
|
||||||
|
|
||||||
def ws_message(self, payload):
|
def ws_message(self, payload):
|
||||||
msg = bytes_to_dict(payload)
|
msg = bytes_to_dict(payload)
|
||||||
if self.DEBUG and msg["type"]!="ack": print("DIS", msg["type"], msg)
|
if self.DEBUG and msg["type"]!="ack":
|
||||||
|
dmsg(self._side, "R.rx(%s %s%s)" %
|
||||||
|
(msg["type"], msg.get("phase",""),
|
||||||
|
"[mine]" if msg.get("side","") == self._side else "",
|
||||||
|
))
|
||||||
self._timing.add("ws_receive", _side=self._side, message=msg)
|
self._timing.add("ws_receive", _side=self._side, message=msg)
|
||||||
mtype = msg["type"]
|
mtype = msg["type"]
|
||||||
meth = getattr(self, "_response_handle_"+mtype, None)
|
meth = getattr(self, "_response_handle_"+mtype, None)
|
||||||
|
@ -143,6 +153,8 @@ class RendezvousConnector(object):
|
||||||
return meth(msg)
|
return meth(msg)
|
||||||
|
|
||||||
def ws_close(self, wasClean, code, reason):
|
def ws_close(self, wasClean, code, reason):
|
||||||
|
if self.DEBUG:
|
||||||
|
dmsg(self._side, "R.lost")
|
||||||
self._ws = None
|
self._ws = None
|
||||||
self._C.lost()
|
self._C.lost()
|
||||||
self._M.lost()
|
self._M.lost()
|
||||||
|
@ -158,9 +170,10 @@ class RendezvousConnector(object):
|
||||||
# their receives, and vice versa. They are also correlated with the
|
# their receives, and vice versa. They are also correlated with the
|
||||||
# ACKs we get back from the server (which we otherwise ignore). There
|
# ACKs we get back from the server (which we otherwise ignore). There
|
||||||
# are so few messages, 16 bits is enough to be mostly-unique.
|
# are so few messages, 16 bits is enough to be mostly-unique.
|
||||||
if self.DEBUG: print("SEND", mtype)
|
|
||||||
kwargs["id"] = bytes_to_hexstr(os.urandom(2))
|
kwargs["id"] = bytes_to_hexstr(os.urandom(2))
|
||||||
kwargs["type"] = mtype
|
kwargs["type"] = mtype
|
||||||
|
if self.DEBUG:
|
||||||
|
dmsg(self._side, "R.tx(%s %s)" % (mtype.upper(), kwargs.get("phase", "")))
|
||||||
payload = dict_to_bytes(kwargs)
|
payload = dict_to_bytes(kwargs)
|
||||||
self._timing.add("ws_send", _side=self._side, **kwargs)
|
self._timing.add("ws_send", _side=self._side, **kwargs)
|
||||||
self._ws.sendMessage(payload, False)
|
self._ws.sendMessage(payload, False)
|
||||||
|
@ -184,6 +197,11 @@ class RendezvousConnector(object):
|
||||||
def _response_handle_ack(self, msg):
|
def _response_handle_ack(self, msg):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def _response_handle_error(self, msg):
|
||||||
|
err = msg["error"]
|
||||||
|
orig = msg["orig"]
|
||||||
|
self._B.rx_error(err, orig)
|
||||||
|
|
||||||
def _response_handle_welcome(self, msg):
|
def _response_handle_welcome(self, msg):
|
||||||
self._B.rx_welcome(msg["welcome"])
|
self._B.rx_welcome(msg["welcome"])
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,9 @@ class Send(object):
|
||||||
_timing = attrib(validator=provides(_interfaces.ITiming))
|
_timing = attrib(validator=provides(_interfaces.ITiming))
|
||||||
m = MethodicalMachine()
|
m = MethodicalMachine()
|
||||||
|
|
||||||
|
def __attrs_post_init__(self):
|
||||||
|
self._queue = []
|
||||||
|
|
||||||
def wire(self, mailbox):
|
def wire(self, mailbox):
|
||||||
self._M = _interfaces.IMailbox(mailbox)
|
self._M = _interfaces.IMailbox(mailbox)
|
||||||
|
|
||||||
|
@ -49,7 +52,8 @@ class Send(object):
|
||||||
self._encrypt_and_send(phase, plaintext)
|
self._encrypt_and_send(phase, plaintext)
|
||||||
|
|
||||||
def _encrypt_and_send(self, phase, plaintext):
|
def _encrypt_and_send(self, phase, plaintext):
|
||||||
data_key = derive_phase_key(self._side, phase)
|
assert self._key
|
||||||
|
data_key = derive_phase_key(self._key, self._side, phase)
|
||||||
encrypted = encrypt_data(data_key, plaintext)
|
encrypted = encrypt_data(data_key, plaintext)
|
||||||
self._M.add_message(phase, encrypted)
|
self._M.add_message(phase, encrypted)
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ from ._interfaces import IWormhole
|
||||||
from .util import bytes_to_hexstr
|
from .util import bytes_to_hexstr
|
||||||
from .timing import DebugTiming
|
from .timing import DebugTiming
|
||||||
from .journal import ImmediateJournal
|
from .journal import ImmediateJournal
|
||||||
from ._boss import Boss
|
from ._boss import Boss, WormholeError
|
||||||
|
|
||||||
# We can provide different APIs to different apps:
|
# We can provide different APIs to different apps:
|
||||||
# * Deferreds
|
# * Deferreds
|
||||||
|
@ -61,6 +61,9 @@ class _DelegatedWormhole(object):
|
||||||
def closed(self, result):
|
def closed(self, result):
|
||||||
self._delegate.wormhole_closed(result)
|
self._delegate.wormhole_closed(result)
|
||||||
|
|
||||||
|
class WormholeClosed(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
@implementer(IWormhole)
|
@implementer(IWormhole)
|
||||||
class _DeferredWormhole(object):
|
class _DeferredWormhole(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -70,6 +73,7 @@ class _DeferredWormhole(object):
|
||||||
self._verifier_observers = []
|
self._verifier_observers = []
|
||||||
self._received_data = []
|
self._received_data = []
|
||||||
self._received_observers = []
|
self._received_observers = []
|
||||||
|
self._closed_observers = []
|
||||||
|
|
||||||
def _set_boss(self, boss):
|
def _set_boss(self, boss):
|
||||||
self._boss = boss
|
self._boss = boss
|
||||||
|
@ -107,6 +111,9 @@ class _DeferredWormhole(object):
|
||||||
self._boss.send(plaintext)
|
self._boss.send(plaintext)
|
||||||
def close(self):
|
def close(self):
|
||||||
self._boss.close()
|
self._boss.close()
|
||||||
|
d = defer.Deferred()
|
||||||
|
self._closed_observers.append(d)
|
||||||
|
return d
|
||||||
|
|
||||||
# from below
|
# from below
|
||||||
def got_code(self, code):
|
def got_code(self, code):
|
||||||
|
@ -127,7 +134,17 @@ class _DeferredWormhole(object):
|
||||||
self._received_data.append(plaintext)
|
self._received_data.append(plaintext)
|
||||||
|
|
||||||
def closed(self, result):
|
def closed(self, result):
|
||||||
print("closed", result)
|
print("closed", result, type(result))
|
||||||
|
if isinstance(result, WormholeError):
|
||||||
|
e = result
|
||||||
|
else:
|
||||||
|
e = WormholeClosed(result)
|
||||||
|
for d in self._verifier_observers:
|
||||||
|
d.errback(e)
|
||||||
|
for d in self._received_observers:
|
||||||
|
d.errback(e)
|
||||||
|
for d in self._closed_observers:
|
||||||
|
d.callback(result)
|
||||||
|
|
||||||
def _wormhole(appid, relay_url, reactor, delegate=None,
|
def _wormhole(appid, relay_url, reactor, delegate=None,
|
||||||
tor_manager=None, timing=None,
|
tor_manager=None, timing=None,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user