home *** CD-ROM | disk | FTP | other *** search
- /*
- crypt.c (full version) by Info-ZIP. Last revised: [see crypt.h]
-
- This code is not copyrighted and is put in the public domain. The
- encryption/decryption parts (as opposed to the non-echoing password
- parts) were originally written in Europe; the whole file can there-
- fore be freely distributed from any country except the USA. If this
- code is imported into the USA, it cannot be re-exported from from
- there to another country. (This restriction might seem curious, but
- this is what US law requires.)
- */
-
- /* This encryption code is a direct transcription of the algorithm from
- Roger Schlafly, described by Phil Katz in the file appnote.txt. This
- file (appnote.txt) is distributed with the PKZIP program (even in the
- version without encryption capabilities).
- */
-
- #define ZCRYPT_INTERNAL
- #include "zip.h"
- #include "crypt.h"
- #include "ttyio.h"
-
- #ifndef FALSE
- # define FALSE 0
- #endif
-
- #ifdef ZIP
- /* For the encoding task used in Zip (and ZipCloak), we want to initialize
- the crypt algorithm with some reasonably unpredictable bytes, see
- the crypthead() function. The standard rand() library function is
- used to supply these `random' bytes, which in turn is initialized by
- a srand() call. The srand() function takes an "unsigned" (at least 16bit)
- seed value as argument to determine the starting point of the rand()
- pseudo-random number generator.
- This seed number is constructed as "Seed = Seed1 .XOR. Seed2" with
- Seed1 supplied by the current time (= "(unsigned)time()") and Seed2
- as some (hopefully) nondeterministic bitmask. On many (most) systems,
- we use some "process specific" number, as the PID or something similar,
- but when nothing unpredictable is available, a fixed number may be
- sufficient.
- NOTE:
- 1.) This implementation requires the availability of the following
- standard UNIX C runtime library functions: time(), rand(), srand().
- On systems where some of them are missing, the environment that
- incorporates the crypt routines must supply suitable replacement
- functions.
- 2.) It is a very bad idea to use a second call to time() to set the
- "Seed2" number! In this case, both "Seed1" and "Seed2" would be
- (almost) identical, resulting in a (mostly) "zero" constant seed
- number passed to srand().
-
- The implementation environment defined in the "zip.h" header should
- supply a reasonable definition for ZCR_SEED2 (an unsigned number; for
- most implementations of rand() and srand(), only the lower 16 bits are
- significant!). An example that works on many systems would be
- "#define ZCR_SEED2 (unsigned)getpid()".
- The default definition for ZCR_SEED2 supplied below should be regarded
- as a fallback to allow successful compilation in "beta state"
- environments.
- */
- # include <time.h> /* time() function supplies first part of crypt seed */
- /* "last resort" source for second part of crypt seed pattern */
- # ifndef ZCR_SEED2
- # define ZCR_SEED2 (unsigned)3141592654L /* use PI as default pattern */
- # endif
- # ifdef GLOBAL /* used in Amiga system headers, maybe others too */
- # undef GLOBAL
- # endif
- # define GLOBAL(g) g
- #else /* !ZIP */
- # define GLOBAL(g) G.g
- #endif /* ?ZIP */
-
-
- #ifdef UNZIP
- /* char *key = (char *)NULL; moved to globals.h */
- # ifndef FUNZIP
- local int testp OF((__GPRO__ uch *h));
- local int testkey OF((__GPRO__ uch *h, char *key));
- # endif
- #endif /* UNZIP */
-
- #ifndef UNZIP /* moved to globals.h for UnZip */
- local ulg keys[3]; /* keys defining the pseudo-random sequence */
- #endif /* !UNZIP */
-
- #ifndef Trace
- # ifdef CRYPT_DEBUG
- # define Trace(x) fprintf x
- # else
- # define Trace(x)
- # endif
- #endif
-
- #ifndef CRC_32_TAB
- # define CRC_32_TAB crc_32_tab
- #endif
-
- #define CRC32(c, b) (CRC_32_TAB[((int)(c) ^ (b)) & 0xff] ^ ((c) >> 8))
-
- /***********************************************************************
- * Return the next byte in the pseudo-random sequence
- */
- int decrypt_byte(__G)
- __GDEF
- {
- unsigned temp; /* POTENTIAL BUG: temp*(temp^1) may overflow in an
- * unpredictable manner on 16-bit systems; not a problem
- * with any known compiler so far, though */
-
- temp = ((unsigned)GLOBAL(keys[2]) & 0xffff) | 2;
- return (int)(((temp * (temp ^ 1)) >> 8) & 0xff);
- }
-
- /***********************************************************************
- * Update the encryption keys with the next byte of plain text
- */
- int update_keys(__G__ c)
- __GDEF
- int c; /* byte of plain text */
- {
- GLOBAL(keys[0]) = CRC32(GLOBAL(keys[0]), c);
- GLOBAL(keys[1]) += GLOBAL(keys[0]) & 0xff;
- GLOBAL(keys[1]) = GLOBAL(keys[1]) * 134775813L + 1;
- {
- register int keyshift = (int)(GLOBAL(keys[1]) >> 24);
- GLOBAL(keys[2]) = CRC32(GLOBAL(keys[2]), keyshift);
- }
- return c;
- }
-
-
- /***********************************************************************
- * Initialize the encryption keys and the random header according to
- * the given password.
- */
- void init_keys(__G__ passwd)
- __GDEF
- char *passwd; /* password string with which to modify keys */
- {
- GLOBAL(keys[0]) = 305419896L;
- GLOBAL(keys[1]) = 591751049L;
- GLOBAL(keys[2]) = 878082192L;
- while (*passwd != '\0') {
- update_keys(__G__ (int)*passwd);
- passwd++;
- }
- }
-
-
- #ifdef ZIP
-
- /***********************************************************************
- * Write encryption header to file zfile using the password passwd
- * and the cyclic redundancy check crc.
- */
- void crypthead(passwd, crc, zfile)
- char *passwd; /* password string */
- ulg crc; /* crc of file being encrypted */
- FILE *zfile; /* where to write header */
- {
- int n; /* index in random header */
- int t; /* temporary */
- int c; /* random byte */
- int ztemp; /* temporary for zencoded value */
- uch header[RAND_HEAD_LEN-2]; /* random header */
- static unsigned calls = 0; /* ensure different random header each time */
-
- /* First generate RAND_HEAD_LEN-2 random bytes. We encrypt the
- * output of rand() to get less predictability, since rand() is
- * often poorly implemented.
- */
- if (++calls == 1) {
- srand((unsigned)time(NULL) ^ ZCR_SEED2);
- }
- init_keys(passwd);
- for (n = 0; n < RAND_HEAD_LEN-2; n++) {
- c = (rand() >> 7) & 0xff;
- header[n] = (uch)zencode(c, t);
- }
- /* Encrypt random header (last two bytes is high word of crc) */
- init_keys(passwd);
- for (n = 0; n < RAND_HEAD_LEN-2; n++) {
- ztemp = zencode(header[n], t);
- putc(ztemp, zfile);
- }
- ztemp = zencode((int)(crc >> 16) & 0xff, t);
- putc(ztemp, zfile);
- ztemp = zencode((int)(crc >> 24) & 0xff, t);
- putc(ztemp, zfile);
- }
-
-
- #ifdef UTIL
-
- /***********************************************************************
- * Encrypt the zip entry described by z from file source to file dest
- * using the password passwd. Return an error code in the ZE_ class.
- */
- int zipcloak(z, source, dest, passwd)
- struct zlist far *z; /* zip entry to encrypt */
- FILE *source, *dest; /* source and destination files */
- char *passwd; /* password string */
- {
- int c; /* input byte */
- int res; /* result code */
- ulg n; /* holds offset and counts size */
- ush flag; /* previous flags */
- int t; /* temporary */
- int ztemp; /* temporary storage for zencode value */
-
- /* Set encrypted bit, clear extended local header bit and write local
- header to output file */
- if ((n = ftell(dest)) == -1L) return ZE_TEMP;
- z->off = n;
- flag = z->flg;
- z->flg |= 1, z->flg &= ~8;
- z->lflg |= 1, z->lflg &= ~8;
- z->siz += RAND_HEAD_LEN;
- if ((res = putlocal(z, dest)) != ZE_OK) return res;
-
- /* Initialize keys with password and write random header */
- crypthead(passwd, z->crc, dest);
-
- /* Skip local header in input file */
- if (fseek(source, (long)(4 + LOCHEAD + (ulg)z->nam + (ulg)z->ext),
- SEEK_CUR)) {
- return ferror(source) ? ZE_READ : ZE_EOF;
- }
-
- /* Encrypt data */
- for (n = z->siz - RAND_HEAD_LEN; n; n--) {
- if ((c = getc(source)) == EOF) {
- return ferror(source) ? ZE_READ : ZE_EOF;
- }
- ztemp = zencode(c, t);
- putc(ztemp, dest);
- }
- /* Skip extended local header in input file if there is one */
- if ((flag & 8) != 0 && fseek(source, 16L, SEEK_CUR)) {
- return ferror(source) ? ZE_READ : ZE_EOF;
- }
- if (fflush(dest) == EOF) return ZE_TEMP;
- return ZE_OK;
- }
-
- /***********************************************************************
- * Decrypt the zip entry described by z from file source to file dest
- * using the password passwd. Return an error code in the ZE_ class.
- */
- int zipbare(__G__ z, source, dest, passwd)
- __GDEF
- struct zlist far *z; /* zip entry to encrypt */
- FILE *source, *dest; /* source and destination files */
- char *passwd; /* password string */
- {
- int c0, c1; /* last two input bytes */
- ulg offset; /* used for file offsets */
- ulg size; /* size of input data */
- int r; /* size of encryption header */
- int res; /* return code */
- ush flag; /* previous flags */
-
- /* Save position and skip local header in input file */
- if ((offset = ftell(source)) == -1L ||
- fseek(source, (long)(4 + LOCHEAD + (ulg)z->nam + (ulg)z->ext),
- SEEK_CUR)) {
- return ferror(source) ? ZE_READ : ZE_EOF;
- }
- /* Initialize keys with password */
- init_keys(passwd);
-
- /* Decrypt encryption header, save last two bytes */
- c1 = 0;
- for (r = RAND_HEAD_LEN; r; r--) {
- c0 = c1;
- if ((c1 = getc(source)) == EOF) {
- return ferror(source) ? ZE_READ : ZE_EOF;
- }
- Trace((stdout, " (%02x)", c1));
- zdecode(c1);
- Trace((stdout, " %02x", c1));
- }
- Trace((stdout, "\n"));
-
- /* If last two bytes of header don't match crc (or file time in the
- * case of an extended local header), back up and just copy. For
- * pkzip 2.0, the check has been reduced to one byte only.
- */
- #ifdef ZIP10
- if ((ush)(c0 | (c1<<8)) !=
- (z->flg & 8 ? (ush) z->tim & 0xffff : (ush)(z->crc >> 16))) {
- #else
- c0++; /* avoid warning on unused variable */
- if ((ush)c1 != (z->flg & 8 ? (ush) z->tim >> 8 : (ush)(z->crc >> 24))) {
- #endif
- if (fseek(source, offset, SEEK_SET)) {
- return ferror(source) ? ZE_READ : ZE_EOF;
- }
- if ((res = zipcopy(z, source, dest)) != ZE_OK) return res;
- return ZE_MISS;
- }
-
- /* Clear encrypted bit and local header bit, and write local header to
- output file */
- if ((offset = ftell(dest)) == -1L) return ZE_TEMP;
- z->off = offset;
- flag = z->flg;
- z->flg &= ~9;
- z->lflg &= ~9;
- z->siz -= RAND_HEAD_LEN;
- if ((res = putlocal(z, dest)) != ZE_OK) return res;
-
- /* Decrypt data */
- for (size = z->siz; size; size--) {
- if ((c1 = getc(source)) == EOF) {
- return ferror(source) ? ZE_READ : ZE_EOF;
- }
- zdecode(c1);
- putc(c1, dest);
- }
- /* Skip extended local header in input file if there is one */
- if ((flag & 8) != 0 && fseek(source, 16L, SEEK_CUR)) {
- return ferror(source) ? ZE_READ : ZE_EOF;
- }
- if (fflush(dest) == EOF) return ZE_TEMP;
-
- return ZE_OK;
- }
-
-
- #else /* !UTIL */
-
- /***********************************************************************
- * If requested, encrypt the data in buf, and in any case call fwrite()
- * with the arguments to zfwrite(). Return what fwrite() returns.
- */
- unsigned zfwrite(buf, item_size, nb, f)
- zvoid *buf; /* data buffer */
- extent item_size; /* size of each item in bytes */
- extent nb; /* number of items */
- FILE *f; /* file to write to */
- {
- int t; /* temporary */
-
- if (key != (char *)NULL) { /* key is the global password pointer */
- ulg size; /* buffer size */
- char *p = (char*)buf; /* steps through buffer */
-
- /* Encrypt data in buffer */
- for (size = item_size*(ulg)nb; size != 0; p++, size--) {
- *p = (char)zencode(*p, t);
- }
- }
- /* Write the buffer out */
- return fwrite(buf, item_size, nb, f);
- }
-
- #endif /* ?UTIL */
- #endif /* ZIP */
-
-
- #if (defined(UNZIP) && !defined(FUNZIP))
-
- /***********************************************************************
- * Get the password and set up keys for current zipfile member. Return
- * PK_ class error.
- */
- int decrypt(__G)
- __GDEF
- {
- ush b;
- int n, r;
- uch h[RAND_HEAD_LEN];
-
- Trace((stdout, "\n[incnt = %d]: ", GLOBAL(incnt)));
-
- /* get header once (turn off "encrypted" flag temporarily so we don't
- * try to decrypt the same data twice) */
- GLOBAL(pInfo->encrypted) = FALSE;
- defer_leftover_input(__G);
- for (n = 0; n < RAND_HEAD_LEN; n++) {
- b = NEXTBYTE;
- h[n] = (uch)b;
- Trace((stdout, " (%02x)", h[n]));
- }
- undefer_input(__G);
- GLOBAL(pInfo->encrypted) = TRUE;
-
- if (GLOBAL(newzip)) { /* this is first encrypted member in this zipfile */
- GLOBAL(newzip) = FALSE;
- if (GLOBAL(P_flag)) { /* user gave password on command line */
- if (!GLOBAL(key)) {
- if ((GLOBAL(key) = (char *)malloc(PWLEN+1)) == (char *)NULL)
- return PK_MEM2;
- strncpy(GLOBAL(key), GLOBAL(pwdarg), PWLEN);
- GLOBAL(nopwd) = TRUE; /* inhibit password prompting! */
- }
- } else if (GLOBAL(key)) { /* get rid of previous zipfile's key */
- free(GLOBAL(key));
- GLOBAL(key) = (char *)NULL;
- }
- }
-
- /* if have key already, test it; else allocate memory for it */
- if (GLOBAL(key)) {
- if (!testp(__G__ h))
- return PK_COOL; /* existing password OK (else prompt for new) */
- else if (GLOBAL(nopwd))
- return PK_WARN; /* user indicated no more prompting */
- } else if ((GLOBAL(key) = (char *)malloc(PWLEN+1)) == (char *)NULL)
- return PK_MEM2;
-
- /* try a few keys */
- n = 0;
- do {
- r = (*G.decr_passwd)((zvoid *)&G, &n, GLOBAL(key), PWLEN+1,
- GLOBAL(zipfn), GLOBAL(filename));
- if (r == IZ_PW_ERROR) { /* internal error in fetch of PW */
- free (GLOBAL(key));
- GLOBAL(key) = NULL;
- return PK_MEM2;
- }
- if (r != IZ_PW_ENTERED) { /* user replied "skip" or "skip all" */
- *GLOBAL(key) = '\0'; /* We try the NIL password, ... */
- n = 0; /* and cancel fetch for this item. */
- }
- if (!testp(__G__ h))
- return PK_COOL;
- if (r == IZ_PW_CANCELALL) /* User replied "Skip all" */
- GLOBAL(nopwd) = TRUE; /* inhibit any further PW prompt! */
- } while (n > 0);
-
- return PK_WARN;
-
- } /* end function decrypt() */
-
-
-
- /***********************************************************************
- * Test the password. Return -1 if bad, 0 if OK.
- */
- local int testp(__G__ h)
- __GDEF
- uch *h;
- {
- int r;
- char *key_translated;
-
- /* On systems with "obscure" native character coding (e.g., EBCDIC),
- * the first test translates the password to the "main standard"
- * character coding. */
-
- #ifdef STR_TO_CP1
- /* allocate buffer for translated password */
- if ((key_translated = malloc(strlen(GLOBAL(key)) + 1)) == (char *)NULL)
- return -1;
- /* first try, test password translated "standard" charset */
- r = testkey(__G__ h, STR_TO_CP1(key_translated, GLOBAL(key)));
- #else /* !STR_TO_CP1 */
- /* first try, test password as supplied on the extractor's host */
- r = testkey(__G__ h, GLOBAL(key));
- #endif /* ?STR_TO_CP1 */
-
- #ifdef STR_TO_CP2
- if (r != 0) {
- #ifndef STR_TO_CP1
- /* now prepare for second (and maybe third) test with translated pwd */
- if ((key_translated = malloc(strlen(GLOBAL(key)) + 1)) == (char *)NULL)
- return -1;
- #endif
- /* second try, password translated to alternate ("standard") charset */
- r = testkey(__G__ h, STR_TO_CP2(key_translated, GLOBAL(key)));
- #ifdef STR_TO_CP3
- if (r != 0)
- /* third try, password translated to another "standard" charset */
- r = testkey(__G__ h, STR_TO_CP3(key_translated, GLOBAL(key)));
- #endif
- #ifndef STR_TO_CP1
- free(key_translated);
- #endif
- }
- #endif /* STR_TO_CP2 */
-
- #ifdef STR_TO_CP1
- free(key_translated);
- if (r != 0) {
- /* last resort, test password as supplied on the extractor's host */
- r = testkey(__G__ h, GLOBAL(key));
- }
- #endif /* STR_TO_CP1 */
-
- return r;
-
- } /* end function testp() */
-
-
- local int testkey(__G__ h, key)
- __GDEF
- uch *h; /* decrypted header */
- char *key; /* decryption password to test */
- {
- ush b;
- #ifdef ZIP10
- ush c;
- #endif
- int n;
- uch *p;
- uch hh[RAND_HEAD_LEN]; /* decrypted header */
-
- /* set keys and save the encrypted header */
- init_keys(__G__ key);
- memcpy(hh, h, RAND_HEAD_LEN);
-
- /* check password */
- for (n = 0; n < RAND_HEAD_LEN; n++) {
- zdecode(hh[n]);
- Trace((stdout, " %02x", hh[n]));
- }
-
- Trace((stdout,
- "\n lrec.crc= %08lx crec.crc= %08lx pInfo->ExtLocHdr= %s\n",
- GLOBAL(lrec.crc32), GLOBAL(pInfo->crc),
- GLOBAL(pInfo->ExtLocHdr) ? "true":"false"));
- Trace((stdout, " incnt = %d unzip offset into zipfile = %ld\n",
- GLOBAL(incnt),
- GLOBAL(cur_zipfile_bufstart)+(GLOBAL(inptr)-GLOBAL(inbuf))));
-
- /* same test as in zipbare(): */
-
- #ifdef ZIP10 /* check two bytes */
- c = hh[RAND_HEAD_LEN-2], b = hh[RAND_HEAD_LEN-1];
- Trace((stdout,
- " (c | (b<<8)) = %04x (crc >> 16) = %04x lrec.time = %04x\n",
- (ush)(c | (b<<8)), (ush)(GLOBAL(lrec.crc32) >> 16),
- GLOBAL(lrec.last_mod_file_time)));
- if ((ush)(c | (b<<8)) != (GLOBAL(pInfo->ExtLocHdr) ?
- GLOBAL(lrec.last_mod_file_time) :
- (ush)(GLOBAL(lrec.crc32) >> 16)))
- return -1; /* bad */
- #else
- b = hh[RAND_HEAD_LEN-1];
- Trace((stdout, " b = %02x (crc >> 24) = %02x (lrec.time >> 8) = %02x\n",
- b, (ush)(GLOBAL(lrec.crc32) >> 24),
- (GLOBAL(lrec.last_mod_file_time) >> 8)));
- if (b != (GLOBAL(pInfo->ExtLocHdr) ? GLOBAL(lrec.last_mod_file_time) >> 8 :
- (ush)(GLOBAL(lrec.crc32) >> 24)))
- return -1; /* bad */
- #endif
- /* password OK: decrypt current buffer contents before leaving */
- for (n = (long)GLOBAL(incnt) > GLOBAL(csize) ?
- (int)GLOBAL(csize) : GLOBAL(incnt),
- p = GLOBAL(inptr); n--; p++)
- zdecode(*p);
- return 0; /* OK */
-
- } /* end function testkey() */
-
- #endif /* UNZIP && !FUNZIP */
-