--- pgp/src/crypto.c 2018/04/24 16:38:51 1.1.1.2 +++ pgp/src/crypto.c 2018/04/24 16:39:34 1.1.1.3 @@ -21,6 +21,12 @@ Add FDL stuff for VAX/VMS local mode. Reopen temporary files rather than create new version. + Modified: 13-Dec-92 Derek Atkins @@ -35,19 +41,33 @@ #include "idea.h" #include "crypto.h" #include "keymgmt.h" +#include "keymaint.h" #include "mdfile.h" #include "fileio.h" +#include "charset.h" #include "language.h" #include "pgp.h" +#include "exitpgp.h" +#include "zipup.h" #define ENCRYPT_IT FALSE /* to pass to idea_file */ #define DECRYPT_IT TRUE /* to pass to idea_file */ -/* The kbhit() function: Determines if a key has been hit. May not be - available in some implementations */ +#define USE_LITERAL2 + + +/* This variable stores the md5 hash of the current file, if it is + available. It is used in open_strong_pseudorandom. */ +static unsigned char md5buf[16]; + +/* This flag is set if the buffer above has been filled. */ +static char already_have_md5 = 0; + + +/* Used by encryptfile */ +static int encryptkeyintofile(FILE *g, char *mcguffin, byte *keybuf, + char *keyfile, int ckp_length, int keys_used); -int kbhit( void ); -int zipup(FILE *, FILE *); #ifdef M_XENIX long time(); #endif @@ -124,7 +144,7 @@ word32 get_timestamp(byte *timestamp) } /* get_timestamp */ -int date_ymd(word32 *tstamp, int *year, int *month, int *day) +static int date_ymd(word32 *tstamp, int *year, int *month, int *day) /* Given timestamp as seconds elapsed since 1970 Jan 1 00:00:00, returns year (1970-2106), month (1-12), day (1-31). Not valid for dates after 2100 Feb 28 (no leap day that year). @@ -184,8 +204,7 @@ char *ctdate(word32 *tstamp) /* Warn user he if key in keyfile at position fp of length pktlen, belonging * to userid, is untrusted. Return -1 if the user doesn't want to proceed. */ -int warn_signatures(char *keyfile, long fp, int pktlen, char *userid, - boolean warn_only) +static int warn_signatures(char *keyfile, long fp, char *userid, boolean warn_only) { FILE *f; long fpusr; int usrpktlen; @@ -193,7 +212,6 @@ int warn_signatures(char *keyfile, long int trust_status = -1; keyctrl = KC_LEGIT_UNKNOWN; /* Assume the worst */ - PascalToC(userid); if (getpubuserid (keyfile, fp, (byte *) userid, &fpusr, &usrpktlen, FALSE) >= 0) { f = fopen(keyfile, FOPRBIN); fseek (f, fpusr+usrpktlen, SEEK_SET); @@ -227,18 +245,27 @@ possibly because the secret key was comp PascalToC(userid); strcpy ((char *) userid0, userid); CToPascal(userid); + if ((keyctrl & KC_LEGIT_MASK) == KC_LEGIT_UNKNOWN) fprintf(pgpout,PSTR("\007\nWARNING: Because this public key is not certified with a trusted\n\ signature, it is not known with high confidence that this public key\n\ actually belongs to: \"%s\".\n"), LOCAL_CHARSET((char *)userid0)); + if ((keyctrl & KC_LEGIT_MASK) == KC_LEGIT_UNTRUSTED) fprintf(pgpout, PSTR("\007\nWARNING: This public key is not trusted to actually belong to:\n\ \"%s\".\n"), LOCAL_CHARSET((char *)userid0)); + if ((keyctrl & KC_LEGIT_MASK) == KC_LEGIT_MARGINAL) fprintf(pgpout,PSTR("\007\nWARNING: Because this public key is not certified with enough trusted\n\ signatures, it is not known with high confidence that this public key\n\ actually belongs to: \"%s\".\n"), LOCAL_CHARSET((char *)userid0)); - if (!filter_mode && !warn_only && !(keyctrl & KC_WARNONLY)) + + if (keyctrl & KC_WARNONLY) + { /* KC_WARNONLY bit already set, user must have approved before. */ + fprintf(pgpout, PSTR("But you previously approved using this public key anyway.\n")); + } + + if (!filter_mode && !batchmode && !warn_only && !(keyctrl & KC_WARNONLY)) { fprintf(pgpout,PSTR("\nAre you sure you want to use this public key (y/N)? ")); if (!getyesno('n')) return(-1); @@ -250,8 +277,6 @@ actually belongs to: \"%s\".\n"), LOCAL_ } } } - if( pktlen ); /* Get rid of unused parameter warning */ - return(0); } /* warn_signatures */ @@ -294,112 +319,169 @@ version_error(int val, int checkval) /*-------------------------------------------------------------------------*/ +#define RAND_PREFIX_LENGTH 8 /* Length of IV for IDEA encryption */ -int strong_pseudorandom(byte *buf, int bufsize) -/* - Reads IDEA random key and random number seed from file, cranks the - the seed through the idearand strong pseudorandom number generator, - and writes them back out. This is used for generation of - cryptographically strong pseudorandom numbers. This is mainly to - save the user the trouble of having to type in lengthy keyboard - sequences for generation of truly random numbers every time we want - to make a random session key. This pseudorandom generator will only - work if the file containing the random seed exists and is not empty. - If it doesn't exist, it will be automatically created. If it exists - and is empty or nearly empty, it will not be used. -*/ +int seedfile_exists(void) +/* + If the file randseed.bin does not exist, this returns 0 and + schedules the appropriate number of random bytes for later + collection from the keyboard. + */ { char seedfile[MAX_PATH]; /* Random seed filename */ - FILE *f; - byte key[IDEAKEYSIZE]; - byte seed[IDEABLOCKSIZE]; - int i; - word32 tstamp; byte *timestamp = (byte *) &tstamp; buildfilename(seedfile,RANDSEED_FILENAME); + if (file_exists(seedfile)) + return 1; /* True */ + randaccum_later(8*(IDEAKEYSIZE+RAND_PREFIX_LENGTH)); + return 0; +} - if (!file_exists(seedfile)) /* No seed file. Start one... */ - { f = fopen(seedfile,FOPWBIN); /* open for writing binary */ - if (f==NULL) /* failed to create seedfile */ - return(-1); /* error: no random number seed file available */ - fclose(f); /* close new empty seed file */ - /* kickstart the generator with true random numbers */ - fprintf(pgpout,PSTR("Initializing random seed file...")); - randaccum(8*(sizeof(key)+sizeof(seed))); - for (i=1; i0) - { ideacfb(textbuf,count); - if (fwrite(textbuf,1,count,g) != count) - { status = -3; - break; - } + /* read and write the whole file in CFB mode... */ + count = (lenfile < DISKBUFSIZE) ? (int)lenfile : DISKBUFSIZE; + while (count && status == 0) + { if ((count = fread(textbuf,1,count,f)) <= 0) + { status = -3; + break; } - /* if text block was short, exit loop */ - } while (count==DISKBUFSIZE); + lenfile -= count; + ideacfb(textbuf,count); + if (fwrite(textbuf,1,count,g) != count) + status = -3; + count = (lenfile < DISKBUFSIZE) ? (int)lenfile : DISKBUFSIZE; + } close_idea(); /* Clean up data structures */ burn(iv); /* burn sensitive data on stack */ @@ -630,6 +711,7 @@ int get_header_info_from_file(char *infi #define REASONABLE_DATE ((unsigned long) 0x27804180L) /* 91 Jan 01 00:00:00 */ +static int make_signature_certificate(byte *certificate, MD5_CTX *MD, byte class, unitptr n, unitptr d, unitptr p, unitptr q, unitptr u) /* Constructs a signed message digest in a signature certificate. @@ -667,24 +749,21 @@ int make_signature_certificate(byte *cer MD_addbuffer (MD, &class, 1, FALSE); MD_addbuffer (MD, timestamp, 4, TRUE); -#ifdef XLOWFIRST /* Assumes LSB-first order */ - mdbufptr = MD->digest; /* point at actual message digest */ - for (i=0; idigest, sizeof(MD->digest), n, NULL); +#else /* ! PKCS_COMPAT */ +/* Assume MSB external byte ordering */ mdpacket[0] = MD5_ALGORITHM_BYTE; mdbufptr = MD->digest; /* point at actual message digest */ for (i=1; i 93-02-25 + */ + if (*password) + memset(password, 0, sizeof(password)); + + if (getsecretkey(0, NULL, NULL, timestamp, NULL, NULL, userid, n, e, d, p, q, u) < 0) { fclose(f); return(-1); /* problem with secret key file. error return. */ @@ -1206,7 +1263,7 @@ above user ID (y/N)? ")); fwrite(certificate,1,certificate_length,g); /* Add "trusty" control packet */ - write_trust (g, KC_SIGTRUST_ULTIMATE | KC_CONTIG); + write_trust (g, KC_SIGTRUST_ULTIMATE|KC_CONTIG|KC_SIG_CHECKED); /* Copy the remainder from file f to file g */ copyfile (f, g, -1L); @@ -1235,7 +1292,7 @@ int check_signaturefile(char *infile, ch also write the signature to outfile.sig. the original filename is stored in preserved_name */ - byte ctb,ctb2; /* Cipher Type Bytes */ + byte ctb,ctb2=0; /* Cipher Type Bytes */ char keyfile[MAX_PATH]; /* for getpublickey */ char sigfile[MAX_PATH]; /* .sig file if strip_signature */ char plainfile[MAX_PATH]; /* buffer for getstring() */ @@ -1243,8 +1300,7 @@ int check_signaturefile(char *infile, ch char *tempFileName; /* Name for temporary uncanonicalized file */ FILE *tempFile; #endif /* !CANONICAL_TEXT */ - long fp; /* unused, just to satisfy getpublickey */ - int pktlen; /* unused, just to satisfy getpublickey */ + long fp; FILE *f; FILE *g; long start_text; /* marks file position */ @@ -1263,8 +1319,7 @@ int check_signaturefile(char *infile, ch MD5_CTX MD; boolean separate_signature; boolean fixedLiteral = FALSE; /* Whether it's a fixed literal2 packet */ - extern boolean moreflag; - extern char *myArgv[]; + extern char **myArgv; extern int myArgc; char lit_mode = MODE_BINARY; unsigned char litfile[MAX_PATH]; @@ -1282,6 +1337,7 @@ int check_signaturefile(char *infile, ch char *fdl; short fdl_len; #endif + int outbufoffset; fill0( keyID, KEYFRAGSIZE ); @@ -1363,8 +1419,8 @@ int check_signaturefile(char *infile, ch /* getpublickey() sets precision for mpi2reg, if key not found: use maximum precision to avoid error return from mpi2reg() */ - if (getpublickey(FALSE, verbose, keyfile, &fp, &pktlen, - keyID, (byte *)&dummystamp, userid, n, e) < 0) + if (getpublickey(0, keyfile, &fp, NULL, keyID, + (byte *)&dummystamp, userid, n, e) < 0) set_precision(MAX_UNIT_PRECISION); /* safest opening assumption */ if (mpi2reg((unitptr)inbuf,certificate) == -1) /* get signed message digest */ @@ -1398,7 +1454,10 @@ int check_signaturefile(char *infile, ch if (file_exists(outfile)) fprintf(pgpout,PSTR("\nText is assumed to be in file '%s'.\n"),outfile); else - { fprintf(pgpout,PSTR("\nPlease enter filename of text that signature applies to: ")); + { + if (batchmode) + return -1; + fprintf(pgpout,PSTR("\nPlease enter filename of text that signature applies to: ")); getstring(outfile,59,TRUE); /* echo keyboard */ if ((int)strlen(outfile) == 0) return(-1); @@ -1440,9 +1499,15 @@ int check_signaturefile(char *infile, ch the filename and the timestamp and mode byte and litfile length byte */ text_len -= litfile[0] + sizeof(dummystamp) + 2; if (litfile[0] > 0) - fread (litfile+1,1,litfile[0],f); + { if ((int)litfile[0] >= MAX_PATH) + { fseek(f, litfile[0], SEEK_CUR); + litfile[0] = 0; + } + else + fread (litfile+1,1,litfile[0],f); + } /* Use litfile if it's writeable and he didn't say an outfile */ - if (litfile[0] < MAX_PATH) + if (litfile[0]) { PascalToC( (char *)litfile ); if (verbose) fprintf(pgpout, PSTR("Original plaintext file name was: '%s'\n"), litfile); @@ -1485,25 +1550,23 @@ int check_signaturefile(char *infile, ch /* Use keyID prefix to look up key... */ /* Get and validate public key from a key file: */ - if (getpublickey(FALSE, verbose, keyfile, &fp, &pktlen, - keyID, (byte *)&dummystamp, userid, n, e) < 0) + if (getpublickey(0, keyfile, &fp, NULL, keyID, + (byte *)&dummystamp, userid, n, e) < 0) { /* Can't get public key. Complain and process file copy anyway. */ fprintf(pgpout,PSTR("\n\007WARNING: Can't find the right public key-- can't check signature integrity.\n")); goto outsig; } /* Can't find public key */ - if (warn_signatures(keyfile, fp, pktlen, (char *)userid, TRUE) < 0) - goto err1; - /* Recover message digest via public key */ mp_modexp((unitptr)outbuf,(unitptr)inbuf,e,n); - if (!filter_mode) + if (!quietmode) fputc('.',pgpout); /* Signal RSA completion. */ /* Unblock message digest, and convert to external byte order: */ count = postunblock(outbuf, (unitptr)outbuf, n); - if (count < 0) + + if (count == -1) { fprintf(pgpout,PSTR("\n\007Error: RSA-decrypted block is corrupted.\n\ This may be caused either by corrupted data or by using the wrong RSA key.\n")); goto outsig; /* Output data anyway */ @@ -1513,24 +1576,20 @@ This may be caused either by corrupted d /*==================================================================*/ /* Look at nested stuff within RSA block... */ -#ifdef XLOWFIRST - /* Position of algorithm byte assumes LSB-first byteorder... */ - if (outbuf[count-1] != MD5_ALGORITHM_BYTE) -#else - /* Position of algorithm byte assumes MSB-first byteorder... */ - if (outbuf[0] != MD5_ALGORITHM_BYTE) -#endif + if (count == -2 || + (count != sizeof(MD.digest) && count != sizeof(MD.digest)+1) || + (count == sizeof(MD.digest)+1 && outbuf[0] != MD5_ALGORITHM_BYTE)) { fprintf(pgpout,PSTR("\007\nUnrecognized message digest algorithm.\n")); fprintf(pgpout,PSTR("This may require a newer version of PGP.\n")); fprintf(pgpout,PSTR("Can't check signature integrity.\n")); goto outsig; /* Output data anyway */ } -#ifdef XLOWFIRST - if (outbuf[0] != mdlow2[0] || outbuf[1] != mdlow2[1]) -#else - if (outbuf[1] != mdlow2[0] || outbuf[2] != mdlow2[1]) -#endif + /* Distinguish PKCS-compatible from pre-3.3 which has an extra byte */ + outbufoffset = (count==sizeof(MD.digest)) ? 0 : 1; + + if (outbuf[outbufoffset] != mdlow2[0] || + outbuf[outbufoffset+1] != mdlow2[1]) { fprintf(pgpout,PSTR("\n\007Error: RSA-decrypted block is corrupted.\n\ This may be caused either by corrupted data or by using the wrong RSA key.\n")); goto outsig; /* Output data anyway */ @@ -1547,11 +1606,8 @@ This may be caused either by corrupted d PascalToC((char *)userid); /* for display */ /* now compare computed MD with claimed MD */ -#ifdef XLOWFIRST - if (!equal_buffers((byte *)(MD.digest), outbuf, 16)) -#else - if (!equal_buffers((byte *)(MD.digest), outbuf+1, 16)) -#endif +/* Assume MSB external byte ordering */ + if (!equal_buffers((byte *)(MD.digest), outbuf+outbufoffset, 16)) { #ifndef CANONICAL_TEXT /* IF the signature is bad, AND this machine does not use MSDOS-style @@ -1569,7 +1625,7 @@ This may be caused either by corrupted d tempFileName = tempfile( TMP_WIPE | TMP_TMPDIR ); if (verbose) fprintf(stderr, "signature checking failed, trying in canonical mode\n"); - if( ( tempFile = fopen( tempFileName, FOPRWBIN ) ) != NULL ) + if( ( tempFile = fopen( tempFileName, FOPWPBIN ) ) != NULL ) { /* We've opened a temporary work file, copy the text to it with canonicalization */ copyfile_to_canon( f, tempFile, -1L ); @@ -1584,11 +1640,8 @@ This may be caused either by corrupted d rmtemp( tempFileName ); /* Check if the signature is OK this time round */ -#ifdef XLOWFIRST - if( equal_buffers( ( byte * )( MD.digest ), outbuf, 16 ) ) -#else - if( equal_buffers( ( byte * )( MD.digest ), outbuf + 1, 16 ) ) -#endif /* XLOWFIRST */ +/* Assume MSB external byte ordering */ + if(equal_buffers((byte *)(MD.digest),outbuf+outbufoffset,16)) goto goodsig; } } @@ -1598,26 +1651,34 @@ This may be caused either by corrupted d fprintf(pgpout,PSTR("\nBad signature from user \"%s\".\n"), LOCAL_CHARSET((char *)userid)); fprintf(pgpout,PSTR("Signature made %s\n"),ctdate((word32 *)timestamp)); - if (moreflag) + if (moreflag && !batchmode) { /* more will scroll the message off the screen */ fprintf(pgpout, PSTR("\nPress ENTER to continue...")); fflush(pgpout); getyesno('n'); } - goto outsig; /* Output data anyway */ + goto warnsig; /* Output data anyway */ } goodsig: + signature_checked = TRUE; /* set flag for batch processing */ fprintf(pgpout,PSTR("\nGood signature from user \"%s\".\n"), LOCAL_CHARSET((char *)userid)); fprintf(pgpout,PSTR("Signature made %s\n"),ctdate((word32 *)timestamp)); +warnsig: + /* warn only, don't ask if user wants to use the key */ + warn_signatures(keyfile, fp, (char *)userid, TRUE); + outsig: /* Reposition file to where that plaintext begins... */ fseek(f,start_text,SEEK_SET); /* reposition file from last ftell */ if (separate_signature) - fprintf(pgpout,PSTR("\nSignature and text are separate. No output file produced. ")); + { + if (!quietmode) + fprintf(pgpout,PSTR("\nSignature and text are separate. No output file produced. ")); + } else /* signature precedes plaintext in file... */ { /* produce a plaintext output file from signature file */ /* open file g for write, in binary or text mode...*/ @@ -1633,7 +1694,9 @@ outsig: } fdl_close(g); #endif /*VMS */ - } else { + } + else + { if (lit_mode == MODE_BINARY) g = fopen(outfile, FOPWBIN); else @@ -1659,7 +1722,7 @@ outsig: { /* Copy signature to a .sig file */ strcpy (sigfile, outfile); force_extension(sigfile,SIG_EXTENSION); - if (file_exists(sigfile)) + if (!force_flag && file_exists(sigfile)) { fprintf(pgpout,PSTR("\n\007Signature file '%s' already exists. Overwrite (y/N)? "), sigfile); if (!getyesno('n')) @@ -1676,7 +1739,8 @@ outsig: goto err1; } fclose(g); - fprintf(pgpout,PSTR("\nWriting signature certificate to '%s'\n"),sigfile); + if (!quietmode) + fprintf(pgpout,PSTR("\nWriting signature certificate to '%s'\n"),sigfile); } } @@ -1714,8 +1778,7 @@ int check_key_sig(FILE *fkey, long fpkey we can't check the signature, -2 if bad or other problem. */ byte ctb; /* Cipher Type Bytes */ - long fp; /* unused, just to satisfy getpublickey */ - int pktlen; /* unused, just to satisfy getpublickey */ + long fp; word16 cert_length; int i, count; byte certbuf[MAX_SIGCERT_LENGTH]; @@ -1734,6 +1797,7 @@ int check_key_sig(FILE *fkey, long fpkey byte class; byte algorithm; byte mdlow2[2]; + int outbufoffset; fill0( keyID, KEYFRAGSIZE ); @@ -1810,8 +1874,8 @@ int check_key_sig(FILE *fkey, long fpkey * key here to set precision, before we go on. */ /* This sets precision, too, based on n. */ - if (getpublickey(TRUE, FALSE, keyfile, &fp, &pktlen, - keyID, xtimestamp, (unsigned char *)siguserid, n, e) < 0) + if (getpublickey(GPK_GIVEUP, keyfile, &fp, NULL, keyID, + xtimestamp, (unsigned char *)siguserid, n, e) < 0) goto err1; if (mpi2reg((unitptr)inbuf,certificate) == -1) /* get signed message digest */ @@ -1827,25 +1891,27 @@ int check_key_sig(FILE *fkey, long fpkey mp_modexp((unitptr)outbuf,(unitptr)inbuf,e,n); /* Unblock message digest, and convert to external byte order: */ - if ((count = postunblock(outbuf, (unitptr)outbuf, n)) < 0) + count = postunblock(outbuf, (unitptr)outbuf, n); + + if (count == -2) + goto err1; /* Unrecognized digest algorithm */ + + if (count != sizeof(MD.digest) && count != sizeof(MD.digest)+1) goto err2; /* Bad RSA decrypt. Corruption, or wrong key. */ + /* Distinguish PKCS-compatible from pre-3.3 which has an extra byte */ + outbufoffset = (count==sizeof(MD.digest)) ? 0 : 1; + /* outbuf should contain message digest packet */ /*==================================================================*/ /* Look at nested stuff within RSA block... */ -#ifdef XLOWFIRST /* assumes LSB-first */ - if (outbuf[count-1] != MD5_ALGORITHM_BYTE) +/* Assume MSB external byte ordering */ + if (outbufoffset && outbuf[0] != MD5_ALGORITHM_BYTE) goto err2; /* Bad RSA decrypt. Corruption, or wrong key. */ - if (outbuf[0] != mdlow2[0] || outbuf[1] != mdlow2[1]) + if (outbuf[outbufoffset] != mdlow2[0] || + outbuf[outbufoffset+1] != mdlow2[1]) goto err2; /* Bad RSA decrypt. Corruption, or wrong key. */ -#else - if( count ); /* Get rid of unused variable warning */ - if (outbuf[0] != MD5_ALGORITHM_BYTE) - goto err2; /* Bad RSA decrypt. Corruption, or wrong key. */ - if (outbuf[1] != mdlow2[0] || outbuf[2] != mdlow2[1]) - goto err2; /* Bad RSA decrypt. Corruption, or wrong key. */ -#endif /* Position file to where that plaintext begins... */ fseek(fkey,fpkey,SEEK_SET); @@ -1859,13 +1925,9 @@ int check_key_sig(FILE *fkey, long fpkey MD_addbuffer (&MD, mdextras, mdlensave, TRUE); /* Finish message digest */ /* now compare computed MD with claimed MD */ -#ifdef XLOWFIRST /* assumes LSB-first */ - if (!equal_buffers((byte *)(MD.digest), outbuf, 16)) - goto err2; -#else - if (!equal_buffers((byte *)(MD.digest), outbuf+1, 16)) - goto err2; -#endif +/* Assume MSB external byte ordering */ + if (!equal_buffers((byte *)(MD.digest), outbuf+outbufoffset, 16)) + goto err2; convert_byteorder(timestamp,4); /* convert timestamp from external form */ memcpy (xtimestamp, timestamp, 4); /* Return signature timestamp */ @@ -1893,11 +1955,11 @@ err2: /*======================================================================*/ -int squish_and_idea_file(byte *ideakey, FILE *f, FILE *g, +static int squish_and_idea_file(byte *ideakey, FILE *f, FILE *g, boolean attempt_compression) { FILE *t; - char *tempf; + char *tempf = NULL; byte ctb; word32 fpos, fpos0; extern char plainfile[]; @@ -1917,15 +1979,14 @@ int squish_and_idea_file(byte *ideakey, t = f; /* skip compression attempt */ else /* attempt compression-- get a tempfile */ if ((tempf = tempfile(TMP_TMPDIR|TMP_WIPE)) == NULL || - (t = fopen(tempf, FOPRWBIN)) == NULL) /* error: no tempfile */ + (t = fopen(tempf, FOPWPBIN)) == NULL) /* error: no tempfile */ t = f; /* skip compression attempt */ else /* attempt compression */ { extern int zipup( FILE *, FILE * ); - if (verbose) fprintf(pgpout,"\nCompressing [%s] to %s ", - plainfile, tempf ); + if (verbose) fprintf(pgpout,"\nCompressing [%s] ", plainfile); /* We don't put a length field on CTB_COMPRESSED yet */ ctb = CTB_COMPRESSED; /* use compression prefix CTB */ @@ -1938,11 +1999,12 @@ int squish_and_idea_file(byte *ideakey, zipup( f, t); if (write_error(t)) { fclose(t); - rmtemp(tempf); + if (tempf) + rmtemp(tempf); return(-1); } if (verbose) fprintf(pgpout, PSTR("compressed. ") ); - else if (!filter_mode) + else if (!quietmode) fputc('.',pgpout); /* show progress */ rewind( t ); } @@ -1962,7 +2024,8 @@ int squish_and_idea_file(byte *ideakey, if (t != f) { fclose( t ); /* close and remove the temporary file */ - rmtemp(tempf); + if (tempf) + rmtemp(tempf); } return(0); /* normal return */ @@ -2028,7 +2091,6 @@ int idea_encryptfile(char *infile, char FILE *g; /* output file */ byte ideakey[16]; byte passphrase[256]; - extern char password[]; if (verbose) fprintf(pgpout,"idea_encryptfile: infile = '%s', outfile = '%s'\n", @@ -2053,15 +2115,16 @@ int idea_encryptfile(char *infile, char if (*password != '\0') hashpass(password, strlen(password), ideakey); else { - fprintf(pgpout,PSTR("\nYou need a pass phrase to encrypt the file. ")); - if (GetHashedPassPhrase((char *)passphrase,(char *)ideakey,NOECHO2) <= 0) + if (!quietmode) + fprintf(pgpout,PSTR("\nYou need a pass phrase to encrypt the file. ")); + if (batchmode || GetHashedPassPhrase((char *)passphrase,(char *)ideakey,NOECHO2) <= 0) { fclose(f); fclose(g); return(-1); } } - if (!filter_mode) + if (!quietmode) { fprintf(pgpout,PSTR("Just a moment...")); /* this may take a while */ fflush(pgpout); } @@ -2086,57 +2149,29 @@ int idea_encryptfile(char *infile, char /*======================================================================*/ +static byte (*keyID_list)[KEYFRAGSIZE] = NULL; -int encryptfile(char *mcguffin, char *infile, char *outfile, +int encryptfile(char **mcguffins, char *infile, char *outfile, boolean attempt_compression) { - byte randompad[MAX_BYTE_PRECISION]; /* buffer of random pad bytes */ - int i,blocksize,ckp_length; + int i,ckp_length; FILE *f; FILE *g; - unit n[MAX_UNIT_PRECISION], e[MAX_UNIT_PRECISION]; - byte inbuf[MAX_BYTE_PRECISION]; - byte outbuf[MAX_BYTE_PRECISION]; - word32 tstamp; byte *timestamp = (byte *) &tstamp; /* key certificate timestamp */ - byte userid[256]; - byte ideakey[16]; /* must be big enough for make_random_ideakey */ - char keyfile[MAX_PATH]; /* for getpublickey */ - long fp; /* unused, just to satisfy getpublickey */ - int pktlen; /* unused, just to satisfy getpublickey */ + byte keybuf[MAX_BYTE_PRECISION]; /* This keeps our IDEA to encrypt */ + byte ideakey[24]; /* must be big enough for make_random_ideakey */ word32 chksum; - byte ver, alg; - + char keyfile[MAX_PATH]; + int keys_used = 0; - buildfilename(keyfile,PUBLIC_KEYRING_FILENAME); /* use default pathname */ + if (mcguffins == NULL || *mcguffins == NULL || **mcguffins == '\0') { + /* Well, we haven't gotten a user, lets die here */ + return -1; + } if (verbose) fprintf(pgpout,"encryptfile: infile = %s, outfile = %s\n", infile,outfile); - userid[0] = '\0'; - if (mcguffin) - strcpy((char *)userid,mcguffin);/* Who we are looking for (C string) */ - - /* Get and validate public key from a key file: */ - if (getpublickey(FALSE, !filter_mode, keyfile, &fp, &pktlen, NULL, timestamp, userid, n, e) < 0) - { return(-1); - } - - if (warn_signatures(keyfile, fp, pktlen, (char *)userid, FALSE) < 0) - return(-1); - - /* set_precision has been properly called by getpublickey */ - - /* Note that RSA key must be at least big enough to encipher a - complete conventional key packet in a single RSA block. - */ - - blocksize = countbytes(n)-1; /* size of a plaintext block */ - if (blocksize < 31) - { fprintf(pgpout,"\n\007Error: RSA key length must be at least 256 bits.\n"); - return(-1); - } - /* open file f for read, in binary (not text) mode...*/ if ((f = fopen( infile, FOPRBIN )) == NULL) { @@ -2152,87 +2187,101 @@ int encryptfile(char *mcguffin, char *in return(-1); } - /* Now we have to time some user keystrokes to get some random - bytes for generating a random IDEA cipher key. - We would have to solicit fewer keystrokes for random IDEA - key generation if we had already accumulated some keystrokes - incidental to some other purpose, such as asking for a password - to decode an RSA secret key so that a signature could be applied - to the message before encrypting it. + /* Now we have to generate a random session key and IV. + As part of this computation, we use the MD5 hash of the + current file, if it has previously been obtained due to a + signing operation. If it has not been obtained, we hash + the first 2K (for efficiency reasons) for input into + the key generatrion process. This is to ensure that + capturing a randseed.bin file will not allow reconstruction + of subsequent session keys without knowing the message + that was encrypted. (A session key only protects a + single message, so it is reasonable to assume that an + opponent trying to obtain a session key is trying to + obtain, and thus is ignorant of, the message it encrypts.) + + This is not perfect, but it's an improvement on how session + keys used to be generated, and can be changed in future + without compatibility worries. */ + if (!already_have_md5) /* Obtain some random bits from the input file */ + { MD5_CTX MD; + + MD5Init(&MD); + MDfile0_len(&MD, f, 4096); /* Ignore errors - what could be done? */ + MD5Final(&MD); + + fseek(f, 0, SEEK_SET); /* Get back to the beginning for encryption */ + + memcpy(md5buf, MD.digest, 16); + + burn(&MD); + already_have_md5 = 1; + } + ckp_length = make_random_ideakey(ideakey); - /* Returns an 16 byte random IDEA key */ + /* Returns a 24 byte random IDEA key */ -#ifdef XLOWFIRST /* Assumes LSB-first order */ - for (i=0; i 13 Dec 1992 + */ - /* convert RSA ciphertext block via reg2mpi and write to file */ + for (i = 0; mcguffins[i] != NULL; ++i) + ; + if (encrypt_to_self) + ++i; + keyID_list = xmalloc(i * KEYFRAGSIZE); + /* Iterate through users */ + for (; *mcguffins && **mcguffins ; ++mcguffins) { + buildfilename(keyfile,PUBLIC_KEYRING_FILENAME); + /* use default pathname */ + + keys_used = + encryptkeyintofile(g, *mcguffins, keybuf, + keyfile, ckp_length, keys_used); + } /* for */ - write_mpi( (unitptr)outbuf, g, FALSE ); + if (!keys_used) + { fclose(f); + fclose(g); + free(keyID_list); + return -1; + } - burn(inbuf); /* burn sensitive data on stack */ - burn(outbuf); /* burn sensitive data on stack */ + /* encrypt to myself if need be */ + if (encrypt_to_self && *my_name) { + keys_used = + encryptkeyintofile(g, my_name, keybuf, + keyfile, ckp_length, keys_used); + } + free(keyID_list); + close_idearand(); /** Finished with RSA block containing IDEA key. */ /* Now compress the plaintext and encrypt it with IDEA... */ squish_and_idea_file( ideakey, f, g, attempt_compression ); + burn(keybuf); /* burn the Idea Key Packet */ burn(ideakey); /* burn sensitive data on stack */ fclose(f); @@ -2246,6 +2295,149 @@ int encryptfile(char *mcguffin, char *in } /* encryptfile */ +static int +encryptkeyintofile(FILE *g, char *mcguffin, byte *keybuf, + char *keyfile, int ckp_length, int keys_used) { + int i; + byte randompad[MAX_BYTE_PRECISION]; /* buffer of random pad bytes */ + unit n[MAX_UNIT_PRECISION], e[MAX_UNIT_PRECISION]; + byte keyID[KEYFRAGSIZE]; + byte inbuf[MAX_BYTE_PRECISION]; + byte outbuf[MAX_BYTE_PRECISION]; + word32 tstamp; byte *timestamp = (byte *) &tstamp; /* key certificate timestamp */ + byte userid[256]; + long fp; + int blocksize; + byte ver, alg; + byte (*keyp)[KEYFRAGSIZE]; + + + /* This "loop" is so we can break out at opportune moments */ + do { + userid[0] = '\0'; + + strcpy((char *)userid,mcguffin); + /* Who we are looking for (C string) */ + + /* Get and validate public key from a key file: + * We will be nice and ask the user ONCE (and ONLY once) + * for a keyfile if its not in the default. + */ + + if (getpublickey((quietmode?0:GPK_SHOW)|GPK_NORVK, keyfile, &fp, NULL, NULL, + timestamp, userid, n, e) < 0) + { fprintf(pgpout, PSTR("\n\007Cannot find the public key matching userid '%s'\n\ +This user will not be able to decrypt this message.\n"), + LOCAL_CHARSET(mcguffin)); + continue; + } + + /* Make sure we haven't already used this key */ + extract_keyID(keyID, n); + for (keyp = keyID_list; keyp < keyID_list+keys_used; ++keyp) + { if (!memcmp(keyp, keyID, KEYFRAGSIZE)) + break; + } + + if (keyp < keyID_list + keys_used) + { /* This key was already specified. Quietly ignore it. */ + continue; + } + + /* Add this keyID to the list of keys used so far */ + memcpy(keyp, keyID, KEYFRAGSIZE); + + PascalToC((char *)userid); + if (warn_signatures(keyfile, fp, (char *)userid, + FALSE) < 0) { + fprintf(pgpout, "Ok, skipping userid %s\n", mcguffin); + continue; + } + + /* set_precision has been properly called by getpublickey */ + + /* Note that RSA key must be at least big enough + to encipher a complete conventional key packet + in a single RSA block. + */ + + blocksize = countbytes(n)-1; + /* size of a plaintext block */ + + if (blocksize < 31) { + fprintf(pgpout,"\n\007Error: RSA key length must be at least 256 bits.\n"); + fprintf(pgpout, "Skipping userid %s\n", mcguffin); + continue; + } + + + /* + ** Messages encrypted with a public key should use + ** random padding, while messages "signed" with a + ** secret key should use constant padding. + */ + + for (i = 0; i < (blocksize - ckp_length); i++) + while (!(randompad[i] = idearand())) + ; /* Allow only nonzero values */ + + /* + ** Note that RSA key must be at least big enough + ** to encipher a complete conventional key packet + ** in a single RSA block. + */ + + /* ckp_length is conventional key packet length. */ + + /* Do the padding here */ + preblock( (unitptr)inbuf, keybuf, ckp_length, n, randompad ); + + /* XXX This is dangerous... This will print out the + * IDEA Key, which is a breach of security! + */ +#ifdef MR_DEBUG + fprintf(pgpout, "IdeaKey and Random Pad: "); + for (i = 0; i < blocksize; i++) { + fprintf(pgpout, "%02X ", inbuf[i]); + } + fprintf(pgpout, "\n"); +#endif + mp_modexp( (unitptr)outbuf, (unitptr)inbuf, e, n ); + /* RSA encrypt */ + + /* write out header record to outfile ... */ + + /* PKE is Public Key Encryption */ + write_ctb_len (g, CTB_PKE_TYPE, + 1+KEYFRAGSIZE+1+2+countbytes((unitptr)outbuf), + FALSE); + + /* Write version byte */ + ver = VERSION_BYTE; + fwrite (&ver, 1, 1, g); + + writekeyID( n, g ); + /* write msg prefix fragment of modulus n */ + + /* Write algorithm byte */ + alg = RSA_ALGORITHM_BYTE; + fwrite (&alg, 1, 1, g); + + /* convert RSA ciphertext block via reg2mpi and + * write to file + */ + + write_mpi( (unitptr)outbuf, g, FALSE ); + + burn(inbuf); /* burn sensitive data on stack */ + burn(outbuf); /* burn sensitive data on stack */ + ++keys_used; + + } while (0); + + return(keys_used); +} /* encryptkeyintofile */ + /*======================================================================*/ int make_literal(char *infile, char *outfile, char lit_mode, char *literalfile) { /* Prepend a CTB_LITERAL prefix to a file. Convert to canonical form if @@ -2435,14 +2627,20 @@ int strip_literal(char *infile, char *ou /* Read literal file name, use it if possible */ litfile[0] = 0; fread (litfile,1,1,f); - if (litfile[0] > 0) - fread (litfile+1,1,litfile[0],f); if (is_ctb_type(ctb, CTB_LITERAL2_TYPE)) { /* subtract header length: namelength + lengthbyte + modebyte + stamp */ LITlength -= litfile[0] + 2 + sizeof(dummystamp); } /* Use litfile if it's writeable and he didn't say an outfile */ - if (litfile[0] < MAX_PATH) + if (litfile[0] > 0) + { if ((int)litfile[0] >= MAX_PATH) + { fseek(f, litfile[0], SEEK_CUR); + litfile[0] = 0; + } + else + fread (litfile+1,1,litfile[0],f); + } + if (litfile[0]) { PascalToC( (char *)litfile ); if (verbose) fprintf(pgpout, PSTR("Original plaintext file name was: '%s'\n"), litfile); @@ -2534,7 +2732,7 @@ int decryptfile(char *infile, char *outf byte ctbCKE; /* Cipher Type Byte */ FILE *f; FILE *g; - int count, status; + int count, status, thiskey, gotkey, end_of_pkes; unit n[MAX_UNIT_PRECISION], e[MAX_UNIT_PRECISION], d[MAX_UNIT_PRECISION]; unit p[MAX_UNIT_PRECISION], q[MAX_UNIT_PRECISION], u[MAX_UNIT_PRECISION]; byte inbuf[MAX_BYTE_PRECISION]; @@ -2543,10 +2741,14 @@ int decryptfile(char *infile, char *outf word32 tstamp; byte *timestamp = (byte *) &tstamp; /* key certificate timestamp */ byte userid[256]; word32 flen; + word32 fpos = 0; byte ver, alg; + short realprecision = 0; word16 chksum; - - set_precision(MAX_UNIT_PRECISION); /* safest opening assumption */ + struct nkey { + byte keyID[KEYFRAGSIZE]; + struct nkey *next; + } *nkey, *nkeys = NULL; if (verbose) fprintf(pgpout,"decryptfile: infile = %s, outfile = %s\n", @@ -2558,62 +2760,156 @@ int decryptfile(char *infile, char *outf return(-1); } - fread(&ctb,1,1,f); /* read Cipher Type Byte */ - if (!is_ctb(ctb)) - { fprintf(pgpout,PSTR("\n\007'%s' is not a cipher file.\n"),infile); - fclose(f); - return(-1); - } + /* Now we have to keep reading in packets until we either get + * to a non PKE-type packet or we find our own... Once we find + * our own, we're gonna have to get our private key, and then + * keep going until we find the end of the PKE packets + * + * -derek 13 Dec 1992 + */ - /* PKE is Public Key Encryption */ - if (!is_ctb_type(ctb,CTB_PKE_TYPE)) - { fprintf(pgpout,PSTR("\n\007'%s' is not enciphered with a public key.\n"),infile); - fclose(f); - return(-1); - } + gotkey = end_of_pkes = 0; /* Set this flag now. */ + do { + thiskey = 0; + + set_precision(MAX_UNIT_PRECISION); + /* Need to set this EACH TIME... Sigh. This is because + * read_mpi needs to have a global_precision which is + * >= the size of the key. Therefore once we find the + * real key, we save off the precision and then we'll + * reset it later. -derek + */ + + fread(&ctb,1,1,f); /* read Cipher Type Byte */ + if (!is_ctb(ctb)) { + fprintf(pgpout,PSTR("\n\007'%s' is not a cipher file.\n"),infile); + fclose(f); + return(-1); + } - getpastlength(ctb, f); /* read packet length */ + /* PKE is Public Key Encryption */ + if (!is_ctb_type(ctb,CTB_PKE_TYPE)) { + end_of_pkes = 1; + continue; + } - /* Read and check version */ - fread (&ver, 1, 1, f); - if (version_error(ver, VERSION_BYTE)) - { fclose (f); - return (-1); - } - - fread(keyID,1,KEYFRAGSIZE,f); /* read key ID */ - /* Use keyID prefix to look up key. */ - - /* Read and check algorithm */ - fread (&alg, 1, 1, f); - if (version_error(alg, RSA_ALGORITHM_BYTE)) - { fclose (f); - return (-1); - } - - /* Get and validate secret key from a key file: */ - if (getsecretkey(FALSE, !filter_mode, NULL, keyID, timestamp, NULL, NULL,userid, - n, e, d, p, q, u) < 0) - { fclose(f); - return(-1); - } + getpastlength(ctb, f); /* read packet length */ + + /* Read and check version */ + fread (&ver, 1, 1, f); + if (version_error(ver, VERSION_BYTE)) + { fclose (f); + return (-1); + } - /* Note that RSA key must be at least big enough to encipher a - complete conventional key packet in a single RSA block. */ + fread(keyID,1,KEYFRAGSIZE,f); /* read key ID */ + /* Use keyID prefix to look up key. */ - /*==================================================================*/ - /* read ciphertext block, converting to internal format: */ - read_mpi((unitptr)inbuf, f, FALSE, FALSE); + /* Add this keyID to the list of keys read in */ + if ((nkey = (struct nkey *) malloc(sizeof(struct nkey))) + == NULL) { + fprintf(stderr, PSTR("\n\007Out of memory.\n")); + exitPGP(7); + } + memcpy(nkey->keyID, keyID, KEYFRAGSIZE); + nkey->next = nkeys; + nkeys = nkey; + + /* Read and check algorithm */ + fread (&alg, 1, 1, f); + if (version_error(alg, RSA_ALGORITHM_BYTE)) + { fclose (f); + return (-1); + } - if (!filter_mode) - { fprintf(pgpout,PSTR("Just a moment...")); /* RSA will take a while. */ - fflush(pgpout); - } + if (!gotkey) /* Only do this if we havent already */ + /* Get and validate secret key from a key file: */ + if (getsecretkey(GPK_GIVEUP|(quietmode?0:GPK_SHOW), NULL, keyID, + timestamp, NULL, NULL,userid, + n, e, d, p, q, u) == 0) + { + thiskey = gotkey = 1; + realprecision = global_precision; + } else { + set_precision(MAX_UNIT_PRECISION); + } + /* DAMN this... This is REALLY frustrating, that I have to + * do this... Basically, if I go to getsecretkey, it will + * set the precision, and the precision might NOT be correct + * if the key I get is not correct, so I have to set the + * precision NUMEROUS times in this loop.. This sucks, + * but its the only way. Sigh. + * + * -derek 13 Dec 1992 + */ + + /* Note that RSA key must be at least big enough + to encipher a complete conventional key packet in + a single RSA block. */ + + /*========================================================*/ + /* read ciphertext block, converting to internal format: */ + read_mpi((unitptr)inbuf, f, FALSE, FALSE); + + if (thiskey) { + if (!quietmode && thiskey) { + fprintf(pgpout,PSTR("Just a moment...")); + /* RSA will take a while. */ + fflush(pgpout); + } - rsa_decrypt((unitptr)outbuf, (unitptr)inbuf, d, p, q, u); + rsa_decrypt((unitptr)outbuf, (unitptr)inbuf, d, + p, q, u); - if (!filter_mode) - fputc('.',pgpout); /* Signal RSA completion. */ + if (!quietmode) + fputc('.',pgpout); + /* Signal RSA completion. */ + } + + fpos = ftell(f); /* Save this position */ + + } while (!end_of_pkes); /* Loop until end of PKE packets */ + + /* Should we list the recipients? */ + if (!gotkey || verbose) { + char *user; + + setkrent(NULL); + init_userhash(); + if (gotkey) /* verbose flag */ + fprintf(pgpout,"\nRecipients:\n"); + else + fprintf(pgpout,PSTR("\nThis message can only be read by:\n")); + + for (nkey = nkeys; nkey; nkey = nkey->next) { + if ((user = user_from_keyID(nkey->keyID)) == NULL) + fprintf(pgpout, " keyID: %s\n", keyIDstring(nkey->keyID)); + else + fprintf(pgpout, " %s\n", LOCAL_CHARSET(user)); + } + endkrent(); + } + for (nkey = nkeys; nkey; ) + { nkey = nkey->next; + free(nkeys); + nkeys = nkey; + } + + /* Ok, Now lets clean up, and continue on to the rest of the file so + * that it can be decrypted properly. Things should be ok once I + * reset some stuff here... -derek + */ + if (gotkey) + { + fseek(f, fpos, SEEK_SET); /* Get back to the Real McCoy! */ + set_precision(realprecision); /* reset the precision */ + } + else + { /* No secret key, exit gracefully (NOT!) */ + fprintf(pgpout, PSTR("\n\007You do not have the secret key needed to decrypt this file.\n")); + fclose(f); + return(-1); + } if ((count = postunblock(outbuf, (unitptr)outbuf, n)) < 0) { fprintf(pgpout,PSTR("\n\007Error: RSA-decrypted block is corrupted.\n\ @@ -2624,24 +2920,17 @@ This may be caused either by corrupted d /* Verify that top of buffer has correct algorithm byte */ --count; /* one less byte to drop algorithm byte */ -#ifdef XLOWFIRST /* Assumes LSB-first order */ - if (version_error(outbuf[count], IDEA_ALGORITHM_BYTE)) -#else /* Assumes MSB-first order */ +/* Assume MSB external byte ordering */ if (version_error(outbuf[0], IDEA_ALGORITHM_BYTE)) -#endif { fclose(f); return(-1); } /* Verify checksum */ count -= 2; /* back up before checksum */ -#ifdef XLOWFIRST /* Assumes LSB-first order */ - chksum = fetch_word16(outbuf+count); - if (chksum != checksum(outbuf, count)) -#else /* Assumes MSB-first order */ +/* Assume MSB external byte ordering */ chksum = fetch_word16(outbuf+1+count); if (chksum != checksum(outbuf+1, count)) -#endif { fprintf(pgpout,PSTR("\n\007Error: RSA-decrypted block is corrupted.\n\ This may be caused either by corrupted data or by using the wrong RSA key.\n")); fclose(f); @@ -2668,15 +2957,12 @@ This may be caused either by corrupted d flen = getpastlength(ctbCKE, f); /* read packet length */ /* Decrypt ciphertext file */ -#ifdef XLOWFIRST /* Assumes LSB-first order */ - status = idea_file( outbuf, DECRYPT_IT, f, g, flen ); -#else /* Assumes MSB-first order */ +/* Assume MSB external byte ordering */ status = idea_file( outbuf+1, DECRYPT_IT, f, g, flen ); -#endif if (status < 0) { fprintf(pgpout,PSTR("\n\007Error: Decrypted plaintext is corrupted.\n")); } - if (!filter_mode) + if (!quietmode) fputc('.',pgpout); /* show progress */ if (write_error(g)) @@ -2716,8 +3002,7 @@ int idea_decryptfile(char *infile, char FILE *g; byte ideakey[16]; byte passphrase[256]; - extern char password[]; - int status; + int status, retries = 0; word32 flen; if (verbose) @@ -2730,45 +3015,57 @@ int idea_decryptfile(char *infile, char return(-1); } - fread(&ctb,1,1,f); /* read Cipher Type Byte, should be CTB_CKE */ - - if (!is_ctb(ctb) || !is_ctb_type(ctb,CTB_CKE_TYPE)) - { /* Should never get here. */ - fprintf(pgpout,"\007\nBad or missing CTB_CKE byte.\n"); - goto err1; /* Abandon ship! */ - } - - flen = getpastlength(ctb, f); /* read packet length */ - /* open file g for write, in binary (not text) mode... */ if ((g = fopen( outfile, FOPWBIN )) == NULL) { fprintf(pgpout, PSTR("\n\007Can't create plaintext file '%s'\n"), outfile ); goto err1; } - /* Get IDEA password, hashed */ - if (*password != '\0') - hashpass(password, strlen(password), ideakey); - else { - fprintf(pgpout,PSTR("\nYou need a pass phrase to decrypt this file. ")); - if (GetHashedPassPhrase((char *)passphrase,(char *)ideakey,NOECHO1) <= 0) - { fclose(f); + if (*password == '\0') + ++retries; /* no PGPPASS or -z */ + do /* while pass phrase is bad */ + { + fread(&ctb,1,1,f); /* read Cipher Type Byte, should be CTB_CKE */ + + if (!is_ctb(ctb) || !is_ctb_type(ctb,CTB_CKE_TYPE)) + { /* Should never get here. */ + fprintf(pgpout,"\007\nBad or missing CTB_CKE byte.\n"); fclose(g); - return(-1); + goto err1; /* Abandon ship! */ } - } + flen = getpastlength(ctb, f); /* read packet length */ - if (!filter_mode) - { fprintf(pgpout,PSTR("Just a moment...")); /* this may take a while */ - fflush(pgpout); - } + /* Get IDEA password, hashed */ + if (retries == 0) + /* first try environment password */ + hashpass(password, strlen(password), ideakey); + else { + fprintf(pgpout,PSTR("\nYou need a pass phrase to decrypt this file. ")); + if (batchmode || GetHashedPassPhrase((char *)passphrase,(char *)ideakey,NOECHO1) <= 0) + { fclose(f); + fclose(g); + return(-1); + } + } - status = idea_file( ideakey, DECRYPT_IT, f, g, flen ); + if (!quietmode) + { fprintf(pgpout,PSTR("Just a moment...")); /* this may take a while */ + fflush(pgpout); + } + + status = idea_file( ideakey, DECRYPT_IT, f, g, flen ); + if (status == 0) + break; + if (retries) /* first try is PGPPASS */ + fprintf(pgpout,PSTR("\n\007Error: Bad pass phrase.\n")); + rewind(f); + rewind(g); + } while (status == -2 && ++retries < 3); burn(ideakey); /* burn sensitive data on stack */ burn(passphrase); - if (!filter_mode) + if (status == 0 && !quietmode) fputc('.',pgpout); /* show progress */ if (write_error(g)) @@ -2779,11 +3076,10 @@ int idea_decryptfile(char *infile, char fclose(f); if (status < 0) /* if idea_file failed, then complain */ - { fprintf(pgpout,PSTR("\n\007Error: Bad pass phrase.\n")); - remove(outfile); /* throw away our mistake */ + { remove(outfile); /* throw away our mistake */ return(status); /* error return */ } - if (!filter_mode) + if (!quietmode) fprintf(pgpout,PSTR("Pass phrase appears good. ")); return(1); /* always indicate output file has nested stuff in it. */ @@ -2839,7 +3135,7 @@ This may require a newer version of PGP. unzip( f, g ); if (verbose) fprintf(pgpout, PSTR("done. ") ); - else if (!filter_mode) + else if (!quietmode) fputc('.',pgpout); /* show progress */ if (write_error(g))