Mercurial > ppgen
annotate ppgen.py @ 3:757625ec8364
Comment added hint about SLT's Go implementation.
author | Bernhard Reiter <bernhard@intevation.de> |
---|---|
date | Mon, 02 May 2016 09:26:29 +0200 |
parents | a099246680ae |
children | 85c65a597420 |
rev | line source |
---|---|
0 | 1 #!/usr/bin/env python3 |
2 """Create a passphrase from a few random words. DRAFT | |
3 | |
4 Relies on the entropy of python's | |
5 random.SystemRandom class | |
6 which (according to the documentation) calls os.urandom() | |
7 which (according to the documentation) calls the operating system | |
8 specific randomness source which "should be unpredictable | |
9 enough for cryptographic applications" | |
10 | |
11 Requires: | |
12 * Python v>=3.2 | |
13 * a dictionary, Ding's trans-de-en by default. | |
14 E.g. on a Debian/Ubuntu system in package "trans-de-en". | |
15 or from http://ftp.tu-chemnitz.de/pub/Local/urz/ding/de-en/ | |
16 | |
1
00ed7df30fe4
Checking for 8k entries now. Comment improvements.
Bernhard Reiter <bernhard@intevation.de>
parents:
0
diff
changeset
|
17 Uses a hardcoded filepath and language. |
0 | 18 Search for **customize** below to change it. |
19 | |
3
757625ec8364
Comment added hint about SLT's Go implementation.
Bernhard Reiter <bernhard@intevation.de>
parents:
2
diff
changeset
|
20 Related: There is a Go implementation started by Sascha L. Teichmann at |
757625ec8364
Comment added hint about SLT's Go implementation.
Bernhard Reiter <bernhard@intevation.de>
parents:
2
diff
changeset
|
21 https://bitbucket.org/s_l_teichmann/ppgen |
757625ec8364
Comment added hint about SLT's Go implementation.
Bernhard Reiter <bernhard@intevation.de>
parents:
2
diff
changeset
|
22 |
757625ec8364
Comment added hint about SLT's Go implementation.
Bernhard Reiter <bernhard@intevation.de>
parents:
2
diff
changeset
|
23 |
0 | 24 Copyright 2016 by Intevation GmbH. |
1
00ed7df30fe4
Checking for 8k entries now. Comment improvements.
Bernhard Reiter <bernhard@intevation.de>
parents:
0
diff
changeset
|
25 Author: Bernhard E. Reiter <bernhard@intevation.de> |
0 | 26 |
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). | |
29 """ | |
30 | |
31 import math | |
32 import re | |
33 import sys | |
34 | |
35 from random import SystemRandom | |
36 _srandom = SystemRandom() | |
37 | |
38 tainted = False # to be set if we find a hint that the passphrase may be weak | |
39 | |
40 def buildDictionary(): | |
41 """Build up a dictionary of unique words, calculate stats.""" | |
42 global tainted | |
43 d = [] | |
44 | |
45 # dictionary for testing | |
46 #d = ["abc", "aBc", "cde", "efg", "hij", "blubber", "jikf", "zug", "lmf", "opq"] | |
2
a099246680ae
Fix for the unique test.
Bernhard Reiter <bernhard@intevation.de>
parents:
1
diff
changeset
|
47 # second test dictionary to show that different string functions are used. |
a099246680ae
Fix for the unique test.
Bernhard Reiter <bernhard@intevation.de>
parents:
1
diff
changeset
|
48 #d = [''.join('A' * 1000) for _ in range(1000)] |
0 | 49 |
50 # Using the dictionary from Ding **customize** | |
51 d = readDingDict(filename="/usr/share/trans/de-en", useLeft=True) | |
52 | |
53 ## for debugging purpuses, dump dictionary | |
54 #dumpfilename = "ddump.txt" | |
55 #print("Writing out {}.".format(dumpfilename)) | |
56 #with open(dumpfilename, "w") as f: | |
57 # for i in d: | |
58 # f.write("{}\n".format(i)) | |
59 | |
60 # Print some stats on the dictionary to be used | |
61 dl = len(d) | |
1
00ed7df30fe4
Checking for 8k entries now. Comment improvements.
Bernhard Reiter <bernhard@intevation.de>
parents:
0
diff
changeset
|
62 print("Found {:d} dictionary entries.".format(dl)) |
00ed7df30fe4
Checking for 8k entries now. Comment improvements.
Bernhard Reiter <bernhard@intevation.de>
parents:
0
diff
changeset
|
63 if dl < 8000: |
00ed7df30fe4
Checking for 8k entries now. Comment improvements.
Bernhard Reiter <bernhard@intevation.de>
parents:
0
diff
changeset
|
64 print("!Your dictionary is below 8k entries, that is quite small!") |
0 | 65 tainted = True |
66 | |
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 return d | |
72 | |
73 | |
74 def readDingDict(filename = "/usr/share/trans/de-en", useLeft=False): | |
75 """Read dictionary with unique words from file in Ding format. | |
76 | |
77 useLeft: Boolean to control which language to use | |
78 | |
79 TODO: add option to use both languages for people that speak them both? | |
80 """ | |
81 | |
82 dset = set() #using the datatype 'set' to aviod duplicates | |
83 | |
84 splitter = re.compile(r"""\ \|\ # first pattern ' | ' | |
85 |;\ # second pattern '; ' | |
86 |(?<=\S)/(?=\S) # 3.: '\' surrounded by chars | |
87 |\s+ # by whitespace | |
88 """,re.VERBOSE) | |
89 | |
90 print("Reading entries from {}.".format(filename), end='') | |
91 counter = 0 # for progress or stopping early | |
92 with open(filename, "r") as f: | |
93 for line in f: | |
94 if line[0] == '#': continue | |
95 | |
96 # languages are separated by " :: " | |
97 p = line.partition(" :: ") | |
98 languageEntry = p[0] if useLeft else p[2] | |
99 | |
100 for word in splitter.split(languageEntry): | |
101 word = word.strip('(",.)\'!:;').rstrip('/') | |
102 if len(word) > 2 and not word[0] in '[{/': | |
103 dset.add(word) | |
104 | |
105 #TODO: check for very common words and remove them? | |
106 | |
107 counter += 1 | |
108 ## stop early when debugging | |
109 #if counter > 10: break | |
110 if not counter % 10000: | |
111 print('.', end='') | |
112 sys.stdout.flush() | |
113 print() | |
114 | |
115 return list(dset) | |
116 | |
117 def main(): | |
118 global tainted | |
119 dictionary = buildDictionary() | |
120 | |
121 howMany = 4 | |
122 | |
123 # use a dictionary with lower case words for a simple check if | |
124 # our random source is okay | |
125 print("\nGenerated passphrase with {} randomly selected words:\n".format( | |
126 howMany)) | |
127 print(" ", end='') | |
128 words = {} | |
129 for x in range(howMany): | |
130 word = _srandom.choice(dictionary) | |
2
a099246680ae
Fix for the unique test.
Bernhard Reiter <bernhard@intevation.de>
parents:
1
diff
changeset
|
131 words[word.lower()]= True |
0 | 132 print(word, end='\n ') |
133 print("\n") | |
134 | |
135 if len(words) < howMany: | |
136 print("! Your random generator is weak") | |
137 print("! or you are being very lucky.") | |
138 tainted = True | |
139 | |
140 if tainted: | |
141 print("!!! Don't use the resulting passphrase !!!") | |
142 | |
143 if __name__ == "__main__": | |
144 main() |