Hide keyboard shortcuts

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 """ 

2 

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 

17 

18from csv import reader 

19from os.path import dirname 

20from os.path import join as path_join 

21from secrets import randbelow 

22 

23 

24class TrigramLoader(object): 

25 """Wrapper for loading the word frequency data.""" 

26 

27 trigrams = None 

28 

29 @classmethod 

30 def is_loaded(cls): 

31 """Return whether the trigrams have been loaded.""" 

32 return cls.trigrams is not None 

33 

34 @classmethod 

35 def load_trigrams(cls): 

36 """Load the trigrams from the data files.""" 

37 if cls.is_loaded(): 

38 return cls.trigrams 

39 

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 

52 

53 

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] 

61 

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 

68 

69 

70def pronounceable_passwd(pwl): 

71 """Return a password of length in range [3, max(pwl, 3)].""" 

72 _alphabet = "abcdefghijklmnopqrstuvwxyz" 

73 

74 # letter frequencies 

75 _trigram = TrigramLoader.load_trigrams() 

76 

77 # Pick a random starting point. 

78 ranno = randbelow(125730) 

79 output, rotating_sum = first_three_chrs(_alphabet, _trigram, ranno) 

80 

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 

100 

101 return output