Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option -D/--delimiter-special and some wordlists #61

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ Changes
0.9.7.dev0 (yet unreleased)
---------------------------

(no changes yet)
- Added option ``--delimiter-special``. Thanks to `Hartmut Goebel
<https://crazy-compilers.com>`_.
- Added German, Italien, Latin and two Spanish wordlists. Thanks to
`Hartmut Goebel <https://crazy-compilers.com>`_.

0.9.6 (2018-12-19)
------------------
Expand Down
5 changes: 5 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ Once installed, use ``--help`` to list all available options::
Insert NUM special chars into generated word.
-d DELIMITER, --delimiter DELIMITER
Separate words by DELIMITER. Empty string by default.
-D NUM, --delimiter-special NUM
Separate words by NUM special chars (none by default).
-r SOURCE, --randomsource SOURCE
Get randomness from this source. Possible values:
`realdice', `system'. Default: system
Expand Down Expand Up @@ -208,6 +210,7 @@ directory. This file could look like this::
caps = off
specials = 2
delimiter = "MYDELIMITER"
delimiter_special = 0
randomsource = "system"
wordlist = "en_securedrop"

Expand Down Expand Up @@ -412,6 +415,8 @@ People that helped spotting bugs, providing solutions, etc.:
- `Simon Fondrie-Teitler <https://github.com/simonft>`_ contributed a
machine-readable copyright file, with improvements from `@anarcat`_
- `Doug Muth <https://github.com/dmuth>`_ fixed formatting in docs.
- `Hartmut Goebel <https://crazy-compilers.com>`_ added option
``--delimiter-special`` and some wordlists.

Many thanks to all of them!

Expand Down
27 changes: 26 additions & 1 deletion diceware/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,9 @@ def handle_options(args):
parser.add_argument(
'-d', '--delimiter', default='',
help="Separate words by DELIMITER. Empty string by default.")
parser.add_argument(
'-D', '--delimiter-special', default=0, type=int, metavar='NUM',
help="Separate words by NUM special chars (none by default).")
parser.add_argument(
'-r', '--randomsource', default='system', choices=random_sources,
metavar="SOURCE",
Expand Down Expand Up @@ -160,6 +163,24 @@ def insert_special_char(word, specials=SPECIAL_CHARS, rnd=None):
char_list[rnd.choice(range(len(char_list)))] = rnd.choice(specials)
return ''.join(char_list)

def insert_special_delimiter(words, max_delimiter_chars,
specials=SPECIAL_CHARS, rnd=None):
"""Insert a char out of `specials` into `word`.

`rnd`, if passed in, will be used as a (pseudo) random number
generator. We use `.choice()` only.

Returns the modified word.
"""
if rnd is None:
rnd = SystemRandom()
words = words[:]
lengths = list(range(1, max_delimiter_chars + 1))
for pos in range(len(words)-1, 0, -1):
num_chars = rnd.choice(lengths) # choose number of chars to insert
deli = "".join(rnd.choice(specials) for j in range(num_chars))
words.insert(pos, deli)
return words

def get_passphrase(options=None):
"""Get a diceware passphrase.
Expand Down Expand Up @@ -190,7 +211,11 @@ def get_passphrase(options=None):
words = [rnd.choice(list(word_list)) for x in range(options.num)]
if options.caps:
words = [x.capitalize() for x in words]
result = options.delimiter.join(words)
if options.delimiter_special:
words = insert_special_delimiter(words, options.delimiter_special)
result = "".join(words)
else:
result = options.delimiter.join(words)
for _ in range(options.specials):
result = insert_special_char(result, rnd=rnd)
return result
Expand Down
1 change: 1 addition & 0 deletions diceware/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
caps=True,
specials=0,
delimiter="",
delimiter_special=0,
randomsource="system",
verbose=0,
wordlist="en_eff",
Expand Down
1 change: 1 addition & 0 deletions tests/sample_dot_diceware.ini
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ num = 6
caps = on
specials = 0
delimiter = ""
delimiter_special = 0
randomsource = "system"
verbose = 0
wordlist = "en_securedrop"
Expand Down
42 changes: 41 additions & 1 deletion tests/test_diceware.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from diceware import (
get_wordlists_dir, SPECIAL_CHARS, insert_special_char, get_passphrase,
handle_options, main, __version__, print_version, get_random_sources,
get_wordlist_names
get_wordlist_names, insert_special_delimiter
)


Expand Down Expand Up @@ -86,6 +86,15 @@ def test_handle_options_delimiter(self):
options = handle_options(['-d', 'WOW'])
assert options.delimiter == 'WOW'

def test_handle_options_delimiter_special(self):
# we can set number of special characters to be used as delimiter
options = handle_options([])
assert options.delimiter_special == 0
options = handle_options(['-D', '3'])
assert options.delimiter_special == 3
options = handle_options(['--delimiter-special', '1'])
assert options.delimiter_special == 1

def test_handle_options_randomsource(self):
# we can choose the source of randomness
source_names = get_random_sources().keys()
Expand Down Expand Up @@ -244,6 +253,37 @@ def test_get_passphrase_delimiters(self):
phrase = get_passphrase(options)
assert " " in phrase

def test_get_passphrase_special_delimiter(self):
# delimiter_special overrules delemiter
options = handle_options(args=[])
options.delimiter = " "
options.delimiter_special = 2
phrase = get_passphrase(options)
assert " " not in phrase

def test_insert_special_delimiter(self):
# we can insert special chars between the words.
fake_rnd = FakeRandom()
fake_rnd.nums_to_draw = [1, 2, 1, # (num of chars)-1, char-idx
0, 1,
2, 1, 2, 0]
words_in = ['aaa', 'bbb', 'ccc', 'ddd']
result1 = insert_special_delimiter(words_in, 3,
specials='!$&', rnd=fake_rnd)
assert result1 == ['aaa', '$&!', 'bbb', '$', 'ccc', '&$', 'ddd']
assert words_in == ['aaa', 'bbb', 'ccc', 'ddd'] # unchanges

def test_insert_special_delimiter_defaults(self):
# defaults are respected
words_in = ['aaa', 'bbb']
result1 = insert_special_delimiter(words_in, 2)
assert result1[0] == 'aaa'
assert result1[2] == 'bbb'
assert 1 <= len(result1[1]) <= 2
assert result1[1][0] in SPECIAL_CHARS
if len(result1[1]) == 2:
result1[1][1] in SPECIAL_CHARS

def test_print_version(self, capsys):
# we can print version infos
print_version()
Expand Down