clean up wordlist handling

This commit is contained in:
Brian Warner 2017-03-18 00:50:37 +01:00
parent ae95948c17
commit 175fef2ab4
13 changed files with 317 additions and 202 deletions

View File

@ -7,8 +7,8 @@ digraph {
S0A -> S0B [label="connected"] S0A -> S0B [label="connected"]
S0B -> S0A [label="lost"] S0B -> S0A [label="lost"]
S0B [label="S0B:\nidle\nconnected"] S0B [label="S0B:\nidle\nconnected"]
S0A -> S1A [label="allocate" color="orange"] S0A -> S1A [label="allocate(length, wordlist)" color="orange"]
S0B -> P_allocate [label="allocate"] S0B -> P_allocate [label="allocate(length, wordlist)"]
P_allocate [shape="box" label="RC.tx_allocate" color="orange"] P_allocate [shape="box" label="RC.tx_allocate" color="orange"]
P_allocate -> S1B [color="orange"] P_allocate -> S1B [color="orange"]
{rank=same; S1A P_allocate S1B} {rank=same; S1A P_allocate S1B}
@ -21,7 +21,7 @@ digraph {
S1A -> P_allocate [label="connected" color="orange"] S1A -> P_allocate [label="connected" color="orange"]
S1B -> P_allocated [label="rx_allocated" color="orange"] S1B -> P_allocated [label="rx_allocated" color="orange"]
P_allocated [shape="box" label="C.allocated_nameplate" color="orange"] P_allocated [shape="box" label="choose words\nC.allocated(nameplate,code)" color="orange"]
P_allocated -> S2 [color="orange"] P_allocated -> S2 [color="orange"]
S2 [label="S2:\ndone" color="orange"] S2 [label="S2:\ndone" color="orange"]

View File

@ -4,36 +4,31 @@ digraph {
{rank=same; start S0} {rank=same; start S0}
start -> S0 [style="invis"] start -> S0 [style="invis"]
S0 [label="S0:\nidle"] S0 [label="S0:\nidle"]
S0 -> P0_got_code [label="set_code"] S0 -> P0_got_code [label="set_code\n(code)"]
P0_got_code [shape="box" label="N.set_nameplate"] P0_got_code [shape="box" label="N.set_nameplate"]
P0_got_code -> P_done P0_got_code -> P_done
P_done [shape="box" label="K.got_code\nB.got_code"] P_done [shape="box" label="K.got_code\nB.got_code"]
P_done -> S5 P_done -> S4
S5 [label="S5: known" color="green"] S4 [label="S4: known" color="green"]
{rank=same; S1_inputting_nameplate S3_allocating} {rank=same; S1_inputting_nameplate S3_allocating}
{rank=same; P0_got_code P1_set_nameplate P3_got_nameplate} {rank=same; P0_got_code P1_set_nameplate P3_got_nameplate}
S0 -> P_input [label="input_code"] S0 -> P_input [label="input_code"]
P_input [shape="box" label="I.start"] P_input [shape="box" label="I.start\n(helper)"]
P_input -> S1_inputting_nameplate P_input -> S1_inputting_nameplate
S1_inputting_nameplate [label="S1:\ninputting\nnameplate"] S1_inputting_nameplate [label="S1:\ninputting\nnameplate"]
S1_inputting_nameplate -> P1_set_nameplate [label="got_nameplate"] S1_inputting_nameplate -> P1_set_nameplate [label="got_nameplate\n(nameplate)"]
P1_set_nameplate [shape="box" label="N.set_nameplate"] P1_set_nameplate [shape="box" label="N.set_nameplate"]
P1_set_nameplate -> S2_inputting_words P1_set_nameplate -> S2_inputting_words
S2_inputting_words [label="S2:\ninputting\nwords"] S2_inputting_words [label="S2:\ninputting\nwords"]
S2_inputting_words -> P1_got_words [label="finished_input"] S2_inputting_words -> P_done [label="finished_input\n(code)"]
P1_got_words [shape="box" label="assemble\ncode"]
P1_got_words -> P_done
P_done
S0 -> P_allocate [label="allocate_code"] S0 -> P_allocate [label="allocate_code\n(length,\nwordlist)"]
P_allocate [shape="box" label="A.allocate"] P_allocate [shape="box" label="A.allocate\n(length, wordlist)"]
P_allocate -> S3_allocating P_allocate -> S3_allocating
S3_allocating [label="S3:\nallocating"] S3_allocating [label="S3:\nallocating"]
S3_allocating -> P3_got_nameplate [label="allocated_nameplate"] S3_allocating -> P3_got_nameplate [label="allocated\n(nameplate,\ncode)"]
P3_got_nameplate [shape="box" label="N.set_nameplate"] P3_got_nameplate [shape="box" label="N.set_nameplate"]
P3_got_nameplate -> P3_generate P3_got_nameplate -> P_done
P3_generate [shape="box" label="append\nrandom words"]
P3_generate -> P_done
} }

View File

@ -31,7 +31,7 @@ digraph {
S3 [label="S3: typing\ncode\n(yes wordlist)"] S3 [label="S3: typing\ncode\n(yes wordlist)"]
S3 -> S3 [label="got_nameplates"] S3 -> S3 [label="got_nameplates"]
S3 -> P_done [label="choose_words" color="orange" fontcolor="orange"] S3 -> P_done [label="choose_words" color="orange" fontcolor="orange"]
P_done [shape="box" label="build code\nC.finished_input"] P_done [shape="box" label="build code\nC.finished_input(code)"]
P_done -> S4 P_done -> S4
S4 [label="S4: done" color="green"] S4 [label="S4: done" color="green"]
S4 -> S4 [label="got_nameplates\ngot_wordlist"] S4 -> S4 [label="got_nameplates\ngot_wordlist"]

View File

@ -61,6 +61,7 @@ digraph {
label="connected\nlost\nrx_claimed\nrx_released"] label="connected\nlost\nrx_claimed\nrx_released"]
Mailbox -> Nameplate [style="dashed" label="release"] Mailbox -> Nameplate [style="dashed" label="release"]
Nameplate -> Mailbox [style="dashed" label="got_mailbox"] Nameplate -> Mailbox [style="dashed" label="got_mailbox"]
Nameplate -> Input [style="dashed" label="got_wordlist"]
Mailbox -> Connection [style="dashed" color="red" fontcolor="red" Mailbox -> Connection [style="dashed" color="red" fontcolor="red"
label="tx_open\ntx_add\ntx_close" label="tx_open\ntx_add\ntx_close"

View File

@ -38,7 +38,7 @@ digraph {
S2A -> S3A [label="(none)" style="invis"] S2A -> S3A [label="(none)" style="invis"]
S2B -> P_open [label="rx_claimed" color="orange" fontcolor="orange"] S2B -> P_open [label="rx_claimed" color="orange" fontcolor="orange"]
P_open [shape="box" label="M.got_mailbox" color="orange"] P_open [shape="box" label="I.got_wordlist\nM.got_mailbox" color="orange"]
P_open -> S3B [color="orange"] P_open -> S3B [color="orange"]
subgraph {rank=same; S3A S3B} subgraph {rank=same; S3A S3B}

View File

@ -30,7 +30,7 @@ class Allocator(object):
# from Code # from Code
@m.input() @m.input()
def allocate(self): pass def allocate(self, length, wordlist): pass
# from RendezvousConnector # from RendezvousConnector
@m.input() @m.input()
@ -40,26 +40,37 @@ class Allocator(object):
@m.input() @m.input()
def rx_allocated(self, nameplate): pass def rx_allocated(self, nameplate): pass
@m.output()
def stash(self, length, wordlist):
self._length = length
self._wordlist = _interfaces.IWordlist(wordlist)
@m.output()
def stash_and_RC_rx_allocate(self, length, wordlist):
self._length = length
self._wordlist = _interfaces.IWordlist(wordlist)
self._RC.tx_allocate()
@m.output() @m.output()
def RC_tx_allocate(self): def RC_tx_allocate(self):
self._RC.tx_allocate() self._RC.tx_allocate()
@m.output() @m.output()
def C_allocated_nameplate(self, nameplate): def build_and_notify(self, nameplate):
self._C.allocated_nameplate(nameplate) words = self._wordlist.choose_words(self._length)
code = nameplate + "-" + words
self._C.allocated(nameplate, code)
S0A_idle.upon(connected, enter=S0B_idle_connected, outputs=[]) S0A_idle.upon(connected, enter=S0B_idle_connected, outputs=[])
S0B_idle_connected.upon(lost, enter=S0A_idle, outputs=[]) S0B_idle_connected.upon(lost, enter=S0A_idle, outputs=[])
S0A_idle.upon(allocate, enter=S1A_allocating, outputs=[]) S0A_idle.upon(allocate, enter=S1A_allocating, outputs=[stash])
S0B_idle_connected.upon(allocate, enter=S1B_allocating_connected, S0B_idle_connected.upon(allocate, enter=S1B_allocating_connected,
outputs=[RC_tx_allocate]) outputs=[stash_and_RC_rx_allocate])
S1A_allocating.upon(connected, enter=S1B_allocating_connected, S1A_allocating.upon(connected, enter=S1B_allocating_connected,
outputs=[RC_tx_allocate]) outputs=[RC_tx_allocate])
S1B_allocating_connected.upon(lost, enter=S1A_allocating, outputs=[]) S1B_allocating_connected.upon(lost, enter=S1A_allocating, outputs=[])
S1B_allocating_connected.upon(rx_allocated, enter=S2_done, S1B_allocating_connected.upon(rx_allocated, enter=S2_done,
outputs=[C_allocated_nameplate]) outputs=[build_and_notify])
S2_done.upon(connected, enter=S2_done, outputs=[]) S2_done.upon(connected, enter=S2_done, outputs=[])
S2_done.upon(lost, enter=S2_done, outputs=[]) S2_done.upon(lost, enter=S2_done, outputs=[])

View File

@ -19,6 +19,7 @@ from ._allocator import Allocator
from ._input import Input from ._input import Input
from ._code import Code from ._code import Code
from ._terminator import Terminator from ._terminator import Terminator
from ._wordlist import PGPWordList
from .errors import (ServerError, LonelyError, WrongPasswordError, from .errors import (ServerError, LonelyError, WrongPasswordError,
KeyFormatError, OnlyOneCodeError) KeyFormatError, OnlyOneCodeError)
from .util import bytes_to_dict from .util import bytes_to_dict
@ -50,13 +51,13 @@ class Boss(object):
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,
self._tor_manager, self._timing) self._tor_manager, self._timing)
self._L = Lister() self._L = Lister(self._timing)
self._A = Allocator(self._timing) self._A = Allocator(self._timing)
self._I = Input(self._timing) self._I = Input(self._timing)
self._C = Code(self._timing) self._C = Code(self._timing)
self._T = Terminator() self._T = Terminator()
self._N.wire(self._M, self._RC, self._T) self._N.wire(self._M, self._I, self._RC, self._T)
self._M.wire(self._N, self._RC, self._O, self._T) self._M.wire(self._N, self._RC, self._O, self._T)
self._S.wire(self._M) self._S.wire(self._M)
self._O.wire(self._K, self._R) self._O.wire(self._K, self._R)
@ -129,7 +130,8 @@ class Boss(object):
if self._did_start_code: if self._did_start_code:
raise OnlyOneCodeError() raise OnlyOneCodeError()
self._did_start_code = True self._did_start_code = True
self._C.allocate_code(code_length) wl = PGPWordList()
self._C.allocate_code(code_length, wl)
def set_code(self, code): def set_code(self, code):
if ' ' in code: if ' ' in code:
raise KeyFormatError("code (%s) contains spaces." % code) raise KeyFormatError("code (%s) contains spaces." % code)

View File

@ -1,24 +1,9 @@
from __future__ import print_function, absolute_import, unicode_literals from __future__ import print_function, absolute_import, unicode_literals
import os
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 from attr.validators import provides
from automat import MethodicalMachine from automat import MethodicalMachine
from . import _interfaces 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))
@attrs @attrs
@implementer(_interfaces.ICode) @implementer(_interfaces.ICode)
@ -28,7 +13,7 @@ class Code(object):
@m.setTrace() @m.setTrace()
def set_trace(): pass # pragma: no cover def set_trace(): pass # pragma: no cover
def wire(self, boss, rendezvous_connector, lister): def wire(self, boss, allocator, nameplate, key, input):
self._B = _interfaces.IBoss(boss) self._B = _interfaces.IBoss(boss)
self._A = _interfaces.IAllocator(allocator) self._A = _interfaces.IAllocator(allocator)
self._N = _interfaces.INameplate(nameplate) self._N = _interfaces.INameplate(nameplate)
@ -36,37 +21,27 @@ class Code(object):
self._I = _interfaces.IInput(input) self._I = _interfaces.IInput(input)
@m.state(initial=True) @m.state(initial=True)
def S0A_unknown(self): pass # pragma: no cover def S0_idle(self): pass # pragma: no cover
@m.state() @m.state()
def S0B_unknown_connected(self): pass # pragma: no cover def S1_inputting_nameplate(self): pass # pragma: no cover
@m.state() @m.state()
def S1A_connecting(self): pass # pragma: no cover def S2_inputting_words(self): pass # pragma: no cover
@m.state() @m.state()
def S1B_allocating(self): pass # pragma: no cover def S3_allocating(self): pass # pragma: no cover
@m.state() @m.state()
def S2_input_nameplate(self): pass # pragma: no cover def S4_known(self): pass # pragma: no cover
@m.state()
def S3_input_code_no_wordlist(self): pass # pragma: no cover
@m.state()
def S4_input_code_wordlist(self): pass # pragma: no cover
@m.state()
def S5_known(self): pass # pragma: no cover
# from App # from App
@m.input() @m.input()
def allocate_code(self, code_length): pass def allocate_code(self, length, wordlist): pass
@m.input() @m.input()
def input_code(self, input_helper): pass def input_code(self, input_helper): pass
@m.input() @m.input()
def set_code(self, code): pass def set_code(self, code): pass
# from Lister # from Allocator
@m.input() @m.input()
def got_nameplates(self, nameplates): pass def allocated(self, nameplate, code): pass
# from Nameplate
@m.input()
def got_wordlist(self, wordlist): pass
# from Input # from Input
@m.input() @m.input()
@ -75,109 +50,38 @@ class Code(object):
def finished_input(self, code): pass def finished_input(self, code): pass
@m.output() @m.output()
def L_refresh_nameplates(self): def do_set_code(self, code):
self._L.refresh_nameplates() nameplate = code.split("-", 2)[0]
@m.output()
def start_input_and_L_refresh_nameplates(self, input_helper):
self._input_helper = input_helper
self._L.refresh_nameplates()
@m.output()
def stash_code_length_and_RC_tx_allocate(self, code_length):
self._code_length = code_length
self._RC.tx_allocate()
@m.output()
def stash_code_length(self, code_length):
self._code_length = code_length
@m.output()
def RC_tx_allocate(self):
self._RC.tx_allocate()
@m.output()
def stash_wordlist(self, wordlist):
# TODO
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 record_nameplate(self, nameplate):
self._nameplate = nameplate
@m.output()
def N_set_nameplate(self, nameplate):
self._N.set_nameplate(nameplate) self._N.set_nameplate(nameplate)
self._K.got_code(code)
self._B.got_code(code)
@m.output() @m.output()
def generate_and_B_got_code(self, nameplate): def do_start_input(self, input_helper):
self._code = make_code(nameplate, self._code_length) self._I.start(input_helper)
self._B_got_code() @m.output()
def do_middle_input(self, nameplate):
self._N.set_nameplate(nameplate)
@m.output()
def do_finish_input(self, code):
self._K.got_code(code)
self._B.got_code(code)
@m.output() @m.output()
def submit_words_and_B_got_code(self, words): def do_start_allocate(self, length, wordlist):
assert self._nameplate self._A.allocate(length, wordlist)
self._code = self._nameplate + "-" + words
self._B_got_code()
@m.output() @m.output()
def B_got_code(self, code): def do_finish_allocate(self, nameplate, code):
self._code = code self._N.set_nameplate(nameplate)
self._B_got_code() self._K.got_code(code)
self._B.got_code(code)
def _B_got_code(self): S0_idle.upon(set_code, enter=S4_known, outputs=[do_set_code])
self._N.set_nameplate(nameplate) XXX S0_idle.upon(input_code, enter=S1_inputting_nameplate,
self._B.got_code(self._code) outputs=[do_start_input])
S1_inputting_nameplate.upon(got_nameplate, enter=S2_inputting_words,
S0A_unknown.upon(connected, enter=S0B_unknown_connected, outputs=[]) outputs=[do_middle_input])
S0B_unknown_connected.upon(lost, enter=S0A_unknown, outputs=[]) S2_inputting_words.upon(finished_input, enter=S4_known,
outputs=[do_finish_input])
S0A_unknown.upon(set_code, enter=S5_known, outputs=[B_got_code]) S0_idle.upon(allocate_code, enter=S3_allocating, outputs=[do_start_allocate])
S0B_unknown_connected.upon(set_code, enter=S5_known, outputs=[B_got_code]) S3_allocating.upon(allocated, enter=S4_known, outputs=[do_finish_allocate])
S0A_unknown.upon(allocate_code, enter=S1A_connecting,
outputs=[stash_code_length])
S0B_unknown_connected.upon(allocate_code, enter=S1B_allocating,
outputs=[stash_code_length_and_RC_tx_allocate])
S1A_connecting.upon(connected, enter=S1B_allocating,
outputs=[RC_tx_allocate])
S1B_allocating.upon(lost, enter=S1A_connecting, outputs=[])
S1B_allocating.upon(rx_allocated, enter=S5_known,
outputs=[generate_and_B_got_code])
S0A_unknown.upon(input_code, enter=S2_input_nameplate,
outputs=[start_input_and_L_refresh_nameplates])
S0B_unknown_connected.upon(input_code, enter=S2_input_nameplate,
outputs=[start_input_and_L_refresh_nameplates])
S2_input_nameplate.upon(update_nameplates, enter=S2_input_nameplate,
outputs=[L_refresh_nameplates])
S2_input_nameplate.upon(got_nameplates,
enter=S2_input_nameplate,
outputs=[stash_nameplates])
S2_input_nameplate.upon(claim_nameplate, enter=S3_input_code_no_wordlist,
outputs=[record_nameplate, N_set_nameplate])
S2_input_nameplate.upon(connected, enter=S2_input_nameplate, outputs=[])
S2_input_nameplate.upon(lost, enter=S2_input_nameplate, outputs=[])
S3_input_code_no_wordlist.upon(got_wordlist,
enter=S4_input_code_wordlist,
outputs=[stash_wordlist])
S3_input_code_no_wordlist.upon(submit_words, enter=S5_known,
outputs=[submit_words_and_B_got_code])
S3_input_code_no_wordlist.upon(connected, enter=S3_input_code_no_wordlist,
outputs=[])
S3_input_code_no_wordlist.upon(lost, enter=S3_input_code_no_wordlist,
outputs=[])
S4_input_code_wordlist.upon(submit_words, enter=S5_known,
outputs=[submit_words_and_B_got_code])
S4_input_code_wordlist.upon(connected, enter=S4_input_code_wordlist,
outputs=[])
S4_input_code_wordlist.upon(lost, enter=S4_input_code_wordlist,
outputs=[])
S5_known.upon(connected, enter=S5_known, outputs=[])
S5_known.upon(lost, enter=S5_known, outputs=[])

View File

@ -16,7 +16,6 @@ class Input(object):
def __attrs_post_init__(self): def __attrs_post_init__(self):
self._nameplate = None self._nameplate = None
self._wordlist = None self._wordlist = None
self._claimed_waiter = None
def wire(self, code, lister): def wire(self, code, lister):
self._C = _interfaces.ICode(code) self._C = _interfaces.ICode(code)
@ -25,23 +24,23 @@ class Input(object):
@m.state(initial=True) @m.state(initial=True)
def S0_idle(self): pass # pragma: no cover def S0_idle(self): pass # pragma: no cover
@m.state() @m.state()
def S1_nameplate(self): pass # pragma: no cover def S1_typing_nameplate(self): pass # pragma: no cover
@m.state() @m.state()
def S2_code_no_wordlist(self): pass # pragma: no cover def S2_typing_code_no_wordlist(self): pass # pragma: no cover
@m.state() @m.state()
def S3_code_yes_wordlist(self): pass # pragma: no cover def S3_typing_code_yes_wordlist(self): pass # pragma: no cover
@m.state(terminal=True) @m.state(terminal=True)
def S4_done(self): pass # pragma: no cover def S4_done(self): pass # pragma: no cover
# from Code # from Code
@m.input() @m.input()
def start(self): pass def start(self, input_helper): pass
# from Lister # from Lister
@m.input() @m.input()
def got_nameplates(self, nameplates): pass def got_nameplates(self, nameplates): pass
# from Nameplate?? # from Nameplate
@m.input() @m.input()
def got_wordlist(self, wordlist): pass def got_wordlist(self, wordlist): pass
@ -49,58 +48,62 @@ class Input(object):
@m.input() @m.input()
def refresh_nameplates(self): pass def refresh_nameplates(self): pass
@m.input() @m.input()
def _choose_nameplate(self, nameplate): pass def choose_nameplate(self, nameplate): pass
@m.input() @m.input()
def choose_words(self, words): pass def choose_words(self, words): pass
@m.output() @m.output()
def L_refresh_nameplates(self): def do_start(self, input_helper):
self._L.refresh_nameplates()
@m.output()
def start_and_L_refresh_nameplates(self, input_helper):
self._input_helper = input_helper self._input_helper = input_helper
self._L.refresh_nameplates() self._L.refresh_nameplates()
@m.output() @m.output()
def stash_wordlist_and_notify(self, wordlist): def do_refresh(self):
self._wordlist = wordlist self._L.refresh_nameplates()
if self._claimed_waiter:
self._claimed_waiter.callback(None)
del self._claimed_waiter
@m.output() @m.output()
def stash_nameplate(self, nameplate): def do_nameplate(self, nameplate):
self._nameplate = nameplate self._nameplate = nameplate
@m.output()
def C_got_nameplate(self, nameplate):
self._C.got_nameplate(nameplate) self._C.got_nameplate(nameplate)
@m.output()
def do_wordlist(self, wordlist):
self._wordlist = wordlist
@m.output() @m.output()
def finished(self, words): def do_words(self, words):
code = self._nameplate + "-" + words code = self._nameplate + "-" + words
self._C.finished_input(code) self._C.finished_input(code)
S0_idle.upon(start, enter=S1_nameplate, outputs=[L_refresh_nameplates]) S0_idle.upon(start, enter=S1_typing_nameplate, outputs=[do_start])
S1_nameplate.upon(refresh_nameplates, enter=S1_nameplate, S1_typing_nameplate.upon(refresh_nameplates, enter=S1_typing_nameplate,
outputs=[L_refresh_nameplates]) outputs=[do_refresh])
S1_nameplate.upon(_choose_nameplate, enter=S2_code_no_wordlist, S1_typing_nameplate.upon(choose_nameplate, enter=S2_typing_code_no_wordlist,
outputs=[stash_nameplate, C_got_nameplate]) outputs=[do_nameplate])
S2_code_no_wordlist.upon(got_wordlist, enter=S3_code_yes_wordlist, S2_typing_code_no_wordlist.upon(got_wordlist,
outputs=[stash_wordlist_and_notify]) enter=S3_typing_code_yes_wordlist,
S2_code_no_wordlist.upon(choose_words, enter=S4_done, outputs=[finished]) outputs=[do_wordlist])
S3_code_yes_wordlist.upon(choose_words, enter=S4_done, outputs=[finished]) S2_typing_code_no_wordlist.upon(choose_words, enter=S4_done,
outputs=[do_words])
S2_typing_code_no_wordlist.upon(got_nameplates,
enter=S2_typing_code_no_wordlist, outputs=[])
S3_typing_code_yes_wordlist.upon(choose_words, enter=S4_done,
outputs=[do_words])
S3_typing_code_yes_wordlist.upon(got_nameplates,
enter=S3_typing_code_yes_wordlist,
outputs=[])
S4_done.upon(got_nameplates, enter=S4_done, outputs=[])
S4_done.upon(got_wordlist, enter=S4_done, outputs=[])
# methods for the CodeInputHelper to use # methods for the CodeInputHelper to use
#refresh_nameplates/_choose_nameplate/choose_words: @m.input methods #refresh_nameplates/_choose_nameplate/choose_words: @m.input methods
def get_nameplate_completions(self, prefix): def get_nameplate_completions(self, prefix):
lp = len(prefix)
completions = [] completions = []
for nameplate in self._nameplates for nameplate in self._nameplates:
pass if nameplate.startswith(prefix):
def choose_nameplate(self, nameplate): completions.append(nameplate[lp:])
if self._claimed_waiter is not None: return completions
raise X
d = self._claimed_waiter = defer.Deferred()
self._choose_nameplate(nameplate)
def get_word_completions(self, prefix): def get_word_completions(self, prefix):
pass if self._wordlist:
return self._wordlist.get_completions(prefix)
return []

View File

@ -33,6 +33,13 @@ class ITiming(Interface):
pass pass
class ITorManager(Interface): class ITorManager(Interface):
pass pass
class IWordlist(Interface):
def choose_words(length):
"""Randomly select LENGTH words, join them with hyphens, return the
result."""
def get_completions(prefix):
"""Return a list of all suffixes that could complete the given
prefix."""
class IJournal(Interface): # TODO: this needs to be public class IJournal(Interface): # TODO: this needs to be public
pass pass

View File

@ -1,10 +1,13 @@
from __future__ import print_function, absolute_import, unicode_literals from __future__ import print_function, absolute_import, unicode_literals
from zope.interface import implementer from zope.interface import implementer
from attr import attrib
from attr.validators import provides
from automat import MethodicalMachine from automat import MethodicalMachine
from . import _interfaces from . import _interfaces
@implementer(_interfaces.ILister) @implementer(_interfaces.ILister)
class Lister(object): class Lister(object):
_timing = attrib(validator=provides(_interfaces.ITiming))
m = MethodicalMachine() m = MethodicalMachine()
@m.setTrace() @m.setTrace()
def set_trace(): pass # pragma: no cover def set_trace(): pass # pragma: no cover

View File

@ -2,6 +2,7 @@ from __future__ import print_function, absolute_import, unicode_literals
from zope.interface import implementer from zope.interface import implementer
from automat import MethodicalMachine from automat import MethodicalMachine
from . import _interfaces from . import _interfaces
from ._wordlist import PGPWordList
@implementer(_interfaces.INameplate) @implementer(_interfaces.INameplate)
class Nameplate(object): class Nameplate(object):
@ -12,8 +13,9 @@ class Nameplate(object):
def __init__(self): def __init__(self):
self._nameplate = None self._nameplate = None
def wire(self, mailbox, rendezvous_connector, terminator): def wire(self, mailbox, input, rendezvous_connector, terminator):
self._M = _interfaces.IMailbox(mailbox) self._M = _interfaces.IMailbox(mailbox)
self._I = _interfaces.IInput(input)
self._RC = _interfaces.IRendezvousConnector(rendezvous_connector) self._RC = _interfaces.IRendezvousConnector(rendezvous_connector)
self._T = _interfaces.ITerminator(terminator) self._T = _interfaces.ITerminator(terminator)
@ -96,6 +98,11 @@ class Nameplate(object):
# when invoked via M.connected(), we must use the stored nameplate # when invoked via M.connected(), we must use the stored nameplate
self._RC.tx_claim(self._nameplate) self._RC.tx_claim(self._nameplate)
@m.output() @m.output()
def I_got_wordlist(self, mailbox):
# TODO select wordlist based on nameplate properties, in rx_claimed
wordlist = PGPWordList()
self._I.got_wordlist(wordlist)
@m.output()
def M_got_mailbox(self, mailbox): def M_got_mailbox(self, mailbox):
self._M.got_mailbox(mailbox) self._M.got_mailbox(mailbox)
@m.output() @m.output()
@ -120,7 +127,7 @@ class Nameplate(object):
S2A.upon(connected, enter=S2B, outputs=[RC_tx_claim]) S2A.upon(connected, enter=S2B, outputs=[RC_tx_claim])
S2A.upon(close, enter=S4A, outputs=[]) S2A.upon(close, enter=S4A, outputs=[])
S2B.upon(lost, enter=S2A, outputs=[]) S2B.upon(lost, enter=S2A, outputs=[])
S2B.upon(rx_claimed, enter=S3B, outputs=[M_got_mailbox]) S2B.upon(rx_claimed, enter=S3B, outputs=[I_got_wordlist, M_got_mailbox])
S2B.upon(close, enter=S4B, outputs=[RC_tx_release]) S2B.upon(close, enter=S4B, outputs=[RC_tx_release])
S3A.upon(connected, enter=S3B, outputs=[]) S3A.upon(connected, enter=S3B, outputs=[])

182
src/wormhole/_wordlist.py Normal file
View File

@ -0,0 +1,182 @@
from __future__ import unicode_literals
import os
# The PGP Word List, which maps bytes to phonetically-distinct words. There
# are two lists, even and odd, and encodings should alternate between then to
# detect dropped words. https://en.wikipedia.org/wiki/PGP_Words
# Thanks to Warren Guy for transcribing them:
# https://github.com/warrenguy/javascript-pgp-word-list
from binascii import unhexlify
raw_words = {
'00': ['aardvark', 'adroitness'], '01': ['absurd', 'adviser'],
'02': ['accrue', 'aftermath'], '03': ['acme', 'aggregate'],
'04': ['adrift', 'alkali'], '05': ['adult', 'almighty'],
'06': ['afflict', 'amulet'], '07': ['ahead', 'amusement'],
'08': ['aimless', 'antenna'], '09': ['Algol', 'applicant'],
'0A': ['allow', 'Apollo'], '0B': ['alone', 'armistice'],
'0C': ['ammo', 'article'], '0D': ['ancient', 'asteroid'],
'0E': ['apple', 'Atlantic'], '0F': ['artist', 'atmosphere'],
'10': ['assume', 'autopsy'], '11': ['Athens', 'Babylon'],
'12': ['atlas', 'backwater'], '13': ['Aztec', 'barbecue'],
'14': ['baboon', 'belowground'], '15': ['backfield', 'bifocals'],
'16': ['backward', 'bodyguard'], '17': ['banjo', 'bookseller'],
'18': ['beaming', 'borderline'], '19': ['bedlamp', 'bottomless'],
'1A': ['beehive', 'Bradbury'], '1B': ['beeswax', 'bravado'],
'1C': ['befriend', 'Brazilian'], '1D': ['Belfast', 'breakaway'],
'1E': ['berserk', 'Burlington'], '1F': ['billiard', 'businessman'],
'20': ['bison', 'butterfat'], '21': ['blackjack', 'Camelot'],
'22': ['blockade', 'candidate'], '23': ['blowtorch', 'cannonball'],
'24': ['bluebird', 'Capricorn'], '25': ['bombast', 'caravan'],
'26': ['bookshelf', 'caretaker'], '27': ['brackish', 'celebrate'],
'28': ['breadline', 'cellulose'], '29': ['breakup', 'certify'],
'2A': ['brickyard', 'chambermaid'], '2B': ['briefcase', 'Cherokee'],
'2C': ['Burbank', 'Chicago'], '2D': ['button', 'clergyman'],
'2E': ['buzzard', 'coherence'], '2F': ['cement', 'combustion'],
'30': ['chairlift', 'commando'], '31': ['chatter', 'company'],
'32': ['checkup', 'component'], '33': ['chisel', 'concurrent'],
'34': ['choking', 'confidence'], '35': ['chopper', 'conformist'],
'36': ['Christmas', 'congregate'], '37': ['clamshell', 'consensus'],
'38': ['classic', 'consulting'], '39': ['classroom', 'corporate'],
'3A': ['cleanup', 'corrosion'], '3B': ['clockwork', 'councilman'],
'3C': ['cobra', 'crossover'], '3D': ['commence', 'crucifix'],
'3E': ['concert', 'cumbersome'], '3F': ['cowbell', 'customer'],
'40': ['crackdown', 'Dakota'], '41': ['cranky', 'decadence'],
'42': ['crowfoot', 'December'], '43': ['crucial', 'decimal'],
'44': ['crumpled', 'designing'], '45': ['crusade', 'detector'],
'46': ['cubic', 'detergent'], '47': ['dashboard', 'determine'],
'48': ['deadbolt', 'dictator'], '49': ['deckhand', 'dinosaur'],
'4A': ['dogsled', 'direction'], '4B': ['dragnet', 'disable'],
'4C': ['drainage', 'disbelief'], '4D': ['dreadful', 'disruptive'],
'4E': ['drifter', 'distortion'], '4F': ['dropper', 'document'],
'50': ['drumbeat', 'embezzle'], '51': ['drunken', 'enchanting'],
'52': ['Dupont', 'enrollment'], '53': ['dwelling', 'enterprise'],
'54': ['eating', 'equation'], '55': ['edict', 'equipment'],
'56': ['egghead', 'escapade'], '57': ['eightball', 'Eskimo'],
'58': ['endorse', 'everyday'], '59': ['endow', 'examine'],
'5A': ['enlist', 'existence'], '5B': ['erase', 'exodus'],
'5C': ['escape', 'fascinate'], '5D': ['exceed', 'filament'],
'5E': ['eyeglass', 'finicky'], '5F': ['eyetooth', 'forever'],
'60': ['facial', 'fortitude'], '61': ['fallout', 'frequency'],
'62': ['flagpole', 'gadgetry'], '63': ['flatfoot', 'Galveston'],
'64': ['flytrap', 'getaway'], '65': ['fracture', 'glossary'],
'66': ['framework', 'gossamer'], '67': ['freedom', 'graduate'],
'68': ['frighten', 'gravity'], '69': ['gazelle', 'guitarist'],
'6A': ['Geiger', 'hamburger'], '6B': ['glitter', 'Hamilton'],
'6C': ['glucose', 'handiwork'], '6D': ['goggles', 'hazardous'],
'6E': ['goldfish', 'headwaters'], '6F': ['gremlin', 'hemisphere'],
'70': ['guidance', 'hesitate'], '71': ['hamlet', 'hideaway'],
'72': ['highchair', 'holiness'], '73': ['hockey', 'hurricane'],
'74': ['indoors', 'hydraulic'], '75': ['indulge', 'impartial'],
'76': ['inverse', 'impetus'], '77': ['involve', 'inception'],
'78': ['island', 'indigo'], '79': ['jawbone', 'inertia'],
'7A': ['keyboard', 'infancy'], '7B': ['kickoff', 'inferno'],
'7C': ['kiwi', 'informant'], '7D': ['klaxon', 'insincere'],
'7E': ['locale', 'insurgent'], '7F': ['lockup', 'integrate'],
'80': ['merit', 'intention'], '81': ['minnow', 'inventive'],
'82': ['miser', 'Istanbul'], '83': ['Mohawk', 'Jamaica'],
'84': ['mural', 'Jupiter'], '85': ['music', 'leprosy'],
'86': ['necklace', 'letterhead'], '87': ['Neptune', 'liberty'],
'88': ['newborn', 'maritime'], '89': ['nightbird', 'matchmaker'],
'8A': ['Oakland', 'maverick'], '8B': ['obtuse', 'Medusa'],
'8C': ['offload', 'megaton'], '8D': ['optic', 'microscope'],
'8E': ['orca', 'microwave'], '8F': ['payday', 'midsummer'],
'90': ['peachy', 'millionaire'], '91': ['pheasant', 'miracle'],
'92': ['physique', 'misnomer'], '93': ['playhouse', 'molasses'],
'94': ['Pluto', 'molecule'], '95': ['preclude', 'Montana'],
'96': ['prefer', 'monument'], '97': ['preshrunk', 'mosquito'],
'98': ['printer', 'narrative'], '99': ['prowler', 'nebula'],
'9A': ['pupil', 'newsletter'], '9B': ['puppy', 'Norwegian'],
'9C': ['python', 'October'], '9D': ['quadrant', 'Ohio'],
'9E': ['quiver', 'onlooker'], '9F': ['quota', 'opulent'],
'A0': ['ragtime', 'Orlando'], 'A1': ['ratchet', 'outfielder'],
'A2': ['rebirth', 'Pacific'], 'A3': ['reform', 'pandemic'],
'A4': ['regain', 'Pandora'], 'A5': ['reindeer', 'paperweight'],
'A6': ['rematch', 'paragon'], 'A7': ['repay', 'paragraph'],
'A8': ['retouch', 'paramount'], 'A9': ['revenge', 'passenger'],
'AA': ['reward', 'pedigree'], 'AB': ['rhythm', 'Pegasus'],
'AC': ['ribcage', 'penetrate'], 'AD': ['ringbolt', 'perceptive'],
'AE': ['robust', 'performance'], 'AF': ['rocker', 'pharmacy'],
'B0': ['ruffled', 'phonetic'], 'B1': ['sailboat', 'photograph'],
'B2': ['sawdust', 'pioneer'], 'B3': ['scallion', 'pocketful'],
'B4': ['scenic', 'politeness'], 'B5': ['scorecard', 'positive'],
'B6': ['Scotland', 'potato'], 'B7': ['seabird', 'processor'],
'B8': ['select', 'provincial'], 'B9': ['sentence', 'proximate'],
'BA': ['shadow', 'puberty'], 'BB': ['shamrock', 'publisher'],
'BC': ['showgirl', 'pyramid'], 'BD': ['skullcap', 'quantity'],
'BE': ['skydive', 'racketeer'], 'BF': ['slingshot', 'rebellion'],
'C0': ['slowdown', 'recipe'], 'C1': ['snapline', 'recover'],
'C2': ['snapshot', 'repellent'], 'C3': ['snowcap', 'replica'],
'C4': ['snowslide', 'reproduce'], 'C5': ['solo', 'resistor'],
'C6': ['southward', 'responsive'], 'C7': ['soybean', 'retraction'],
'C8': ['spaniel', 'retrieval'], 'C9': ['spearhead', 'retrospect'],
'CA': ['spellbind', 'revenue'], 'CB': ['spheroid', 'revival'],
'CC': ['spigot', 'revolver'], 'CD': ['spindle', 'sandalwood'],
'CE': ['spyglass', 'sardonic'], 'CF': ['stagehand', 'Saturday'],
'D0': ['stagnate', 'savagery'], 'D1': ['stairway', 'scavenger'],
'D2': ['standard', 'sensation'], 'D3': ['stapler', 'sociable'],
'D4': ['steamship', 'souvenir'], 'D5': ['sterling', 'specialist'],
'D6': ['stockman', 'speculate'], 'D7': ['stopwatch', 'stethoscope'],
'D8': ['stormy', 'stupendous'], 'D9': ['sugar', 'supportive'],
'DA': ['surmount', 'surrender'], 'DB': ['suspense', 'suspicious'],
'DC': ['sweatband', 'sympathy'], 'DD': ['swelter', 'tambourine'],
'DE': ['tactics', 'telephone'], 'DF': ['talon', 'therapist'],
'E0': ['tapeworm', 'tobacco'], 'E1': ['tempest', 'tolerance'],
'E2': ['tiger', 'tomorrow'], 'E3': ['tissue', 'torpedo'],
'E4': ['tonic', 'tradition'], 'E5': ['topmost', 'travesty'],
'E6': ['tracker', 'trombonist'], 'E7': ['transit', 'truncated'],
'E8': ['trauma', 'typewriter'], 'E9': ['treadmill', 'ultimate'],
'EA': ['Trojan', 'undaunted'], 'EB': ['trouble', 'underfoot'],
'EC': ['tumor', 'unicorn'], 'ED': ['tunnel', 'unify'],
'EE': ['tycoon', 'universe'], 'EF': ['uncut', 'unravel'],
'F0': ['unearth', 'upcoming'], 'F1': ['unwind', 'vacancy'],
'F2': ['uproot', 'vagabond'], 'F3': ['upset', 'vertigo'],
'F4': ['upshot', 'Virginia'], 'F5': ['vapor', 'visitor'],
'F6': ['village', 'vocalist'], 'F7': ['virus', 'voyager'],
'F8': ['Vulcan', 'warranty'], 'F9': ['waffle', 'Waterloo'],
'FA': ['wallet', 'whimsical'], 'FB': ['watchword', 'Wichita'],
'FC': ['wayside', 'Wilmington'], 'FD': ['willow', 'Wyoming'],
'FE': ['woodlark', 'yesteryear'], 'FF': ['Zulu', 'Yucatan']
};
byte_to_even_word = dict([(unhexlify(k.encode("ascii")), both_words[0])
for k,both_words
in raw_words.items()])
byte_to_odd_word = dict([(unhexlify(k.encode("ascii")), both_words[1])
for k,both_words
in raw_words.items()])
even_words_lowercase, odd_words_lowercase = set(), set()
for k,both_words in raw_words.items():
even_word, odd_word = both_words
even_words_lowercase.add(even_word.lower())
odd_words_lowercase.add(odd_word.lower())
class PGPWordList(object):
def get_completions(self, prefix):
# start with the odd words
if prefix.count("-") % 2 == 0:
words = odd_words_lowercase
else:
words = even_words_lowercase
last_partial_word = prefix.split("-")[-1]
lp = len(last_partial_word)
completions = []
for word in words:
if word.startswith(prefix):
completions.append(word[lp:])
return completions
def choose_words(self, length):
words = []
for i in range(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 "-".join(words)