istvan@hhb.UUCP (04/14/87)
In order to enable mailing of "mix"-ed messages like --------------------------- cut here --------------------------------- atcmne yscedfcliodpfhl wiaa cih a whadnnt tsIairme.onii fciooaops shi ineaeaeC g nhee scnmc iate u- taeen cfmisrfrwgrrT bs noitihe h ygfiks xdreipnshnt i ia.' n tacli u s ot ls tsh slea anhb osoeaaln nntrncmepudcosoeutqu aoi ld sufotuump ,c cdwtlt ay irsriswsp otd stowtner srut v ctioo r t soh yaoahtoohfurerthaoetg iu --------------------------- cut here --------------------------------- Rev A of mix.c (re-posted to net.sources on Apr 14, 1987) is modified to leave the last byte (newline) of a file undisturbed. Before changing over to the new version, files encoded with the old version should be decoded with the old version; or alternately a newline should be appended to the old cyphertext. BRIEF: In-place (non-filter) encoding/decoding of ASCII files, using an interactively acquired key, or the value of the environment variable MIXKEY, or one constructed from user's effective user id. Shuffles the bytes of the file; the retained character set permits ASCII file transfers. Provides the key holder immediate access (vi, grep, make, etc.) to scrambled files such that the scrambled nature of the files is transparent. The objects edmix, keymix, unmix, catmix are links of mix, argv[0] is hashed to decode context. # This is a shell archive. Remove anything before this line, then # unpack it by saving it in a file and typing "sh file". (Files # unpacked will be owned by you and have default permissions.) # # This archive contains: # mix.man mix.c echo x - mix.man cat > "mix.man" << '//E*O*F mix.man//' .TH MIX hhb "" CMC .UC 4 .SH NAME .nh .nf \fBmix\ unmix\ catmix\ keymix\ edmix\fR \ \ - source code protection routines \fBmix\fR - scramble a file \fBunmix\fR - descramble a file \fBcatmix\fR - print a scrambled file \fBkeymix\fR - print the minimum security key \fBedmix\fR - edit a scrambled file .SH SYNOPSYS .nf .B mix\ \ \t\ [\ -k\ key\ ]\ \ files .B unmix\t\ [\ -k\ key\ ]\ \ files .B catmix\t\ [\ -k\ key\ ]\ \ files .B keymix .B edmix\t\ [\ -k\ key\ ]\ [\ -e\ exec_cmd\ ]\ \ files .fi .SH DESCRIPTION The \fBmix\fR routines scramble/descramble ASCII files based on a supplied keyword. Users in possession of the key have immediate access to scrambled files such that the scrambled nature of the files is transparent. The routines link to a single object. .PP \fBMix\fR scrambles, \fBunmix\fR descrambles the files in its argument list. For each file in its argument list, \fBedmix\fR descrambles the file, effects a system call of the form \fBsystem(exec_cmd file)\fR, followed by rescrambling the file. \fBCatmix\fR writes a descrambled version of its arguments to stdout. \fBKeymix\fR prints the "minimum security" key to stderr. A given key affects the entire argument list. .PP The user normally supplies the key for the transformation mechanism. Allowed key lengths are 1 through 128 bytes. Longer keys are silently truncated to 128 bytes; null keys trigger the "minimum security" option instead. .PP Performance characteristics are comparable to those of the standard \fBcrypt\fR command, while not incurring the restrictions that limit the use of \fBcrypt\fR to the United States. Unlike \fBcrypt\fR, the executable is not a filter, precluding the necessity for redirecting output to temporary files followed by a move of the temporary files to the original files. Also unlike \fBcrypt\fR, the scrambling effect is not the same as the descrambling effect: two successive scramblings do not return a file to its original form. The command "mix myfile myfile" for example, twice scrambles myfile; the appropriate reversal is "unmix myfile myfile". Still another difference with respect to \fBcrypt\fR is that the character set of a file scrambled by \fBmix\fR is identical to that of the original file, permitting remote file transfer protocol without "control characters". .PP The \fBmix\fR routines do not alter files that contain less than 256 bytes, or files containing null (ASCII zero) bytes. When encountering such, a warning message is generated, without any other side effects. A \fBmix\fR process locks the file on which it operates; concurrent \fBmix\fR processes competing for the same file wait until the locking process completes the transformation and releases the lock. .sp .PP .SH KEY ACQUISITION The \fB-k key\fR command line option directly specifies the key for the transformations, overriding the two other methods described below. Due to the visible records of the command line, this option is not the most secure alternative. .PP The environment variable \fBMIXKEY\fR value if non-null, is automatically used as the key throughout the life of the user's shell. The command "setenv MIXKEY key" assigns a value to \fBMIXKEY\fR under the Berkeley system. The System V equivalent is "MIXKEY=key ; export MIXKEY". .PP Key acquisition is interactive in lieu of the above methods. Echoing is turned off and the user responds to the "Key: " prompt appearing on stderr. The commands reprompt with "Again: " under "Key: " and verify the match to avoid typos. On a mismatch, a control-G (bel) is printed, and the program exits without any file access. .PP For minimum security applications, the \fBnull key\fR given by the user (interactively or with the command line option \fB-k ""\fR) is supplanted with one constructed from the effective user id of the user. This permits a number of individuals logging in to an environment, easy access to information scrambled in that environment, while excluding others. For example, if myfile is scrambled by \fBproject\fR using "mix myfile < nullfile" where a redirected, empty "nullfile" supplies a zero length key to the interactive acquisition routine, all users logging in as \fBproject\fR have continued access to the original "myfile" using a null key. Note that scrambling/descrambling with minimum security keys does not port to other environments, unless the user provides the key previously acquired in the \fBoriginal\fR environment through the \fBkeymix\fR command. .sp .PP .SH EDMIX COMMANDS The default \fBsystem\fR command executed by \fBedmix\fR is the \fBvi\fR editor. Thus, executing "edmix myfile.c myfile.h" commences with key acquisition, then myfile.c is descrambled, a system("vi myfile.c") call follows, then - the possibly modified - myfile.c is rescrambled. Myfile.h is processed next, using the same key. .PP The \fB-e exec_cmd\fR supplants the default \fBvi\fR system call with exec_cmd. Thus, "edmix -e emacs myfile" allows direct editing of the unscrambled version of a scrambled "myfile", using the \fBemacs\fR editor. Exec_cmd normally is a command that takes a file name as a last argument. Alternately, dollar ($) signs contained by exec_cmd are expanded to the name of the currently descrambled file. Commands longer than a word should be quoted. Examples: .nf edmix -e "grep xyz" file.c file.h edmix -e '( echo $; cat -n $ | grep xyz ) >> lines_of_xyz' file.c file.h .fi .sp .SH DATA INTEGRITY All file reads and writes performed by the \fBmix\fR routines are single (atomic) operations pertaining to the length of the file, to minimize the danger of mangled data. User interrupts are guaranteed to leave the entirety of a file either in its scrambled or in its descrambled state. However the operating system using delayed writes to disk, may perform an incomplete flush during a system crash and make a file unrecoverable. For this reason keeping some form of backup source is recommended. .PP Shell wild cards occasionally have the effect of cueing multiple instances of the same file in the argument list. The command "mix myfile* *.c" for example, scrambles myfile.c twice in succession; a later "edmix myfile.c" fills the \fBvi\fR buffer with a first generation scrambling of myfile.c. .PP Loss of a key, not surprisingly, will present the user with a substantial exercise in cryptography. //E*O*F mix.man// echo x - mix.c cat > "mix.c" << '//E*O*F mix.c//' /* mix.c */ /********************************************************************** * File Name : mix.c * Object : mix - also link to unmix, edmix, catmix, keymix * Berkeley cc : cc -s -O % -o $BIN/mix * System V cc : cc -s -O -DREALUNIX % -o $BIN/mix * Author : Istvan Mohos, March 1987 * Rev A Apr 14 : Do not mix last byte of file ***********************************************************************/ #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <errno.h> #include <signal.h> #ifdef REALUNIX #include <termio.h> struct termio tbuf, tbufsave; echo_off() { ioctl (0, TCGETA, &tbuf); tbufsave = tbuf; tbuf.c_lflag &= ~ECHO; ioctl (0, TCSETAF, &tbuf); } echo_ret() { ioctl (0, TCSETAF, &tbufsave); } #else #include <sys/ioctl.h> struct sgttyb tbuf, tbufsave; echo_off() { ioctl (0, TIOCGETP, &tbuf); tbufsave = tbuf; tbuf.sg_flags &= ~ECHO; ioctl (0, TIOCSETP, &tbuf); } echo_ret() { ioctl (0, TIOCSETP, &tbufsave); } #endif #define SMALLIM 256 /* minimum file size */ #define KMAX 128 /* maximum key size */ #define NONE -1 /* data buffer references */ #define PLAIN 0 #define CIPHER 1 #define SMALLBUF 2 #define SCREEN 1 #define MIX 334 /* argv[0] hash values */ #define EDMIX 535 #define UNMIX 561 #define CATMIX 646 #define KEYMIX 663 struct keychar { /* links to form chain of key bytes */ int val; struct keychar *next; }; int fstat(); struct stat sbuf; int fd = -1, lockfd; /* subject file, lock file */ char *plain, *cipher; /* malloc pointers */ char *head, *tail; /* main buffer limits */ char *gets(), *getenv(); struct keychar key[KMAX]; /* links */ int klen, flen; /* key length, file length */ int loop; /* negative offset: size of main buffer */ int eflag = 0; /* edmix command flag */ int unphase; /* unmix phase of edmix command */ char *estring = "vi"; /* edmix default command */ static char cmd[SMALLIM]; /* edmix system command buffer */ int argindx = 1; /* pointer to current file */ char *fstring; /* current file name */ static char mstring[KMAX]; /* minimum security key buffer */ char *kstring; /* pointer to user key string */ char smallbuf[SMALLIM]; /* file buffer for files less than SMALLIM */ char lockstr[SMALLIM]; /* lock file: /tmp/mixlock$inode */ int locked = 0; /* lock flag */ int context = 0; /* argv[0] hash value */ int cleanup(); /* remove lock file, exit */ extern char **environ; extern int errno; main(argc,argv) int argc; char *argv[]; { register char *here; int regi; int donttry; /* if unphase of edmix fails */ int argneed = 2; char kbuf[SMALLIM]; /* interactive key acquisition buffers */ char verify[SMALLIM]; if (signal(SIGHUP, SIG_IGN) == SIG_DFL) signal(SIGHUP, cleanup); if (signal(SIGINT, SIG_IGN) == SIG_DFL) signal(SIGINT, cleanup); if (signal(SIGQUIT, SIG_IGN) == SIG_DFL) signal(SIGQUIT, cleanup); if (signal(SIGTERM, SIG_IGN) == SIG_DFL) signal(SIGTERM, cleanup); here = argv[0] + strlen(argv[0]); while(--here > argv[0]) { if (*here == '/') { ++here; break; } } if (*here == '/') /* first byte of argv[0] */ ++here; while(*here) context += *here++; switch (context) { case KEYMIX: fillm(); fprintf(stderr, "%s\n", mstring), cleanup(0); case EDMIX: if (argc > argindx) { for (regi = 1; regi < 5;) { if (strcmp(argv[regi], "-k") == 0) { argneed += 2; argindx += 2; if (++regi < argc) kstring = argv[regi]; } else if (strcmp(argv[regi], "-e") == 0) { eflag = 1; argneed += 2; argindx += 2; if (++regi < argc) estring = argv[regi]; } if (++regi >= argc) break; } } break; default: if (argc > argindx) { for (regi = 1; regi < 3;) { if (strcmp(argv[regi], "-k") == 0) { argneed += 2; argindx += 2; if (++regi < argc) kstring = argv[regi]; } if (++regi >= argc) break; } } break; } if (argneed > argc) { switch (context) { default: fprintf(stderr, "Usage: %s [ -k key ] file ...\n", argv[0]), cleanup(0); case EDMIX: fprintf(stderr, "Usage: %s [ -k key ] [ -e editor ] file ...\n", argv[0]), cleanup(0); } } if (kstring == NULL) { kstring = getenv("MIXKEY"); if (kstring != NULL) if (strlen(kstring) == 0) kstring = NULL; /* to prevent a NULL environment variable automatically triggering the euid conversion */ } if (kstring == NULL) { kstring = kbuf; fprintf(stderr, "Key: "), fflush(stderr); echo_off(); gets(kstring); fprintf(stderr, "\n"); fprintf(stderr, "Again: "), fflush(stderr); gets(verify); fprintf(stderr, "\n"); echo_ret(); if (strcmp(kstring, verify) != 0) fprintf(stderr, "%c%c",7, 7), cleanup(1); } /* verify key length validity */ if ((klen = strlen(kstring)) == 0) { kstring = mstring; fillm(); klen = strlen(kstring); } else if (klen > KMAX) *(kstring + KMAX) = '\0', klen = KMAX; /* link up selectors into circular chain */ for (regi = klen; --regi; key[regi].next = key + regi -1); key[0].next = key + klen -1; while (argindx < argc) { fstring = argv[argindx++]; donttry = 0; switch (context) { case MIX: switch (readfile(PLAIN)) { case 0: mix(); writefile(CIPHER, 0); break; case 1: break; default: writefile(NONE, 0); break; } break; case UNMIX: switch (readfile(CIPHER)) { case 0: unmix(); writefile(PLAIN, 0); break; case 1: break; default: writefile(NONE, 0); break; } break; case EDMIX: unphase = 1; switch(readfile(CIPHER)) { case 0: unmix(); writefile(PLAIN, 0); break; case 1: donttry = 1; break; default: writefile(NONE, 0); break; } unphase = 0; if (!donttry) { mksys(); system(cmd); switch(readfile(PLAIN)) { case 0: mix(); writefile(CIPHER, 0); break; default: writefile(NONE, 0); break; } } break; case CATMIX: default: switch(readfile(CIPHER)) { case 0: unmix(); writefile(PLAIN, SCREEN); break; case 1: break; default: writefile(SMALLBUF, SCREEN); break; } break; } } cleanup(0); } /* hash buffer to manufacture minimum security key, filled byte-by-byte to silence the "strings" command */ static260(buf) char *buf; { register char *h = buf; *h++ = 106; *h++ = 97; *h++ = 101; *h++ = 111; *h++ = 108; *h++ = 102; *h++ = 107; *h++ = 108; *h++ = 116; *h++ = 106; *h++ = 109; *h++ = 118; *h++ = 114; *h++ = 107; *h++ = 111; *h++ = 111; *h++ = 118; *h++ = 107; *h++ = 109; *h++ = 117; *h++ = 112; *h++ = 104; *h++ = 109; *h++ = 108; *h++ = 114; *h++ = 103; *h++ = 105; *h++ = 112; *h++ = 106; *h++ = 97; *h++ = 99; *h++ = 98; *h++ = 103; *h++ = 117; *h++ = 117; *h++ = 97; *h++ = 116; *h++ = 106; *h++ = 107; *h++ = 104; *h++ = 110; *h++ = 122; *h++ = 121; *h++ = 100; *h++ = 119; *h++ = 111; *h++ = 112; *h++ = 109; *h++ = 112; *h++ = 99; *h++ = 97; *h++ = 101; *h++ = 118; *h++ = 107; *h++ = 106; *h++ = 101; *h++ = 104; *h++ = 114; *h++ = 113; *h++ = 116; *h++ = 107; *h++ = 120; *h++ = 119; *h++ = 113; *h++ = 114; *h++ = 97; *h++ = 119; *h++ = 121; *h++ = 110; *h++ = 122; *h++ = 120; *h++ = 114; *h++ = 115; *h++ = 98; *h++ = 120; *h++ = 97; *h++ = 112; *h++ = 98; *h++ = 121; *h++ = 115; *h++ = 116; *h++ = 99; *h++ = 121; *h++ = 98; *h++ = 113; *h++ = 100; *h++ = 122; *h++ = 116; *h++ = 117; *h++ = 100; *h++ = 122; *h++ = 99; *h++ = 114; *h++ = 101; *h++ = 100; *h++ = 117; *h++ = 118; *h++ = 101; *h++ = 97; *h++ = 100; *h++ = 115; *h++ = 105; *h++ = 101; *h++ = 118; *h++ = 119; *h++ = 102; *h++ = 98; *h++ = 101; *h++ = 116; *h++ = 106; *h++ = 102; *h++ = 119; *h++ = 120; *h++ = 103; *h++ = 99; *h++ = 102; *h++ = 118; *h++ = 107; *h++ = 103; *h++ = 120; *h++ = 121; *h++ = 105; *h++ = 100; *h++ = 103; *h++ = 119; *h++ = 108; *h++ = 106; *h++ = 121; *h++ = 122; *h++ = 106; *h++ = 101; *h++ = 104; *h++ = 120; *h++ = 110; *h++ = 108; *h++ = 122; *h++ = 97; *h++ = 107; *h++ = 102; *h++ = 105; *h++ = 122; *h++ = 113; *h++ = 109; *h++ = 98; *h++ = 98; *h++ = 108; *h++ = 103; *h++ = 107; *h++ = 97; *h++ = 99; *h++ = 112; *h++ = 99; *h++ = 101; *h++ = 109; *h++ = 104; *h++ = 108; *h++ = 98; *h++ = 102; *h++ = 115; *h++ = 100; *h++ = 103; *h++ = 110; *h++ = 105; *h++ = 109; *h++ = 99; *h++ = 104; *h++ = 116; *h++ = 102; *h++ = 105; *h++ = 111; *h++ = 108; *h++ = 110; *h++ = 100; *h++ = 106; *h++ = 117; *h++ = 103; *h++ = 108; *h++ = 112; *h++ = 109; *h++ = 111; *h++ = 102; *h++ = 109; *h++ = 120; *h++ = 104; *h++ = 110; *h++ = 113; *h++ = 110; *h++ = 112; *h++ = 104; *h++ = 111; *h++ = 121; *h++ = 105; *h++ = 112; *h++ = 115; *h++ = 111; *h++ = 113; *h++ = 105; *h++ = 113; *h++ = 122; *h++ = 106; *h++ = 114; *h++ = 116; *h++ = 113; *h++ = 114; *h++ = 107; *h++ = 115; *h++ = 119; *h++ = 114; *h++ = 97; *h++ = 120; *h++ = 115; *h++ = 115; *h++ = 108; *h++ = 118; *h++ = 121; *h++ = 116; *h++ = 98; *h++ = 122; *h++ = 117; *h++ = 117; *h++ = 109; *h++ = 119; *h++ = 98; *h++ = 118; *h++ = 99; *h++ = 99; *h++ = 119; *h++ = 110; *h++ = 110; *h++ = 120; *h++ = 100; *h++ = 121; *h++ = 102; *h++ = 101; *h++ = 121; *h++ = 102; *h++ = 122; *h++ = 113; *h++ = 115; *h++ = 100; *h++ = 111; *h++ = 116; *h++ = 103; *h++ = 103; *h++ = 104; *h++ = 104; *h++ = 105; *h++ = 105; *h++ = 117; *h++ = 110; *h++ = 111; *h++ = 113; *h++ = 112; *h++ = 114; *h++ = 118; *h++ = 115; *h++ = 117; *h++ = 118; *h++ = 119; *h++ = 120; *h = 0; } /* manufacture minimum security key */ fillm() { register char *here; int regi; int ck, mlen; static char fbuf[12], mbuf[36]; char bstring[261]; char *from, *to; ck = geteuid(); ++ck; /* don't want root dividing by 0 */ strcpy(fbuf, "%c"); strcat(fbuf, "%o"); strcat(fbuf, " %"); strcat(fbuf, "x "); strcat(fbuf, "%d"); /* just so "strings" couldn't find it */ sprintf(mbuf, fbuf, (ck & 95) + ' ' , ck, ~ck & 0xfff, (~ck & 0xffff) / ck); static260(bstring); head = bstring; here = head + strlen(bstring); tail = here -1; loop = head - tail -1; from = mbuf; to = mstring; mlen = strlen(mbuf); for (regi = mlen; --regi >=0; ) { if ((here += *from++) > tail) here += loop; *to++ = *here; } } /* manufacture system command of edmix */ mksys() { char *here; char *esp; int fnamlen, did_sub = 0; if (!eflag) { strcpy(cmd, estring); strcat(cmd, " "); strcat(cmd, fstring); } else { here = cmd; esp = estring; fnamlen = strlen(fstring); while (*esp) { if (*esp == '$') { strcpy(here, fstring); here += fnamlen; esp++; did_sub = 1; } else *here++ = *esp++; } if (!did_sub) { *here++ = ' '; strcpy(here, fstring); here += fnamlen; } *here = 0; } } cleanup(exitval) int exitval; { signal(SIGINT, SIG_IGN); signal(SIGHUP, SIG_IGN); signal(SIGQUIT, SIG_IGN); signal(SIGTERM, SIG_IGN); if (locked) unlock(); exit(exitval); } lock() { sprintf(lockstr, "/tmp/mixlock%d", sbuf.st_ino); while ((lockfd = creat(lockstr, 0)) == -1 && errno == EACCES) { fprintf(stderr, "Concurrent process lock %s, retry in 1 sec.\n", lockstr); sleep(1); } locked = 1; close(lockfd); } unlock() { if (unlink(lockstr) == -1) fprintf(stderr, "Cannot unlink process lock %s, exiting\n", lockstr), exit(1); locked = 0; } readfile(source) int source; { int checkval; char *where; register char *here; char *calloc(), *malloc(); if ((fd = open(fstring, 2)) == -1) { fprintf(stderr, "File not changed: %s (no access)\n", fstring); return(1); } if ((checkval = fstat(fd, &sbuf)) == -1) fprintf(stderr, "File system read error %d at %s, exiting\n", checkval, fstring), cleanup(1); if (context != EDMIX || unphase) lock(); if ((flen = (int)sbuf.st_size) < SMALLIM) { fprintf(stderr, "File not changed: %s (less than %d bytes)\n", fstring, SMALLIM); return(2); } if (((plain = calloc((unsigned)flen, 1)) == NULL) || ((cipher = malloc((unsigned)flen)) == NULL)) fprintf(stderr, "Can't allocate %lu bytes at %s, exiting\n", flen << 1, fstring), cleanup(1); source ? (where = cipher) : (where = plain); if (read(fd, where, flen) != flen) fprintf(stderr, "File read error at %s, exiting\n", fstring), cleanup(1); for (here = where + flen; --here >= where;) if (!*here) { fprintf(stderr, "File not changed: %s (contains NULL bytes)\n", fstring); return(3); } return(0); } writefile(source, outflag) int source, outflag; { long lseek(); if (fd > 2) lseek(fd, 0L, 0); switch (source) { case NONE: break; case PLAIN: if (outflag) { if (write(SCREEN, plain, flen) != flen) fprintf(stderr, "Can't write to stdout, exiting\n"), cleanup(1); } else if (write(fd, plain, flen) != flen) fprintf(stderr, "Can't write file %s, exiting\n", fstring), cleanup(1); break; case CIPHER: if (write(fd, cipher, flen) != flen) fprintf(stderr, "Can't write file %s, exiting\n", fstring), cleanup(1); break; case SMALLBUF: if (read(fd, smallbuf, flen) != flen) fprintf(stderr, "File read error at %s, exiting\n", fstring), cleanup(1); *(smallbuf + flen) = '\0'; if (write(SCREEN, smallbuf, flen) != flen) fprintf(stderr, "Can't write to stdout, exiting\n", fstring), cleanup(1); break; } if (fd > 2) { close(fd); fd = -1; } if (context != EDMIX || !unphase) unlock(); if (plain != NULL) free(plain); if (cipher != NULL) free(cipher); } /* refill chain links with original key values */ rekey() { register char *here; int regi; here = kstring + klen; for (regi = klen; --regi;) key[regi].val = *--here; key[0].val = *--here; } /* mix: given key byte ASCII values "a b c ... n" in circularly linked list, and plaintext char pointer ptr initially at plaintext[0], advance ptr "a" bytes; install its value as the first byte of cyphertext; zero out plaintext byte pointed to by ptr; decrement value of "a"; while (not done) { advance to next link of key; advance ptr by the value of the link; install ptr value as the next byte of cyphertext; zero out plaintext byte pointed to by ptr; decrement value of link; } Throughout, plaintext is assumed to be a circular list of bytes: ptr increments beyond the buffer continue from the beginning. If ptr value is null (byte already assigned to cyphertext), ptr is incremented until the next non-null plaintext byte. When key link value reaches zero, the just assigned plaintext byte is swapped into its position. */ mix() { register struct keychar *K; register char *here, *cip; int regi; rekey(); head = plain; here = head + flen -1; tail = here -1; loop = head - tail -1; /* always negative */ cip = cipher; K = key; for (regi = flen -1; --regi >= 0; ) { if ((here = here + K->val) > tail) here += loop; while (!*here) if(++here > tail) here = head; if (!--K->val) K->val = *here; K = K->next; *cip++ = *here; *here = 0; } *cip = *++tail; } unmix() { register struct keychar *K; register char *here, *cip; int regi; rekey(); head = plain; K = key; here = head + K->val; tail = head + flen -2; loop = head - tail -1; /* always negative */ cip = cipher; for (regi = flen -1; --regi >= 0; ) { while (*here) if(++here > tail) here = head; *here = *cip++; if (!--K->val) K->val = *here; K = K->next; if ((here = here + K->val) > tail) here += loop; } *(tail +1) = *cip; } //E*O*F mix.c// echo Possible errors detected by \'wc\' [hopefully none]: temp=/tmp/shar$$ trap "rm -f $temp; exit" 0 1 2 3 15 cat > $temp <<\!!! 156 1004 6337 mix.man 682 2630 19458 mix.c 838 3634 25795 total !!! wc mix.man mix.c | sed 's=[^ ]*/==' | diff -b $temp - exit 0 -- Istvan Mohos {ihnp4,decvax,allegra}!philabs!hhb!istvan HHB Systems 1000 Wyckoff Ave. Mahwah NJ 07430 201-848-8000 ====================================================================