/*
 *
 * Adobe's encryption/decryption algorithm for eexec and show. Runs in
 * eexec mode unless told otherwise. Use,
 *
 *		pscrypt file.cypher > file.clear
 *
 * to decrypt eexec input. Assumes file.cypher is hex with the key as the
 * first four bytes, and writes file.clear as binary (omitting the key).
 * Use
 *
 *		pscrypt -e12ab34ef file.clear >file.cypher
 *
 * to encrypt file.clear (for eexec) using 12ab34ef as the key. Input is
 * binary and output is hex. The key must be given as a hex number. Use
 * -sshow to encrypt or decrypt a CharString or Subr,
 *
 *		pscrypt -sshow file.cypher > file.clear
 *
 * Use -b or -x to read binary or hex input, and -B or -X to output binary
 * or hex.
 *
 */

#include <stdio.h>
#include <ctype.h>

#define ENCRYPT		0
#define DECRYPT		1

#define NOTSET		-1
#define BINARY		0
#define HEX		1
#define LINELENGTH	40

#define CHARSTRING	4330
#define EEXEC		55665
#define MAGIC1		52845
#define MAGIC2		22719

int	argc;
char	**argv;

int	mode = DECRYPT;
int	input = NOTSET;
int	output = NOTSET;
int	outoffset = NOTSET;
int	inoffset = NOTSET;

int	cryptkey = 0;			/* encryption key set with -e */
int	linelength = LINELENGTH;	/* only for hex output */
int	lastchar = 0;

unsigned long	seed = EEXEC;
unsigned long	key;

FILE	*fp_in = stdin;

/*****************************************************************************/

main(agc, agv)

    int		agc;
    char	*agv[];

{

/*
 *
 * Implementation of the encryption/decryption used by eexec and show.
 *
 */

    argc = agc;
    argv = agv;

    options();
    initialize();
    arguments();

    exit(0);

}   /* End of main */

/*****************************************************************************/

options()

{

    int		ch;
    char	*names = "bde:l:os:xBSX";

    extern char	*optarg;
    extern int	optind;

/*
 *
 * Command line options.
 *
 */

    while ( (ch = getopt(argc, argv, names)) != EOF )
	switch ( ch ) {
	    case 'b':			/* binary input */
		    input = BINARY;
		    break;

	    case 'd':			/* decrypt */
		    mode = DECRYPT;
		    break;

	    case 'e':			/* encrypt */
		    mode = ENCRYPT;
		    if ( *optarg == '0' && *optarg == 'x' )
			optarg += 2;
		    sscanf(optarg, "%8x", &cryptkey);
		    break;

	    case 'l':			/* line length hex output */
		    linelength = atoi(optarg);
		    break;

	    case 'o':			/* output all bytes - debugging */
		    outoffset = 0;
		    break;

	    case 's':			/* seed */
		    if ( *optarg == 'e' )
			seed = EEXEC;
		    else if ( *optarg == 's' )
			seed = CHARSTRING;
		    else if ( *optarg == '0' && *(optarg+1) == 'x' )
			sscanf(optarg+2, "%x", &seed);
		    else if ( *optarg == '0' )
			sscanf(optarg, "%o", &seed);
		    else sscanf(optarg, "%d", &seed);
		    break;

	    case 'x':			/* hex input */
		    input = HEX;
		    break;

	    case 'B':			/* binary output */
		    output = BINARY;
		    break;

	    case 'X':			/* hex output */
		    output = HEX;
		    break;

	    case '?':			/* don't understand the option */
		    fprintf(stderr, "bad option -%c\n", ch);
		    exit(1);
		    break;

	    default:			/* don't know what to do for ch */
		    fprintf(stderr, "missing case for option -%c\n", ch);
		    exit(1);
		    break;
	}   /* End switch */

    argc -= optind;			/* get ready for non-option args */
    argv += optind;

}   /* End of options */

/*****************************************************************************/

initialize()

{

/*
 *
 * Initialization that has to be done after the options.
 *
 */

    key = seed;

    if ( mode == DECRYPT ) {
	input = (input == NOTSET) ? HEX : input;
	output = (output == NOTSET) ? BINARY : output;
	inoffset = (inoffset == NOTSET) ? 0 : inoffset;
	outoffset = (outoffset == NOTSET) ? -4 : outoffset;
    } else {
	input = (input == NOTSET) ? BINARY : input;
	output = (output == NOTSET) ? HEX : output;
	inoffset = (inoffset == NOTSET) ? 4 : inoffset;
	outoffset = (outoffset == NOTSET) ? 0 : outoffset;
    }	/* End else */

    if ( linelength <= 0 )
	linelength = LINELENGTH;

}   /* End of initialize */

/*****************************************************************************/

arguments()

{

/*
 *
 * Everything left is an input file. No arguments or '-' means stdin.
 *
 */

    if ( argc < 1 )
	crypt();
    else
	while ( argc > 0 ) {
	    if ( strcmp(*argv, "-") == 0 )
		fp_in = stdin;
	    else if ( (fp_in = fopen(*argv, "r")) == NULL ) {
		fprintf(stderr, "can't open %s\n", *argv);
		exit(1);
	    }	/* End if */
	    crypt();
	    if ( fp_in != stdin )
		fclose(fp_in);
	    argc--;
	    argv++;
	}   /* End while */

}   /* End of arguments */

/*****************************************************************************/

crypt()

{

    unsigned int	cypher;
    unsigned int	clear;

/*
 *
 * Runs the encryption/decryption algorithm.
 *
 */

    while ( lastchar != EOF ) {
	cypher = nextbyte();
	clear = ((key >> 8) ^ cypher) & 0xFF;
	key = (key + (mode == DECRYPT ? cypher : clear)) * MAGIC1 + MAGIC2;
	if ( ++outoffset > 0 && lastchar != EOF ) {
	    if ( output == HEX ) {
		printf("%.2X", clear);
		if ( linelength > 0 && (outoffset % linelength) == 0 )
		    putchar('\n');
	    } else putchar(clear);
	}   /* End if */
    }	/* End while */

}   /* End of crypt */

/*****************************************************************************/

nextbyte()

{

    int		val = EOF;

/*
 *
 * Returns the next byte. Uses cryptkey (i.e. what followed -e) while inoffset is
 * positive, otherwise reads (hex or binary) from fp_in.
 *
 */

    if ( inoffset-- > 0 )
	val = (cryptkey >> (inoffset*8)) & 0xFF;
    else if ( input == HEX ) {
	if ( (val = nexthexchar()) != EOF )
	    val = (val << 4) | nexthexchar();
    } else if ( input == BINARY )
	val = Getc(fp_in);

    return(val);

}   /* End of nextbyte */

/*****************************************************************************/

nexthexchar()

{

    int		ch;

/*
 *
 * Reads the next hex character.
 *
 */

    while ( (ch = Getc(fp_in)) != EOF && ! isxdigit(ch) ) ;

    if ( isdigit(ch) )
	ch -= '0';
    else if ( isupper(ch) )
	ch -= 'A' - 10;
    else if ( islower(ch) )
	ch -= 'a' - 10;

    return(ch);

}   /* End of nexthexchar */

/*****************************************************************************/

Getc(fp)

    FILE	*fp;

{

/*
 *
 * Reads the next byte from *fp, sets lastchar, and returns the character.
 *
 */

    return(lastchar = getc(fp));

}   /* End of Getc */

/*****************************************************************************/

