#include #include #include #include #include #include #include #include #include #include extern const char *__progname; /* MAXBLOCK can be anything above the encryption block size; making it larger will reduce the best-case expansion due to -stream. */ /* SLOP can be anything that is (a) at least one encryption block and (b) such that 7*SLOP is no smaller than the number of bits needed to represent the value of MAXBLOCK. Normally (a) is more stringent. :-) */ #define MAXBLOCK 8192 #define SLOP 8 #define MODE_NONE 0 #define MODE_E 1 #define MODE_D 2 #define MODE_K 3 #define MODE_GENVEC 4 #define MODE_EBLOCK 5 #define MODE_DBLOCK 6 static int opmode = MODE_NONE; static const char *keyphrase = 0; static const char *blockdata = 0; #define KS_PHRASE 1 #define KS_HASH 2 static int keystyle = KS_PHRASE; static const char *ivarg = 0; #define MODE_ECB 1 #define MODE_CBC 2 #define MODE_CFB 3 #define MODE_OFB 4 static int ciphermode = MODE_CBC; #define TYPE_SINGLE 1 #define TYPE_TRIPLE 3 static int type = TYPE_SINGLE; #define KEYBYTES (type*8) static void (*desfn)(void *, const void *, const void *, int); static int streaming = 0; static const char *keyfdarg = 0; static int debugging = 0; static unsigned char iv[8]; static unsigned char k[24]; static unsigned char stealbuf[8]; 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; } 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 != '-') { 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,"-e")) { WANTARG(); opmode = MODE_E; copy_sensitive(av[skip],&keyphrase); continue; } if (!strcmp(*av,"-d")) { WANTARG(); opmode = MODE_D; copy_sensitive(av[skip],&keyphrase); continue; } if (!strcmp(*av,"-k")) { WANTARG(); opmode = MODE_K; copy_sensitive(av[skip],&keyphrase); continue; } if (!strcmp(*av,"-genvec")) { opmode = MODE_GENVEC; continue; } if (!strcmp(*av,"-eblock")) { WANTARG(); opmode = MODE_EBLOCK; keystyle = KS_HASH; copy_sensitive(av[skip],&keyphrase); WANTARG(); copy_sensitive(av[skip],&blockdata); continue; } if (!strcmp(*av,"-dblock")) { WANTARG(); opmode = MODE_DBLOCK; keystyle = KS_HASH; copy_sensitive(av[skip],&keyphrase); WANTARG(); copy_sensitive(av[skip],&blockdata); continue; } if (!strcmp(*av,"-keyhash")) { keystyle = KS_HASH; continue; } if (!strcmp(*av,"-keyphrase")) { keystyle = KS_PHRASE; continue; } if (!strcmp(*av,"-iv")) { WANTARG(); copy_sensitive(av[skip],&ivarg); continue; } if (!strcmp(*av,"-ecb")) { ciphermode = MODE_ECB; continue; } if (!strcmp(*av,"-cbc")) { ciphermode = MODE_CBC; continue; } if (!strcmp(*av,"-cfb")) { ciphermode = MODE_CFB; continue; } if (!strcmp(*av,"-ofb")) { ciphermode = MODE_OFB; continue; } if (!strcmp(*av,"-stream")) { streaming = 1; continue; } if (!strcmp(*av,"-nostream")) { streaming = 1; continue; } if (!strcmp(*av,"-readkey")) { WANTARG(); keyfdarg = av[skip]; continue; } if (!strcmp(*av,"-1")) { type = TYPE_SINGLE; continue; } if (!strcmp(*av,"-3")) { type = TYPE_TRIPLE; 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); } 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 const char *process_hexarg(const char *arg, void *buf, int nb) { const char *ap; unsigned char *bp; int nib_h; int nib_l; if (! arg) { fprintf(stderr,"%s: nil argument to process_hexarg\n",__progname); abort(); } ap = arg; bp = buf; for (;nb>0;nb--) { nib_h = hdigval(*ap++); if (nib_h < 0) goto err; nib_l = hdigval(*ap++); if (nib_l < 0) goto err; *bp++ = (nib_h << 4) | nib_l; } return(0); err:; if (ap[-1] == '\0') return("too short"); return("invalid hex character"); } 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 int get_key_fd(void) { int fd; char *ep; fd = strtol(keyfdarg,&ep,0); if (*ep || (ep == 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); } } return(fd); } 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(); m2 = md5_init(); md5_process_bytes(m2,"",1); 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,"",1); 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; } } int main(int, char **); int main(int ac, char **av) { handleargs(ac,av); switch (type) { case TYPE_SINGLE: desfn = des; break; case TYPE_TRIPLE: desfn = des3; break; } if (! debugging) no_cores_please(); switch (opmode) { case MODE_E: get_iv(); do_encrypt(); break; case MODE_D: do_decrypt(); break; case MODE_K: get_keyhash(); print_hex(&k[0],KEYBYTES); break; case MODE_GENVEC: get_iv(); print_hex(&iv[0],8); break; case MODE_EBLOCK: if (keystyle != KS_HASH) { fprintf(stderr,"%s: -eblock implies and requires -keyhash\n",__progname); exit(1); } get_keyhash(); do_eblock(); break; case MODE_DBLOCK: if (keystyle != KS_HASH) { fprintf(stderr,"%s: -eblock implies and requires -keyhash\n",__progname); exit(1); } get_keyhash(); do_dblock(); break; default: fprintf(stderr,"%s: impossible operation mode %d\n",__progname,opmode); abort(); break; } exit(0); }