//**************************************************************************** // mkotp.cpp // One-time-pad encryption/decryption utility. // // Notice // Copyright ©1996-97, David R. Tribble. // // This source code is copyrighted. Distribution of this source code is // granted, provided that this copyright notice remains attached to the // source code, and that adequate acknowledgement is given to the // original author. // // History // 1.00 1996-02-12 dtribble. // First cut. // 1.01 1996-03-27 dtribble. // Increased MAX_RUNC value to 80. // 1.02 1997-08-14 dtribble. // Decreased MAX_RUNC value to 40. // Identification #define ID_TITLE "MKOTP" #define ID_DATE "1997-08-14" #define ID_VERS "1.02" #define ID_FILE __FILE__ #define ID_AUTH "David R. Tribble" #define ID_COMP __DATE__ " " __TIME__ static const char copyright[] = "Copyright ©1996-97, " ID_AUTH "."; static const char id[] = "@(#) " ID_DATE " " ID_VERS " " ID_FILE; static const char id_compile[] = "[" ID_TITLE " " ID_VERS ", " ID_DATE "] <" ID_COMP ">"; static const char title[] = ID_TITLE; static const char date[] = ID_DATE; static const char vers[] = ID_VERS; static const char auth[] = ID_AUTH; static const char file[] = ID_FILE; static const char * prog = title; // System includes #include #include #include #include #include #include #include #include #include // Local includes #include "osdefs.h" #include "drtmem.h" #include "debug.h" #include "fotplib.hpp" #if !DEBUG #undef DL #define DL(e) (0) #endif // Return codes #define RC_OKAY 0 // Successful #define RC_ENV 1 // Environment var not set #define RC_OUTF 2 // Can't open/write output file #define RC_TTY 3 // Can't open/read tty device #define RC_NOTTY 4 // Tty stream is not a tty #define RC_READ 5 // Can't read input #define RC_WRITE 6 // Can't write output #define RC_STAT 7 // Can't stat a file #define RC_SEEK 8 // Can't seek a file #define RC_USAGE 255 // Invalid usage // Private constants #define MAX_RUNC 40 // Max run length of nonrepeating pad data // Private variables struct ltime { time_t sec; // Seconds unsigned long usec; // Microseconds }; struct option { const char * infname; // Input file name FILE * infp; // Input file const char * outfname; // Output file name FILE * outfp; // Output file int verbose; // Verbose messages int r; // Pseudo-random seed int a; // Pseudo-random coefficient A int b; // Pseudo-random coefficient B }; static struct option opt; //**************************************************************************** // make_otp // Make a one-time pad out of data read from stream `in', writing it out // to stream `out'. // // Returns // RC_OKAY on success, otherwise a nonzero RC_xxx value. static int make_otp(FILE *in, FILE *out) { int rc = RC_OKAY; int byte; int i; int obufc; unsigned char obuf[MAX_RUNC]; unsigned long icount, ocount; // Read data bytes from input DL(dbgf("Read from %p, write to %p\n", in, out)); icount = 0; ocount = 0; obufc = 0; for (i = 0; i < MAX_RUNC; i++) obuf[i] = 0x00; while (byte = getc(in), byte != EOF) { icount++; // Cull out unacceptable bytes switch (byte) { case 0x00: case 0x80: goto another; } // Scramble the byte if (opt.r != 0) { byte ^= opt.r; byte = (unsigned char) byte; opt.r = (((opt.r ^ opt.a) + opt.b) & 0x7FFF) + 1; } // Cull out unacceptable bytes byte &= 0xFF; switch (byte) { case 0x00: case 0x80: case 0x40: goto another; } // Cull out this byte if it has previously appeared recently for (i = 0; i < MAX_RUNC; i++) if (byte == obuf[i]) goto another; // Write OTP data byte putc(byte, out); ocount++; // Append data byte to recent byte list obuf[obufc++] = byte; if (obufc >= MAX_RUNC) obufc = 0; another: ; } fflush(out); DL(dbgf("%lu Bytes in, %lu bytes out\n", icount, ocount)); if (opt.verbose) fprintf(stderr, "%lu Bytes in, %lu bytes out\n", icount, ocount); // Done DL(dbgf("Return %d\n", rc)); return (rc); } //**************************************************************************** // about // Print info about this program. static const char * about_m[] = { id_compile, #if DEBUG "(with debugs)", #endif "", copyright, "", "If you find this program useful, you may register it by sending the", "version and date shown above along with your name and address and a", "donation of $10.00(US) to:", "", " David R. Tribble", " 6004 Cave River Dr.", " Plano, TX 75093-6951", " USA", "", " dtribble@technologist.com", " dtribble@flash.net", NULL }; static void about() { int i; // Print `about' text for (i = 0; about_m[i] != NULL; i++) fprintf(stdout, "%s\n", about_m[i]); } //**************************************************************************** // usage // Print usage message, then punt. // // Returns // Doesn't, calls exit(). static const char * usage_m[] = { "If `file' is `-', input is read from standard input.", "", "Options:", " -? About this program.", #if DEBUG " -D Enable debugging output.", #endif " -o file Output is `file' (default is standard output).", " -r A,B Scrambling function numbers `A' and `B'.", // " -s num Skip (ignore) the first `num' bytes of the file.", " -v Verbose output.", NULL }; static void usage() { int i; // Print usage message fprintf(stderr, "[%s %s, %s]%s\n\n", title, vers, date, (DEBUG ? " +debug" : "")); fprintf(stderr, "Make a one-time pad from one or more data files.\n\n"); fprintf(stderr, "usage: %s [-option...] file...\n\n", prog); for (i = 0; usage_m[i] != NULL; i++) fprintf(stderr, "%s\n", usage_m[i]); exit(RC_USAGE); } //**************************************************************************** // get_options // Parse command line options. // // Returns // Number of argv[] strings parsed. static int get_options(int argc, char **argv) { int optind = 1; int optch; char * optarg = NULL; char * env; // Parse command line options DL(dbgf("getoptions, argc=%d\n", argc)); if (opt.verbose) fprintf(stderr, "Options:"); for (optind = 1; optind < argc && argv[optind][0] == '-' && argv[optind][1] != '\0'; optind++) { // Parse next command line arg DL(dbgf("argv[optind=%d]=`%s'\n", optind, argv[optind])); optarg = NULL; while (argv[optind]++, optch = argv[optind][0], optch != '\0') { // Parse next single command line option optarg = argv[optind]+1; DL(dbgf("optch=`%c' optarg=`%s'\n", optch, optarg)); switch (optch) { case 'D': // -D, debugging output #if DEBUG dbg_f = stderr; #endif optarg = NULL; break; case 'o': // -o file, output file name if (optarg[0] == '\0') { optarg = argv[optind+1]; optind++; if (optind >= argc) goto bad_usage; } opt.outfname = optarg; DL(dbgf("opt.outfname=`%s'\n", opt.outfname)); goto next_arg; case 'r': // -r A,B, pseudo-randomizing coefficients if (optarg[0] == '\0') { optarg = argv[optind+1]; optind++; if (optind >= argc) goto bad_usage; } opt.a = 4 * atoi(optarg) + 3; DL(dbgf("opt.a=%d\n", opt.a)); env = strchr(optarg, ','); if (env == NULL) goto bad_usage; optarg = env+1; if (optarg[0] == '\0') goto bad_usage; opt.b = 4 * atoi(optarg) + 3; DL(dbgf("opt.b=%d\n", opt.b)); opt.r = 1; goto next_arg; case 'v': // -v, verbose output opt.verbose = TRUE; optarg = NULL; break; case '?': // -?, help about(); optarg = NULL; exit(RC_OKAY); break; default: // Invalid option fprintf(stderr, "%s: Invalid option `-%c'\n\n", prog, optch); optarg = NULL; goto bad_usage; } if (opt.verbose && optch != '\0') { fprintf(stderr, " -%c", optch); if (optarg != NULL) fprintf(stderr, " \"%s\"", optarg); optch = '\0'; } } next_arg: if (opt.verbose && optch != '\0') { fprintf(stderr, " -%c", optch); if (optarg != NULL) fprintf(stderr, " \"%s\"", optarg); optch = '\0'; } } if (opt.verbose) fprintf(stderr, "\n"); DL(dbgf("Return optind=%d\n", optind)); return (optind); conflict: // Two or more command line options conflict fprintf(stderr, "%s: Option `-%c' conflicts with other options\n\n", prog, optch); usage(); return (-1); bad_usage: // Improper command usage usage(); return (-1); } //**************************************************************************** // main // Main function for program. // // Returns // One of the RC_xxx codes. int main(int argc, char **argv) { int rc = RC_OKAY; int i, j; #if DEBUG // Handle debug stuff fprintf(stderr, "=== %s %s %s ===\n", title, date, vers); for (i = 1; i < argc; i++) { if (strcmp(argv[i], "-D") == 0) dbg_f = stderr; } #endif // Precheck options for (i = 1; i < argc; i++) { if (strcmp(argv[i], "-v") == 0) opt.verbose = TRUE; } // Parse command line options i = get_options(argc, argv); argc -= i; argv += i; // Check args if (argc < 1) usage(); DL(dbgf("\n")); // Open output file if (opt.outfname != NULL) { // Open output file DL(dbgf("Write to `%s'\n", opt.outfname)); opt.outfp = fopen(opt.outfname, "wb"); if (opt.outfp == NULL) { fprintf(stderr, "%s: Can't open (write) `%s': %s\n", prog, opt.outfname, strerror(errno)); rc = RC_OUTF; goto done; } } else { // Write output to stdout DL(dbgf("Output is stdout=%p\n", stdout)); opt.outfname = "-"; opt.outfp = stdout; #ifdef O_BINARY // Set binary file mode DL(dbgf("setmode binary, fp=%p fd=%d\n", opt.outfp, fileno(opt.outfp))); setmode(fileno(opt.outfp), O_BINARY); #endif } #if BUFSIZ < OUT_BUFSZ // Give output file a bigger buffer if (setvbuf(opt.outfp, obuf, _IOFBF, sizeof(obuf)) != 0) { fprintf(stderr, "%s: Can't setvbuf on `%s': %s\n", prog, opt.outfname, strerror(errno)); } #endif DL(dbgf("outfname=`%s', outfp=%p\n", opt.outfname, opt.outfp)); DL(dbgf("\n")); // Process command line args for (i = 0; i < argc; i++) { // Get input file opt.infname = argv[i]; DL(dbgf("infname=`%s'\n", opt.infname)); if (opt.verbose) fprintf(stderr, "%s:\n", opt.infname); if (strcmp(opt.infname, "-") == 0) { // Get input from stdin DL(dbgf("Input is stdin=%p\n", stdin)); opt.infp = stdin; #ifdef O_BINARY // Set binary file mode DL(dbgf("setmode binary, fp=%p, fd=%d\n", opt.infp, fileno(opt.infp))); setmode(fileno(opt.infp), O_BINARY); #endif } else { // Open input file opt.infp = fopen(opt.infname, "rb"); DL(dbgf("infname=`%s'\n", opt.infname)); if (opt.infp == NULL) { fprintf(stderr, "%s: Can't open (read) `%s': %s\n", prog, opt.infname, strerror(errno)); continue; } } #if BUFSIZ < IN_BUFSZ // Give input file a bigger buffer if (setvbuf(opt.infp, ibuf, _IOFBF, sizeof(ibuf)) != 0) { fprintf(stderr, "%s: Can't setvbuf on `%s': %s\n", prog, opt.infname, strerror(errno)); } #endif // Make a one-time pad data file DL(dbgf("Make OTP from `%s'\n", opt.infname)); rc = make_otp(opt.infp, opt.outfp); DL(dbgf("\n")); // Clean up if (opt.infp != NULL && opt.infp != stdin) { DL(dbgf("Close input fp=%p\n", opt.infp)); if (opt.verbose) fprintf(stderr, "Close input `%s'\n", opt.infname); fclose(opt.infp); opt.infp = NULL; } DL(dbgf("\n")); } done: // Clean up // Close output file if (opt.outfp != NULL && opt.outfp != stdout) { DL(dbgf("Close output fp=%p\n", opt.outfp)); if (opt.verbose) fprintf(stderr, "Close output `%s'\n", opt.outfname); fclose(opt.outfp); opt.outfp = NULL; } DL(dbgf("\n")); // Done if (opt.verbose) fprintf(stderr, "Done\n"); DL(dbgf("Done, rc=%d\n", rc)); return (rc); } // End mkotp.cpp