|
From: cwe@it.kth.se (Christian Wettergren) Newsgroups: mail.cypherpunks Subject: Re: Netscape SSL implementation is broken! Message-ID: <199509180525.HAA06519@piraya.electrum.kth.se> Date: 18 Sep 95 05:26:16 GMT 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