Coverage for genpw/genpw.py : 98%
Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1""" GENPW - Generate pronounceable passwords """
3# This program uses statistics on the frequency of three-letter sequences in
4# English to generate passwords. The statistics are generated from your
5# dictionary by the program load_trigram.
6#
7# See www.multicians.org/thvv/gpw.html for history and info.
8# Tom Van Vleck
9#
10# THVV 06/01/94 Coded
11# THVV 04/14/96 converted to Java
12# THVV 07/30/97 fixed for Netscape 4.0
13# THVV 11/27/09 ported to Javascript
14# CB 10/12/16 ported to Python
15# python port Copyright (C) 2017 Emergence by Design Inc. - All Rights Reserved
16# python port Copyright (C) 2020 Arrai Innovations Inc. - All Rights Reserved
18from csv import reader
19from os.path import dirname
20from os.path import join as path_join
21from secrets import randbelow
24class TrigramLoader(object):
25 """Wrapper for loading the word frequency data."""
27 trigrams = None
29 @classmethod
30 def is_loaded(cls):
31 """Return whether the trigrams have been loaded."""
32 return cls.trigrams is not None
34 @classmethod
35 def load_trigrams(cls):
36 """Load the trigrams from the data files."""
37 if cls.is_loaded():
38 return cls.trigrams
40 trigrams = []
41 for char_one in [chr(x) for x in range(ord("A"), ord("Z") + 1)]:
42 char_data = []
43 trigram_file = open(path_join(dirname(__file__), "trigrams", "{}.csv".format(char_one)))
44 trigram_reader = reader(trigram_file, delimiter="\t")
45 for data_line in trigram_reader:
46 if data_line:
47 char_data.append(tuple(int(x) for x in data_line))
48 trigrams.append(tuple(char_data))
49 trigram_file.close()
50 cls.trigrams = tuple(trigrams)
51 return trigrams
54def first_three_chrs(_alphabet, _trigram, ranno):
55 output = ""
56 rotating_sum = 0
57 for ch1 in range(0, 26):
58 for ch2 in range(0, 26):
59 for ch3 in range(0, 26):
60 rotating_sum += _trigram[ch1][ch2][ch3]
62 if rotating_sum > ranno:
63 output += _alphabet[ch1]
64 output += _alphabet[ch2]
65 output += _alphabet[ch3]
66 return output, rotating_sum
67 return output, rotating_sum
70def pronounceable_passwd(pwl):
71 """Return a password of length in range [3, max(pwl, 3)]."""
72 _alphabet = "abcdefghijklmnopqrstuvwxyz"
74 # letter frequencies
75 _trigram = TrigramLoader.load_trigrams()
77 # Pick a random starting point.
78 ranno = randbelow(125730)
79 output, rotating_sum = first_three_chrs(_alphabet, _trigram, ranno)
81 # Now do a random walk.
82 nchar = 3
83 while nchar < pwl:
84 ch1 = _alphabet.find(output[nchar - 2])
85 ch2 = _alphabet.find(output[nchar - 1])
86 rotating_sum = 0
87 for ch3 in range(0, 26):
88 rotating_sum += _trigram[ch1][ch2][ch3]
89 if not rotating_sum:
90 # the trigrams don't know where to go next, end early
91 return output
92 ranno = randbelow(rotating_sum)
93 rotating_sum = 0
94 for ch3 in range(0, 26):
95 rotating_sum += _trigram[ch1][ch2][ch3]
96 if rotating_sum > ranno:
97 output += _alphabet[ch3]
98 break # break for loop
99 nchar += 1
101 return output