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"]
S0B -> S0A [label="lost"]
S0B [label="S0B:\nidle\nconnected"]
S0A -> S1A [label="allocate" color="orange"]
S0B -> P_allocate [label="allocate"]
S0A -> S1A [label="allocate(length, wordlist)" color="orange"]
S0B -> P_allocate [label="allocate(length, wordlist)"]
P_allocate [shape="box" label="RC.tx_allocate" color="orange"]
P_allocate -> S1B [color="orange"]
{rank=same; S1A P_allocate S1B}
@ -21,7 +21,7 @@ digraph {
S1A -> P_allocate [label="connected" 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"]
S2 [label="S2:\ndone" color="orange"]

View File

@ -4,36 +4,31 @@ digraph {
{rank=same; start S0}
start -> S0 [style="invis"]
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 -> P_done
P_done [shape="box" label="K.got_code\nB.got_code"]
P_done -> S5
S5 [label="S5: known" color="green"]
P_done -> S4
S4 [label="S4: known" color="green"]
{rank=same; S1_inputting_nameplate S3_allocating}
{rank=same; P0_got_code P1_set_nameplate P3_got_nameplate}
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
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 -> S2_inputting_words
S2_inputting_words [label="S2:\ninputting\nwords"]
S2_inputting_words -> P1_got_words [label="finished_input"]
P1_got_words [shape="box" label="assemble\ncode"]
P1_got_words -> P_done
P_done
S2_inputting_words -> P_done [label="finished_input\n(code)"]
S0 -> P_allocate [label="allocate_code"]
P_allocate [shape="box" label="A.allocate"]
S0 -> P_allocate [label="allocate_code\n(length,\nwordlist)"]
P_allocate [shape="box" label="A.allocate\n(length, wordlist)"]
P_allocate -> S3_allocating
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 -> P3_generate
P3_generate [shape="box" label="append\nrandom words"]
P3_generate -> P_done
P3_got_nameplate -> P_done
}

View File

@ -31,7 +31,7 @@ digraph {
S3 [label="S3: typing\ncode\n(yes wordlist)"]
S3 -> S3 [label="got_nameplates"]
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
S4 [label="S4: done" color="green"]
S4 -> S4 [label="got_nameplates\ngot_wordlist"]

View File

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

View File

@ -38,7 +38,7 @@ digraph {
S2A -> S3A [label="(none)" style="invis"]
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"]
subgraph {rank=same; S3A S3B}

View File

@ -30,7 +30,7 @@ class Allocator(object):
# from Code
@m.input()
def allocate(self): pass
def allocate(self, length, wordlist): pass
# from RendezvousConnector
@m.input()
@ -40,26 +40,37 @@ class Allocator(object):
@m.input()
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()
def RC_tx_allocate(self):
self._RC.tx_allocate()
@m.output()
def C_allocated_nameplate(self, nameplate):
self._C.allocated_nameplate(nameplate)
def build_and_notify(self, 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=[])
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,
outputs=[RC_tx_allocate])
outputs=[stash_and_RC_rx_allocate])
S1A_allocating.upon(connected, enter=S1B_allocating_connected,
outputs=[RC_tx_allocate])
S1B_allocating_connected.upon(lost, enter=S1A_allocating, outputs=[])
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(lost, enter=S2_done, outputs=[])

View File

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

View File

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

View File

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

View File

@ -1,10 +1,13 @@
from __future__ import print_function, absolute_import, unicode_literals
from zope.interface import implementer
from attr import attrib
from attr.validators import provides
from automat import MethodicalMachine
from . import _interfaces
@implementer(_interfaces.ILister)
class Lister(object):
_timing = attrib(validator=provides(_interfaces.ITiming))
m = MethodicalMachine()
@m.setTrace()
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 automat import MethodicalMachine
from . import _interfaces
from ._wordlist import PGPWordList
@implementer(_interfaces.INameplate)
class Nameplate(object):
@ -12,8 +13,9 @@ class Nameplate(object):
def __init__(self):
self._nameplate = None
def wire(self, mailbox, rendezvous_connector, terminator):
def wire(self, mailbox, input, rendezvous_connector, terminator):
self._M = _interfaces.IMailbox(mailbox)
self._I = _interfaces.IInput(input)
self._RC = _interfaces.IRendezvousConnector(rendezvous_connector)
self._T = _interfaces.ITerminator(terminator)
@ -96,6 +98,11 @@ class Nameplate(object):
# when invoked via M.connected(), we must use the stored nameplate
self._RC.tx_claim(self._nameplate)
@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):
self._M.got_mailbox(mailbox)
@m.output()
@ -120,7 +127,7 @@ class Nameplate(object):
S2A.upon(connected, enter=S2B, outputs=[RC_tx_claim])
S2A.upon(close, enter=S4A, 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])
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)