/* crypto/des/des.c */ /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) * All rights reserved. * * This package is an SSL implementation written * by Eric Young (eay@cryptsoft.com). * The implementation was written so as to conform with Netscapes SSL. * * This library is free for commercial and non-commercial use as long as * the following conditions are aheared to. The following conditions * apply to all code found in this distribution, be it the RC4, RSA, * lhash, DES, etc., code; not just the SSL code. The SSL documentation * included with this distribution is covered by the same copyright terms * except that the holder is Tim Hudson (tjh@cryptsoft.com). * * Copyright remains Eric Young's, and as such any Copyright notices in * the code are not to be removed. * If this package is used in a product, Eric Young should be given attribution * as the author of the parts of the library used. * This can be in the form of a textual message at program startup or * in documentation (online or textual) provided with the package. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * "This product includes cryptographic software written by * Eric Young (eay@cryptsoft.com)" * The word 'cryptographic' can be left out if the rouines from the library * being used are not cryptographic related :-). * 4. If you include any Windows specific code (or a derivative thereof) from * the apps directory (application code) you must include an acknowledgement: * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" * * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * The licence and distribution terms for any publically available version or * derivative of this code cannot be changed. i.e. this code cannot simply be * copied and put under another distribution licence * [including the GNU Public Licence.] */ #include #include #include #include #ifndef OPENSSL_SYS_MSDOS # ifndef OPENSSL_SYS_VMS # include OPENSSL_UNISTD # else /* OPENSSL_SYS_VMS */ # ifdef __DECC # include # else /* not __DECC */ # include # endif /* __DECC */ # endif /* OPENSSL_SYS_VMS */ #else /* OPENSSL_SYS_MSDOS */ # include #endif #include #include "des_ver.h" #ifdef OPENSSL_SYS_VMS # include # include #else # ifndef _IRIX # include # endif # include #endif #include #include #include void usage(void); void doencryption(void); int uufwrite(unsigned char *data, int size, unsigned int num, FILE *fp); void uufwriteEnd(FILE *fp); int uufread(unsigned char *out, int size, unsigned int num, FILE *fp); int uuencode(unsigned char *in, int num, unsigned char *out); int uudecode(unsigned char *in, int num, unsigned char *out); void DES_3cbc_encrypt(DES_cblock *input, DES_cblock *output, long length, DES_key_schedule sk1, DES_key_schedule sk2, DES_cblock *ivec1, DES_cblock *ivec2, int enc); #ifdef OPENSSL_SYS_VMS # define EXIT(a) exit(a&0x10000000L) #else # define EXIT(a) exit(a) #endif #define BUFSIZE (8*1024) #define VERIFY 1 #define KEYSIZ 8 #define KEYSIZB 1024 /* should hit tty line limit first :-) */ char key[KEYSIZB + 1]; int do_encrypt, longk = 0; FILE *DES_IN, *DES_OUT, *CKSUM_OUT; char uuname[200]; unsigned char uubuf[50]; int uubufnum = 0; #define INUUBUFN (45*100) #define OUTUUBUF (65*100) unsigned char b[OUTUUBUF]; unsigned char bb[300]; DES_cblock cksum = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; char cksumname[200] = ""; int vflag, cflag, eflag, dflag, kflag, bflag, fflag, sflag, uflag, flag3, hflag, error; int main(int argc, char **argv) { int i; struct stat ins, outs; char *p; char *in = NULL, *out = NULL; vflag = cflag = eflag = dflag = kflag = hflag = bflag = fflag = sflag = uflag = flag3 = 0; error = 0; memset(key, 0, sizeof(key)); for (i = 1; i < argc; i++) { p = argv[i]; if ((p[0] == '-') && (p[1] != '\0')) { p++; while (*p) { switch (*(p++)) { case '3': flag3 = 1; longk = 1; break; case 'c': cflag = 1; strncpy(cksumname, p, 200); cksumname[sizeof(cksumname) - 1] = '\0'; p += strlen(cksumname); break; case 'C': cflag = 1; longk = 1; strncpy(cksumname, p, 200); cksumname[sizeof(cksumname) - 1] = '\0'; p += strlen(cksumname); break; case 'e': eflag = 1; break; case 'v': vflag = 1; break; case 'E': eflag = 1; longk = 1; break; case 'd': dflag = 1; break; case 'D': dflag = 1; longk = 1; break; case 'b': bflag = 1; break; case 'f': fflag = 1; break; case 's': sflag = 1; break; case 'u': uflag = 1; strncpy(uuname, p, 200); uuname[sizeof(uuname) - 1] = '\0'; p += strlen(uuname); break; case 'h': hflag = 1; break; case 'k': kflag = 1; if ((i + 1) == argc) { fputs("must have a key with the -k option\n", stderr); error = 1; } else { int j; i++; strncpy(key, argv[i], KEYSIZB); for (j = strlen(argv[i]) - 1; j >= 0; j--) argv[i][j] = '\0'; } break; default: fprintf(stderr, "'%c' unknown flag\n", p[-1]); error = 1; break; } } } else { if (in == NULL) in = argv[i]; else if (out == NULL) out = argv[i]; else error = 1; } } if (error) usage(); /*- * We either * do checksum or * do encrypt or * do decrypt or * do decrypt then ckecksum or * do checksum then encrypt */ if (((eflag + dflag) == 1) || cflag) { if (eflag) do_encrypt = DES_ENCRYPT; if (dflag) do_encrypt = DES_DECRYPT; } else { if (vflag) { #ifndef _Windows fprintf(stderr, "des(1) built with %s\n", libdes_version); #endif EXIT(1); } else usage(); } #ifndef _Windows if (vflag) fprintf(stderr, "des(1) built with %s\n", libdes_version); #endif if ((in != NULL) && (out != NULL) && #ifndef OPENSSL_SYS_MSDOS (stat(in, &ins) != -1) && (stat(out, &outs) != -1) && (ins.st_dev == outs.st_dev) && (ins.st_ino == outs.st_ino)) #else /* OPENSSL_SYS_MSDOS */ (strcmp(in, out) == 0)) #endif { fputs("input and output file are the same\n", stderr); EXIT(3); } if (!kflag) if (des_read_pw_string (key, KEYSIZB + 1, "Enter key:", eflag ? VERIFY : 0)) { fputs("password error\n", stderr); EXIT(2); } if (in == NULL) DES_IN = stdin; else if ((DES_IN = fopen(in, "r")) == NULL) { perror("opening input file"); EXIT(4); } CKSUM_OUT = stdout; if (out == NULL) { DES_OUT = stdout; CKSUM_OUT = stderr; } else if ((DES_OUT = fopen(out, "w")) == NULL) { perror("opening output file"); EXIT(5); } #ifdef OPENSSL_SYS_MSDOS /* This should set the file to binary mode. */ { # include if (!(uflag && dflag)) setmode(fileno(DES_IN), O_BINARY); if (!(uflag && eflag)) setmode(fileno(DES_OUT), O_BINARY); } #endif doencryption(); fclose(DES_IN); fclose(DES_OUT); EXIT(0); } void usage(void) { char **u; static const char *Usage[] = { "des [input-file [output-file]]", "options:", "-v : des(1) version number", "-e : encrypt using SunOS compatible user key to DES key conversion.", "-E : encrypt ", "-d : decrypt using SunOS compatible user key to DES key conversion.", "-D : decrypt ", "-c[ckname] : generate a cbc_cksum using SunOS compatible user key to", " DES key conversion and output to ckname (stdout default,", " stderr if data being output on stdout). The checksum is", " generated before encryption and after decryption if used", " in conjunction with -[eEdD].", "-C[ckname] : generate a cbc_cksum as for -c but compatible with -[ED].", "-k key : use key 'key'", "-h : the key that is entered will be a hexadecimal number", " that is used directly as the des key", "-u[uuname] : input file is uudecoded if -[dD] or output uuencoded data if -[eE]", " (uuname is the filename to put in the uuencode header).", "-b : encrypt using DES in ecb encryption mode, the default is cbc mode.", "-3 : encrypt using triple DES encryption. This uses 2 keys", " generated from the input key. If the input key is less", " than 8 characters long, this is equivalent to normal", " encryption. Default is triple cbc, -b makes it triple ecb.", NULL }; for (u = (char **)Usage; *u; u++) { fputs(*u, stderr); fputc('\n', stderr); } EXIT(1); } void doencryption(void) { #ifdef _LIBC extern unsigned long time(); #endif register int i; DES_key_schedule ks, ks2; DES_cblock iv, iv2; char *p; int num = 0, j, k, l, rem, ll, len, last, ex = 0; DES_cblock kk, k2; FILE *O; int Exit = 0; #ifndef OPENSSL_SYS_MSDOS static unsigned char buf[BUFSIZE + 8], obuf[BUFSIZE + 8]; #else static unsigned char *buf = NULL, *obuf = NULL; if (buf == NULL) { if (((buf = OPENSSL_malloc(BUFSIZE + 8)) == NULL) || ((obuf = OPENSSL_malloc(BUFSIZE + 8)) == NULL)) { fputs("Not enough memory\n", stderr); Exit = 10; goto problems; } } #endif if (hflag) { j = (flag3 ? 16 : 8); p = key; for (i = 0; i < j; i++) { k = 0; if ((*p <= '9') && (*p >= '0')) k = (*p - '0') << 4; else if ((*p <= 'f') && (*p >= 'a')) k = (*p - 'a' + 10) << 4; else if ((*p <= 'F') && (*p >= 'A')) k = (*p - 'A' + 10) << 4; else { fputs("Bad hex key\n", stderr); Exit = 9; goto problems; } p++; if ((*p <= '9') && (*p >= '0')) k |= (*p - '0'); else if ((*p <= 'f') && (*p >= 'a')) k |= (*p - 'a' + 10); else if ((*p <= 'F') && (*p >= 'A')) k |= (*p - 'A' + 10); else { fputs("Bad hex key\n", stderr); Exit = 9; goto problems; } p++; if (i < 8) kk[i] = k; else k2[i - 8] = k; } DES_set_key_unchecked(&k2, &ks2); OPENSSL_cleanse(k2, sizeof(k2)); } else if (longk || flag3) { if (flag3) { DES_string_to_2keys(key, &kk, &k2); DES_set_key_unchecked(&k2, &ks2); OPENSSL_cleanse(k2, sizeof(k2)); } else DES_string_to_key(key, &kk); } else for (i = 0; i < KEYSIZ; i++) { l = 0; k = key[i]; for (j = 0; j < 8; j++) { if (k & 1) l++; k >>= 1; } if (l & 1) kk[i] = key[i] & 0x7f; else kk[i] = key[i] | 0x80; } DES_set_key_unchecked(&kk, &ks); OPENSSL_cleanse(key, sizeof(key)); OPENSSL_cleanse(kk, sizeof(kk)); /* woops - A bug that does not showup under unix :-( */ memset(iv, 0, sizeof(iv)); memset(iv2, 0, sizeof(iv2)); l = 1; rem = 0; /* first read */ if (eflag || (!dflag && cflag)) { for (;;) { num = l = fread(&(buf[rem]), 1, BUFSIZE, DES_IN); l += rem; num += rem; if (l < 0) { perror("read error"); Exit = 6; goto problems; } rem = l % 8; len = l - rem; if (feof(DES_IN)) { for (i = 7 - rem; i > 0; i--) RAND_pseudo_bytes(buf + l++, 1); buf[l++] = rem; ex = 1; len += rem; } else l -= rem; if (cflag) { DES_cbc_cksum(buf, &cksum, (long)len, &ks, &cksum); if (!eflag) { if (feof(DES_IN)) break; else continue; } } if (bflag && !flag3) for (i = 0; i < l; i += 8) DES_ecb_encrypt((DES_cblock *)&(buf[i]), (DES_cblock *)&(obuf[i]), &ks, do_encrypt); else if (flag3 && bflag) for (i = 0; i < l; i += 8) DES_ecb2_encrypt((DES_cblock *)&(buf[i]), (DES_cblock *)&(obuf[i]), &ks, &ks2, do_encrypt); else if (flag3 && !bflag) { char tmpbuf[8]; if (rem) memcpy(tmpbuf, &(buf[l]), (unsigned int)rem); DES_3cbc_encrypt((DES_cblock *)buf, (DES_cblock *)obuf, (long)l, ks, ks2, &iv, &iv2, do_encrypt); if (rem) memcpy(&(buf[l]), tmpbuf, (unsigned int)rem); } else { DES_cbc_encrypt(buf, obuf, (long)l, &ks, &iv, do_encrypt); if (l >= 8) memcpy(iv, &(obuf[l - 8]), 8); } if (rem) memcpy(buf, &(buf[l]), (unsigned int)rem); i = 0; while (i < l) { if (uflag) j = uufwrite(obuf, 1, (unsigned int)l - i, DES_OUT); else j = fwrite(obuf, 1, (unsigned int)l - i, DES_OUT); if (j == -1) { perror("Write error"); Exit = 7; goto problems; } i += j; } if (feof(DES_IN)) { if (uflag) uufwriteEnd(DES_OUT); break; } } } else { /* decrypt */ ex = 1; for (;;) { if (ex) { if (uflag) l = uufread(buf, 1, BUFSIZE, DES_IN); else l = fread(buf, 1, BUFSIZE, DES_IN); ex = 0; rem = l % 8; l -= rem; } if (l < 0) { perror("read error"); Exit = 6; goto problems; } if (bflag && !flag3) for (i = 0; i < l; i += 8) DES_ecb_encrypt((DES_cblock *)&(buf[i]), (DES_cblock *)&(obuf[i]), &ks, do_encrypt); else if (flag3 && bflag) for (i = 0; i < l; i += 8) DES_ecb2_encrypt((DES_cblock *)&(buf[i]), (DES_cblock *)&(obuf[i]), &ks, &ks2, do_encrypt); else if (flag3 && !bflag) { DES_3cbc_encrypt((DES_cblock *)buf, (DES_cblock *)obuf, (long)l, ks, ks2, &iv, &iv2, do_encrypt); } else { DES_cbc_encrypt(buf, obuf, (long)l, &ks, &iv, do_encrypt); if (l >= 8) memcpy(iv, &(buf[l - 8]), 8); } if (uflag) ll = uufread(&(buf[rem]), 1, BUFSIZE, DES_IN); else ll = fread(&(buf[rem]), 1, BUFSIZE, DES_IN); ll += rem; rem = ll % 8; ll -= rem; if (feof(DES_IN) && (ll == 0)) { last = obuf[l - 1]; if ((last > 7) || (last < 0)) { fputs("The file was not decrypted correctly.\n", stderr); Exit = 8; last = 0; } l = l - 8 + last; } i = 0; if (cflag) DES_cbc_cksum(obuf, (DES_cblock *)cksum, (long)l / 8 * 8, &ks, (DES_cblock *)cksum); while (i != l) { j = fwrite(obuf, 1, (unsigned int)l - i, DES_OUT); if (j == -1) { perror("Write error"); Exit = 7; goto problems; } i += j; } l = ll; if ((l == 0) && feof(DES_IN)) break; } } if (cflag) { l = 0; if (cksumname[0] != '\0') { if ((O = fopen(cksumname, "w")) != NULL) { CKSUM_OUT = O; l = 1; } } for (i = 0; i < 8; i++) fprintf(CKSUM_OUT, "%02X", cksum[i]); fprintf(CKSUM_OUT, "\n"); if (l) fclose(CKSUM_OUT); } problems: OPENSSL_cleanse(buf, sizeof(buf)); OPENSSL_cleanse(obuf, sizeof(obuf)); OPENSSL_cleanse(&ks, sizeof(ks)); OPENSSL_cleanse(&ks2, sizeof(ks2)); OPENSSL_cleanse(iv, sizeof(iv)); OPENSSL_cleanse(iv2, sizeof(iv2)); OPENSSL_cleanse(kk, sizeof(kk)); OPENSSL_cleanse(k2, sizeof(k2)); OPENSSL_cleanse(uubuf, sizeof(uubuf)); OPENSSL_cleanse(b, sizeof(b)); OPENSSL_cleanse(bb, sizeof(bb)); OPENSSL_cleanse(cksum, sizeof(cksum)); if (Exit) EXIT(Exit); } /* We ignore this parameter but it should be > ~50 I believe */ int uufwrite(unsigned char *data, int size, unsigned int num, FILE *fp) { int i, j, left, rem, ret = num; static int start = 1; if (start) { fprintf(fp, "begin 600 %s\n", (uuname[0] == '\0') ? "text.d" : uuname); start = 0; } if (uubufnum) { if (uubufnum + num < 45) { memcpy(&(uubuf[uubufnum]), data, (unsigned int)num); uubufnum += num; return (num); } else { i = 45 - uubufnum; memcpy(&(uubuf[uubufnum]), data, (unsigned int)i); j = uuencode((unsigned char *)uubuf, 45, b); fwrite(b, 1, (unsigned int)j, fp); uubufnum = 0; data += i; num -= i; } } for (i = 0; i < (((int)num) - INUUBUFN); i += INUUBUFN) { j = uuencode(&(data[i]), INUUBUFN, b); fwrite(b, 1, (unsigned int)j, fp); } rem = (num - i) % 45; left = (num - i - rem); if (left) { j = uuencode(&(data[i]), left, b); fwrite(b, 1, (unsigned int)j, fp); i += left; } if (i != num) { memcpy(uubuf, &(data[i]), (unsigned int)rem); uubufnum = rem; } return (ret); } void uufwriteEnd(FILE *fp) { int j; static const char *end = " \nend\n"; if (uubufnum != 0) { uubuf[uubufnum] = '\0'; uubuf[uubufnum + 1] = '\0'; uubuf[uubufnum + 2] = '\0'; j = uuencode(uubuf, uubufnum, b); fwrite(b, 1, (unsigned int)j, fp); } fwrite(end, 1, strlen(end), fp); } /* * int size: should always be > ~ 60; I actually ignore this parameter :-) */ int uufread(unsigned char *out, int size, unsigned int num, FILE *fp) { int i, j, tot; static int done = 0; static int valid = 0; static int start = 1; if (start) { for (;;) { b[0] = '\0'; fgets((char *)b, 300, fp); if (b[0] == '\0') { fprintf(stderr, "no 'begin' found in uuencoded input\n"); return (-1); } if (strncmp((char *)b, "begin ", 6) == 0) break; } start = 0; } if (done) return (0); tot = 0; if (valid) { memcpy(out, bb, (unsigned int)valid); tot = valid; valid = 0; } for (;;) { b[0] = '\0'; fgets((char *)b, 300, fp); if (b[0] == '\0') break; i = strlen((char *)b); if ((b[0] == 'e') && (b[1] == 'n') && (b[2] == 'd')) { done = 1; while (!feof(fp)) { fgets((char *)b, 300, fp); } break; } i = uudecode(b, i, bb); if (i < 0) break; if ((i + tot + 8) > num) { /* num to copy to make it a multiple of 8 */ j = (num / 8 * 8) - tot - 8; memcpy(&(out[tot]), bb, (unsigned int)j); tot += j; memcpy(bb, &(bb[j]), (unsigned int)i - j); valid = i - j; break; } memcpy(&(out[tot]), bb, (unsigned int)i); tot += i; } return (tot); } #define ccc2l(c,l) (l =((DES_LONG)(*((c)++)))<<16, \ l|=((DES_LONG)(*((c)++)))<< 8, \ l|=((DES_LONG)(*((c)++)))) #define l2ccc(l,c) (*((c)++)=(unsigned char)(((l)>>16)&0xff), \ *((c)++)=(unsigned char)(((l)>> 8)&0xff), \ *((c)++)=(unsigned char)(((l) )&0xff)) int uuencode(unsigned char *in, int num, unsigned char *out) { int j, i, n, tot = 0; DES_LONG l; register unsigned char *p; p = out; for (j = 0; j < num; j += 45) { if (j + 45 > num) i = (num - j); else i = 45; *(p++) = i + ' '; for (n = 0; n < i; n += 3) { ccc2l(in, l); *(p++) = ((l >> 18) & 0x3f) + ' '; *(p++) = ((l >> 12) & 0x3f) + ' '; *(p++) = ((l >> 6) & 0x3f) + ' '; *(p++) = ((l) & 0x3f) + ' '; tot += 4; } *(p++) = '\n'; tot += 2; } *p = '\0'; l = 0; return (tot); } int uudecode(unsigned char *in, int num, unsigned char *out) { int j, i, k; unsigned int n = 0, space = 0; DES_LONG l; DES_LONG w, x, y, z; unsigned int blank = (unsigned int)'\n' - ' '; for (j = 0; j < num;) { n = *(in++) - ' '; if (n == blank) { n = 0; in--; } if (n > 60) { fprintf(stderr, "uuencoded line length too long\n"); return (-1); } j++; for (i = 0; i < n; j += 4, i += 3) { /* * the following is for cases where spaces are removed from * lines. */ if (space) { w = x = y = z = 0; } else { w = *(in++) - ' '; x = *(in++) - ' '; y = *(in++) - ' '; z = *(in++) - ' '; } if ((w > 63) || (x > 63) || (y > 63) || (z > 63)) { k = 0; if (w == blank) k = 1; if (x == blank) k = 2; if (y == blank) k = 3; if (z == blank) k = 4; space = 1; switch (k) { case 1: w = 0; in--; case 2: x = 0; in--; case 3: y = 0; in--; case 4: z = 0; in--; break; case 0: space = 0; fprintf(stderr, "bad uuencoded data values\n"); w = x = y = z = 0; return (-1); break; } } l = (w << 18) | (x << 12) | (y << 6) | (z); l2ccc(l, out); } if (*(in++) != '\n') { fprintf(stderr, "missing nl in uuencoded line\n"); w = x = y = z = 0; return (-1); } j++; } *out = '\0'; w = x = y = z = 0; return (n); }