--- pgp/src/crypto.c 2018/04/24 16:37:53 1.1 +++ pgp/src/crypto.c 2018/04/24 16:39:34 1.1.1.3 @@ -1,2601 +1,3153 @@ -/* crypto.c - Cryptographic routines for PGP. - PGP: Pretty Good(tm) Privacy - public key cryptography for the masses. - - (c) Copyright 1990-1992 by Philip Zimmermann. All rights reserved. - The author assumes no liability for damages resulting from the use - of this software, even if the damage results from defects in this - software. No warranty is expressed or implied. - - All the source code Philip Zimmermann wrote for PGP is available for - free under the "Copyleft" General Public License from the Free - Software Foundation. A copy of that license agreement is included in - the source release package of PGP. Code developed by others for PGP - is also freely available. Other code that has been incorporated into - PGP from other sources was either originally published in the public - domain or was used with permission from the various authors. See the - PGP User's Guide for more complete information about licensing, - patent restrictions on certain algorithms, trademarks, copyrights, - and export controls. -*/ - -#include -#include -#include -#include - -#include "mpilib.h" -#include "mpiio.h" -#include "random.h" -#include "idea.h" -#include "crypto.h" -#include "keymgmt.h" -#include "mdfile.h" -#include "fileio.h" -#include "language.h" -#include "pgp.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 */ - -int kbhit( void ); -int zipup(FILE *, FILE *); -#ifdef M_XENIX -long time(); -#endif - -/*--------------------------------------------------------------------------*/ - - -void CToPascal(char *s) -{ /* "xyz\0" --> "\3xyz" ... converts C string to Pascal string */ - int i,j; - j = string_length(s); - for (i=j; i!=0; i--) - s[i] = s[i-1]; /* move everything 1 byte to the right */ - s[0] = j; /* Pascal length byte at beginning */ -} /* CToPascal */ - - -void PascalToC( char *s ) -{ /* "\3xyz" --> "xyz\0" ... converts Pascal string to C string */ - int i,j; - for (i=0,j=((byte *) s)[0]; i= 0) - { f = fopen(keyfile, "rb"); - fseek (f, fpusr+usrpktlen, SEEK_SET); - /* Read trust byte */ - trust_status = read_trust(f, &keyctrl); - fseek(f, fp, SEEK_SET); - if (is_compromised(f)) - { - CToPascal(userid); - fprintf(pgpout, "\n"); - show_key(f, fp, 0); - fclose (f); - fprintf(pgpout, PSTR("\007\nWARNING: This key has been revoked by its owner,\n\ -possibly because the secret key was compromised.\n")); - if (warn_only) - { /* this is only for checking signatures */ - fprintf(pgpout, PSTR("This could mean that this signature is a forgery.\n")); - return(1); - } - else - { /* don't use it for encryption */ - fprintf(pgpout, PSTR("You cannot use this revoked key.\n")); - return(-1); - } - } - fclose (f); - } - CToPascal(userid); - if ((keyctrl & KC_LEGIT_MASK) != KC_LEGIT_COMPLETE) - { byte userid0[256]; - 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"), EXTERNAL((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"), EXTERNAL((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"), EXTERNAL((char *)userid0)); - if (!filter_mode && !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); - if (trust_status == 0 && (f = fopen(keyfile, "r+b")) != NULL) - { fseek (f, fpusr+usrpktlen, SEEK_SET); - keyctrl |= KC_WARNONLY; - write_trust(f, keyctrl); - fclose(f); - } - } - } - return(0); -} /* warn_signatures */ - - -boolean legal_ctb(byte ctb) -{ /* Used to determine if nesting should be allowed. */ - boolean legal; - byte ctbtype; - if (!is_ctb(ctb)) /* not even a bonafide CTB */ - return(FALSE); - /* Sure hope CTB internal bit definitions don't change... */ - ctbtype = (ctb & CTB_TYPE_MASK) >> 2; - /* Only allow these CTB types to be nested... */ - legal = ( - (ctbtype==CTB_PKE_TYPE) - || (ctbtype==CTB_SKE_TYPE) - || (ctbtype==CTB_CERT_SECKEY_TYPE) - || (ctbtype==CTB_CERT_PUBKEY_TYPE) - || (ctbtype==CTB_LITERAL_TYPE) - || (ctbtype==CTB_COMPRESSED_TYPE) - || (ctbtype==CTB_CKE_TYPE) - ); - return(legal); -} /* legal_ctb */ - - -/* Return nonzero if val doesn't match checkval, after printing a - * warning. - */ -int -version_error(int val, int checkval) -{ if (val != checkval) - { fprintf (pgpout, PSTR( -"\n\007Unsupported packet format - you need a newer version of PGP for this file.\n")); - return(1); - } - return(0); -} - -/*-------------------------------------------------------------------------*/ - - -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. -*/ -{ 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)) /* No seed file. Start one... */ - { f = fopen(seedfile,"wb"); /* 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; i 0xFFFFL)) - { llength = 4; - llenb = 2; - } - else if ((word16)length > 0xFF) - { llength = 2; - llenb = 1; - } - else - { llength = 1; - llenb = 0; - } - ctb = CTB_BYTE(ctb_type, llenb); - fwrite( &ctb, 1, 1, f ); - /* convert length to external byteorder... */ - if (llength==1) - buf[0] = length; - if (llength==2) - put_word16((word16) length, buf); - if (llength==4) - put_word32(length, buf); - fwrite( buf, 1, llength, f ); -} /* write_ctb_len */ - - -int idea_file(byte *ideakey, boolean decryp, FILE *f, FILE *g, word32 lenfile) -/* Use IDEA in cipher feedback (CFB) mode to encrypt or decrypt a file. - The encrypted material starts out with a 64-bit random prefix, which - serves as an encrypted random CFB initialization vector, and - following that is 16 bits of "key check" material, which is a - duplicate of the last 2 bytes of the random prefix. Encrypted key - check bytes detect if correct IDEA key was used to decrypt ciphertext. -*/ -{ int count; - word16 iv[4]; - extern byte textbuf[DISKBUFSIZE]; -#define RAND_PREFIX_LENGTH 8 - - /* init CFB key */ - fill0(iv,sizeof(iv)); /* define initialization vector IV as 0 */ - initcfb_idea(iv,ideakey,decryp); - - if (!decryp) /* encrypt-- insert key check bytes */ - { /* There is a random prefix followed by 2 key check bytes */ - int i; - - for (i=0; i0) - { ideacfb(textbuf,count); - fwrite(textbuf,1,count,g); - } - /* if text block was short, exit loop */ - } while (count==DISKBUFSIZE); - - close_idea(); /* Clean up data structures */ - burn(iv); /* burn sensitive data on stack */ - burn(textbuf); /* burn sensitive data on stack */ - return(0); /* should always take normal return */ -} /* idea_file */ - - -/* Checksum maintained as a running sum by read_mpi and write_mpi. - * The checksum is maintained based on the plaintext values being - * read and written. To use it, store a 0 to it before doing a set - * of read_mpi's or write_mpi's. Then read it aftwerwards. - */ -word16 mpi_checksum; - -int read_mpi(unitptr r, FILE *f, boolean adjust_precision, boolean scrambled) -/* Read a mutiprecision integer from a file. - adjust_precision is TRUE iff we should call set_precision to the - size of the number read in. - scrambled is TRUE iff field is encrypted (protects secret key fields). - Returns the bitcount of the number read in, or returns a negative - number if an error is detected. -*/ -{ byte buf[MAX_BYTE_PRECISION+2]; - unsigned int count; - word16 bytecount,bitcount; - - mp_init(r,0); - - if ((count = fread(buf,1,2,f)) < 2) - return (-1); /* error -- read failure or premature eof */ - - bitcount = fetch_word16(buf); - if (bits2units(bitcount) > global_precision) - return(-1); /* error -- possible corrupted bitcount */ - - bytecount = bits2bytes(bitcount); - - count = fread(buf+2,1,bytecount,f); - if (count < bytecount) - return(-1); /* error -- premature eof */ - - if (scrambled) /* decrypt the field */ - ideacfb(buf+2,bytecount); - - /* Update running checksum, in case anyone cares... */ - mpi_checksum += checksum (buf, bytecount+2); - - /* We assume that the bitcount prefix we read is an exact - bitcount, not rounded up to the next byte boundary. - Otherwise we would have to call mpi2reg, then call - countbits, then call set_precision, then recall mpi2reg - again. - */ - if (adjust_precision && bytecount) - { /* set the precision to that specified by the number read. */ - set_precision(bits2units(bitcount+SLOP_BITS)); - /* Now that precision is optimally set, call mpi2reg */ - } - - mpi2reg(r,buf); /* convert to internal format */ - burn(buf); /* burn sensitive data on stack */ - return (bitcount); -} /* read_mpi */ - - - -void write_mpi(unitptr n, FILE *f, boolean scrambled) -/* Write a multiprecision integer to a file. - scrambled is TRUE iff we should scramble field on the way out, - which is used to protect secret key fields. -*/ -{ byte buf[MAX_BYTE_PRECISION+2]; - short bytecount; - bytecount = reg2mpi(buf,n); - mpi_checksum += checksum (buf, bytecount+2); - if (scrambled) /* encrypt the field, skipping over the bitcount */ - ideacfb(buf+2,bytecount); - fwrite(buf,1,bytecount+2,f); - burn(buf); /* burn sensitive data on stack */ -} /* write_mpi */ - - -/*======================================================================*/ - - -int get_header_info_from_file(char *infile, byte *header, int count) -/* Reads the first count bytes from infile into header. */ -{ FILE *f; - fill0(header,count); - /* open file f for read, in binary (not text) mode...*/ - if ((f = fopen(infile,"rb")) == NULL) - return(-1); - /* read Cipher Type Byte, and maybe more */ - count = fread(header,1,count,f); - fclose(f); - return(count); /* normal return */ -} /* get_header_info_from_file */ - - -/* System clock must be broken if it isn't past this date: */ -#define REASONABLE_DATE ((unsigned long) 0x27804180L) /* 91 Jan 01 00:00:00 */ - - -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. - Returns total certificate length in bytes, or returns negative - error status. -*/ -{ - byte inbuf[MAX_BYTE_PRECISION], outbuf[MAX_BYTE_PRECISION]; - byte mdpacket[17]; - byte *mdbufptr; - int i,j,certificate_length,blocksize,bytecount; - word16 ske_length; - word32 tstamp; byte *timestamp = (byte *) &tstamp; - byte keyID[KEYFRAGSIZE]; - byte val; - int mdlen = 5; /* length of class plus timestamp, for adding to MD */ - - /* Note that RSA key must be at least big enough to encipher a - complete message digest 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); - } - - get_timestamp(timestamp); /* Timestamp when signature was made */ - if (tstamp < REASONABLE_DATE) /* complain about bad time/date setting */ - { fprintf(pgpout,PSTR("\n\007Error: System clock/calendar is set wrong.\n")); - return(-1); - } - convert_byteorder(timestamp,4); /* convert to external form */ - - /* Finish off message digest calculation with this information */ - 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; /* point at actual message digest */ - for (i=1; i0) - { for (j=0; j0) - { val = 0; /* Validation period */ - put_word16(val, certificate+certificate_length); - certificate_length+=2; /* advance past word */ - mdlen-=2; - } - /* hopefully, mdlen is now zero. */ - - /* ...end of fields that are included in MD calculation */ - - /* Now append keyID... */ - extract_keyID(keyID, n); /* gets keyID */ - for (i=0; idigest; - certificate[certificate_length++] = *mdbufptr++; - certificate[certificate_length++] = *mdbufptr++; - - /* Now append the RSA-signed message digest packet: */ - for (i=0; i MAX_SIGCERT_LENGTH-3) /* Huge packet length */ - goto badcert; /* complain and return bad status */ - - /* read whole certificate: */ - if (fread((byteptr) certificate, 1, cert_length, f) < cert_length) - /* bad packet length field */ - goto badcert; /* complain and return bad status */ - - version = *certificate++; - if (version_error(version, VERSION_BYTE)) - goto err1; - - mdlensave = mdlen = *certificate++; /* length of material to be added to MD */ - mdextras = certificate; /* pointer to extra material for MD calculation */ - - class = *certificate++; - if (class != SM_SIGNATURE_BYTE && class != SB_SIGNATURE_BYTE) - { (void) version_error(class, SM_SIGNATURE_BYTE); - goto err1; - } - mdlen--; - - if (mdlen>0) /* if more MD material is included... */ - { for (i=0; i0) /* if more MD material is included... */ - { certificate+=2; /* skip past unused validity period field */ - mdlen-=2; - } - - for (i=0; i MAX_PATH) - litfile[0] = 0; /* If too long for us, ignore it */ - if (litfile[0] > 0) - fread (litfile+1,1,litfile[0],f); - /* Use litfile if it's writeable and he didn't say an outfile */ - if (litfile[0] > 0) - { PascalToC( (char *)litfile ); - if (verbose) - fprintf(pgpout, PSTR("Original plaintext file name was: '%s'\n"), litfile); - if (!strcmp((char *) litfile, CONSOLE_FILENAME) || !explicit_outfile_flag) - { file_from_canon( (char *)litfile ); - if (file_ok_write( (char *)litfile )) - strcpy ( outfile, (char *)litfile ); - } - } - /* Discard file creation timestamp for now */ - fread (&dummystamp, 1, sizeof(dummystamp), f); - start_text = ftell(f); /* mark position of text for later */ - } /* packet is CTB_LITERAL_TYPE */ - } - - /* 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) - { /* 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); - - fputc('.',pgpout); /* Signal RSA completion. */ - - /* Unblock message digest, and convert to external byte order: */ - count = postunblock(outbuf, (unitptr)outbuf, n); - if (count < 0) - { 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 */ - } - - /* outbuf should contain message digest packet */ - /*==================================================================*/ - /* 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 - { 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 - { 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 */ - } - - /* Reposition file to where that plaintext begins... */ - fseek(f,start_text,SEEK_SET); /* reposition file from last ftell */ - - MDfile0_len(&MD,f,text_len);/* compute a message digest from rest of file */ - - MD_addbuffer (&MD, mdextras, mdlensave, TRUE); /* Finish message digest */ - - convert_byteorder(timestamp,4); /* convert timestamp from external form */ - 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 - { fprintf(pgpout,PSTR("\007\nWARNING: Bad signature, doesn't match file contents!\007\n")); - fprintf(pgpout,PSTR("\nBad signature from user \"%s\".\n"),EXTERNAL((char *)userid)); - fprintf(pgpout,PSTR("Signature made %s\n"),ctdate((word32 *)timestamp)); -#ifndef CANONICAL_TEXT /* native text format is not canonical text */ - /* NOTE: IF the signature is bad, AND this machine does not - use MSDOS-style canonical text as its native text format, - AND this is a detached signature certificate, AND this - file appears to contain ASCII text, THEN maybe we should - convert the file to canonical text form and try checking - the signature again. - This is because a detached signature certificate probably - means the file is not currently in a canonical text packet, - but it may have been in canonical text form when the - signature was created. - */ - if (class == SM_SIGNATURE_BYTE && separate_signature) - { if (is_text_file(outfile)) - fprintf(pgpout,PSTR("\n\007PGP may have problems checking signatures against text files\n\ -if the text was created on a different system with a different\n\ -text file format.\n")); - /* Unfortunately, we don't give the user a way to convert it. */ - } -#endif /* not CANONICAL_TEXT */ - if (moreflag) - { /* more will scroll the message off the screen */ - fprintf(pgpout, PSTR("\nPress ENTER to continue...")); - fflush(pgpout); - getyesno('n'); - } - goto outsig; /* Output data anyway */ - } - - fprintf(pgpout,PSTR("\nGood signature from user \"%s\".\n"),EXTERNAL((char *)userid)); - fprintf(pgpout,PSTR("Signature made %s\n"),ctdate((word32 *)timestamp)); - -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. ")); - else /* signature precedes plaintext in file... */ - { /* produce a plaintext output file from signature file */ - if (file_exists(outfile)) - { fprintf(pgpout,PSTR("\n\007Output file '%s' already exists. Overwrite (y/N)? "),outfile); - if (!getyesno('n')) /* user said don't do it. */ - goto err1; /* abort operation */ - } - /* open file g for write, in binary or text mode...*/ -#ifdef CANONICAL_TEXT - if ((g = fopen(outfile,"wb")) == NULL) -#else - if ((g = fopen(outfile,(lit_mode==MODE_BINARY)?"wb":"w")) == NULL) -#endif - { fprintf(pgpout,PSTR("\n\007Can't create plaintext file '%s'\n"),outfile); - goto err1; - } - CONVERSION = (lit_mode == MODE_TEXT) ? EXT_CONV : NO_CONV; -#ifdef CANONICAL_TEXT - copyfile(f,g,-1L); /* copy rest of file from file f to g */ -#else - if (lit_mode == MODE_BINARY) - copyfile( f, g, -1L ); - else - copyfile_from_canon( f, g, -1L ); -#endif - CONVERSION = NO_CONV; - - fclose(g); - - if (strip_signature) - { /* Copy signature to a .sig file */ - strcpy (sigfile, outfile); - force_extension(sigfile,SIG_EXTENSION); - if (file_exists(sigfile)) - { fprintf(pgpout,PSTR("\n\007Signature file '%s' already exists. Overwrite (y/N)? "),sigfile); - if (!getyesno('n')) - goto err1; - } - if ((g = fopen(sigfile,"wb")) == NULL) - { fprintf(pgpout,PSTR("\n\007Can't create signature file '%s'\n"),sigfile); - goto err1; - } - fseek (f,0L,SEEK_SET); - copyfile (f,g,(unsigned long)(cert_length+ctb_llength(ctb)+1)); - fclose(g); - fprintf(pgpout,PSTR("\nWriting signature certificate to '%s'\n"),sigfile); - } - } - - burn(inbuf); /* burn sensitive data on stack */ - burn(outbuf); /* burn sensitive data on stack */ - fclose(f); - if (separate_signature) - { *outfile = '\0'; - return(0); /* normal return, no nested info */ - } - if (is_ctb(ctb2) && (is_ctb_type(ctb2,CTB_LITERAL_TYPE))) - /* we already stripped away the CTB_LITERAL */ - return(0); /* normal return, no nested info */ - /* Otherwise, it's best to assume a nested CTB */ - return(1); /* nested information return */ - -badcert: /* Bad packet. Complain. */ - fprintf(pgpout,PSTR("\n\007Error: Badly-formed or corrupted signature certificate.\n")); - fprintf(pgpout,PSTR("File \"%s\" does not have a properly-formed signature.\n"),infile); - /* Now just drop through to error exit... */ - -err1: - burn(inbuf); /* burn sensitive data on stack */ - burn(outbuf); /* burn sensitive data on stack */ - fclose(f); - return(-1); /* error return */ - -} /* check_signaturefile */ - - -int check_key_sig(FILE *fkey, long fpkey, int keypktlen, char *keyuserid, - FILE *fsig, long fpsig, char *keyfile, char *siguserid, byte *xtimestamp, - byte *sigclass) -{ /* Check signature of key in file fkey at position fpkey, using signature - in file fsig and position fpsig. keyfile tells the file to use to - look for the public key in to check the sig. Return 0 if OK, -1 if - 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 */ - word16 cert_length; - int i, count; - byte certbuf[MAX_SIGCERT_LENGTH]; - byteptr certificate; /* for parsing certificate buffer */ - unit n[MAX_UNIT_PRECISION], e[MAX_UNIT_PRECISION]; - byte inbuf[MAX_BYTE_PRECISION]; - byte outbuf[MAX_BYTE_PRECISION]; - byte keyID[KEYFRAGSIZE]; - MD5_CTX MD; - byte mdlensave; - byte *mdextras; - word32 tstamp; - byte *timestamp = (byte *) &tstamp; /* key certificate timestamp */ - byte version; - byte mdlen; /* length of material to be added to MD calculation */ - byte class; - byte algorithm; - byte mdlow2[2]; - - fill0( keyID, KEYFRAGSIZE ); - - set_precision(MAX_UNIT_PRECISION); /* safest opening assumption */ - - /******************** Read header CTB and length field ******************/ - - fseek(fsig, fpsig, SEEK_SET); - fread(&ctb,1,1,fsig); /* read certificate CTB byte */ - certificate = certbuf; - *certificate++ = ctb; /* copy ctb into certificate */ - - if (!is_ctb(ctb) || !is_ctb_type(ctb,CTB_SKE_TYPE)) - goto badcert2; /* complain and return bad status */ - - cert_length = getpastlength(ctb, fsig); /* read certificate length */ - certificate += ctb_llength(ctb); /* either 1, 2, 4, or 8 */ - if (cert_length > MAX_SIGCERT_LENGTH-3) /* Huge packet length */ - goto badcert2; /* complain and return bad status */ - - /* read whole certificate: */ - if (fread((byteptr) certificate, 1, cert_length, fsig) < cert_length) - /* bad packet length field */ - goto badcert2; /* complain and return bad status */ - - version = *certificate++; - if (version_error(version, VERSION_BYTE)) - goto err2; - - mdlensave = mdlen = *certificate++; /* length of material to be added to MD */ - mdextras = certificate; /* pointer to extra material for MD calculation */ - - *sigclass = class = *certificate++; - if (class != K0_SIGNATURE_BYTE && class != K1_SIGNATURE_BYTE && - class != K2_SIGNATURE_BYTE && class != K3_SIGNATURE_BYTE && - class != KC_SIGNATURE_BYTE) - { (void) version_error(class, K0_SIGNATURE_BYTE); - goto err2; - } - mdlen--; - - if (mdlen>0) /* if more MD material is included... */ - { for (i=0; i0) /* if more MD material is included... */ - { certificate+=2; /* skip past unused validity period word */ - mdlen-=2; - } - - if (mdlen>0) /* if more MD material is included... */ - certificate+=mdlen; /* skip over the rest */ - - for (i=0; i MAX_PATH) - litfile[0] = 0; /* If too long for us, ignore it */ - if (litfile[0] > 0) - fread (litfile+1,1,litfile[0],f); - /* Use litfile if it's writeable and he didn't say an outfile */ - if (litfile[0] > 0) - { PascalToC( (char *)litfile ); - if (verbose) - fprintf(pgpout, PSTR("Original plaintext file name was: '%s'\n"), litfile); - if (!strcmp((char *) litfile, CONSOLE_FILENAME) || !explicit_outfile_flag) - { file_from_canon( (char *)litfile ); - if (file_ok_write( (char *)litfile )) - strcpy ( outfile, (char *)litfile ); - } - } - /* Discard file creation timestamp for now */ - fread (&dummystamp, 1, sizeof(dummystamp), f); - - if (file_exists( outfile )) - { fprintf(pgpout, PSTR("\n\007Output file '%s' already exists. Overwrite (y/N)? "), outfile ); - if (! getyesno( 'n' )) - goto err1; /* user said don't do it - abort operation */ - } - - /* open file g for write, in binary or text mode... */ -#ifdef CANONICAL_TEXT - if ((g = fopen( outfile, "wb" )) == NULL) /* Always binary */ -#else - if ((g = fopen( outfile, (*lit_mode==MODE_BINARY)?"wb":"w" )) == NULL) -#endif - { fprintf(pgpout, PSTR("\n\007Can't create plaintext file '%s'\n"), outfile ); - goto err1; - } - - /* copy rest of literal plaintext file */ - CONVERSION = (*lit_mode == MODE_TEXT) ? EXT_CONV : NO_CONV; -#ifdef CANONICAL_TEXT - copyfile( f, g, LITlength ); /* Simple copy on canonical systems */ -#else - if (*lit_mode == MODE_BINARY) - copyfile( f, g, LITlength ); - else - copyfile_from_canon( f, g, LITlength ); -#endif - CONVERSION = NO_CONV; - - fclose(g); - fclose(f); - return(0); /* normal return */ - -err1: - fclose(f); - return(-1); /* error return */ - -} /* strip_literal */ - - -/*======================================================================*/ - - -int decryptfile(char *infile, char *outfile) -{ - byte ctb; /* Cipher Type Byte */ - byte ctbCKE; /* Cipher Type Byte */ - FILE *f; - FILE *g; - int count, status; - 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]; - byte outbuf[MAX_BYTE_PRECISION]; - byte keyID[KEYFRAGSIZE]; - word32 tstamp; byte *timestamp = (byte *) &tstamp; /* key certificate timestamp */ - byte userid[256]; - word32 flen; - byte ver, alg; - word16 chksum; - - set_precision(MAX_UNIT_PRECISION); /* safest opening assumption */ - - if (verbose) - fprintf(pgpout,PSTR("\nCiphertext file: %s, plaintext file: %s\n"), - infile,outfile); - - /* open file f for read, in binary (not text) mode...*/ -#ifdef VMS - if ((f = fopen(infile,"rb","ctx=stm")) == NULL) -#else - if ((f = fopen(infile,"rb")) == NULL) -#endif - { fprintf(pgpout,PSTR("\n\007Can't open ciphertext file '%s'\n"),infile); - 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); - } - - /* 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); - } - - 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); - } - - 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, TRUE, NULL, keyID, timestamp, NULL, NULL,userid, - n, e, d, p, q, u) < 0) - { 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. */ - - /*==================================================================*/ - /* read ciphertext block, converting to internal format: */ - read_mpi((unitptr)inbuf, f, FALSE, FALSE); - - fprintf(pgpout,PSTR("Just a moment...")); /* RSA will take a while. */ - fflush(pgpout); - - rsa_decrypt((unitptr)outbuf, (unitptr)inbuf, d, p, q, u); - - fputc('.',pgpout); /* Signal RSA completion. */ - - if ((count = postunblock(outbuf, (unitptr)outbuf, n)) < 0) - { 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); - return(-1); - } - - /* 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 */ - 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 */ - 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); - return(-1); - } - - /* outbuf should contain random IDEA key packet */ - /*==================================================================*/ - - if (file_exists( outfile )) - { fprintf(pgpout, PSTR("\n\007Output file '%s' already exists. Overwrite (y/N)? "), outfile ); - if (! getyesno( 'n' )) - goto err1; /* user said don't do it - abort operation */ - } - - /* open file g for write, in binary (not text) mode... */ - if ((g = fopen( outfile, "wb" )) == NULL) - { fprintf(pgpout, PSTR("\n\007Can't create plaintext file '%s'\n"), outfile ); - goto err1; - } - - fread(&ctbCKE,1,1,f); /* read Cipher Type Byte, should be CTB_CKE */ - if (!is_ctb(ctbCKE) || !is_ctb_type(ctbCKE,CTB_CKE_TYPE)) - { /* Should never get here. */ - fprintf(pgpout,"\007\nBad or missing CTB_CKE byte.\n"); - goto err1; /* Abandon ship! */ - } - - 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 */ - status = idea_file( outbuf+1, DECRYPT_IT, f, g, flen ); -#endif - if (status < 0) - { fprintf(pgpout,PSTR("\n\007Error: Decrypted plaintext is corrupted.\n")); - } - fputc('.',pgpout); /* show progress */ - - fclose(g); - fclose(f); - burn(inbuf); /* burn sensitive data on stack */ - burn(outbuf); /* burn sensitive data on stack */ - mp_burn(d); /* burn sensitive data on stack */ - mp_burn(p); /* burn sensitive data on stack */ - mp_burn(q); /* burn sensitive data on stack */ - mp_burn(u); /* burn sensitive data on stack */ - if (status < 0) /* if idea_file failed, then error return */ - return(status); - return(1); /* always indicate output file has nested stuff in it. */ - -err1: - fclose(f); - burn(inbuf); /* burn sensitive data on stack */ - burn(outbuf); /* burn sensitive data on stack */ - mp_burn(d); /* burn sensitive data on stack */ - mp_burn(p); /* burn sensitive data on stack */ - mp_burn(q); /* burn sensitive data on stack */ - mp_burn(u); /* burn sensitive data on stack */ - return(-1); /* error return */ - -} /* decryptfile */ - - - -int idea_decryptfile(char *infile, char *outfile) -{ - byte ctb; /* Cipher Type Byte */ - FILE *f; - FILE *g; - byte ideakey[16]; - byte passphrase[256]; - int status; - word32 flen; - - if (verbose) - fprintf(pgpout,PSTR("\nCiphertext file: %s, plaintext file: %s\n"), - infile,outfile); - - /* open file f for read, in binary (not text) mode...*/ -#ifdef VMS - if ((f = fopen(infile,"rb","ctx=stm")) == NULL) -#else - if ((f = fopen(infile,"rb")) == NULL) -#endif - { fprintf(pgpout,PSTR("\n\007Can't open ciphertext file '%s'\n"),infile); - 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 */ - - if (file_exists( outfile )) - { fprintf(pgpout, PSTR("\n\007Output file '%s' already exists. Overwrite (y/N)? "), outfile ); - if (! getyesno( 'n' )) - goto err1; /* user said don't do it - abort operation */ - } - - /* open file g for write, in binary (not text) mode... */ - if ((g = fopen( outfile, "wb" )) == NULL) - { fprintf(pgpout, PSTR("\n\007Can't create plaintext file '%s'\n"), outfile ); - goto err1; - } - - /* Get IDEA password, hashed */ - fprintf(pgpout,PSTR("\nYou need a pass phrase to decrypt this file. ")); - if (getideakey((char *)passphrase,(char *)ideakey,NOECHO1) <= 0) - { fclose(f); - fclose(g); - return(-1); - } - - fprintf(pgpout,PSTR("Just a moment...")); /* this may take a while */ - fflush(pgpout); - - status = idea_file( ideakey, DECRYPT_IT, f, g, flen ); - - burn(ideakey); /* burn sensitive data on stack */ - burn(passphrase); - - fputc('.',pgpout); /* show progress */ - - fclose(g); - 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 */ - return(status); /* error return */ - } - fprintf(pgpout,PSTR("Pass phrase appears good. ")); - return(1); /* always indicate output file has nested stuff in it. */ - -err1: - fclose(f); - return(-1); /* error return */ - -} /* idea_decryptfile */ - - - -int decompress_file(char *infile, char *outfile) -{ - byte ctb; - FILE *f; - FILE *g; - extern void lzhDecode( FILE *, FILE * ); - extern void unzip( FILE *, FILE * ); - if (verbose) fprintf(pgpout, PSTR("Decompressing plaintext...") ); - - /* open file f for read, in binary (not text) mode...*/ -#ifdef VMS - if ((f = fopen(infile,"rb","ctx=stm")) == NULL) -#else - if ((f = fopen(infile,"rb")) == NULL) -#endif - { fprintf(pgpout,PSTR("\n\007Can't open compressed file '%s'\n"),infile); - return(-1); - } - - fread(&ctb,1,1,f); /* read and skip over Cipher Type Byte */ - if (!is_ctb_type( ctb, CTB_COMPRESSED_TYPE )) - { /* Shouldn't get here, or why were we called to begin with? */ - fprintf(pgpout,"\007\nBad or missing CTB_COMPRESSED byte.\n"); - goto err1; /* Abandon ship! */ - } - - getpastlength(ctb, f); /* read packet length */ - /* The packet length is ignored. Assume it's huge. */ - - fread(&ctb,1,1,f); /* read and skip over compression algorithm byte */ - if (ctb != ZIP2_ALGORITHM_BYTE) - { /* We only know how to uncompress deflate-compressed data. We - may hit imploded or Lharc'ed data but treat it as an error just - the same */ - fprintf(pgpout,PSTR("\007\nUnrecognized compression algorithm.\n\ -This may require a newer version of PGP.\n")); - goto err1; /* Abandon ship! */ - } - - /* open file g for write, in binary (not text) mode... */ - if ((g = fopen( outfile, "wb" )) == NULL) - { fprintf(pgpout, PSTR("\n\007Can't create decompressed file '%s'\n"), outfile ); - goto err1; - } - - unzip( f, g ); - if (verbose) fprintf(pgpout, PSTR("done. ") ); - else fputc('.',pgpout); /* show progress */ - - fclose(g); - fclose(f); - return(1); /* always indicate output file has nested stuff in it. */ -err1: - fclose(f); - return(-1); /* error return */ - -} /* decompress_file */ - +/* crypto.c - Cryptographic routines for PGP. + PGP: Pretty Good(tm) Privacy - public key cryptography for the masses. + + (c) Copyright 1990-1992 by Philip Zimmermann. All rights reserved. + The author assumes no liability for damages resulting from the use + of this software, even if the damage results from defects in this + software. No warranty is expressed or implied. + + All the source code Philip Zimmermann wrote for PGP is available for + free under the "Copyleft" General Public License from the Free + Software Foundation. A copy of that license agreement is included in + the source release package of PGP. Code developed by others for PGP + is also freely available. Other code that has been incorporated into + PGP from other sources was either originally published in the public + domain or was used with permission from the various authors. See the + PGP User's Guide for more complete information about licensing, + patent restrictions on certain algorithms, trademarks, copyrights, + and export controls. + + Modified: 12-Nov-92 HAJK + Add FDL stuff for VAX/VMS local mode. + Reopen temporary files rather than create new version. + + Modified: 13-Dec-92 Derek Atkins +#include +#include +#include +#include + +#include "mpilib.h" +#include "mpiio.h" +#include "random.h" +#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 */ + +#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); + +#ifdef M_XENIX +long time(); +#endif + +/*--------------------------------------------------------------------------*/ + + +void CToPascal(char *s) +{ /* "xyz\0" --> "\3xyz" ... converts C string to Pascal string */ + int i,j; + j = string_length(s); + for (i=j; i!=0; i--) + s[i] = s[i-1]; /* move everything 1 byte to the right */ + s[0] = j; /* Pascal length byte at beginning */ +} /* CToPascal */ + + +void PascalToC( char *s ) +{ /* "\3xyz" --> "xyz\0" ... converts Pascal string to C string */ + int i,j; + for (i=0,j=((byte *) s)[0]; i= 0) + { f = fopen(keyfile, FOPRBIN); + fseek (f, fpusr+usrpktlen, SEEK_SET); + /* Read trust byte */ + trust_status = read_trust(f, &keyctrl); + fseek(f, fp, SEEK_SET); + if (is_compromised(f)) + { + CToPascal(userid); + fprintf(pgpout, "\n"); + show_key(f, fp, 0); + fclose (f); + fprintf(pgpout, PSTR("\007\nWARNING: This key has been revoked by its owner,\n\ +possibly because the secret key was compromised.\n")); + if (warn_only) + { /* this is only for checking signatures */ + fprintf(pgpout, PSTR("This could mean that this signature is a forgery.\n")); + return(1); + } + else + { /* don't use it for encryption */ + fprintf(pgpout, PSTR("You cannot use this revoked key.\n")); + return(-1); + } + } + fclose (f); + } + CToPascal(userid); + if ((keyctrl & KC_LEGIT_MASK) != KC_LEGIT_COMPLETE) + { byte userid0[256]; + 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 (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); + if (trust_status == 0 && (f = fopen(keyfile, FOPRWBIN)) != NULL) + { fseek (f, fpusr+usrpktlen, SEEK_SET); + keyctrl |= KC_WARNONLY; + write_trust(f, keyctrl); + fclose(f); + } + } + } + return(0); +} /* warn_signatures */ + + +boolean legal_ctb(byte ctb) +{ /* Used to determine if nesting should be allowed. */ + boolean legal; + byte ctbtype; + if (!is_ctb(ctb)) /* not even a bonafide CTB */ + return(FALSE); + /* Sure hope CTB internal bit definitions don't change... */ + ctbtype = (ctb & CTB_TYPE_MASK) >> 2; + /* Only allow these CTB types to be nested... */ + legal = ( + (ctbtype==CTB_PKE_TYPE) + || (ctbtype==CTB_SKE_TYPE) + || (ctbtype==CTB_CERT_SECKEY_TYPE) + || (ctbtype==CTB_CERT_PUBKEY_TYPE) + || (ctbtype==CTB_LITERAL_TYPE) + || (ctbtype==CTB_LITERAL2_TYPE) + || (ctbtype==CTB_COMPRESSED_TYPE) + || (ctbtype==CTB_CKE_TYPE) + ); + return(legal); +} /* legal_ctb */ + + +/* Return nonzero if val doesn't match checkval, after printing a + * warning. + */ +int +version_error(int val, int checkval) +{ if (val != checkval) + { fprintf (pgpout, PSTR( +"\n\007Unsupported packet format - you need a newer version of PGP for this file.\n")); + return(1); + } + return(0); +} + +/*-------------------------------------------------------------------------*/ + +#define RAND_PREFIX_LENGTH 8 /* Length of IV for IDEA encryption */ + +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 */ + + buildfilename(seedfile,RANDSEED_FILENAME); + if (file_exists(seedfile)) + return 1; /* True */ + randaccum_later(8*(IDEAKEYSIZE+RAND_PREFIX_LENGTH)); + return 0; +} + +void create_seedfile(void) +/* + Create the seedfile, from the random bits that seedfile_exists + buffered for accumulation. "randseed.bin" is used to generate + cryptographically strong pseudorandom numbers for session keys. + */ +{ char seedfile[MAX_PATH]; /* Random seed filename */ + FILE *f; + byte randbuf[IDEAKEYSIZE+RAND_PREFIX_LENGTH]; + int i; + + buildfilename(seedfile,RANDSEED_FILENAME); + + f = fopen(seedfile,FOPWBIN); /* open for writing binary */ + if (f==NULL) /* failed to create seedfile */ + return; /* error: no random number seed file available */ + fprintf(pgpout,PSTR("Initializing random seed file...")); +/* This is not part of the string above because it was changed after the 2.2 + release translations were started. It should be cleaned up eventually. */ + fputc('\n', pgpout); + for (i=1; i 0xFFFFL)) + { llength = 4; + llenb = 2; + } + else if ((word16)length > 0xFF) + { llength = 2; + llenb = 1; + } + else + { llength = 1; + llenb = 0; + } + ctb = CTB_BYTE(ctb_type, llenb); + fwrite( &ctb, 1, 1, f ); + /* convert length to external byteorder... */ + if (llength==1) + buf[0] = length; + if (llength==2) + put_word16((word16) length, buf); + if (llength==4) + put_word32(length, buf); + fwrite( buf, 1, llength, f ); +} /* write_ctb_len */ + + +static +int idea_file(byte *ideakey, boolean decryp, FILE *f, FILE *g, word32 lenfile) +/* Use IDEA in cipher feedback (CFB) mode to encrypt or decrypt a file. + The encrypted material starts out with a 64-bit random prefix, which + serves as an encrypted random CFB initialization vector, and + following that is 16 bits of "key check" material, which is a + duplicate of the last 2 bytes of the random prefix. Encrypted key + check bytes detect if correct IDEA key was used to decrypt ciphertext. +*/ +{ int count, status = 0; + word16 iv[4]; + extern byte textbuf[DISKBUFSIZE]; +#define RAND_PREFIX_LENGTH 8 + + /* init CFB key */ + fill0(iv,sizeof(iv)); /* define initialization vector IV as 0 */ + initcfb_idea(iv,ideakey,decryp); + + if (!decryp) /* encrypt-- insert key check bytes */ + { /* There is a random prefix followed by 2 key check bytes */ + + memcpy(textbuf, ideakey+IDEAKEYSIZE, RAND_PREFIX_LENGTH); + /* key check bytes are simply duplicates of final 2 random bytes */ + textbuf[RAND_PREFIX_LENGTH] = textbuf[RAND_PREFIX_LENGTH-2]; + textbuf[RAND_PREFIX_LENGTH+1] = textbuf[RAND_PREFIX_LENGTH-1]; + + ideacfb(textbuf,RAND_PREFIX_LENGTH+2); + fwrite(textbuf,1,RAND_PREFIX_LENGTH+2,g); + } + else /* decrypt-- check for key check bytes */ + { /* See if the redundancy is present after the random prefix */ + count = fread(textbuf,1,RAND_PREFIX_LENGTH+2,f); + lenfile -= count; + if (count==(RAND_PREFIX_LENGTH+2)) + { ideacfb(textbuf,RAND_PREFIX_LENGTH+2); + if ((textbuf[RAND_PREFIX_LENGTH] != textbuf[RAND_PREFIX_LENGTH-2]) + || (textbuf[RAND_PREFIX_LENGTH+1] != textbuf[RAND_PREFIX_LENGTH-1])) + { status = -2; /* bad key error */ + } + } + else /* file too short for key check bytes */ + status = -3; /* error of the weird kind */ + } + + + /* 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; + } + 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 */ + burn(textbuf); /* burn sensitive data on stack */ + return(status); /* should always take normal return */ +} /* idea_file */ + + +/* Checksum maintained as a running sum by read_mpi and write_mpi. + * The checksum is maintained based on the plaintext values being + * read and written. To use it, store a 0 to it before doing a set + * of read_mpi's or write_mpi's. Then read it aftwerwards. + */ +word16 mpi_checksum; + +int read_mpi(unitptr r, FILE *f, boolean adjust_precision, boolean scrambled) +/* Read a mutiprecision integer from a file. + adjust_precision is TRUE iff we should call set_precision to the + size of the number read in. + scrambled is TRUE iff field is encrypted (protects secret key fields). + Returns the bitcount of the number read in, or returns a negative + number if an error is detected. +*/ +{ byte buf[MAX_BYTE_PRECISION+2]; + unsigned int count; + word16 bytecount,bitcount; + + mp_init(r,0); + + if ((count = fread(buf,1,2,f)) < 2) + return (-1); /* error -- read failure or premature eof */ + + bitcount = fetch_word16(buf); + if (bits2units(bitcount) > global_precision) + return(-1); /* error -- possible corrupted bitcount */ + + bytecount = bits2bytes(bitcount); + + count = fread(buf+2,1,bytecount,f); + if (count < bytecount) + return(-1); /* error -- premature eof */ + + if (scrambled) /* decrypt the field */ + ideacfb(buf+2,bytecount); + + /* Update running checksum, in case anyone cares... */ + mpi_checksum += checksum (buf, bytecount+2); + + /* We assume that the bitcount prefix we read is an exact + bitcount, not rounded up to the next byte boundary. + Otherwise we would have to call mpi2reg, then call + countbits, then call set_precision, then recall mpi2reg + again. + */ + if (adjust_precision && bytecount) + { /* set the precision to that specified by the number read. */ + if (bitcount > MAX_BIT_PRECISION-SLOP_BITS) + return(-1); + set_precision(bits2units(bitcount+SLOP_BITS)); + /* Now that precision is optimally set, call mpi2reg */ + } + + if (mpi2reg(r,buf) == -1) /* convert to internal format */ + return(-1); + burn(buf); /* burn sensitive data on stack */ + return (bitcount); +} /* read_mpi */ + + + +void write_mpi(unitptr n, FILE *f, boolean scrambled) +/* Write a multiprecision integer to a file. + scrambled is TRUE iff we should scramble field on the way out, + which is used to protect secret key fields. +*/ +{ byte buf[MAX_BYTE_PRECISION+2]; + short bytecount; + bytecount = reg2mpi(buf,n); + mpi_checksum += checksum (buf, bytecount+2); + if (scrambled) /* encrypt the field, skipping over the bitcount */ + ideacfb(buf+2,bytecount); + fwrite(buf,1,bytecount+2,f); + burn(buf); /* burn sensitive data on stack */ +} /* write_mpi */ + + +/*======================================================================*/ + + +int get_header_info_from_file(char *infile, byte *header, int count) +/* Reads the first count bytes from infile into header. */ +{ FILE *f; + fill0(header,count); + /* open file f for read, in binary (not text) mode...*/ + if ((f = fopen(infile,FOPRBIN)) == NULL) + return(-1); + /* read Cipher Type Byte, and maybe more */ + count = fread(header,1,count,f); + fclose(f); + return(count); /* normal return */ +} /* get_header_info_from_file */ + + +/* System clock must be broken if it isn't past this date: */ +#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. + Returns total certificate length in bytes, or returns negative + error status. +*/ +{ + byte inbuf[MAX_BYTE_PRECISION], outbuf[MAX_BYTE_PRECISION]; + byte mdpacket[17]; + byte *mdbufptr; + int i,j,certificate_length,blocksize,bytecount; + word16 ske_length; + word32 tstamp; byte *timestamp = (byte *) &tstamp; + byte keyID[KEYFRAGSIZE]; + byte val; + int mdlen = 5; /* length of class plus timestamp, for adding to MD */ + + /* Note that RSA key must be at least big enough to encipher a + complete message digest 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); + } + + get_timestamp(timestamp); /* Timestamp when signature was made */ + if (tstamp < REASONABLE_DATE) /* complain about bad time/date setting */ + { fprintf(pgpout,PSTR("\n\007Error: System clock/calendar is set wrong.\n")); + return(-1); + } + convert_byteorder(timestamp,4); /* convert to external form */ + + /* Finish off message digest calculation with this information */ + MD_addbuffer (MD, &class, 1, FALSE); + MD_addbuffer (MD, timestamp, 4, TRUE); + +#ifdef PKCS_COMPAT + /* Pre-block digest, and convert to INTERNAL byte order: */ + preblock((unitptr)inbuf, MD->digest, 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; i0) + { for (j=0; j0) + { val = 0; /* Validation period */ + put_word16(val, certificate+certificate_length); + certificate_length+=2; /* advance past word */ + mdlen-=2; + } + /* hopefully, mdlen is now zero. */ + + /* ...end of fields that are included in MD calculation */ + + /* Now append keyID... */ + extract_keyID(keyID, n); /* gets keyID */ + for (i=0; idigest; + certificate[certificate_length++] = *mdbufptr++; + certificate[certificate_length++] = *mdbufptr++; + + /* Now append the RSA-signed message digest packet: */ + for (i=0; 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. */ + } + + certificate_length = make_signature_certificate(certificate, &MD, + K0_SIGNATURE_BYTE, n, d, p, q, u); + if (certificate_length < 0) + return(-1); /* error return from make_signature_certificate() */ + + } /* end of scope for some buffers */ + + /* open file g for write, in binary (not text) mode...*/ + tempring = tempfile(TMP_TMPDIR); + if ((g = fopen(tempring,FOPWBIN)) == NULL) + { fprintf(pgpout,PSTR("\n\007Can't create output file to update key ring.\n")); + fclose(f); + return(-1); + } + + /* Copy pre-key and key to file g */ + copyfile (f, g, fpusr+usrpktlen+usrctrllen); + + /* write out certificate record to outfile ... */ + fwrite(certificate,1,certificate_length,g); + + /* Add "trusty" control packet */ + write_trust (g, KC_SIGTRUST_ULTIMATE|KC_CONTIG|KC_SIG_CHECKED); + + /* Copy the remainder from file f to file g */ + copyfile (f, g, -1L); + + fclose(f); + if (write_error(g)) + { fclose(g); + return(-1); + } + fclose(g); + + savetempbak(tempring,keyfile); + + fprintf(pgpout, PSTR("\nKey signature certificate added.\n")); + return(0); /* normal return */ + +} /* signkey */ + + +/*======================================================================*/ + +int check_signaturefile(char *infile, char *outfile, boolean strip_signature, + char *preserved_name) +{ /* Check signature in infile for validity. Strip off the signature + and write the remaining packet to outfile. If strip_signature, + also write the signature to outfile.sig. + the original filename is stored in preserved_name + */ + 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() */ +#ifndef CANONICAL_TEXT + char *tempFileName; /* Name for temporary uncanonicalized file */ + FILE *tempFile; +#endif /* !CANONICAL_TEXT */ + long fp; + FILE *f; + FILE *g; + long start_text; /* marks file position */ + int i,count; + word16 cert_length; + byte certbuf[MAX_SIGCERT_LENGTH]; + byteptr certificate; /* for parsing certificate buffer */ + unit n[MAX_UNIT_PRECISION], e[MAX_UNIT_PRECISION]; + byte inbuf[MAX_BYTE_PRECISION]; + byte outbuf[MAX_BYTE_PRECISION]; + byte keyID[KEYFRAGSIZE]; + word32 tstamp; + byte *timestamp = (byte *) &tstamp; /* key certificate timestamp */ + word32 dummystamp; + byte userid[256]; + MD5_CTX MD; + boolean separate_signature; + boolean fixedLiteral = FALSE; /* Whether it's a fixed literal2 packet */ + extern char **myArgv; + extern int myArgc; + char lit_mode = MODE_BINARY; + unsigned char litfile[MAX_PATH]; + word32 text_len = -1; + int status; + byte *mdextras; + byte mdlensave; + byte version; + byte mdlen; /* length of material to be added to MD calculation */ + byte class; + byte algorithm; + byte mdlow2[2]; + char org_sys[5]; /* Name of originating system */ +#ifdef VMS + char *fdl; + short fdl_len; +#endif + int outbufoffset; + + fill0( keyID, KEYFRAGSIZE ); + + set_precision(MAX_UNIT_PRECISION); /* safest opening assumption */ + + buildfilename(keyfile,PUBLIC_KEYRING_FILENAME); /* use default pathname */ + + if (verbose) + fprintf(pgpout,"check_signaturefile: infile = '%s', outfile = '%s'\n", + infile,outfile); + + if (preserved_name) + *preserved_name = '\0'; + + /* open file f for read, in binary (not text) mode...*/ + if ((f = fopen(infile,FOPRBIN)) == NULL) + { fprintf(pgpout,PSTR("\n\007Can't open ciphertext file '%s'\n"),infile); + return(-1); + } + + /******************** Read header CTB and length field ******************/ + + fread(&ctb,1,1,f); /* read certificate CTB byte */ + certificate = certbuf; + *certificate++ = ctb; /* copy ctb into certificate */ + + if (!is_ctb(ctb) || !is_ctb_type(ctb,CTB_SKE_TYPE)) + goto badcert; /* complain and return bad status */ + + cert_length = getpastlength(ctb, f); /* read certificate length */ + certificate += ctb_llength(ctb); /* either 1, 2, 4, or 8 */ + if (cert_length > MAX_SIGCERT_LENGTH-3) /* Huge packet length */ + goto badcert; /* complain and return bad status */ + + /* read whole certificate: */ + if (fread((byteptr) certificate, 1, cert_length, f) < cert_length) + /* bad packet length field */ + goto badcert; /* complain and return bad status */ + + version = *certificate++; + if (version_error(version, VERSION_BYTE)) + goto err1; + + mdlensave = mdlen = *certificate++; /* length of material to be added to MD */ + mdextras = certificate; /* pointer to extra material for MD calculation */ + + class = *certificate++; + if (class != SM_SIGNATURE_BYTE && class != SB_SIGNATURE_BYTE) + { (void) version_error(class, SM_SIGNATURE_BYTE); + goto err1; + } + mdlen--; + + if (mdlen>0) /* if more MD material is included... */ + { for (i=0; i0) /* if more MD material is included... */ + { certificate+=2; /* skip past unused validity period field */ + mdlen-=2; + } + + for (i=0; i 3 && file_exists(myArgv[3])) + { outfile = myArgv[3]; + fprintf(pgpout,PSTR("\nText is assumed to be in file '%s'.\n"),outfile); + } + else + { strcpy(plainfile, outfile); + outfile = plainfile; + drop_extension(outfile); + + if (file_exists(outfile)) + fprintf(pgpout,PSTR("\nText is assumed to be in file '%s'.\n"),outfile); + else + { + 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); + } + } + /* open file f for read, in binary (not text) mode...*/ + if ((f = fopen(outfile,FOPRBIN)) == NULL) + { fprintf(pgpout,PSTR("\n\007Can't open file '%s'\n"),outfile); + return(-1); + } + start_text = ftell(f); /* mark position of text for later */ + text_len = fsize(f); /* remember length of text */ + } /* had to open new input file */ + else + { separate_signature = FALSE; + /* We just read 1 byte, so outbuf[0] should contain a ctb, + maybe a CTB_LITERAL byte. */ + ctb2 = outbuf[0]; + fixedLiteral = is_ctb_type(ctb2,CTB_LITERAL2_TYPE); + if (is_ctb(ctb2) && (is_ctb_type(ctb2,CTB_LITERAL_TYPE)||fixedLiteral)) + { /* Read literal data */ + text_len = getpastlength(ctb2, f); /* read packet length */ + lit_mode = '\0'; + fread (&lit_mode,1,1,f); /* get literal packet mode byte */ + if (lit_mode != MODE_TEXT && lit_mode != MODE_BINARY && + lit_mode != MODE_LOCAL) + { fprintf(pgpout,"\n\007Error: Illegal mode byte %02x in literal packet.\n", + lit_mode); /* English-only diagnostic for debugging */ + (void) version_error(lit_mode, MODE_BINARY); + goto err1; + } + if (verbose) + fprintf(pgpout, PSTR("File type: '%c'\n"), lit_mode); + /* Read literal file name, use it if possible */ + litfile[0] = 0; + fread (litfile,1,1,f); + if( fixedLiteral ) + /* Get corrected text_len value by subtracting the length of + the filename and the timestamp and mode byte and litfile length byte */ + text_len -= litfile[0] + sizeof(dummystamp) + 2; + 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); + } + /* Use litfile if it's writeable and he didn't say an outfile */ + if (litfile[0]) + { PascalToC( (char *)litfile ); + if (verbose) + fprintf(pgpout, PSTR("Original plaintext file name was: '%s'\n"), litfile); + if (preserved_name) + strcpy(preserved_name, (char *) litfile); + } + if (lit_mode == MODE_LOCAL) { + fread(org_sys, 1, 4, f); org_sys[4] = '\0'; +#ifdef VMS +#define LOCAL_TEST !strncmp("VMS ",org_sys,4) +#else +#define LOCAL_TEST FALSE +#endif + if (LOCAL_TEST) { +#ifdef VMS + fread(&fdl_len, 2, 1, f); + fdl = (char *) malloc(fdl_len); + fread(fdl, 1, fdl_len, f); + if ((g = fdl_create( fdl, fdl_len, outfile, (char *) litfile)) == NULL) { + fprintf(pgpout,"\n\007Unable to create file %s\n", outfile); + return(-1); + } + free(fdl); + if (preserved_name) + strcpy(preserved_name, (char *) litfile); + text_len -= (fdl_len + sizeof(fdl_len)); +#endif /* VMS */ + } else { + fprintf(pgpout,"\n\007Unrecognised local binary type %s\n",org_sys); + return(-1); + } + } else { + /* Discard file creation timestamp for now */ + fread (&dummystamp, 1, sizeof(dummystamp), f); + } + start_text = ftell(f); /* mark position of text for later */ + } /* packet is CTB_LITERAL_TYPE */ + } + + /* Use keyID prefix to look up key... */ + + /* Get and validate public key from a key file: */ + 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 */ + + /* Recover message digest via public key */ + mp_modexp((unitptr)outbuf,(unitptr)inbuf,e,n); + + if (!quietmode) + fputc('.',pgpout); /* Signal RSA completion. */ + + /* Unblock message digest, and convert to external byte order: */ + count = postunblock(outbuf, (unitptr)outbuf, n); + + 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 */ + } + + /* outbuf should contain message digest packet */ + /*==================================================================*/ + /* Look at nested stuff within RSA block... */ + + 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 */ + } + + /* 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 */ + } + + /* Reposition file to where that plaintext begins... */ + fseek(f,start_text,SEEK_SET); /* reposition file from last ftell */ + + MDfile0_len(&MD,f,text_len);/* compute a message digest from rest of file */ + + MD_addbuffer (&MD, mdextras, mdlensave, TRUE); /* Finish message digest */ + + convert_byteorder(timestamp,4); /* convert timestamp from external form */ + PascalToC((char *)userid); /* for display */ + + /* now compare computed MD with claimed MD */ +/* 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 + canonical text as its native text format, AND this is a detached + signature certificate, AND this file appears to contain non- + canonical ASCII text, THEN we convert the file to canonical text + form and check the signature again. This is because a detached + signature certificate probably means the file is not currently in + a canonical text packet, but it was in canonical text form when + the signature was created, so by re-canonicalizing it we can + check the signature. */ + if (class == SM_SIGNATURE_BYTE && separate_signature && is_text_file(outfile)) + { /* Reposition file to where the plaintext begins and canonicalize it */ + rewind( f ); + tempFileName = tempfile( TMP_WIPE | TMP_TMPDIR ); + if (verbose) + fprintf(stderr, "signature checking failed, trying in canonical mode\n"); + 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 ); + + /* Move back to the start of the file and recalculate the MD */ + rewind( tempFile ); + MDfile0_len( &MD, tempFile, -1L ); + MD_addbuffer( &MD, mdextras, mdlensave, TRUE ); + + /* Clean up behind us */ + fclose( tempFile ); + rmtemp( tempFileName ); + + /* Check if the signature is OK this time round */ +/* Assume MSB external byte ordering */ + if(equal_buffers((byte *)(MD.digest),outbuf+outbufoffset,16)) + goto goodsig; + } + } +#endif /* !CANONICAL_TEXT */ + + fprintf(pgpout,PSTR("\007\nWARNING: Bad signature, doesn't match file contents!\007\n")); + 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 && !batchmode) + { /* more will scroll the message off the screen */ + fprintf(pgpout, PSTR("\nPress ENTER to continue...")); + fflush(pgpout); + getyesno('n'); + } + 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) + { + 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...*/ + if (lit_mode==MODE_LOCAL) { +#ifdef VMS + if (status = fdl_copyfile2bin( f, g, text_len)) { /* Copy ok? */ + if (status > 0) + fprintf(stderr,"\n...copying to literal file\n"); + else + perror("\nError copying from work file"); + fdl_close(g); + goto err1; + } + fdl_close(g); +#endif /*VMS */ + } + else + { + if (lit_mode == MODE_BINARY) + g = fopen(outfile, FOPWBIN); + else + g = fopen(outfile, FOPWTXT); + if (g == NULL) + { fprintf(pgpout,PSTR("\n\007Can't create plaintext file '%s'\n"),outfile); + goto err1; + } + CONVERSION = (lit_mode == MODE_TEXT) ? EXT_CONV : NO_CONV; + if (lit_mode == MODE_BINARY) + status = copyfile(f, g, text_len); + else + status = copyfile_from_canon(f, g, text_len); + CONVERSION = NO_CONV; + if (write_error(g) || status < 0) + { fclose(g); + goto err1; + } + fclose(g); + } + + if (strip_signature) + { /* Copy signature to a .sig file */ + strcpy (sigfile, outfile); + force_extension(sigfile,SIG_EXTENSION); + if (!force_flag && file_exists(sigfile)) + { fprintf(pgpout,PSTR("\n\007Signature file '%s' already exists. Overwrite (y/N)? "), + sigfile); + if (!getyesno('n')) + goto err1; + } + if ((g = fopen(sigfile,FOPWBIN)) == NULL) + { fprintf(pgpout,PSTR("\n\007Can't create signature file '%s'\n"),sigfile); + goto err1; + } + fseek (f,0L,SEEK_SET); + copyfile (f,g,(unsigned long)(cert_length+ctb_llength(ctb)+1)); + if (write_error(g)) + { fclose(g); + goto err1; + } + fclose(g); + if (!quietmode) + fprintf(pgpout,PSTR("\nWriting signature certificate to '%s'\n"),sigfile); + } + } + + burn(inbuf); /* burn sensitive data on stack */ + burn(outbuf); /* burn sensitive data on stack */ + fclose(f); + if (separate_signature) + return(0); /* normal return, no nested info */ + if (is_ctb(ctb2) && (is_ctb_type(ctb2,CTB_LITERAL_TYPE) || fixedLiteral)) + /* we already stripped away the CTB_LITERAL */ + return(0); /* normal return, no nested info */ + /* Otherwise, it's best to assume a nested CTB */ + return(1); /* nested information return */ + +badcert: /* Bad packet. Complain. */ + fprintf(pgpout,PSTR("\n\007Error: Badly-formed or corrupted signature certificate.\n")); + fprintf(pgpout,PSTR("File \"%s\" does not have a properly-formed signature.\n"),infile); + /* Now just drop through to error exit... */ + +err1: + burn(inbuf); /* burn sensitive data on stack */ + burn(outbuf); /* burn sensitive data on stack */ + fclose(f); + return(-1); /* error return */ + +} /* check_signaturefile */ + + +int check_key_sig(FILE *fkey, long fpkey, int keypktlen, char *keyuserid, + FILE *fsig, long fpsig, char *keyfile, char *siguserid, byte *xtimestamp, + byte *sigclass) +{ /* Check signature of key in file fkey at position fpkey, using signature + in file fsig and position fpsig. keyfile tells the file to use to + look for the public key in to check the sig. Return 0 if OK, -1 if + we can't check the signature, -2 if bad or other problem. + */ + byte ctb; /* Cipher Type Bytes */ + long fp; + word16 cert_length; + int i, count; + byte certbuf[MAX_SIGCERT_LENGTH]; + byteptr certificate; /* for parsing certificate buffer */ + unit n[MAX_UNIT_PRECISION], e[MAX_UNIT_PRECISION]; + byte inbuf[MAX_BYTE_PRECISION]; + byte outbuf[MAX_BYTE_PRECISION]; + byte keyID[KEYFRAGSIZE]; + MD5_CTX MD; + byte mdlensave; + byte *mdextras; + word32 tstamp; + byte *timestamp = (byte *) &tstamp; /* key certificate timestamp */ + byte version; + byte mdlen; /* length of material to be added to MD calculation */ + byte class; + byte algorithm; + byte mdlow2[2]; + int outbufoffset; + + fill0( keyID, KEYFRAGSIZE ); + + set_precision(MAX_UNIT_PRECISION); /* safest opening assumption */ + + /******************** Read header CTB and length field ******************/ + + fseek(fsig, fpsig, SEEK_SET); + fread(&ctb,1,1,fsig); /* read certificate CTB byte */ + certificate = certbuf; + *certificate++ = ctb; /* copy ctb into certificate */ + + if (!is_ctb(ctb) || !is_ctb_type(ctb,CTB_SKE_TYPE)) + goto badcert2; /* complain and return bad status */ + + cert_length = getpastlength(ctb, fsig); /* read certificate length */ + certificate += ctb_llength(ctb); /* either 1, 2, 4, or 8 */ + if (cert_length > MAX_SIGCERT_LENGTH-3) /* Huge packet length */ + goto badcert2; /* complain and return bad status */ + + /* read whole certificate: */ + if (fread((byteptr) certificate, 1, cert_length, fsig) < cert_length) + /* bad packet length field */ + goto badcert2; /* complain and return bad status */ + + version = *certificate++; + if (version_error(version, VERSION_BYTE)) + goto err2; + + mdlensave = mdlen = *certificate++; /* length of material to be added to MD */ + mdextras = certificate; /* pointer to extra material for MD calculation */ + + *sigclass = class = *certificate++; + if (class != K0_SIGNATURE_BYTE && class != K1_SIGNATURE_BYTE && + class != K2_SIGNATURE_BYTE && class != K3_SIGNATURE_BYTE && + class != KC_SIGNATURE_BYTE) + { (void) version_error(class, K0_SIGNATURE_BYTE); + goto err2; + } + mdlen--; + + if (mdlen>0) /* if more MD material is included... */ + { for (i=0; i0) /* if more MD material is included... */ + { certificate+=2; /* skip past unused validity period word */ + mdlen-=2; + } + + if (mdlen>0) /* if more MD material is included... */ + certificate+=mdlen; /* skip over the rest */ + + for (i=0; i 13 Dec 1992 + */ + + 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 */ + + if (!keys_used) + { fclose(f); + fclose(g); + free(keyID_list); + return -1; + } + + /* 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); + if (write_error(g)) + { fclose(g); + return -1; + } + fclose(g); + + return(0); +} /* 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 + lit_mode is MODE_TEXT. + */ + char lfile[MAX_PATH]; + FILE *f; + FILE *g; + int status = 0; +#ifdef VMS + char *fdl; + short fdl_len; +#endif /* VMS */ + + word32 flen, fpos; + word32 dummystamp = 0; + + if (verbose) + fprintf(pgpout,"make_literal: infile = %s, outfile = %s, mode = '%c', literalfile = '%s'\n", + infile,outfile,lit_mode,literalfile); + + /* open file f for read, in binary or text mode...*/ + +#ifdef VMS + if (lit_mode == MODE_LOCAL) { + if (!(fdl_generate(infile, &fdl, &fdl_len ) & 01)) { + fprintf(pgpout,PSTR("\n\007Can't open input plaintext file '%s'\n"),infile); + return(-1); + } + } +#endif /*VMS*/ + if (lit_mode == MODE_TEXT) + f = fopen(infile, FOPRTXT); + else + f = fopen(infile, FOPRBIN); + if (f == NULL) + { fprintf(pgpout,PSTR("\n\007Can't open input plaintext file '%s'\n"),infile); + return(-1); + } + flen = fsize(f); + + /* 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; + } + + if (literalfile == NULL) + { /* Put in a zero byte to indicate no filename */ + lfile[0] = '\0'; + } + else + { strcpy( lfile, literalfile ); + file_to_canon( lfile ); + CToPascal( lfile ); + } + +#ifdef USE_LITERAL2 +#define LENGTH_FIELD (flen + (unsigned char) lfile[0] + 6) +#define LIT_TYPE CTB_LITERAL2_TYPE +#else +#define LENGTH_FIELD flen +#define LIT_TYPE CTB_LITERAL_TYPE +#endif + if (lit_mode == MODE_BINARY) + write_ctb_len (g, LIT_TYPE, LENGTH_FIELD, FALSE); +#ifdef VMS + else if (lit_mode == MODE_LOCAL) + write_ctb_len (g, CTB_LITERAL2_TYPE, flen + fdl_len + sizeof(fdl_len) + 6, TRUE); +#endif /* VMS */ + else /* Will put in size field later for text mode */ + write_ctb_len (g, LIT_TYPE, 0L, TRUE); +#ifdef USE_LITERAL2 + fpos = ftell(g); +#endif + fwrite ( &lit_mode, 1, 1, g ); /* write lit_mode */ + + if (lit_mode == MODE_LOCAL) { +#ifdef VMS + write_litlocal( g, fdl, fdl_len); + free(fdl); +#else + ; /* Null statement if we don't have anything to do! */ +#endif /* VMS */ + } else { + /* write literalfile name */ + fwrite (lfile, 1, (unsigned char) lfile[0]+1, g); + /* Dummy file creation timestamp */ + fwrite ( &dummystamp, 1, sizeof(dummystamp), g); + } +#ifndef USE_LITERAL2 + fpos = ftell(g); +#endif + + if ((lit_mode == MODE_BINARY) || (lit_mode == MODE_LOCAL)) { + if (copyfile( f, g, -1L )) { + fprintf(pgpout,"\n\007Unable to append to literal plaintext file"); + perror("\n"); + fclose(g); + goto err1; + } + } else { + CONVERSION = (lit_mode == MODE_TEXT) ? INT_CONV : NO_CONV; + status = copyfile_to_canon( f, g, -1L ); + CONVERSION = NO_CONV; + /* Re-write CTB with correct length info */ + rewind (g); + write_ctb_len (g, LIT_TYPE, fsize(g)-fpos, TRUE); + } + if (write_error(g) || status < 0) + { fclose(g); + goto err1; + } + fclose(g); + fclose(f); + return(0); /* normal return */ + +err1: + fclose(f); + return(-1); /* error return */ + +} /* make_literal */ +#undef LENGTH_FIELD +#undef LIT_TYPE + + +/*======================================================================*/ +int strip_literal(char *infile, char *outfile, char *preserved_name, + char *lit_mode) +{ /* Strip off literal prefix from infile, copying to outfile. + Get lit_mode and literalfile info from + the prefix. Replace outfile with literalfile unless + literalfile is illegal + the original filename is stored in preserved_name + If lit_mode is MODE_TEXT, convert from canonical form as we + copy the data. + */ + byte ctb; /* Cipher Type Byte */ + FILE *f; + FILE *g; + word32 LITlength = 0; + unsigned char litfile[MAX_PATH]; + word32 dummystamp; + char org_sys[5]; /* Name of originating system */ + int status; +#ifdef VMS + char *fdl; + short fdl_len; +#endif + *lit_mode = MODE_BINARY; + if (verbose) + fprintf(pgpout,"strip_literal: infile = %s, outfile = %s\n", + infile,outfile); + + if (preserved_name) + *preserved_name = '\0'; + + /* open file f for read, in binary (not text) mode...*/ + if ((f = fopen(infile,FOPRBIN)) == NULL) + { fprintf(pgpout,PSTR("\n\007Can't open input plaintext file '%s'\n"),infile); + return(-1); + } + + fread(&ctb,1,1,f); /* read Cipher Type Byte */ + + if (!is_ctb(ctb) || !(is_ctb_type(ctb,CTB_LITERAL_TYPE) || + is_ctb_type(ctb,CTB_LITERAL2_TYPE))) + { /* debug message in English only -- something got corrupted */ + fprintf(pgpout,"\n\007'%s' is not a literal plaintext file.\n",infile); + fclose(f); + return(-1); + } + + LITlength = getpastlength(ctb, f); /* read packet length */ + + /* Read literal data */ + *lit_mode = '\0'; + fread (lit_mode,1,1,f); + if ((*lit_mode != MODE_BINARY) && (*lit_mode != MODE_TEXT) + && (*lit_mode != MODE_LOCAL)) + { (void) version_error(*lit_mode, MODE_TEXT); + fclose(f); + return(-1); + } + if (verbose) + fprintf(pgpout, PSTR("File type: '%c'\n"), *lit_mode); + /* Read literal file name, use it if possible */ + litfile[0] = 0; + fread (litfile,1,1,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] > 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); + if (preserved_name) + strcpy(preserved_name, (char *) litfile); + } + if (*lit_mode == MODE_LOCAL) { + fread(org_sys, 1, 4, f); org_sys[4] = '\0'; +#ifdef VMS +#define LOCAL_TEST !strncmp("VMS ",org_sys,4) +#else +#define LOCAL_TEST FALSE +#endif + if (LOCAL_TEST) { +#ifdef VMS + remove(outfile); /* Prevent litter, we recreate the file with correct chars. */ + fread(&fdl_len, 2, 1, f); + fdl = (char *) malloc(fdl_len); + fread(fdl, 1, fdl_len, f); + if ((g = fdl_create( fdl, fdl_len, outfile, (char *) litfile)) == NULL) { + fprintf(pgpout,"\n\007Unable to create file %s\n", outfile); + return(-1); + } + free(fdl); + if (preserved_name) + strcpy(preserved_name, (char *) litfile); + LITlength -= (fdl_len + sizeof(fdl_len)); +#endif /* VMS */ + } else { + fprintf(pgpout,"\n\007Unrecognised local binary type %s\n",org_sys); + return(-1); + } + } else { + /* Discard file creation timestamp for now */ + fread (&dummystamp, 1, sizeof(dummystamp), f); + } + + if (*lit_mode==MODE_LOCAL) { +#ifdef VMS + if (status = fdl_copyfile2bin( f, g, LITlength)) { /* Copy ok? */ + if (status > 0) + fprintf(stderr,"\n...copying to literal file\n"); + else + perror("\nError copying from work file"); + fdl_close(g); + goto err1; + } + fdl_close(g); +#endif /*VMS */ + } else { + if (*lit_mode == MODE_TEXT) + g = fopen(outfile, FOPWTXT); + else + g = fopen(outfile, FOPWBIN); + if (g == NULL) + { fprintf(pgpout, PSTR("\n\007Can't create plaintext file '%s'\n"), outfile ); + goto err1; + } + /* copy rest of literal plaintext file */ + CONVERSION = (*lit_mode == MODE_TEXT) ? EXT_CONV : NO_CONV; + if (*lit_mode == MODE_BINARY) + status = copyfile(f, g, LITlength); + else + status = copyfile_from_canon(f, g, LITlength); + CONVERSION = NO_CONV; + if (write_error(g) || status < 0) + { fclose(g); + goto err1; + } + fclose(g); + } + + fclose(f); + return(0); /* normal return */ + +err1: + fclose(f); + return(-1); /* error return */ + +} /* strip_literal */ + + +/*======================================================================*/ + + +int decryptfile(char *infile, char *outfile) +{ + byte ctb; /* Cipher Type Byte */ + byte ctbCKE; /* Cipher Type Byte */ + FILE *f; + FILE *g; + 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]; + byte outbuf[MAX_BYTE_PRECISION]; + byte keyID[KEYFRAGSIZE]; + 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; + struct nkey { + byte keyID[KEYFRAGSIZE]; + struct nkey *next; + } *nkey, *nkeys = NULL; + + if (verbose) + fprintf(pgpout,"decryptfile: infile = %s, outfile = %s\n", + infile,outfile); + + /* open file f for read, in binary (not text) mode...*/ + if ((f = fopen(infile,FOPRBIN)) == NULL) + { fprintf(pgpout,PSTR("\n\007Can't open ciphertext file '%s'\n"),infile); + 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 + */ + + 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); + } + + /* PKE is Public Key Encryption */ + if (!is_ctb_type(ctb,CTB_PKE_TYPE)) { + end_of_pkes = 1; + continue; + } + + 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); + } + + fread(keyID,1,KEYFRAGSIZE,f); /* read key ID */ + /* Use keyID prefix to look up key. */ + + /* 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 (!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); + + 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\ +This may be caused either by corrupted data or by using the wrong RSA key.\n")); + fclose(f); + return(-1); + } + + /* Verify that top of buffer has correct algorithm byte */ + --count; /* one less byte to drop algorithm byte */ +/* Assume MSB external byte ordering */ + if (version_error(outbuf[0], IDEA_ALGORITHM_BYTE)) + { fclose(f); + return(-1); + } + + /* Verify checksum */ + count -= 2; /* back up before checksum */ +/* Assume MSB external byte ordering */ + chksum = fetch_word16(outbuf+1+count); + if (chksum != checksum(outbuf+1, count)) + { 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); + return(-1); + } + + /* outbuf should contain random IDEA key packet */ + /*==================================================================*/ + + /* 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; + } + + fread(&ctbCKE,1,1,f); /* read Cipher Type Byte, should be CTB_CKE */ + if (!is_ctb(ctbCKE) || !is_ctb_type(ctbCKE,CTB_CKE_TYPE)) + { /* Should never get here. */ + fprintf(pgpout,"\007\nBad or missing CTB_CKE byte.\n"); + goto err1; /* Abandon ship! */ + } + + flen = getpastlength(ctbCKE, f); /* read packet length */ + + /* Decrypt ciphertext file */ +/* Assume MSB external byte ordering */ + status = idea_file( outbuf+1, DECRYPT_IT, f, g, flen ); + if (status < 0) + { fprintf(pgpout,PSTR("\n\007Error: Decrypted plaintext is corrupted.\n")); + } + if (!quietmode) + fputc('.',pgpout); /* show progress */ + + if (write_error(g)) + { fclose(g); + goto err1; + } + fclose(g); + fclose(f); + burn(inbuf); /* burn sensitive data on stack */ + burn(outbuf); /* burn sensitive data on stack */ + mp_burn(d); /* burn sensitive data on stack */ + mp_burn(p); /* burn sensitive data on stack */ + mp_burn(q); /* burn sensitive data on stack */ + mp_burn(u); /* burn sensitive data on stack */ + if (status < 0) /* if idea_file failed, then error return */ + return(status); + return(1); /* always indicate output file has nested stuff in it. */ + +err1: + fclose(f); + burn(inbuf); /* burn sensitive data on stack */ + burn(outbuf); /* burn sensitive data on stack */ + mp_burn(d); /* burn sensitive data on stack */ + mp_burn(p); /* burn sensitive data on stack */ + mp_burn(q); /* burn sensitive data on stack */ + mp_burn(u); /* burn sensitive data on stack */ + return(-1); /* error return */ + +} /* decryptfile */ + + + +int idea_decryptfile(char *infile, char *outfile) +{ + byte ctb; /* Cipher Type Byte */ + FILE *f; + FILE *g; + byte ideakey[16]; + byte passphrase[256]; + int status, retries = 0; + word32 flen; + + if (verbose) + fprintf(pgpout,"idea_decryptfile: infile = %s, outfile = %s\n", + infile,outfile); + + /* open file f for read, in binary (not text) mode...*/ + if ((f = fopen(infile,FOPRBIN)) == NULL) + { fprintf(pgpout,PSTR("\n\007Can't open ciphertext file '%s'\n"),infile); + return(-1); + } + + /* 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; + } + + 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); + goto err1; /* Abandon ship! */ + } + flen = getpastlength(ctb, f); /* read packet length */ + + /* 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); + } + } + + 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 (status == 0 && !quietmode) + fputc('.',pgpout); /* show progress */ + + if (write_error(g)) + { fclose(g); + goto err1; + } + fclose(g); + fclose(f); + + if (status < 0) /* if idea_file failed, then complain */ + { remove(outfile); /* throw away our mistake */ + return(status); /* error return */ + } + if (!quietmode) + fprintf(pgpout,PSTR("Pass phrase appears good. ")); + return(1); /* always indicate output file has nested stuff in it. */ + +err1: + fclose(f); + return(-1); /* error return */ + +} /* idea_decryptfile */ + + + +int decompress_file(char *infile, char *outfile) +{ + byte ctb; + FILE *f; + FILE *g; + extern void lzhDecode( FILE *, FILE * ); + extern void unzip( FILE *, FILE * ); + if (verbose) fprintf(pgpout, PSTR("Decompressing plaintext...") ); + + /* open file f for read, in binary (not text) mode...*/ + if ((f = fopen(infile,FOPRBIN)) == NULL) + { fprintf(pgpout,PSTR("\n\007Can't open compressed file '%s'\n"),infile); + return(-1); + } + + fread(&ctb,1,1,f); /* read and skip over Cipher Type Byte */ + if (!is_ctb_type( ctb, CTB_COMPRESSED_TYPE )) + { /* Shouldn't get here, or why were we called to begin with? */ + fprintf(pgpout,"\007\nBad or missing CTB_COMPRESSED byte.\n"); + goto err1; /* Abandon ship! */ + } + + getpastlength(ctb, f); /* read packet length */ + /* The packet length is ignored. Assume it's huge. */ + + fread(&ctb,1,1,f); /* read and skip over compression algorithm byte */ + if (ctb != ZIP2_ALGORITHM_BYTE) + { /* We only know how to uncompress deflate-compressed data. We + may hit imploded or Lharc'ed data but treat it as an error just + the same */ + fprintf(pgpout,PSTR("\007\nUnrecognized compression algorithm.\n\ +This may require a newer version of PGP.\n")); + goto err1; /* Abandon ship! */ + } + + /* open file g for write, in binary (not text) mode... */ + if ((g = fopen( outfile, FOPWBIN )) == NULL) + { fprintf(pgpout, PSTR("\n\007Can't create decompressed file '%s'\n"), outfile ); + goto err1; + } + + unzip( f, g ); + if (verbose) + fprintf(pgpout, PSTR("done. ") ); + else if (!quietmode) + fputc('.',pgpout); /* show progress */ + + if (write_error(g)) + { fclose(g); + goto err1; + } + fclose(g); + fclose(f); + return(1); /* always indicate output file has nested stuff in it. */ +err1: + fclose(f); + return(-1); /* error return */ + +} /* decompress_file */ +