[comp.unix.admin] Is the encrypted password's salt simply random?

glenn@rigel.econ.uga.edu (Glenn F. Leavell) (02/27/91)

I'm writing a simple C program to encrypt passwords, so that I can call
it from a shell script when generating new accounts.  To generate the
encrypted password, I'm making use of the crypt() function:

	char *crypt( char *password, char *salt )

I'm using a randomly generated two-character salt from the set [a-zA-Z0-9./],
and everything seems to be working fine.  Here's my question:  is this
the right way to choose the salt - just a random thing?

Please follow-up to comp.unix.admin.  Thanks in advance!
--  
Glenn F. Leavell  Systems Administrator  glenn@rigel.econ.uga.edu  404-542-3488
 University of Georgia Economics Department. 147 Brooks Hall. Athens, GA 30602

kimcm@diku.dk (Kim Christian Madsen) (02/27/91)

glenn@rigel.econ.uga.edu (Glenn F. Leavell) writes:

>I'm writing a simple C program to encrypt passwords, so that I can call
>it from a shell script when generating new accounts.  To generate the
>encrypted password, I'm making use of the crypt() function:

>	char *crypt( char *password, char *salt )

>I'm using a randomly generated two-character salt from the set [a-zA-Z0-9./],
>and everything seems to be working fine.  Here's my question:  is this
>the right way to choose the salt - just a random thing?

The salt is chosen from the indicated range, by random, that is there
is a minor twist.  Usually you use only the time to seed the random
generator, this is also done in finding the salt however the result of
getpid is also used in order to make it further random (two users
updating their passwords at the same time will not get the same
salt).


				Kim Chr. Madsen
				kimcm@diku.dk

glenn@rigel.econ.uga.edu (Glenn F. Leavell) (02/28/91)

In article <1991Feb26.201846.22584@rigel.econ.uga.edu> I recently wrote:
>I'm using a randomly generated two-character salt from the set [a-zA-Z0-9./],
>and everything seems to be working fine.  Here's my question:  is this
>the right way to choose the salt - just a random thing?

Thanks to all who responded!  They are:

  "Ric Anderson" <ric@cs.arizona.edu>
  dkeisen@Gang-of-Four.Stanford.EDU (Dave Eisen)
  auvhess@auvsun1.tamu.edu (David K. Hess)
  chris@cs.uwa.oz.au (chris mcdonald)
  Tim Tsai <it1@Ra.MsState.Edu>
  kimcm@diku.dk (Kim Christian Madsen)
  smb@ulysses.att.com
  Dave Turner (ptsfa!dmturne@ns.PacBell.COM)
  Chris Siebenmann <cks@hawkwind.utcs.toronto.edu>
  jfh@rpp386.Cactus.ORG (John F Haugh II)

The consensus seems to be that using a random salt is the right thing
to do.  Several people where nice enough to give me some advice on
good ways to choose random numbers or to point out how the BSD 4.3
"passwd" program does it.  More on that later.

I'd like to quote from the 9 May 1988 Sun Security Features Guide:

	The password encryption routine crypt() involves a "salt"
	used to perturb the encrypting algorithm, so that DES chips
	cannot be used to assist in cracking login passwords.

Becuase of this, and becuase the two characters of the salt are always the
first two characters of the encrypted password, I was wondering why the
choice of the salt really made any difference.   It was Chris Siebenmann
who sent me a response that cleared that up.  So, I'll highlight some of  
the respones that I received including Chris Siebenmann's.

Chris Siebenmann <cks@hawkwind.utcs.toronto.edu> writes:
 >The purpose of the salt is to increase the amount of encryption work that
 >must be done to check every password in the password file (and to move
 >it out of the realm of reasonableness to store, pre-encrypted, all the
 >words in a dictionary). Thus, you want (if possible) each password in
 >your password file to have a different salt, so a would-be cracker must
 >do
 >	# of words * # of passwords
 >encryptions to check every word against every password, instead of (at
 >worst) just the number of words. A useful reference for this is the
 >paper on password security for Unix in the V7 manual or the 1st Bell
 >Technical Journal on Unix (reprinted as something like "AT&T Readings
 >on Unix: <something>").

Ric Anderson <ric@cs.arizona.edu> writes:
 >The old BSD 4.3 "passwd" program uses 
 >    (void)time(&salt);
 >    salt = 9 * getpid();
 >    saltc[0] = salt & 077;
 >    saltc[1] = (salt>>6) & 077;
 >    for (i = 0; i < 2; i++) {
 >        c = saltc[i] + '.';
 >        if (c > '9')
 >            c += 7;
 >        if (c > 'Z')
 >            c += 6;
 >        saltc[i] = c;
 >    }
 >    return(crypt(pwbuf, saltc));
 >which is based on the time of day clock.

Kim Christian Madsen <kimcm@diku.dk> writes:
 >The salt is chosen from the indicated range, by random, that is there
 >is a minor twist.  Usually you use only the time to seed the random
 >generator, this is also done in finding the salt however the result of
 >getpid is also used in order to make it further random (two users
 >updating their passwords at the same time will not get the same
 >salt).

Finally, John F Haugh II <jfh@rpp386.Cactus.ORG> sent the following, which
describes a method for generating a random number:

 >what you are doing is correct, but i doubt that how you are doing
 >it is.  you need to get a =real= random number.  there are several
 >good sources of random numbers, and random number generators aren't
 >one of them.
 >
 >this is just an idea, but you might find it helpful if you've
 >never done anything like this and want to do it up right.
 >
 >you need to get several numbers, such as process ID, uptime in 
 >the finest resolution you can get, time of day clock in the
 >finest resolution you can get, and fold all of those bits into
 >the 12-bit resolution of the salt.  you want to do the folding in
 >a chaos-preserving fashion - that is, don't just stick all the
 >low 12 bits in the lower 12 bits of the salt, shift, repeat, etc.,
 >because if you do this carelessly with a 32 bit number you wind
 >up with more slop on the low end than the high end, and that will
 >cost you some amount of randomness lost.  try something like
 >merging the low order TOD bits with the high order uptime bits
 >and splater the PID bits in the middle, fold in half (which will
 >give you 32 bits on BSD machines because (struct timeval) is 64
 >bits wide - in which case, fold in half twice), then take that
 >16 bit mess and fold the top four bits into the other 12 bits
 >using the 12 bits to determine where they go.  something like
 >counting the one bits in the each of four three bit groups, then
 >use that to decide which bits gets twiddled.  after you are done
 >with that, split that 12 bit result into a pair of 6 bit numbers
 >that give you the [./A-Za-z0-9] alphabet characters for the salt.

Again, thanks to all who responded!
--  
Glenn F. Leavell  Systems Administrator  glenn@rigel.econ.uga.edu  404-542-3488
 University of Georgia Economics Department. 147 Brooks Hall. Athens, GA 30602

seeger@thedon.cis.ufl.edu (F. L. Charles Seeger III) (03/01/91)

In article <1991Feb27.202424.16444@rigel.econ.uga.edu> glenn@rigel.econ.uga.edu (Glenn F. Leavell) summarizes:
|In article <1991Feb26.201846.22584@rigel.econ.uga.edu> I recently wrote:
|>I'm using a randomly generated two-character salt from the set [a-zA-Z0-9./],
|>and everything seems to be working fine.  Here's my question:  is this
|>the right way to choose the salt - just a random thing?

Included as part of the summary:

| >The old BSD 4.3 "passwd" program uses 
| >    (void)time(&salt);
| >    salt = 9 * getpid();
| >    saltc[0] = salt & 077;
| >    saltc[1] = (salt>>6) & 077;
| >    for (i = 0; i < 2; i++) {
| >        c = saltc[i] + '.';
| >        if (c > '9')
| >            c += 7;
| >        if (c > 'Z')
| >            c += 6;
| >        saltc[i] = c;
| >    }
| >    return(crypt(pwbuf, saltc));
| >which is based on the time of day clock.

Note that the salt generated by this code does not depend on the time.  The
assignment in the second line discards the result of the time() call.  The
fix that I have seen suggest is to change the assignment operator from "="
to "+=".  However, the UCB folks seem to have changed the code more
drastically.

static char sccsid[] = "@(#)passwd.c    4.42 (Berkeley) 6/19/90";
...
char *
getnewpasswd(pw, temp)
        register struct passwd *pw;
        char *temp;
{
        register char *p, *t;
        char buf[_PASSWORD_LEN+1], salt[2], *crypt(), *getpass();
...
        /* grab a random printable character that isn't a colon */
        (void)srandom((int)time((time_t *)NULL));
#ifdef NEWSALT
        salt[0] = '_';
        to64(&salt[1], (long)(29*25), 4);
        to64(&salt[5], (long)random(), 4);
#else
        to64(&salt[0], (long)random(), 2);
#endif
        return(crypt(buf, salt));
}

Personally, I'm a bit suspicious of NEWSALT and the "&salt[5]".

Chuck
-- 
  Charles Seeger    E301 CSE Building             Office: +1 904 392 1508
  CIS Department    University of Florida         Fax:    +1 904 392 1220
  seeger@ufl.edu    Gainesville, FL 32611-2024