/*
* Generate (hopefully) pronounceable random passwords. These can often be
* remembered more easily than completely random passwords, and are immune to
* dictionary searches, etc.
*
* The original version of this program was written in BASIC on an OSI
* Superboard II SBC. That version is long gone (the SB2's cassette drive
* was never trustworthy, it eventually scrambled the tape), but the basic
* (pardon the pun) idea lives on here, with a few modification like basing
* the selection on "graphs" (actually, they are a restricted set of phonemes)
* and having randomly-selected spellings for those graphs.
*/
#include <stdio.h>
#include <math.h>
#include <sys/time.h>
#include <unistd.h>
#include <stdlib.h>
#ifdef ALLBYOPTS
#include <getopt.h>
#endif
#ifdef RAND48
extern double drand48();
#define RANDOM(c) ((int) ((drand48() * (c))))
#else
#define RANDOM(c) ((int) (rand(c) / 32767.0 * (c)))
#endif
char *spelling[] = {
/*a*/ "a", (char *) 0, /* 2*/
/*A*/ "a", "ae", "ai", (char *) 0, /* 6*/
/*b*/ "b", (char *) 0, /* 8*/
/*ch*/ "ch", (char *) 0, /*10*/
/*d*/ "d", (char *) 0, /*12*/
/*e*/ "e", (char *) 0, /*14*/
/*E*/ "e", "ee", "ie", (char *) 0, /*18*/
/*f*/ "f", "ph", "gh", (char *) 0, /*22*/
/*g*/ "g", (char *) 0, /*24*/
/*h*/ "h", (char *) 0, /*26*/
/*i*/ "i", "e", (char *) 0, /*29*/
/*I*/ "i", "ai", (char *) 0, /*32*/
/*i'*/ "i", "ei", (char *) 0, /*35*/
/*j*/ "j", "g", (char *) 0, /*38*/
/*k*/ "k", "c", (char *) 0, /*41*/
/*l*/ "l", (char *) 0, /*43*/
/*m*/ "m", (char *) 0, /*45*/
/*n*/ "n", (char *) 0, /*47*/
/*ng*/ "ng", (char *) 0, /*49*/
/*o*/ "o", "a", "ah", (char *) 0, /*53*/
/*O*/ "o", "oh", (char *) 0, /*56*/
#ifndef ALLBYOPT
/*place */"oo", "u", (char *) 0, /*59*/
/*holder*/"oo", "w", (char *) 0, /*62*/
#else
#ifdef ALTPHON
/*oo*/ "u", "u", (char *) 0, /*59*/
/*OO*/ "oo", "w", (char *) 0, /*62*/
#else
/*oo*/ "oo", "u", (char *) 0, /*59*/
/*OO*/ "oo", "w", (char *) 0, /*62*/
#endif
#endif
/*p*/ "p", (char *) 0, /*64*/
/*qu*/ "qu", (char *) 0, /*66*/
/*r*/ "r", (char *) 0, /*68*/
/*s*/ "s", "c", (char *) 0, /*71*/
/*sh*/ "sh", "s", (char *) 0, /*74*/
/*t*/ "t", (char *) 0, /*76*/
/*th*/ "th", (char *) 0, /*78*/
/*TH*/ "th", (char *) 0, /*80*/
/*u*/ "u", (char *) 0, /*82*/
/*U*/ "u", "oo", (char *) 0, /*85*/
/*v*/ "v", (char *) 0, /*87*/
/*x*/ "x", (char *) 0, /*89*/
/*y*/ "y", (char *) 0, /*91*/
/*z*/ "z", "s", (char *) 0, /*94*/
};
#ifdef ALLBYOPTS
char *alt_phonics[] =
{
/* 0*/ /*56*/
/*oo*/ "u", "u", (char *) 0, /* 3*/ /*59*/
/*OO*/ "oo", "w", (char *) 0 /* 6*/ /*62*/
};
char *reg_phonics[] =
{
/* 0*/ /*56*/
/*oo*/ "oo", "u", (char *) 0, /* 3*/ /*59*/
/*OO*/ "oo", "w", (char *) 0 /* 6*/ /*62*/
};
void alter_spelling_table(int wants_alternate_phonics)
{
char **p;
char **q = &spelling[56];
int i;
if(wants_alternate_phonics)
p = alt_phonics;
else
p = reg_phonics;
for(i = 0; i < 6; ++i)
*q++ = *p++;
}
#endif
struct graph {
char *graph;
char type;
#define CONSONANT 0
#define VOWEL_LONG 1
#define VOWEL_SHORT 2
#define VOWEL_OTHER 3
#define VOWEL_MASK 3
#ifdef ALLBYOPTS
#define NUMERAL 8
#else
#ifdef NUMERALS
#define NUMERAL 8
#endif
#endif
#define iscons(c) (((c)->type & VOWEL_MASK) == 0)
#define isvowel(c) (((c)->type & VOWEL_MASK) != 0)
/* char frequency; */ /* unused for now */
char **spellings;
/* struct graph **following; */ /* maybe later */
} graph[] = {
"a", VOWEL_SHORT, &spelling[0],
"A", VOWEL_LONG, &spelling[2],
"b", CONSONANT, &spelling[6],
"ch", CONSONANT, &spelling[8],
"d", CONSONANT, &spelling[10],
"e", VOWEL_SHORT, &spelling[12],
"E", VOWEL_LONG, &spelling[14],
"f", CONSONANT, &spelling[18],
"g", CONSONANT, &spelling[22],
"h", CONSONANT, &spelling[24],
"i", VOWEL_SHORT, &spelling[26],
"I", VOWEL_LONG, &spelling[29],
"i'", VOWEL_OTHER, &spelling[32],
"j", CONSONANT, &spelling[35],
"k", CONSONANT, &spelling[38],
"l", CONSONANT, &spelling[41],
"m", CONSONANT, &spelling[43],
"n", CONSONANT, &spelling[45],
"ng", CONSONANT, &spelling[47],
"o", VOWEL_SHORT, &spelling[49],
"O", VOWEL_LONG, &spelling[53],
"oo", VOWEL_SHORT, &spelling[56],
"OO", VOWEL_LONG, &spelling[59],
"p", CONSONANT, &spelling[62],
"qu", CONSONANT, &spelling[64],
"r", CONSONANT, &spelling[66],
"s", CONSONANT, &spelling[68],
"sh", CONSONANT, &spelling[71],
"t", CONSONANT, &spelling[74],
"th", CONSONANT, &spelling[76],
"TH", CONSONANT, &spelling[78],
"u", VOWEL_SHORT, &spelling[80],
"U", VOWEL_LONG, &spelling[82],
"v", CONSONANT, &spelling[85],
"x", CONSONANT, &spelling[87],
"y", CONSONANT, &spelling[89],
"z", CONSONANT, &spelling[91],
0, 0, &spelling[94],
};
struct graph *vowel[] = {
&graph[0], &graph[1], &graph[5], &graph[6],
&graph[10], &graph[11], &graph[12], &graph[19],
&graph[20], &graph[21], &graph[22], &graph[30],
&graph[31],
(struct graph *) 0,
};
struct graph *consonant[] = {
&graph[2], &graph[3], &graph[4], &graph[7],
&graph[8], &graph[9], &graph[13], &graph[14],
&graph[15], &graph[16], &graph[17], &graph[18],
&graph[23], &graph[24], &graph[25], &graph[26],
&graph[27], &graph[28], &graph[29], &graph[32],
&graph[33], &graph[34], &graph[35],
(struct graph *) 0,
};
/*
* Randomly select a graph from the specifield array. Eventually, this should
* account for graph frequencies as well.
*/
struct graph *selgraph(graphs)
struct graph **graphs;
{
register int cnt;
for (cnt = 0; graphs[cnt] != (struct graph *) 0; cnt++)
;
return graphs[RANDOM(cnt)];
}
/*
* Randomly select a spelling for the specified graph. This is not linear:
* earlier spellings are preferred over later ones, but the latter do
* sometimes sneak in.
*/
char *selspell(graph)
struct graph *graph;
{
register int cnt, sel;
for (cnt = 0; graph->spellings[cnt] != (char *) 0; cnt++)
;
if (cnt == 0) {
fprintf(stderr, "PANIC: selspell(%s) got count(spellings) == 0\n", graph->graph);
exit(2);
}
if (cnt == 1)
return *graph->spellings;
/*
* This may not be the best way to do it... maybe Weemba'd care to lend a
* hand here? After all, my specialty is programming, NOT math.
*/
if
(
(sel = cnt - (int) sqrt((double) RANDOM(cnt * cnt) + 1) - 1) < 0
||
sel >= cnt
)
{
#ifdef BUGCATCH
fprintf
(
stderr,
"PANIC: selspell(%s) got nlrand(%d) == %d\n",
graph->graph,
cnt,
sel
);
exit(2);
#else
sel = 0;
#endif
}
return graph->spellings[sel];
}
/*
* Choose the next source for a graph. The rules are: a consonant MUST be
* followed by a vowel; a vowel may be followed by a vowel of a different
* type or by a consonant, but never more than two consecutive vowel graphs.
*/
choosenext(cur, prev)
{
if (cur == CONSONANT)
return VOWEL_MASK;
else if (prev == -1 || (prev & VOWEL_MASK) != 0)
return CONSONANT;
else if (RANDOM(10) == 5)
return VOWEL_MASK;
else
return CONSONANT;
}
/*
* We are passed an array of (struct graph *); choose an entry randomly and
* assemble a string fitting the size constraint. We use the original (OSI)
* paradigm: alternate consonants and vowels, with the option of two vowels
* in a row occasionally. The only difference is that they must be different
* *types* of vowels, a distinction that the OSI version didn't consider.
*/
#ifdef ALLBYOPTS
pwgen
(
struct graph **initial,
char *pw,
int maxlen,
int wants_capitalize,
int wants_numerals
)
#else
pwgen
(
struct graph **initial,
char *pw,
int maxlen
)
#endif
{
int pwlen, state, prev, tmp;
struct graph *graph;
char *spelling;
int capc;
capc=0;
pwlen = 0;
state = initial[0]->type;
prev = -1;
while (pwlen < maxlen - 1)
{
do
{
graph = selgraph(initial);
} while (state != CONSONANT && graph->type == prev);
if ((spelling = selspell(graph)) == (char *) 0)
{
fprintf
(
stderr,
"PANIC: got NULL in selspell(%s)\n",
graph->graph
);
exit(2);
}
strcpy(pw, spelling);
if ( (state==CONSONANT) && ( (capc++)==1) )
{
#ifdef ALLBYOPTS
if(wants_numerals)
{
*pw++ = (RANDOM(10) + '0');
pwlen++;
strcpy(pw, spelling);
}
#else
#ifdef NUMERALS
*pw++ = (RANDOM(10) + '0');
pwlen++;
strcpy(pw, spelling);
#endif
#endif
#ifdef ALLBYOPTS
if(wants_capitalize)
{
*pw = toupper(*pw);
}
#else
#ifdef CAPITALIZE
*pw = toupper(*pw);
#endif
#endif
}
while (*pw != '\0')
pwlen++, pw++;
tmp = prev;
prev = graph->type;
if ((state = choosenext(prev, tmp)) == CONSONANT)
initial = consonant;
else
initial = vowel;
}
}
void sec_pass(char *buf, int len) {
static const int low = '!';
static const int high = '~';
const float range = high - low;
struct timeval t;
int i;
gettimeofday(&t, 0);
srand48(t.tv_sec + t.tv_usec * 102457 + getpid() + getppid());
for ( i = 0 ; i < len ; i++ ) {
int c = low + (int)(drand48() * range);
switch ( c ) {
case '\\':
case '\'':
case '\"':
c = '%';
}
buf[i]=c;
}
buf[i]='\0';
}
#ifdef ALLBYOPTS
#define ALTPHON_OPT 'a'
#define CAPITALIZE_OPT 'c'
#define NUMERALS_OPT 'n'
#define SECURE_OPT 's'
#define HELP_OPT 'h'
#define END_SHORT_OPT '\0'
#define END_LONG_OPT {(char *) 0, 0, (int *) 0, '\0'}
const char short_blank = END_SHORT_OPT;
const struct option long_blank = END_LONG_OPT;
int optionlen(struct option *option_array)
{
int result = 0;
while(option_array[result].name != NULL)
result++;
return result;
}
void remove_short_option(char option, char *option_array)
{
int i;
int found_index = -1; /* index of found index !or! -1 if none */
int size = strlen(option_array);
for(i = 0; i < size; i++)
{
if(option_array[i] == option)
{
found_index = i;
break;
}
}
if(found_index != -1)
{
if(found_index != size - 1) /* not last item? */
{
/* replace found item with last item */
option_array[found_index] = option_array[size - 1];
}
/* blank out last item */
option_array[size - 1] = short_blank;
}
}
void remove_long_option(char option, struct option *option_array)
{
int i;
int found_index = -1; /* index of found index !or! -1 if none */
int size = optionlen(option_array);
for(i = 0; i < size; i++)
{
if(option_array[i].val == option)
{
found_index = i;
break;
}
}
if(found_index != -1)
{
if(found_index != size - 1) /* not last item? */
{
/* replace found item with last item */
option_array[found_index] = option_array[size - 1];
}
/* blank out last item */
option_array[size - 1] = long_blank;
}
}
void print_option_array(struct option *option_array)
{
int i = 0;
while(option_array[i].val != short_blank)
{
if(option_array[i].flag != NULL)
printf
(
"{%s, %d, %d, '%c'=%x}\n",
option_array[i].name,
option_array[i].has_arg,
*option_array[i].flag,
(char) option_array[i].val,
option_array[i].val
);
else
printf
(
"{%s, %d, NULL, '%c'=%x}\n",
option_array[i].name,
option_array[i].has_arg,
(char) option_array[i].val,
option_array[i].val
);
i++;
}
}
struct option longopts[] =
{
{"alt-phonics", no_argument, (int *) 0, ALTPHON_OPT },
{"capitalize", no_argument, (int *) 0, CAPITALIZE_OPT },
{"numerals", no_argument, (int *) 0, NUMERALS_OPT },
{"secure", no_argument, (int *) 0, SECURE_OPT },
{"help", no_argument, (int *) 0, HELP_OPT },
END_LONG_OPT
};
char opt_string[] =
{
ALTPHON_OPT,
CAPITALIZE_OPT,
NUMERALS_OPT,
SECURE_OPT,
HELP_OPT,
END_SHORT_OPT
};
void disable_option(char *opt_chars)
{
while(*opt_chars)
{
remove_short_option(*opt_chars, opt_string);
remove_long_option(*opt_chars, longopts);
opt_chars++;
}
}
#endif
void usage(FILE *out, char *name)
{
fprintf(out, "Usage: %s ", name);
#ifdef ALLBYOPTS
fprintf(out, "[ -acns --alt-phonics --capitalize ");
fprintf(out, "--numerals --secure ] ");
fprintf(out, "maxlength [count]\n");
fprintf(out, "Use the command \"pwgen -s maxlen [count]\" ");
#else
fprintf(out, "maxlength [count]\n");
fprintf(out, "Use the command \"spwgen maxlen [count]\" ");
#endif
fprintf(out, "to generate more secure passwords.\n");
}
int main(int argc, char **argv)
{
int first_nonopt = 1; /* index of the first non-option in argv */
int is_bad_opt = 0; /* true if unrecognized option */
int cnt, len, wants_secure=0;
char buf[20];
#ifdef ALLBYOPTS
int wants_alternate_phonics = 0; /* boolean from options */
int wants_capitalize = 0; /* boolean from options */
int wants_numerals = 0; /* boolean from options */
int wants_help = 0; /* boolean from options */
int getopt_result;
int longindex = 0;
getopt_result =
getopt_long
(
argc,
argv,
opt_string,
longopts,
&longindex
);
while(getopt_result != EOF)
{
/* process opts here */
switch(getopt_result)
{
case ALTPHON_OPT:
wants_alternate_phonics = 1;
disable_option("ash");
break;
case CAPITALIZE_OPT:
wants_capitalize = 1;
disable_option("csh");
break;
case NUMERALS_OPT:
wants_numerals = 1;
disable_option("nsh");
break;
case SECURE_OPT:
wants_secure = 1;
disable_option("acnsh");
break;
case HELP_OPT:
wants_help = 1;
disable_option("acnsh");
break;
case '?':
default:
is_bad_opt = 1;
break;
}
if(is_bad_opt)
break;
else
getopt_result =
getopt_long
(
argc,
argv,
opt_string,
longopts,
&longindex
);
}
if(! is_bad_opt)
{
alter_spelling_table(wants_alternate_phonics);
}
first_nonopt = optind;
#endif
if(wants_help)
{
usage(stdout, argv[0]);
printf("You can use -a, -c or -n in any combination.\n");
printf("You can't use these with -s or vise versa.\n");
printf("\n");
printf("\n");
printf("OPTIONS\n");
printf("\n");
printf(" -a or --alt-phonics\n");
printf(" use a slightly altered phonics table to generate\n");
printf(" syllables.\n");
printf("\n");
printf(" -c or --capitalize\n");
printf(" use (not so) random capitalizations in generated\n");
printf(" passwords\n");
printf("\n");
printf(" -n or --numerals\n");
printf(" use (not so) random placement of numerals in\n");
printf(" generated passwords\n");
printf("\n");
printf(" -s or --secure\n");
printf(" generate totally random, unmemorable, unpronouncable,\n");
printf(" but secure and unguessable passwords\n");
printf("\n");
printf(" -h or --help\n");
printf(" print this message and exit successfully\n");
printf("\n");
printf("\n");
printf("ARGUMENTS\n");
printf("\n");
printf(" maxlength (required with -a, -c, -n or -s)\n");
printf(" a number spec ifying the masimum length\n");
printf(" of each generated password\n");
printf("\n");
printf(" count\n");
printf(" the number of passwords generated. each will\n");
printf(" be on its own line.\n");
printf("\n");
printf("SEE ALSO: man pwgen for details.\n");
printf("\n");
exit(0);
}
else if
(
(argc < first_nonopt + 1)
||
(argc > first_nonopt + 3)
|| (is_bad_opt)
)
{
usage(stderr, argv[0]);
return 1;
}
if ((len = atoi(argv[first_nonopt + 0])) < 4 || len > 16)
{
fprintf
(
stderr,
"%s: invalid maxlength %s\n",
argv[0],
argv[first_nonopt + 0]
);
return 1;
}
if (argc == first_nonopt + 1)
cnt = 1;
else if ((cnt = atoi(argv[first_nonopt + 1])) < 1)
{
fprintf
(
stderr,
"%s: invalid count %s\n",
argv[0],
argv[first_nonopt + 1]
);
return 1;
}
/*
* Changed '|' into '^' in the srand() formula to get a better RNG
* Vincent Renardias <vincent@waw.com>
* Jan, 11th 1997.
*/
#ifdef RAND48
srand48((time(0)<<9) ^ (getpgrp()<<15) ^ (getpid()) ^ (time(0)>>11));
#else
srand(time(0) ^ (getpgrp() << 8) + getpid());
#endif
while (cnt-- != 0)
{
if (wants_secure == 1)
{
sec_pass(buf,len);
}
else
{
#ifdef ALLBYOPTS
pwgen
(
(RANDOM(10) < 4? vowel: consonant),
buf,
len,
wants_capitalize,
wants_numerals
);
#else
pwgen((RANDOM(10) < 4? vowel: consonant), buf, len);
#endif
}
printf("%s\n", buf);
}
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1