comparison ppgen.py @ 8:200c2c3c5f67

Adds comfort. * Adds options ** '--just-passphrase': to get a passphrase on one line for use in skripts. ** '--number-of-words': the passphrase shall consists of * Refactors to implement the options and preparing for future developments: ** Warnings will be written to stderr and tained will bail out with sys.exit(). ** The output_string is build before printing.
author Bernhard Reiter <bernhard@intevation.de>
date Thu, 18 Jan 2018 08:44:33 +0100
parents 8b2f8f439817
children 35c468a37b54
comparison
equal deleted inserted replaced
7:8b2f8f439817 8:200c2c3c5f67
19 19
20 Related: There is a Go implementation started by Sascha L. Teichmann at 20 Related: There is a Go implementation started by Sascha L. Teichmann at
21 https://bitbucket.org/s_l_teichmann/ppgen 21 https://bitbucket.org/s_l_teichmann/ppgen
22 22
23 23
24 Copyright 2016, 2017 by Intevation GmbH. 24 Copyright 2016, 2017, 2018 by Intevation GmbH.
25 Author: Bernhard E. Reiter <bernhard@intevation.de> 25 Author: Bernhard E. Reiter <bernhard@intevation.de>
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 """
49 # "jikf", "zug", "lmf", "opq"] 49 # "jikf", "zug", "lmf", "opq"]
50 # second test dictionary to show that different string functions are used. 50 # second test dictionary to show that different string functions are used.
51 #d = [''.join('A' * 1000) for _ in range(1000)] 51 #d = [''.join('A' * 1000) for _ in range(1000)]
52 52
53 # Using the dictionary from Ding **customize** 53 # Using the dictionary from Ding **customize**
54 d = readDingDict(filename="/usr/share/trans/de-en", useLeft=True) 54 d = readDingDict(options, filename="/usr/share/trans/de-en", useLeft=True)
55 55
56 ## for debugging purposes, dump dictionary 56 # for debugging purposes, dump dictionary
57 if options.ddump_filename: 57 if options.ddump_filename:
58 print("Writing out dictionary in '{}'.".format(options.ddump_filename)) 58 print("Writing out dictionary in '{}'.".format(options.ddump_filename))
59 with open(options.ddump_filename, "w") as f: 59 with open(options.ddump_filename, "w") as f:
60 for i in d: 60 for i in d:
61 f.write("{}\n".format(i)) 61 f.write("{}\n".format(i))
62 62
63 # Print some stats on the dictionary to be used 63 # Print some stats on the dictionary to be used
64 dl = len(d) 64 dl = len(d)
65 print("Found {:d} dictionary entries.".format(dl)) 65 if not options.just_passphrase:
66 print("Found {:d} dictionary entries.".format(dl))
67 print("|= Number of words |= possibilities |")
68 for i in range(1, 5):
69 print("| {:2d} | 2^{:4.1f} |".format(
70 i, math.log(dl**i, 2)))
71
66 if dl < 8000: 72 if dl < 8000:
67 print("!Your dictionary is below 8k entries, that is quite small!") 73 sys.stderr.write("!Your dictionary is below 8k entries, "
74 "that is quite small!\n")
68 tainted = True 75 tainted = True
69
70 print("|= Number of words |= possibilities |")
71 for i in range(1, 5):
72 print("| {:2d} | 2^{:4.1f} |".format(
73 i, math.log(dl**i, 2)))
74 return d 76 return d
75 77
76 78
77 def readDingDict(filename="/usr/share/trans/de-en", useLeft=False): 79 def readDingDict(options, filename="/usr/share/trans/de-en", useLeft=False):
78 """Read dictionary with unique words from file in Ding format. 80 """Read dictionary with unique words from file in Ding format.
79 81
80 useLeft: Boolean to control which language to use 82 useLeft: Boolean to control which language to use
81 83
82 TODO: add option to use both languages for people that speak them both? 84 TODO: add option to use both languages for people that speak them both?
88 |;\ # second pattern '; ' 90 |;\ # second pattern '; '
89 |(?<=\S)/(?=\S) # 3.: '/' surrounded by chars 91 |(?<=\S)/(?=\S) # 3.: '/' surrounded by chars
90 |\s+ # by whitespace 92 |\s+ # by whitespace
91 """, re.VERBOSE) 93 """, re.VERBOSE)
92 94
93 print("Reading entries from {}.".format(filename), end='') 95 if not options.just_passphrase:
96 print("Reading entries from {}.".format(filename), end='')
94 counter = 0 # for progress or stopping early 97 counter = 0 # for progress or stopping early
95 with open(filename, "r") as f: 98 with open(filename, "r") as f:
96 for line in f: 99 for line in f:
97 if line[0] == '#': 100 if line[0] == '#':
98 continue 101 continue
109 #TODO: check for very common words and remove them? 112 #TODO: check for very common words and remove them?
110 113
111 counter += 1 114 counter += 1
112 ## stop early when debugging 115 ## stop early when debugging
113 #if counter > 10: break 116 #if counter > 10: break
114 if not counter % 10000: 117 if not options.just_passphrase and counter % 10000 == 0:
115 print('.', end='') 118 print('.', end='')
116 sys.stdout.flush() 119 sys.stdout.flush()
117 print() 120 if not options.just_passphrase:
121 print()
118 122
119 return list(dset) 123 return list(dset)
120 124
121 125
122 def main(): 126 def main():
123 global tainted 127 global tainted
124 128
125 parser = argparse.ArgumentParser(description=__doc__.splitlines()[0]) 129 parser = argparse.ArgumentParser(
130 description=__doc__.splitlines()[0],
131 formatter_class=argparse.ArgumentDefaultsHelpFormatter)
132 parser.add_argument('-n', '--number-of-words', type=int, default=4,
133 help='how many words to draw for the passphrase, '
134 'most useful with -j')
135 parser.add_argument('-j', '--just-passphrase', action="store_true",
136 help='only output the passphrase on a single line')
126 parser.add_argument('--ddump-filename', 137 parser.add_argument('--ddump-filename',
127 help='filename to dump the dictionary to') 138 help='filename to dump the dictionary to')
128 options = parser.parse_args() 139 options = parser.parse_args()
129 140
130 dictionary = buildDictionary(options) 141 dictionary = buildDictionary(options)
131 142
132 howMany = 4 143 how_many = options.number_of_words
133 144
134 # use a dictionary with lower cased words for a simple check if 145 output_string = ""
135 # our random source is okay 146 if not options.just_passphrase:
136 print("\nGenerated passphrase with {} randomly selected words:\n".format( 147 print("\nGenerated passphrase with {}"
137 howMany)) 148 " randomly selected words:\n".format(how_many))
138 print(" ", end='') 149 print(" ", end='')
150 separator = '\n '
151 else:
152 separator = ' '
153
154 # use a dictionary `words` with lower cased words for a rudimentary check
139 words = {} 155 words = {}
140 for x in range(howMany): 156 for x in range(how_many):
141 word = _srandom.choice(dictionary) 157 word = _srandom.choice(dictionary)
142 words[word.lower()] = True 158 words[word.lower()] = True
143 print(word, end='\n ') 159 output_string += word + separator
144 print("\n")
145 160
146 if len(words) < howMany: 161 print(output_string)
147 print("! Your random generator is weak") 162
148 print("! or you are being very lucky.") 163 if len(words) < how_many:
164 sys.stderr.write("! You've drawn a word more than once, this means:\n"
165 "! Your random generation is weak"
166 " or you are being very lucky.\n")
149 tainted = True 167 tainted = True
150 168
151 if tainted: 169 if tainted:
152 print("!!! Don't use the resulting passphrase !!!") 170 sys.exit("!!! Don't use the resulting passphrase !!!")
153 171
154 if __name__ == "__main__": 172 if __name__ == "__main__":
155 main() 173 main()
This site is hosted by Intevation GmbH (Datenschutzerklärung und Impressum | Privacy Policy and Imprint)