[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[CRYPTO] Cryptography of a sort [FAQ]
Attached is FAQ, source code, and documentation for a program I've
arbitrarily called "Cryptography Of A Sort" (COAS). If anyone is
using that acronym for something which could conflict, I guess I'd
have to change it....
The FAQ is self-explanatory. The source code contains an unoptimized
routine or two; the line comments should take care of that. Following
the source code is the user documentation.
FAQ: 132 lines
Source: 349 lines
User doc: 60 lines
I've been programming since Feb. 2, 1975, when I bought my first HP-65.
21.66 years and 30 or so personal computers later, I have an HP-48GX,
a Win95 laptop, and an HP-200LX with an 85 mb flash card STAC'd 170 mb.
I've done several national articles, the last in Dr. Dobb's, June 1991.
I prefer languages which offer medium-to-high-level calls along with
direct OS/BIOS/etc. access, although the HP-48 RPL is kinda fun....
FAQ for Cryptography Of A Sort (COAS)
Author : Dale Thorn <[email protected]>
Revised : 29 Sep 1996
[Source code and documentation follow this FAQ]
Q: Is COAS an actual product?
A: COAS is an encryption engine supplied in source-code format, which
calls some commonly-available (and replaceable) functions included
with commercial computer-language libraries, which in turn perform
some of the rudimentary tasks required by the program. Public Key
features are not currently supported in COAS, therefore, messaging
applications are not as well supported as is local file encryption.
Q: What are the main differences between COAS and other non-messaging-oriented
crypto products?
A: 1. COAS repositions bits based on multiple encoding passes using one or more
Pseudo-Random Number Generators (PRNG's). Since COAS is provided only in
source code format, and since the source code calls the PRNG function in
the compiler library(s), COAS is actually independent of specific PRNG's.
NOTE: PRNG limitations, as described in the popular literature, do not
necessarily apply when repositioning bits in multiple passes, as
opposed to modifying bits as is normally done in other software.
Think of "brute force encryption" (more on this below).
2. COAS does not use a "key" as such, and thus does not "encrypt" the bits
in a text bitstream. Instead, it uses an input value (text or numeric)
as an entry point into a common PRN sequence. Since the entry point is
a secret, and since bits are moved using random block sizes, from their
original bytes into unrelated destination bytes, cryptanalytic attempts
must necessarily begin with brute-force guessing as to the entry points
in the PRN sequences, in order to associate the correct bits with their
original bytes of text. Multiple encoding passes raise the number of
guesses exponentially.
3. COAS source code is extremely small, the primary intent for which was
to provide a sample encoding engine for local/personal computer files.
Due to its small size and simplicity, the source code can be easily
modified by casual users, who may add in their own custom routines.
NOTE: It cannot be overemphasized, that crypto programs which have
a widely-respected reputation must also be held suspect when
A) The very nature of those programs is to deceive, -and-
B) The source code is either not available, or is so complex
as to discourage ordinary people from working with it.
Q: But if COAS uses a common, ordinary PRNG, how can it possibly be secure?
A: I can think of two arguments against using PRNG's:
1. Encoded text is easy to decode by brute force on most computers, -and-
2. Encoded text can be seen as having regular patterns when "viewed" from
the vantage point of programs employing higher-dimensional mathematics.
Addressing the former, a single-pass encryption of a text file using the
typical PRNG might be breakable in as little as .000001 second on one of
the larger, faster computers available, however, the same approach might
require as many as 10^24 years if the number of encoding passes reaches
ten or more. To simplify: try to guess the number I'm thinking between
zero and 32,000. You can make 16 billion guesses per second, so it will
take only .000001 second (on average) to get the correct answer. If you
had to guess ten numbers correctly (and sequentially), it would require
roughly (16,000^10) / 16,000,000,000 seconds, approximately 10^24 years.
Addressing the latter, the ability to "view" the text as a lattice in a
higher dimension is likewise diminished by the discontinuities inherent
in multi-pass encoding, when bit-group sizes are determined dynamically
by PRN's following the secret entry points into the PRNG sequences.
Q: What about the possibility that two or more encryption passes could be
decrypted in a single pass, as in the scenario where a third key K3 is
functionally equivalent to two separate encrypting keys K1 and K2?
A: Since COAS encoding is controlled through entry points into a PRNG's
number sequences (adjacent encryptions may also use different PRNG's
and/or bit-move logic), searching for a "key" or algorithm which can
unpack two or more layers of coding will prove futile when all entry
points into the PRNG's are different, and different PRNG's are used.
A couple of points to consider:
One, the output of the PRNG (or any number series) does not describe
the bit move-to locations; those are determined by sorting the PRN's
then moving the bits according to the sequence of the original array
positions of the PRN's prior to sorting. Since some of the PRN's are
duplicates, the original array positions relative to each other will
be determined by chance, i.e., the vagaries of the sort process, etc.
Two, since the bits are moved rather than modified, and since groups
of bits vary in size, an attempt to find particular bits that belong
to specific bytes after multi-move shuffling, using any compound key
or algorithm in a single decoding pass, will certainly prove futile.
Q: Since the personal computer implementation of COAS uses 16-bit integers to
initialize (set entry points into) the PRNG's, would ten encryption passes
be somehow equivalent to the use of a 160-bit key in conventional programs?
A: If the conventional program used a 160-bit key in a manner similar to COAS,
it would still have to: 1) move bits, not change them. 2) use an indirect
method for specifying move locations. 3) model the processes used in COAS
quite closely, since there's no straightforward mathematical approach that
can duplicate the conditions described in the previous question and answer.
Q: What's the difference between the techniques used by COAS and the use of a
One-Time Pad (OTP)?
A: The theory behind the OTP assumes that (unlike the use of a Public/Private
key) subsequent encryptions using the same OTP key would reveal the nature
of the OTP, i.e., any newly-encoded files and messages would share certain
common identifiable characteristics which could be exploited to facilitate
the decryption of all files using that pad.
COAS, on the other hand, doesn't alter any of a file's bits, and therefore
does not "add" its PRNG entry points' characteristics to a file other than
shuffling bits in accordance with the original physical positions of PRN's
which have been sorted by size.
Q: Is it possible for anyone to alter the contents of files encrypted by COAS
so that a person performing the eventual decryption would not realize that
the file(s) were indeed altered?
A: Less likely than incidental or brute-force decryption. Each bit is moved
once in each encryption pass, and if any bits were moved or changed, that
many bytes (or nearly as many, since bits are not moved in byte-divisible
groups, so most will end up in unrelated bytes after encryption) would be
affected, and the resulting bytes would not likely pass even the simplest
checksum test.
Q: Is COAS a "weak" product (cryptographically speaking), either because of
limitations in its own internal algorithms, or in the commercial library
functions it calls?
A: COAS can be used in ways that produce weak encryption, which is really
an advantage in encouraging beginners to get started, given its simple
user interface. Whether it can produce "strong" encryption or not is a
matter of opinion, where said opinion is not so much a function of the
product's alleged weaknesses, as it is the fact that cryptography grew
up from a long history of hand-ciphering and the mathematics attending
that growth, and the obvious resistance to new paradigms in this field.
While mathematical proof of encryption strength is highly desirable in
most applications (some would argue essential in certain applications),
I see things this way: Computer software of any kind, which cannot be
analyzed by common persons (average programmers), whose innards cannot
be exposed to the masses for whatever reason, should not be used where
it could effect control over the lives of those people. Looking at it
a different way, it's wise for any individual or group to evaluate the
software that's available, and make their own judgements independently
of "expert opinion" in the field.
*******************************************************************************
*******************************************************************************
*******************************************************************************
/* CCRP.C Encrypt/Decrypt a DOS file */
/* By: Dale Thorn */
/* Version 2.9 */
/* Rev. 03.07.1996 */
#include "stdlib.h"
#include "string.h"
#include "stdio.h"
#include "dos.h"
#include "io.h"
#include "ccrp.h"
V main(I argc, C **argv) { /* command-line arguments (input file/offset) */
C cmsg[23]; /* initialize the User message string */
U ibit = 0; /* initialize the bit offset in cbuf */
U ibuf = 2048; /* set maximum file buffer length */
U idot; /* initialize the filename extension separator */
I ieof = 0; /* initialize the EOF flag */
U ilen; /* initialize a temporary length variable */
U indx; /* initialize a temporary loop variable */
I iopr; /* initialize the operation code */
U irnd = 0; /* initialize the randomizer seed */
L lbyt; /* initialize the file pointer variable */
L llof; /* initialize the file length variable */
L lrnd = 0; /* initialize the randomizer accumulator */
U _far *uvadr = 0; /* video display pointer */
struct _iobuf *ebuf; /* source file access structure */
C *cbuf = (C *)malloc(2048); /* initialize the file buffer */
C *ctmp = (C *)malloc(2048); /* initialize the temp buffer */
I *int1 = (I *)malloc(3074); /* allocate the sort index array */
I *int2 = (I *)malloc(3074); /* allocate the sort random number array */
I *istk = (I *)malloc(3074); /* allocate the sort stack array */
if (argc == 1) { /* a command line was not supplied */
ifn_msgs("Usage: CCRP(v2.9) filename [/e /d] [key]", 4, 24, 79, 0, 1);
} /* display the usage message [above] and exit */
if (argc < 3 || argc > 4) { /* no. of parameters should be one or two */
ifn_msgs("Invalid number of parameters", 4, 24, 79, 1, 1);
} /* display no.-of-parameters message [above] and exit */
if (argv[2][0] != '/') { /* slash preceding parameter missing */
ifn_msgs("Invalid operation parameter", 4, 24, 79, 1, 1);
} /* display invalid-parameter message [above] and exit */
strupr(argv[1]); /* uppercase the filename */
strupr(argv[2]); /* uppercase the operation code */
if (argv[2][1] != 'D' && argv[2][1] != 'E') { /* invalid parameter */
ifn_msgs("Invalid operation parameter", 4, 24, 79, 1, 1);
} /* display invalid-parameter message [above] and exit */
idot = strcspn(argv[1], "."); /* position of filename extension separator */
ilen = strlen(argv[1]); /* length of filename */
if (idot == 0 || idot > 8 || ilen - idot > 4) { /* filename tests bad */
ifn_msgs("Invalid filename", 4, 24, 79, 1, 1);
} /* display invalid-filename message [above] and exit */
if (idot < ilen) { /* filename extension separator found! */
if (strcspn(argv[1] + idot + 1, ".") < ilen - idot - 1) {/* 2nd found! */
ifn_msgs("Invalid filename", 4, 24, 79, 1, 1);
} /* display invalid-filename message [above] and exit */
}
strcpy(cmsg, argv[1]); /* copy filename to message */
strcat(cmsg, " not found"); /* add "not found" to message */
ebuf = fopen(argv[1], "rb+"); /* open the selected file */
llof = filelength(fileno(ebuf)); /* filelength of selected file */
if (ebuf == NULL || llof == -1L || llof == 0) {/* length=0 or call failed */
fclose(ebuf); /* close the file */
remove(argv[1]); /* kill the zero-length file */
ifn_msgs(cmsg, 4, 24, 79, 1, 1); /* display message and exit */
}
iopr = argv[2][1] - 68; /* operation code (1=encrypt, 2=decrypt) */
if (argc == 4) { /* a seed key was supplied */
ilen = strlen(argv[3]); /* length of optional seed key */
for (indx = 0; indx < ilen; indx++) { /* loop through the seed key */
irnd = argv[3][indx]; /* character at byte position */
switch (indx % 3) { /* select on byte significance */
case 0: /* least significant byte */
lrnd += irnd; /* add to randomizer accum. */
break;
case 1: /* 2nd least significant byte */
lrnd += (L)irnd * 256; /* add to randomizer accum. */
break;
case 2: /* most significant byte */
lrnd += (L)irnd * 65536; /* add to randomizer accum. */
break;
default:
break;
}
}
irnd = (U)(lrnd % 32640) + 1; /* mod randomizer seed to <= 32640 */
}
ifn_msgs("Please standby", 4, 24, 79, 0, 0); /* standby message */
srand(irnd); /* initialize the random number generator */
for (lbyt = 0; lbyt < llof; lbyt += ibuf) {/* proc. file in ibuf segments */
if (lbyt + ibuf >= llof) { /* current file pointer + ibuf spans EOF */
ibuf = (U)(llof - lbyt); /* reset maximum file buffer length */
/* cbuf = "" /* deallocate file buffer */
/* cbuf = space$(ibuf) /* reallocate file buffer */
ieof = 1; /* set the EOF flag ON */
}
fseek(ebuf, lbyt, SEEK_SET); /* set file-read position */
fread((V *)cbuf, 1, ibuf, ebuf); /* read data into the file buffer */
while (1) { /* loop to process bit groups in cbuf */
ilen = (rand() / 26) + 256;/* buffer seg. bit-len.: 256<=ilen<=1536 */
if (ibit + ilen > ibuf * 8) {/* current bit-pointer+ilen spans cbuf */
if (ieof) { /* EOF flag is ON */
ilen = ibuf * 8 - ibit; /* reset bit-length of buffer segment */
} else { /* EOF flag is OFF; adjust file pointer */
fseek(ebuf, lbyt, SEEK_SET); /* set file-write position */
fwrite((V *)cbuf, 1, ibuf, ebuf);/* save curr. buffer to file */
lbyt -= (ibuf - ibit / 8);/* set file ptr to reload from ibit */
ibit %= 8; /* set ibit to first byte of <new> cbuf */
break; /* exit loop to reload cbuf from lbyt */
}
} /* encrypt or decrypt the current segment [below] */
ifn_cryp(int1, int2, istk, cbuf, ctmp, (I)ibit, ilen, iopr);
ibit += ilen; /* increment ibit to next bit-segment */
if (ibit == ibuf * 8) { /* loop until ibit == length of cbuf */
fseek(ebuf, lbyt, SEEK_SET); /* set file-write position */
fwrite((V *)cbuf, 1, ibuf, ebuf);/* write current buffer to file */
ibit = 0; /* set ibit to first byte of <new> cbuf */
break;
}
}
}
ifn_msgs("Translation complete", 4, 24, 79, 0, 1);/* disp. message & exit */
}
I bitget(C *cstr, I ibit) { /* get a bit-value from a string */
I ival; /* initialize the bit value */
switch (ibit % 8) { /* switch on bit# within character */
case 0: /* bit #0 in target character */
ival = 1; /* value of bit #0 */
break;
case 1: /* bit #1 in target character */
ival = 2; /* value of bit #1 */
break;
case 2: /* bit #2 in target character */
ival = 4; /* value of bit #2 */
break;
case 3: /* bit #3 in target character */
ival = 8; /* value of bit #3 */
break;
case 4: /* bit #4 in target character */
ival = 16; /* value of bit #4 */
break;
case 5: /* bit #5 in target character */
ival = 32; /* value of bit #5 */
break;
case 6: /* bit #6 in target character */
ival = 64; /* value of bit #6 */
break;
case 7: /* bit #7 in target character */
ival = 128; /* value of bit #7 */
break;
default:
break;
}
return ((cstr[ibit / 8] & ival) != 0); /* return value of target bit */
}
V bitput(C *cstr, I ibit, I iput) { /* put a bit-value to a string */
I ival; /* initialize the bit value */
I ipos = ibit / 8; /* position of 8-bit char. in cstr */
switch (ibit % 8) { /* switch on bit# within character */
case 0: /* bit #0 in target character */
ival = 1; /* value of bit #0 */
break;
case 1: /* bit #1 in target character */
ival = 2; /* value of bit #1 */
break;
case 2: /* bit #2 in target character */
ival = 4; /* value of bit #2 */
break;
case 3: /* bit #3 in target character */
ival = 8; /* value of bit #3 */
break;
case 4: /* bit #4 in target character */
ival = 16; /* value of bit #4 */
break;
case 5: /* bit #5 in target character */
ival = 32; /* value of bit #5 */
break;
case 6: /* bit #6 in target character */
ival = 64; /* value of bit #6 */
break;
case 7: /* bit #7 in target character */
ival = 128; /* value of bit #7 */
break;
default:
break;
}
if (iput) { /* OK to set the bit ON */
if (!(cstr[ipos] & ival)) { /* bit is NOT already ON */
cstr[ipos] += ival; /* set bit ON by adding ival */
}
} else { /* OK to set the bit OFF */
if (cstr[ipos] & ival) { /* bit is NOT already OFF */
cstr[ipos] -= ival; /* set bit OFF by subt. ival */
}
}
}
V ifn_cryp(I *int1, I *int2, I *istk, C *cbuf, C *ctmp, I ibit, I ilen, I iopr) {
I indx; /* initialize the for-next loop counter */
for (indx = 0; indx < ilen; indx++) { /* loop through ilen array elements */
int1[indx] = indx; /* bit offsets from current ibit offset */
int2[indx] = rand(); /* random number values for sort function */
}
ifn_sort(int1, int2, istk, ilen - 1); /* Quicksort by random no. array */
memcpy(ctmp, cbuf, 2048); /* copy data buffer to temp destination buffer */
if (iopr) { /* encrypt operation */
for (indx = 0; indx < ilen; indx++) { /* loop thru ilen array elements */
bitput(ctmp, indx + ibit, bitget(cbuf, int1[indx] + ibit));/*encrypt*/
}
} else { /* decrypt operation */
for (indx = 0; indx < ilen; indx++) { /* loop thru ilen array elements */
bitput(ctmp, int1[indx] + ibit, bitget(cbuf, indx + ibit));/*decrypt*/
}
}
memcpy(cbuf, ctmp, 2048); /* copy temp destination buffer to data buffer */
}
V ifn_msgs(C *cmsg, I iofs, I irow, I icol, I ibrp, I iext) {/* display msgs */
io_vcls(7); /* clear the screen */
io_vdsp(cmsg, 4, iofs, 7); /* display the user message */
if (ibrp) { /* OK to sound user-alert (beep) */
printf("\a"); /* sound the user-alert */
}
if (iext) { /* OK to exit the program */
io_vcsr(5, 0, 0); /* relocate the cursor */
fcloseall(); /* close all open files */
exit(0); /* return to DOS */
} else { /* do NOT exit the program */
io_vcsr(irow, icol, 0); /* 'hide' the cursor */
}
}
V ifn_sort(I *int1, I *int2, I *istk, I imax) { /* array Quicksort function */
I iext; /* initialize the outer-loop exit flag */
I ilow; /* initialize the low array pointer */
I irdx = 0; /* initialize the sort radix */
I isp1; /* initialize the low stack pointer */
I isp2; /* initialize the top stack pointer */
I itop; /* initialize the top array pointer */
I iva1; /* initialize array value from low stack pointer */
I iva2; /* initialize array value from low stack pointer */
istk[0] = 0; /* initialize the low array pointer */
istk[1] = imax; /* initialize the top array pointer */
while (irdx >= 0) { /* loop until sort radix < 0 */
isp1 = istk[irdx + irdx]; /* set the low stack pointer */
isp2 = istk[irdx + irdx + 1]; /* set the top stack pointer */
irdx--; /* decrement the sort radix */
iva1 = int1[isp1]; /* get array value from low stack pointer */
iva2 = int2[isp1]; /* get array value from low stack pointer */
itop = isp2 + 1; /* set the top array pointer */
ilow = isp1; /* set the low array pointer */
while (1) { /* loop to sort within the radix limit */
itop--; /* decrement the top array pointer */
if (itop == ilow) { /* top array pointer==low array pointer */
break; /* skip to next radix value */
}
if (iva2 > int2[itop]) { /* value @low pointer>value @top pointer */
int1[ilow] = int1[itop]; /* swap low and top array values */
int2[ilow] = int2[itop]; /* swap low and top array values */
iext = 0; /* initialize outer-loop exit flag */
while (1) { /* loop to compare and swap array values */
ilow++; /* increment the low array pointer */
if (itop == ilow) { /* top array pointer==low array pointer */
iext = 1; /* set outer-loop exit flag ON */
break; /* skip to next radix value */
}
if (iva2 < int2[ilow]) { /* value @low ptr.<value @low ptr. */
int1[itop] = int1[ilow]; /* swap top and low array values */
int2[itop] = int2[ilow]; /* swap top and low array values */
break; /* repeat sort within the radix limit */
}
}
if (iext) { /* outer-loop exit flag is ON */
break; /* skip to next radix value */
}
}
}
int1[ilow] = iva1; /* put array value from low stack pointer */
int2[ilow] = iva2; /* put array value from low stack pointer */
if (isp2 - ilow > 1) { /* low segment-width is > 1 */
irdx++; /* increment the sort radix */
istk[irdx + irdx] = ilow + 1; /* reset low array pointer */
istk[irdx + irdx + 1] = isp2; /* reset top array pointer */
}
if (itop - isp1 > 1) { /* top segment-width is > 1 */
irdx++; /* increment the sort radix */
istk[irdx + irdx] = isp1; /* reset low array pointer */
istk[irdx + irdx + 1] = itop - 1; /* reset top array pointer */
}
}
}
U io_vadr(I inop) { /* get video address (color or b/w) */
rg.h.ah = 15; /* video-address function */
int86(0x10, &rg, &rg); /* call DOS for video address */
if (rg.h.al == 7) { /* register A-low is 7 */
return(0xb000); /* return b/w address */
} else { /* register A-low is NOT 7 */
return(0xb800); /* return color address */
}
}
V io_vcls(I iclr) { /* clear screen function */
I irow; /* initialize the row number variable */
C cdat[81]; /* initialize the row data buffer */
memset(cdat, ' ', 80); /* clear the row data buffer */
cdat[80] = '\0'; /* terminate the row data buffer */
for (irow = 0; irow < 25; irow++) { /* loop thru the screen rows */
io_vdsp(cdat, irow, 0, iclr); /* display each <blank> screen row */
}
}
V io_vcsr(I irow, I icol, I icsr) { /* set cursor position [and size] */
rg.h.ah = 2; /* cursor-position function */
rg.h.bh = 0; /* video page zero */
rg.h.dh = (C)irow; /* row number */
rg.h.dl = (C)icol; /* column number */
int86(0x10, &rg, &rg); /* call DOS to position cursor */
if (icsr) { /* cursor-size specified */
rg.h.ah = 1; /* cursor-size function */
rg.h.ch = (C)(13 - icsr); /* set cursor-begin line */
rg.h.cl = 12; /* set cursor-end line */
int86(0x10, &rg, &rg); /* call DOS to set cursor size */
}
}
V io_vdsp(C *cdat, I irow, I icol, I iclr) { /* display data on screen */
I ilen = strlen(cdat); /* length of string to be displayed */
I iptr; /* byte-counter for displayed string */
U uclr = iclr * 256; /* unsigned attribute high-byte value */
if (!uvadr) { /* video pointer segment not set */
FP_SEG(uvadr) = io_vadr(0); /* set video pointer segment */
}
FP_OFF(uvadr) = irow * 160 + icol * 2; /* set video pointer offset */
for (iptr = 0; iptr < ilen; iptr ++) { /* loop thru displayed string */
*uvadr = uclr + (UC)cdat[iptr]; /* put data to video memory */
uvadr++; /* increment video display pointer */
}
}
*******************************************************************************
*******************************************************************************
*******************************************************************************
New CCRP documentation - changes as of 28.02.1996
----------Command---------- ------------------Output-------------------
CCRP Usage parameters.
CCRP filename /e Encrypt each byte in 'filename' so that the
data cannot be seen, or, if the file was an
executable file, it cannot be executed.
CCRP filename /d Decrypt (restore) each byte in 'filename'.
CCRP filename /e key Encrypt or decrypt 'filename', but add an
CCRP filename /d key additional factor (a key, or a password)
to the encryption and decryption.
NOTE 1: The key/password (if used) must be a
contiguous string of characters with
no blank spaces between any characters.
NOTE 2: If a key is entered for encryption, the
same key must be entered for decryption.
NOTE 3: Encryption may be performed 2 or more
times in sequence before decryption,
using a different key each time, for
additional encryption security. In
such case, the decryption steps must
be performed in the reverse order
(last encryption/first decryption).
NOTE 4: Encryption and decryption are mere
complementary processes, so that if
the decryption step were performed
first, followed by encryption, the
end effect would be the same.
WARNING(!) Encryption changes the contents of a file, and if you cannot
perform the decryption process properly, including the use of
keys/passwords, you won't be able to recover the file at all.
Normally, before making changes to a file, you are advised
to make a backup copy of the file, but since the purpose of
encryption is to make the file unreadable and unusable, to
have a usable backup copy of the file on the same computer,
or even in the same area that the computer is located in,
wouldn't suit the primary purpose of encryption.
NOTES: If maximum security is the objective, you might want to encrypt a file
several times (in several passes) with a different encryption key each
pass, using different programs, and mixing the encryption/decryption
order (OK as long as different keys are used). Examples:
ENCRYPT.BAT (encrypt the file; see Note 4 above concerning the /d switch)
bcrp filename /d Little_Miss_Muffet_Sat_On_Her_Tuffet
ccrp filename /e The_Quick_Brown_Fox_Jumped_Over_The_Lazy_Dog
bcrp filename /e We_Have_Met_The_Enemy_And_They_Are_Us
ccrp filename /d Let_Him_That_Hath_Understanding_Count_The_Number_Of_The_Beast
DECRYPT.BAT (decrypt the file; see Note 4 above concerning the /e switch)
ccrp filename /e Let_Him_That_Hath_Understanding_Count_The_Number_Of_The_Beast
bcrp filename /d We_Have_Met_The_Enemy_And_They_Are_Us
ccrp filename /d The_Quick_Brown_Fox_Jumped_Over_The_Lazy_Dog
bcrp filename /e Little_Miss_Muffet_Sat_On_Her_Tuffet