Subject: crypt(3) again
To: None <current-users@netbsd.org>
From: der Mouse <mouse@Collatz.McRCIM.McGill.EDU>
List: current-users
Date: 11/18/1994 12:39:26
This is my replacement lib/libcrypt, with patches to usr.bin/passwd.
Note that other programs haven't been touched; login, su, etc are all
entirely compatible with the new libcrypt, as far as I can tell.

To use this, blow away your lib/libcrypt source, then replace it with
the shar below.  Then apply the patches (after the shar) to
usr.bin/passwd.  Then rebuild and reinstall libcrypt and passwd, and if
you care about them, anything you may have that's linked statically
with -lcrypt.  sbin/init is the only thing I know of that qualifies by
default.

NOTE: This is still fairly alpha.  I'm running it successfully here,
but I can't promise anything.

This does not include a working descrypt.c.  I will happily mail the
one I have (which I wrote) to anyone whom I have reason to believe is
in the US or Canada.  If you have a working old-style DES-based
crypt(), rename it to des_crypt and it should drop right in.

Comments, criticism, compliments, etc, invited.

					der Mouse

			    mouse@collatz.mcrcim.mcgill.edu

First the shar of lib/libcrypt:
#! /bin/sh
#
# Shar: Shell Archiver
#
# This archive created Fri Nov 18 12:16:04 1994
# Run this through sh to create:
#	Makefile
#	crypt.3
#	crypt.c
#	descrypt_dummy.c
#	md5crypt.c
echo x - Makefile \(592 characters\)
sed 's/^X//' > Makefile << \EOF
X# This directory contains an implementation of crypt(3) and associated
X# routines.  the file descrypt.c can't be shipped out of the US.  it was put
X# into this directory to make distribution of exportable and non-exportable
X# systems easier.
X#
X#	$Id: Makefile,v 1.4 1993/10/07 01:36:21 cgd Exp $
X
XLIB=	crypt
X
XSRCS = crypt.c md5crypt.c
X.if exists(descrypt.c) && !defined(EXPORTABLE_SYSTEM)
XSRCS += descrypt.c
X.else
XSRCS += descrypt_dummy.c
X.endif
X
XMAN3=	crypt.0
XMLINKS= crypt.3 encrypt.3 crypt.3 setkey.3 crypt.3 crypt_makesalt.3 crypt.3 des_crypt.3 crypt.3 md5_crypt.3
X
X.include <bsd.lib.mk>
EOF
if test 592 -ne "`wc -c Makefile`"
then
echo shar: error transmitting Makefile \(should have been 592 characters\)
fi
echo x - crypt.3 \(10173 characters\)
sed 's/^X//' > crypt.3 << \EOF
X.\" Copyright (c) 1989, 1991 The Regents of the University of California.
X.\" All rights reserved.
X.\"
X.\" Redistribution and use in source and binary forms, with or without
X.\" modification, are permitted provided that the following conditions
X.\" are met:
X.\" 1. Redistributions of source code must retain the above copyright
X.\"    notice, this list of conditions and the following disclaimer.
X.\" 2. Redistributions in binary form must reproduce the above copyright
X.\"    notice, this list of conditions and the following disclaimer in the
X.\"    documentation and/or other materials provided with the distribution.
X.\" 3. All advertising materials mentioning features or use of this software
X.\"    must display the following acknowledgement:
X.\"	This product includes software developed by the University of
X.\"	California, Berkeley and its contributors.
X.\" 4. Neither the name of the University nor the names of its contributors
X.\"    may be used to endorse or promote products derived from this software
X.\"    without specific prior written permission.
X.\"
X.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
X.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
X.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
X.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
X.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
X.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
X.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
X.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
X.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
X.\" SUCH DAMAGE.
X.\"
X.\"     from: @(#)crypt.3	6.7 (Berkeley) 5/21/91
X.\"	$Id: crypt.3,v 1.1 1993/10/07 01:36:22 cgd Exp $
X.\"
X.Dd May 21, 1991
X.Dt CRYPT 3
X.Os
X.Sh NAME
X.Nm crypt ,
X.Nm crypt_makesalt ,
X.Nm md5_crypt ,
X.Nm des_crypt ,
X.Nm setkey ,
X.Nm encrypt ,
X.Nm des_setkey ,
X.Nm des_cipher
X.Nd password and DES encryption
X.Sh SYNOPSIS
X.Ft char
X.Fn *crypt_makesalt "void"
X.Ft char
X.Fn *crypt "const char *key" "const char *hash"
X.Ft char
X.Fn *md5_crypt "const char *key" "const char *hash"
X.Ft char
X.Fn *des_crypt "const char *key" "const char *setting"
X.Ft int
X.Fn setkey "char *key"
X.Ft int
X.Fn encrypt "char *block" "int flag"
X.Ft int
X.Fn des_setkey "const char *key"
X.Ft int
X.Fn des_cipher "const char *in" "char *out" "long salt" "int count"
X.Sh DESCRIPTION
XThe
X.Fn crypt
Xfunction performs password hashing.  It is based on the MD5
Xmessage-digest algorithm (see RFC1321).  The first argument to
X.Fn crypt
Xis a
X.Dv NUL Ns -terminated
Xcleartext password (normally obtained from the user).  The second is
Xeither a hashed password (obtained from wherever it was stored when it
Xwas initially set) or a salt string, typically returned by
X.Eo \&
X.Fn crypt_makesalt
X.Ec ,
Xthough you can pass your own salt string; see below.  (The first form
Xis normally used when checking a password, the second when setting
Xone.)
X.Fn crypt
Xunderstands three types of hashed passwords: old
X.Dq traditional
XDES-style hashed passwords, such as most other UNIX-related systems
Xuse, new DES-style hashed passwords, such as previous versions of
XNetBSD used, and new MD5-style hashed passwords.  Support for the first
Xtwo depends on having
X.Fn des_crypt
Xavailable (see below); support for MD5-style hashed passwords is always
Xavailable.  This is done by testing the
X.Fa hash
Xargument; if it fits the syntax of either of the two DES-style hashed
Xpasswords, or the salt strings typically used with them,
X.Fn crypt
Xsimply calls
X.Fn des_crypt
Xand returns the result.  Otherwise,
X.Fn crypt
Xcalls
X.Fn md5_crypt
Xand returns the result.
X.Pp
X.Fn md5_crypt
Xis just like
X.Eo \&
X.Fn crypt
X.Ec ,
Xexcept that it does not check its salt string to see if it's a
XDES-style hashed password.
X.Pp
X.Fn crypt_makesalt
Xconstructs and returns a salt string suitable for passing to
X.Fn crypt
Xas the
X.Fa hash
Xargument; it obtains the salt values from the exact time of day, the
Xprocess ID, and any other convenient values it thinks may be useful in
Xproducing a
X.Dq random
Xstring.  If you have on hand other strings you feel may help make the
Xsalt string even more
X.Do
Xrandom
X.Dc ,
Xyou can build a longer string yourself, including them, and pass that
Xto
X.Eo \&
X.Fn crypt
X.Ec .
XThe only restriction is that the string you ultimately pass to
X.Fn crypt
Xmust not be confusable with a hashed password.  The simplest way to
Xensure this is to pass a string including a
X.Do
X.Li :
X.Dc ,
Xbecause colons can never appear in a hashed password.
XAny string beginning with a string returned by
X.Fn crypt_makesalt
Xwill always be suitable.
X.Pp
XThe remaining functions exist in all versions of the system, but since
Xthey are based on the Data Encryption Standard and thus fall under
Xexport restrictions, systems outside the United States of America will
Xnormally have them replaced by dummy functions that do not do anything
Xuseful.  They are used internally by
X.Fn crypt
Xfor checking DES-style hashed passwords, and may additionally be useful
Xfor doing DES encryption.  This document describes the non-dummy
Xversions; the dummy versions simply print a warning and return.
X.Pp
XThe
X.Xr des_crypt
Xfunction
Xperforms old-style password encryption.
XIt is derived from the
X.Tn NBS
XData Encryption Standard.
XAdditional code has been added to deter
Xkey search attempts.
XThe first argument to
X.Nm des_crypt
Xis
Xa
X.Dv NUL Ns -terminated
Xstring (normally a password typed by a user).
XThe second is a character array, 9 bytes in length, consisting of an
Xunderscore (``_'') followed by 4 bytes of iteration count and 4 bytes
Xof salt.
XBoth the iteration
X.Fa count
Xand the 
X.Fa salt
Xare encoded with 6 bits per character, least significant bits first.
XThe values 0 to 63 are encoded by the characters ``./0-9A-Za-z'',
Xrespectively.
X.Pp
XThe
X.Fa salt
Xis used to induce disorder in to the
X.Tn DES
Xalgorithm
Xin one of 16777216
Xpossible ways
X(specifically, if bit
X.Em i
Xof the
X.Ar salt
Xis set then bits
X.Em i
Xand
X.Em i+24
Xare swapped in the
X.Tn DES
X``E'' box output).
XThe
X.Ar key
Xis divided into groups of 8 characters (a short final group is null-padded)
Xand the low-order 7 bits of each each character (56 bits per group) are
Xused to form the DES key as follows: the first group of 56 bits becomes the
Xinitial DES key.
XFor each additional group, the XOR of the group bits and the encryption of
Xthe DES key with itself becomes the next DES key.
XThen the final DES key is used to perform
X.Ar count
Xcumulative encryptions of a 64-bit constant.
XThe value returned is a
X.Dv NUL Ns -terminated
Xstring, 20 bytes in length, consisting
Xof the
X.Ar setting
Xfollowed by the encoded 64-bit encryption.
X.Pp
XFor compatibility with historical versions of
X.Xr crypt 3 ,
Xthe
X.Ar setting
Xmay consist of 2 bytes of salt, encoded as above, in which case an
Xiteration
X.Ar count
Xof 25 is used, fewer perturbations of
X.Tn DES
Xare available, at most 8
Xcharacters of
X.Ar key
Xare used, and the returned value is a
X.Dv NUL Ns -terminated
Xstring 13 bytes in length.
X.Pp
XThe
Xfunctions,
X.Fn encrypt ,
X.Fn setkey ,
X.Fn des_setkey
Xand
X.Fn des_cipher
Xallow limited access to the
X.Tn DES
Xalgorithm itself.
XThe
X.Ar key
Xargument to
X.Fn setkey
Xis a 64 character array of
Xbinary values (numeric 0 or 1).
XA 56-bit key is derived from this array by dividing the array
Xinto groups of 8 and ignoring the last bit in each group.
X.Pp
XThe
X.Fn encrypt
Xargument
X.Fa block
Xis also a 64 character array of
Xbinary values.
XIf the value of
X.Fa flag
Xis 0,
Xthe argument
X.Fa block
Xis encrypted, otherwise it
Xis decrypted.
XThe encryption or decryption is returned in the original
Xarray
X.Fa block
Xafter using the
Xkey specified
Xby
X.Fn setkey
Xto process it.
X.Pp
XThe
X.Fn des_setkey
Xand
X.Fn des_cipher
Xfunctions are faster but less portable than
X.Fn setkey
Xand
X.Fn encrypt .
XThe argument to
X.Fn des_setkey
Xis a character array of length 8.
XThe
X.Em least
Xsignificant bit in each character is ignored and the next 7 bits of each
Xcharacter are concatenated to yield a 56-bit key.
XThe function
X.Fn des_cipher
Xencrypts (or decrypts if
X.Fa count
Xis negative) the 64-bits stored in the 8 characters at
X.Fa in
Xusing
X.Xr abs 3
Xof
X.Fa count
Xiterations of
X.Tn DES
Xand stores the 64-bit result in the 8 characters at
X.Fa out .
XThe
X.Fa salt
Xspecifies perturbations to
X.Tn DES
Xas described above.
X.Pp
XThe functions
X.Fn crypt
Xand
X.Fn des_crypt
Xreturn a pointer to the encrypted value on success and NULL on failure.
XThe functions
X.Fn setkey ,
X.Fn encrypt ,
X.Fn des_setkey ,
Xand
X.Fn des_cipher
Xreturn 0 on success and 1 on failure.
XThe function
X.Fn crypt_makesalt
Xreturns a pointer to the generated salt string; it
X.Do
Xcannot fail
X.Dc .
XHistorically, the functions
X.Fn setkey
Xand
X.Fn encrypt
Xdid not return any value.
XThey have been provided return values primarily to distinguish
Ximplementations where hardware support is provided but not
Xavailable or where the DES encryption is not available due to the
Xusual political silliness.
X.Sh SEE ALSO
X.Xr login 1 ,
X.Xr passwd 1 ,
X.Xr getpass 3 ,
X.Xr passwd 5
X.sp
X.Rs
X.%T "Mathematical Cryptology for Computer Scientists and Mathematicians"
X.%A Wayne Patterson
X.%D 1987
X.%N ISBN 0-8476-7438-X
X.Re
X.Rs
X.%T "Password Security: A Case History"
X.%A R. Morris
X.%A Ken Thompson
X.%J "Communications of the ACM"
X.%V vol. 22
X.%P pp. 594-597
X.%D Nov. 1979
X.Re
X.Rs
X.%T "DES will be Totally Insecure within Ten Years"
X.%A M.E. Hellman
X.%J "IEEE Spectrum"
X.%V vol. 16
X.%P pp. 32-39
X.%D July 1979
X.Re
X.Sh HISTORY
XA rotor-based
X.Fn crypt
Xfunction appeared in
X.At v6 .
XDES-based
X.Fn crypt
Xfirst appeared in
X.At v7 .
XThe current MD5-based
X.Fn crypt
Xfirst appeared post-1.0 NetBSD.
X.Sh BUGS
X.Fn crypt_makesalt
Xand
X.Fn crypt
Xreturn pointers to internal static strings.  The string returned by one
Xof these functions is valid only until the next time that function is
Xcalled again, at which point the string will be modified by the return
Xstring from the later call.
X.Pp
XDropping the
X.Em least
Xsignificant bit in each character of the argument to
X.Fn des_setkey
Xis ridiculous.
EOF
if test 10173 -ne "`wc -c crypt.3`"
then
echo shar: error transmitting crypt.3 \(should have been 10173 characters\)
fi
echo x - crypt.c \(813 characters\)
sed 's/^X//' > crypt.c << \EOF
X#include <string.h>
X
Xextern char *des_crypt(const char *, const char *);
Xextern char *md5_crypt(const char *, const char *);
X
Xint __crypt_tst64stringp(const void *sarg)
X{
X const unsigned char *s;
X static unsigned char v[] = {   0,  0,  0,  0,  0,192,255,  3, /*  0.. 63*/
X			      254,255,255,  7,254,255,255,  7, /* 64..127*/
X			      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*128..255*/
X			      };
X
X for (s=sarg;*s;s++) if (! (v[*s/8] & (1<<(*s%8)))) return(0);
X return(1);
X}
X
Xchar *crypt(const char *key, const char *salt)
X{
X int saltlen;
X
X saltlen = strlen(salt);
X if ( ( ((saltlen == 13) || (saltlen == 2)) &&
X	__crypt_tst64stringp(salt) ) ||
X      ( (salt[0] == '_') &&
X	((saltlen == 20) || (saltlen == 9)) &&
X	__crypt_tst64stringp(salt+1) ) )
X  { return(des_crypt(key,salt));
X  }
X return(md5_crypt(key,salt));
X}
EOF
if test 813 -ne "`wc -c crypt.c`"
then
echo shar: error transmitting crypt.c \(should have been 813 characters\)
fi
echo x - descrypt_dummy.c \(3621 characters\)
sed 's/^X//' > descrypt_dummy.c << \EOF
X/*
X * Copyright (c) 1989 The Regents of the University of California.
X * All rights reserved.
X *
X * This code is derived from software contributed to Berkeley by
X * Tom Truscott.
X *
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions
X * are met:
X * 1. Redistributions of source code must retain the above copyright
X *    notice, this list of conditions and the following disclaimer.
X * 2. Redistributions in binary form must reproduce the above copyright
X *    notice, this list of conditions and the following disclaimer in the
X *    documentation and/or other materials provided with the distribution.
X * 3. All advertising materials mentioning features or use of this software
X *    must display the following acknowledgement:
X *	This product includes software developed by the University of
X *	California, Berkeley and its contributors.
X * 4. Neither the name of the University nor the names of its contributors
X *    may be used to endorse or promote products derived from this software
X *    without specific prior written permission.
X *
X * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
X * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
X * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
X * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
X * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
X * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
X * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
X * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
X * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
X * SUCH DAMAGE.
X */
X
X#if defined(LIBC_SCCS) && !defined(lint)
X/*static char *sccsid = "from: @(#)crypt.c	5.11 (Berkeley) 6/25/91";*/
Xstatic char *rcsid = "$Id: crypt_dummy.c,v 1.2 1993/10/07 01:43:14 cgd Exp $";
X#endif /* LIBC_SCCS and not lint */
X
X#include <unistd.h>
X#include <stdio.h>
X
X/*
X * UNIX password, and DES, encryption.
X *
X * since this is non-exportable, this is just a dummy.  if you want
X * real encryption, make sure you've got libcrypt.a around.
X */
X
Xstatic char     cryptresult[1+4+4+11+1];        /* "encrypted" result */
X
Xchar *
Xdes_crypt(key, setting)
X	register const char *key;
X	register const char *setting;
X{
X	static int warned;
X
X	if (!warned) {
X		fprintf(stderr, "WARNING!  des_crypt(3) not present in the system!\n");
X		warned = 1;
X	}
X	strncpy(cryptresult, key, sizeof cryptresult);
X	cryptresult[sizeof cryptresult - 1] = '\0';
X	return(cryptresult);
X}
X
X
Xdes_setkey(key)
X	register const char *key;
X{
X	static int warned;
X
X	if (!warned) {
X        	fprintf(stderr, "WARNING!  des_setkey(3) not present in the system!\n");
X		warned = 1;
X	}
X	return(0);
X}
X
Xdes_cipher(in, out, salt, num_iter)
X	const char *in;
X	char *out;
X	long salt;
X	int num_iter;
X{
X	static int warned;
X
X	if (!warned) {
X        	fprintf(stderr, "WARNING!  des_cipher(3) not present in the system!\n");
X		warned = 1;
X	}
X	bcopy(in, out, 8);
X	return(0);
X}
X
Xsetkey(key)
X	register const char *key;
X{
X	static int warned;
X
X	if (!warned) {
X        	fprintf(stderr, "WARNING!  setkey(3) not present in the system!\n");
X		warned = 1;
X	}
X	return(0);
X}
X
Xencrypt(block, flag)
X	register char *block;
X	int flag;
X{
X	static int warned;
X
X	if (!warned) {
X	        fprintf(stderr, "WARNING!  encrypt(3) not present in the system!\n");
X		warned = 1;
X	}
X	return(0);
X}
EOF
if test 3621 -ne "`wc -c descrypt_dummy.c`"
then
echo shar: error transmitting descrypt_dummy.c \(should have been 3621 characters\)
fi
echo x - md5crypt.c \(9743 characters\)
sed 's/^X//' > md5crypt.c << \EOF
X#include <stdio.h>
X#include <sys/time.h>
X
X#define VERSION_MD5 1
X
X#define DEF_VERSION VERSION_MD5
X
X#define MIN_ROUNDS 2
X#define DEF_ROUNDS 64
X#define MAX_ROUNDS 10240
X
X/* MD5 stuff, mostly straight out of the RFC */
X
Xtypedef unsigned long int U32; /* unsigned, 32 bits */
X
X/* T[i] = floor((1<<32)*abs(sin(i+1))), where sin arg is in radians */
Xstatic U32 T[64] = {
X	0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
X	0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
X	0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
X	0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
X	0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
X	0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
X	0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
X	0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
X	0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
X	0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
X	0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
X	0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
X	0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
X	0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
X	0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
X	0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 };
X
Xtypedef struct state STATE;
Xstruct state {
X  U32 A;
X  U32 B;
X  U32 C;
X  U32 D;
X  U32 X[16];
X  int xfill; /* # of bytes added since last crunch_block() */
X  U32 bitlen_l;
X  U32 bitlen_h;
X  } ;
X
X#define F(X,Y,Z) (((X)&(Y))|((~(X))&(Z)))
X#define G(X,Y,Z) (((X)&(Z))|((Y)&(~(Z))))
X#define H(X,Y,Z) ((X)^(Y)^(Z))
X#define I(X,Y,Z) ((Y)^((X)|(~(Z))))
X
X/* rotate v left nbits bits */
Xinline static U32 ROTLEFT(U32 v, unsigned int nbits)
X{
X return((v<<nbits)|(v>>(32-nbits)));
X}
X
X/*
X * A block of 64 input bytes has been accumulated in X[]; crunch it
X *  through the hash machine.  Essentially a transcription of the RFC
X *  algorithm into C.
X */
Xstatic void crunch_block(STATE *state)
X{
X U32 A;
X U32 B;
X U32 C;
X U32 D;
X U32 *X;
X
X A = state->A;
X B = state->B;
X C = state->C;
X D = state->D;
X X = &state->X[0];
X#define OP(a,b,c,d,k,s,i) a = b + ROTLEFT(a+F(b,c,d)+X[k]+T[i-1],s)
X OP(A,B,C,D, 0, 7, 1);OP(D,A,B,C, 1,12, 2);OP(C,D,A,B, 2,17, 3);OP(B,C,D,A, 3,22, 4);
X OP(A,B,C,D, 4, 7, 5);OP(D,A,B,C, 5,12, 6);OP(C,D,A,B, 6,17, 7);OP(B,C,D,A, 7,22, 8);
X OP(A,B,C,D, 8, 7, 9);OP(D,A,B,C, 9,12,10);OP(C,D,A,B,10,17,11);OP(B,C,D,A,11,22,12);
X OP(A,B,C,D,12, 7,13);OP(D,A,B,C,13,12,14);OP(C,D,A,B,14,17,15);OP(B,C,D,A,15,22,16);
X#undef OP
X#define OP(a,b,c,d,k,s,i) a = b + ROTLEFT(a+G(b,c,d)+X[k]+T[i-1],s)
X OP(A,B,C,D, 1, 5,17);OP(D,A,B,C, 6, 9,18);OP(C,D,A,B,11,14,19);OP(B,C,D,A, 0,20,20);
X OP(A,B,C,D, 5, 5,21);OP(D,A,B,C,10, 9,22);OP(C,D,A,B,15,14,23);OP(B,C,D,A, 4,20,24);
X OP(A,B,C,D, 9, 5,25);OP(D,A,B,C,14, 9,26);OP(C,D,A,B, 3,14,27);OP(B,C,D,A, 8,20,28);
X OP(A,B,C,D,13, 5,29);OP(D,A,B,C, 2, 9,30);OP(C,D,A,B, 7,14,31);OP(B,C,D,A,12,20,32);
X#undef OP
X#define OP(a,b,c,d,k,s,i) a = b + ROTLEFT(a+H(b,c,d)+X[k]+T[i-1],s)
X OP(A,B,C,D, 5, 4,33);OP(D,A,B,C, 8,11,34);OP(C,D,A,B,11,16,35);OP(B,C,D,A,14,23,36);
X OP(A,B,C,D, 1, 4,37);OP(D,A,B,C, 4,11,38);OP(C,D,A,B, 7,16,39);OP(B,C,D,A,10,23,40);
X OP(A,B,C,D,13, 4,41);OP(D,A,B,C, 0,11,42);OP(C,D,A,B, 3,16,43);OP(B,C,D,A, 6,23,44);
X OP(A,B,C,D, 9, 4,45);OP(D,A,B,C,12,11,46);OP(C,D,A,B,15,16,47);OP(B,C,D,A, 2,23,48);
X#undef OP
X#define OP(a,b,c,d,k,s,i) a = b + ROTLEFT(a+I(b,c,d)+X[k]+T[i-1],s)
X OP(A,B,C,D, 0, 6,49);OP(D,A,B,C, 7,10,50);OP(C,D,A,B,14,15,51);OP(B,C,D,A, 5,21,52);
X OP(A,B,C,D,12, 6,53);OP(D,A,B,C, 3,10,54);OP(C,D,A,B,10,15,55);OP(B,C,D,A, 1,21,56);
X OP(A,B,C,D, 8, 6,57);OP(D,A,B,C,15,10,58);OP(C,D,A,B, 6,15,59);OP(B,C,D,A,13,21,60);
X OP(A,B,C,D, 4, 6,61);OP(D,A,B,C,11,10,62);OP(C,D,A,B, 2,15,63);OP(B,C,D,A, 9,21,64);
X#undef OP
X state->A += A;
X state->B += B;
X state->C += C;
X state->D += D;
X}
X
X/*
X * Feed some bytes to MD5.  Accumulates them into X[], calling
X *  crunch_block() at each 64-byte boundary to process them.
X */
Xstatic void crunch_bytes(STATE *state, const unsigned char *buf, unsigned int nbytes)
X{
X int xfill;
X U32 *X;
X
X xfill = state->xfill;
X X = &state->X[0];
X for (;nbytes>0;nbytes--)
X  { switch (xfill % 4)
X     { case 0:
X	  X[xfill/4] = *buf;
X	  break;
X       case 1:
X	  X[xfill/4] |= ((U32)*buf) << 8;
X	  break;
X       case 2:
X	  X[xfill/4] |= ((U32)*buf) << 16;
X	  break;
X       case 3:
X	  X[xfill/4] |= ((U32)*buf) << 24;
X	  break;
X     }
X    buf ++;
X    xfill ++;
X    if (xfill >= 64)
X     { crunch_block(state);
X       xfill = 0;
X     }
X  }
X state->xfill = xfill;
X}
X
X/*
X * Initialize the state, per the RFC.
X */
Xstatic void md5_init(STATE *s)
X{
X s->A = 0x67452301;
X s->B = 0xefcdab89;
X s->C = 0x98badcfe;
X s->D = 0x10325476;
X s->xfill = 0;
X s->bitlen_l = 0;
X s->bitlen_h = 0;
X}
X
X/*
X * Process some bytes.  Feed them to crunch_bytes() and also update the
X *  bitlen values to reflect the number of bits fed in.
X */
Xstatic void md5_process_bytes(STATE *s, const void *vbuf, unsigned int nbytes)
X{
X U32 ltmp;
X U32 lsum;
X
X crunch_bytes(s,vbuf,nbytes);
X s->bitlen_h += ((U32)nbytes) >> 29;
X ltmp = ((U32)nbytes) << 3;
X lsum = ltmp + s->bitlen_l;
X if ( (ltmp & s->bitlen_l & 0x80000000) ||
X      ( ((ltmp|s->bitlen_l) & 0x80000000) &&
X	!(lsum & 0x80000000) ) )
X  { s->bitlen_h ++;
X  }
X s->bitlen_l = lsum;
X#undef s
X}
X
X/*
X * Finish the MD5 algorithm; insert padding and the bitstring length,
X *  then extract the result into the supplied 16-byte buffer.
X */
Xstatic void md5_result(STATE *s, unsigned char *result)
X{
X unsigned char lenbytes[8];
X
X crunch_bytes(s,"\200",1);
X while (s->xfill != 56) crunch_bytes(s,"\0",1);
X lenbytes[0] =  s->bitlen_l        & 0xff;
X lenbytes[1] = (s->bitlen_l >>  8) & 0xff;
X lenbytes[2] = (s->bitlen_l >> 16) & 0xff;
X lenbytes[3] = (s->bitlen_l >> 24) & 0xff;
X lenbytes[4] =  s->bitlen_h        & 0xff;
X lenbytes[5] = (s->bitlen_h >>  8) & 0xff;
X lenbytes[6] = (s->bitlen_h >> 16) & 0xff;
X lenbytes[7] = (s->bitlen_h >> 24) & 0xff;
X crunch_bytes(s,&lenbytes[0],8);
X result[ 0] =  s->A        & 0xff;
X result[ 1] = (s->A >>  8) & 0xff;
X result[ 2] = (s->A >> 16) & 0xff;
X result[ 3] = (s->A >> 24) & 0xff;
X result[ 4] =  s->B        & 0xff;
X result[ 5] = (s->B >>  8) & 0xff;
X result[ 6] = (s->B >> 16) & 0xff;
X result[ 7] = (s->B >> 24) & 0xff;
X result[ 8] =  s->C        & 0xff;
X result[ 9] = (s->C >>  8) & 0xff;
X result[10] = (s->C >> 16) & 0xff;
X result[11] = (s->C >> 24) & 0xff;
X result[12] =  s->D        & 0xff;
X result[13] = (s->D >>  8) & 0xff;
X result[14] = (s->D >> 16) & 0xff;
X result[15] = (s->D >> 24) & 0xff;
X}
X
X/* End MD5 stuff */
X
X/* Base 64 conversion */
X
Xstatic const char base64[64] = "./1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM";
X
X/*
X * Convert "from" to base 64 and store it in "to".  Returns a pointer
X *  to just after the last byte of "to" that was modified.  (This will
X *  always be to+ceil((nfb*4)/3).)
X */
Xstatic char *to64(unsigned char *from, char *to, unsigned int nfb)
X{
X unsigned long int cvbuf;
X int cvbits;
X
X cvbuf = 0;
X cvbits = 0;
X while (1)
X  { if (cvbits < 6)
X     { if (nfb < 1)
X	{ if (cvbits > 0) *to++ = base64[cvbuf];
X	  break;
X	}
X       cvbuf |= (*from++ << cvbits);
X       cvbits += 8;
X       nfb --;
X     }
X    *to++ = base64[cvbuf&63];
X    cvbuf >>= 6;
X    cvbits -= 6;
X  }
X return(to);
X}
X
X/* End base 64 conversion */
X
X/*
X * Publicized interface: make a salt string.  This returns something
X *  suitable to hand to crypt()'s second argument, when hashing a
X *  new password to store somewhere.
X */
Xchar *crypt_makesalt(void)
X{
X static char saltbuf[256];
X unsigned char tmp[256];
X struct timeval tv;
X STATE s;
X
X gettimeofday(&tv,0);
X sprintf(&tmp[0],"%lu %lu %d %d",
X	(unsigned long int)tv.tv_sec,
X	(unsigned long int)tv.tv_usec,
X	(int)getpid(),
X	(int)getppid());
X md5_init(&s);
X md5_process_bytes(&s,&tmp[0],sizeof(tmp)); /* deliberately use stack trash */
X md5_result(&s,&tmp[0]);
X saltbuf[0] = ':';
X to64(&tmp[0],&saltbuf[1],16);
X saltbuf[23] = '\0';
X return(&saltbuf[0]);
X}
X
X/*
X * Encrypt a password.  key is the cleartext password; hash is either
X *  the hashed password, from wherever it was stored, or some salt string,
X *  such as one returned by crypt_makesalt().
X *
X * Returned string has the format "=%d.%d.%s%s", where the first number
X *  is the format version (currently 1) and the second is the number of
X *  rounds.  The first string is the salt string, the second the hash
X *  string; both strings will always be 22 bytes long.  If the hash
X *  argument appears to match this format, it is assumed to be a hashed
X *  password; otherwise, it is taken as an arbitrary string and is
X *  MD5-hashed down to a 22-byte salt string that's used.
X */
Xchar *md5_crypt(key,hash)
Xconst char *key;
Xconst char *hash;
X{
X static char rvbuf[256];
X char saltbuf[22];
X unsigned char md5res[16];
X char databuf[22];
X const char *data;
X int datalen;
X int keylen;
X STATE s;
X int v;
X int r;
X int i;
X char *hp;
X char *salt;
X
X salt = 0;
X if (hash[0] == '=')
X  { v = strtol(hash+1,&hp,10);
X    if ((v == VERSION_MD5) && (*hp == '.'))
X     { r = strtol(hp+1,&hp,10);
X       if ((r >= MIN_ROUNDS) && (r <= MAX_ROUNDS) && (*hp == '.'))
X	{ if ((strlen(hp) == 45) && __crypt_tst64stringp(hp+1))
X	   { salt = hp + 1;
X	   }
X	}
X     }
X  }
X if (! salt)
X  { md5_init(&s);
X    md5_process_bytes(&s,hash,strlen(hash));
X    md5_result(&s,&md5res[0]);
X    to64(&md5res[0],&saltbuf[0],16);
X    salt = &saltbuf[0];
X    v = DEF_VERSION;
X    r = DEF_ROUNDS;
X  }
X keylen = strlen(key);
X data = key;
X datalen = keylen;
X for (i=0;i<r;i++)
X  { md5_init(&s);
X    md5_process_bytes(&s,data,datalen);
X    md5_process_bytes(&s,salt,22);
X    md5_process_bytes(&s,data,datalen);
X    md5_process_bytes(&s,key,keylen);
X    md5_process_bytes(&s,data,datalen);
X    md5_result(&s,&md5res[0]);
X    to64(&md5res[0],&databuf[0],16);
X    data = &databuf[0];
X    datalen = 22;
X  }
X sprintf(&rvbuf[0],"=%d.%d.%.22s%.22s",v,r,salt,data);
X return(&rvbuf[0]);
X}
EOF
if test 9743 -ne "`wc -c md5crypt.c`"
then
echo shar: error transmitting md5crypt.c \(should have been 9743 characters\)
fi
exit 0
# end of shell archive

Next, the patches to usr.bin/passwd:
diff -P -r -u 1.0-usr-src/usr.bin/passwd/local_passwd.c working-usr-src/usr.bin/passwd/local_passwd.c
--- 1.0-usr-src/usr.bin/passwd/local_passwd.c	Wed Jan  5 06:20:34 1994
+++ working-usr-src/usr.bin/passwd/local_passwd.c	Thu Nov 17 12:02:21 1994
@@ -92,7 +92,9 @@
 {
 	register char *p, *t;
 	int tries;
-	char buf[_PASSWORD_LEN+1], salt[9], *crypt(), *getpass();
+	char buf[_PASSWORD_LEN+1], salt[9], *getpass();
+	extern char *crypt();
+	extern char *crypt_makesalt();
 
 	(void)printf("Changing local password for %s.\n", pw->pw_name);
 
@@ -123,16 +125,7 @@
 			break;
 		(void)printf("Mismatch; try again, EOF to quit.\n");
 	}
-	/* grab a random printable character that isn't a colon */
-	(void)srandom((int)time((time_t *)NULL));
-#ifdef NEWSALT
-	salt[0] = _PASSWORD_EFMT1;
-	to64(&salt[1], (long)(29 * 25), 4);
-	to64(&salt[5], random(), 4);
-#else
-	to64(&salt[0], random(), 2);
-#endif
-	return(crypt(buf, salt));
+	return(crypt(buf,crypt_makesalt()));
 }
 
 static unsigned char itoa64[] =		/* 0 ... 63 => ascii - 64 */
diff -P -r -u 1.0-usr-src/usr.bin/passwd/yp_passwd.c working-usr-src/usr.bin/passwd/yp_passwd.c
--- 1.0-usr-src/usr.bin/passwd/yp_passwd.c	Wed Aug 17 02:06:42 1994
+++ working-usr-src/usr.bin/passwd/yp_passwd.c	Thu Nov 17 12:05:11 1994
@@ -188,6 +188,7 @@
 	register char *p, *t;
 	int tries;
 	char salt[9], *crypt(), *getpass();
+	char *crypt_makesalt();
 	
 	(void)printf("Changing YP password for %s.\n", pw->pw_name);
 
@@ -222,13 +223,11 @@
 			break;
 		(void)printf("Mismatch; try again, EOF to quit.\n");
 	}
+#ifdef NEW_YP_ENTRIES
+	salt = crypt_makesalt();
+#else
 	/* grab a random printable character that isn't a colon */
 	(void)srandom((int)time((time_t *)NULL));
-#ifdef NEWSALT
-	salt[0] = _PASSWORD_EFMT1;
-	to64(&salt[1], (long)(29 * 25), 4);
-	to64(&salt[5], random(), 4);
-#else
 	to64(&salt[0], random(), 2);
 #endif
 	return(strdup(crypt(buf, salt)));