comparison ppgen.py @ 4:85c65a597420

Improves: command line options and code style. Adds a command line option for dumping the dictionary into a file. Improves source code style to better conform to pep8. Calls it BETA now.
author Bernhard Reiter <bernhard@intevation.de>
date Thu, 06 Oct 2016 17:28:46 +0200
parents 757625ec8364
children f8e24b2b6b6a
comparison
equal deleted inserted replaced
3:757625ec8364 4:85c65a597420
1 #!/usr/bin/env python3 1 #!/usr/bin/env python3
2 """Create a passphrase from a few random words. DRAFT 2 """Create a random passphrase from a dictionary of words. BETA
3 3
4 Relies on the entropy of python's 4 Relies on the entropy of python's
5 random.SystemRandom class 5 random.SystemRandom class
6 which (according to the documentation) calls os.urandom() 6 which (according to the documentation) calls os.urandom()
7 which (according to the documentation) calls the operating system 7 which (according to the documentation) calls the operating system
8 specific randomness source which "should be unpredictable 8 specific randomness source which "should be unpredictable
9 enough for cryptographic applications" 9 enough for cryptographic applications"
10 10
11 Requires: 11 Requires:
12 * Python v>=3.2 12 * Python v>=3.2
13 * a dictionary, Ding's trans-de-en by default. 13 * a dictionary, Ding's trans-de-en by default.
26 26
27 This file is Free Software under the Apache 2.0 license and thus 27 This file is Free Software under the Apache 2.0 license and thus
28 comes without any warranty (to extend permissible under applicable law). 28 comes without any warranty (to extend permissible under applicable law).
29 """ 29 """
30 30
31 import argparse
31 import math 32 import math
32 import re 33 import re
33 import sys 34 import sys
34 35
35 from random import SystemRandom 36 from random import SystemRandom
36 _srandom = SystemRandom() 37 _srandom = SystemRandom()
37 38
38 tainted = False # to be set if we find a hint that the passphrase may be weak 39 tainted = False # to be set if we find a hint that the passphrase may be weak
39 40
40 def buildDictionary(): 41
42 def buildDictionary(options):
41 """Build up a dictionary of unique words, calculate stats.""" 43 """Build up a dictionary of unique words, calculate stats."""
42 global tainted 44 global tainted
43 d = [] 45 d = []
44 46
45 # dictionary for testing 47 # dictionary for testing
46 #d = ["abc", "aBc", "cde", "efg", "hij", "blubber", "jikf", "zug", "lmf", "opq"] 48 #d = ["abc", "aBc", "cde", "efg", "hij", "blubber",
49 # "jikf", "zug", "lmf", "opq"]
47 # second test dictionary to show that different string functions are used. 50 # second test dictionary to show that different string functions are used.
48 #d = [''.join('A' * 1000) for _ in range(1000)] 51 #d = [''.join('A' * 1000) for _ in range(1000)]
49 52
50 # Using the dictionary from Ding **customize** 53 # Using the dictionary from Ding **customize**
51 d = readDingDict(filename="/usr/share/trans/de-en", useLeft=True) 54 d = readDingDict(filename="/usr/share/trans/de-en", useLeft=True)
52 55
53 ## for debugging purpuses, dump dictionary 56 ## for debugging purpuses, dump dictionary
54 #dumpfilename = "ddump.txt" 57 if options.ddump_filename:
55 #print("Writing out {}.".format(dumpfilename)) 58 print("Writing out dictionary in '{}'.".format(options.ddump_filename))
56 #with open(dumpfilename, "w") as f: 59 with open(options.ddump_filename, "w") as f:
57 # for i in d: 60 for i in d:
58 # f.write("{}\n".format(i)) 61 f.write("{}\n".format(i))
59 62
60 # Print some stats on the dictionary to be used 63 # Print some stats on the dictionary to be used
61 dl = len(d) 64 dl = len(d)
62 print("Found {:d} dictionary entries.".format(dl)) 65 print("Found {:d} dictionary entries.".format(dl))
63 if dl < 8000: 66 if dl < 8000:
64 print("!Your dictionary is below 8k entries, that is quite small!") 67 print("!Your dictionary is below 8k entries, that is quite small!")
65 tainted = True 68 tainted = True
66 69
67 print("|= Number of words |= possibilities |") 70 print("|= Number of words |= possibilities |")
68 for i in range(1,5): 71 for i in range(1, 5):
69 print("| {:2d} | 2^{:4.1f} |".format( 72 print("| {:2d} | 2^{:4.1f} |".format(
70 i, math.log(dl**i,2))) 73 i, math.log(dl**i, 2)))
71 return d 74 return d
72 75
73 76
74 def readDingDict(filename = "/usr/share/trans/de-en", useLeft=False): 77 def readDingDict(filename="/usr/share/trans/de-en", useLeft=False):
75 """Read dictionary with unique words from file in Ding format. 78 """Read dictionary with unique words from file in Ding format.
76 79
77 useLeft: Boolean to control which language to use 80 useLeft: Boolean to control which language to use
78 81
79 TODO: add option to use both languages for people that speak them both? 82 TODO: add option to use both languages for people that speak them both?
80 """ 83 """
81 84
82 dset = set() #using the datatype 'set' to aviod duplicates 85 dset = set() # using the datatype 'set' to aviod duplicates
83 86
84 splitter = re.compile(r"""\ \|\ # first pattern ' | ' 87 splitter = re.compile(r"""\ \|\ # first pattern ' | '
85 |;\ # second pattern '; ' 88 |;\ # second pattern '; '
86 |(?<=\S)/(?=\S) # 3.: '\' surrounded by chars 89 |(?<=\S)/(?=\S) # 3.: '\' surrounded by chars
87 |\s+ # by whitespace 90 |\s+ # by whitespace
88 """,re.VERBOSE) 91 """, re.VERBOSE)
89 92
90 print("Reading entries from {}.".format(filename), end='') 93 print("Reading entries from {}.".format(filename), end='')
91 counter = 0 # for progress or stopping early 94 counter = 0 # for progress or stopping early
92 with open(filename, "r") as f: 95 with open(filename, "r") as f:
93 for line in f: 96 for line in f:
94 if line[0] == '#': continue 97 if line[0] == '#':
98 continue
95 99
96 # languages are separated by " :: " 100 # languages are separated by " :: "
97 p = line.partition(" :: ") 101 p = line.partition(" :: ")
98 languageEntry = p[0] if useLeft else p[2] 102 languageEntry = p[0] if useLeft else p[2]
99 103
112 sys.stdout.flush() 116 sys.stdout.flush()
113 print() 117 print()
114 118
115 return list(dset) 119 return list(dset)
116 120
121
117 def main(): 122 def main():
118 global tainted 123 global tainted
119 dictionary = buildDictionary() 124
125 parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
126 parser.add_argument('--ddump-filename',
127 help='filename to dump the dictionary to')
128 options = parser.parse_args()
129
130 dictionary = buildDictionary(options)
120 131
121 howMany = 4 132 howMany = 4
122 133
123 # use a dictionary with lower case words for a simple check if 134 # use a dictionary with lower case words for a simple check if
124 # our random source is okay 135 # our random source is okay
126 howMany)) 137 howMany))
127 print(" ", end='') 138 print(" ", end='')
128 words = {} 139 words = {}
129 for x in range(howMany): 140 for x in range(howMany):
130 word = _srandom.choice(dictionary) 141 word = _srandom.choice(dictionary)
131 words[word.lower()]= True 142 words[word.lower()] = True
132 print(word, end='\n ') 143 print(word, end='\n ')
133 print("\n") 144 print("\n")
134 145
135 if len(words) < howMany: 146 if len(words) < howMany:
136 print("! Your random generator is weak") 147 print("! Your random generator is weak")
This site is hosted by Intevation GmbH (Datenschutzerklärung und Impressum | Privacy Policy and Imprint)