|
|
1.1 ! root 1: /* ! 2: Pretty Good(tm) Privacy - RSA public key cryptography for the masses ! 3: Written by Philip Zimmermann, Phil's Pretty Good(tm) Software. ! 4: Beta test version 1.0 - Last revised 5 Jun 91 by PRZ ! 5: ! 6: PGP combines the convenience of the Rivest-Shamir-Adleman (RSA) ! 7: public key cryptosystem with the speed of fast conventional ! 8: cryptographic algorithms, fast message digest algorithms, data ! 9: compression, and sophisticated key management. And PGP performs ! 10: the RSA functions faster than most other software implementations. ! 11: PGP is RSA public key cryptography for the masses. ! 12: ! 13: Uses RSA Data Security, Inc. MD4 Message Digest Algorithm ! 14: for signatures. Uses the LZHUF algorithm for compression. ! 15: Uses my own algorithm, BassOmatic, for conventional encryption. ! 16: ! 17: (c) Copyright 1990 by Philip Zimmermann. All rights reserved. ! 18: The author assumes no liability for damages resulting from the use ! 19: of this software, even if the damage results from defects in this ! 20: software. No warranty is expressed or implied. ! 21: ! 22: All the source code I wrote for PGP is available for free under ! 23: the "Copyleft" General Public License from the Free Software ! 24: Foundation. A copy of that license agreement is included in the ! 25: source release package of PGP. The source code for the MD4 ! 26: functions and the LZHUF functions were separately placed in the ! 27: public domain by their respective authors. See the PGP User's ! 28: Guide for more complete information about licensing, patent ! 29: restrictions on the RSA algorithm, trademarks, copyrights, and ! 30: export controls. Technical assistance from me is available for ! 31: an hourly fee. ! 32: ! 33: ! 34: PGP generally zeros its used stack and memory areas before exiting. ! 35: This avoids leaving sensitive information in RAM where other users ! 36: could find it later. The RSA library and keygen routines also ! 37: sanitize their own stack areas. This stack sanitizing has not been ! 38: checked out under all the error exit conditions, when routines exit ! 39: abnormally. Also, we must find a way to clear the C I/O library ! 40: file buffers, and the MSDOS disk buffers. ! 41: ! 42: The code in this source file (pgp.c) was hastily written, and it ! 43: shows. It has a lot of redundant code, developed by ad-hoc ! 44: "accretion" rather than by well-planned design. It isn't buggy, but ! 45: it needs to be reorganized to make it cleaner, clearer, and more ! 46: succinct. Maybe someday. Better and more typical examples of my ! 47: programming style can be seen in the RSA library code in rsalib.c ! 48: and keygen.c, and in the BassOmatic conventional encryption routines ! 49: in basslib.c and related files. ! 50: ! 51: If you modify this code, PLEASE preserve the style of indentation ! 52: used for {begin...end} blocks. It drives me bats to have to deal ! 53: with more than one style in the same program. ! 54: ! 55: */ ! 56: ! 57: ! 58: #include <stdlib.h> /* for exit(), malloc(), free(), etc. */ ! 59: #include <stdio.h> /* for printf(), tmpfile(), etc. */ ! 60: #include <time.h> /* for timestamps and performance measurement */ ! 61: #include <string.h> /* for strcat(), etc. */ ! 62: #include <io.h> ! 63: #include <conio.h> /* for kbhit() */ ! 64: ! 65: #include "md4.h" /* for MD4 message digest stuff */ ! 66: #include "rsalib.h" ! 67: #include "rsaio.h" ! 68: #include "keygen.h" ! 69: #include "random.h" ! 70: #include "basslib.h" ! 71: #include "basslib2.h" ! 72: ! 73: #define KEYFRAGSIZE 8 /* # of bytes in key ID modulus fragment */ ! 74: #define SIZEOF_TIMESTAMP 4 /* 32-bit timestamp */ ! 75: ! 76: /* This macro is for burning sensitive data (byte arrays only) on stack */ ! 77: #define burn(x) fill0(x,sizeof(x)) ! 78: ! 79: /* ! 80: ********************************************************************** ! 81: */ ! 82: ! 83: /* Cipher Type Byte (CTB) definitions follow...*/ ! 84: #define CTB_DESIGNATOR 0x80 ! 85: #define is_ctb(c) (((c) & CTB_DESIGNATOR)==CTB_DESIGNATOR) ! 86: #define CTB_TYPE_MASK 0x7c ! 87: #define CTB_LLEN_MASK 0x03 ! 88: ! 89: /* length of length field of packet, in bytes (1, 2, 4, 8 bytes): */ ! 90: #define ctb_llength(ctb) ((int) 1 << (int) ((ctb) & CTB_LLEN_MASK)) ! 91: ! 92: #define is_ctb_type(ctb,type) (((ctb) & CTB_TYPE_MASK)==(4*type)) ! 93: #define CTB_BYTE(type,llen) (CTB_DESIGNATOR + (4*type) + llen) ! 94: ! 95: #define CTB_PKE_TYPE 1 /* packet encrypted with RSA public key */ ! 96: #define CTB_SKE_TYPE 2 /* packet signed with RSA secret key */ ! 97: #define CTB_MD_TYPE 3 /* message digest packet */ ! 98: #define CTB_CONKEY_TYPE 4 /* conventional key packet */ ! 99: #define CTB_CERT_SECKEY_TYPE 5 /* secret key certificate */ ! 100: #define CTB_CERT_PUBKEY_TYPE 6 /* public key certificate */ ! 101: #define CTB_COMPRESSED_TYPE 8 /* compressed data packet */ ! 102: #define CTB_CKE_TYPE 9 /* conventional-key-encrypted data */ ! 103: #define CTB_LITERAL_TYPE 12 /* raw data */ ! 104: ! 105: /* Unimplemented CTB packet types follow... */ ! 106: /* #define CTB_RAW1_TYPE 13 /* raw data, with filename, date, crc32 prefix */ ! 107: /* #define CTB_PATTERN_TYPE 14 /* unique file prefix autorecognition pattern */ ! 108: /* #define CTB_EXTENDED_TYPE 15 /* 2-byte CTB, 256 extra CTB types */ ! 109: ! 110: #define CTB_PKE CTB_BYTE(CTB_PKE_TYPE,1) ! 111: /* CTB_PKE len16 keyID mpi(RSA(CONKEYPKT)) */ ! 112: /* 1 2 SIZE countbytes()+2 */ ! 113: #define CTB_SKE CTB_BYTE(CTB_SKE_TYPE,1) ! 114: /* CTB_SKE len16 keyID mpi(RSA(MDPKT)) */ ! 115: /* 1 2 SIZE countbytes()+2 */ ! 116: #define CTB_MD CTB_BYTE(CTB_MD_TYPE,0) ! 117: /* CTB_MD len8 algorithm MD timestamp */ ! 118: #define CTB_CONKEY CTB_BYTE(CTB_CONKEY_TYPE,0) ! 119: /* CTB_CONKEY len8 algorithm key */ ! 120: #define CTB_CERT_SECKEY CTB_BYTE(CTB_CERT_SECKEY_TYPE,1) ! 121: /* CTB_CERT_SECKEY len16 timestamp userID mpi(n) mpi(e) mpi(d) mpi(p) mpi(q) mpi(u) crc16 */ ! 122: #define CTB_CERT_PUBKEY CTB_BYTE(CTB_CERT_PUBKEY_TYPE,1) ! 123: /* CTB_CERT_PUBKEY len16 timestamp userID mpi(n) mpi(e) crc16 */ ! 124: ! 125: /* Note that a "secret key compromised" certificate is exactly the same ! 126: as a public key certificate, but with mpi(e)==0. */ ! 127: ! 128: #define CTB_CKE CTB_BYTE(CTB_CKE_TYPE,3) ! 129: /* CTB_CKE ciphertext */ ! 130: ! 131: #define CTB_LITERAL CTB_BYTE(CTB_LITERAL_TYPE,3) ! 132: /* CTB_LITERAL data */ ! 133: ! 134: #define CTB_COMPRESSED CTB_BYTE(CTB_COMPRESSED_TYPE,3) ! 135: /* CTB_COMPRESSED compressedtext */ ! 136: ! 137: #define CTB_PATTERN CTB_BYTE(CTB_PATTERN_TYPE,0) ! 138: /* Unique 40-bit auto-recognition prefix pattern: B8 03 'P' 'R' 'Z' */ ! 139: ! 140: /* Conventional encryption algorithm selector bytes. */ ! 141: #define DES_ALGORITHM_BYTE 1 /* use the DES (unimplemented) */ ! 142: #define BASS_ALGORITHM_BYTE 2 /* use the BassOmatic */ ! 143: ! 144: /* Message digest algorithm selector bytes. */ ! 145: #define MD4_ALGORITHM_BYTE 1 /* MD4 message digest algorithm */ ! 146: ! 147: /* Data compression algorithm selector bytes. */ ! 148: #define LZH_ALGORITHM_BYTE 1 /* LZH compression algorithm */ ! 149: ! 150: #define is_secret_key(ctb) is_ctb_type(ctb,CTB_CERT_SECKEY_TYPE) ! 151: ! 152: #define MAX_SIGCERT_LENGTH (1+2 + KEYFRAGSIZE + 2+MAX_BYTE_PRECISION) ! 153: ! 154: #define MAX_KEYCERT_LENGTH (1+2+4+256 + 5*(2+MAX_BYTE_PRECISION)) ! 155: ! 156: ! 157: /* Global filenames and system-wide file extensions... */ ! 158: char CTX_EXTENSION[] = ".ctx"; ! 159: char PUB_EXTENSION[] = ".pub"; ! 160: char SEC_EXTENSION[] = ".sec"; ! 161: char SCRATCH_CTX_FILENAME[] = "_pgptemp.ctx"; ! 162: char SCRATCH_PTX_FILENAME[] = "_pgptemp.ptx"; ! 163: char SCRATCH_KEYRING_FILENAME[] = "_tmpring.pub"; /* gets modified */ ! 164: char PGPPATH[] = "PGPPATH"; /* environmental variable */ ! 165: ! 166: /* These files use the environmental variable PGPPATH as a default path: */ ! 167: char PUBLIC_KEYRING_FILENAME[32] = "keyring.pub"; ! 168: char SECRET_KEYRING_FILENAME[32] = "keyring.sec"; ! 169: char RANDSEED_FILENAME[32] = "randseed.pgp"; ! 170: ! 171: boolean verbose = FALSE; /* -l option: display maximum information */ ! 172: ! 173: /* ! 174: ********************************************************************** ! 175: */ ! 176: ! 177: ! 178: ! 179: boolean pkzipSignature( byte *header ) ! 180: { ! 181: /* ! 182: ** Return TRUE if header begins with the PKzip signature ! 183: ** Useful for MSDOS only. ! 184: */ ! 185: ! 186: if ((header[0] == 'P') && (header[1] == 'K') ! 187: && (header[2] == '\03') && (header[3] == '\04')) ! 188: return(TRUE); ! 189: return(FALSE); ! 190: } /* pkzipSignature */ ! 191: ! 192: ! 193: /* ! 194: ** Convert to or from external byte order. ! 195: ** Note that hilo_swap does nothing if this is a LSB-first CPU. ! 196: */ ! 197: ! 198: #define convert2(x,lx) hilo_swap( (byteptr)&(x), (lx) ) ! 199: #define convert(x) convert2( (x), sizeof(x) ) ! 200: ! 201: word16 fetch_word16(byte *buf) ! 202: /* Fetches a 16-bit word from where byte pointer is pointing. ! 203: buf points to external-format byteorder array, assuming LSB-first. ! 204: */ ! 205: { word16 w0,w1; ! 206: w0 = *buf++; ! 207: w1 = *buf++; ! 208: return((w1<<8) + w0); ! 209: } /* fetch_word16 */ ! 210: ! 211: ! 212: void get_timestamp(byte *timestamp) ! 213: /* Returns timestamp byte array, in internal byteorder */ ! 214: { word32 t; ! 215: t = time(0); ! 216: timestamp[0] = t; /* fill array in external byte order */ ! 217: timestamp[1] = t>>8; ! 218: timestamp[2] = t>>16; ! 219: timestamp[3] = t>>24; ! 220: /* Note that hilo_swap does nothing if this is a LSB-first CPU. */ ! 221: hilo_swap(timestamp,4); /* convert to internal byteorder */ ! 222: } /* get_timestamp */ ! 223: ! 224: ! 225: void CToPascal(char *s) ! 226: { /* "xyz\0" --> "\3xyz" ... converts C string to Pascal string */ ! 227: int i,j; ! 228: j = string_length(s); ! 229: for (i=j; i!=0; i--) ! 230: s[i] = s[i-1]; /* move everything 1 byte to the right */ ! 231: s[0] = j; /* Pascal length byte at beginning */ ! 232: } /* CToPascal */ ! 233: ! 234: ! 235: void PascalToC( char *s ) ! 236: { /* "\3xyz" --> "xyz\0" ... converts Pascal string to C string */ ! 237: int i,j; ! 238: for (i=0,j=s[0]; i<j; i++) ! 239: s[i] = s[i+1]; /* move everything 1 byte to the left */ ! 240: s[i] = '\0'; /* append C string terminator */ ! 241: } /* PascalToC */ ! 242: ! 243: ! 244: ! 245: int date_ymd(word32 *tstamp, int *year, int *month, int *day) ! 246: /* Given timestamp as seconds elapsed since 1970 Jan 1 00:00:00, ! 247: returns year (1970-2106), month (1-12), day (1-31). ! 248: Not valid for dates after 2100 Feb 28 (no leap day that year). ! 249: Also returns day of week (0-6) as functional return. ! 250: */ ! 251: { word32 days,y; ! 252: int m,d,i; ! 253: static short mdays[12] = {31,28,31,30,31,30,31,31,30,31,30,31}; ! 254: days = (*tstamp)/86400UL; /* day 0 is 1970/1/1 */ ! 255: days -= 730UL; /* align days relative to 1st leap year, 1972 */ ! 256: y = ((days*4UL)/1461UL); /* 1972 is year 0 */ ! 257: /* reduce to days elapsed since 1/1 last leap year: */ ! 258: d = days - ((y/4UL)*1461UL); ! 259: *year = y+1972; ! 260: for (i=0; i<48; i++) /* count months 0-47 */ ! 261: { m = i % 12; ! 262: d -= mdays[m] + (i==1); /* i==1 is the only leap month */ ! 263: if (d < 0) ! 264: { d += mdays[m] + (i==1); ! 265: break; ! 266: } ! 267: } ! 268: *month = m+1; ! 269: *day = d+1; ! 270: i = (days-2UL) % 7UL; /* compute day of week 0-6 */ ! 271: return(i); /* returns weekday 0-6; 0=Sunday, 6=Saturday */ ! 272: } /* date_ymd */ ! 273: ! 274: ! 275: ! 276: void show_date(word32 *tstamp) ! 277: { int m,d,y; ! 278: static char *month[12] = ! 279: {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"}; ! 280: date_ymd(tstamp,&y,&m,&d); ! 281: fprintf(stderr,"%2d-%s-%02d", d, month[m-1], y % 100); ! 282: } /* show_date */ ! 283: ! 284: ! 285: ! 286: boolean file_exists(char *filename) ! 287: /* Returns TRUE iff file is can be opened for reading. */ ! 288: { FILE *f; ! 289: /* open file f for read, in binary (not text) mode...*/ ! 290: if ((f = fopen(filename,"rb")) == NULL) ! 291: return(FALSE); ! 292: fclose(f); ! 293: return(TRUE); ! 294: } /* file_exists */ ! 295: ! 296: ! 297: ! 298: #define diskbufsize 1024 ! 299: ! 300: int wipeout(FILE *f) ! 301: { /* Completely overwrite and erase file, so that no sensitive ! 302: information is left on the disk. ! 303: NOTE: File MUST be open for read/write. ! 304: */ ! 305: ! 306: long flength; ! 307: int count; ! 308: byte textbuf[diskbufsize]; ! 309: ! 310: fseek(f, 0L, SEEK_END); ! 311: flength = ftell(f); ! 312: rewind(f); ! 313: ! 314: fill0(textbuf,diskbufsize); ! 315: while (flength > 0L) ! 316: { /* write zeros to the whole file... */ ! 317: if (flength < (word32) diskbufsize) ! 318: count = flength; ! 319: else ! 320: count = diskbufsize; ! 321: fwrite(textbuf,1,count,f); ! 322: flength -= count; ! 323: } ! 324: rewind(f); /* maybe this isn't necessary */ ! 325: return(0); /* normal return */ ! 326: } /* wipeout */ ! 327: ! 328: ! 329: int wipefile(char *filename) ! 330: { /* Completely overwrite and erase file, so that no sensitive ! 331: information is left on the disk. ! 332: */ ! 333: FILE *f; ! 334: /* open file f for read/write, in binary (not text) mode...*/ ! 335: if ((f = fopen(filename,"rb+")) == NULL) ! 336: return(-1); /* error - file can't be opened */ ! 337: wipeout(f); ! 338: fclose(f); ! 339: return(0); /* normal return */ ! 340: } /* wipefile */ ! 341: ! 342: ! 343: ! 344: #define strhas(s,c) (strchr((s),(c)) != NULL) ! 345: ! 346: boolean strhasany( char *s1, char *s2 ) ! 347: { /* Searches s1 for any of the characters in s2. ! 348: Returns TRUE if found. ! 349: */ ! 350: while (*s2) ! 351: { if (strhas(s1,*s2)) ! 352: return(TRUE); ! 353: s2++; ! 354: } ! 355: return(FALSE); ! 356: } /* strhasany */ ! 357: ! 358: ! 359: boolean strcontains( char *s1, char *s2 ) ! 360: { /* ! 361: ** Searches s1 for s2, without case sensitivity. ! 362: ** Return TRUE if found. ! 363: ** ! 364: ** If s2 is an empty string then return TRUE. This is because, ! 365: ** at least in the world of mathematics, the empty set is contained ! 366: ** in all other sets. The Microsoft C version 6.0 strstr function ! 367: ** behaves this way but version 5.1 does not, so we need to ! 368: ** explicitly test for the situation. -- ALH 91/2/17 ! 369: */ ! 370: ! 371: if (s2[0] != '\0') ! 372: { ! 373: char buf1[256], buf2[256]; /* scratch buffers */ ! 374: ! 375: strncpy( buf1, s1, 256 ); strlwr( buf1 ); /* converts to lower case */ ! 376: strncpy( buf2, s2, 256 ); strlwr( buf2 ); /* converts to lower case */ ! 377: ! 378: if (strstr( buf1, buf2 ) == NULL) ! 379: return( FALSE ); /* string not found */ ! 380: } ! 381: return(TRUE); ! 382: } /* strcontains */ ! 383: ! 384: ! 385: void translate_spaces(char *s) ! 386: /* Changes all the underlines to spaces in a string. */ ! 387: { while (strchr(s,'_') != NULL) ! 388: *strchr(s,'_') = ' '; ! 389: } ! 390: ! 391: ! 392: boolean no_extension(char *filename) ! 393: /* Returns TRUE if user left off file extension, allowing default. */ ! 394: { if (strrchr(filename,'.')==NULL) ! 395: return(TRUE); ! 396: /* see if the last '.' is followed by a backslash...*/ ! 397: if (*(strrchr(filename,'.')+1) == '\\') ! 398: return(TRUE); /* just a "..\filename" construct */ ! 399: return(FALSE); /* user specified extension, even if a blank one */ ! 400: } /* no_extension */ ! 401: ! 402: ! 403: void drop_extension(char *filename) ! 404: { /* deletes trailing ".xxx" file extension after the period. */ ! 405: if (!no_extension(filename)) ! 406: *strrchr(filename,'.') = '\0'; ! 407: } /* drop_extension */ ! 408: ! 409: ! 410: void default_extension(char *filename, char *extension) ! 411: { /* append filename extension if there isn't one already. */ ! 412: if (no_extension(filename)) ! 413: strcat(filename,extension); ! 414: } /* default_extension */ ! 415: ! 416: ! 417: void force_extension(char *filename, char *extension) ! 418: { /* change the filename extension. */ ! 419: drop_extension(filename); /* out with the old */ ! 420: strcat(filename,extension); /* in with the new */ ! 421: } /* force_extension */ ! 422: ! 423: ! 424: boolean getyesno(char default_answer) ! 425: { /* Get yes/no answer from user, returns TRUE for yes, FALSE for no. */ ! 426: char buf[8]; ! 427: while (keypress()) /* flush typahead buffer */ ! 428: getkey(); ! 429: getstring(buf,6,TRUE); /* echo keyboard input */ ! 430: if (strlen(buf)==0) /* if user didn't give an answer... */ ! 431: buf[0] = default_answer; /* assume default answer */ ! 432: buf[0] = tolower(buf[0]); ! 433: return(buf[0]=='y'); ! 434: } /* getyesno */ ! 435: ! 436: ! 437: void maybe_force_extension(char *filename, char *extension) ! 438: { /* if user consents to it, change the filename extension. */ ! 439: char newname[64]; ! 440: if (!strcontains(filename,extension)) ! 441: { strcpy(newname,filename); ! 442: force_extension(newname,extension); ! 443: if (!file_exists(newname)) ! 444: { fprintf(stderr,"\nShould '%s' be renamed to '%s' [Y/n]? ", ! 445: filename,newname); ! 446: if (getyesno('y')) ! 447: rename(filename,newname); ! 448: } ! 449: } ! 450: } /* maybe_force_extension */ ! 451: ! 452: ! 453: /*---------------------------------------------------------------------*/ ! 454: /* Begin uuencode routines. ! 455: This converts a binary file into printable ASCII characters, in a ! 456: form compatible with the Unix uuencode utility. ! 457: This makes it easier to send encrypted files over a 7-bit channel. ! 458: */ ! 459: ! 460: /* ENC is the basic 1 character encoding function to make a char printing */ ! 461: #define ENC(c) (((c) & 077) + ' ') ! 462: ! 463: /* ! 464: * output one group of 3 bytes, pointed at by p, on file f. ! 465: */ ! 466: void outdec(char *p, FILE *f) ! 467: { ! 468: int c1, c2, c3, c4; ! 469: ! 470: c1 = *p >> 2; ! 471: c2 = (*p << 4) & 060 | (p[1] >> 4) & 017; ! 472: c3 = (p[1] << 2) & 074 | (p[2] >> 6) & 03; ! 473: c4 = p[2] & 077; ! 474: putc(ENC(c1), f); ! 475: putc(ENC(c2), f); ! 476: putc(ENC(c3), f); ! 477: putc(ENC(c4), f); ! 478: } /* outdec */ ! 479: ! 480: ! 481: /* fr: like read but stdio */ ! 482: int fr(FILE *fd, char *buf, int cnt) ! 483: { ! 484: int c, i; ! 485: ! 486: for (i=0; i<cnt; i++) ! 487: { ! 488: c = getc(fd); ! 489: if (c == EOF) ! 490: return(i); ! 491: buf[i] = c; ! 492: } ! 493: return (cnt); ! 494: } /* fr */ ! 495: ! 496: ! 497: /* ! 498: * copy from in to out, uuencoding as you go along. ! 499: */ ! 500: void uuencode(FILE *in, FILE *out) ! 501: { ! 502: char buf[80]; ! 503: int i, n; ! 504: ! 505: for (;;) ! 506: { /* 1 (up to) 45 character line */ ! 507: n = fr(in, buf, 45); ! 508: putc(ENC(n), out); ! 509: ! 510: for (i=0; i<n; i += 3) ! 511: outdec(&buf[i], out); ! 512: ! 513: putc('\n', out); ! 514: if (n <= 0) ! 515: break; ! 516: } ! 517: } /* uuencode */ ! 518: ! 519: ! 520: int uue_file(char *infile, char *outfile) ! 521: { /* translates infile to uuencode format, writing to outfile */ ! 522: FILE *in,*out; ! 523: int mode; ! 524: ! 525: if (verbose) ! 526: fprintf(stderr,"Converting output to uuecode format.\n"); ! 527: ! 528: /* open input file as binary */ ! 529: if ((in = fopen(infile,"rb")) == NULL) ! 530: { ! 531: return(1); ! 532: } ! 533: ! 534: /* open output file as text */ ! 535: if ((out = fopen(outfile,"w")) == NULL) ! 536: { fclose(in); ! 537: return(1); ! 538: } ! 539: ! 540: mode = 0666; /* Assume a reasonable dummy default for file mode */ ! 541: ! 542: fprintf(out,"begin %o %s\n", mode, infile); ! 543: ! 544: uuencode(in, out); ! 545: ! 546: fprintf(out,"end\n"); ! 547: fclose(out); ! 548: fclose(in); ! 549: ! 550: return(0); ! 551: } /* uue_file */ ! 552: ! 553: ! 554: /* End uuencode routines. */ ! 555: ! 556: /* uudecode routines. ! 557: Portions derived from unix uudecode utility by Mark Horton. ! 558: */ ! 559: ! 560: #define SUMSIZE 64 ! 561: #define DEC(c) (((c) - ' ') & 077) /* single character decode */ ! 562: ! 563: ! 564: int uud_buffer(char *inbuf, char *outbuf, int *outlength) ! 565: { ! 566: char *bp; ! 567: boolean has_checksum=FALSE; ! 568: ! 569: register int j; ! 570: register int n; ! 571: int checksum; ! 572: int status; ! 573: ! 574: ! 575: status = 0; ! 576: *outlength = 0; ! 577: ! 578: /* Pad end of lines in case some editor truncated trailing ! 579: spaces */ ! 580: ! 581: for (n=0; n<79; n++) /* search for first \r, \n or \000 */ ! 582: { ! 583: if (inbuf[n]=='\176') /* If BITNET made a twiddle, */ ! 584: inbuf[n]='\136'; /* we make a caret */ ! 585: if (inbuf[n]=='\r' || inbuf[n]=='\n' || inbuf[n]=='\000') ! 586: break; ! 587: if ((inbuf[n] < '\040') || (inbuf[n] > '\137')) ! 588: status = -1; /* illegal uudecode character */ ! 589: } ! 590: for (; n<79; n++) /* when found, fill rest of line with space */ ! 591: inbuf[n]=' '; ! 592: ! 593: inbuf[79]=0; /* terminate new string */ ! 594: ! 595: checksum = 0; ! 596: n = DEC(inbuf[0]); ! 597: if (n == 0) ! 598: return(0); /* 0 bytes on a line?? Must be the last line */ ! 599: ! 600: if (status) ! 601: return(status); /* bad character, out of range */ ! 602: ! 603: bp = &inbuf[1]; ! 604: ! 605: /* FOUR input characters go into each THREE output charcters */ ! 606: ! 607: while (n >= 4) ! 608: { ! 609: j = DEC(bp[0]) << 2 | DEC(bp[1]) >> 4; ! 610: checksum += j; ! 611: outbuf[(*outlength)++]=j; ! 612: j = DEC(bp[1]) << 4 | DEC(bp[2]) >> 2; ! 613: checksum += j; ! 614: outbuf[(*outlength)++]=j; ! 615: j = DEC(bp[2]) << 6 | DEC(bp[3]); ! 616: checksum += j; ! 617: outbuf[(*outlength)++]=j; ! 618: checksum = checksum % SUMSIZE; ! 619: bp += 4; ! 620: n -= 3; ! 621: } ! 622: ! 623: j = DEC(bp[0]) << 2 | DEC(bp[1]) >> 4; ! 624: checksum += j; ! 625: if (n >= 1) ! 626: outbuf[(*outlength)++]=j; ! 627: j = DEC(bp[1]) << 4 | DEC(bp[2]) >> 2; ! 628: checksum += j; ! 629: if (n >= 2) ! 630: outbuf[(*outlength)++]=j; ! 631: j = DEC(bp[2]) << 6 | DEC(bp[3]); ! 632: checksum += j; ! 633: if (n >= 3) ! 634: outbuf[(*outlength)++]=j; ! 635: checksum = checksum % SUMSIZE; ! 636: bp += 4; ! 637: n -= 3; ! 638: ! 639: /* The line has been decoded; now check that sum */ ! 640: ! 641: has_checksum |= !isspace(*bp); ! 642: if (has_checksum) /* Is there a checksum at all?? */ ! 643: if (checksum != DEC(*bp)) /* Does that checksum match? */ ! 644: return(-2); /* checksum error */ ! 645: ! 646: return(status); /* normal return */ ! 647: ! 648: } /* uud_buffer */ ! 649: ! 650: /* ! 651: * Copy from in to out, decoding as you go. ! 652: * If a return or newline is encountered too early in a line, it is ! 653: * assumed that means that some editor has truncated trailing spaces. ! 654: */ ! 655: int uudecode(FILE *in, FILE *out) ! 656: { ! 657: char inbuf[81]; ! 658: char outbuf[81]; ! 659: char *bp; ! 660: boolean has_checksum=FALSE; ! 661: ! 662: register int j; ! 663: int n, status; ! 664: int checksum, line; ! 665: ! 666: for (line = 1; ; line++) /* for each input line */ ! 667: { ! 668: if (fgets(inbuf, sizeof inbuf, in) == NULL) ! 669: { ! 670: fprintf(stderr,"ERROR: uudecode input ended unexpectedly!\n"); ! 671: return(18); ! 672: } ! 673: ! 674: status = uud_buffer(inbuf,outbuf,&n); ! 675: ! 676: if (status == -1) ! 677: fprintf(stderr,"ERROR: bad uudecode character decoding line %d.\n", line); ! 678: if (status == -2) ! 679: fprintf(stderr,"ERROR: checksum mismatch decoding line %d.\n", line); ! 680: if (n==0) /* zero-length line is the end. */ ! 681: break; ! 682: ! 683: fwrite(outbuf,1,n,out); ! 684: ! 685: } /* line */ ! 686: ! 687: return(0); /* normal return */ ! 688: } /* uudecode */ ! 689: ! 690: ! 691: boolean is_uufile(char *infile) ! 692: { ! 693: FILE *in; ! 694: char inbuf[80]; ! 695: char outbuf[80]; ! 696: int i, n, status; ! 697: ! 698: if ((in = fopen(infile, "r")) == NULL) ! 699: { /* can't open file */ ! 700: return(FALSE); ! 701: } ! 702: ! 703: /* search file for header line */ ! 704: for (i=0; i<50; i++) /* give up after 50 lines of garbage */ ! 705: { ! 706: if (fgets(inbuf, sizeof inbuf, in) == NULL) ! 707: break; ! 708: else ! 709: { ! 710: if (strncmp(inbuf, "begin ", 6) == 0) ! 711: { ! 712: if (fgets(inbuf, sizeof inbuf, in) == NULL) ! 713: break; ! 714: status = uud_buffer(inbuf,outbuf,&n); ! 715: if (status < 0) ! 716: break; ! 717: fclose(in); ! 718: return(TRUE); ! 719: } ! 720: } ! 721: } ! 722: ! 723: fclose(in); ! 724: return(FALSE); ! 725: ! 726: } /* is_uufile */ ! 727: ! 728: ! 729: int uud_file(char *infile, char *outfile) ! 730: { ! 731: FILE *in, *out; ! 732: int mode; /* file's mode (from header) */ ! 733: long filesize; /* theoretical file size (from header) */ ! 734: char buf[80]; ! 735: int status; ! 736: ! 737: if ((in = fopen(infile, "r")) == NULL) ! 738: { ! 739: fprintf(stderr,"ERROR: can't find %s\n", infile); ! 740: return(10); ! 741: } ! 742: ! 743: /* Loop through file, searching for header. Decode anything with a ! 744: header, complain if there where no headers. */ ! 745: ! 746: /* search file for header line */ ! 747: for (;;) ! 748: { ! 749: if (fgets(buf, sizeof buf, in) == NULL) ! 750: { ! 751: fprintf(stderr,"ERROR: no `begin' line!\n"); ! 752: fclose(in); ! 753: return(12); ! 754: } ! 755: if (strncmp(buf, "begin ", 6) == 0) ! 756: break; ! 757: } ! 758: ! 759: /* Ignore filename and mode. Use outfile instead of dest. */ ! 760: /* sscanf(buf, "begin %o %s", &mode, dest); */ ! 761: ! 762: /* create output file */ ! 763: if ((out = fopen(outfile, "wb")) == NULL) ! 764: { ! 765: fprintf(stderr,"ERROR: can't open output file %s\n", outfile); ! 766: fclose(in); ! 767: return(15); ! 768: } ! 769: ! 770: status = uudecode(in, out); ! 771: if (status != 0) ! 772: { fclose(in); ! 773: fclose(out); ! 774: return(status); ! 775: } ! 776: ! 777: if (fgets(buf, sizeof buf, in) == NULL || strncmp(buf,"end",3)) ! 778: { /* don't be overly picky about newline ^ */ ! 779: fprintf(stderr,"ERROR: no `end' line\n"); ! 780: fclose(in); ! 781: fclose(out); ! 782: return(16); ! 783: } ! 784: ! 785: if (!(fgets(buf,sizeof buf,in) == NULL || strncmp(buf,"size ",3))) ! 786: { ! 787: sscanf(buf, "size %ld", &filesize); ! 788: if (ftell(out) != filesize) ! 789: { ! 790: fprintf(stderr,"ERROR: file should have been %ld bytes long but was %ld.\n", filesize, ftell(out)); ! 791: return(17); ! 792: } ! 793: } ! 794: fclose(out); ! 795: fclose(in); ! 796: return(0); /* normal return */ ! 797: } /* uud_file */ ! 798: ! 799: ! 800: /* End uudecode routines. */ ! 801: /*---------------------------------------------------------------------*/ ! 802: ! 803: ! 804: ! 805: boolean legal_ctb(byte ctb) ! 806: { /* Used to determine if nesting should be allowed. */ ! 807: boolean legal; ! 808: byte ctbtype; ! 809: if (!is_ctb(ctb)) /* not even a bonafide CTB */ ! 810: return(FALSE); ! 811: /* Sure hope CTB internal bit definitions don't change... */ ! 812: ctbtype = (ctb & CTB_TYPE_MASK) >> 2; ! 813: /* Only allow these CTB types to be nested... */ ! 814: legal = ( ! 815: (ctbtype==CTB_PKE_TYPE) ! 816: || (ctbtype==CTB_SKE_TYPE) ! 817: || (ctbtype==CTB_CERT_SECKEY_TYPE) ! 818: || (ctbtype==CTB_CERT_PUBKEY_TYPE) ! 819: || (ctbtype==CTB_LITERAL_TYPE) ! 820: || (ctbtype==CTB_COMPRESSED_TYPE) ! 821: || (ctbtype==CTB_CKE_TYPE) ! 822: /* || (ctbtype==CTB_CONKEY_TYPE) */ ! 823: /* || (ctbtype==CTB_MD_TYPE) */ ! 824: ); ! 825: return(legal); ! 826: } /* legal_ctb */ ! 827: ! 828: ! 829: /*======================================================================*/ ! 830: ! 831: /* MDfile0(MD, f) ! 832: ** Computes and returns the message digest from a file position to eof. ! 833: ** Uses RSA Data Security, Inc. MD4 Message Digest Algorithm. ! 834: */ ! 835: int MDfile0(MDstruct *MD, FILE *f) ! 836: { byte X[64]; ! 837: int bytecount; ! 838: ! 839: MDbegin(MD); ! 840: /* Process 512 bits, or 64 bytes, at a time... */ ! 841: while ((bytecount = fread(X, 1, 64, f)) != 0) ! 842: MDupdate(MD, X, bytecount<<3); /* pass bitcount */ ! 843: MDupdate(MD, X, 0); /* finish with a bitcount of 0 */ ! 844: /* MDprint(MD); */ ! 845: ! 846: return(0); /* normal return */ ! 847: } /* MDfile0 */ ! 848: ! 849: ! 850: /* MDfile(MD, filename) ! 851: ** Computes and returns the message digest for a specified file. ! 852: */ ! 853: int MDfile(MDstruct *MD, char *filename) ! 854: { FILE *f; ! 855: f = fopen(filename,"rb"); ! 856: if (f == NULL) ! 857: { fprintf(stderr,"Can't open file '%s'\n",filename); ! 858: return(-1); /* error return */ ! 859: } ! 860: MDfile0(MD, f); ! 861: fclose(f); ! 862: return(0); /* normal return */ ! 863: } /* MDfile */ ! 864: ! 865: ! 866: /* MD_of_buffer(MD, s, len) ! 867: ** Computes and returns the message digest for buffer s. ! 868: ** len is the length in bytes of buffer s. ! 869: ** Uses RSA Data Security, Inc. MD4 Message Digest Algorithm. ! 870: */ ! 871: void MD_of_buffer(MDstruct *MD, byte *s, int len) ! 872: { int i; ! 873: ! 874: MDbegin(MD); ! 875: /* Process 512 bits, or 64 bytes, at a time... */ ! 876: for (i=0; i+64<=len; i+=64) ! 877: MDupdate(MD, s+i, 512); ! 878: MDupdate(MD, s+i, (len-i)<<3); /* finish with short block */ ! 879: /* MDprint(MD); */ ! 880: ! 881: } /* MD_of_buffer */ ! 882: ! 883: ! 884: boolean equal_buffers(byte *buf1, byte *buf2, word16 count) ! 885: /* Compares two byte buffers. */ ! 886: { while (count--) ! 887: if (*buf1++ != *buf2++) ! 888: return(FALSE); /* mismatch. */ ! 889: return(TRUE); /* compares OK */ ! 890: } /* equal_buffers */ ! 891: ! 892: ! 893: char *buildfilename(char *result, char *fname) ! 894: /* Builds a filename with a complete path specifier from the environmental ! 895: variable PGPPATH. Assumes MSDOS pathname conventions. ! 896: */ ! 897: { char *s = getenv(PGPPATH); ! 898: if (strlen(s) > 50) /* too long to use */ ! 899: s = ""; ! 900: strcpy(result,s); ! 901: if (strlen(result) != 0) ! 902: if (result[strlen(result)-1] != '\\') ! 903: strcat(result,"\\"); ! 904: strcat(result,fname); ! 905: return(result); ! 906: } /* buildfilename */ ! 907: ! 908: ! 909: int strong_pseudorandom(byte *buf, int bufsize) ! 910: /* Reads BassOmatic random key and random number seed from file, ! 911: cranks the the seed through the bassrand strong pseudorandom ! 912: number generator, and writes them back out. This is used for ! 913: generation of cryptographically strong pseudorandom numbers. ! 914: This is mainly to save the user the trouble of having to ! 915: type in lengthy keyboard sequences for generation of truly ! 916: random numbers every time we want to make a random BassOmatic ! 917: session key. This pseudorandom generator will only work if ! 918: the file containing the random seed exists and is not empty. ! 919: If it doesn't exist, it will be automatically created. ! 920: If it exists and is empty or nearly empty, it will not be used. ! 921: */ ! 922: { char seedfile[64]; /* Random seed filename */ ! 923: FILE *f; ! 924: byte key[64]; /* not to exceed 256 byes in length */ ! 925: byte seed[256]; /* not to exceed 256 byes in length */ ! 926: int i; ! 927: word32 tstamp; byte *timestamp = (byte *) &tstamp; ! 928: ! 929: buildfilename(seedfile,RANDSEED_FILENAME); ! 930: ! 931: if (!file_exists(seedfile)) /* No seed file. Start one... */ ! 932: { f = fopen(seedfile,"wb"); /* open for writing binary */ ! 933: if (f==NULL) /* failed to create seedfile */ ! 934: return(-1); /* error: no random number seed file available */ ! 935: fclose(f); /* close new empty seed file */ ! 936: /* kickstart the generator with true random numbers */ ! 937: fprintf(stderr,"Initializing random seed file..."); ! 938: randaccum(8*(sizeof(key)+32)); ! 939: for (i=1; i<sizeof(key); i++) ! 940: key[i] ^= randombyte(); ! 941: for (i=0; i<sizeof(seed); i++) ! 942: seed[i] ^= randombyte(); ! 943: } /* seedfile does not exist */ ! 944: ! 945: else /* seedfile DOES exist. Open it and read it. */ ! 946: { f = fopen(seedfile,"rb"); /* open for reading binary */ ! 947: if (f==NULL) /* did open fail? */ ! 948: return(-1); /* error: no random number seed file available */ ! 949: /* read BassOmatic random generator key */ ! 950: if (fread(key,1,sizeof(key),f) < sizeof(key)) /* empty file? */ ! 951: { /* Empty or nearly empty file means don't use it. */ ! 952: fclose(f); ! 953: return(-1); /* error: no random number seed file available */ ! 954: } ! 955: else ! 956: fread(seed,1,sizeof(seed),f); /* read pseudorandom seed */ ! 957: fclose(f); ! 958: } /* seedfile exists */ ! 959: ! 960: ! 961: get_timestamp(timestamp); ! 962: for (i=0; i<4; i++) ! 963: key[i+1] ^= timestamp[i]; ! 964: ! 965: key[0] = 0x0f; /* BassOmatic key control byte */ ! 966: ! 967: /* Initialize, key, and seed the BassOmatic pseudorandom generator: */ ! 968: initbassrand(key, sizeof(key), seed, sizeof(seed)); ! 969: ! 970: /* Note that the seed will be cycled thru BassOmatic once before use */ ! 971: ! 972: /* Now fill the user's buffer with gobbledygook... */ ! 973: while (bufsize--) ! 974: *buf++ = bassrand() ^ randombyte(); ! 975: ! 976: /* now cover up evidence of what user got */ ! 977: for (i=1; i<sizeof(key); i++) ! 978: key[i] ^= bassrand() ^ randombyte(); ! 979: for (i=0; i<sizeof(seed); i++) ! 980: seed[i] = bassrand() ^ randombyte(); ! 981: ! 982: closebass(); /* close BassOmatic random number generator */ ! 983: ! 984: f = fopen(seedfile,"wb"); /* open for writing binary */ ! 985: if (f==NULL) /* did open fail? */ ! 986: return(-1); /* error: no random number seed file available */ ! 987: /* Now at start of file again */ ! 988: fwrite(key,1,sizeof(key),f); ! 989: fwrite(seed,1,sizeof(seed),f); ! 990: fclose(f); ! 991: burn(key); /* burn sensitive data on stack */ ! 992: burn(seed); /* burn sensitive data on stack */ ! 993: return(0); /* normal return */ ! 994: } /* strong_pseudorandom */ ! 995: ! 996: ! 997: ! 998: int make_random_basskey(byte *key, int keybytes) ! 999: /* Make a keybytes-byte random BassOmatic key, plus 1 key control byte. ! 1000: The byte count returned includes key control byte. ! 1001: */ ! 1002: { int count; ! 1003: ! 1004: key[0] = 0x1f; /* Default is Military grade BassOmatic key control byte */ ! 1005: if (keybytes <= 24) ! 1006: key[0] = 0x12; /* Commercial grade BassOmatic key control byte */ ! 1007: if (keybytes <= 16) ! 1008: key[0] = 0x00; /* Casual grade BassOmatic key control byte */ ! 1009: ! 1010: if (strong_pseudorandom(key+1, keybytes) == 0) ! 1011: return(keybytes+1); /* return length of key, including control byte */ ! 1012: ! 1013: fprintf(stderr,"Preparing random conventional crypto session key..."); ! 1014: ! 1015: randaccum(keybytes*8); /* get some random key bits */ ! 1016: ! 1017: count=0; ! 1018: while (++count <= keybytes) ! 1019: key[count] = randombyte(); ! 1020: ! 1021: return(count+1); /* return length of key, including control byte */ ! 1022: ! 1023: } /* make_random_basskey */ ! 1024: ! 1025: ! 1026: ! 1027: void copyfile(FILE *f, FILE *g, word32 longcount) ! 1028: { /* copy file f to file g, for longcount bytes */ ! 1029: int count; ! 1030: byte textbuf[diskbufsize]; ! 1031: do /* read and write the whole file... */ ! 1032: { ! 1033: if (longcount < (word32) diskbufsize) ! 1034: count = longcount; ! 1035: else ! 1036: count = diskbufsize; ! 1037: count = fread(textbuf,1,count,f); ! 1038: if (count>0) ! 1039: { fwrite(textbuf,1,count,g); ! 1040: longcount -= count; ! 1041: } ! 1042: /* if text block was short, exit loop */ ! 1043: } while (count==diskbufsize); ! 1044: burn(textbuf); /* burn sensitive data on stack */ ! 1045: } /* copyfile */ ! 1046: ! 1047: ! 1048: word32 getpastlength(byte ctb, FILE *f) ! 1049: /* Returns the length of a packet according to the CTB and ! 1050: the length field. */ ! 1051: { word32 length; ! 1052: int llength; /* length of length */ ! 1053: byte buf[8]; ! 1054: ! 1055: fill0(buf,sizeof(buf)); ! 1056: length = 0L; ! 1057: /* Use ctb length-of-length field... */ ! 1058: llength = ctb_llength(ctb); /* either 1, 2, 4, or 8 */ ! 1059: if (llength==8) /* 8 means no length field, assume huge length */ ! 1060: return(-1L); /* return huge length */ ! 1061: ! 1062: /* now read in the actual length field... */ ! 1063: if (fread((byteptr) buf,1,llength,f) < llength) ! 1064: return (-2L); /* error -- read failure or premature eof */ ! 1065: /* convert length from external LSB-first format... */ ! 1066: while (llength--) ! 1067: { length <<= 8; ! 1068: length += buf[llength]; ! 1069: } ! 1070: return(length); ! 1071: } /* getpastlength */ ! 1072: ! 1073: ! 1074: ! 1075: int bass_file(byte *basskey, int lenbasskey, boolean decryp, ! 1076: FILE *f, FILE *g) ! 1077: /* Use BassOmatic in cipher feedback (CFB) mode to encrypt ! 1078: or decrypt a file. Encrypted key check bytes determine ! 1079: if correct BassOmatic key was used to decrypt ciphertext. ! 1080: */ ! 1081: { int count; ! 1082: byte textbuf[diskbufsize], iv[256]; ! 1083: #define KEYCHECKLENGTH 4 ! 1084: ! 1085: /* init CFB BassOmatic key */ ! 1086: fill0(iv,256); /* define initialization vector IV as 0 */ ! 1087: if ( initcfb(iv,basskey,lenbasskey,decryp) < 0 ) ! 1088: return(-1); /* Error return should be impossible. */ ! 1089: ! 1090: if (!decryp) /* encrypt-- insert key check bytes */ ! 1091: { /* key check bytes are 2 copies of 16 random bits */ ! 1092: textbuf[0] = randombyte(); ! 1093: textbuf[1] = randombyte(); ! 1094: textbuf[2] = textbuf[0]; ! 1095: textbuf[3] = textbuf[1]; ! 1096: basscfb(textbuf,KEYCHECKLENGTH); ! 1097: fwrite(textbuf,1,KEYCHECKLENGTH,g); ! 1098: } ! 1099: else /* decrypt-- check for key check bytes */ ! 1100: { /* See if there are 2 copies of 16 random bits */ ! 1101: count = fread(textbuf,1,KEYCHECKLENGTH,f); ! 1102: if (count==KEYCHECKLENGTH) ! 1103: { basscfb(textbuf,KEYCHECKLENGTH); ! 1104: if ((textbuf[0] != textbuf[2]) ! 1105: || (textbuf[1] != textbuf[3])) ! 1106: { return(-2); /* bad key error */ ! 1107: } ! 1108: } ! 1109: else /* file too short for key check bytes */ ! 1110: return(-3); /* error of the weird kind */ ! 1111: } ! 1112: ! 1113: ! 1114: do /* read and write the whole file in CFB mode... */ ! 1115: { count = fread(textbuf,1,diskbufsize,f); ! 1116: if (count>0) ! 1117: { basscfb(textbuf,count); ! 1118: fwrite(textbuf,1,count,g); ! 1119: } ! 1120: /* if text block was short, exit loop */ ! 1121: } while (count==diskbufsize); ! 1122: ! 1123: closebass(); /* release BassOmatic resources */ ! 1124: burn(textbuf); /* burn sensitive data on stack */ ! 1125: return(0); /* should always take normal return */ ! 1126: } /* bass_file */ ! 1127: ! 1128: ! 1129: ! 1130: int read_mpi(unitptr r, FILE *f, boolean adjust_precision, boolean scrambled) ! 1131: /* Read a mutiprecision integer from a file. ! 1132: adjust_precision is TRUE iff we should call set_precision to the ! 1133: size of the number read in. ! 1134: scrambled is TRUE iff field is encrypted (protects secret key fields). ! 1135: Returns the bitcount of the number read in, or returns a negative ! 1136: number if an error is detected. ! 1137: */ ! 1138: { byte buf[MAX_BYTE_PRECISION+2]; ! 1139: int count; ! 1140: word16 bytecount,bitcount,lowcount,highcount; ! 1141: ! 1142: mp_init(r,0); ! 1143: ! 1144: if ((count = fread(buf,1,2,f)) < 2) ! 1145: return (-1); /* error -- read failure or premature eof */ ! 1146: ! 1147: /* Assumes external format is LSB-first */ ! 1148: bitcount = (((word16) buf[1]) << 8) + (word16) buf[0]; ! 1149: if (bits2units(bitcount) > global_precision) ! 1150: return(-1); /* error -- possible corrupted bitcount */ ! 1151: ! 1152: bytecount = bits2bytes(bitcount); ! 1153: ! 1154: count = fread(buf+2,1,bytecount,f); ! 1155: if (count < bytecount) ! 1156: return(-1); /* error -- premature eof */ ! 1157: ! 1158: if (scrambled) /* decrypt the field */ ! 1159: basscfb(buf+2,bytecount); ! 1160: ! 1161: /* We assume that the bitcount prefix we read is an exact ! 1162: bitcount, not rounded up to the next byte boundary. ! 1163: Otherwise we would have to call mpi2reg, then call ! 1164: countbits, then call set_precision, then recall mpi2reg ! 1165: again. ! 1166: */ ! 1167: if (adjust_precision && bytecount) ! 1168: { /* set the precision to that specified by the number read. */ ! 1169: set_precision(bits2units(bitcount+SLOP_BITS)); ! 1170: /* Now that precision is optimally set, call mpi2reg */ ! 1171: } ! 1172: ! 1173: mpi2reg(r,buf); /* convert to internal format */ ! 1174: burn(buf); /* burn sensitive data on stack */ ! 1175: return (bitcount); ! 1176: } /* read_mpi */ ! 1177: ! 1178: ! 1179: ! 1180: void write_mpi(unitptr n, FILE *f, boolean scrambled) ! 1181: /* Write a multiprecision integer to a file. ! 1182: scrambled is TRUE iff we should scramble field on the way out, ! 1183: which is used to protect secret key fields. ! 1184: */ ! 1185: { byte buf[MAX_BYTE_PRECISION+2]; ! 1186: short bytecount; ! 1187: bytecount = reg2mpi(buf,n); ! 1188: if (scrambled) /* encrypt the field, skipping over the bitcount */ ! 1189: basscfb(buf+2,bytecount); ! 1190: fwrite(buf,1,bytecount+2,f); ! 1191: burn(buf); /* burn sensitive data on stack */ ! 1192: } /* write_mpi */ ! 1193: ! 1194: ! 1195: ! 1196: void showkeyID(byte *buf) ! 1197: /* Print key fragment, which is an abbreviation or "fingerprint" ! 1198: of the key. ! 1199: Show LEAST significant 64 bits (KEYFRAGSIZE bytes) of modulus, ! 1200: LSB last. Yes, that's LSB LAST. ! 1201: */ ! 1202: { short i,j; ! 1203: /* fputc('[',stderr); */ ! 1204: j = KEYFRAGSIZE; ! 1205: for (i=KEYFRAGSIZE-1; i>=0; i--) /* print LSB last */ ! 1206: { if (--j < 3) /* only show bottom 3 bytes of keyID */ ! 1207: fprintf(stderr,"%02X",buf[i]); ! 1208: } ! 1209: /* fputc(']',stderr); */ ! 1210: } /* showkeyID */ ! 1211: ! 1212: ! 1213: ! 1214: void extract_keyID(byteptr keyID, unitptr n) ! 1215: /* Extract key fragment from modulus n. keyID byte array must be ! 1216: at least KEYFRAGSIZE bytes long. ! 1217: */ ! 1218: { byte buf[MAX_BYTE_PRECISION+2]; ! 1219: short i, j; ! 1220: ! 1221: fill0(buf,KEYFRAGSIZE+2); /* in case n is too short */ ! 1222: reg2mpi(buf,n); /* MUST be at least KEYFRAGSIZE long */ ! 1223: /* For low-byte-first keyID format, start of keyID is: */ ! 1224: i = 2; /* skip over the 2 bytes of bitcount */ ! 1225: for (j=0; j<KEYFRAGSIZE; ) ! 1226: keyID[j++] = buf[i++]; ! 1227: ! 1228: } /* extract_keyID */ ! 1229: ! 1230: ! 1231: ! 1232: void writekeyID(unitptr n, FILE *f) ! 1233: /* Write message prefix keyID to a file. ! 1234: n is key modulus from which to extract keyID. ! 1235: */ ! 1236: { byte keyID[KEYFRAGSIZE]; ! 1237: extract_keyID(keyID, n); ! 1238: fwrite(keyID,1,KEYFRAGSIZE,f); ! 1239: } /* writekeyID */ ! 1240: ! 1241: ! 1242: ! 1243: void showkeyID2(unitptr n) ! 1244: /* Derive the key abbreviation fragment from the modulus n, and print it. ! 1245: n is key modulus from which to extract keyID. ! 1246: */ ! 1247: { byte keyID[KEYFRAGSIZE]; ! 1248: extract_keyID(keyID, n); ! 1249: showkeyID(keyID); ! 1250: } /* showkeyID2 */ ! 1251: ! 1252: ! 1253: ! 1254: boolean checkkeyID(byte *keyID, unitptr n) ! 1255: /* Compare specified keyID with one derived from actual key modulus n. */ ! 1256: { ! 1257: byte keyID0[KEYFRAGSIZE]; ! 1258: if (keyID==NULL) /* no key ID -- assume a good match */ ! 1259: return (TRUE); ! 1260: extract_keyID(keyID0, n); ! 1261: return(equal_buffers(keyID,keyID0,KEYFRAGSIZE)); ! 1262: } /* checkkeyID */ ! 1263: ! 1264: ! 1265: ! 1266: /* external function prototype, from rsaio.c */ ! 1267: void dump_unit_array(string s, unitptr r); ! 1268: ! 1269: ! 1270: short writekeyfile(char *fname, boolean hidekey, byte *timestamp, byte *userid, ! 1271: unitptr n, unitptr e, unitptr d, unitptr p, unitptr q, unitptr u) ! 1272: /* Write key components p, q, n, e, d, and u to specified file. ! 1273: hidekey is TRUE iff key should be encrypted. ! 1274: userid is a length-prefixed Pascal-type character string. ! 1275: */ ! 1276: { FILE *f; ! 1277: byte ctb,c; ! 1278: word16 cert_length; ! 1279: /* open file f for write, in binary (not text) mode...*/ ! 1280: if ((f = fopen(fname,"wb")) == NULL) ! 1281: { fprintf(stderr,"\n\aCan't create key file '%s'\n",fname); ! 1282: return(-1); ! 1283: } ! 1284: else ! 1285: { ! 1286: /*** Begin key certificate header fields ***/ ! 1287: if (d==NULL) ! 1288: { /* public key certificate */ ! 1289: ctb = CTB_CERT_PUBKEY; ! 1290: cert_length = SIZEOF_TIMESTAMP + userid[0]+1 + (countbytes(n)+2) ! 1291: + (countbytes(e)+2); /* no crc16 */ ! 1292: } /* public key certificate */ ! 1293: else ! 1294: { /* secret key certificate */ ! 1295: ctb = CTB_CERT_SECKEY; ! 1296: cert_length = SIZEOF_TIMESTAMP + userid[0]+1 ! 1297: + (countbytes(n)+2) ! 1298: + (countbytes(e)+2) + (countbytes(d)+2) ! 1299: + (countbytes(p)+2) + (countbytes(q)+2) ! 1300: + (countbytes(u)+2); /* no crc16 */ ! 1301: ! 1302: } /* secret key certificate */ ! 1303: ! 1304: fwrite(&ctb,1,1,f); /* write key certificate header byte */ ! 1305: convert(cert_length); /* convert to external byteorder */ ! 1306: fwrite(&cert_length,1,sizeof(cert_length),f); ! 1307: hilo_swap(timestamp,4); /* convert to external LSB-first form */ ! 1308: fwrite(timestamp,1,4,f); /* write certificate timestamp */ ! 1309: hilo_swap(timestamp,4); /* convert back to internal form */ ! 1310: fwrite(userid,1,userid[0]+1,f); /* write user ID */ ! 1311: write_mpi(n,f,FALSE); ! 1312: write_mpi(e,f,FALSE); ! 1313: ! 1314: if (is_secret_key(ctb)) /* secret key */ ! 1315: { ! 1316: write_mpi(d,f,hidekey); ! 1317: write_mpi(p,f,hidekey); ! 1318: write_mpi(q,f,hidekey); ! 1319: write_mpi(u,f,hidekey); ! 1320: } ! 1321: fclose(f); ! 1322: #ifdef DEBUG ! 1323: fprintf(stderr,"\n%d-bit %s key written to file '%s'.\n", ! 1324: countbits(n), ! 1325: is_secret_key(ctb) ? "secret" : "public" , ! 1326: fname); ! 1327: #endif ! 1328: return(0); ! 1329: } ! 1330: } /* writekeyfile */ ! 1331: ! 1332: ! 1333: /*======================================================================*/ ! 1334: ! 1335: ! 1336: int get_header_info_from_file(char *infile, byte *header, int count) ! 1337: /* Reads the first count bytes from infile into header. */ ! 1338: { FILE *f; ! 1339: fill0(header,count); ! 1340: /* open file f for read, in binary (not text) mode...*/ ! 1341: if ((f = fopen(infile,"rb")) == NULL) ! 1342: return(-1); ! 1343: /* read Cipher Type Byte, and maybe more */ ! 1344: count = fread(header,1,count,f); ! 1345: fclose(f); ! 1346: return(count); /* normal return */ ! 1347: } /* get_header_info_from_file */ ! 1348: ! 1349: ! 1350: ! 1351: short readkeypacket(FILE *f, boolean hidekey, byte *ctbyte, ! 1352: byte *timestamp, char *userid, ! 1353: unitptr n ,unitptr e, unitptr d, unitptr p, unitptr q, unitptr u) ! 1354: /* Reads a key certificate from the current file position of file f. ! 1355: It will return the ctb, timestamp, userid, public key components ! 1356: n and e, and if the secret key components are present in the ! 1357: certificate and d is not a NULL, it will read and return d, p, q, ! 1358: and u. The file pointer is left positioned after the certificate. ! 1359: hidekey is TRUE iff key is expected to be encrypted. ! 1360: */ ! 1361: { ! 1362: byte ctb; ! 1363: word32 cert_length; ! 1364: long file_position; ! 1365: int count; ! 1366: ! 1367: set_precision(MAX_UNIT_PRECISION); /* safest opening assumption */ ! 1368: ! 1369: /*** Begin certificate header fields ***/ ! 1370: *ctbyte = 0; /* assume no ctbyte for caller at first */ ! 1371: count = fread(&ctb,1,1,f); /* read key certificate CTB byte */ ! 1372: if (count==0) return(-1); /* premature eof */ ! 1373: *ctbyte = ctb; /* returns type to caller */ ! 1374: if ((ctb != CTB_CERT_PUBKEY) && (ctb != CTB_CERT_SECKEY)) ! 1375: return(-2); /* not a key certificate */ ! 1376: ! 1377: cert_length = getpastlength(ctb, f); /* read certificate length */ ! 1378: ! 1379: if (cert_length > MAX_KEYCERT_LENGTH-3) ! 1380: return(-3); /* bad length */ ! 1381: ! 1382: fread(timestamp,1,4,f); /* read certificate timestamp */ ! 1383: /* note that hilo_swap does nothing if this is a LSB-first CPU */ ! 1384: hilo_swap(timestamp,4); /* convert from external LSB-first form */ ! 1385: count = fread(userid,1,1,f); /* read user ID length byte */ ! 1386: if (count==0) return(-1); /* premature eof */ ! 1387: fread(userid+1,1,userid[0],f); /* read rest of user ID */ ! 1388: /*** End certificate header fields ***/ ! 1389: ! 1390: /* We're past certificate headers, now look at some key material...*/ ! 1391: ! 1392: if (read_mpi(n,f,TRUE,FALSE) < 0) ! 1393: return(-4); /* data corrupted, return error */ ! 1394: ! 1395: /* Note that precision was adjusted for n */ ! 1396: ! 1397: if (read_mpi(e,f,FALSE,FALSE) < 0) ! 1398: return(-4); /* data corrupted, error return */ ! 1399: ! 1400: cert_length -= SIZEOF_TIMESTAMP + userid[0]+1 + ! 1401: (countbytes(n)+2) + (countbytes(e)+2); ! 1402: ! 1403: if (d==NULL) /* skip rest of this key certificate */ ! 1404: { fseek(f, cert_length, SEEK_CUR); ! 1405: cert_length = 0; /* because we are skipping secret fields */ ! 1406: } ! 1407: else /* d is not NULL */ ! 1408: { if (is_secret_key(ctb)) ! 1409: { ! 1410: if (read_mpi(d,f,FALSE,hidekey) < 0) ! 1411: return(-4); /* data corrupted, error return */ ! 1412: if (read_mpi(p,f,FALSE,hidekey) < 0) ! 1413: return(-4); /* data corrupted, error return */ ! 1414: if (read_mpi(q,f,FALSE,hidekey) < 0) ! 1415: return(-4); /* data corrupted, error return */ ! 1416: ! 1417: /* use register 'u' briefly as scratchpad */ ! 1418: mp_mult(u,p,q); /* compare p*q against n */ ! 1419: if (mp_compare(n,u)!=0) /* bad pass phrase? */ ! 1420: return(-5); /* possible bad pass phrase, error return */ ! 1421: /* now read in real u */ ! 1422: if (read_mpi(u,f,FALSE,hidekey) < 0) ! 1423: return(-4); /* data corrupted, error return */ ! 1424: ! 1425: cert_length -= (countbytes(d)+2) + (countbytes(p)+2) ! 1426: + (countbytes(q)+2) + (countbytes(u)+2); ! 1427: ! 1428: } /* secret key */ ! 1429: else /* not a secret key */ ! 1430: { mp_init(d,0); ! 1431: mp_init(p,0); ! 1432: mp_init(q,0); ! 1433: mp_init(u,0); ! 1434: } ! 1435: } /* d != NULL */ ! 1436: ! 1437: if (cert_length != 0) ! 1438: { fprintf(stderr,"\n\aCorrupted key. Bad length, off by %d bytes.\n", ! 1439: (signed int) cert_length); ! 1440: return(-4); /* data corrupted, error return */ ! 1441: } ! 1442: ! 1443: return(0); /* normal return */ ! 1444: ! 1445: } /* readkeypacket */ ! 1446: ! 1447: ! 1448: ! 1449: int getpublickey(boolean giveup, boolean showkey, char *keyfile, ! 1450: long *file_position, int *pktlen, byte *keyID, byte *timestamp, ! 1451: byte *userid, unitptr n, unitptr e) ! 1452: /* keyID contains key fragment we expect to find in keyfile. ! 1453: If keyID is NULL, then userid contains a C string search target of ! 1454: userid to find in keyfile. ! 1455: keyfile is the file to begin search in, and it may be modified ! 1456: to indicate true filename of where the key was found. It can be ! 1457: either a public key file or a secret key file. ! 1458: file_position is returned as the byte offset within the keyfile ! 1459: that the key was found at. ! 1460: giveup is TRUE iff we are just going to do a single file search only. ! 1461: */ ! 1462: { ! 1463: int keytype; /* 1 for secret key, 0 for public key */ ! 1464: byte ctb; /* returned by readkeypacket */ ! 1465: FILE *f; ! 1466: int status; ! 1467: boolean keyfound = FALSE; ! 1468: boolean secret; /* indicates we are called by getsecretkey */ ! 1469: char userid0[256]; /* C string format */ ! 1470: ! 1471: userid0[0] = '\0'; ! 1472: secret = strcontains(keyfile,SEC_EXTENSION); ! 1473: ! 1474: if (keyID==NULL) /* then userid has search target */ ! 1475: strcpy(userid0,userid); ! 1476: ! 1477: top: ! 1478: if (secret) ! 1479: default_extension(keyfile,SEC_EXTENSION); ! 1480: else ! 1481: default_extension(keyfile,PUB_EXTENSION); ! 1482: ! 1483: if (!file_exists(keyfile)) ! 1484: { if (giveup) ! 1485: return(-1); /* give up, error return */ ! 1486: fprintf(stderr,"\nKeyring file '%s' does not exist. ",keyfile); ! 1487: goto nogood; ! 1488: } ! 1489: if (verbose) fprintf(stderr,"\nSearching key ring file '%s'.\n",keyfile); ! 1490: ! 1491: /* open file f for read, in binary (not text) mode...*/ ! 1492: if ((f = fopen(keyfile,"rb")) == NULL) ! 1493: return(-1); /* error return */ ! 1494: ! 1495: while (TRUE) ! 1496: { ! 1497: *file_position = ftell(f); ! 1498: status = readkeypacket(f,FALSE,&ctb,timestamp,userid,n,e, ! 1499: NULL,NULL,NULL,NULL); ! 1500: /* Note that readkeypacket has called set_precision */ ! 1501: ! 1502: if (status == -1) /* end of file */ ! 1503: break; ! 1504: ! 1505: if (status < -1) ! 1506: { fprintf(stderr,"\n\aCould not read key from file '%s'.\n", ! 1507: keyfile); ! 1508: fclose(f); /* close key file */ ! 1509: return(-1); ! 1510: } ! 1511: ! 1512: /* keyID contains key fragment. Check it against n from keyfile. */ ! 1513: if (keyID!=NULL) ! 1514: keyfound = checkkeyID(keyID,n); ! 1515: else ! 1516: { /* userid0 is already a C string */ ! 1517: PascalToC(userid); /* for C string functions */ ! 1518: keyfound = strcontains(userid,userid0); /* any matching subset? */ ! 1519: /* keyfound = (strcmp(userid,userid0)==0); /* exact match? */ ! 1520: CToPascal(userid); ! 1521: } ! 1522: ! 1523: if (keyfound) ! 1524: { *pktlen = (ftell(f) - *file_position); ! 1525: if (showkey) ! 1526: { PascalToC(userid); /* for display */ ! 1527: fprintf(stderr,"\nKey for user ID: %s\n",userid); ! 1528: CToPascal(userid); ! 1529: fprintf(stderr,"%d-bit key, Key ID ",countbits(n)); ! 1530: showkeyID2(n); ! 1531: fprintf(stderr,", created %s",ctime((long *)timestamp)); ! 1532: } ! 1533: fclose(f); ! 1534: return(0); /* normal return */ ! 1535: } ! 1536: } /* while TRUE */ ! 1537: ! 1538: fclose(f); /* close key file */ ! 1539: ! 1540: if (giveup) ! 1541: return(-1); /* give up, error return */ ! 1542: ! 1543: if (keyID!=NULL) ! 1544: { ! 1545: fprintf(stderr,"\n\aKey matching expected Key ID "); ! 1546: showkeyID(keyID); ! 1547: fprintf(stderr," not found in file '%s'.\n",keyfile); ! 1548: } ! 1549: else ! 1550: { fprintf(stderr,"\n\aKey matching userid '%s' not found in file '%s'.\n", ! 1551: userid0,keyfile); ! 1552: } ! 1553: ! 1554: nogood: ! 1555: if (giveup) ! 1556: return(-1); /* give up, error return */ ! 1557: ! 1558: if (secret) ! 1559: fprintf(stderr,"Enter secret key filename: "); ! 1560: else ! 1561: fprintf(stderr,"Enter public key filename: "); ! 1562: ! 1563: getstring(keyfile,59,TRUE); /* echo keyboard input */ ! 1564: if (strlen(keyfile) > 0) ! 1565: goto top; ! 1566: ! 1567: return(-1); /* give up, error return */ ! 1568: ! 1569: } /* getpublickey */ ! 1570: ! 1571: ! 1572: ! 1573: int getsecretkey(byte *keyID, byte *timestamp, byte *userid, ! 1574: unitptr n, unitptr e, unitptr d, unitptr p, unitptr q, unitptr u) ! 1575: /* keyID contains key fragment we expect to find in keyfile. ! 1576: If keyID is NULL, then userid contains search target of ! 1577: userid to find in keyfile. ! 1578: */ ! 1579: { ! 1580: byte ctb; /* returned by readkeypacket */ ! 1581: FILE *f; ! 1582: char keyfile[64]; /* for getpublickey */ ! 1583: long file_position; ! 1584: int pktlen; /* unused, just to satisfy getpublickey */ ! 1585: int status; ! 1586: boolean hidekey = FALSE; /* TRUE iff secret key is encrypted */ ! 1587: char passphrase[256]; ! 1588: byte iv[256]; /* for BassOmatic CFB mode */ ! 1589: int guesses = 3; ! 1590: ! 1591: buildfilename(keyfile,SECRET_KEYRING_FILENAME); /* use default pathname */ ! 1592: ! 1593: status = getpublickey(FALSE, TRUE, keyfile, &file_position, &pktlen, ! 1594: keyID, timestamp, userid, n, e); ! 1595: if (status < 0) ! 1596: return(status); /* error return */ ! 1597: ! 1598: /* open file f for read, in binary (not text) mode...*/ ! 1599: if ((f = fopen(keyfile,"rb")) == NULL) ! 1600: return(-1); /* error return */ ! 1601: ! 1602: /* First guess is null password, so hidekey is FALSE */ ! 1603: ! 1604: do /* until good password */ ! 1605: { /* init CFB BassOmatic key */ ! 1606: if (hidekey) ! 1607: { fill0(iv,256); /* define initialization vector IV as 0 */ ! 1608: if ( initcfb(iv,passphrase,string_length(passphrase),TRUE) < 0 ) ! 1609: { fclose(f); /* close key file */ ! 1610: return(-1); ! 1611: } ! 1612: } ! 1613: burn(passphrase); /* burn sensitive data on stack */ ! 1614: fseek(f,file_position,SEEK_SET); /* reposition file to key */ ! 1615: status = readkeypacket(f,hidekey,&ctb,timestamp,userid,n,e,d,p,q,u); ! 1616: if (hidekey) ! 1617: closebass(); /* release BassOmatic resources */ ! 1618: ! 1619: if (status == -5) /* bad pass phrase status */ ! 1620: { if (guesses!=3) /* not first guess of null password? */ ! 1621: fprintf(stderr,"\n\aUnreadable secret key. Possible bad pass phrase.\n"); ! 1622: if (--guesses) /* not ran out of guesses yet */ ! 1623: { fprintf(stderr,"\nYou need a pass phrase to unlock your RSA secret key. "); ! 1624: hidekey = (getpassword(passphrase,1,0x0f) > 0); ! 1625: continue; /* take it from the top */ ! 1626: } /* more guesses to go */ ! 1627: } ! 1628: if (status < 0) ! 1629: { fprintf(stderr,"\n\aCould not read key from file '%s'.\n", ! 1630: keyfile); ! 1631: fclose(f); /* close key file */ ! 1632: return(-1); ! 1633: } ! 1634: } while (status < 0); /* until key reads OK, with good password */ ! 1635: ! 1636: fclose(f); /* close key file */ ! 1637: ! 1638: if (!hidekey) ! 1639: fprintf(stderr,"\nAdvisory warning: This RSA secret key is not protected by a passphrase.\n"); ! 1640: else ! 1641: fprintf(stderr,"Pass phrase is good. "); ! 1642: ! 1643: /* Note that readkeypacket has called set_precision */ ! 1644: ! 1645: if (testeq(d,0)) /* didn't get secret key components */ ! 1646: { fprintf(stderr,"\n\aKey file '%s' is not a secret key file.\n",keyfile); ! 1647: return(-1); ! 1648: } ! 1649: ! 1650: return(0); /* normal return */ ! 1651: ! 1652: } /* getsecretkey */ ! 1653: ! 1654: ! 1655: ! 1656: int make_signature_certificate(byte *certificate, MDstruct *MD, ! 1657: byte *userid, unitptr n, unitptr d, unitptr p, unitptr q, unitptr u) ! 1658: /* Constructs a signed message digest in a signature certificate. ! 1659: Returns total certificate length in bytes, or returns negative ! 1660: error status. ! 1661: */ ! 1662: { ! 1663: byte inbuf[MAX_BYTE_PRECISION], outbuf[MAX_BYTE_PRECISION]; ! 1664: byte mdpacket[32]; ! 1665: byte *mdbufptr; ! 1666: int i,j,certificate_length,blocksize,bytecount; ! 1667: word16 useridlength,certsig_length,mdp_length,ske_length; ! 1668: word32 tstamp; byte *timestamp = (byte *) &tstamp; ! 1669: byte keyID[KEYFRAGSIZE]; ! 1670: ! 1671: /* Note that RSA key must be at least big enough to encipher a ! 1672: complete message digest packet in a single RSA block. */ ! 1673: ! 1674: blocksize = countbytes(n)-1; /* size of a plaintext block */ ! 1675: if (blocksize < 31) ! 1676: { fprintf(stderr,"\n\aError: RSA key length must be at least 256 bits.\n"); ! 1677: return(-1); ! 1678: } ! 1679: ! 1680: get_timestamp(timestamp); /* Timestamp when signature was made */ ! 1681: hilo_swap(timestamp,4); /* convert to external LSB-first form */ ! 1682: ! 1683: fill0(mdpacket,sizeof(mdpacket)); ! 1684: mdpacket[0] = CTB_MD; /* Message Digest type */ ! 1685: /* mdp_length includes algorithm byte, MD, and timestamp. */ ! 1686: mdp_length = 1+16+4; /* message digest packet length */ ! 1687: /* MD packet length does not include itself or CTB prefix: */ ! 1688: mdpacket[1] = mdp_length; ! 1689: mdpacket[2] = MD4_ALGORITHM_BYTE; /* select MD4 algorithm */ ! 1690: ! 1691: mdbufptr = (byte *) (MD->buffer); /* point at actual message digest */ ! 1692: for (i=0; i<16; i++) ! 1693: mdpacket[i+3] = *mdbufptr++; /* Assumes LSB-first order */ ! 1694: /* Stick a timestamp in here, before signing... */ ! 1695: /* timestamp already in external format */ ! 1696: for (j=0; j<SIZEOF_TIMESTAMP; j++,i++) ! 1697: mdpacket[i+3] = timestamp[j]; ! 1698: ! 1699: /* Pre-block mdpacket, and convert to INTERNAL byte order: */ ! 1700: preblock((unitptr)inbuf, mdpacket, mdp_length+2, n, TRUE, NULL); ! 1701: ! 1702: fprintf(stderr,"Just a moment-- "); /* RSA will take a while. */ ! 1703: ! 1704: /* do RSA signature calculation: */ ! 1705: rsa_decrypt((unitptr)outbuf,(unitptr)inbuf,d,p,q,u); ! 1706: ! 1707: bytecount = reg2mpi(outbuf,(unitptr)outbuf); /* convert to external format */ ! 1708: /* outbuf now contains a MDSB in external byteorder form. ! 1709: Now make a complete signature certificate from this. ! 1710: */ ! 1711: ! 1712: certificate_length = 0; ! 1713: ! 1714: /* SKE is Secret Key Encryption (signed). Append CTB for signed msg. */ ! 1715: certificate[certificate_length++] = CTB_SKE; ! 1716: ! 1717: ske_length = KEYFRAGSIZE + bytecount+2; ! 1718: /* SKE packet length does not include itself or CTB prefix: */ ! 1719: certificate[certificate_length++] = ske_length & 0xff; ! 1720: certificate[certificate_length++] = (ske_length >> 8) & 0xff; ! 1721: ! 1722: /* Now append keyID... */ ! 1723: extract_keyID(keyID, n); /* gets keyID */ ! 1724: for (i=0; i<KEYFRAGSIZE; i++) ! 1725: certificate[certificate_length++] = keyID[i]; ! 1726: ! 1727: /* Now append the RSA-signed message digest packet: */ ! 1728: for (i=0; i<bytecount+2; i++) ! 1729: certificate[certificate_length++] = outbuf[i]; ! 1730: ! 1731: fputc('.',stderr); /* Signal RSA signature completion. */ ! 1732: ! 1733: burn(inbuf); /* burn sensitive data on stack */ ! 1734: burn(outbuf); /* burn sensitive data on stack */ ! 1735: ! 1736: return(certificate_length); /* return length of certificate in bytes */ ! 1737: ! 1738: } /* make_signature_certificate */ ! 1739: ! 1740: ! 1741: /*======================================================================*/ ! 1742: ! 1743: ! 1744: int signfile(boolean nested, boolean separate_signature, ! 1745: char *mcguffin, char *infile, char *outfile) ! 1746: /* Write an RSA-signed message digest of input file to specified ! 1747: output file, and append input file to output file. ! 1748: separate_signature is TRUE iff we should not append the ! 1749: plaintext to the output signature certificate. ! 1750: */ ! 1751: { ! 1752: FILE *f; ! 1753: FILE *g; ! 1754: byte ctb; /* Cipher Type Byte */ ! 1755: int certificate_length; /* signature certificate length */ ! 1756: byte certificate[MAX_SIGCERT_LENGTH]; ! 1757: ! 1758: { /* temporary scope for some buffers */ ! 1759: word32 tstamp; byte *timestamp = (byte *) &tstamp; /* key certificate timestamp */ ! 1760: byte userid[256]; ! 1761: MDstruct MD; ! 1762: unit n[MAX_UNIT_PRECISION], e[MAX_UNIT_PRECISION], d[MAX_UNIT_PRECISION]; ! 1763: unit p[MAX_UNIT_PRECISION], q[MAX_UNIT_PRECISION], u[MAX_UNIT_PRECISION]; ! 1764: ! 1765: set_precision(MAX_UNIT_PRECISION); /* safest opening assumption */ ! 1766: ! 1767: if (verbose) ! 1768: fprintf(stderr,"\nPlaintext file: %s, signature file: %s\n", ! 1769: infile,outfile); ! 1770: ! 1771: if (MDfile(&MD, infile) < 0) ! 1772: return(-1); /* problem with input file. error return */ ! 1773: ! 1774: strcpy(userid,mcguffin); /* Who we are looking for */ ! 1775: ! 1776: if (getsecretkey(NULL, timestamp, userid, n, e, d, p, q, u) < 0) ! 1777: return(-1); /* problem with secret key file. error return. */ ! 1778: ! 1779: certificate_length = make_signature_certificate(certificate, &MD, userid, n, d, p, q, u); ! 1780: ! 1781: } /* end of scope for some buffers */ ! 1782: ! 1783: /* open file f for read, in binary (not text) mode...*/ ! 1784: if ((f = fopen(infile,"rb")) == NULL) ! 1785: { fprintf(stderr,"\n\aCan't open plaintext file '%s'\n",infile); ! 1786: return(-1); ! 1787: } ! 1788: ! 1789: /* open file g for write, in binary (not text) mode...*/ ! 1790: if ((g = fopen(outfile,"wb")) == NULL) ! 1791: { fprintf(stderr,"\n\aCan't create signature file '%s'\n",outfile); ! 1792: fclose(f); ! 1793: return(-1); ! 1794: } ! 1795: ! 1796: /* write out certificate record to outfile ... */ ! 1797: fwrite(certificate,1,certificate_length,g); ! 1798: ! 1799: if (!separate_signature) ! 1800: { ! 1801: if (!nested) ! 1802: { ctb = CTB_LITERAL; ! 1803: fwrite( &ctb, 1, 1, g ); /* write LITERAL CTB */ ! 1804: /* No CTB packet length specified means indefinite length. */ ! 1805: } ! 1806: copyfile(f,g,-1UL); /* copy rest of file from file f to g */ ! 1807: } ! 1808: ! 1809: fclose(g); ! 1810: fclose(f); ! 1811: return(0); /* normal return */ ! 1812: ! 1813: } /* signfile */ ! 1814: ! 1815: ! 1816: /*======================================================================*/ ! 1817: ! 1818: int check_signaturefile(char *infile, char *outfile) ! 1819: { ! 1820: byte ctb,ctb2; /* Cipher Type Bytes */ ! 1821: char keyfile[64]; /* for getpublickey */ ! 1822: long fp; /* unused, just to satisfy getpublickey */ ! 1823: int pktlen; /* unused, just to satisfy getpublickey */ ! 1824: FILE *f; ! 1825: FILE *g; ! 1826: long start_text; /* marks file position */ ! 1827: int i,count,blocksize; ! 1828: word16 SKElength, cert_length; ! 1829: word32 LITlength; ! 1830: int certificate_length; /* signature certificate length */ ! 1831: byte certbuf[MAX_SIGCERT_LENGTH]; ! 1832: byteptr certificate; /* for parsing certificate buffer */ ! 1833: unit n[MAX_UNIT_PRECISION], e[MAX_UNIT_PRECISION]; ! 1834: byte inbuf[MAX_BYTE_PRECISION]; ! 1835: byte outbuf[MAX_BYTE_PRECISION]; ! 1836: byte keyID[KEYFRAGSIZE]; ! 1837: word32 tstamp; ! 1838: byte *timestamp = (byte *) &tstamp; /* key certificate timestamp */ ! 1839: byte userid[256]; ! 1840: MDstruct MD; ! 1841: boolean separate_signature; ! 1842: ! 1843: fill0( keyID, KEYFRAGSIZE ); ! 1844: ! 1845: set_precision(MAX_UNIT_PRECISION); /* safest opening assumption */ ! 1846: ! 1847: buildfilename(keyfile,PUBLIC_KEYRING_FILENAME); /* use default pathname */ ! 1848: ! 1849: if (verbose) ! 1850: fprintf(stderr,"\nSignature file: %s, output file: %s\n", ! 1851: infile,outfile); ! 1852: ! 1853: /* open file f for read, in binary (not text) mode...*/ ! 1854: if ((f = fopen(infile,"rb")) == NULL) ! 1855: { fprintf(stderr,"\n\aCan't open ciphertext file '%s'\n",infile); ! 1856: return(-1); ! 1857: } ! 1858: ! 1859: /******************** Read header CTB and length field ******************/ ! 1860: ! 1861: fread(&ctb,1,1,f); /* read certificate CTB byte */ ! 1862: certificate = certbuf; ! 1863: *certificate++ = ctb; /* copy ctb into certificate */ ! 1864: ! 1865: if (!is_ctb(ctb)) ! 1866: { fprintf(stderr,"\n\a'%s' is not a cipher file.\n",infile); ! 1867: goto err1; ! 1868: } ! 1869: ! 1870: cert_length = getpastlength(ctb, f); /* read certificate length */ ! 1871: certificate += ctb_llength(ctb); /* either 1, 2, 4, or 8 */ ! 1872: if (cert_length > MAX_SIGCERT_LENGTH-3) ! 1873: { fprintf(stderr,"\n\aSignature file '%s' has huge packet length field.\n",infile); ! 1874: goto err1; ! 1875: } ! 1876: ! 1877: /* read whole certificate: */ ! 1878: if (fread((byteptr) certificate, 1, cert_length, f) < cert_length) ! 1879: { fprintf(stderr,"\n\aSignature file '%s' has bad packet length field.\n",infile); ! 1880: goto err1; ! 1881: } ! 1882: ! 1883: if (!is_ctb_type(ctb,CTB_SKE_TYPE)) ! 1884: { fprintf(stderr,"\n\a'%s' is not a signature file.\n",infile); ! 1885: goto err1; ! 1886: } ! 1887: ! 1888: for (i=0; i<KEYFRAGSIZE; i++) ! 1889: keyID[i] = *certificate++; /* copy rest of key fragment */ ! 1890: ! 1891: mpi2reg((unitptr)inbuf,certificate); /* get signed message digest */ ! 1892: certificate += countbytes((unitptr)inbuf)+2; ! 1893: ! 1894: if ((certificate-certbuf) != cert_length+3) ! 1895: { fprintf(stderr,"\n\aBad length in signature certificate. Off by %d.\n", ! 1896: (signed int) ((certificate-certbuf) - (cert_length+3))); ! 1897: goto err1; ! 1898: } ! 1899: ! 1900: start_text = ftell(f); /* mark position of text for later */ ! 1901: ! 1902: if (fread(outbuf,1,1,f) < 1) /* see if any plaintext is there */ ! 1903: { /* Signature certificate has no plaintext following it. ! 1904: Must be in another file. Go look. */ ! 1905: separate_signature = TRUE; ! 1906: fclose(f); ! 1907: fprintf(stderr,"\nFile '%s' has signature, but with no text.",infile); ! 1908: if (file_exists(outfile)) ! 1909: { fprintf(stderr,"\nText is assumed to be in file '%s'.\n",outfile); ! 1910: } ! 1911: else ! 1912: { fprintf(stderr,"\nPlease enter filename of text that signature applies to: "); ! 1913: getstring(outfile,59,TRUE); /* echo keyboard */ ! 1914: if (strlen(outfile) == 0) ! 1915: return(-1); ! 1916: } ! 1917: /* open file f for read, in binary (not text) mode...*/ ! 1918: if ((f = fopen(outfile,"rb")) == NULL) ! 1919: { fprintf(stderr,"\n\aCan't open file '%s'\n",outfile); ! 1920: return(-1); ! 1921: } ! 1922: start_text = ftell(f); /* mark position of text for later */ ! 1923: } /* had to open new input file */ ! 1924: else ! 1925: { separate_signature = FALSE; ! 1926: /* We just read 1 byte, so outbuf[0] should contain a ctb, ! 1927: maybe a CTB_LITERAL byte. */ ! 1928: ctb2 = outbuf[0]; ! 1929: if (is_ctb(ctb2) && is_ctb_type(ctb2,CTB_LITERAL_TYPE)) ! 1930: { /* skip over the CTB_LITERAL header to compute signature */ ! 1931: LITlength = getpastlength(ctb2, f); /* read packet length */ ! 1932: start_text = ftell(f); /* mark position of text for later */ ! 1933: /* Now we are 1 byte past the CTB_LITERAL header. */ ! 1934: } ! 1935: } ! 1936: ! 1937: ! 1938: /* Use keyID prefix to look up key... */ ! 1939: ! 1940: /* Get and validate public key from a key file: */ ! 1941: if (getpublickey(FALSE, verbose, keyfile, &fp, &pktlen, ! 1942: keyID, timestamp, userid, n, e) < 0) ! 1943: { /* Can't get public key. Complain and process file copy anyway. */ ! 1944: fprintf(stderr,"\n\aWARNING: Can't find the right public key-- can't check signature integrity.\n"); ! 1945: } /* Can't find public key */ ! 1946: else /* got good public key, now use it to check signature...*/ ! 1947: { ! 1948: if (testeq(e,0)) /* Means secret key has been compromised */ ! 1949: { PascalToC(userid); ! 1950: fprintf(stderr,"\n\aWarning: Secret key compromised for userid \"%s\".",userid); ! 1951: fprintf(stderr,"\nThus this public key cannot be used.\n"); ! 1952: goto err1; ! 1953: } ! 1954: ! 1955: /* Recover message digest via public key */ ! 1956: mp_modexp((unitptr)outbuf,(unitptr)inbuf,e,n); ! 1957: ! 1958: /* Unblock message digest, and convert to external byte order: */ ! 1959: count = postunblock(outbuf, (unitptr)outbuf, n, TRUE, TRUE); ! 1960: if (count < 0) ! 1961: { fprintf(stderr,"\n\aBad RSA decrypt: checksum or pad error during unblocking.\n"); ! 1962: goto err1; ! 1963: } ! 1964: ! 1965: fputc('.',stderr); /* Signal RSA completion. */ ! 1966: ! 1967: /* outbuf should contain message digest packet */ ! 1968: /*==================================================================*/ ! 1969: /* Look at nested stuff within RSA block... */ ! 1970: ! 1971: if (!is_ctb_type(outbuf[0],CTB_MD_TYPE)) ! 1972: { fprintf(stderr,"\aNested info is not a message digest packet.\n"); ! 1973: goto err1; ! 1974: } ! 1975: ! 1976: if (outbuf[2] != MD4_ALGORITHM_BYTE) ! 1977: { fprintf(stderr,"\a\nUnrecognized message digest algorithm.\n"); ! 1978: goto err1; ! 1979: } ! 1980: ! 1981: /* Reposition file to where that plaintext begins... */ ! 1982: fseek(f,start_text,SEEK_SET); /* reposition file from last ftell */ ! 1983: ! 1984: MDfile0(&MD,f); /* compute a message digest from rest of file */ ! 1985: ! 1986: hilo_swap(outbuf+19,4); /* convert timestamp from external LSB-first form */ ! 1987: PascalToC(userid); /* for display */ ! 1988: ! 1989: /* now compare computed MD with claimed MD */ ! 1990: if (!equal_buffers((byte *)(MD.buffer), outbuf+3, 16)) ! 1991: { fprintf(stderr,"\a\nWARNING: Bad signature, doesn't match file contents!\a\n"); ! 1992: fprintf(stderr,"\nBad signature from user \"%s\".\n",userid); ! 1993: fprintf(stderr,"Signature made %s",ctime((long *)(outbuf+19))); /* '\n' */ ! 1994: goto xnormal; /* normal exit */ ! 1995: } ! 1996: ! 1997: fprintf(stderr,"\nGood signature from user \"%s\".\n",userid); ! 1998: fprintf(stderr,"Signature made %s",ctime((long *)(outbuf+19))); /* '\n' */ ! 1999: ! 2000: } /* Found correct public key */ ! 2001: ! 2002: /* Reposition file to where that plaintext begins... */ ! 2003: fseek(f,start_text,SEEK_SET); /* reposition file from last ftell */ ! 2004: ! 2005: if (separate_signature) ! 2006: fprintf(stderr,"\nSignature and text are separate. No output file produced. "); ! 2007: else /* signature precedes plaintext in file... */ ! 2008: { /* produce a plaintext output file from signature file */ ! 2009: if (file_exists(outfile)) ! 2010: { fprintf(stderr,"\n\aOutput file '%s' already exists. Overwrite (y/N)? ",outfile); ! 2011: if (!getyesno('n')) /* user said don't do it. */ ! 2012: goto err1; /* abort operation */ ! 2013: } ! 2014: /* open file g for write, in binary (not text) mode...*/ ! 2015: if ((g = fopen(outfile,"wb")) == NULL) ! 2016: { fprintf(stderr,"\n\aCan't create plaintext file '%s'\n",outfile); ! 2017: goto err1; ! 2018: } ! 2019: copyfile(f,g,-1UL); /* copy rest of file from file f to g */ ! 2020: fclose(g); ! 2021: } ! 2022: ! 2023: xnormal: ! 2024: burn(inbuf); /* burn sensitive data on stack */ ! 2025: burn(outbuf); /* burn sensitive data on stack */ ! 2026: fclose(f); ! 2027: if (separate_signature) ! 2028: return(0); /* normal return, no nested info */ ! 2029: if (is_ctb(ctb2) && is_ctb_type(ctb2,CTB_LITERAL_TYPE)) ! 2030: /* we already stripped away the CTB_LITERAL */ ! 2031: return(0); /* normal return, no nested info */ ! 2032: /* Otherwise, it's best to assume a nested CTB */ ! 2033: return(1); /* nested information return */ ! 2034: ! 2035: err1: ! 2036: burn(inbuf); /* burn sensitive data on stack */ ! 2037: burn(outbuf); /* burn sensitive data on stack */ ! 2038: fclose(f); ! 2039: return(-1); /* error return */ ! 2040: ! 2041: } /* check_signaturefile */ ! 2042: ! 2043: ! 2044: ! 2045: /*======================================================================*/ ! 2046: int squish_and_bass_file(byte *basskey, int lenbasskey, FILE *f, FILE *g) ! 2047: { ! 2048: FILE *t; ! 2049: byte header[4]; ! 2050: byte ctb; ! 2051: ! 2052: /* ! 2053: ** Create a temporary file 't' and compress our input file 'f' into ! 2054: ** 't'. If we get a good compression ratio then use file 't' for ! 2055: ** input and write a CTB_COMPRESSED prefix. ! 2056: ** But, if the file looks like a PKZIP file then skip our compression. ! 2057: */ ! 2058: ! 2059: fread( header, 1, 4, f ); ! 2060: rewind( f ); ! 2061: ! 2062: if (pkzipSignature( header )) ! 2063: t = f; ! 2064: else ! 2065: if ((t = tmpfile()) != NULL) ! 2066: { ! 2067: extern int lzhEncode( FILE *, FILE * ); ! 2068: ! 2069: if (verbose) fprintf(stderr, "Compressing plaintext..." ); ! 2070: ! 2071: ctb = CTB_COMPRESSED; /* use compression prefix CTB */ ! 2072: fwrite( &ctb, 1, 1, t ); /* write CTB_COMPRESSED */ ! 2073: /* No CTB packet length specified means indefinite length. */ ! 2074: ctb = LZH_ALGORITHM_BYTE; /* use lzh compression */ ! 2075: fwrite( &ctb, 1, 1, t ); /* write LZH algorithm byte */ ! 2076: ! 2077: /* lzhEncode returns the ratio of file size t to size f. */ ! 2078: ! 2079: if (lzhEncode( f, t) < 9) ! 2080: { ! 2081: /* Compression made the input file smaller by at least ! 2082: 10 per cent, so use the 't' file. */ ! 2083: ! 2084: if (verbose) fprintf(stderr, "compressed. " ); ! 2085: ! 2086: rewind( t ); ! 2087: } ! 2088: else ! 2089: { ! 2090: /* Compression made no significant difference in size so ! 2091: pass the input file along as it is. Close and remove ! 2092: the temporary file. */ ! 2093: ! 2094: if (verbose) fprintf(stderr, "incompressible. " ); ! 2095: ! 2096: wipeout( t ); ! 2097: fclose( t ); ! 2098: rewind( f ); ! 2099: t = f; ! 2100: } ! 2101: } ! 2102: else ! 2103: t = f; ! 2104: ! 2105: /* Now write out file thru BassOmatic ... */ ! 2106: ! 2107: ctb = CTB_CKE; /* CKE is Conventional Key Encryption */ ! 2108: fwrite( &ctb, 1, 1, g ); /* write CTB_CKE */ ! 2109: /* No CTB packet length specified means indefinite length. */ ! 2110: ! 2111: bass_file( basskey, lenbasskey, FALSE, t, g ); /* encrypt file */ ! 2112: ! 2113: if (t != f) ! 2114: { wipeout( t ); ! 2115: fclose( t ); /* close and remove the temporary file */ ! 2116: } ! 2117: ! 2118: return(0); /* normal return */ ! 2119: ! 2120: } /* squish_and_bass_file */ ! 2121: ! 2122: ! 2123: #define NOECHO1 1 /* Disable password from being displayed on screen */ ! 2124: #define NOECHO2 2 /* Disable password from being displayed on screen */ ! 2125: ! 2126: int bass_encryptfile(boolean nested, char *infile, char *outfile) ! 2127: { ! 2128: FILE *f; /* input file */ ! 2129: FILE *g; /* output file */ ! 2130: byte basskey[256]; ! 2131: int basskeylen; /* must get no bigger than sizeof(basskey)-2 */ ! 2132: ! 2133: if (verbose) ! 2134: fprintf(stderr,"\nPlaintext file: %s, ciphertext file: %s\n", ! 2135: infile,outfile); ! 2136: ! 2137: /* open file f for read, in binary (not text) mode...*/ ! 2138: if ((f = fopen( infile, "rb" )) == NULL) ! 2139: { ! 2140: fprintf(stderr,"\n\aCan't open plaintext file '%s'\n", infile ); ! 2141: return(-1); ! 2142: } ! 2143: ! 2144: /* open file g for write, in binary (not text) mode...*/ ! 2145: if ((g = fopen( outfile, "wb" )) == NULL) ! 2146: { ! 2147: fprintf(stderr,"\n\aCan't create ciphertext file '%s'\n", outfile ); ! 2148: fclose(f); ! 2149: return(-1); ! 2150: } ! 2151: ! 2152: /* Get BassOmatic password with leading BassOmatic control byte: */ ! 2153: /* Default is Military grade BassOmatic key control byte */ ! 2154: if (getpassword(basskey,NOECHO2,0x1f) <= 0) ! 2155: return(-1); ! 2156: ! 2157: basskeylen = strlen(basskey); ! 2158: ! 2159: /* Now compress the plaintext and encrypt it with BassOmatic... */ ! 2160: squish_and_bass_file( basskey, basskeylen, f, g ); ! 2161: ! 2162: burn(basskey); /* burn sensitive data on stack */ ! 2163: ! 2164: fclose(g); ! 2165: fclose(f); ! 2166: ! 2167: return(0); ! 2168: ! 2169: } /* bass_encryptfile */ ! 2170: ! 2171: ! 2172: /*======================================================================*/ ! 2173: ! 2174: ! 2175: int encryptfile(boolean nested, char *mcguffin, char *infile, char *outfile) ! 2176: { ! 2177: byte ctb; ! 2178: byte ctbCKE = CTB_CKE; ! 2179: byte randompad[MAX_BYTE_PRECISION]; /* buffer of random pad bytes */ ! 2180: int i,blocksize,ckp_length,PKElength,bytecount; ! 2181: FILE *f; ! 2182: FILE *g; ! 2183: FILE *t; ! 2184: byte header[4]; ! 2185: unit n[MAX_UNIT_PRECISION], e[MAX_UNIT_PRECISION]; ! 2186: byte inbuf[MAX_BYTE_PRECISION]; ! 2187: byte outbuf[MAX_BYTE_PRECISION]; ! 2188: word32 tstamp; byte *timestamp = (byte *) &tstamp; /* key certificate timestamp */ ! 2189: byte userid[256]; ! 2190: byte basskey[64]; /* must be big enough for make_random_basskey */ ! 2191: int basskeylen; /* must get no bigger than sizeof(basskey)-2 */ ! 2192: char keyfile[64]; /* for getpublickey */ ! 2193: long fp; /* unused, just to satisfy getpublickey */ ! 2194: int pktlen; /* unused, just to satisfy getpublickey */ ! 2195: ! 2196: ! 2197: buildfilename(keyfile,PUBLIC_KEYRING_FILENAME); /* use default pathname */ ! 2198: ! 2199: if (verbose) ! 2200: fprintf(stderr,"\nPlaintext file: %s, ciphertext file: %s\n", ! 2201: infile,outfile); ! 2202: ! 2203: strcpy(userid,mcguffin); /* Who we are looking for (C string) */ ! 2204: ! 2205: /* Get and validate public key from a key file: */ ! 2206: if (getpublickey(FALSE, TRUE, keyfile, &fp, &pktlen, NULL, timestamp, userid, n, e) < 0) ! 2207: { return(-1); ! 2208: } ! 2209: ! 2210: if (testeq(e,0)) /* Means secret key has been compromised */ ! 2211: { PascalToC(userid); ! 2212: fprintf(stderr,"\n\aWarning: Secret key compromised for userid \"%s\".",userid); ! 2213: fprintf(stderr,"\nThus this public key cannot be used.\n"); ! 2214: return(-1); ! 2215: } ! 2216: ! 2217: ! 2218: /* set_precision has been properly called by getpublickey */ ! 2219: ! 2220: /* Note that RSA key must be at least big enough to encipher a ! 2221: complete conventional key packet in a single RSA block. ! 2222: The BassOmatic key packet is 28 bytes long, which requires ! 2223: an RSA key 32 bytes (256 bits) long. ! 2224: If we implemented DES, the DES key packet is 37 bytes long ! 2225: (with IV, prewhitener and postwhitener), requiring an RSA ! 2226: key 41 bytes (328 bits) long. ! 2227: */ ! 2228: ! 2229: blocksize = countbytes(n)-1; /* size of a plaintext block */ ! 2230: if (blocksize < 31) ! 2231: { fprintf(stderr,"\n\aError: RSA key length must be at least 256 bits.\n"); ! 2232: return(-1); ! 2233: } ! 2234: ! 2235: /* open file f for read, in binary (not text) mode...*/ ! 2236: if ((f = fopen( infile, "rb" )) == NULL) ! 2237: { ! 2238: fprintf(stderr,"\n\aCan't open plaintext file '%s'\n", infile ); ! 2239: return(-1); ! 2240: } ! 2241: ! 2242: /* open file g for write, in binary (not text) mode...*/ ! 2243: if ((g = fopen( outfile, "wb" )) == NULL) ! 2244: { ! 2245: fprintf(stderr,"\n\aCan't create ciphertext file '%s'\n", outfile ); ! 2246: fclose(f); ! 2247: return(-1); ! 2248: } ! 2249: ! 2250: /* Now we have to time some user keystrokes to get some random ! 2251: bytes for generating a random BassOmatic key. ! 2252: We would have to solicit fewer keystrokes for random BassOmatic ! 2253: key generation if we had already accumulated some keystrokes ! 2254: incidental to some other purpose, such as asking for a password ! 2255: to decode an RSA secret key so that a signature could be applied ! 2256: to the message before encrypting it. ! 2257: */ ! 2258: ! 2259: basskeylen = 32; /* Default is big BassOmatic key */ ! 2260: if (blocksize < 64) /* <= 512 bits */ ! 2261: basskeylen = 24; ! 2262: if (blocksize < 36) /* <= 288 bits */ ! 2263: basskeylen = 16; ! 2264: ckp_length = make_random_basskey(basskey,basskeylen); ! 2265: /* Returns a basskeylen+1 byte random BassOmatic key */ ! 2266: ! 2267: outbuf[0] = CTB_CONKEY; /* conventional key packet */ ! 2268: ! 2269: ckp_length += 1; /* add length of algorithm field */ ! 2270: /* Conventional key packet length does not include itself or CTB prefix: */ ! 2271: outbuf[1] = ckp_length; ! 2272: ! 2273: outbuf[2] = BASS_ALGORITHM_BYTE; /* select BassOmatic algorithm */ ! 2274: ! 2275: for (i=0; i<ckp_length-1; i++) ! 2276: outbuf[3+i] = basskey[i]; ! 2277: ! 2278: /* ! 2279: ** Messages encrypted with a public key should use random padding, ! 2280: ** while messages "signed" with a secret key should use constant ! 2281: ** padding. ! 2282: */ ! 2283: ! 2284: for (i = 0; i < (blocksize - (ckp_length + 2)); i++) ! 2285: randompad[i] = randombyte(); ! 2286: ! 2287: /* ! 2288: ** Note that RSA key must be at least big enough to encipher a ! 2289: ** complete conventional key packet in a single RSA block. ! 2290: */ ! 2291: ! 2292: /* ckp_length+2 is conventional key packet length. */ ! 2293: ! 2294: preblock( (unitptr)inbuf, outbuf, ckp_length+2, n, TRUE, randompad ); ! 2295: mp_modexp( (unitptr)outbuf, (unitptr)inbuf, e, n ); /* RSA encrypt */ ! 2296: ! 2297: /* write out header record to outfile ... */ ! 2298: ! 2299: ctb = CTB_PKE; /* PKE is Public Key Encryption */ ! 2300: PKElength = KEYFRAGSIZE + countbytes( (unitptr)outbuf ) + 2; ! 2301: fwrite( &ctb, 1, 1, g ); /* write RSA msg CTB */ ! 2302: ! 2303: /* Change PKElength to external byte order: */ ! 2304: ! 2305: convert( PKElength ); ! 2306: fwrite( &PKElength, 1, sizeof( PKElength ), g ); /* write length */ ! 2307: ! 2308: writekeyID( n, g ); /* write msg prefix fragment of modulus n */ ! 2309: ! 2310: /* convert RSA ciphertext block via reg2mpi and write to file */ ! 2311: ! 2312: write_mpi( (unitptr)outbuf, g, FALSE ); ! 2313: ! 2314: burn(inbuf); /* burn sensitive data on stack */ ! 2315: burn(outbuf); /* burn sensitive data on stack */ ! 2316: ! 2317: /** Finished with RSA block containing BassOmatic key. */ ! 2318: ! 2319: /* Now compress the plaintext and encrypt it with BassOmatic... */ ! 2320: squish_and_bass_file( basskey, ckp_length-1, f, g ); ! 2321: ! 2322: burn(basskey); /* burn sensitive data on stack */ ! 2323: ! 2324: fclose(g); ! 2325: fclose(f); ! 2326: ! 2327: return(0); ! 2328: } /* encryptfile */ ! 2329: ! 2330: ! 2331: /*======================================================================*/ ! 2332: int make_literal(char *infile, char *outfile) ! 2333: { /* An awful lot of hassle to go thru just to prepend 1 lousy byte. ! 2334: Prepends a CTB_LITERAL prefix byte to a file. ! 2335: */ ! 2336: byte ctb; /* Cipher Type Byte */ ! 2337: FILE *f; ! 2338: FILE *g; ! 2339: ! 2340: if (verbose) ! 2341: fprintf(stderr,"\nInput plaintext file: %s, Output plaintext file: %s\n", ! 2342: infile,outfile); ! 2343: ! 2344: /* open file f for read, in binary (not text) mode...*/ ! 2345: if ((f = fopen(infile,"rb")) == NULL) ! 2346: { fprintf(stderr,"\n\aCan't open input plaintext file '%s'\n",infile); ! 2347: return(-1); ! 2348: } ! 2349: ! 2350: /* open file g for write, in binary (not text) mode... */ ! 2351: if ((g = fopen( outfile, "wb" )) == NULL) ! 2352: { fprintf(stderr, "\n\aCan't create plaintext file '%s'\n", outfile ); ! 2353: goto err1; ! 2354: } ! 2355: ! 2356: ctb = CTB_LITERAL; /* prepend this byte prefix to message */ ! 2357: fwrite( &ctb, 1, 1, g ); /* write LITERAL CTB */ ! 2358: /* No CTB packet length specified means indefinite length. */ ! 2359: ! 2360: copyfile( f, g, -1UL ); /* copy rest of literal plaintext file */ ! 2361: ! 2362: fclose(g); ! 2363: fclose(f); ! 2364: return(0); /* normal return */ ! 2365: ! 2366: err1: ! 2367: fclose(f); ! 2368: return(-1); /* error return */ ! 2369: ! 2370: } /* make_literal */ ! 2371: ! 2372: ! 2373: /*======================================================================*/ ! 2374: int strip_literal(char *infile, char *outfile) ! 2375: { /* A lot of hassle to go thru just to strip off 1 lousy prefix byte. ! 2376: Strips off the CTB_LITERAL prefix byte from a file. ! 2377: */ ! 2378: byte ctb; /* Cipher Type Byte */ ! 2379: FILE *f; ! 2380: FILE *g; ! 2381: word32 LITlength = 0; ! 2382: ! 2383: if (verbose) ! 2384: fprintf(stderr,"\nInput plaintext file: %s, output plaintext file: %s\n", ! 2385: infile,outfile); ! 2386: ! 2387: /* open file f for read, in binary (not text) mode...*/ ! 2388: if ((f = fopen(infile,"rb")) == NULL) ! 2389: { fprintf(stderr,"\n\aCan't open input plaintext file '%s'\n",infile); ! 2390: return(-1); ! 2391: } ! 2392: ! 2393: fread(&ctb,1,1,f); /* read Cipher Type Byte */ ! 2394: ! 2395: if (!is_ctb(ctb) || !is_ctb_type(ctb,CTB_LITERAL_TYPE)) ! 2396: { fprintf(stderr,"\n\a'%s' is not a literal plaintext file.\n",infile); ! 2397: fclose(f); ! 2398: return(-1); ! 2399: } ! 2400: ! 2401: LITlength = getpastlength(ctb, f); /* read packet length */ ! 2402: ! 2403: if (file_exists( outfile )) ! 2404: { fprintf(stderr, "\n\aOutput file '%s' already exists. Overwrite (y/N)? ", outfile ); ! 2405: if (! getyesno( 'n' )) ! 2406: goto err1; /* user said don't do it - abort operation */ ! 2407: } ! 2408: ! 2409: /* open file g for write, in binary (not text) mode... */ ! 2410: ! 2411: if ((g = fopen( outfile, "wb" )) == NULL) ! 2412: { fprintf(stderr, "\n\aCan't create plaintext file '%s'\n", outfile ); ! 2413: goto err1; ! 2414: } ! 2415: ! 2416: copyfile( f, g, LITlength ); /* copy rest of literal plaintext file */ ! 2417: ! 2418: fclose(g); ! 2419: fclose(f); ! 2420: return(0); /* normal return */ ! 2421: ! 2422: err1: ! 2423: fclose(f); ! 2424: return(-1); /* error return */ ! 2425: ! 2426: } /* strip_literal */ ! 2427: ! 2428: ! 2429: /*======================================================================*/ ! 2430: ! 2431: ! 2432: int decryptfile(char *infile, char *outfile) ! 2433: { ! 2434: byte ctb; /* Cipher Type Byte */ ! 2435: byte ctbCKE; /* Cipher Type Byte */ ! 2436: FILE *f; ! 2437: FILE *g; ! 2438: int count, status; ! 2439: word32 PKElength, CKElength; ! 2440: unit n[MAX_UNIT_PRECISION], e[MAX_UNIT_PRECISION], d[MAX_UNIT_PRECISION]; ! 2441: unit p[MAX_UNIT_PRECISION], q[MAX_UNIT_PRECISION], u[MAX_UNIT_PRECISION]; ! 2442: byte inbuf[MAX_BYTE_PRECISION]; ! 2443: byte outbuf[MAX_BYTE_PRECISION]; ! 2444: byte keyID[KEYFRAGSIZE]; ! 2445: word32 tstamp; byte *timestamp = (byte *) &tstamp; /* key certificate timestamp */ ! 2446: byte userid[256]; ! 2447: ! 2448: set_precision(MAX_UNIT_PRECISION); /* safest opening assumption */ ! 2449: ! 2450: if (verbose) ! 2451: fprintf(stderr,"\nCiphertext file: %s, plaintext file: %s\n", ! 2452: infile,outfile); ! 2453: ! 2454: /* open file f for read, in binary (not text) mode...*/ ! 2455: if ((f = fopen(infile,"rb")) == NULL) ! 2456: { fprintf(stderr,"\n\aCan't open ciphertext file '%s'\n",infile); ! 2457: return(-1); ! 2458: } ! 2459: ! 2460: fread(&ctb,1,1,f); /* read Cipher Type Byte */ ! 2461: if (!is_ctb(ctb)) ! 2462: { fprintf(stderr,"\n\a'%s' is not a cipher file.\n",infile); ! 2463: fclose(f); ! 2464: return(-1); ! 2465: } ! 2466: ! 2467: /* PKE is Public Key Encryption */ ! 2468: if (!is_ctb_type(ctb,CTB_PKE_TYPE)) ! 2469: { fprintf(stderr,"\n\a'%s' is not enciphered with a public key.\n",infile); ! 2470: fclose(f); ! 2471: return(-1); ! 2472: } ! 2473: ! 2474: PKElength = getpastlength(ctb, f); /* read packet length */ ! 2475: ! 2476: fread(keyID,1,KEYFRAGSIZE,f); /* read key ID */ ! 2477: /* Use keyID prefix to look up key. */ ! 2478: ! 2479: /* Get and validate secret key from a key file: */ ! 2480: if (getsecretkey(keyID, timestamp, userid, n, e, d, p, q, u) < 0) ! 2481: { fclose(f); ! 2482: return(-1); ! 2483: } ! 2484: ! 2485: /* Note that RSA key must be at least big enough to encipher a ! 2486: complete conventional key packet in a single RSA block. */ ! 2487: ! 2488: /*==================================================================*/ ! 2489: /* read ciphertext block, converting to internal format: */ ! 2490: read_mpi((unitptr)inbuf, f, FALSE, FALSE); ! 2491: ! 2492: fprintf(stderr,"Just a moment-- "); /* RSA will take a while. */ ! 2493: ! 2494: rsa_decrypt((unitptr)outbuf, (unitptr)inbuf, d, p, q, u); ! 2495: ! 2496: if ((count = postunblock(outbuf, (unitptr)outbuf, n, TRUE, TRUE)) < 0) ! 2497: { fprintf(stderr,"\n\aBad RSA decrypt: checksum or pad error during unblocking.\n"); ! 2498: fclose(f); ! 2499: return(-1); ! 2500: } ! 2501: ! 2502: fputc('.',stderr); /* Signal RSA completion. */ ! 2503: ! 2504: /* outbuf should contain random BassOmatic key packet */ ! 2505: /*==================================================================*/ ! 2506: /* Look at nested stuff within RSA block... */ ! 2507: ! 2508: ctb = outbuf[0]; /* get nested CTB, should be CTB_CONKEY */ ! 2509: ! 2510: if (!is_ctb_type(ctb,CTB_CONKEY_TYPE)) ! 2511: { fprintf(stderr,"\aNested info is not a conventional key packet.\n"); ! 2512: goto err1; ! 2513: } ! 2514: ! 2515: /* Test the Conventional Key Packet for supported algorithms. ! 2516: (currently, just the BassOmatic is supported) */ ! 2517: ! 2518: if ( outbuf[2] != BASS_ALGORITHM_BYTE ) ! 2519: { fprintf(stderr,"\a\nUnrecognized conventional encryption algorithm.\n"); ! 2520: goto err1; ! 2521: } ! 2522: ! 2523: if (file_exists( outfile )) ! 2524: { fprintf(stderr, "\n\aOutput file '%s' already exists. Overwrite (y/N)? ", outfile ); ! 2525: if (! getyesno( 'n' )) ! 2526: goto err1; /* user said don't do it - abort operation */ ! 2527: } ! 2528: ! 2529: /* open file g for write, in binary (not text) mode... */ ! 2530: if ((g = fopen( outfile, "wb" )) == NULL) ! 2531: { fprintf(stderr, "\n\aCan't create plaintext file '%s'\n", outfile ); ! 2532: goto err1; ! 2533: } ! 2534: ! 2535: fread(&ctbCKE,1,1,f); /* read Cipher Type Byte, should be CTB_CKE */ ! 2536: if (ctbCKE != CTB_CKE) ! 2537: { /* Should never get here. */ ! 2538: fprintf(stderr,"\a\nBad or missing CTB_CKE byte.\n"); ! 2539: goto err1; /* Abandon ship! */ ! 2540: } ! 2541: ! 2542: CKElength = getpastlength(ctbCKE, f); /* read packet length */ ! 2543: ! 2544: status = bass_file( outbuf+3, count-3, TRUE, f, g ); /* Decrypt ciphertext file */ ! 2545: ! 2546: fclose(g); ! 2547: fclose(f); ! 2548: burn(inbuf); /* burn sensitive data on stack */ ! 2549: burn(outbuf); /* burn sensitive data on stack */ ! 2550: mp_burn(d); /* burn sensitive data on stack */ ! 2551: mp_burn(p); /* burn sensitive data on stack */ ! 2552: mp_burn(q); /* burn sensitive data on stack */ ! 2553: mp_burn(u); /* burn sensitive data on stack */ ! 2554: if (status < 0) /* if bass_file failed, then error return */ ! 2555: return(status); ! 2556: return(1); /* always indicate output file has nested stuff in it. */ ! 2557: ! 2558: err1: ! 2559: fclose(f); ! 2560: burn(inbuf); /* burn sensitive data on stack */ ! 2561: burn(outbuf); /* burn sensitive data on stack */ ! 2562: mp_burn(d); /* burn sensitive data on stack */ ! 2563: mp_burn(p); /* burn sensitive data on stack */ ! 2564: mp_burn(q); /* burn sensitive data on stack */ ! 2565: mp_burn(u); /* burn sensitive data on stack */ ! 2566: return(-1); /* error return */ ! 2567: ! 2568: } /* decryptfile */ ! 2569: ! 2570: ! 2571: ! 2572: int bass_decryptfile(char *infile, char *outfile) ! 2573: { ! 2574: byte ctb; /* Cipher Type Byte */ ! 2575: FILE *f; ! 2576: FILE *g; ! 2577: word32 CKElength; ! 2578: byte basskey[256]; ! 2579: int basskeylen; /* must get no bigger than sizeof(basskey)-2 */ ! 2580: int status; ! 2581: ! 2582: if (verbose) ! 2583: fprintf(stderr,"\nCiphertext file: %s, plaintext file: %s\n", ! 2584: infile,outfile); ! 2585: ! 2586: /* open file f for read, in binary (not text) mode...*/ ! 2587: if ((f = fopen(infile,"rb")) == NULL) ! 2588: { fprintf(stderr,"\n\aCan't open ciphertext file '%s'\n",infile); ! 2589: return(-1); ! 2590: } ! 2591: ! 2592: fread(&ctb,1,1,f); /* read Cipher Type Byte, should be CTB_CKE */ ! 2593: ! 2594: if (!is_ctb(ctb) || !is_ctb_type(ctb,CTB_CKE_TYPE)) ! 2595: { /* Should never get here. */ ! 2596: fprintf(stderr,"\a\nBad or missing CTB_CKE byte.\n"); ! 2597: goto err1; /* Abandon ship! */ ! 2598: } ! 2599: ! 2600: CKElength = getpastlength(ctb, f); /* read packet length */ ! 2601: /* The packet length is ignored. Assume it's huge. */ ! 2602: ! 2603: if (file_exists( outfile )) ! 2604: { fprintf(stderr, "\n\aOutput file '%s' already exists. Overwrite (y/N)? ", outfile ); ! 2605: if (! getyesno( 'n' )) ! 2606: goto err1; /* user said don't do it - abort operation */ ! 2607: } ! 2608: ! 2609: /* open file g for write, in binary (not text) mode... */ ! 2610: if ((g = fopen( outfile, "wb" )) == NULL) ! 2611: { fprintf(stderr, "\n\aCan't create plaintext file '%s'\n", outfile ); ! 2612: goto err1; ! 2613: } ! 2614: ! 2615: /* Get BassOmatic password with leading BassOmatic control byte: */ ! 2616: /* Default is Military grade BassOmatic key control byte */ ! 2617: if (getpassword(basskey,NOECHO1,0x1f) <= 0) ! 2618: return(-1); ! 2619: ! 2620: basskeylen = strlen(basskey); ! 2621: ! 2622: status = bass_file( basskey, basskeylen, TRUE, f, g ); /* decrypt file */ ! 2623: ! 2624: burn(basskey); /* burn sensitive data on stack */ ! 2625: ! 2626: fclose(g); ! 2627: fclose(f); ! 2628: ! 2629: if (status < 0) /* if bass_file failed, then complain */ ! 2630: { fprintf(stderr,"\n\aError: Bad pass phrase. "); ! 2631: remove(outfile); /* throw away our mistake */ ! 2632: return(status); /* error return */ ! 2633: } ! 2634: return(1); /* always indicate output file has nested stuff in it. */ ! 2635: ! 2636: err1: ! 2637: fclose(f); ! 2638: return(-1); /* error return */ ! 2639: ! 2640: } /* bass_decryptfile */ ! 2641: ! 2642: ! 2643: ! 2644: int decompress_file(char *infile, char *outfile) ! 2645: { ! 2646: byte ctb; ! 2647: FILE *f; ! 2648: FILE *g; ! 2649: word32 compress_pkt_length; ! 2650: extern void lzhDecode( FILE *, FILE * ); ! 2651: if (verbose) fprintf(stderr, "Decompressing plaintext..." ); ! 2652: ! 2653: /* open file f for read, in binary (not text) mode...*/ ! 2654: if ((f = fopen(infile,"rb")) == NULL) ! 2655: { fprintf(stderr,"\n\aCan't open compressed file '%s'\n",infile); ! 2656: return(-1); ! 2657: } ! 2658: ! 2659: fread(&ctb,1,1,f); /* read and skip over Cipher Type Byte */ ! 2660: if (!is_ctb_type( ctb, CTB_COMPRESSED_TYPE )) ! 2661: { /* Shouldn't get here, or why were we called to begin with? */ ! 2662: fprintf(stderr,"\a\nBad or missing CTB_COMPRESSED byte.\n"); ! 2663: goto err1; /* Abandon ship! */ ! 2664: } ! 2665: ! 2666: compress_pkt_length = getpastlength(ctb, f); /* read packet length */ ! 2667: /* The packet length is ignored. Assume it's huge. */ ! 2668: ! 2669: fread(&ctb,1,1,f); /* read and skip over compression algorithm byte */ ! 2670: if (ctb != LZH_ALGORITHM_BYTE) ! 2671: { /* We only know one compression algorithm */ ! 2672: fprintf(stderr,"\a\nUnrecognized compression algorithm.\n"); ! 2673: goto err1; /* Abandon ship! */ ! 2674: } ! 2675: ! 2676: /* open file g for write, in binary (not text) mode... */ ! 2677: if ((g = fopen( outfile, "wb" )) == NULL) ! 2678: { fprintf(stderr, "\n\aCan't create decompressed file '%s'\n", outfile ); ! 2679: goto err1; ! 2680: } ! 2681: ! 2682: lzhDecode( f, g ); ! 2683: if (verbose) fprintf(stderr, "done. " ); ! 2684: fclose(g); ! 2685: fclose(f); ! 2686: return(1); /* always indicate output file has nested stuff in it. */ ! 2687: err1: ! 2688: fclose(f); ! 2689: return(-1); /* error return */ ! 2690: ! 2691: } /* decompress_file */ ! 2692: ! 2693: ! 2694: ! 2695: int view_keyring(char *mcguffin, char *ringfile) ! 2696: /* Lists all entries in keyring that have mcguffin string in userid. ! 2697: mcguffin is a null-terminated C string. ! 2698: */ ! 2699: { FILE *f; ! 2700: long file_position,fp; ! 2701: byte ctb; ! 2702: int status; ! 2703: unit n[MAX_UNIT_PRECISION], e[MAX_UNIT_PRECISION]; ! 2704: byte keyID[KEYFRAGSIZE]; ! 2705: byte userid[256]; /* key certificate userid */ ! 2706: word32 tstamp; ! 2707: byte *timestamp = (byte *) &tstamp; /* key certificate timestamp */ ! 2708: int keycounter = 0; ! 2709: ! 2710: /* open file f for read, in binary (not text) mode...*/ ! 2711: if ((f = fopen(ringfile,"rb")) == NULL) ! 2712: { fprintf(stderr,"\n\aCan't open key ring file '%s'\n",ringfile); ! 2713: return(-1); ! 2714: } ! 2715: ! 2716: /* Here's a good format for display of key or signature certificates: ! 2717: Type bits/keyID Date User ID ! 2718: pub 990/xxxxxx dd-mmm-yy aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ! 2719: sec 990/xxxxxx dd-mmm-yy aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ! 2720: sig 990/xxxxxx dd-mmm-yy aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ! 2721: com 990/xxxxxx dd-mmm-yy aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ! 2722: */ ! 2723: ! 2724: fprintf(stderr,"\nKey ring: '%s'",ringfile); ! 2725: if (strlen(mcguffin) > 0) ! 2726: fprintf(stderr,", looking for user ID \"%s\".",mcguffin); ! 2727: fprintf(stderr,"\nType bits/keyID Date User ID\n"); ! 2728: do ! 2729: { ! 2730: status = readkeypacket(f,FALSE,&ctb,timestamp,userid,n,e, ! 2731: NULL,NULL,NULL,NULL); ! 2732: /* Note that readkeypacket has called set_precision */ ! 2733: if (status== -1 ) break; /* eof reached */ ! 2734: if (status < 0) ! 2735: { fprintf(stderr,"\n\aCould not read key from file '%s'.\n", ! 2736: ringfile); ! 2737: fclose(f); /* close key file */ ! 2738: return(-1); ! 2739: } ! 2740: ! 2741: if (!is_ctb_type(ctb,CTB_CERT_PUBKEY_TYPE) ! 2742: && !is_ctb_type(ctb,CTB_CERT_SECKEY_TYPE)) ! 2743: { ! 2744: fprintf(stderr,"\n\aError in file '%s'. Not a key certificate.\n", ! 2745: ringfile); ! 2746: return(-1); ! 2747: } ! 2748: ! 2749: keycounter++; ! 2750: ! 2751: extract_keyID(keyID, n); ! 2752: PascalToC(userid); ! 2753: ! 2754: if (strcontains(userid,mcguffin)) ! 2755: { ! 2756: if (is_ctb_type(ctb,CTB_CERT_PUBKEY_TYPE)) ! 2757: { ! 2758: if (testeq(e,0)) /* e==0 means key compromised */ ! 2759: fprintf(stderr,"com "); /* "key compromised" certificate */ ! 2760: else ! 2761: fprintf(stderr,"pub "); /* public key certificate */ ! 2762: } ! 2763: else if (is_ctb_type(ctb,CTB_CERT_SECKEY_TYPE)) ! 2764: fprintf(stderr,"sec "); /* secret key certificate */ ! 2765: else ! 2766: fprintf(stderr,"??? "); /* otherwise, who knows? */ ! 2767: ! 2768: fprintf(stderr,"%4d/",countbits(n)); ! 2769: showkeyID(keyID); ! 2770: fputc(' ',stderr); ! 2771: show_date((long *)timestamp); ! 2772: fprintf(stderr," "); ! 2773: fprintf(stderr,userid); ! 2774: fputc('\n',stderr); ! 2775: } /* if it has mcguffin */ ! 2776: } while (status >= 0); ! 2777: ! 2778: fclose(f); /* close key file */ ! 2779: fprintf(stderr,"%d key(s) examined. ",keycounter); ! 2780: ! 2781: return(0); /* normal return */ ! 2782: ! 2783: } /* view_keyring */ ! 2784: ! 2785: ! 2786: ! 2787: int remove_from_keyring(byte *keyID, char *mcguffin, char *ringfile) ! 2788: /* Remove the first entry in key ring that has mcguffin string in userid. ! 2789: Or it removes the first matching keyID from the ring. ! 2790: A non-NULL keyID takes precedence over a mcguffin specifier. ! 2791: mcguffin is a null-terminated C string. ! 2792: */ ! 2793: { ! 2794: FILE *f; ! 2795: FILE *g; ! 2796: long file_position,fp,after_key; ! 2797: int packetlength=0; ! 2798: byte ctb; ! 2799: int status; ! 2800: unit n[MAX_UNIT_PRECISION], e[MAX_UNIT_PRECISION]; ! 2801: byte userid[256]; /* key certificate userid */ ! 2802: word32 tstamp; byte *timestamp = (byte *) &tstamp; /* key certificate timestamp */ ! 2803: ! 2804: default_extension(ringfile,PUB_EXTENSION); ! 2805: ! 2806: if ((keyID==NULL) && (strlen(mcguffin)==0)) ! 2807: return(-1); /* error, null mcguffin will match everything */ ! 2808: ! 2809: strcpy(userid,mcguffin); ! 2810: ! 2811: fprintf(stderr,"\nRemoving from key ring: '%s'",ringfile); ! 2812: if (strlen(mcguffin) > 0) ! 2813: fprintf(stderr,", userid \"%s\".\n",mcguffin); ! 2814: ! 2815: status = getpublickey(TRUE, TRUE, ringfile, &fp, &packetlength, NULL, timestamp, userid, n, e); ! 2816: if (status < 0) ! 2817: { fprintf(stderr,"\n\aKey not found in key ring '%s'.\n",ringfile); ! 2818: return(0); /* normal return */ ! 2819: } ! 2820: after_key = fp + packetlength; ! 2821: ! 2822: if (testeq(e,0)) /* This is a key compromise certificate. */ ! 2823: { /* Wish there was a more elegant way to handle this... */ ! 2824: fprintf(stderr,"\n\aWARNING: This is a \"key compromised\" certificate."); ! 2825: fprintf(stderr,"\nIt should not be removed from the key ring!\n"); ! 2826: if (keyID != NULL) /* Decision requires human confirmation. */ ! 2827: return(-1); ! 2828: } ! 2829: ! 2830: if (keyID==NULL) /* Human confirmation is required. */ ! 2831: { /* Supposedly the key was fully displayed by getpublickey */ ! 2832: fprintf(stderr,"\nAre you sure you want this key removed (y/N)? "); ! 2833: if (!getyesno('n')) ! 2834: return(-1); /* user said "no" */ ! 2835: } ! 2836: ! 2837: /* open file f for read, in binary (not text) mode...*/ ! 2838: if ((f = fopen(ringfile,"rb")) == NULL) ! 2839: { fprintf(stderr,"\n\aCan't open key ring file '%s'\n",ringfile); ! 2840: return(-1); ! 2841: } ! 2842: ! 2843: remove(SCRATCH_KEYRING_FILENAME); ! 2844: /* open file g for writing, in binary (not text) mode...*/ ! 2845: if ((g = fopen(SCRATCH_KEYRING_FILENAME,"wb")) == NULL) ! 2846: { fclose(f); ! 2847: return(-1); ! 2848: } ! 2849: rewind(f); ! 2850: copyfile(f,g,fp); /* copy file f to g up to position fp */ ! 2851: fseek(f,after_key,SEEK_SET); /* reposition file to after key */ ! 2852: copyfile(f,g,-1UL); /* copy rest of file from file f to g */ ! 2853: fclose(g); /* close scratch file */ ! 2854: fclose(f); /* close key file */ ! 2855: remove(ringfile); /* dangerous. sure hope rename works... */ ! 2856: rename(SCRATCH_KEYRING_FILENAME,ringfile); ! 2857: fprintf(stderr,"\nKey removed from key ring. "); ! 2858: ! 2859: return(0); /* normal return */ ! 2860: ! 2861: } /* remove_from_keyring */ ! 2862: ! 2863: ! 2864: ! 2865: int addto_keyring(char *keyfile, char *ringfile) ! 2866: /* Adds (prepends) key file to key ring file */ ! 2867: { FILE *f; ! 2868: FILE *g; ! 2869: long file_position,fp; ! 2870: int pktlen; /* unused, just to satisfy getpublickey */ ! 2871: byte ctb; ! 2872: int status; ! 2873: unit n[MAX_UNIT_PRECISION], e[MAX_UNIT_PRECISION]; ! 2874: byte keyID[KEYFRAGSIZE]; ! 2875: byte userid[256]; /* key certificate userid */ ! 2876: word32 tstamp; byte *timestamp = (byte *) &tstamp; /* key certificate timestamp */ ! 2877: boolean keycompromised; ! 2878: ! 2879: if (strcontains(ringfile,SEC_EXTENSION)) ! 2880: force_extension(SCRATCH_KEYRING_FILENAME,SEC_EXTENSION); ! 2881: else ! 2882: force_extension(SCRATCH_KEYRING_FILENAME,PUB_EXTENSION); ! 2883: ! 2884: /* open file f for read, in binary (not text) mode...*/ ! 2885: if ((f = fopen(keyfile,"rb")) == NULL) ! 2886: { fprintf(stderr,"\n\aCan't open key file '%s'\n",keyfile); ! 2887: return(-1); ! 2888: } ! 2889: ! 2890: /* Check to see if the keyID is already in key ring before we add it in. */ ! 2891: ! 2892: file_position = ftell(f); ! 2893: status = readkeypacket(f,FALSE,&ctb,timestamp,userid,n,e, ! 2894: NULL,NULL,NULL,NULL); ! 2895: /* Note that readkeypacket has called set_precision */ ! 2896: if (status < 0) ! 2897: { fprintf(stderr,"\n\aCould not read key from file '%s'.\n", ! 2898: keyfile); ! 2899: fclose(f); /* close key file */ ! 2900: return(-1); ! 2901: } ! 2902: ! 2903: if (!is_ctb_type(ctb,CTB_CERT_PUBKEY_TYPE) ! 2904: && !is_ctb_type(ctb,CTB_CERT_SECKEY_TYPE)) ! 2905: { fprintf(stderr,"\n\aError in file '%s'. Not a key certificate.\n", ! 2906: keyfile); ! 2907: return(-1); ! 2908: } ! 2909: ! 2910: extract_keyID(keyID, n); /* from keyfile, not ringfile */ ! 2911: ! 2912: if (!file_exists(ringfile)) ! 2913: { /* ringfile does not exist. Can it be created? */ ! 2914: /* open file g for writing, in binary (not text) mode...*/ ! 2915: g = fopen(ringfile,"wb"); ! 2916: if (g==NULL) ! 2917: { fprintf(stderr,"\n\aKey ring file '%s' cannot be created.\n",ringfile); ! 2918: fclose(f); ! 2919: return(-1); ! 2920: } ! 2921: fclose(g); ! 2922: } ! 2923: ! 2924: /* See if we are adding a "secret key compromised" certificate: */ ! 2925: keycompromised = testeq(e,0); ! 2926: ! 2927: /* If this is a key compromise certificate, maybe we should ! 2928: remove the real public key from the key ring if it's on the ! 2929: key ring before adding the key compromise certificate. ! 2930: Probably not, though, because the prepended key compromise ! 2931: certificate will take search order precedence. ! 2932: And it may be nice to keep the original public key certificate ! 2933: around for its timestamp, to check old signatures. ! 2934: It should not be possible to later add the same public key to ! 2935: the ring again if the key compromise certificate was there first. ! 2936: ! 2937: These tests for duplicates should have to be applied for all ! 2938: the keys being added to the ring, in case the added key file ! 2939: is itself a multikey ring. Fix this later. ! 2940: */ ! 2941: ! 2942: /* Check for duplicate key in key ring: */ ! 2943: if (getpublickey(TRUE, TRUE, ringfile, &fp, &pktlen, keyID, timestamp, userid, n, e) >= 0) ! 2944: { fprintf(stderr,"\n\aKey already included in key ring '%s'.\n",ringfile); ! 2945: if (!keycompromised) /* allows duplicate if key compromised */ ! 2946: { fclose(f); /* close key file */ ! 2947: return(0); /* normal return */ ! 2948: } ! 2949: } ! 2950: ! 2951: if (keycompromised) ! 2952: fprintf(stderr,"\nAdding \"key compromise\" certificate '%s' to key ring '%s'.\n", ! 2953: keyfile,ringfile); ! 2954: else ! 2955: fprintf(stderr,"\nAdding key certificate '%s' to key ring '%s'.\n",keyfile,ringfile); ! 2956: ! 2957: /* The key is prepended to the ring to give it search precedence ! 2958: over other keys with that same userid. */ ! 2959: ! 2960: fseek(f,file_position,SEEK_SET); /* reposition file to key */ ! 2961: ! 2962: remove(SCRATCH_KEYRING_FILENAME); ! 2963: /* open file g for writing, in binary (not text) mode...*/ ! 2964: if ((g = fopen(SCRATCH_KEYRING_FILENAME,"wb")) == NULL) ! 2965: { fclose(f); ! 2966: return(-1); ! 2967: } ! 2968: copyfile(f,g,-1UL); /* copy rest of file from file f to g */ ! 2969: fclose(f); ! 2970: ! 2971: ! 2972: /* open file f for reading, in binary (not text) mode...*/ ! 2973: if ((f = fopen(ringfile,"rb")) != NULL) ! 2974: { copyfile(f,g,-1UL); /* copy rest of file from file f to g */ ! 2975: fclose(f); ! 2976: } ! 2977: fclose(g); ! 2978: ! 2979: remove(ringfile); /* dangerous. sure hope rename works... */ ! 2980: rename(SCRATCH_KEYRING_FILENAME,ringfile); ! 2981: ! 2982: return(0); /* normal return */ ! 2983: ! 2984: } /* addto_keyring */ ! 2985: ! 2986: ! 2987: /*======================================================================*/ ! 2988: ! 2989: ! 2990: ! 2991: int dokeygen(char *keyfile, char *numstr, char *numstr2) ! 2992: /* Do an RSA key pair generation, and write them out to a pair of files. ! 2993: The keyfile filename string must not have a file extension. ! 2994: numstr is a decimal string, the desired bitcount for the modulus n. ! 2995: numstr2 is a decimal string, the desired bitcount for the exponent e. ! 2996: */ ! 2997: { unit n[MAX_UNIT_PRECISION], e[MAX_UNIT_PRECISION], d[MAX_UNIT_PRECISION], ! 2998: p[MAX_UNIT_PRECISION], q[MAX_UNIT_PRECISION], u[MAX_UNIT_PRECISION]; ! 2999: char fname[64]; ! 3000: char ringfile[64]; ! 3001: byte iv[256]; /* for BassOmatic CFB mode, to protect RSA secret key */ ! 3002: byte userid[256]; ! 3003: short keybits,ebits,i; ! 3004: word32 tstamp; byte *timestamp = (byte *) &tstamp; /* key certificate timestamp */ ! 3005: boolean hidekey; /* TRUE iff secret key is encrypted */ ! 3006: ! 3007: strcpy(fname,keyfile); ! 3008: if (strlen(fname)==0) ! 3009: { fprintf(stderr,"\nKey file name is required for RSA key pair: "); ! 3010: getstring(fname,sizeof(fname)-4,TRUE); ! 3011: } ! 3012: ! 3013: if (strlen(numstr)==0) ! 3014: { fprintf(stderr,"\nPick your RSA key size: " ! 3015: "\n 1) 288 bits- Casual grade, fast but less secure" ! 3016: "\n 2) 512 bits- Commercial grade, medium speed, good security" ! 3017: "\n 3) 992 bits- Military grade, very slow, highest security" ! 3018: "\nChoose 1, 2, or 3, or enter desired number of bits: "); ! 3019: numstr = userid; /* use userid buffer as scratchpad */ ! 3020: getstring(numstr,5,TRUE); /* echo keyboard */ ! 3021: } ! 3022: ! 3023: keybits = 0; ! 3024: while ((*numstr>='0') && (*numstr<='9')) ! 3025: keybits = keybits*10 + (*numstr++ - '0'); ! 3026: ! 3027: /* Standard default key sizes: */ ! 3028: if (keybits==1) keybits=286; /* Casual grade */ ! 3029: if (keybits==2) keybits=510; /* Commercial grade */ ! 3030: if (keybits==3) keybits=990; /* Military grade */ ! 3031: ! 3032: /* minimum RSA keysize for BassOmatic bootstrap: */ ! 3033: if (keybits<286) keybits=286; ! 3034: ! 3035: ebits = 0; /* number of bits in e */ ! 3036: while ((*numstr2>='0') && (*numstr2<='9')) ! 3037: ebits = ebits*10 + (*numstr2++ - '0'); ! 3038: ! 3039: fprintf(stderr,"\nGenerating an RSA key with a %d-bit modulus... ",keybits); ! 3040: ! 3041: fprintf(stderr,"\nEnter a user ID for your public key (your name): "); ! 3042: getstring(userid,255,TRUE); /* echo keyboard input */ ! 3043: CToPascal(userid); /* convert to length-prefixed string */ ! 3044: ! 3045: { char passphrase[256]; ! 3046: fprintf(stderr,"\nYou need a pass phrase to protect your RSA secret key. "); ! 3047: hidekey = (getpassword(passphrase,2,0x0f) > 0); ! 3048: /* init CFB BassOmatic key */ ! 3049: if (hidekey) ! 3050: { fill0(iv,256); /* define initialization vector IV as 0 */ ! 3051: if ( initcfb(iv,passphrase,string_length(passphrase),FALSE) < 0 ) ! 3052: return(-1); ! 3053: burn(passphrase); /* burn sensitive data on stack */ ! 3054: } ! 3055: } ! 3056: ! 3057: fprintf(stderr,"\nNote that key generation is a VERY lengthy process.\n"); ! 3058: ! 3059: if (keygen(n,e,d,p,q,u,keybits,ebits) < 0) ! 3060: { fprintf(stderr,"\n\aKeygen failed!\n"); ! 3061: return(-1); /* error return */ ! 3062: } ! 3063: ! 3064: if (verbose) ! 3065: { ! 3066: fprintf(stderr,"Key ID "); ! 3067: showkeyID2(n); fputc('\n',stderr); ! 3068: ! 3069: mp_display(" modulus n = ",n); ! 3070: mp_display("exponent e = ",e); ! 3071: ! 3072: mp_display("exponent d = ",d); ! 3073: mp_display(" prime p = ",p); ! 3074: mp_display(" prime q = ",q); ! 3075: mp_display(" inverse u = ",u); ! 3076: } ! 3077: ! 3078: get_timestamp(timestamp); /* Timestamp when key was generated */ ! 3079: ! 3080: fputc('\a',stderr); /* sound the bell when done with lengthy process */ ! 3081: ! 3082: force_extension(fname,SEC_EXTENSION); ! 3083: writekeyfile(fname,hidekey,timestamp,userid,n,e,d,p,q,u); ! 3084: force_extension(fname,PUB_EXTENSION); ! 3085: writekeyfile(fname,FALSE,timestamp,userid,n,e,NULL,NULL,NULL,NULL); ! 3086: ! 3087: if (hidekey) /* done with Bassomatic to protect RSA secret key */ ! 3088: closebass(); ! 3089: ! 3090: mp_burn(d); /* burn sensitive data on stack */ ! 3091: mp_burn(p); /* burn sensitive data on stack */ ! 3092: mp_burn(q); /* burn sensitive data on stack */ ! 3093: mp_burn(u); /* burn sensitive data on stack */ ! 3094: mp_burn(e); /* burn sensitive data on stack */ ! 3095: mp_burn(n); /* burn sensitive data on stack */ ! 3096: burn(iv); /* burn sensitive data on stack */ ! 3097: ! 3098: force_extension(fname,PUB_EXTENSION); ! 3099: buildfilename(ringfile,PUBLIC_KEYRING_FILENAME); ! 3100: fprintf(stderr,"\nAdd public key to key ring '%s' (y/N)? ",ringfile); ! 3101: if (getyesno('n')) ! 3102: addto_keyring(fname,ringfile); ! 3103: force_extension(fname,SEC_EXTENSION); ! 3104: buildfilename(ringfile,SECRET_KEYRING_FILENAME); ! 3105: fprintf(stderr,"Add secret key to key ring '%s' (y/N)? ",ringfile); ! 3106: if (getyesno('n')) ! 3107: addto_keyring(fname,ringfile); ! 3108: ! 3109: /* Force initialization of cryptographically strong pseudorandom ! 3110: number generator seed file for later use... ! 3111: */ ! 3112: strong_pseudorandom(iv,1); ! 3113: ! 3114: return(0); /* normal return */ ! 3115: } /* dokeygen */ ! 3116: ! 3117: ! 3118: /*======================================================================*/ ! 3119: ! 3120: ! 3121: void main(int argc, char *argv[]) ! 3122: { char keyfile[64], plainfile[64], cipherfile[64], ringfile[64], tempfile[64]; ! 3123: int status,i; ! 3124: boolean nestflag = FALSE; ! 3125: boolean uu_emit = FALSE; ! 3126: boolean wipeflag = FALSE; ! 3127: byte ctb; ! 3128: byte header[6]; /* used to classify file type at the end. */ ! 3129: char mcguffin[256]; /* userid search tag */ ! 3130: ! 3131: #ifdef DEBUG1 ! 3132: verbose = TRUE; ! 3133: #endif ! 3134: ! 3135: fprintf(stderr,"Pretty Good Privacy 1.0 - RSA public key cryptography for the masses.\n" ! 3136: "(c) Copyright 1990 Philip Zimmermann, Phil's Pretty Good Software. 5 Jun 91\n"); ! 3137: ! 3138: if (argc <= 1) ! 3139: { fprintf(stderr, ! 3140: "\nFor details on free licensing and distribution, see the PGP User's Guide." ! 3141: "\nFor other cryptography products and custom development services, contact:" ! 3142: "\nPhilip Zimmermann, 3021 11th St, Boulder CO 80304 USA, phone (303)444-4541" ! 3143: ); ! 3144: goto usage; ! 3145: } ! 3146: ! 3147: /* Make sure arguments will fit into filename strings: */ ! 3148: for (i = 1; i <= argc; i++) ! 3149: { ! 3150: if (strlen(argv[i]) >= sizeof(cipherfile)-4) ! 3151: { ! 3152: fprintf(stderr, "\aInvalid filename: [%s] too long\n", argv[i] ); ! 3153: goto user_error; ! 3154: } ! 3155: } ! 3156: ! 3157: if (argv[1][0] == '-') ! 3158: { ! 3159: if (strhas(argv[1],'l')) ! 3160: verbose = TRUE; ! 3161: ! 3162: nestflag = strhas(argv[1],'n'); ! 3163: ! 3164: uu_emit = strhas(argv[1],'u'); ! 3165: ! 3166: wipeflag = strhas(argv[1],'w'); ! 3167: ! 3168: /*-------------------------------------------------------*/ ! 3169: if ( (argc >= 3) ! 3170: && strhasany(argv[1],"sS") && strhasany(argv[1],"eE") ) ! 3171: { /* Sign AND encrypt file */ ! 3172: /* Arguments: plainfile, her_userid, your_userid, cipherfile */ ! 3173: boolean separate_signature = FALSE; ! 3174: ! 3175: strcpy( plainfile, argv[2] ); ! 3176: ! 3177: if (argc>=6) /* default signature file extension */ ! 3178: { strcpy( cipherfile, argv[5] ); ! 3179: default_extension( cipherfile, CTX_EXTENSION ); ! 3180: } ! 3181: else ! 3182: { /* Default the signature file name */ ! 3183: strcpy( cipherfile, plainfile ); ! 3184: /* ...but replace file extension: */ ! 3185: force_extension( cipherfile, CTX_EXTENSION ); ! 3186: } ! 3187: ! 3188: if (strcmp( plainfile, cipherfile ) == 0) ! 3189: { fprintf(stderr, "\aFile [%s] must be specified just once.\n", plainfile ); ! 3190: goto user_error; /* same filenames for both files */ ! 3191: } ! 3192: ! 3193: if (argc>=5) ! 3194: { strcpy( mcguffin, argv[4] ); /* Userid of signer */ ! 3195: translate_spaces( mcguffin ); /* change all '_' to ' ' */ ! 3196: } ! 3197: else ! 3198: { fprintf(stderr, "\nEnter userid to look up your secret key for signature: "); ! 3199: getstring( mcguffin, 255, TRUE ); /* echo keyboard */ ! 3200: } ! 3201: ! 3202: if (nestflag) /* user thinks this file has nested info */ ! 3203: { get_header_info_from_file( plainfile, &ctb, 1); ! 3204: if (!legal_ctb(ctb)) ! 3205: { nestflag = FALSE; ! 3206: fprintf(stderr,"\n\aNo nestable data in plaintext file '%s'.\n",plainfile); ! 3207: } ! 3208: } ! 3209: ! 3210: status = signfile( nestflag, separate_signature, ! 3211: mcguffin, plainfile, SCRATCH_CTX_FILENAME ); ! 3212: ! 3213: if (status < 0) /* signfile failed */ ! 3214: { fprintf(stderr, "\aSignature error\n" ); ! 3215: goto user_error; ! 3216: } ! 3217: ! 3218: if (wipeflag) ! 3219: { wipefile(plainfile); /* destroy every trace of plaintext */ ! 3220: remove(plainfile); ! 3221: fprintf(stderr,"\nFile %s wiped and deleted. ",plainfile); ! 3222: } ! 3223: ! 3224: if (argc>=4) ! 3225: { strcpy( mcguffin, argv[3] ); /* Userid of recipient */ ! 3226: translate_spaces( mcguffin ); /* change all '_' to ' ' */ ! 3227: } ! 3228: else ! 3229: { fprintf(stderr, "\nEnter userid to look up recipient's public key: "); ! 3230: getstring( mcguffin, 255, TRUE ); /* echo keyboard */ ! 3231: } ! 3232: ! 3233: /* Indicate that encrypted data has nested signature: */ ! 3234: ! 3235: status = encryptfile( TRUE, mcguffin, SCRATCH_CTX_FILENAME, cipherfile ); ! 3236: ! 3237: wipefile( SCRATCH_CTX_FILENAME ); ! 3238: remove( SCRATCH_CTX_FILENAME ); ! 3239: ! 3240: if (status < 0) ! 3241: { fprintf(stderr, "\aEncryption error\n" ); ! 3242: goto user_error; ! 3243: } ! 3244: ! 3245: if (uu_emit) ! 3246: { status = uue_file(cipherfile, SCRATCH_CTX_FILENAME); ! 3247: remove(cipherfile); /* dangerous. sure hope rename works... */ ! 3248: rename(SCRATCH_CTX_FILENAME, cipherfile); ! 3249: } ! 3250: ! 3251: if (!verbose) /* if other filename messages were supressed */ ! 3252: fprintf(stderr,"\nCiphertext file: %s ", cipherfile); ! 3253: ! 3254: exit(0); ! 3255: } /* Sign AND encrypt file */ ! 3256: ! 3257: ! 3258: /*-------------------------------------------------------*/ ! 3259: if ( (argc >= 3) && strhasany(argv[1],"sS") ) ! 3260: { /* Sign file ! 3261: Arguments: plaintextfile, your_userid, signedtextfile ! 3262: Two kinds of signature: full signature certificate, ! 3263: or just an RSA-signed message digest. ! 3264: */ ! 3265: ! 3266: boolean separate_signature = FALSE; ! 3267: ! 3268: separate_signature = strhas( argv[1], 'b' ); ! 3269: ! 3270: strcpy( plainfile, argv[2] ); ! 3271: ! 3272: if (argc>=5) /* default signature file extension */ ! 3273: { strcpy( cipherfile, argv[4] ); ! 3274: default_extension( cipherfile, CTX_EXTENSION ); ! 3275: } ! 3276: else ! 3277: { /* Default the signature file name */ ! 3278: strcpy( cipherfile, plainfile ); ! 3279: /* ...but replace file extension: */ ! 3280: force_extension( cipherfile, CTX_EXTENSION ); ! 3281: } ! 3282: ! 3283: if (strcmp( plainfile, cipherfile ) == 0) ! 3284: { fprintf(stderr, "\aFile [%s] must be specified just once.\n", plainfile ); ! 3285: goto user_error; /* same filenames for both files */ ! 3286: } ! 3287: ! 3288: if (argc>=4) ! 3289: { strcpy( mcguffin, argv[3] ); /* Userid of signer */ ! 3290: translate_spaces( mcguffin ); /* change all '_' to ' ' */ ! 3291: } ! 3292: else ! 3293: { fprintf(stderr, "\nEnter userid to look up your secret key for signature: "); ! 3294: getstring( mcguffin, 255, TRUE ); /* echo keyboard */ ! 3295: } ! 3296: ! 3297: if (nestflag) /* user thinks this file has nested info */ ! 3298: { get_header_info_from_file( plainfile, &ctb, 1); ! 3299: if (!legal_ctb(ctb)) ! 3300: { nestflag = FALSE; ! 3301: fprintf(stderr,"\n\aNo nestable data in plaintext file '%s'.\n",plainfile); ! 3302: } ! 3303: } ! 3304: ! 3305: status = signfile( nestflag, separate_signature, ! 3306: mcguffin, plainfile, cipherfile ); ! 3307: ! 3308: if (status < 0) /* signfile failed */ ! 3309: { fprintf(stderr, "\aSignature error\n" ); ! 3310: goto user_error; ! 3311: } ! 3312: ! 3313: if (uu_emit) ! 3314: { status = uue_file(cipherfile, SCRATCH_CTX_FILENAME); ! 3315: remove(cipherfile); /* dangerous. sure hope rename works... */ ! 3316: rename(SCRATCH_CTX_FILENAME, cipherfile); ! 3317: } ! 3318: ! 3319: if (!verbose) /* if other filename messages were supressed */ ! 3320: fprintf(stderr,"\nSignature file: %s ", cipherfile); ! 3321: ! 3322: exit(0); ! 3323: } /* Sign file */ ! 3324: ! 3325: ! 3326: /*-------------------------------------------------------*/ ! 3327: if ( (argc >= 3) && strhasany(argv[1],"eE") ) ! 3328: { /* Encrypt file ! 3329: Arguments: plaintextfile, her_userid, ciphertextfile ! 3330: */ ! 3331: ! 3332: strcpy( plainfile, argv[2] ); ! 3333: ! 3334: if (argc >= 5) /* default cipher file extension */ ! 3335: { strcpy( cipherfile, argv[4] ); ! 3336: default_extension( cipherfile, CTX_EXTENSION ); ! 3337: } ! 3338: else ! 3339: { /* Default the cipherfile name */ ! 3340: strcpy( cipherfile, plainfile ); ! 3341: force_extension( cipherfile, CTX_EXTENSION ); ! 3342: } ! 3343: ! 3344: if (strcmp( plainfile, cipherfile) == 0) ! 3345: { fprintf(stderr, "\aFile [%s] must be specified just once.\n", plainfile ); ! 3346: goto user_error; /* same filenames for both files */ ! 3347: } ! 3348: ! 3349: if (argc >= 4) ! 3350: { strcpy( mcguffin, argv[3] ); /* Userid of recipient */ ! 3351: translate_spaces( mcguffin ); /* change all '_' to ' ' */ ! 3352: } ! 3353: else ! 3354: { fprintf(stderr, "\nEnter userid to look up recipient's public key: "); ! 3355: getstring( mcguffin, 255, TRUE ); /* echo keyboard */ ! 3356: } ! 3357: ! 3358: if (nestflag) /* user thinks this file has nested info */ ! 3359: { get_header_info_from_file( plainfile, &ctb, 1); ! 3360: if (!legal_ctb(ctb)) ! 3361: { nestflag = FALSE; ! 3362: fprintf(stderr,"\n\aNo nestable data in plaintext file '%s'.\n",plainfile); ! 3363: } ! 3364: } ! 3365: ! 3366: if (!nestflag) ! 3367: { /* Prepend CTB_LITERAL byte to plaintext file. ! 3368: --sure wish this pass could be optimized away. */ ! 3369: strcpy( tempfile, plainfile ); ! 3370: strcpy( plainfile, SCRATCH_PTX_FILENAME ); ! 3371: status = make_literal( tempfile, plainfile ); ! 3372: } ! 3373: status = encryptfile( nestflag, mcguffin, plainfile, cipherfile ); ! 3374: ! 3375: if (!nestflag) ! 3376: { wipefile( SCRATCH_PTX_FILENAME ); ! 3377: remove( SCRATCH_PTX_FILENAME ); ! 3378: strcpy( plainfile, tempfile ); ! 3379: } ! 3380: ! 3381: if (status < 0) /* encryptfile failed */ ! 3382: { fprintf(stderr, "\aEncryption error\n" ); ! 3383: goto user_error; ! 3384: } ! 3385: ! 3386: if (wipeflag) ! 3387: { wipefile(plainfile); /* destroy every trace of plaintext */ ! 3388: remove(plainfile); ! 3389: fprintf(stderr,"\nFile %s wiped and deleted. ",plainfile); ! 3390: } ! 3391: ! 3392: if (uu_emit) ! 3393: { status = uue_file(cipherfile, SCRATCH_CTX_FILENAME); ! 3394: remove(cipherfile); /* dangerous. sure hope rename works... */ ! 3395: rename(SCRATCH_CTX_FILENAME, cipherfile); ! 3396: } ! 3397: ! 3398: if (!verbose) /* if other filename messages were supressed */ ! 3399: fprintf(stderr,"\nCiphertext file: %s ", cipherfile); ! 3400: ! 3401: exit(0); ! 3402: } /* Encrypt file */ ! 3403: ! 3404: ! 3405: /*-------------------------------------------------------*/ ! 3406: if ( (argc >= 3) && strhasany(argv[1],"cC") ) ! 3407: { /* Encrypt file with BassOmatic only ! 3408: Arguments: plaintextfile, ciphertextfile ! 3409: */ ! 3410: ! 3411: strcpy( plainfile, argv[2] ); ! 3412: ! 3413: if (argc >= 4) /* default cipher file extension */ ! 3414: { strcpy( cipherfile, argv[3] ); ! 3415: default_extension( cipherfile, CTX_EXTENSION ); ! 3416: } ! 3417: else ! 3418: { /* Default the cipherfile name */ ! 3419: strcpy( cipherfile, plainfile ); ! 3420: force_extension( cipherfile, CTX_EXTENSION ); ! 3421: } ! 3422: ! 3423: if (strcmp( plainfile, cipherfile) == 0) ! 3424: { fprintf(stderr, "\aFile [%s] must be specified just once.\n", plainfile ); ! 3425: goto user_error; /* same filenames for both files */ ! 3426: } ! 3427: ! 3428: if (nestflag) /* user thinks this file has nested info */ ! 3429: { get_header_info_from_file( plainfile, &ctb, 1); ! 3430: if (!legal_ctb(ctb)) ! 3431: { nestflag = FALSE; ! 3432: fprintf(stderr,"\n\aNo nestable data in plaintext file '%s'.\n",plainfile); ! 3433: } ! 3434: } ! 3435: ! 3436: if (!nestflag) ! 3437: { /* Prepend CTB_LITERAL byte to plaintext file. ! 3438: --sure wish this pass could be optimized away. */ ! 3439: strcpy( tempfile, plainfile ); ! 3440: strcpy( plainfile, SCRATCH_PTX_FILENAME ); ! 3441: status = make_literal( tempfile, plainfile ); ! 3442: } ! 3443: ! 3444: status = bass_encryptfile( nestflag, plainfile, cipherfile ); ! 3445: ! 3446: if (!nestflag) ! 3447: { wipefile( SCRATCH_PTX_FILENAME ); ! 3448: remove( SCRATCH_PTX_FILENAME ); ! 3449: strcpy( plainfile, tempfile ); ! 3450: } ! 3451: ! 3452: if (status < 0) /* encryptfile failed */ ! 3453: { fprintf(stderr, "\aEncryption error\n" ); ! 3454: goto user_error; ! 3455: } ! 3456: ! 3457: if (wipeflag) ! 3458: { wipefile(plainfile); /* destroy every trace of plaintext */ ! 3459: remove(plainfile); ! 3460: fprintf(stderr,"\nFile %s wiped and deleted. ",plainfile); ! 3461: } ! 3462: ! 3463: if (uu_emit) ! 3464: { status = uue_file(cipherfile, SCRATCH_CTX_FILENAME); ! 3465: remove(cipherfile); /* dangerous. sure hope rename works... */ ! 3466: rename(SCRATCH_CTX_FILENAME, cipherfile); ! 3467: } ! 3468: ! 3469: if (!verbose) /* if other filename messages were supressed */ ! 3470: fprintf(stderr,"\nCiphertext file: %s ", cipherfile); ! 3471: ! 3472: exit(0); ! 3473: } /* Encrypt file with BassOmatic only */ ! 3474: ! 3475: ! 3476: /*-------------------------------------------------------*/ ! 3477: if (argv[1][1] == 'k') ! 3478: { /* Key generation ! 3479: Arguments: keyfile, bitcount, bitcount ! 3480: */ ! 3481: char keyfile[64], keybits[6], ebits[6]; ! 3482: ! 3483: if (argc > 2) ! 3484: strcpy( keyfile, argv[2] ); ! 3485: else ! 3486: strcpy( keyfile, "" ); ! 3487: ! 3488: if (argc > 3) ! 3489: strncpy( keybits, argv[3], sizeof(keybits)-1 ); ! 3490: else ! 3491: strcpy( keybits, "" ); ! 3492: ! 3493: if (argc > 4) ! 3494: strncpy( ebits, argv[4], sizeof(ebits)-1 ); ! 3495: else ! 3496: strcpy( ebits, "" ); ! 3497: ! 3498: status = dokeygen( keyfile, keybits, ebits ); ! 3499: ! 3500: if (status < 0) ! 3501: { fprintf(stderr, "\aKeygen error. " ); ! 3502: goto user_error; ! 3503: } ! 3504: exit(0); ! 3505: } /* Key generation */ ! 3506: ! 3507: /*-------------------------------------------------------*/ ! 3508: if ((argc >= 3) && (argv[1][1] == 'a')) ! 3509: { /* Add key to key ring ! 3510: Arguments: keyfile, ringfile ! 3511: */ ! 3512: if (argc < 4) /* default key ring filename */ ! 3513: buildfilename( ringfile, PUBLIC_KEYRING_FILENAME ); ! 3514: else ! 3515: strncpy( ringfile, argv[3], sizeof(ringfile)-1 ); ! 3516: strncpy( keyfile, argv[2], sizeof(keyfile)-1 ); ! 3517: ! 3518: strlwr( keyfile ); ! 3519: strlwr( ringfile ); ! 3520: if (! file_exists( keyfile )) ! 3521: default_extension( keyfile, PUB_EXTENSION ); ! 3522: ! 3523: if (strcontains( keyfile, SEC_EXTENSION )) ! 3524: force_extension( ringfile, SEC_EXTENSION ); ! 3525: else ! 3526: force_extension( ringfile, PUB_EXTENSION ); ! 3527: ! 3528: if (! file_exists( keyfile )) ! 3529: { fprintf(stderr, "\n\aKey file '%s' does not exist.\n", keyfile ); ! 3530: goto user_error; ! 3531: } ! 3532: ! 3533: status = addto_keyring( keyfile, ringfile ); ! 3534: ! 3535: if (status < 0) ! 3536: { fprintf(stderr, "\aKeyring add error. " ); ! 3537: goto user_error; ! 3538: } ! 3539: exit(0); ! 3540: } /* Add key to key ring */ ! 3541: ! 3542: /*-------------------------------------------------------*/ ! 3543: if ((argc >= 2) ! 3544: && strhasany( argv[1], "vr" ) ) ! 3545: { /* View or remove key ring entries, with userid match ! 3546: Arguments: userid, ringfile ! 3547: */ ! 3548: if (argc < 4) /* default key ring filename */ ! 3549: buildfilename( ringfile, PUBLIC_KEYRING_FILENAME ); ! 3550: else ! 3551: strcpy( ringfile, argv[3] ); ! 3552: ! 3553: strcpy( mcguffin, argv[2] ); ! 3554: if (strcmp( mcguffin, "*" ) == 0) ! 3555: strcpy( mcguffin, "" ); ! 3556: ! 3557: translate_spaces( mcguffin ); /* change all '_' to ' ' */ ! 3558: ! 3559: if ((argc < 4) ! 3560: && (strcontains( argv[2], PUB_EXTENSION ) ! 3561: || strcontains( argv[2], SEC_EXTENSION ))) ! 3562: { strcpy( ringfile, argv[2] ); ! 3563: strcpy( mcguffin, "" ); ! 3564: } ! 3565: ! 3566: strlwr( ringfile ); ! 3567: if (! file_exists( ringfile )) ! 3568: default_extension( ringfile, PUB_EXTENSION ); ! 3569: ! 3570: if (strhas( argv[1], 'v' )) ! 3571: if (view_keyring( mcguffin, ringfile ) < 0) ! 3572: { fprintf(stderr, "\aKeyring view error. " ); ! 3573: goto user_error; ! 3574: } ! 3575: if (strhas( argv[1], 'r' )) ! 3576: if (remove_from_keyring( NULL, mcguffin, ringfile ) < 0) ! 3577: { fprintf(stderr, "\aKeyring remove error. " ); ! 3578: goto user_error; ! 3579: } ! 3580: exit(0); ! 3581: } /* view or remove key ring entries, with userid match */ ! 3582: /*-------------------------------------------------------*/ ! 3583: ! 3584: fprintf(stderr, "\aUnrecognizable parameters. " ); ! 3585: goto user_error; ! 3586: } /* -options specified */ ! 3587: ! 3588: ! 3589: /*---------------------------------------------------------*/ ! 3590: /* no options specified */ ! 3591: ! 3592: if (argc >= 2) ! 3593: { /* Decrypt file ! 3594: Arguments: ciphertextfile, plaintextfile ! 3595: */ ! 3596: strcpy( cipherfile, argv[1] ); ! 3597: default_extension( cipherfile, CTX_EXTENSION ); ! 3598: ! 3599: if (argc >= 3) ! 3600: { strcpy( plainfile, argv[2] ); ! 3601: default_extension( plainfile, "." ); ! 3602: } ! 3603: else ! 3604: { /* Default the plaintext file name */ ! 3605: strcpy( plainfile, argv[1] ); ! 3606: force_extension( plainfile, "." ); ! 3607: } ! 3608: ! 3609: if (strcmp( plainfile, cipherfile ) == 0) ! 3610: { fprintf(stderr, "\aFile '%s' cannot be both input and output file.\n", plainfile ); ! 3611: goto user_error; /* error: same filenames for both files */ ! 3612: } ! 3613: ! 3614: if (! file_exists( cipherfile )) ! 3615: { fprintf(stderr, "\a\nError: Cipher or signature file '%s' does not exist.\n", ! 3616: cipherfile); ! 3617: goto user_error; ! 3618: } ! 3619: ! 3620: get_header_info_from_file( cipherfile, header, 4 ); ! 3621: if (!is_ctb(header[0]) && is_uufile(cipherfile)) ! 3622: { ! 3623: if (verbose) fprintf(stderr,"uudecoding %s...",cipherfile); ! 3624: status = uud_file(cipherfile, SCRATCH_CTX_FILENAME); ! 3625: if (status==0) ! 3626: { if (verbose) fprintf(stderr,"...done.\n"); ! 3627: remove( cipherfile ); /* dangerous. sure hope rename works... */ ! 3628: rename( SCRATCH_CTX_FILENAME, cipherfile ); ! 3629: } ! 3630: else fprintf(stderr,"\n\aError: uudecode failed for file %s\n",cipherfile); ! 3631: } ! 3632: ! 3633: /*---------------------------------------------------------*/ ! 3634: do /* while nested parsable info present */ ! 3635: { ! 3636: if (get_header_info_from_file( cipherfile, &ctb, 1) < 0) ! 3637: { fprintf(stderr,"\n\aCan't open ciphertext file '%s'\n",cipherfile); ! 3638: goto user_error; ! 3639: } ! 3640: ! 3641: if (!is_ctb(ctb)) /* not a real CTB -- complain */ ! 3642: goto reject; ! 3643: ! 3644: /* PKE is Public Key Encryption */ ! 3645: if (is_ctb_type( ctb, CTB_PKE_TYPE )) ! 3646: { ! 3647: fprintf(stderr,"\nFile is encrypted. Secret key is required to read it. "); ! 3648: ! 3649: status = decryptfile( cipherfile, plainfile ); ! 3650: ! 3651: if (status < 0) /* error return */ ! 3652: goto user_error; ! 3653: if (status < 1) /* output file has no nested info? */ ! 3654: break; /* no nested parsable info. exit loop. */ ! 3655: ! 3656: /* Nested parsable info indicated. Process it. */ ! 3657: wipefile( SCRATCH_CTX_FILENAME ); ! 3658: remove( SCRATCH_CTX_FILENAME ); ! 3659: rename( plainfile, SCRATCH_CTX_FILENAME ); ! 3660: strcpy( cipherfile, SCRATCH_CTX_FILENAME ); ! 3661: continue; /* skip rest of loop */ ! 3662: } /* outer CTB is PKE type */ ! 3663: ! 3664: ! 3665: if (is_ctb_type( ctb, CTB_SKE_TYPE )) ! 3666: { ! 3667: fprintf(stderr,"\nFile has signature. Public key is required to check signature. "); ! 3668: ! 3669: status = check_signaturefile( cipherfile, plainfile ); ! 3670: ! 3671: if (status < 0) /* error return */ ! 3672: goto user_error; ! 3673: ! 3674: if (status < 1) /* output file has no nested info? */ ! 3675: break; /* no nested parsable info. exit loop. */ ! 3676: ! 3677: /* Nested parsable info indicated. Process it. */ ! 3678: /* Destroy signed plaintext, if it's in a scratchfile. */ ! 3679: wipefile( SCRATCH_CTX_FILENAME ); ! 3680: remove( SCRATCH_CTX_FILENAME ); ! 3681: rename( plainfile, SCRATCH_CTX_FILENAME ); ! 3682: strcpy( cipherfile, SCRATCH_CTX_FILENAME ); ! 3683: continue; /* skip rest of loop */ ! 3684: } /* outer CTB is SKE type */ ! 3685: ! 3686: ! 3687: if (ctb == CTB_CKE) ! 3688: { /* Conventional Key Encrypted ciphertext. */ ! 3689: fprintf(stderr,"\nFile is conventionally encrypted. Pass phrase required to read it. "); ! 3690: status = bass_decryptfile( cipherfile, plainfile ); ! 3691: if (status < 0) /* error return */ ! 3692: goto user_error; ! 3693: if (status < 1) /* output file has no nested info? */ ! 3694: break; /* no nested parsable info. exit loop. */ ! 3695: /* Nested parsable info indicated. Process it. */ ! 3696: wipefile( SCRATCH_CTX_FILENAME ); ! 3697: remove( SCRATCH_CTX_FILENAME ); ! 3698: rename( plainfile, SCRATCH_CTX_FILENAME ); ! 3699: strcpy( cipherfile, SCRATCH_CTX_FILENAME ); ! 3700: continue; /* skip rest of loop */ ! 3701: } /* CTB is CKE type */ ! 3702: ! 3703: ! 3704: if (is_ctb_type( ctb, CTB_COMPRESSED_TYPE )) ! 3705: { /* Compressed text. */ ! 3706: status = decompress_file( cipherfile, plainfile ); ! 3707: if (status < 0) /* error return */ ! 3708: goto user_error; ! 3709: /* Always assume nested information... */ ! 3710: /* Destroy compressed plaintext, if it's in a scratchfile. */ ! 3711: wipefile( SCRATCH_CTX_FILENAME ); ! 3712: remove( SCRATCH_CTX_FILENAME ); ! 3713: rename( plainfile, SCRATCH_CTX_FILENAME ); ! 3714: strcpy( cipherfile, SCRATCH_CTX_FILENAME ); ! 3715: continue; /* skip rest of loop */ ! 3716: } /* CTB is COMPRESSED type */ ! 3717: ! 3718: ! 3719: if (is_ctb_type( ctb, CTB_LITERAL_TYPE )) ! 3720: { /* Raw plaintext. Just copy it. No more nesting. */ ! 3721: /* Strip off CTB_LITERAL prefix byte from file: */ ! 3722: status = strip_literal( cipherfile, plainfile ); ! 3723: break; /* no nested parsable info. exit loop. */ ! 3724: } /* CTB is LITERAL type */ ! 3725: ! 3726: ! 3727: if ((ctb == CTB_CERT_SECKEY) ! 3728: || (ctb == CTB_CERT_PUBKEY)) ! 3729: { /* Key ring. View it. */ ! 3730: fprintf(stderr, "\nFile contains key(s). Contents follow..." ); ! 3731: ! 3732: if (view_keyring( NULL, cipherfile ) < 0) ! 3733: goto user_error; ! 3734: ! 3735: /* No output file--what should we do with plainfile? ! 3736: We know that we have already prevented original ! 3737: cipher filename from being same as plain filename. ! 3738: */ ! 3739: ! 3740: if (strcmp( cipherfile, SCRATCH_CTX_FILENAME ) == 0) ! 3741: { /* key was nested in signed or enciphered file */ ! 3742: remove( plainfile ); ! 3743: rename( cipherfile, plainfile ); ! 3744: } ! 3745: exit(0); /* no nested parsable info. */ ! 3746: /* strcpy(plainfile,""); /* no further nesting */ ! 3747: /* break; /* no nested parsable info. exit loop. */ ! 3748: } /* key ring. view it. */ ! 3749: ! 3750: reject: fprintf(stderr,"\a\nError: '%s' is not a cipher, signature, or key file.\n", ! 3751: cipherfile); ! 3752: goto user_error; ! 3753: ! 3754: } while (TRUE); ! 3755: ! 3756: /* No more nested parsable information */ ! 3757: ! 3758: /* Destroy any sensitive information in scratchfile: */ ! 3759: wipefile( SCRATCH_CTX_FILENAME ); ! 3760: remove( SCRATCH_CTX_FILENAME ); ! 3761: ! 3762: if (!verbose) /* if other filename messages were supressed */ ! 3763: fprintf(stderr,"\nPlaintext filename: %s ", plainfile); ! 3764: ! 3765: ! 3766: /*---------------------------------------------------------*/ ! 3767: ! 3768: /* One last thing-- let's attempt to classify some of the more ! 3769: frequently occurring cases of plaintext output files, as an ! 3770: aid to the user. ! 3771: ! 3772: For example, if output file is a public key, it should have ! 3773: the right extension on the filename. ! 3774: ! 3775: Also, it will likely be common to encrypt PKZIP files, so ! 3776: they should be renamed with the .zip extension. ! 3777: */ ! 3778: get_header_info_from_file( plainfile, header, 4 ); ! 3779: ! 3780: if (header[0] == CTB_CERT_PUBKEY) ! 3781: { /* Special case--may be public key, worth renaming */ ! 3782: fprintf(stderr, "\nPlaintext file '%s' looks like it contains a public key.", ! 3783: plainfile ); ! 3784: maybe_force_extension( plainfile, PUB_EXTENSION ); ! 3785: } /* Possible public key output file */ ! 3786: ! 3787: else ! 3788: if (pkzipSignature( header )) ! 3789: { /* Special case--may be a PKZIP file, worth renaming */ ! 3790: fprintf(stderr, "\nPlaintext file '%s' looks like a PKZIP file.", ! 3791: plainfile ); ! 3792: maybe_force_extension( plainfile, ".zip" ); ! 3793: } /* Possible PKZIP output file */ ! 3794: ! 3795: else ! 3796: if ((header[0] == CTB_PKE) ! 3797: || (header[0] == CTB_SKE) ! 3798: || (header[0] == CTB_CKE)) ! 3799: { /* Special case--may be another ciphertext file, worth renaming */ ! 3800: fprintf(stderr, "\n\aOutput file '%s' may contain more ciphertext or signature.", ! 3801: plainfile ); ! 3802: maybe_force_extension( plainfile, ".ctx" ); ! 3803: } /* Possible ciphertext output file */ ! 3804: ! 3805: exit(0); /* no nested parsable info. */ ! 3806: ! 3807: } /* Decrypt file, or check signature, or show key */ ! 3808: ! 3809: ! 3810: user_error: /* comes here if user made a boo-boo. */ ! 3811: fprintf(stderr,"\nFor more help, consult the PGP User's Guide."); ! 3812: ! 3813: usage: ! 3814: fprintf(stderr,"\nUsage summary:"); ! 3815: fprintf(stderr,"\nTo encrypt a plaintext file with recipent's public key, type:"); ! 3816: fprintf(stderr,"\n pgp -e textfile her_userid (produces textfile.ctx)"); ! 3817: fprintf(stderr,"\nTo sign a plaintext file with your secret key, type:"); ! 3818: fprintf(stderr,"\n pgp -s textfile your_userid (produces textfile.ctx)"); ! 3819: fprintf(stderr,"\nTo sign a plaintext file with your secret key, and then encrypt it " ! 3820: "\n with recipent's public key, producing a .ctx file:"); ! 3821: fprintf(stderr,"\n pgp -es textfile her_userid your_userid"); ! 3822: fprintf(stderr,"\nTo encrypt with conventional encryption only: pgp -c textfile"); ! 3823: fprintf(stderr,"\nTo decrypt or check a signature for a ciphertext (.ctx) file:"); ! 3824: fprintf(stderr,"\n pgp ciphertextfile [plaintextfile]"); ! 3825: fprintf(stderr,"\nTo generate your own unique public/secret key pair, type: pgp -k"); ! 3826: fprintf(stderr,"\nTo add a public or secret key file's contents to your public " ! 3827: "\n or secret key ring: pgp -a keyfile [keyring]"); ! 3828: fprintf(stderr,"\nTo remove a key from your public key ring: pgp -r userid [keyring]"); ! 3829: fprintf(stderr,"\nTo view the contents of your public key ring: pgp -v [userid] [keyring] "); ! 3830: exit(1); /* error exit */ ! 3831: ! 3832: } /* main */ ! 3833:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.