#include #include #include #include #include #include #include #include extern const char *__progname; static int binkey = 0; static const char *keyfdarg = 0; static char *keyarg = 0; static unsigned int discard = 32768; static int debugging = 0; static const char *key; static int keylen; #if 0 static void *deconst(const void *cp) { void *p; bcopy(&cp,&p,sizeof(void *)); return(p); } static void copy_sensitive(char *from, const char **to_p) { int l; char *s; l = strlen(from); s = malloc(l+1); if (! s) { fprintf(stderr,"%s: out of memory\n",__progname); exit(1); } strcpy(s,from); bzero(from,l); if (*to_p) free(deconst(to_p)); *to_p = s; } #endif static void handleargs(int ac, char **av) { int skip; int errs; skip = 0; errs = 0; for (ac--,av++;ac;ac--,av++) { if (skip > 0) { skip --; continue; } if (**av != '-') { if (! keyarg) { keyarg = *av; continue; } fprintf(stderr,"%s: unrecognized argument `%s'\n",__progname,*av); errs ++; continue; } if (0) { needarg:; fprintf(stderr,"%s: %s needs a following argument\n",__progname,*av); errs ++; continue; } #define WANTARG() do { if (++skip >= ac) goto needarg; } while (0) if (!strcmp(*av,"-binkey")) { binkey = 1; continue; } if (!strcmp(*av,"-textkey")) { binkey = 0; continue; } if (!strcmp(*av,"-key")) { WANTARG(); keyarg = av[skip]; continue; } if (!strcmp(*av,"-readkey")) { WANTARG(); keyfdarg = av[skip]; continue; } if (!strcmp(*av,"-discard")) { unsigned long int v; WANTARG(); v = strtoul(av[skip],0,0); discard = v; if (discard != v) { fprintf(stderr,"%s: unreasonable -discard value\n",__progname); exit(1); } continue; } if (!strcmp(*av,"-debug")) { debugging = 1; continue; } #undef WANTARG fprintf(stderr,"%s: unrecognized option `%s'\n",__progname,*av); errs ++; } if (errs) exit(1); } static void no_cores_please(void) { struct rlimit rl; rl.rlim_cur = 0; rl.rlim_max = 0; setrlimit(RLIMIT_CORE,&rl); } #if 0 static void get_iv(void) { struct timeval tv; char buf[256]; int i; long int l; void *m; if (ivarg) { const char *why; why = process_hexarg(ivarg,&iv[0],8); if (! why) return; fprintf(stderr,"%s: invalid IV argument `%s' (%s)\n",__progname,ivarg,why); exit(1); } m = md5_init(); md5_process_bytes(m,&m,sizeof(m)); i = getpid(); md5_process_bytes(m,&i,sizeof(i)); l = gethostid(); md5_process_bytes(m,&l,sizeof(l)); i = gethostname(&buf[0],sizeof(buf)); md5_process_bytes(m,&i,sizeof(i)); md5_process_bytes(m,&buf[0],sizeof(buf)); gettimeofday(&tv,0); md5_process_bytes(m,&tv,sizeof(tv)); md5_result(m,&buf[0]); bcopy(&buf[0],&iv[0],8); } static void keydata_read_stdin(char *cp) { int rv; rv = read(0,cp,1); switch (rv) { case -1: fprintf(stderr,"%s: error reading key data: %s\n",__progname,strerror(errno)); exit(1); break; case 0: fprintf(stderr,"%s: unexpected EOF reading key data\n",__progname); exit(1); break; case 1: break; default: fprintf(stderr,"%s: unexpected return %d from key data read (please report this; it \"can't happen\")\n",__progname,rv); exit(1); break; } } static void read_fd_hex(void *buf, int nb) { int fd; unsigned char *bp; int nib_h; int nib_l; char ch; int ich; FILE *f; fd = get_key_fd(); bp = buf; nib_h = -1; if (fd != 0) f = fdopen(fd,"r"); while (1) { if (fd == 0) { keydata_read_stdin(&ch); if (nb < 1) { if (ch == '\n') return; if (nb == 0) fprintf(stderr,"%s: junk ignored after key data on stdin\n",__progname); nb = -1; continue; } if (ch == '\n') { fprintf(stderr,"%s: invalid key data on stdin (too short)\n",__progname); exit(1); } } else { if (nb < 1) { fclose(f); return; } ich = getc(f); if (ich == EOF) { if (ferror(f)) { fprintf(stderr,"%s: error reading key data: %s\n",__progname,strerror(errno)); } else { fprintf(stderr,"%s: unexpected EOF reading key data\n",__progname); } exit(1); } ch = ich; } if (nib_h < 0) { nib_h = hdigval(ch); if (nib_h < 0) { fprintf(stderr,"%s: invalid key data (invalid hex character)\n",__progname); exit(1); } continue; } else { nib_l = hdigval(ch); if (nib_l < 0) { fprintf(stderr,"%s: invalid key data (invalid hex character)\n",__progname); exit(1); } *bp++ = (nib_h << 4) + nib_l; nb --; } } } static void hash_fd_phrase(void) { void *m; void *m2; int fd; fd = get_key_fd(); m = md5_init(); if (fd == 0) { while (1) { char ch; keydata_read_stdin(&ch); if (ch == '\n') break; md5_process_bytes(m,&ch,1); md5_process_bytes(m2,&ch,1); } } else { char buf[8192]; int rv; while (1) { rv = read(fd,&buf[0],sizeof(buf)); if (fd < 0) { fprintf(stderr,"%s: error reading key data: %s\n",__progname,strerror(errno)); exit(1); } if (rv == 0) break; md5_process_bytes(m,&buf[0],rv); md5_process_bytes(m2,&buf[0],rv); } } md5_result(m,&k[0]); md5_process_bytes(m2,&k[0],16); md5_result(m,&k[8]); } static void get_keyhash(void) { switch (keystyle) { case KS_HASH: if (keyfdarg) { read_fd_hex(&k[0],KEYBYTES); } else { const char *why; why = process_hexarg(keyphrase,&k[0],KEYBYTES); if (! why) return; fprintf(stderr,"%s: invalid key argument `%s' (%s)\n",__progname,keyphrase,why); exit(1); } break; case KS_PHRASE: if (keyfdarg) { hash_fd_phrase(); } else { void *m; m = md5_init(); md5_process_bytes(m,keyphrase,strlen(keyphrase)); md5_result(m,&k[0]); m = md5_init(); md5_process_bytes(m,keyphrase,strlen(keyphrase)); md5_process_bytes(m,&k[0],16); md5_result(m,&k[8]); } break; default: fprintf(stderr,"%s: impossible key mode %d\n",__progname,keystyle); abort(); break; } } static void print_hex(const void *buf, int nb) { const unsigned char *bp; bp = buf; for (;nb>0;nb--) printf("%02x",*bp++); printf("\n"); } static void do_eblock(void) { const char *why; unsigned char blk[8]; why = process_hexarg(blockdata,&blk[0],8); (*desfn)(&blk[0],&blk[0],&k[0],ENCRYPT); print_hex(&blk[0],8); } static void do_dblock(void) { const char *why; unsigned char blk[8]; why = process_hexarg(blockdata,&blk[0],8); (*desfn)(&blk[0],&blk[0],&k[0],DECRYPT); print_hex(&blk[0],8); } static void write_out(unsigned char *buf, int nb) { unsigned char *bp; int nw; bp = buf; while (nb > 0) { nw = write(1,bp,nb); if (nw < 0) { fprintf(stderr,"%s: output write error: %s\n",__progname,strerror(errno)); exit(1); } if (nw > nb) { fprintf(stderr,"%s: output wrote more than requested (please report this; it \"can't happen\")\n",__progname); fprintf(stderr,"%s: wanted %d did %d\n",__progname,nb,nw); abort(); } if (nw != nb) { fprintf(stderr,"%s: short output write: wanted %d did %d\n",__progname,nb,nw); fprintf(stderr,"%s: retrying remainder\n",__progname); } nb -= nw; bp += nw; } } static void do_ecb(int decrypt) { unsigned char buf[8192]; unsigned char *bp; int bf; int nr; bf = 0; while (1) { nr = read(0,&buf[bf],sizeof(buf)-bf); if (nr < 0) { fprintf(stderr,"%s: input read error: %s\n",__progname,strerror(errno)); exit(1); } if (nr == 0) { if (bf > 0) { fprintf(stderr,"%s: partial block at EOF ignored in ECB mode\n",__progname); } break; } nr += bf; bp = &buf[0]; for (;nr>=8;nr-=8,bp+=8) (*desfn)(bp,bp,&k[0],decrypt?DECRYPT:ENCRYPT); write_out(&buf[0],bp-&buf[0]); if (nr > 0) bcopy(bp,&buf[0],nr); bf = nr; } } static int Read(int fd, void *buf, int nb) { char *bp; int left; int did; bp = buf; left = nb; while (left > 0) { did = read(fd,bp,left); if (did < 0) { fprintf(stderr,"%s: input read error: %s\n",__progname,strerror(errno)); exit(1); } if (did == 0) return(nb-left); bp += did; left -= did; } return(nb); } static void read_data_block(unsigned char *buf) { int nr; nr = Read(0,buf,8); if (nr != 8) { fprintf(stderr,"%s: premature EOF reading ciphertext\n",__progname); exit(1); } } static void do_ofb(void) { unsigned char buf[8192]; unsigned char *bp; int nr; int io; io = 0; while (1) { nr = read(0,&buf[0],sizeof(buf)); if (nr < 0) { fprintf(stderr,"%s: input read error: %s\n",__progname,strerror(errno)); exit(1); } if (nr == 0) break; bp = &buf[0]; for (;nr>0;nr--) { if (io == 0) (*desfn)(&iv[0],&iv[0],&k[0],ENCRYPT); *bp++ ^= iv[io]; io = (io + 1) & 7; } write_out(&buf[0],bp-&buf[0]); } } static void do_cfb(int decrypt) { unsigned char buf[8192]; unsigned char *bp; int nr; int io; io = 0; while (1) { nr = read(0,&buf[0],sizeof(buf)); if (nr < 0) { fprintf(stderr,"%s: input read error: %s\n",__progname,strerror(errno)); exit(1); } if (nr == 0) break; bp = &buf[0]; for (;nr>0;nr--) { if (io == 0) (*desfn)(&iv[0],&iv[0],&k[0],ENCRYPT); if (decrypt) { register unsigned char ch; ch = *bp ^ iv[io]; iv[io] = *bp; *bp++ = ch; } else { register unsigned char ch; ch = *bp ^ iv[io]; *bp++ = ch; iv[io] = ch; } io = (io + 1) & 7; } write_out(&buf[0],bp-&buf[0]); } } static void do_cbc_block(int decrypt) { unsigned char buf[8192]; unsigned char *bp; unsigned char tmp[8]; int bf; int nr; int i; bf = 0; while (1) { nr = read(0,&buf[bf],sizeof(buf)-bf); if (nr < 0) { fprintf(stderr,"%s: input read error: %s\n",__progname,strerror(errno)); exit(1); } if (nr == 0) { if (decrypt) { if (bf == 0) { write_out(&stealbuf[0],8); } else { /* buf<0..bf-1> holds C[n]; iv holds C[n-1] */ /* stealbuf holds what we thought at the time was P[n-1], ie, D(C[n-1]) ^ C[n-2]. */ (*desfn)(&tmp[0],&iv[0],&k[0],DECRYPT); for (i=0;i<8;i++) stealbuf[i] ^= tmp[i]; for (i=0;i=8;nr-=8,bp+=8) { if (decrypt) { (*desfn)(&tmp[0],bp,&k[0],DECRYPT); for (i=0;i<8;i++) { register unsigned char ch; ch = bp[i]; bp[i] = stealbuf[i]; stealbuf[i] = iv[i] ^ tmp[i]; iv[i] = ch; } } else { for (i=0;i<8;i++) { tmp[i] = iv[i] ^ bp[i]; bp[i] = iv[i]; } (*desfn)(&iv[0],&tmp[0],&k[0],ENCRYPT); } } write_out(&buf[0],bp-&buf[0]); if (nr > 0) bcopy(bp,&buf[0],nr); bf = nr; } } static int cbc_steal_start(void) { int n; unsigned char tmp1[8]; unsigned char tmp2[8]; int i; n = Read(0,&tmp1[0],8); switch (n) { case 0: return(0); break; case 8: (*desfn)(&tmp2[0],&tmp1[0],&k[0],DECRYPT); for (i=0;i<8;i++) { stealbuf[i] = iv[i] ^ tmp2[i]; iv[i] = tmp1[i]; } return(1); break; } (*desfn)(&tmp2[0],&iv[0],&k[0],DECRYPT); for (i=0;i>7;i>0;i>>=7) *--bp = (i & 0x7f) | 0x80; nr = (nr + (&buf[SLOP] - bp) + 7) & ~7; i = 0; for (;nr>0;nr-=8,bp+=8,i+=8) (*desfn)(&buf[i],bp,&k[0],ENCRYPT); if (debugging) fprintf(stderr,"%s: CBC stream encoder: writing %d\n",__progname,i); write_out(&buf[0],i); } } static void do_cbc_stream_d(void) { unsigned char ibuf[8192]; unsigned char *ip; int ibf; unsigned char obuf[8192]; int oo; int inlen; int oblen; int nr; int i; ibf = 0; inlen = 1; oblen = 0; while (1) { if (debugging) fprintf(stderr,"%s: CBC stream decoder: ibf %d\n",__progname,ibf); nr = read(0,&ibuf[ibf],sizeof(ibuf)-ibf); if (nr < 0) { fprintf(stderr,"%s: input read error: %s\n",__progname,strerror(errno)); exit(1); } if (nr == 0) { if (ibf > 0) fprintf(stderr,"%s: warning: decryption error: EOF partway through a basic block\n",__progname); if (! inlen) fprintf(stderr,"%s: warning: decryption error: EOF partway through a data chunk\n",__progname); if (inlen && (oblen != 0)) fprintf(stderr,"%s: warning: decryption error: EOF partway through a chunk length\n",__progname); break; } if (debugging) fprintf(stderr,"%s: CBC stream decoder: read %d\n",__progname,nr); nr += ibf; ip = &ibuf[0]; while (nr >= 8) { if (inlen) { (*desfn)(&obuf[0],ip,&k[0],DECRYPT); for (i=0;inlen&&(i<8);i++) { oblen = (oblen << 7) | (obuf[i] & 0x7f); if (! (obuf[i] & 0x80)) inlen = 0; } if (debugging) fprintf(stderr,"%s: CBC stream decoder: inlen %d, oblen %d\n",__progname,inlen,oblen); if (! inlen) { if (i < 8) bcopy(&obuf[i],&obuf[0],8-i); oo = 8 - i; if (debugging) fprintf(stderr,"%s: CBC stream decoder: oo %d\n",__progname,oo); } } else { (*desfn)(&obuf[oo],ip,&k[0],DECRYPT); oo += 8; } if (!inlen && (oo >= oblen)) { write_out(&obuf[0],oblen); if (debugging) fprintf(stderr,"%s: CBC stream decoder: finished block\n",__progname); inlen = 1; oblen = 0; } nr -= 8; ip += 8; } if (!inlen && (oo > 0)) { write_out(&obuf[0],oo); oblen -= oo; if (debugging) fprintf(stderr,"%s: CBC stream decoder: writing partial block %d (oblen %d)\n",__progname,oo,oblen); oo = 0; } if (nr > 0) bcopy(ip,&ibuf[0],nr); ibf = nr; } } static void do_encrypt(void) { switch (ciphermode) { case MODE_ECB: get_keyhash(); do_ecb(1); break; case MODE_CBC: get_keyhash(); if (streaming) { write_out(&iv[0],8); do_cbc_stream_e(); } else { do_cbc_block(0); } break; case MODE_CFB: get_keyhash(); write_out(&iv[0],8); do_cfb(0); break; case MODE_OFB: get_keyhash(); write_out(&iv[0],8); do_ofb(); break; default: fprintf(stderr,"%s: impossible cipher mode %d\n",__progname,ciphermode); abort(); break; } } static void do_decrypt(void) { switch (ciphermode) { case MODE_ECB: get_keyhash(); do_ecb(0); break; case MODE_CBC: get_keyhash(); if (streaming) { read_data_block(&iv[0]); do_cbc_stream_d(); } else { read_data_block(&iv[0]); if (cbc_steal_start()) do_cbc_block(1); } break; case MODE_CFB: get_keyhash(); read_data_block(&iv[0]); do_cfb(1); break; case MODE_OFB: get_keyhash(); read_data_block(&iv[0]); do_ofb(); break; default: fprintf(stderr,"%s: impossible cipher mode %d\n",__progname,ciphermode); abort(); break; } } #endif static void set_key(const void *k, int l) { char *t; if (l > 256) l = 256; t = malloc(l); bcopy(k,t,l); keylen = l; key = t; } static void setupkey_fd(void) { int fd; char *t; char rbuf[256]; int l; int r; fd = strtol(keyfdarg,&t,0); if (*t || (t == keyfdarg) || (fd < 0)) { fprintf(stderr,"%s: invalid key file descriptor number `%s'\n",__progname,keyfdarg); exit(1); } if (fcntl(fd,F_GETFD,0) < 0) { if (errno == EBADF) { fprintf(stderr,"%s: key file descriptor %d isn't open\n",__progname,fd); exit(1); } else { fprintf(stderr,"%s: fcntl key file descriptor %d: %s (please report this; it \"can't happen\"!)\n",__progname,fd,strerror(errno)); exit(1); } } l = 0; while (l < 256) { if (fd) { r = read(fd,&rbuf[l],256-l); } else { r = read(fd,&rbuf[l],1); if ((r == 1) && (rbuf[l] == '\n')) r = 0; } if (r < 0) { fprintf(stderr,"%s: key data read error on %d: %s\n",__progname,fd,strerror(errno)); exit(1); } if (r == 0) break; l += r; } set_key(&rbuf[0],l); if (l >= 256) { if (fd) { while (read(fd,&rbuf[0],256) > 0) ; } else { while ((read(fd,&rbuf[0],1) == 1) && (rbuf[0] != '\n')) ; } } } static int hdigval(char c) { switch (c) { case '0': return(0); break; case '1': return(1); break; case '2': return(2); break; case '3': return(3); break; case '4': return(4); break; case '5': return(5); break; case '6': return(6); break; case '7': return(7); break; case '8': return(8); break; case '9': return(9); break; case 'a': case 'A': return(10); break; case 'b': case 'B': return(11); break; case 'c': case 'C': return(12); break; case 'd': case 'D': return(13); break; case 'e': case 'E': return(14); break; case 'f': case 'F': return(15); break; } return(-1); } static void needkey(void) { fprintf(stderr,"%s: must specify a key, or use -readkey\n",__progname); exit(1); } static void setupkey_bin(void) { unsigned char kbuf[256]; int kx; int nib_h; int nib_l; int v; char *kp; if (keyarg == 0) needkey(); kx = 0; for (kp=keyarg;*kp;*kp++=0) { nib_h = hdigval(*kp); if (nib_h < 0) continue; nib_l = hdigval(*kp); v = (nib_l < 0) ? nib_h : ((nib_h << 4) | nib_l); if (kx < 256) kbuf[kx++] = v; } set_key(&kbuf[0],kx); } static void setupkey_text(void) { int l; if (keyarg == 0) needkey(); l = strlen(keyarg); set_key(keyarg,l); bzero(keyarg,l); } static void setupkey(void) { if (keyfdarg) setupkey_fd(); else if (binkey) setupkey_bin(); else setupkey_text(); } static void run(void) { ARC4_STATE s; char buf[8192]; int r; int w; arc4_init(&s); arc4_setkey(&s,key,keylen,discard); while (1) { r = read(0,&buf[0],sizeof(buf)); if (r < 0) { fprintf(stderr,"%s: input read error: %s\n",__progname,strerror(errno)); exit(1); } if (r == 0) break; arc4_crypt(&s,&buf[0],r,&buf[0]); w = write(1,&buf[0],r); if (w < 0) { fprintf(stderr,"%s: output write error: %s\n",__progname,strerror(errno)); exit(1); } if (w != r) { fprintf(stderr,"%s: short output write: wanted %d, wrote %d\n",__progname,r,w); exit(1); } } } int main(int, char **); int main(int ac, char **av) { handleargs(ac,av); if (! debugging) no_cores_please(); setupkey(); run(); exit(0); }