1995-09-18 - Re: Netscape SSL implementation is broken!

Header Data

From: Christian Wettergren <cwe@it.kth.se>
To: cypherpunks@toad.com
Message Hash: de1a59c8e5014333ef857317189e077cb63baa096fef5b732bb011529b386395
Message ID: <199509180525.HAA06519@piraya.electrum.kth.se>
Reply To: N/A
UTC Datetime: 1995-09-18 05:26:37 UTC
Raw Date: Sun, 17 Sep 95 22:26:37 PDT

Raw message

From: Christian Wettergren <cwe@it.kth.se>
Date: Sun, 17 Sep 95 22:26:37 PDT
To: cypherpunks@toad.com
Subject: Re: Netscape SSL implementation is broken!
Message-ID: <199509180525.HAA06519@piraya.electrum.kth.se>
MIME-Version: 1.0
Content-Type: text/plain



Hi!

Neat, I'd say. Has everyone sold their Netscape Comm stock yet? :-)

I guess we should send them the draft-ietf-security-randomness-00.txt
asap.

I was also thinking about how many Credit Card numbers will pass
between now and the moment Netscape has done anything about it.
This piece of information does have quite a value, I'd say.

I have included a short program that tries to generate non-guessable
random numbers. It was written a bit back, and my coding style isn't
all that good. It might be interesting, or not.  (Btw, if you find any
problems with it, I'd appreciate to know about it.)

/Christian


----- ZZ: README -----
asadi -
   "As strong as DES is" (hopefully)

Written by: Christian Wettergren
            cwe@nada.kth.se
            February 1993

Introduction
------------

This utility generates a "random" hexstring, which can be used as input
to xauth, for example. It uses a private secret and some other input
to generate the hexstring. In this way a long-term secret can be used
to generate a short-term secret. Since the short-term secret might be
compromised (different xauth cookies might be tried repeatedly, since 
o warning is emitted from the Xserver) it is not safe to use the
same secret on repeated occasions.

This utility does not even need a private long-term secret, since it
may use the ticket generated within the Kerberos authentication system.
In this way the long-term secret is as guarded as your private password
or the Kerberos master-password.

If you don't use the Kerberos system, you have to regenerate the long-term
secret sometimes (in the same way as you change your password). This
utility tries to help you with that step too. A decent pseudo-random
generator is included, and a routine that helps you generate the secret
might be run.

The short-term secret wont reveal anything about the long-term secret,
since the long-term secret is altered and then encrypted with DES. The
result is the short-term secret. 


Search space
------------

The algorithm used to generate the long-term secret tries to enlarge the
search-space as much as possible. so that an exhaustive attack becomes
difficult.

The factors involved are:

     1/ A good random generator
     2/ time-of-day
     3/ user entered text
     4/ user dependant elapsed time
     5/ pid of process
     6/ hostid of computer
     7/ not using consecutive values from
        random generator (initial throw-away &
        intermediate throw-away.)

The most important of all is of course the random generator. The included 
generator is a minimum standard.







    Probable usage: 
      echo add $DISPLAY MIT-MAGIC-COOKIE-1 `asadi` | xauth 

    DISCLAIMER: No guarantees are made about this
    program, explicit or implicit. It is distributed
    AS IS. etc... :-)

  opensafely() -
  open file, try not to reveal when it was
  written.

  Unfortunately, this is not possible!
  No matter how this is done, at least the
  ctime reveals when it was written.

  If I don't remember incorrectly, there are
  bugs in the filesystem under SunOS, so that
  the ctime-field is updated too often. Maybe 
  this might distort this field sometimes.

  Another approach might be to chmod the file
  whenever it is used. In this way it's ctime
  field is updated and hence overwritten.
  It does not reveal anything extra either, 
  since the approximate time when the cookie is
  generated is probably shown in ~/.Xauthority
  anyway.


/* gensecret() -

   generate a good secret "random" file.

   This routine tries to enlarge the search-space
   for a potential cracker. The involved factors
   are:

     1/ A good random generator (see accompanying file.)
     2/ time-of-day
     3/ user entered text
     4/ user dependant elapsed time
     5/ pid of process
     6/ hostid of computer
     7/ not using consecutive values from
        random generator (initial throw-away &
        intermediate throw-away.)

   This approach hopefully deters an attack, or at least
   makes it considerably harder for the attacker.

   Does anyone see any weakness in the above approach? It
   is not based on any cryptological analysis, so no
   guarantees are made of it's appropriateness.
*/

  fprintf(stderr, "asadi - generate a good random hexstring based on\n");
  fprintf(stderr, "        a private secret as a seed. (This secret can\n");
  fprintf(stderr, "        be the Kerberos ticket-file.) Could be used\n");
  fprintf(stderr, "        for getting a good xauth-cookie, for example.\n\n");
  fprintf(stderr, "Usage: asadi [-r] [-l n] [-v] [filename]\n");
  fprintf(stderr, "       -r   -- generate a secret file (default: ~/.secret\n");
  fprintf(stderr, "       -l n -- how many 8-byte blocks to output (default: 16)\n");
  fprintf(stderr, "       -v   -- verbose, not very interesting.\n");
  fprintf(stderr, "       filename -- name of secret file (default: Kerberos\n");
  fprintf(stderr, "                   ticket file, or ~/.secret)\n");


---- ZZ: asadi.c ----

/*****************************************
    asadi v1.1 -
      "As strong as DES is" (hopefully)

    This utility generates a "random" 
    hexstring, which can be used as input
    to xauth, for example. Either a Kerberos
    ticketfile or a secret file is used as
    seed to the random generator. Other input
    is probably hostid, process id and time,
    depending on the implementation of the 
    DES-library.

    The random generator used is the DES-
    algorithm. This method is guaranteed 
    not to reveal anything about the used 
    seed. To crack the cookie you have to 
    crack DES. (There is also a normal good 
    pseudo-random generator included, to
    facilitate the generation of secrets.)


    Written by: Christian Wettergren
                cwe@nada.kth.se
                February 1993

    Usage:
      asadi [keyfilename] [-v] [-l num] [-r]

    The number of 8-byte blocks to 
    generate can be controlled with the 
    -l-switch. There is also a verbose-
    switch.

    If one does not use Kerberos, a secret file
    can be used as a key instead. The contents 
    of this file will not be revealed by this 
    program, but you should of course NOT USE 
    your password anyway!

    To help generate this secret file is a
    decent (according to it's author, not me) random-
    generator included in this program. Use
    the r-switch for this. It deposits the secret
    in the file ~/.secret (with the appropriate
    chmod). This file is also used if there is
    no Kerberos.

    Probable usage: 
      echo add $DISPLAY MIT-MAGIC-COOKIE-1 `asadi` | xauth 

    DISCLAIMER: No guarantees are made about this
    program, explicit or implicit. It is distributed
    AS IS. etc... :-)
*****************************************/


/* Settings of this program */

#define USEKRB /* switches the use of Kerberos on/off */
#define DEFAULTKEYLEN  8 /* multiples of 16 nibbles */
#define SECRETFILE ".secret"


#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <utime.h>
#include <fcntl.h>
#include <des.h>
#ifdef USEKRB
#include <krb.h>
#endif

/* some unprototyped routines */
extern char *getpass();
extern void goodsrand(unsigned long);
extern unsigned long goodrand(void);
extern char *getenv(char *);
extern void *malloc(int);


/* Globals */
char *keyfile = NULL;
int vflag = 0;
int keylen = DEFAULTKEYLEN;

int major = 1; /* version of program */
int minor = 0;

/* calculate a secret key from the file. */

/* Algorithm: read 8 bytes, add them byte-wise to the
   cblock, continue until eof.

   Hence there is no actual reason to use files
   larger than eight bytes, but the above approach is
   used since the ticket-files are larger. (In this 
   way I don't have to care about file's structure 
   too much, either.)
*/
void calcsecretkey(char *file, des_cblock *key) {

  des_cblock tmp;
  int i;
  int fd;

  if (vflag)
    fprintf(stderr, "file: %s\n", keyfile);

  if ((fd = open(file, O_RDONLY)) == -1) {
    fprintf(stderr, 
	    "Could not open '%s', no cookie generated! (errno=%d)\n", 
	    file, errno);
    exit(1);
  }

  while (read(fd, &tmp, sizeof(des_cblock)) == sizeof(des_cblock)) {
    for(i = 0; i < sizeof(des_cblock); i++)
      *((unsigned char *)key + i)
	= *((unsigned char *)key + i) + *((unsigned char *)tmp + i);
    DES_ZERO_CBLOCK(tmp);  /* fixes eof-condition */
  }

  /* close the file */
  if (close(fd) == -1) {
    fprintf(stderr, "Could not close file! (errno=%d)\n", errno);
    exit(1);
  }
}


void printcblock(FILE *fd, des_cblock *blk) {

  int i;
  for(i=0; i < sizeof(des_cblock); i++) {
    fprintf(fd, "%02x", 
	   (unsigned int)(*((unsigned char *)blk + i) & 255));
  }
}

/* 
  opensafely() -
  open file, try not to reveal when it was
  written.

  Unfortunately, this is not possible!
  No matter how this is done, at least the
  ctime reveals when it was written.

  If I don't remember incorrectly, there are
  bugs in the filesystem under SunOS, so that
  the ctime-field is updated too often. Maybe 
  this might distort this field sometimes.

  Another approach might be to chmod the file
  whenever it is used. In this way it's ctime
  field is updated and hence overwritten.
  It does not reveal anything extra either, 
  since the approximate time when the cookie is
  generated is probably shown in ~/.Xauthority
  anyway.
*/
int opensafely(char *file) {

  int fd;

  /* delete old file, if any */
  if (unlink(file) == -1) {
    if (errno != ENOENT) {
      fprintf(stderr, "Error: could not unlink '%s'. (errno=%d)\n", 
	      file, errno);
      exit(1);
    }
  }

  /* open it again */
  if ((fd = open(file, O_WRONLY|O_CREAT, S_IRUSR)) == -1) {
    fprintf(stderr, "Error: could not create '%s'. (errno=%d)\n", 
	    file, errno);
    exit(1);
  }
  return(fd);
}

/* gensecret() -
   generate a good secret "random" file.

   This routine tries to enlarge the search-space
   for a potential cracker. The involved factors
   are:

     1/ A good random generator (see accompanying file.)
     2/ time-of-day
     3/ user entered text
     4/ user dependant elapsed time
     5/ pid of process
     6/ hostid of computer
     7/ not using consecutive values from
        random generator (initial throw-away &
        intermediate throw-away.)

   This approach hopefully deters an attack, or at least
   makes it considerably harder for the attacker.

   Does anyone see any weakness in the above approach? It
   is not based on any cryptological analysis, so no
   guarantees are made of it's appropriateness.
*/
void gensecret(char *file) {

  int i,j;
  struct timeval t, s;
  unsigned long d, u, v;
  int fd;
  unsigned long x;
  char *c;
  char n[100];
  int ta, b;
  struct utimbuf tm;

  /* Get time before enter */
  gettimeofday(&s, (struct timezone *)0);
  
  /* heading */
  printf("\nGenerating a Secret!\n");

  /* get user's response */
  printf("\nCAUTION! Don't use your password below!\n");
  printf("You don't have to remember this data, so\n");
  printf("just type something in.\n\n");
  c = getpass("Enter something:");

  /* get time after enter */
  gettimeofday(&t, (struct timezone *)0);
  d = t.tv_usec - s.tv_usec;

  /* make something of input */
  for(j=0; j < strlen(c) / sizeof(unsigned long); j++) {

    /* collect sizeof(long) bytes of input */
    for(v=0, i=0; i < sizeof(unsigned long); i++) 
      v = (v << 8) + c[i+j*sizeof(unsigned long)];

    /* xor them together */
    u ^= v;
  }

  /* get throw-away factors */
  printf("\nEnter a throw-away factor: ");
  gets(n);
  ta = atoi(n);
  printf("\nEnter a step factor: ");
  gets(n);
  b = atoi(n);

  /* verbose */
  if (vflag) {
    fprintf(stderr, "text: %s\n", c);
    fprintf(stderr, "garbled text: %ld\n", u);
    fprintf(stderr, "elapsed: %ld\n", d);
    fprintf(stderr, "time: %ld %ld\n", t.tv_sec, t.tv_usec);
    fprintf(stderr, "pid: %d\n", getpid());
    fprintf(stderr, "hostid: %d\n", gethostid());
    fprintf(stderr, "throw-away factor: %d\n", ta);
    fprintf(stderr, "step factor: %d\n", b);
    fprintf(stderr, "generated seed: %ld\n", t.tv_usec ^ t.tv_sec ^ d ^ getpid() ^ gethostid() ^ u);
  }

  /* init random generator */
  goodsrand(t.tv_usec ^ t.tv_sec ^ d ^ getpid() ^ gethostid() ^ u);
  
  /* open the file safely */
  fd = opensafely(keyfile);

  /* throw-away ta numbers */
  for(i=0; i < ta; i++)
    (void)goodrand();

  /* 
     this actually writes sizeof(long) 
     times too much data, but it does not
     matter. 
  */
  for (i=0; i<sizeof(des_cblock); i++) {

    x = goodrand();
    if (write(fd, &x, sizeof(unsigned long)) != sizeof(unsigned long)) {
      fprintf(stderr, 
	      "Error: could not write '%s'. (errno=%d)\n", 
	      keyfile, errno);
      exit(1);
    }

    /* throw-away (step) b numbers */
    for(j=0; j < b; j++)
      (void)goodrand();
  }

  /* close the file */
  if (close(fd) == -1) {
    fprintf(stderr, 
	    "Error: could not close '%s'. (errno=%d)\n", 
	    SECRETFILE, errno);
    exit(1);
  }
  
  /* 
     reset times on file.
     (ctime still shows the creation-time though) 
  */
  tm.actime = 0;
  tm.modtime = 0;
  if (utime(keyfile, &tm) == -1) {
    fprintf(stderr, "warning: could not reset times on file '%s'.\n", keyfile);
    fprintf(stderr, "You should /bin/touch it manually at a later time!\n");
  }

  fprintf(stderr, "Secret written to '%s'.\n", keyfile);
}

void hidedate(char *file) {

  if (chmod(file, 0) == -1) {
    fprintf(stderr, "warning: could not chmod '%s'. (errno=%d)\n");
  }
  if (chmod(file, S_IRUSR) == -1) {
    fprintf(stderr, "warning: could not chmod '%s'. (errno=%d)\n");
  }
}

void usage(void) {

  fprintf(stderr, "asadi v%d.%d\n", major, minor);
  fprintf(stderr, "        generate a good random hexstring based on\n");
  fprintf(stderr, "        a private secret as a seed. (This secret can\n");
  fprintf(stderr, "        be the Kerberos ticket-file.) Could be used\n");
  fprintf(stderr, "        for getting a good xauth-cookie, for example.\n\n");
  fprintf(stderr, "Usage: asadi [-r] [-l n] [-v] [filename]\n");
  fprintf(stderr, "       -r   -- generate a secret file (default: ~/.secret\n");
  fprintf(stderr, "       -l n -- how many 8-byte blocks to output (default: 16)\n");
  fprintf(stderr, "       -v   -- verbose, not very interesting.\n");
  fprintf(stderr, "       filename -- name of secret file (default: Kerberos\n");
  fprintf(stderr, "                   ticket file, or ~/.secret)\n");
}

int main(int argc, char *argv[]) {

  int i;
  int l;
  int rflag = 0;
  int gfn = 0;
  des_cblock key;
  des_cblock out;

  /* Handle options */
  for (i=1; i < argc; i++) {
    
    if (argv[i][0] == '-') {
      switch(argv[i][1]) {

      case 'v':
	vflag++;
	break;

      case 'l':
	i++;
	keylen = atoi(argv[i]);
	break;

      case 'r':
	rflag++;
	break;

      case 'h':
	usage();
	return(1);
	break;

      default:
	fprintf(stderr, "error: Unknown option '%s'.\n", argv[i]);
	usage();
	return(1);
      }
    }
    else {
      keyfile = argv[i];
      gfn++;
    }
  }

#ifdef USEKRB
  if (!keyfile && !rflag) {
    keyfile = tkt_string();
    gfn = 0;
  }
#endif
  if (!keyfile) {
    char *h;
    if ((h = getenv("HOME")) == NULL) 
      h = ".";
    keyfile = malloc(sizeof(char) * strlen(h) + 2 + strlen(SECRETFILE));
    strcpy(keyfile, h);
    strcat(keyfile, "/");
    strcat(keyfile, SECRETFILE);
    gfn++;
  }

  /* Which mode are we in? */
  if (rflag) {
    gensecret(keyfile);
    return(1);
  }

  /* hide creation-date */
  if (gfn)
    hidedate(keyfile);

  /* calculate secret key from file */
  calcsecretkey(keyfile, &key);

  /* show secret key, if verbose mode */
  if (vflag) {
    fprintf(stderr, "secret key: ");
    printcblock(stderr, &key);
    fprintf(stderr, "\n");
  }

  /* init the DES random-generator */
  des_init_random_number_generator(&key);

  /* remove traces of secret key */
  DES_ZERO_CBLOCK(key);

  /* generate output */
  for(l=0; l < keylen; l++) {
    des_random_cblock(&out);
    printcblock(stdout, &out);
    DES_ZERO_CBLOCK(out);
  }
  printf("\n");

  return(0);
}


----- ZZ: rand.c -----
/*
 * This program is public domain and was written by William S. England
 * (Oct 1988).  It is based on an article by:
 *
 * Stephen K. Park and Keith W. Miller. RANDOM NUMBER GENERATORS:
 * GOOD ONES ARE HARD TO FIND. Communications of the ACM,
 * New York, NY.,October 1988 p.1192

   From: wengland@stephsf.com (Bill England)
   Newsgroups: alt.sources,comp.lang.c,rec.games.programmer,comp.os.msdos.programmer
   Subject: Re: random number generator (random.c)
   Date: 5 Jan 92 20:12:08 GMT
   Organization: Stephen Software Systems Inc., Tacoma/Seattle, +1 800 829 1684

Modifications;

   Sun Feb 10 18:20:38 PST 1991
    WSE, modified for replacement of random number object file
    under unix and for use with Perl.

 The following is a portable c program for generating random numbers.
 The modulus and multipilier have been extensively tested and should
 not be changed except by someone who is a professional Lehmer generator
 writer.  THIS GENERATOR REPRESENTS THE MINIMUM STANDARD AGAINST WHICH
 OTHER GENERATORS SHOULD BE JUDGED. ("Quote from the referanced article's
 authors. WSE" )
*/

#include <stdio.h> 

#define	m  (unsigned long)2147483647
#define	q  (unsigned long)127773

#define	a (unsigned int)16807
#define	r (unsigned int)2836

/*
** F(z)	= (az)%m
**	= az-m(az/m)
**
** F(z)  = G(z)+mT(z)
** G(z)  = a(z%q)- r(z/q)
** T(z)  = (z/q) - (az/m)
**
** F(z)  = a(z%q)- rz/q+ m((z/q) - a(z/m))
** 	 = a(z%q)- rz/q+ m(z/q) - az
*/

/*
**
*/
unsigned long seed;

void goodsrand( /* unsigned long*/ initial_seed)
unsigned long initial_seed;
{
    seed = initial_seed; 
}
/*
**
*/
unsigned long goodrand(/*void*/){

register
int 	lo, hi, test;

    hi   = seed/q;
    lo   = seed%q;

    test = a*lo - r*hi;

    if (test > 0)
	seed = test;
    else
	seed = test+ m;

    return seed;
}

#ifdef TEST1
/*  
**   The result of running this program should be
**   1043618065.  If this program does not yeild this
**   value then your compiler has not implemented this
**   program correctly.
*/

main(/*void*/)
{
unsigned 
long	n_rand;

register int 	i;
int	success = 0;

    goodsrand(1);

    for( i = 1; i <= 10001; i++){
        n_rand = goodrand();

        if( i> 9998)  
	    printf("Sequence %5i, Seed= %10i\n", i, seed ); 

	if( i == 10000) 
	    if( seed == 1043618065 ) 
		success = 1;
    }

    if (success){
	printf("The random number generator works correctly.\n\n");
	exit(0);
    }else{
	printf("The random number generator DOES NOT WORK!\n\n");
	exit(1);
    }
}
#endif
/*
-- 
 +-  Bill England,  wengland@stephsf.COM -----------------------------------+
 |   * *      H -> He +24Mev                                                |
 |  * * * ... Oooo, we're having so much fun making itty bitty suns *       |
 |__ * * ___________________________________________________________________| 
*/


---- ZZ: Makefile ----

#
# Makefile for asadi.
#

CC=gcc
CFLAGS=-g
LIBS=-lkrb -ldes

asadi: asadi.c rand.o
	${CC} ${CFLAGS} -o asadi asadi.c rand.o ${LIBS}

rand.o: rand.c
	$(CC) -c rand.c





Thread