[net.sources] random password generator

charles@utastro.UUCP (Charles Sandel) (07/25/84)

---------

Below is a "shar" archive file which contains the ingredients
to make "randpasswd" - a program which will generate random
passwords.  With no arguments, the passwords generated
may contain lower- or upper-case ASCII alphas, or ASCII numerics, and
will be of a random length between MINLENGTH (default=6) and MAXDESLEN
(default=10).  The program will accept two optional arguments:
a numeric argument indicating the desired length of the generated
password, and a "-w" indicating that the program should attempt
to generate a "real-sounding English word".

The program has its uses, particularly for "uucp" administrators
who have a separate login and password for each neighboring site,
and are faced with the problem of occasionally coming up with a new password
for everyone.

The attempt at making "real" words is done by using a table lookup to
find out what characters are legal to follow the previous character.
The advantage of this method is that it is simple; the disadvantage
is that it is *too* simple, and doesn't take into account any
rules of English grammar.  But this can be overcome, by having the
program generate passwords until you find one you like.  Of course,
you can always use the generated password as inspiration and mung
it until you like it.  Try this from the cshell:

while (1)
? randpasswd -w
? sleep 1
? end

... the "sleep" will give you time to decide if you liked the
previous password.

"Randpasswd" is written for sites running UN*X 4.2BSD, as it uses both
gettimeofday(2) and the suite of routines from random(3).  There
is no reason why it should prove difficult to adapt to other
UN*Xes, though, just making appropriate substitutions of time(2)
and rand(3).

The archive file contains the Makefile, the source and include
files, and a manual page.

------------(cut here)------------------------------------
: This is a shar archive.  Extract with sh, not csh.
echo x - Makefile
cat > Makefile << '!Funky!Stuff!'
DEST	      = .
EXTHDRS	      = /usr/include/sys/time.h
HDRS	      = defs.h
LDFLAGS	      = -s
CFLAGS	      = -O
LIBS	      =
LINKER	      = cc
MAKEFILE      = Makefile
OBJS	      = randpasswd.o
PRINT	      = pr
PROGRAM	      = randpasswd
SRCS	      = randpasswd.c

all:		$(PROGRAM)

$(PROGRAM):     $(OBJS) $(LIBS)
		@echo -n "Loading $(PROGRAM) ... "
		@$(LINKER) $(LDFLAGS) $(OBJS) $(LIBS) -o $(PROGRAM)
		@echo "done"

clean:;		@rm -f $(OBJS)

depend:;	@mkmf -f $(MAKEFILE) PROGRAM=$(PROGRAM) DEST=$(DEST)

index:;		@ctags -wx $(HDRS) $(SRCS)

install:	$(PROGRAM)
		@echo Installing $(PROGRAM) in $(DEST)
		@install -s $(PROGRAM) $(DEST)

print:;		@$(PRINT) $(HDRS) $(SRCS)

program:        $(PROGRAM)

tags:           $(HDRS) $(SRCS); @ctags $(HDRS) $(SRCS)

update:		$(DEST)/$(PROGRAM)

$(DEST)/$(PROGRAM): $(SRCS) $(LIBS) $(HDRS) $(EXTHDRS)
		@make -f $(MAKEFILE) DEST=$(DEST) install
###
randpasswd.o: /usr/include/sys/time.h defs.h
!Funky!Stuff!
echo x - defs.h
cat > defs.h << '!Funky!Stuff!'
#define	MAXLENGTH	16	/* maximum length of passwords */
#define	MINLENGTH	6	/* minimum length of passwords */
#define	MAXDESLEN	10	/* maximum desired length of passwords */

/* defined indexes into the randrange array of structures */
#define	ANY		-1	/* produce any random value from any range */
#define	NUMERIC		0	/* index of the numeric range */
#define	LALPHA		1	/* index of the lower alpha range */
#define	UALPHA		2	/* index of the upper alpha range */
/* extra ranges to cover the entire 7-bit ASCII range */
/*#define	CONTROL		3	/* index of the range NUL through US */
/*#define	PUNCT1		4	/* index of the range SP through /  */
/*#define	PUNCT2		5	/* index of the range : through @  */
/*#define	PUNCT3		6	/* index of the range [ through `  */
/*#define	PUNCT4		7	/* index of the range { through DEL  */

#define	STATESZ		256	/* determines randomness of number generation */
#define	NRANGE		3	/* number of ranges used */


struct	randrange	{
		unsigned	seed;
		char	state[STATESZ];
		int	width;
		char	offset;
		};

/* define what characters may follow any character */
struct	after	{
	char	ch;
	char	*follow;
	};

struct	randrange	start, range[NRANGE] =
/* 	seed		state		width		offset */
{
	0,		"",		10,		'0',
	0,		"",		26,		'a',
	0,		"",		26,		'A'
/* other ASCII ranges may be useful; uncomment the ones needed
,
	0,		"",		32,		'\0',
	0,		"",		16,		' ',
	0,		"",		7,		':',
	0,		"",		6,		'\0134',
	0,		"",		5,		'{'
*/
};

/* ch		follow */
struct	after	aft[] = {
'a',	"abcdefghijklmnopqrstuvwxyz",
'b',	"abdeilorstuwy",
'c',	"aehikorstuwy",
'd',	"adeioruy",
'e',	"abcdefghijklmnopqrstuvwxyz",
'f',	"aefiloruy",
'g',	"aeghilnopruwy",
'h',	"aefimnorsy",
'i',	"abcdefghijklmnopqrstuvwxyz",
'j',	"aeghijklouy",
'k',	"acehijklorstuwxy",
'l',	"aeiklmnoptuvwy",
'm',	"abegiklmnopqrstuwxyz",
'n',	"adegiknorstuxyz",
'o',	"abcdefghijklmnopqrstuvwxyz",
'p',	"aehilopqrstuwyz",
'q',	"aeioruy",
'r',	"acdefgiklmnopqrstuvxyz",
's',	"acehiklmnopstuvwxyz",
't',	"aehiortuyz",
'u',	"abcdefghijklmnopqrstuvwxyz",
'v',	"aeiouvwy",
'w',	"aefghiklorsuvwy",
'x',	"aeilouxyz",
'y',	"acdeiklopqsuvwxyz",
'z',	"acehilopqrstuwyz"
};
!Funky!Stuff!
echo x - randpasswd.c
cat > randpasswd.c << '!Funky!Stuff!'
/*
	randpasswd - generate a random password

	Calling syntax:

	randpasswd [-w] [length]

		where "length" is an optional numeric argument 
		specifying the length of the password needed.
		If "length" is lesser or greater than MINLENGTH or
		MAXLENGTH, it is rounded up or down to a legal value.
		Passwords of length between MINLENGTH and MAXLENGTH
		are therefore
		possible, but if no argument is provided, a password of
		random length between MINLENGTH and MAXDESLEN is produced.
		If the argument "-w" is specified, "randpasswd" will attempt
		to generate a "real-sounding" English word, in lower-case
		alpha, sans numerics.

	Compilation:  cc -O -s randpasswd.c -o randpasswd

	Author: Charles Sandel
		University of Texas
		Department of Astronomy
		Austin, Texas  78712
		July 24, 1984

		uucp:  utastro!charles
		arpa:  charles@utastro.UTEXAS.ARPA

*/

#include	<sys/time.h>
#include	"defs.h"

char	passwd[MAXLENGTH+1];

long	random();
char	*initstate(), *setstate();

main(argc, argv)
char	*argv[];
{
	int	i, length, word=0;
	struct	timeval	tp;
	struct	timezone tzp;

	/* initialize generic state */
	gettimeofday(&tp, &tzp);
	start.seed = tp.tv_usec;
	initstate(start.seed, start.state, STATESZ);
	setstate(start.state);

	length = -1;

	for(i=1; i<argc && i<3; i++)
	{
		if(strcmp("-w", argv[i]) == 0)
			word++;
		else
		{
			length = atoi(argv[i]);
			if(length < MINLENGTH)
				length = MINLENGTH;
			if(length > MAXLENGTH)
				length = MAXLENGTH;
		}
	}

	if(length == -1)
		length = random()%(MAXDESLEN - MINLENGTH + 1) + MINLENGTH;

	/* initalize range states & seeds */
	for(i=0; i<NRANGE; i++)
	{
		gettimeofday(&tp, &tzp);
		range[i].seed = tp.tv_usec + random()%(NRANGE*STATESZ);
		initstate(range[i].seed, range[i].state, STATESZ);
	}

	/* make a password */
	if(word != 0)
	{
		passwd[0] = randchar(LALPHA);
		setstate(range[LALPHA].state);
		for(i=1; i<length; i++)
			passwd[i] = wordchar(passwd[i-1]);
	}
	else
		for(i=0; i<length; i++)
			passwd[i] = randchar(ANY);

	passwd[++i] = '\0';

	printf("%s\n", passwd);
}

randchar(inx)
{
	long	r;
	char	c;

	if(inx == ANY || inx < 0 || inx > NRANGE-1)
	{
		/* set generic state and get a random index into the array */
		setstate(start.state);
		r = random()%NRANGE;
	}
	else
		r = inx;

	/* set state for that range and get a random character from it */
	setstate(range[r].state);
	c = random() % range[r].width + range[r].offset;

	return(c);
}

wordchar(pc)
char	pc;
{
	register	len, i;
	char	c;

	i = pc - 'a';
	len = strlen(aft[i].follow);

	c = aft[i].follow[random()%len];

	return(c);
	}
!Funky!Stuff!
echo x - randpasswd.l
cat > randpasswd.l << '!Funky!Stuff!'
.TH RANDPASSWD 1L "24 July 1984"
.UC 4
.SH NAME
randpasswd \- generate a random password
.SH SYNOPSIS
.B randpasswd
[
.B \-w
]
[
.B length
]
.SH DESCRIPTION
This command generates a random password
which can be used as a login password, or as
an encryption key.
.PP
There are two optional arguments:

.TP
.B \-w
If included, an attempt will be made to generate a password which, though
nonsensical, might sound like a "real word".
The resulting password will be in all lower-case ASCII characters.
If omitted, the resulting password my contain lower- or upper-case
ASCII characters, or ASCII numerics.
.TP
.B length
"Length" is a number indicating the desired length
of the resulting password.  "Length" may be between
6 and 16.  Larger or smaller requests are rounded
up or down to a legal value.  If this argument is
omitted, the resulting length will be a random value
between 6 and 10.
.PP
For most uses, you should probably get
.B randpasswd
to generate passwords until you find one you like.  Try the
following from csh:
.ti +5
while (1)
.ti +10
randpasswd -w
.ti +5
end

or this from sh:
.ti +5
while (true)
.ti +5
do
.ti +10
randpasswd -w
.ti +5
done
.PP
.SH "SEE ALSO"
login(1), passwd(5), crypt(3)
.br
Robert Morris and Ken Thompson,
.I UNIX password security
.SH BUGS
The attempt to generate a "real word" is simplistic, and
makes no attempt at simulating the rules of English grammar.
It is, however, effective if you try enough times.
.SH DIAGNOSTICS
None.  Silence is Golden.
.SH AUTHOR
Charles Sandel
.br
University of Texas
.br
Department of Astronomy
.br
Austin, Texas  78712
!Funky!Stuff!
-- 
                     *>> Charles Sandel <<*
      uucp:  {ut-sally, ut-ngp, noao, charm}!utastro!charles
            arpa:  chaz@ut-ngp  or  charles@ut-sally
                      at&t:  (512) 471-4461 x439

perl@rdin2.UUCP (Robert Perlberg) (07/27/84)

<Isn't it sad what happened to the G'gugvunts and the Vl'hurgs?>

>Try this from the cshell:
>
>while (1)
>? randpasswd -w
>? sleep 1
>? end
>
>... the "sleep" will give you time to decide if you liked the
>previous password.

You've been caught, Charles.  I ran the above loop without
the sleep and found out why you REALLY put the sleep in.
Since the random number generator is seeded by the time of
day IN SECONDS, two runs of randpasswd within the same
second will generate the same password.  Users of randpasswd
should keep this in mind if using randpasswd to generate a
block of passwords that will not be examined.

Robert Perlberg
Resource Dynamics Inc.
New York
philabs!rdin!rdin2!perl