~gagbo/diceware.py

87923d7f683e7d1fda54829e9413999c94854e9b — Gerry Agbobada 3 years ago ec92035
Diceware gen : Make a usable password generator

- Added a reader to transform a words list into a dictionary

- Added a method to DicewareResult to transform DicewareResult.words
(which are actually rolls) into a password using a words list passed as
dictionary

- Added a check to salt the passphrase with a random symbol if the user
asked for it (during the creation of the DicewareResult object)
1 files changed, 71 insertions(+), 9 deletions(-)

M diceware.py
M diceware.py => diceware.py +71 -9
@@ 2,10 2,6 @@
# coding: utf-8
""" diceware : generate passwords with diceware method
"""
# TODO : find the URL of the diceware paper for reference
#    Then it can be pulled for justifying stuff, like the reason for naming
#    the arguments in get_separator digitThree, digitFour

import datetime
import random
import sys


@@ 18,11 14,20 @@ class DicewareResult:
    """

    def __init__(self, wordsCount=5, systemRand=True, bonusRoll=True):
        """ Constructor of a Diceware Result
        wordsCount is the number of words in the passphrase
        systemRand asks to use system true random instead of pseudo-random
        bonusRoll asks for one more roll to salt the passphrase with a symbol
        """
        self.wordsCount = wordsCount
        self.systemRand = systemRand
        self.bonusRoll = bonusRoll

    def make_rolls(self):
        """ make_rolls fills self.words with dice throws
        These 'words' which are really 5-uples are the basis for generating
        the true passphrase using a dictionary
        """
        self.words = generate_rolls(words=self.wordsCount,
                                    systemRand=self.systemRand)
        if self.systemRand:


@@ 36,6 41,9 @@ class DicewareResult:
                self.salt = roll_5_dice(random_machine)

    def __str__(self):
        """ Method called for str(self) and print(self)
        Useful for debugging purposes
        """
        string = "Diceware Result : {} words with {} generator\n".format(
            self.wordsCount, "system" if self.systemRand else "pseudo")
        for i in range(self.wordsCount):


@@ 44,7 52,41 @@ class DicewareResult:
        if self.bonusRoll:
            string += "Salt : {}\n".format(self.salt)

        return string
        return string[:-1]

    def key_from_word(self, i):
        """ Transforms self.words[i] in an integer.
        It should be used to obtain the key for
        the words dictionary (Diceware list)
        """
        roll = self.words[i]
        result = (roll[0] * 10000 + roll[1] * 1000 +
                  roll[2] * 100 + roll[3] * 10 + roll[4])
        return result

    def password_from_dict(self, diceware_dict):
        """ Returns the password as a string
        The string is generated with this instance and diceware_dict param
        diceware_dict must be a dictionary indexed with integers representing
           the rolls concatenated
        """
        result = ''
        # Prepare salting the correct word if relevant
        if self.bonusRoll:
            target_word_index = self.salt[0] - 1

        for i in range(self.wordsCount):
            new_word = list(diceware_dict[self.key_from_word(i)])
            # Salting
            if self.bonusRoll and i == target_word_index:
                target_char = min(self.salt[1], len(new_word))
                replace_char = get_salt_char(self.salt[2], self.salt[3])
                new_word[target_char - 1] = replace_char

            result += "".join(new_word)
            result += ' '
        result = result[:-1]
        return result


def roll_5_dice(gen):


@@ 86,7 128,21 @@ def get_salt_char(digitThree=0, digitFour=0):
                    ('?', '/', '0', '1', '2', '3'),
                    ('4', '5', '6', '7', '8', '9'))

    return symbol_table[digitFour][digitThree]
    return symbol_table[digitFour - 1][digitThree - 1]


def create_dictionary(in_file):
    """ Returns a dict from a file handle to a readable diceware words list
    The keys are the 5 rolls concatenated into an integer
    The values are strings with the actual words matching a 5-roll
    """
    words_dict = {}
    for line in in_file:
        words = line.split()
        key = int(words[0])
        value = words[1]
        words_dict[key] = value
    return words_dict


def print_entropy_help(fileDesc):


@@ 104,24 160,30 @@ def print_entropy_help(fileDesc):
if __name__ == '__main__':
    random.seed(datetime.datetime.now())
    print_entropy_help(sys.stdout)
    with open("data/diceware-fr-5-jets.txt", "r") as fr:
        diceware_dict = create_dictionary(fr)

    print("Default :")
    test_value = DicewareResult()
    test_value.make_rolls()
    print(test_value)
    print(test_value.password_from_dict(diceware_dict))

    print("Only 2 words :")
    print("\nOnly 2 words :")
    test_value_2 = DicewareResult(wordsCount=2)
    test_value_2.make_rolls()
    print(test_value_2)
    print(test_value_2.password_from_dict(diceware_dict))

    print("No salt :")
    print("\nNo salt :")
    test_value_3 = DicewareResult(bonusRoll=False)
    test_value_3.make_rolls()
    print(test_value_3)
    print(test_value_3.password_from_dict(diceware_dict))

    print("Pseudo random - No Salt - 3 words :")
    print("\nPseudo random - No Salt - 3 words :")
    test_value_4 = DicewareResult(
        wordsCount=3, systemRand=False, bonusRoll=False)
    test_value_4.make_rolls()
    print(test_value_4)
    print(test_value_4.password_from_dict(diceware_dict))