1
/* GPW - Generate pronounceable passwords
2
This program uses statistics on the frequency of three-letter sequences
3
in your dictionary to generate passwords. The statistics are in trigram.h,
4
generated there by the program loadtris. Use different dictionaries
5
and you'll get different statistics.
7
This program can generate every word in the dictionary, and a lot of
8
non-words. It won't generate a bunch of other non-words, call them the
9
unpronounceable ones, containing letter combinations found nowhere in
10
the dictionary. My rough estimate is that if there are 10^6 words,
11
then there are about 10^9 pronounceables, out of a total population
12
of 10^11 8-character strings. I base this on running the program a lot
13
and looking for real words in its output.. they are very rare, on the
14
order of one in a thousand.
16
This program uses "drand48()" to get random numbers, and "srand48()"
17
to set the seed to the microsecond part of the system clock. Works
18
for AIX C++ compiler and runtime. Might have to change this to port
19
to another environment.
21
The best way to use this program is to generate multiple words. Then
22
pick one you like and transform it with punctuation, capitalization,
23
and other changes to come up with a new password.
33
#include <sys/types.h>
34
/* following for SysV. */
35
/* #include <bsd/sys/time.h> */
36
/* following for BSD */
39
int main (int argc, char ** argv) {
40
int password_length; /* how long should each password be */
41
int n_passwords; /* number of passwords to generate */
42
int pwnum; /* number generated so far */
43
int c1, c2, c3; /* array indices */
44
long sumfreq; /* total frequencies[c1][c2][*] */
45
double pik; /* raw random number in [0.1] from drand48() */
46
long ranno; /* random number in [0,sumfreq] */
47
long sum; /* running total of frequencies */
48
char password[100]; /* buffer to develop a password */
49
int nchar; /* number of chars in password so far */
50
struct timeval systime; /* time reading for random seed */
51
struct timezone tz; /* unused arg to gettimeofday */
53
password_length = 8; /* Default value for password length */
54
n_passwords = 10; /* Default value for number of pws to generate */
56
gettimeofday (&systime, &tz); /* Read clock. */
57
srand48 (systime.tv_usec); /* Set random seed. */
59
if (argc > 1) { /* If args are given, convert to numbers. */
60
n_passwords = atoi (&argv[1][0]);
62
password_length = atoi (&argv[2][0]);
65
if (argc > 3 || password_length > 99 ||
66
password_length < 0 || n_passwords < 0) {
67
printf (" USAGE: gpw [npasswds] [pwlength]\n");
71
/* Pick a random starting point. */
72
/* (This cheats a little; the statistics for three-letter
73
combinations beginning a word are different from the stats
74
for the general population. For example, this code happily
75
generates "mmitify" even though no word in my dictionary
76
begins with mmi. So what.) */
77
for (pwnum=0; pwnum < n_passwords; pwnum++) {
78
pik = drand48 (); /* random number [0,1] */
79
sumfreq = sigma; /* sigma calculated by loadtris */
80
ranno = pik * sumfreq; /* Weight by sum of frequencies. */
82
for (c1=0; c1 < 26; c1++) {
83
for (c2=0; c2 < 26; c2++) {
84
for (c3=0; c3 < 26; c3++) {
85
sum += tris[c1][c2][c3];
86
if (sum > ranno) { /* Pick first value */
87
password[0] = 'a' + c1;
88
password[1] = 'a' + c2;
89
password[2] = 'a' + c3;
90
c1 = c2 = c3 = 26; /* Break all loops. */
96
/* Do a random walk. */
97
nchar = 3; /* We have three chars so far. */
98
while (nchar < password_length) {
99
password[nchar] = '\0';
100
password[nchar+1] = '\0';
101
c1 = password[nchar-2] - 'a'; /* Take the last 2 chars */
102
c2 = password[nchar-1] - 'a'; /* .. and find the next one. */
104
for (c3=0; c3 < 26; c3++)
105
sumfreq += tris[c1][c2][c3];
106
/* Note that sum < duos[c1][c2] because
107
duos counts all digraphs, not just those
108
in a trigraph. We want sum. */
109
if (sumfreq == 0) { /* If there is no possible extension.. */
110
break; /* Break while nchar loop & print what we have. */
112
/* Choose a continuation. */
114
ranno = pik * sumfreq; /* Weight by sum of frequencies for row. */
116
for (c3=0; c3 < 26; c3++) {
117
sum += tris[c1][c2][c3];
119
password[nchar++] = 'a' + c3;
120
c3 = 26; /* Break the for c3 loop. */
124
printf ("%s\n", password);