|
Primary Cryption
by Vernon Nemitz
// CRYPTION.C, A Data Protection Tool,
// Copyright (C) 2004 by Vernon Nemitz
// Originally published at www.totse.com;
// seek the original there if needed.
//
// Publishing rights are shared with the General Public
// only as follows:
// (1) This copyright description, including the notices given
// by any other author, must be entirely retained as the
// first thing in every copy of this document; its intent
// and meaning may not be altered by anyone.
// (2) All of the content of this document, as originally
// acquired by any person, must be retained in all copies
// made by that person. However, brief passages may be
// quoted in reviews and other descriptions. And anyone
// is free to clone this copyright description by itself
// to a new document, and become the original author of
// the rest of that document.
// (3) Radical editing of this document is allowed, but only
// of the parts that follow this copyright description.
// The way to edit this document while retaining prior
// work is to mark/disable any chosen portion as being
// inadequate and/or unclear and/or obsolete and/or
// [describe!] -- and then, likely, to insert and/or
// append new/replacement material.
// (4) Each who edits this document must clearly identify the
// work done, and is expected to add a brief note and
// copyright declaration, similar to the lines for
// original author(s) above, in the space provided below.
// (5) No one is required to republish this document after
// editing it, but if that is done, then this copyright
// notice will continue to apply to it.
//
//
//
// End of copyright information, for this Evolving
// Multi-Owner Document.
// Notice: This document has been edited from the original
// format, to a narrower column width. No significant other
// changes have been made (besides adding this Notice, and
// fixing the odd typo or comment have been made -- and the
// original author, who made those changes, did not think it
// necessary to disable the entire program (wider form) and
// append a near-duplicate (narrower form).
/*
The purpose of this program is to encrypt any specified file easily
and fairly quickly, and also to reverse the process equally easily
-- yet use an encryption scheme so potent that even a quantum
supercomputer would have trouble breaking it. Of course time will
tell; there are no powerful quantum computers at this writing.
Note that that is the PURPOSE of this program. It DOES modify files,
and it does successfully unmodify those files, but the algorithms by
which it does its work have not at this writing been subjected to
intense scrutiny by Experts In The Field. Perhaps there is a fatal
flaw that is unknown to the original author; if so, the security that
the program is intended to provide would just be an illusion. On the
other hand, perhaps this program really is as good as has been
attempted. One reason for publishing it is to let the Experts analyze
it, so that the Truth will become known. Meanwhile, those risk-takers
who gamble on its security can at least be assured that their data
will be protected UNTIL a flaw is discovered -- which may take a
while.
The algorithms invoke a unique property of prime numbers, which the
original author happened to independently discover, and then publish
in the "Journal of Recreational Mathematics", Volume 14, Number 2
(1981-1982), pg 141. The reciprocal of prime P in at least one
arithmetic Base (and possibly in an infinite quantity of Bases) will
be a repeating series of digits having a length or "period" of P-1.
For example, in the common Base Ten, if P is the prime number 7 and
its reciprocal is computed, the period is P-1 or 6 digits (repeating
142857). And although when P is the equally prime number 3, its
reciprocal in Base Ten only has a period of 1 digit (repeating 3), in
Base Two (and others) its period is indeed P-1 or 2 digits (repeating
01 in Base Two). Meanwhile, for every Composite number C, its
reciprocal always has a period that is less than C-1 digits, no matter
in what Base it is calculated.
When examining the digits of a single P-1 period for a prime number,
some order may be detected. Ignoring 2, primes are odd numbers, so
their longest periods have even quantities of digits. It is
interesting that if the first half of the longest period is added to
the second half, the result is a series of identical digits, each
being B-1, where B is the Base in which the reciprocal had been taken.
For example, look at the 142857 from 7 and Base Ten above; split and
add: 142 + 857 = 999. Other than that, though, the digits in the
first half of the period seem to be reasonably pseudo-random. This is
good because there are lots of prime numbers; we could simply take the
reciprocal of some prime larger than 100 million, and obtain 50
million pseudo-random digits quite easily and repeatably.
One simple way to obtain greater security, however, is to divide that
prime into some other number than One (a reciprocal is defined by
division into One). Any pseudo-random number smaller than the prime
will do (if bigger than the prime, only the division-remainder would
be relevant to these considerations of period-properties). The net
effect is that the same repeating sequence of digits is generated as
if the reciprocal of the prime had been computed, but the sequence
starts in a different place. So, if the sequence has a length of
millions, then making its starting point part of the secret encryption
key does indeed increase security somewhat.
Yet there is more. If multiple large primes are chosen, each able to
yield millions of pseudo-random digits, then those sets of digits can
be combined (effectively increasing randomness) to make the actual
encrypting-numbers for a message. Also, if a message to be encrypted
is only a few thousand bytes long, then aren't vast quantities of
potential encrypting-digits being "wasted"? What if, instead of
using generated digits as soon as they are available, some were
deliberately skipped first? Depending on the way those digits were
created from more than one prime, this can be a far more devious
thing, than just another variation on the theme of "divide into a
different number than One". In fact, consider the notion of skipping
a (semirandom) few digits inside the heart of the algorithm, for every
character of any processed message.... Finally, what if you had
several files to encrypt, and you could encrypt all of them at once,
with the same key, using blocks of pseudo-random digits that were
separated by irregular quantities of unused digits? With this
CRYPTION program, you can do exactly that!
The most perfect encryption system uses totally random numbers to
modify the content of a message. With totally random numbers, the
message cannot be cracked even in theory, no matter how powerful a
quantum supercomputer is used. In practice, though, while totally
random numbers appear to be obtainable for encryption, the message
must still be decryptable by the permitted parties -- and for that it
is necessary to use the exact same sequence of totally random numbers.
But guaranteed-identical sequences of totally random numbers have in
the past been essentially impossible to independently obtain at the
separate locations of sender and receiver (it would be a violation of
the definition of "totally random"!). However, modern quantum
encryption techniques can use the phenomenon known as "entanglement"
to allow completely secure transmission/sharing of totally random
numbers, which are generated just ONCE -- but this trick is just
barely coming out of the laboratory with a high price tag, so it
cannot quite be called ready for prime time. Thus, until then, for
totally random numbers to be used in encryption, the list of numbers
has to be carried about by the message-recipient, which represents a
security risk.
Meanwhile, the repeatable generation of pseudo-random numbers means
that they can be independently and identically computed at separate
locations. Their weakness with respect to encryption is, of course,
the fact that they are not totally random. As noted above, however,
there are ways of mixing pseudorandom numbers together, to make the
result more closely approach the totally random condition. Note that
such procedures can be computationally intensive, and thus detract
from ease and speed of use. The particular mixing scheme employed
here takes advantage of the facts that every large prime number offers
an easy way to compute a lot of pseudo-random digits, that there are
infinitely many prime numbers, and that programs can often "trade"
memory for speed (in this case, it uses a lot of memory to hold a lot
of calculation scratchpads, and its main algorithm runs comfortably
fast enough).
Digression: There is a convenience problem that is directly
associated with encrypting a message using a lot of prime numbers.
Overall, the list of those primes -- and the numbers which they will
be dividing -- constitute the "encryption key", the information needed
to decrypt a message later. Who is going to remember such a key? And
writing it down leads to the same security risk as already mentioned.
The chosen solution is to obtain the list of primes from an
encipherable source....
Enciphering is not the same as encryption. Ciphering represents a
larger concept, of which encryption is just a subset. Thus ciphers
can use random ideas to hide other ideas, while encryption is
thoroughly mathematical these days. So, an example pre-arranged
cipher might be: "If I should happen to use the word 'Friday' in the
next conversation, you will stop listening at the door and call the
fire department." The context of a cipher can be as important as the
cipher itself!
So, let us consider a List Of Only/All Prime Numbers Smaller Than One
Billion (for example). There are many millions of primes on that
List. If we choose to use one thousand of them in combination to
encrypt a message, we can decide to specify those primes either by
exact value, or by position in the List. The difference is quite
significant. Primes are particular numbers, not always easy to
identify (especially when large), while mere positions on a list are
ordinary numbers. And ordinary numbers can be obtained anywhere!
(So, how many ways are there to select a thousand primes, to be used
as an encryption key, from many-millions? It is a large enough number
to give pause to any would-be code-breaker! And we can easily make
message-cracking tougher by choosing a longer Main List of primes
-- CRYPTION.EXE uses more than 200 Million -- and also by selecting
more than a mere thousand of them -- CRYPTION.EXE can select 65
thousand.)
Now please consider an already existing file of some sort, perhaps an
".MP3" music file. Examined from the currently relevant perspective,
this file is just a collection of numbers that represent music-data.
Better, it is compressed data, which means that most of the
repetitiveness of the data in that file has been squeezed out --
leaving mostly random numbers behind! Suppose we use this modestly
random collection of ordinary numbers as a way to select primes from
the List of Only/All Primes -- with the length of that file indicating
how many to select? That means the file is the key to encrypting/
decrypting the message, and easy to remember! More, you can now
encrypt the message and pass it on, and at some unrelated (even
earlier!) point in time, mention to the recipients a particular .MP3
file that they should hear. This enciphering of the key makes
cracking the message at their end easy, but hard for anyone else.
Keep in mind that there are lots of other kinds of compressed data
files that can be used as encryption keys in this scheme. Nor do the
key files have to be compressed, because the overall algorithm only
indirectly uses the data in the key files (it is pulled,
pseudorandomized, and THEN used to select primes!). Nevertheless,
you and your pals could consider owning identical copies of some
particular version of a data-compression program, such that you know
that any file that might get compressed by the program will be
compressed identically. Then you could plan some sneaky thing like
saying, "Go to this Web address ...", which means to do exactly that.
But it also means, "Save that Web page as a local file, compress the
file, and then use it as the decryption key." (Do note that date/time
stamps may interfere with that notion's simplicity -- but then, this
will depend on the "particular version" of the compression program.)
If you are concerned about the Web page being modified before it can
be used as a decryption key, then try www.archive.org -- their job is
to archive billions of Web pages, with the goal of preserving them
just as they were. Anyway, be creative with your enciphering of the
encryption key! Even encrypted files can be used as the keys to other
encrypted files! And, suppose you occasionally put the decryption
information for your next message -- including the Key File! --
somewhere inside a zipped-up current message, just to provide someone
monitoring your communications with not even a hint regarding anS
enciphered key.... Digression ends.
This CRYPTION program will request a file-name to use as the key, and
also a list of files to be encrypted or decrypted, and other
information such as skip-numbers. Only a minimal check will be made
to find some random data in the key file, so you have many choices --
and no record at all will be made of the relationship between the key
file and the message file(s), because such a record is still a
security risk. If the user wishes to create a personal list of "keys
and files", and encrypt THAT, so that its encryption key is the only
thing to remember, fine. All this program will do is encrypt/decrypt.
Managing any other details is not its job.
The reason for taking that position here is, such a list can
potentially be complicated enough to require a special computer
program of its own, just to manage it. After all, just because a file
has been encrypted, that does not mean someone might never want to
encrypt it again, for additional security, with either the same key
or another key. More, it is important to realize that simply because
the decryption process is the exact inverse of the encryption process,
then this program's decryption process could be used INSTEAD of this
program's encryption process, to scramble a message, after which the
encryption process would be needed to unscramble it! When you
consider that a message might be scrambled multiple times in alternate
ways using multiple keys, then you might agree that keeping track of
all the possibilities (and the correct overall unscrambling sequence)
is beyond the scope of the present program, a simple "encryption/
decryption engine".
One minor dilemma results from the fact that any scrambled file will
of course no longer be worthy of its original file extension (like
.TXT). All scrambled files will consist of simple binary data, and
any program that expects something else (the format of the original
file) will likely claim that the file has become corrupted. Therefore
it is logical that the newly created scrambled file be given a new
file extension, a unique group of letters that no other program will
claim an ability to properly digest for presentation to the user. The
chosen replacement extension is the first three consonents of this
program's name: .CRP, which coincidently lets the file now brag about
its own "corruption" (or even "crappiness") as far as its use by any
other program is concerned....
Next, should some special program be written to manage files and keys,
that program might call this one with a long string of "parameters",
and thereby pass the file-name and other information that the CRYPTION
program needs, and bypass the requests it would otherwise make. The
user of such an overall controller program would be convenienced by
that arrangement. Therefore, formally, when the CRYPTION program is
invoked by another, via a command to the computer's Operating System,
the list of parameters must be as follows: (1) A required name of the
file, inside quotation marks, to be used as a key (Drive and Path
optional); (2) An optional letter D, to delete the key file after all
work is done -- the default is to retain the file; (3) A required skip
number, though 0 (zero) is okay; (4) A required algorithm code-letter,
either E for Encrypt or D for Decrypt; (5) The required name of a file
to be affected by that algorithm, again enclosed by quotes; (6) An
optional letter D to delete that file after all work is done -- this
letter is only relevant for files that do not have a .CRP extension,
because any .CRP file is assumed to be scrambled and does not need to
persist beyond its time of intermediacy (so a further-scrambled .CRP
file always REPLACES the prior .CRP file, and a finally-unscrambled
.CRP file will have its file-extension changed). The last four
parameters may be repeated as a group several times, allowing one key
to be used to affect many files. Here is an example (one repetition):
CRYPTION.EXE
"C:\KEY FILE.BIN"2222E"C:\TMP\MESSAGE1.TXT"7654D"MSG2.TXT"D
Above, Values 2222 and 7654 are skip numbers; the file MESSAGE1.TXT
will be processed via Encryption algorithm, and the MSG2.TXT file via
Decryption; and lastly, only MSG2.TXT will be deleted, and then only
if all work has been successfully completed. (NOTE: skip numbers must
be positive, and if they are too large -- say, more than a million --
they will significantly slow down the CRYPTION program. Thus the
maximum allowed skip number is 9999999, just a trifle under ten
million). Files named in that example may be any other thing
appropriate, of course, and if the drive and directory path are not
included, the CRYPTION program will let the Operating System seek it
in the usual default places. Ordinary quotation-mark " symbols are
required to guarantee correct parsing of file names from the
parameters, especially when they contain spaces, such as KEY FILE.BIN
in the example. (Note that " symbols are not allowed, by the
Operating System, to be part of a file name, which means quotes are
perfect markers for the start and end of a path\file name.) Next, the
example shows minimum spaces used as item separators, but additional
spaces are quite acceptable (except that quote marks must exactly
delimit file names). IMPORTANT: The length limit for any one
DRIVE:\PATH\FILENAME.EXT is 48 characters. Equally important: If
parameters are supplied to the CRYPTION program when the Operating
System is told to run it, those parameters will be tested for
validity. Anything missing or incorrect will cause a specific request
for needed information, just as if the CRYPTION program had been
invoked with no parameters. Thus an automated controller program that
invokes CRYPTION must be certain it passes valid data in the
parameters, because otherwise this program will sit and wait until it
gets human input.
Next, another and much more serious dilemma also flows from the
possibility that some file might be scrambled multiple times, and that
the Decryption option might be used to Encrypt it. If this program is
going to replace the original file extension with another, how is it
going to recognize that an original file has been restored, so that
the original extension can be restored? Only a very iron-clad policy
with respect to file-manipulation is going to solve the dilemma, and
this is it:
1. Regardless of encryption/decryption mode, this program will always
pay attention to the file's current extension, and make a quick
examination of the start of the file.
2. If the original file is about to receive its initial scrambling,
then its extension will not be .CRP, and this program will expect
to NOT find a certain key phrase at the start of the file. In
other words, finding that phrase will imply that the file
currently exists in a scrambled state; not finding that phrase
means that it will be inserted at the beginning of the file.
3. When this program inserts the key phrase to the beginning of the
file, it will place it there TWICE, along with an additional
phrase that holds the original file extension. For example, if
the extension was TXT, then this is what gets added to the start
of the to-be-scrambled file:
DATA PROTECTED FROM SNOOPERCOMPUTERS WITH CRYPTIONITE:
Data Protected From Snoopercomputers With Cryptionite:
The Original File Extension is TXT.
4. The preceding will be one long string of text, with two spaces
after each part of it, including the third part (it's not the
multi-line thing just presented). The long message has two
purposes. First, no existing file nor future file has any need to
begin with that exact long sequence of characters, UNLESS it has
been altered by CRYPTION.EXE. Second, the last two parts of that
text will be scrambled along with the rest of the file. When that
text reappears after this program has performed either encryption
or decryption, then this program can make the reasonable
assumption that the rest of the original file has also been
restored.
5. At that point CRYPTION will take the just-revealed file extension
and apply it -- and also completely remove the special text. Note
that the exact identifier just described is 87 characters from
"Data Protected " to " Extension is ". Also note that each
character is normally stored in one "byte" of memory, and that a
byte can hold any of 256 possible characters. Thus, for this
particular sequence of characters to be generated at random, the
odds are 1 in 256-raised-to-the-87th-power! This is the basis
behind the reasonable assumption that when the last two parts of
the inserted text appear, the original file is restored.
6. When multiple scramblings are performed, this will be indicated by
the first (capitalized) part of the specially-inserted text
(including two spaces after the colon). When it is present,
nothing will be done to the start of the file, but everything
after that phrase will have either Encryption or the inverse
(Decryption) applied to it. Of course, after every such process,
this program will check to see if the rest of the original
inserted text has reappeared, as already described.
7. Now, will embedding such a plainly-stated phrase make it easier
for those wanting to unscramble a message, against whom the
message was encrypted in the first place? Perhaps. Nevertheless,
it will remain true that thousands of prime numbers, chosen with
semi-randomness from more than 200 million, must be exactly
identified BEFORE even that initial identifier can be correctly
unscrambled, to say nothing of the rest of the message. With
200-million-raised-to-the-thousandth-power as the MINIMUM number
ways to encrypt a message, by the time they can build big enough
quantum supercomputers to crack the message, not only will the
temporal relevance of the message have passed, but by then the
truly-unbreakable quantum encryption techniques will be in wide
use.
WARNING: Any other program that modifies any aspect of the preceding
may cause an inability to restore the original file! (Of course, if
you really know what you are doing, you might acquire even greater
security for your messages, instead. But if you goof, don't say you
weren't warned!)
A variant dilemma will be discovered by anyone who attempts to do
this:
CRYPTION.EXE
"C:\KEY.BIN" 905 E "C:\MESSAGE.TXT" 823 D "C:\MESSAGE.CRP"
which attempts to first apply the encryption algorithm to MESSAGE.TXT
-- and which should successfully create a file called MESSAGE.CRP (the
newly created file is always located in the same directory as the
original file). However, using a completely different group of
pseudorandom numbers derived from the key file (thanks to that initial
encryption effort and the skip numbers), the above example specifies
applying the decryption algorithm to the just-created file. The
dilemma is NOT the fact that MESSAGE.CRP will not exist at the time
the CRYPTION program checks for valid parameters; the program has code
in it designed to handle this situation -- but you do have to specify
an IDENTICAL path for the second file. No, the dilemma concerns
unscrambling the file! --which requires separate stages. Example:
CRYPTION.EXE
"C:\KEY.BIN" 905 E "C:\GARBAGE.CRP" 823 E "C:\MESSAGE.CRP"
where GARBAGE.CRP is simply a disposable copy of MESSAGE.CRP -- and
the second stage:
CRYPTION.EXE "C:\KEY.BIN" 905 D "C:\MESSAGE.CRP"
See, the algorithms quickly generate, use, and FORGET pseudorandom
numbers (which for one thing is why the skip numbers cannot be
negative numbers). The algorithms are not "commutative", in which the
order of "Encryptions" and "Decryptions" is unimportant -- that order
IS important! So the second scrambling of MESSAGE.TXT has to be
undone first, which naturally requires getting at the precise place in
the sequence of pseudorandom numbers where the second part began, of
the original scrambling. Finding that location involves the length of
the original file MESSAGE.TXT, which was modified by adding the
identifier-text described earlier -- including adding the file
extension, which might not be 3 characters! But by design, the
initial AND any subsequent scramblings of a file always mess with the
same amount of added-on text. This is what makes it possible to copy
MESSAGE.CRP to the new file GARBAGE.CRP, and use it as a "filler", as
if its length was just a skip number. Note that either Encryption or
Decryption can be specified for GARBAGE.CRP, because the result should
be discarded. Also, recall that CRYPTION.EXE does not change the
filename GARBAGE.CRP, so specifying a D (before the 823) in the above
unscramble-example is useless. The CRYPTION program already
automatically deletes "original" .CRP files, by overwriting them with
the do-not-delete "resulting" .CRP files. Thus you would have to
manually delete GARBAGE.CRP sometime after that first
unscramble-stage.
There IS an alternative to using a copied/renamed file like
GARBAGE.CRP. If you know the exact length of the .CRP file, as
measured in bytes (let's pretend here it is 12345), then a grand Skip
Number CAN be computed for the first unscrambling stage. Since all
.CRP files will begin with a special capitalized identifier phrase (56
bytes long) that is never scrambled, the portion of the file that IS
scrambled is always 56 less than the total length (12345-56=12289 in
this pretending). So, add that computed 12289 to all other
appropriate Skip Numbers (905 and 823 above), and the first
unscrambling stage is:
CRYPTION.EXE "C:\KEY.BIN" 14017 E "C:\MESSAGE.CRP"
Well, from the preceding, you may imagine that a special program that
keeps track of scrambling-data is going to need to be intelligent
enough to deal with the dilemma just described. (However, that
computation based on file-length would be a very easy thing for it to
do.) And due to the number of data items about which it will need to
record (although, remember, some parameters ARE optional), users of
CRYPTION.EXE are going to want it sooner, not later. The original
author DOES have tentative plans to develop a controller program, but
getting the CRYPTION program working correctly and "out there" has
greater priority -- and due to other commitments, the author would be
neither surprised nor complaining if someone else wrote it first.
As stated previously, a List of Prime Numbers is required to be
available for this program to work. A suitable List does exist,
prepared in a format that compresses the data yet allows easy access.
That List holds all primes smaller than 4,294,967,296, or all primes
that fit in 32 bits of data. There are 203,280,221 prime numbers on
that List, and they manage to fit in about 100 megabytes of space.
The name of this compressed-Primes file, including the directory where
the CRYPTION.EXE program expects to find it, is
"\PRIMES\COMPRESS.PRM". The actual drive holding this directory and
file doesn't matter; CRYPTION.EXE will search all the computer's
drives (except A: and B:), looking for it (and will abort if not
found). In addition to "COMPRESS.PRM", the "\PRIMES" directory must
also contain a file named "CMPRMDEX.QNT", which is a special index
allowing easy access to the Nth prime inside "COMPRESS.PRM". The
CRYPTION.EXE program will also abort if it cannot find that index
file.
The two files are easily and fairly quickly created by another program
that is called "PRMPRESS.EXE" (a 150-Mhz processor can crank out
COMPRESS.PRM in about 20 minutes). PRMPRESS.EXE can in turn be
obtained by compiling the source file "PRMPRESS.C". You should be
able to find it on the Web, perhaps at www.totse.com. All the details
regarding the compressed-data-format and index files are described in
"PRMPRESS.C". Also, for minor entertainment, there is a program
called "PIKPRIME.EXE", which utilizes both data files. It lets you
enter a number N and then (1) tells you if that number is a prime,
and (2) tells you what the Nth prime is -- provided N is smaller than
203,280,222. PIKPRIME.EXE can be obtained by compiling the source
file "PIKPRIME.C", and it also should be findable on the Web. The
reason that PIKPRIME.EXE is mentioned here is that its methods of
using the index file to access the Nth prime are part of the
CRYPTION.EXE program.
/////////////////////////////////////////////
SEQUENCE OF EVENTS AS CRYPTION.EXE RUNS:
0. Checks each drive, starting at C: for a \PRIMES directory
containing the COMPRESS.PRM and CMPRMDEX.QNT files. Aborts if not
found.
1. Checks parameters. Missing info is requested. The user just types
the data and sometimes presses the ENTER key. (Single-character
data items do not need ENTERing. Simple editing of ENTERed data,
via Up-Arrow and BackSpace keys, is also included.) CRYPTION
verifies existence of the specified Key File, and all specified
"Message" files.
2. The Key File must be at least 8K in length, but can be lots longer.
CRYPTION loads up to a megabyte of the Key File for a minimally-
random test and later use. Invalid info leads to repeated requests
for input.
3. Loads the CMPRMDEX.QNT file and constructs a table which will be
used to quickly locate the Nth prime in the COMPRESS.PRM file.
4. Uses previously-loaded Key-File data to construct an array of
primes, their quantity being roughtly 1/8 the length of the Key
File (so a 16K Key could yield an array of 2048 primes -- except
that duplicates are mostly prevented. And while the maximum-loaded
key-file-data can be a megabyte, no more than a 64K array of primes
will be created). These primes, plucked pseudorandomly from the
COMPRESS.PRM file, will in turn yield pseudorandom numbers used to
process any/all "Message" file(s).
5. Opens each "Message" file, along with a new .CRP file. Adds (or
doesn't add) the initial identifying phrases, and the original file
extension.
6. Processes the "Message" file.
7. If a "Message" file becomes unscrambled, deletes identifiers at the
start of the file, and restores the original file extension.
8. Deletes specified key and/or original "Message" file(s). Always
deletes intermediate "Message" .CRP files.
9. Done; program automatically quits.
COMMENTS IN THE PROGRAM CODE WILL EXPLAIN THE MAIN CRYPTION ALGORITHMS
USED
*/////////////////////////////////////////////
/*
THIS C LANGUAGE SOURCE-CODE FILE CAN BE SUCCESSFULLY COMPILED INTO A
WORKING WINDOWS PROGRAM BY BORLAND (Turbo) C++ 4.52, BY BORLAND C++
BUILDER 5, BY MICROSOFT VISUAL C++ 6, AND BY OPEN WATCOM 1.2. Other
compilers for Windows programs have not been tested. Note that the
first of the two Borland compilers dates from the era when Windows
started to replace DOS widely, and Borland was starting to drop the
word "Turbo" from its application-development tools. The others are
thorough-going Windows compilers, suitable even for Windows 2000/XP.
All four are fairly easily available -- for example, the Borland 4.52
compiler is often given away as part of a self-teaching C programming
package. (And the Open Watcom compiler is a free download, although a
donation is requested).
There are no compiler-specific aspects to this source code. As you
see it here, unchanged, any of those four compilers can crunch it into
a working program. (Whether or not that remains true in the future,
after other workers have edited this code, remains to be seen. :)
The exact steps to compile the program differ with each compiler, but
are similar enough to be described as follows: Set up a Project named
CRYPTION, and use the Project Options Editor/Configuration-Tool to
delete ALL default files, such as "CRYPTION.RES" or "CRYPTION.DEF",
and ensure that the ONLY Project file is this one, CRYPTION.C -- you
may have to specify a C Project and not a C++ Project, and if you can
specify an EMPTY project do so. Ensuring that this CRYPTION.C file is
the sole source-code file then becomes easy. Note that this file is
compatible with pre-1999 ANSI C standards, EXCEPT for lots of usage
of pairs of ordinary slash marks // as comment-indicators. (In 1999
the double-slash comment indicator became part of the ANSI standard
for the C programming language.)
BEWARE OF THE COMPILER/DEVELOPMENT-ENVIRONMENT REPLACING SPACES WITH
TABS!!! THERE ARE NO TABS IN THIS FILE; NEARLY ALL INDENTATIONS HERE
ARE PAIRS OF SPACES. The problem of the moment is that different
people prefer different tab-sizes, and much careful formatting at one
tab-size looks really ugly under almost any other. These compilers
mostly offer built-in source-code-editors, along with options that
allow you to specify that tabs should not be used at all, and this is
recommended. (Exception: The Open Watcom compiler allows you to
specify your favorite text editor as part of its Integrated
Development Environment -- so that editor's own configuration must be
examined to eliminate tabs.)
After compiling, the executable file CRYPTION.EXE should successfully
work as described elsewhere herein. However, if CRYPTION.EXE is
copied to another computer, where it was not compiled, it may be
necessary to copy certain other files that come with the compiler, and
to keep them together. The short list:
If compiled under Borland (Turbo) C++ 4.52, the file CW3215.DLL is
required.
If compiled under Borland C++ Builder 5, the number of required
support-files can depend on a variety of things, such as whether
or not the program was compiled for debugging. You will discover
which ones you need as you attempt to run a copy of CRYPTION.EXE
on a computer that contains no compiler. Note that Borland
considers those files to be "redistributables" -- they are
INTENDED to be copied along with .EXE files.
If compiled under Microsoft Visual C++ 6, no other files are
required. Since this CRYPTION program was written for Windows,
and since Microsoft is the monopolistic provider of the Windows
Operating System, it figures that the Microsoft compiler has a
"home team advantage" over competitor compilers.
If compiled under Open Watcom 1.2, no other files are required. The
requested donation appears to have been earned!
Note that the CRYPTION program uses no obscure Windows functions, and
so is compatible with "WINE" under Linux. Since this file is being
openly shared with the general public, with few restrictions, it is
expected that someone will quickly replace the Windows-specific code
with Linux-specific code, while hardly touching the program's
operational algorithms. Various modifications will probably be
necessary if this program is used with another Operating System (for
example, a lot of backslash \ symbols in Windows file names must
edited into regular slash / symbols under Linux or Unix).
Much of the commentary in this program is intended to help budding
C programmers. Those who know this stuff can ignore it.
NOTE TO ADVANCED WINDOWS/C PROGRAMMERS: Yes, this is known to be a
combination of ugly Windows code and reasonably elegant CRYPTION code.
Most of the original effort was put into the most important portions,
and it was not desired to waste a lot of time on part of a program
that (A) is expected to be superceded by a CALLER program (one that
keeps track of all encryption keys) and/or (B) is expected to be
subjected to #ifdef or commenting by workers who prefer Linux or
some other Operating System.
What would you have done?
The Windows stuff merely needed to work smoothly, and indeed it nicely
does. And the next-most-important thing was to swat all the bugs,
which didn't help the elegance of the Windows coding one bit.
Finally, if anyone wants to think of this as a kind of advertisement
of various skills, feel free to contact the original author through
his ISP, pinn.net.
--Vernon Nemitz (vnemitz) August, 2004
*/
// Header files list -- for those who don't know, these files give the
// compiler access to many "background" functions, variables, and
// constants, in a manner that is standardized, so that the "axle"
// doesn't have to be re-invented every time somebody writes a
// brand-new "wheel" program.
#include <windows.h>
#include <winbase.h>
#include <malloc.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <io.h>
// Error Codes: Out of Memory, Opening File, File Read, Linked List,
// Defective Algorithm, Bad Data, File Write
#define CRP_ERR_OM 1
#define CRP_ERR_OF 2
#define CRP_ERR_FR 3
#define CRP_ERR_LL 4
#define CRP_ERR_DA 5
#define CRP_ERR_BD 6
#define CRP_ERR_FW 7
#define CRP_WM_QUIT 100
// That last one is not an error code; indicates user interrupting by
// pressing ESC key
// Other definitions
#define PROCESSOR_INT_SIZE_32
#define PROCESSOR_ENDIAN_TYPE_LITTLE
// Definitions like these, in conjuction with #ifdef/#endif clauses,
// allow a program to be customized. Blocks of code surrounded by
// #ifdef and #endif will ONLY be compiled if a particular "label",
// like PROCESSOR_INT_SIZE_32, has been defined. These two
// definitions ensure that the source code in this program gets
// compiled to an executable that is suitable for all processors
// compatible with the Intel 80386, to the hyperthreaded Pentium 4.
// The definitions are easily changed if some other processor is the
// intended destination of the compiled executable.
// Preprocessor macro
#define uli(x) (unsigned long int)x
// For those inexperienced with the C programming language, the
// purpose of this is to be lazy; it takes a lot less time to type
// uli(variable) than to type (unsigned long int)variable -- and part
// of the compiler known as the preprocessor will conveniently convert
// all instances of one into the other, thanks to that #define
// statement. The preprocessor even generalizes the conversion of
// uli() so it doesn't matter what variable-name is inside the
// parentheses. THEN the main compiler will use the longer expression
// to convert the variable's value from any other integer-type (such
// as signed short, or char) to unsigned long int. Minor joke on
// original author: This macro was not needed quite as often as was
// anticipated, at the time it was declared here.
// And here is an alternate way to do the same thing, for all the
// common data types
#ifdef PROCESSOR_INT_SIZE_32
typedef signed char S_08; // signed, 8 bits
typedef signed short int S_16; // signed, 16 bits
typedef signed long int S_32; // signed, 32 bits
typedef unsigned char U_08; // unsigned, 8 bits
typedef unsigned short int U_16; // unsigned, 16 bits
typedef unsigned long int U_32; // unsigned, 32 bits
#endif
// NOW THERE IS NO POSSIBLE CONFUSION
// With respect to type-conversions, usually called "casting", the way
// to do it is, for example, like this: (U_32)variable -- which you
// can see is equivalent to the (unsigned long int)variable
// mentioned in the previous paragraph. The obvious syntactic
// difference between (U_32)variable and uli(variable) means that
// the compiler never gets confused -- but both ways are now easy to
// type.
/* SPECIAL NOTE REGARDING VARIABLE SIZES:
In the C language, the int type is supposed to be the
"natural" size for the processor. That is, a compiler for a 32-bit
/ processor will be designed so that simple int variables are 32-bit
variables. In prior years, when 16-bit processors ruled, C
compilers were made so easy-to-write int variables were 16-bit
variables. This is actually the primary reason for creating the
typedefs above, to remove any uncertainty about sizes of declared
variables. HOWEVER, BEWARE! If you use the above typedefs on a
16-bit compiler, the S_16 and U_16 types will actually be only 8
bits wide! And the S_32 and U_32 types will actually be only 16
bits wide. Fortunately, such compilers have mostly fallen by the
wayside over the years. All four of the compilers specified
previously in this file will handle these typedefs as is desired and
expected.
One aspect of the preceding is that there are concerns about the
future. Already the phrase "long long int" is sometimes being used to
specify 64-bit data. The C language does not really have a natural
way to accommodate ever-increasing data sizes, as the decades go by.
PERHAPS something like this will be agreed-upon, someday: (1) Let
int remain the natural data size for a processor -- and let the
compiler's documentation make it VERY clear what that size is!
(2) Let every usage of long specify a doubling of that size.
(3) Let every usage of short specify a halving of that size.
Yes, obviously the preceding typedefs break rule (2) because the
specified compilers for this program set the default size of an int
to 32 bits, so "long int" above should not be needed, unless
specifying S_64 or U_64 data. However, that was done mostly for the
benefit of the older Borland compiler, which evolved from a 16-bit
compiler, and has a chance of being confused about whether or not a
particular int is 32 bits. Using long guarantees that the old
compiler will never be confused (and the other compilers don't have
those suggested Rules incorporated, so no problem). But in the
future, should those suggested Rules be accepted and implemented, then
all that a program like this would need, if compiled for, say, a
128-bit processor which had some 256-bit abilities, is something like
this:
#ifdef PROCESSOR_INT_SIZE_128
typedef signed char S_08; // signed, 8 bits
typedef signed short short short int S_16; // signed, 16 bits
typedef signed short short int S_32; // signed, 32 bits
typedef signed short int S_64; // signed, 64 bits
typedef signed int S128; // signed, 128 bits
typedef signed long int S256; // signed, 256 bits
typedef unsigned char U_08; // unsigned, 8 bits
typedef unsigned short short short int U_16; // unsigned, 16 bits
typedef unsigned short short int U_32; // unsigned, 32 bits
typedef unsigned short int U_64; // unsigned, 64 bits
typedef unsigned int U128; // unsigned, 128 bits
typedef unsigned long int S256; // unsigned, 256 bits
#endif
--AND, of course, that #define PROCESSOR_INT_SIZE_32 above would
have to be changed. Yet that would be the ONLY change, in this entire
program, for that other processor (Little-Endian/Big-Endian
controversy excepted, but that's a long comment for another place).
*/
// SPECIAL NOTE REGARDING "signed" DATA: The key fact to ALWAYS
// remember, with respect to computer programs and computer
// programming, is that all data is represented by numbers, and
// THEREFORE there are many ways in which numbers can be used to
// represent data. It is extremely important that Represenation
// #1 be consistently used, and not be confused with any other
// Representation. In the particular case of "signed" data, it is
// first Decided that one single bit will represent a negative value
// -- but only if that bit is set. For any "unsigned" data, it is
// Decided that that same bit will have no negative effect, even if
// set. More specifically, consider the 8 bits inside a single byte
// of data: Each bit has a particular numerical value if set, and
// these unsigned values are: 1, 2, 4, 8, 16, 32, 64, and 128. Note
// that they can be added in any combination to yield results from 1
// to 255 (as well as Zero if none of the bits are set). If we Decide
// that we want that byte to be able to accommodate negative numbers,
// then this "signed" byte has these bit-values: 1, 2, 4, 8, 16, 32,
// 64, and -128. Now they can be added in any combination to yield a
// range of values from -128 to +127 (note that this is still a range
// of 256 different values). The Rule for signed data is very simple
// for all data-sizes, and is always the same: The largest-magnitude
// bit is negative if set. All the other bits are always positive.
// So, for signed 16-bit (S_16) data, the biggest bit has the value of
// -32,768; for S_32 data, the biggest bit has the value of
// -2,147,483,648, and so on.
// Prepare a data structure suitable for many repeated
// parameter-groups
struct affect
{ S_32 skipno; // Number of times to waste pseudorandom
// calculations
char algo; // algorithm code E (encrypt) or D (decrypt)
char pathname[50]; // limitation due to chosen window display
S_16 dotloc; // For location of period preceding filename
// extension
U_32 length; // length of file-to-affect
char kill; // Y (yes) or N (no)
char newname[50]; // Because files are renamed and not always
// deleted
struct affect *prev, *next; // Ensure link-pointers set NULL after
// malloc()
}; // A linked list of these will accommodate many data files to
// process
// And what is a "linked list"? It is a thing that depends on the
// fact that a variable can hold a number which is the memory-location
// (or "address") of some other variable. Such address-holding
// variables are called "pointers", and the address of a structured
// grouping of variables is no different from the address of a single
// variable (both are just numbers). So, inside each instance of this
// structure called "affect", there is a variable called "next" which
// can hold the address of another "affect" structure. A simple rule
// to follow is that if "next" does NOT hold a known-to-be-valid
// address, then it should hold the value of NULL (zero). That way
// one can follow the "next" pointer-links through every item on the
// list of mastdir structures, and know when the end has been reached.
// The comment above, regarding "malloc()", refers to the usage of
// that function to allocate memory to hold an "affect" structure.
// Such a just-allocated structure should be seen as LAST on a linked
// list, and so it is generally logical that its "next" pointer be set
// to NULL. When ANOTHER "affect" structure gets some just-allocated
// memory, then the earlier structure's "next" pointer can be given
// the address of the new structure, while the new one's "next"
// pointer is set to NULL. Naturally, the "prev" pointers work the
// same general way, only backward, each pointing at the previous
// "affect" structure in the linked list. The FIRST structure's
// "prev" pointer must be NULL, is all....
// prepare a data structure suitable for division calculations
struct scratchpad
{ U_32 dvd; // divided (becomes remainder, which is divided again...)
U_32 dvs; // divisor
}; // For lots and lots of division -- thousands of these are expected
// to get used
// Note that the preceding structure could be suitable for holding
// 64-bit numbers. Some 32-bit compilers include a similar structure,
// because there are occasions when 64-bit numbers are greatly needed.
// Using this scratchpad structure will be somewhat unconventional
// though, because the two variables are not named "high" and "low".
// But it could still WORK, thanks to the availability of dynamic
// data-type casting. Not to mention that some 32-bit compilers don't
// have any equivalent. And, below, a specific reason for doing this
// is presented.
// Although included in winbase.h, the Borland C++ Builder 5 compiler
// somehow fails to fully recognize the WIN32_FIND_DATA structure. It
// may be just a bug for which a patch is available. A work-around is
// this renamed clone:
struct W_F_D // Since this overall W_F_D structure will be typecast
{ U_32 Attribs; // as a WIN_32_FIND_DATA when actually
struct scratchpad CreateTime; // used, the internal data types and
struct scratchpad LastAccessed; // names are irrelevant. Windows
struct scratchpad LastWritten;// just wants the correct total number
U_32 FileSzHi; // of bytes.
U_32 FileSzLo; // This is the only varible WE need.
U_32 Rsrvd0;
U_32 Rsrvd1;
FAR char FilNam[MAX_PATH];
FAR char AltNam[14];
};
// Fortunately, no other work-arounds were needed, to get four
// different compilers to successfully process this file, with no
// editing needed by the user. That is, even though the other three
// compilers don't need this structure, they can still accept it and
// use it and the result works fine. On the other hand, this is to
// be expected when staying within the bounds of the ANSI standards
// for the C language.
// Prototypes (also known as "forward declarations") of subroutine
// functions in this file
LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam,
LPARAM lParam);
S_32 ProcessMessages(void);
S_32 keycheck(void);
S_32 WorkAreaInit(void);
S_32 IndexToPrime(U_32 *dp);
U_32 Divide(struct scratchpad *sp, S_32 bits);
U_08 PseudoRandomize(U_08 uc);
S_32 KeyToPrimes(void);
S_32 AffectFiles(void); // uses struct affect *afflist declared below
/* For those who want to know what that section is all about, note
that the compiler processes a source-code file like this one from
start to finish, sequentially. The compiler will complain if it
encounters any phrase that it cannot recognize, and such phrases
include the names of subroutine functions, if they are specified/
declared/defined in the file AFTER any usage of them. Since usage
usually occurs in the "main" part of the program, one solution is
to place all the subroutine functions/definitions near the start of
the file, so that they are guaranteed to precede any usage of them.
However, when there are many subroutines, someone who is editing
such a file often has to hunt for the start of the main program,
and there is an aura of untidiness about doing things that way.
These prototypes are an alternative, a promise to the compiler that
those functions will be found later on in the file. Thus the main
program can be located near the start of the file's executable
code, and subroutine functions can be located in their logical
place, at the rear.
*/
/*
Global Variables. Programmers encounter many discussions regarding
these things. In a really long program (multiple files) it may be
best to put them all in a structure, and have just one global
structure-pointer, with space for the structure malloc()'d. Most of
the "static" variables in this program would probably get moved into
such a structure (they are essentially global, anyway). However,
constant dereferencing of a structure, to get at the variables, slows
a program down. This program needs to run as fast as possible --
which is why there are relatively few sub-functions (with associated
overhead in calling them frequently, not to mention stack-spasms as
space for local variables is constantly created/destroyed). Still,
discussions continue, and the oldest-fashion way was chosen here.
*/
struct W_F_D wfd;
HWND hwnd;
FILE *dskfil, *prmfil;
HFONT hfont1, hfont2;
HANDLE hPriorF, fil1, fil2;
HINSTANCE previnst;
LPSTR szParams;
RECT rect;
char eraser[100] = " "
" "
" ",
keynam[] = " "
" ", ch,
keyfile[50], tmpstr[800], tadone[60], prm[50],
del1, doit, proceed, *p, *q;
U_08 *ucMemBlk1, *ucMemBlk2, *ucMemBlk3, *ucMemBlk4, *ucp1,
*ucp4, *by, bt, ModType[5], ModData[5];
U_08 skpd[7] = {1, 2, 3, 5, 7, 11, 13};
/* The table below is thoroughly explained in both the PRMPRESS.C and
PIKPRIME.C files. Here it is used to decompress the data in the
COMPRESS.PRM file. */
U_08 tbl[5761] =
{16, 2, 4, 6, 2, 6, 4, 2, 4, 6, 6, 2, 6, 4, 2, 6,
4, 6, 8, 4, 2, 4, 2, 4, 14, 4, 6, 2, 10, 2, 6, 6,
4, 6, 6, 2, 10, 2, 4, 2, 12, 12, 4, 2, 4, 6, 2, 10,
6, 6, 6, 2, 6, 4, 2, 6, 4, 14, 4, 2, 4, 6, 8, 6,
10, 2, 4, 6, 2, 6, 6, 6, 4, 6, 2, 6, 4, 8, 10, 2,
10, 2, 4, 2, 4, 6, 8, 4, 2, 4, 12, 8, 4, 2, 6, 4,
6, 12, 2, 4, 2, 12, 6, 4, 6, 6, 6, 2, 6, 10, 2, 4,
6, 2, 6, 6, 4, 2, 10, 2, 10, 2, 4, 6, 6, 2, 6, 6,
4, 6, 8, 6, 4, 2, 6, 4, 6, 8, 4, 2, 6, 4, 8, 6,
4, 8, 4, 6, 8, 10, 2, 10, 2, 6, 4, 2, 4, 2, 10, 2,
10, 2, 4, 2, 4, 14, 4, 2, 4, 6, 6, 2, 6, 4, 8, 10,
8, 4, 2, 4, 6, 8, 6, 4, 6, 6, 6, 2, 6, 6, 4, 2,
4, 6, 2, 10, 2, 4, 2, 10, 2, 10, 2, 6, 4, 8, 6, 4,
2, 4, 6, 6, 8, 4, 2, 6, 10, 8, 4, 2, 6, 4, 8, 10,
6, 2, 4, 8, 6, 6, 4, 2, 4, 6, 2, 6, 4, 6, 2, 10,
12, 2, 4, 2, 4, 6, 2, 6, 4, 2, 4, 12, 2, 6, 6, 10,
6, 8, 4, 2, 4, 2, 4, 8, 6, 12, 4, 6, 2, 12, 4, 2,
4, 6, 8, 4, 2, 4, 2, 12, 10, 2, 4, 2, 4, 6, 2, 10,
2, 4, 6, 8, 6, 4, 2, 6, 4, 6, 8, 4, 6, 2, 4, 8,
6, 4, 6, 2, 4, 6, 2, 6, 6, 4, 6, 6, 8, 6, 4, 2,
10, 2, 10, 2, 4, 2, 10, 2, 6, 4, 2, 10, 6, 2, 6, 4,
2, 6, 4, 6, 8, 6, 4, 2, 12, 10, 6, 2, 4, 6, 2, 12,
4, 2, 4, 8, 6, 4, 2, 4, 2, 10, 2, 10, 6, 2, 4, 6,
2, 6, 4, 2, 10, 6, 2, 6, 4, 12, 6, 8, 6, 4, 2, 4,
8, 6, 4, 6, 2, 4, 6, 8, 6, 6, 4, 6, 2, 6, 4, 2,
4, 2, 10, 12, 2, 4, 12, 2, 6, 4, 2, 4, 6, 6, 2, 12,
6, 4, 18, 2, 4, 2, 4, 8, 6, 4, 6, 2, 4, 8, 6, 6,
4, 2, 4, 6, 2, 6, 4, 2, 4, 12, 2, 12, 6, 4, 6, 2,
6, 4, 6, 6, 6, 2, 6, 4, 2, 6, 4, 6, 8, 4, 2, 4,
2, 4, 14, 4, 6, 2, 10, 2, 6, 6, 4, 2, 10, 2, 10, 2,
4, 14, 10, 2, 4, 2, 4, 6, 2, 6, 10, 6, 6, 2, 10, 2,
6, 4, 6, 8, 4, 2, 4, 6, 8, 6, 10, 2, 4, 6, 2, 6,
6, 4, 2, 4, 6, 2, 6, 4, 2, 6, 10, 2, 10, 6, 2, 4,
6, 8, 4, 2, 4, 12, 2, 6, 4, 2, 6, 4, 6, 12, 2, 4,
2, 4, 8, 6, 4, 6, 2, 10, 2, 6, 10, 6, 6, 2, 6, 4,
2, 4, 2, 10, 2, 12, 4, 6, 6, 2, 12, 4, 6, 6, 2, 6,
4, 2, 6, 4, 14, 4, 2, 6, 4, 8, 6, 4, 6, 2, 4, 6,
8, 6, 6, 10, 2, 6, 4, 6, 2, 10, 2, 10, 2, 4, 2, 4,
8, 6, 4, 2, 4, 6, 6, 8, 4, 8, 4, 6, 8, 4, 2, 4,
2, 12, 6, 4, 6, 6, 6, 2, 6, 6, 4, 2, 4, 6, 2, 6,
6, 4, 2, 10, 2, 10, 2, 6, 4, 6, 2, 6, 4, 2, 4, 6,
14, 4, 2, 6, 10, 8, 4, 2, 4, 2, 4, 8, 10, 8, 4, 8,
6, 10, 2, 4, 6, 2, 6, 4, 6, 2, 10, 2, 10, 2, 4, 2,
4, 6, 8, 4, 2, 4, 6, 6, 2, 6, 6, 6, 10, 8, 4, 2,
4, 6, 8, 6, 4, 8, 4, 6, 2, 6, 6, 4, 2, 4, 6, 12,
2, 4, 2, 10, 2, 10, 2, 4, 2, 4, 8, 10, 2, 4, 6, 8,
6, 4, 2, 6, 4, 6, 8, 4, 8, 4, 8, 6, 4, 6, 2, 4,
6, 2, 6, 6, 4, 6, 6, 2, 6, 6, 4, 2, 10, 12, 2, 4,
2, 4, 6, 2, 6, 4, 2, 16, 2, 6, 4, 2, 10, 6, 8, 4,
2, 4, 2, 12, 6, 10, 2, 4, 6, 2, 12, 4, 2, 4, 8, 6,
4, 2, 4, 2, 12, 10, 6, 2, 4, 6, 2, 6, 4, 2, 4, 6,
6, 2, 6, 4, 2, 10, 6, 8, 10, 2, 4, 8, 6, 4, 6, 2,
4, 6, 2, 6, 6, 6, 4, 6, 8, 4, 2, 4, 2, 10, 12, 2,
4, 2, 10, 2, 6, 4, 2, 4, 6, 6, 2, 10, 2, 6, 4, 14,
6, 4, 2, 4, 8, 10, 6, 2, 4, 6, 2, 6, 6, 4, 2, 4,
8, 6, 4, 2, 4, 12, 2, 12, 4, 2, 4, 6, 2, 6, 4, 2,
10, 6, 2, 6, 4, 8, 4, 6, 8, 4, 2, 4, 2, 4, 14, 4,
6, 2, 10, 8, 6, 4, 2, 4, 6, 2, 10, 2, 4, 2, 12, 10,
2, 4, 6, 6, 2, 6, 4, 6, 6, 6, 2, 6, 6, 6, 4, 6,
12, 2, 4, 6, 8, 6, 10, 2, 4, 8, 6, 6, 4, 2, 4, 6,
2, 6, 4, 2, 6, 10, 2, 10, 2, 6, 4, 6, 8, 4, 6, 12,
2, 6, 4, 2, 6, 4, 6, 12, 2, 4, 2, 4, 14, 4, 6, 2,
4, 6, 2, 6, 10, 2, 10, 2, 6, 4, 2, 4, 12, 2, 10, 2,
4, 6, 6, 2, 6, 6, 4, 6, 6, 2, 10, 2, 6, 4, 6, 8,
4, 2, 6, 4, 8, 6, 4, 6, 2, 4, 6, 8, 6, 4, 2, 10,
2, 6, 4, 2, 6, 10, 2, 10, 6, 2, 4, 8, 6, 4, 2, 4,
6, 6, 2, 6, 4, 8, 4, 6, 8, 4, 2, 4, 2, 4, 8, 6,
4, 6, 12, 2, 6, 6, 4, 6, 6, 2, 6, 4, 2, 4, 2, 10,
2, 12, 6, 4, 6, 2, 10, 2, 4, 6, 6, 8, 4, 2, 6, 18,
4, 2, 4, 2, 4, 8, 10, 6, 2, 4, 8, 6, 6, 6, 4, 6,
2, 6, 4, 6, 2, 10, 2, 10, 2, 4, 2, 4, 6, 2, 6, 4,
2, 4, 6, 6, 8, 6, 6, 4, 6, 8, 4, 2, 4, 2, 12, 6,
4, 12, 6, 2, 6, 6, 4, 2, 4, 6, 8, 6, 4, 2, 10, 2,
10, 2, 4, 2, 4, 6, 2, 10, 2, 4, 6, 8, 6, 4, 2, 6,
4, 6, 8, 4, 6, 2, 4, 8, 6, 4, 8, 4, 6, 2, 6, 10,
6, 6, 2, 6, 6, 4, 2, 10, 2, 10, 2, 4, 2, 4, 6, 8,
4, 2, 10, 6, 2, 6, 4, 2, 6, 10, 8, 4, 2, 4, 14, 6,
4, 6, 2, 4, 6, 2, 12, 4, 2, 4, 8, 10, 2, 4, 2, 10,
2, 10, 6, 2, 4, 8, 6, 4, 2, 4, 6, 6, 2, 6, 4, 2,
10, 6, 8, 6, 6, 4, 8, 6, 4, 6, 2, 4, 6, 2, 6, 6,
6, 4, 6, 2, 6, 4, 2, 4, 2, 10, 12, 2, 4, 2, 10, 2,
6, 4, 2, 4, 12, 2, 10, 2, 10, 14, 4, 2, 4, 2, 4, 8,
6, 10, 2, 4, 6, 2, 12, 4, 2, 4, 6, 2, 6, 4, 2, 4,
14, 12, 4, 2, 4, 6, 2, 6, 4, 2, 4, 6, 6, 2, 6, 4,
2, 6, 4, 6, 8, 4, 6, 2, 4, 14, 4, 6, 2, 10, 2, 6,
6, 4, 2, 4, 6, 12, 2, 4, 2, 12, 10, 2, 4, 2, 10, 2,
6, 4, 6, 6, 6, 2, 6, 4, 2, 6, 4, 6, 8, 6, 4, 6,
8, 16, 2, 4, 6, 2, 6, 6, 4, 2, 4, 8, 6, 4, 2, 6,
10, 2, 10, 2, 4, 2, 4, 6, 8, 4, 2, 16, 2, 6, 4, 8,
4, 6, 12, 2, 4, 2, 4, 8, 6, 4, 6, 2, 4, 6, 8, 10,
2, 4, 6, 2, 6, 4, 2, 4, 2, 10, 2, 10, 2, 4, 6, 6,
2, 6, 6, 4, 6, 6, 2, 6, 6, 6, 4, 6, 12, 2, 6, 4,
8, 6, 4, 6, 2, 4, 14, 6, 4, 2, 10, 2, 6, 4, 2, 4,
2, 10, 2, 10, 2, 6, 4, 8, 6, 4, 6, 6, 6, 2, 6, 4,
8, 4, 6, 8, 4, 2, 4, 2, 4, 14, 4, 6, 6, 6, 2, 6,
6, 4, 2, 10, 2, 6, 4, 2, 4, 12, 2, 10, 2, 6, 4, 6,
2, 6, 6, 4, 6, 6, 12, 2, 6, 10, 8, 4, 2, 4, 2, 4,
8, 10, 6, 2, 4, 8, 6, 6, 4, 2, 4, 6, 2, 6, 4, 8,
10, 2, 10, 6, 2, 4, 6, 2, 6, 4, 2, 4, 6, 6, 2, 6,
6, 6, 4, 6, 8, 4, 2, 4, 2, 4, 8, 6, 4, 8, 10, 2,
6, 6, 4, 6, 6, 8, 4, 2, 4, 2, 10, 2, 12, 4, 2, 4,
6, 2, 10, 2, 4, 6, 8, 6, 4, 2, 6, 4, 14, 4, 6, 2,
4, 8, 6, 4, 6, 2, 4, 6, 2, 6, 6, 10, 6, 2, 6, 10,
2, 10, 2, 10, 2, 4, 2, 4, 6, 2, 6, 4, 2, 10, 6, 8,
4, 2, 6, 4, 6, 8, 4, 2, 4, 2, 12, 6, 4, 6, 6, 6,
2, 12, 4, 2, 4, 8, 6, 6, 4, 2, 10, 2, 10, 6, 2, 4,
6, 2, 6, 4, 2, 4, 6, 8, 6, 4, 2, 10, 6, 8, 6, 4,
2, 4, 8, 6, 4, 8, 4, 6, 2, 6, 12, 4, 6, 2, 6, 4,
2, 4, 2, 10, 12, 2, 4, 2, 10, 8, 4, 2, 4, 6, 6, 2,
10, 2, 6, 18, 4, 2, 4, 6, 8, 6, 4, 6, 2, 4, 6, 2,
6, 6, 4, 2, 4, 6, 2, 10, 2, 4, 12, 2, 12, 4, 2, 4,
8, 6, 4, 2, 4, 6, 6, 2, 6, 4, 2, 6, 4, 6, 8, 4,
2, 6, 4, 14, 4, 6, 2, 10, 2, 6, 6, 4, 2, 4, 6, 2,
10, 2, 4, 2, 22, 2, 4, 2, 4, 6, 2, 6, 4, 6, 12, 2,
6, 4, 2, 10, 6, 8, 4, 2, 4, 6, 8, 6, 10, 2, 4, 6,
2, 12, 4, 2, 4, 6, 2, 6, 4, 2, 6, 12, 10, 2, 4, 2,
4, 6, 8, 4, 2, 4, 12, 2, 6, 4, 2, 6, 4, 6, 12, 6,
2, 4, 8, 6, 4, 6, 2, 4, 6, 2, 6, 10, 2, 4, 6, 8,
4, 2, 4, 2, 10, 2, 10, 2, 4, 12, 2, 6, 6, 4, 6, 6,
2, 6, 4, 2, 6, 4, 6, 8, 6, 6, 4, 8, 10, 6, 2, 4,
6, 8, 6, 4, 2, 12, 6, 4, 2, 4, 2, 10, 2, 10, 2, 4,
2, 4, 8, 6, 4, 2, 10, 6, 2, 6, 4, 8, 4, 6, 8, 4,
2, 4, 2, 4, 8, 6, 4, 6, 6, 6, 8, 6, 4, 2, 4, 6,
2, 6, 4, 2, 4, 2, 10, 2, 10, 2, 10, 6, 2, 6, 4, 2,
4, 6, 6, 8, 6, 6, 10, 12, 2, 4, 2, 4, 8, 10, 6, 2,
4, 8, 6, 6, 4, 2, 4, 6, 2, 6, 4, 6, 2, 10, 2, 10,
2, 6, 4, 6, 2, 6, 4, 6, 6, 6, 2, 6, 6, 6, 4, 6,
8, 4, 2, 4, 2, 4, 14, 4, 8, 4, 6, 2, 6, 6, 4, 2,
10, 8, 4, 2, 4, 12, 2, 10, 2, 4, 2, 4, 6, 2, 12, 4,
6, 8, 10, 2, 6, 4, 6, 8, 4, 6, 2, 4, 8, 6, 4, 6,
2, 4, 6, 2, 6, 6, 4, 6, 6, 2, 6, 6, 6, 10, 2, 10,
6, 2, 4, 6, 2, 6, 4, 2, 10, 6, 2, 6, 4, 2, 6, 4,
6, 8, 4, 2, 4, 2, 12, 6, 4, 6, 2, 10, 2, 12, 4, 6,
8, 6, 4, 2, 4, 2, 10, 2, 16, 2, 4, 6, 2, 10, 2, 4,
6, 6, 2, 6, 4, 2, 10, 14, 6, 4, 2, 4, 8, 6, 4, 6,
2, 4, 6, 2, 6, 6, 6, 4, 6, 2, 6, 4, 6, 2, 10, 12,
2, 4, 2, 10, 2, 6, 4, 2, 4, 6, 6, 12, 2, 6, 4, 14,
4, 2, 4, 2, 12, 6, 4, 6, 6, 6, 2, 6, 6, 4, 2, 4,
6, 2, 6, 6, 4, 12, 2, 12, 4, 2, 4, 6, 2, 6, 4, 2,
4, 6, 8, 6, 4, 2, 6, 4, 6, 8, 4, 2, 4, 2, 4, 14,
4, 8, 10, 2, 6, 10, 2, 4, 6, 2, 10, 2, 4, 2, 12, 10,
2, 4, 2, 4, 6, 8, 4, 6, 6, 6, 2, 6, 4, 2, 6, 10,
8, 4, 2, 4, 6, 8, 6, 10, 2, 4, 6, 2, 6, 6, 4, 2,
4, 6, 2, 10, 2, 6, 10, 2, 10, 2, 4, 2, 4, 14, 4, 2,
4, 12, 2, 6, 4, 2, 6, 4, 6, 12, 2, 6, 4, 8, 6, 4,
6, 2, 4, 6, 2, 6, 10, 2, 4, 6, 2, 6, 4, 2, 4, 2,
10, 12, 2, 4, 6, 6, 2, 6, 6, 4, 12, 2, 6, 4, 2, 10,
6, 8, 4, 2, 6, 4, 8, 6, 10, 2, 4, 6, 14, 4, 2, 10,
2, 6, 4, 2, 4, 2, 12, 10, 2, 4, 2, 4, 8, 6, 4, 2,
4, 6, 6, 2, 6, 4, 8, 4, 6, 8, 4, 6, 2, 4, 8, 6,
4, 6, 6, 6, 2, 6, 6, 4, 2, 4, 6, 8, 4, 2, 4, 2,
10, 2, 10, 2, 6, 10, 2, 6, 4, 2, 4, 6, 6, 8, 4, 2,
6, 10, 8, 6, 4, 2, 4, 8, 10, 6, 2, 4, 8, 6, 6, 4,
2, 4, 8, 6, 4, 6, 2, 10, 2, 10, 2, 4, 2, 4, 6, 2,
6, 4, 2, 10, 6, 2, 6, 12, 4, 6, 8, 4, 2, 4, 2, 4,
8, 6, 4, 8, 4, 6, 8, 6, 4, 2, 4, 6, 8, 4, 2, 4,
2, 10, 2, 10, 2, 4, 6, 6, 2, 10, 2, 4, 6, 8, 6, 6,
6, 4, 6, 12, 6, 2, 4, 8, 6, 4, 6, 2, 4, 8, 6, 6,
4, 6, 6, 2, 6, 6, 4, 2, 10, 2, 10, 2, 6, 4, 6, 2,
6, 4, 12, 6, 2, 6, 4, 2, 6, 4, 6, 8, 4, 2, 4, 2,
18, 4, 6, 2, 4, 6, 2, 12, 4, 2, 12, 6, 4, 2, 4, 12,
2, 10, 6, 2, 4, 6, 2, 6, 6, 4, 6, 6, 2, 10, 2, 10,
6, 8, 6, 4, 2, 4, 8, 6, 4, 6, 2, 4, 6, 2, 6, 6,
6, 4, 6, 2, 6, 4, 2, 6, 10, 12, 6, 2, 10, 2, 6, 4,
2, 4, 6, 6, 2, 10, 2, 6, 4, 14, 4, 2, 4, 2, 4, 8,
6, 4, 6, 2, 10, 2, 6, 6, 4, 6, 6, 2, 6, 4, 2, 4,
12, 2, 12, 4, 2, 4, 6, 2, 10, 2, 4, 6, 6, 2, 6, 4,
2, 6, 4, 14, 4, 2, 4, 2, 4, 14, 4, 6, 2, 10, 2, 6,
6, 6, 4, 6, 2, 10, 6, 2, 12, 10, 2, 4, 2, 4, 6, 2,
6, 4, 6, 6, 6, 8, 4, 2, 6, 4, 6, 8, 4, 2, 4, 14,
6, 10, 6, 6, 2, 6, 6, 4, 2, 4, 6, 2, 6, 6, 6, 10,
2, 10, 2, 4, 2, 4, 6, 8, 4, 2, 4, 14, 6, 4, 2, 6,
4, 6, 12, 2, 4, 2, 4, 8, 6, 4, 8, 4, 6, 2, 6, 10,
2, 4, 6, 2, 6, 4, 2, 4, 2, 10, 2, 10, 2, 4, 6, 6,
8, 6, 4, 6, 6, 2, 6, 4, 2, 6, 10, 8, 4, 2, 10, 8,
6, 4, 6, 2, 4, 6, 8, 6, 4, 2, 10, 2, 10, 2, 4, 2,
10, 2, 10, 2, 4, 2, 4, 8, 6, 4, 2, 4, 6, 6, 2, 6,
4, 8, 4, 6, 8, 4, 2, 6, 4, 8, 6, 4, 6, 6, 6, 2,
6, 6, 4, 2, 4, 6, 2, 6, 4, 2, 4, 2, 10, 12, 2, 6,
4, 6, 2, 6, 4, 2, 4, 12, 8, 4, 2, 16, 8, 4, 2, 4,
2, 4, 8, 16, 2, 4, 8, 12, 4, 2, 4, 6, 2, 6, 4, 6,
2, 12, 10, 2, 4, 2, 4, 6, 2, 6, 4, 2, 4, 6, 6, 2,
6, 6, 6, 4, 6, 8, 4, 6, 2, 4, 8, 6, 4, 8, 4, 6,
2, 6, 6, 4, 2, 4, 6, 8, 4, 2, 4, 2, 10, 2, 10, 2,
4, 2, 10, 2, 10, 2, 4, 6, 8, 6, 4, 2, 6, 4, 6, 8,
10, 2, 4, 8, 10, 6, 2, 4, 6, 2, 6, 6, 4, 6, 8, 6,
6, 4, 2, 10, 2, 10, 2, 4, 2, 4, 6, 2, 6, 4, 2, 10,
6, 2, 6, 4, 8, 4, 6, 8, 4, 2, 4, 2, 12, 6, 4, 6,
2, 4, 6, 14, 4, 2, 4, 8, 6, 4, 2, 4, 2, 10, 2, 10,
6, 6, 6, 2, 6, 4, 2, 4, 6, 6, 2, 6, 6, 10, 6, 14,
4, 2, 4, 8, 6, 4, 6, 2, 4, 8, 6, 6, 6, 4, 6, 2,
6, 4, 2, 4, 2, 10, 12, 2, 6, 10, 2, 6, 4, 6, 6, 6,
2, 10, 2, 6, 4, 14, 4, 2, 4, 2, 4, 14, 4, 6, 2, 4,
6, 2, 6, 6, 4, 2, 10, 2, 6, 4, 2, 4, 12, 2, 12, 4,
2, 4, 6, 2, 6, 6, 4, 6, 6, 2, 10, 2, 6, 4, 6, 8,
4, 2, 4, 2, 4, 14, 4, 6, 2, 10, 2, 6, 6, 4, 2, 4,
6, 2, 10, 2, 6, 12, 10, 6, 2, 4, 6, 2, 6, 4, 6, 6,
6, 2, 6, 4, 2, 6, 4, 6, 8, 4, 2, 4, 6, 8, 6, 10,
2, 10, 2, 6, 6, 4, 6, 6, 2, 6, 4, 2, 6, 10, 2, 12,
4, 2, 4, 6, 12, 2, 4, 12, 2, 6, 4, 2, 6, 4, 18, 2,
4, 2, 4, 8, 6, 4, 6, 2, 4, 6, 2, 6, 12, 4, 6, 2,
6, 4, 6, 2, 10, 2, 10, 2, 4, 6, 6, 2, 6, 6, 4, 6,
6, 8, 4, 2, 6, 4, 6, 8, 4, 2, 6, 12, 6, 4, 6, 6,
6, 8, 6, 4, 2, 10, 2, 6, 6, 4, 2, 10, 2, 10, 2, 4,
2, 4, 8, 6, 4, 2, 4, 6, 8, 6, 4, 8, 4, 6, 8, 4,
2, 4, 2, 4, 8, 6, 4, 12, 6, 2, 6, 10, 2, 4, 6, 2,
6, 4, 2, 4, 2, 10, 2, 10, 2, 6, 4, 6, 8, 4, 2, 4,
6, 6, 8, 4, 2, 6, 10, 8, 4, 2, 4, 6, 8, 10, 6, 2,
4, 8, 6, 6, 4, 2, 4, 6, 2, 10, 6, 2, 10, 2, 10, 2,
4, 2, 4, 8, 6, 4, 2, 4, 6, 6, 2, 6, 6, 6, 4, 6,
8, 4, 2, 6, 4, 8, 6, 4, 8, 4, 6, 2, 6, 6, 4, 2,
4, 6, 8, 4, 2, 4, 2, 10, 12, 2, 4, 2, 4, 6, 2, 10,
2, 4, 14, 6, 4, 2, 10, 6, 8, 4, 6, 2, 4, 8, 6, 10,
2, 4, 6, 2, 12, 4, 6, 6, 2, 6, 6, 4, 2, 12, 10, 2,
4, 2, 4, 6, 2, 6, 4, 2, 10, 6, 2, 6, 4, 2, 6, 4,
6, 8, 4, 6, 2, 12, 6, 4, 6, 2, 4, 6, 2, 12, 4, 2,
4, 14, 4, 2, 4, 2, 10, 2, 10, 6, 2, 10, 2, 6, 4, 2,
4, 6, 6, 2, 6, 4, 2, 10, 6, 8, 6, 4, 2, 4, 8, 10,
6, 2, 4, 6, 2, 6, 6, 6, 4, 8, 6, 4, 2, 4, 2, 10,
12, 2, 4, 2, 10, 2, 6, 4, 2, 10, 6, 2, 10, 8, 4, 14,
4, 2, 4, 2, 4, 8, 6, 4, 6, 2, 4, 6, 8, 6, 4, 2,
4, 6, 2, 6, 4, 2, 4, 12, 2, 12, 4, 6, 6, 2, 6, 4,
2, 4, 6, 6, 2, 6, 6, 6, 4, 6, 12, 2, 4, 2, 4, 14,
4, 6, 2, 12, 6, 6, 4, 2, 4, 6, 2, 10, 2, 4, 2, 12,
10, 2, 6, 4, 6, 2, 6, 4, 6, 6, 6, 2, 6, 4, 2, 6,
4, 6, 8, 4, 2, 4, 6, 14, 10, 2, 4, 6, 2, 6, 6, 4,
2, 10, 2, 6, 4, 2, 16, 2, 10, 2, 4, 2, 4, 6, 8, 6,
4, 12, 2, 10, 2, 6, 4, 6, 12, 2, 4, 2, 4, 8, 6, 4,
6, 2, 4, 6, 2, 6, 10, 2, 4, 6, 2, 6, 4, 2, 6, 10,
2, 10, 6, 6, 6, 2, 6, 6, 4, 6, 6, 2, 6, 4, 2, 6,
4, 6, 8, 4, 2, 6, 4, 8, 6, 4, 6, 2, 10, 8, 6, 4,
12, 2, 6, 4, 2, 4, 2, 10, 2, 12, 4, 2, 4, 8, 10, 2,
4, 6, 6, 2, 6, 4, 8, 4, 14, 4, 2, 4, 2, 4, 8, 6,
4, 6, 6, 6, 2, 6, 6, 6, 4, 6, 2, 6, 4, 6, 2, 10,
2, 10, 2, 6, 4, 6, 2, 6, 4, 2, 4, 6, 6, 8, 4, 2,
6, 10, 8, 4, 2, 4, 2, 12, 10, 6, 6, 8, 6, 6, 4, 2,
4, 6, 2, 6, 10, 2, 10, 2, 10, 2, 4, 2, 4, 6, 2, 6,
4, 2, 4, 6, 8, 6, 6, 6, 4, 6, 8, 4, 2, 4, 2, 4,
8, 6, 4, 8, 4, 6, 2, 6, 10, 2, 4, 6, 8, 4, 2, 4,
2, 10, 2, 10, 2, 4, 2, 4, 6, 12, 2, 4, 6, 8, 6, 4,
2, 6, 10, 8, 4, 6, 6, 8, 6, 4, 6, 2, 4, 6, 2, 6,
6, 4, 6, 6, 2, 12, 4, 2, 10, 2, 10, 2, 4, 2, 4, 8,
6, 4, 2, 10, 6, 2, 6, 4, 2, 6, 4, 6, 8, 4, 2, 6,
12, 6, 4, 6, 2, 4, 6, 2, 12, 4, 2, 4, 8, 6, 4, 2,
4, 2, 10, 12, 6, 2, 4, 6, 2, 6, 4, 2, 4, 12, 2, 6,
4, 2, 10, 6, 8, 6, 4, 2, 4, 8, 6, 10, 2, 4, 6, 2,
12, 6, 4, 6, 2, 6, 4, 2, 4, 2, 22, 2, 4, 2, 10, 2,
6, 4, 2, 4, 6, 6, 2, 10, 2, 6, 4, 14, 4, 6, 2, 4,
8, 6, 4, 6, 2, 4, 6, 2, 6, 6, 4, 2, 4, 6, 8, 4,
2, 4, 12, 2, 12, 4, 2, 10, 2, 6, 4, 2, 4, 6, 6, 2,
6, 4, 2, 6, 4, 6, 8, 6, 4, 2, 4, 18, 6, 2, 10, 2,
6, 6, 4, 2, 4, 8, 10, 2, 4, 2, 12, 10, 2, 4, 2, 4,
6, 2, 6, 4, 12, 6, 2, 6, 4, 8, 4, 6, 8, 4, 2, 4,
6, 8, 6, 10, 2, 4, 6, 8, 6, 4, 2, 4, 6, 2, 6, 4,
2, 6, 10, 2, 10, 2, 4, 6, 6, 8, 4, 2, 4, 12, 2, 6,
6, 6, 4, 6, 12, 2, 4, 2, 4, 8, 6, 4, 6, 2, 4, 8,
6, 10, 2, 4, 6, 2, 6, 4, 2, 4, 2, 10, 2, 10, 2, 10,
6, 2, 6, 10, 6, 6, 2, 6, 4, 2, 6, 4, 6, 8, 4, 2,
6, 4, 14, 4, 6, 2, 4, 6, 8, 6, 4, 2, 10, 2, 6, 4,
2, 4, 12, 2, 10, 2, 4, 2, 4, 8, 6, 6, 4, 6, 6, 2,
10, 8, 4, 6, 8, 4, 2, 4, 2, 4, 8, 6, 4, 6, 6, 6,
2, 6, 6, 4, 2, 4, 6, 2, 6, 4, 2, 6, 10, 2, 10, 8,
4, 6, 2, 6, 4, 2, 4, 6, 6, 8, 4, 2, 6, 10, 8, 4,
2, 4, 2, 4, 8, 10, 6, 2, 12, 6, 6, 4, 6, 6, 2, 6,
4, 6, 2, 10, 2, 12, 4, 2, 4, 6, 2, 10, 2, 4, 6, 6,
2, 6, 6, 6, 4, 14, 4, 2, 4, 2, 4, 8, 6, 4, 8, 4,
6, 2, 6, 6, 6, 4, 6, 8, 4, 6, 2, 10, 2, 10, 2, 4,
2, 4, 6, 2, 10, 2, 4, 6, 14, 4, 2, 6, 4, 6, 8, 4,
6, 2, 12, 6, 4, 6, 6, 6, 2, 6, 6, 4, 6, 6, 2, 6,
6, 4, 2, 10, 2, 10, 2, 4, 2, 4, 6, 2, 6, 4, 2, 10,
8, 6, 4, 2, 6, 4, 6, 8, 4, 2, 4, 2, 12, 6, 4, 8,
4, 6, 2, 16, 2, 4, 8, 6, 4, 2, 4, 2, 10, 2, 10, 6,
2, 4, 6, 8, 4, 2, 4, 6, 6, 2, 6, 4, 2, 16, 8, 6,
4, 6, 8, 6, 4, 6, 2, 4, 6, 2, 6, 6, 6, 4, 6, 2,
10, 2, 4, 2, 10, 12, 2, 4, 2, 12, 6, 4, 2, 4, 6, 6,
2, 10, 2, 6, 4, 14, 4, 2, 6, 4, 8, 6, 4, 6, 2, 4,
6, 2, 6, 6, 4, 2, 4, 6, 2, 6, 4, 2, 4, 12, 14, 4,
2, 4, 6, 2, 6, 4, 2, 4, 12, 2, 6, 4, 2, 10, 6, 8,
4, 2, 4, 2, 4, 14, 10, 2, 10, 2, 12, 4, 2, 4, 6, 2,
10, 2, 4, 2, 12, 10, 2, 4, 2, 4, 6, 2, 6, 4, 6, 6,
6, 2, 6, 4, 2, 6, 4, 6, 8, 4, 6, 6, 8, 6, 10, 2,
4, 6, 2, 6, 6, 4, 2, 4, 6, 8, 4, 2, 6, 10, 2, 10,
2, 4, 2, 10, 8, 4, 2, 4, 12, 2, 6, 4, 2, 6, 4, 6,
14, 4, 2, 4, 8, 10, 6, 2, 4, 6, 2, 6, 10, 2, 4, 8,
6, 4, 2, 4, 2, 10, 2, 10, 2, 4, 6, 6, 2, 6, 6, 10,
6, 2, 6, 4, 8, 4, 6, 8, 4, 2, 6, 4, 8, 6, 4, 6,
2, 4, 6, 8, 6, 4, 2, 10, 2, 6, 4, 2, 4, 2, 10, 2,
10, 2, 4, 6, 8, 6, 4, 2, 4, 6, 6, 2, 6, 12, 4, 6,
12, 2, 4, 2, 4, 8, 6, 4, 6, 6, 8, 6, 6, 4, 2, 4,
6, 2, 6, 4, 2, 4, 2, 10, 2, 10, 2, 6, 4, 6, 2, 6,
4, 6, 6, 6, 8, 4, 2, 6, 10, 8, 4, 2, 4, 2, 4, 18,
6, 2, 4, 8, 6, 6, 4, 2, 10, 2, 6, 4, 6, 12, 2, 10,
2, 4, 2, 4, 6, 2, 6, 6, 4, 6, 6, 2, 12, 6, 4, 6,
8, 4, 2, 4, 2, 4, 8, 6, 4, 8, 4, 6, 2, 6, 6, 4,
2, 4, 6, 8, 4, 2, 6, 10, 2, 10, 6, 2, 4, 6, 2, 10,
2, 4, 6, 8, 6, 4, 2, 6, 4, 6, 8, 4, 6, 2, 4, 8,
6, 4, 6, 2, 10, 2, 6, 6, 4, 6, 6, 2, 6, 6, 4, 2,
10, 2, 12, 4, 2, 4, 6, 2, 10, 2, 10, 6, 2, 6, 4, 2,
6, 4, 14, 4, 2, 4, 2, 12, 6, 4, 6, 2, 4, 6, 2, 12,
6, 4, 8, 6, 4, 6, 2, 10, 2, 10, 6, 2, 4, 6, 2, 6,
4, 2, 4, 6, 6, 8, 4, 2, 10, 6, 8, 6, 4, 2, 12, 6,
4, 6, 6, 6, 2, 6, 6, 6, 4, 6, 2, 6, 6, 4, 2, 10,
12, 2, 4, 2, 10, 2, 6, 4, 2, 4, 6, 8, 10, 2, 6, 4,
14, 4, 2, 4, 2, 4, 8, 6, 4, 8, 4, 6, 2, 6, 10, 2,
4, 6, 2, 6, 4, 2, 4, 12, 2, 12, 4, 2, 4, 6, 8, 4,
2, 4, 6, 6, 2, 6, 4, 2, 6, 10, 8, 4, 2, 4, 6, 14,
4, 6, 2, 10, 2, 6, 6, 4, 2, 4, 6, 2, 10, 2, 4, 2,
12, 10, 2, 4, 2, 4, 8, 6, 4, 6, 6, 6, 2, 6, 4, 2,
6, 4, 6, 8, 4, 2, 10, 8, 6, 10, 2, 4, 6, 2, 6, 6,
4, 2, 4, 6, 2, 6, 4, 2, 6, 10, 12, 2, 4, 2, 4, 6,
8, 4, 2, 4, 12, 2, 6, 4, 2, 10, 6, 12, 2, 4, 2, 4,
8, 6, 10, 2, 4, 6, 2, 16, 2, 4, 6, 2, 6, 4, 2, 4,
2, 12, 10, 2, 4, 6, 6, 2, 6, 6, 4, 6, 6, 2, 6, 4,
2, 6, 4, 6, 8, 4, 8, 4, 8, 6, 4, 6, 2, 4, 6, 8,
6, 4, 2, 10, 8, 4, 2, 4, 2, 10, 2, 10, 2, 4, 2, 12,
6, 4, 2, 4, 6, 6, 2, 6, 4, 8, 4, 6, 8, 6, 4, 2,
4, 8, 10, 6, 6, 6, 2, 6, 6, 4, 2, 4, 8, 6, 4, 2,
4, 2, 10, 2, 10, 2, 6, 4, 6, 2, 6, 4, 2, 10, 6, 8,
4, 8, 10, 8, 4, 2, 4, 2, 4, 8, 10, 6, 2, 4, 14, 6,
4, 2, 4, 6, 2, 6, 4, 6, 2, 10, 2, 10, 2, 4, 6, 6,
2, 6, 4, 2, 4, 6, 6, 2, 6, 6, 6, 4, 6, 12, 2, 4,
2, 4, 8, 6, 4, 8, 4, 8, 6, 6, 4, 2, 4, 6, 8, 4,
2, 4, 2, 10, 2, 10, 2, 6, 4, 6, 2, 10, 6, 6, 8, 6,
4, 2, 6, 4, 6, 8, 4, 6, 2, 4, 14, 4, 6, 2, 4, 6,
2, 6, 6, 4, 12, 2, 6, 6, 4, 12, 2, 10, 2, 4, 2, 4,
6, 2, 6, 6, 10, 6, 2, 10, 2, 6, 4, 6, 8, 4, 2, 4,
2, 12, 6, 4, 6, 2, 4, 6, 2, 12, 4, 2, 4, 8, 6, 4,
2, 6, 10, 2, 10, 6, 2, 4, 6, 2, 6, 4, 2, 4, 6, 6,
2, 6, 4, 2, 10, 6, 8, 6, 4, 2, 4, 8, 6, 4, 6, 2,
10, 2, 6, 6, 10, 6, 2, 6, 4, 2, 4, 2, 10, 14, 4, 2,
10, 2, 10, 2, 4, 6, 6, 2, 10, 2, 6, 4, 14, 4, 2, 4,
2, 4, 8, 6, 4, 6, 2, 4, 6, 2, 6, 6, 6, 4, 6, 2,
6, 4, 6, 12, 2, 12, 4, 2, 4, 6, 2, 6, 4, 2, 4, 6,
6, 8, 4, 2, 6, 4, 6, 8, 4, 2, 4, 2, 18, 4, 6, 12,
2, 6, 6, 4, 2, 4, 6, 2, 12, 4, 2, 12, 10, 2, 4, 2,
4, 6, 2, 6, 4, 6, 6, 8, 6, 4, 2, 6, 4, 6, 8, 4,
2, 4, 6, 8, 6, 12, 4, 6, 2, 6, 10, 2, 4, 6, 2, 6,
4, 2, 6, 10, 2, 10, 2, 4, 2, 4, 6, 8, 4, 2, 4, 12,
2, 6, 4, 2, 6, 10, 12, 2, 4, 6, 8, 6, 4, 6, 2, 4,
6, 2, 6, 10, 2, 4, 6, 2, 10, 2, 4, 2, 10, 2, 10, 2,
4, 6, 8, 6, 6, 4, 6, 6, 2, 6, 4, 2, 6, 4, 6, 8,
4, 2, 6, 4, 8, 6, 4, 6, 2, 4, 6, 8, 6, 4, 2, 10,
2, 6, 4, 2, 4, 2, 10, 12, 2, 4, 2, 4, 8, 6, 4, 2,
4, 12, 2, 6, 4, 12, 6, 8, 4, 2, 4, 2, 4, 8, 6, 10,
6, 6, 2, 12, 4, 2, 4, 6, 2, 6, 4, 2, 4, 2, 12, 10,
2, 6, 4, 6, 2, 6, 4, 2, 4, 6, 6, 8, 4, 2, 6, 10,
8, 4, 6, 2, 4, 8, 10, 6, 2, 4, 8, 6, 6, 4, 2, 4,
6, 8, 4, 6, 2, 10, 2, 10, 2, 4, 2, 10, 2, 6, 4, 2,
4, 6, 6, 2, 6, 6, 6, 4, 6, 8, 6, 4, 2, 4, 8, 10,
8, 4, 6, 2, 6, 6, 4, 2, 4, 14, 4, 2, 4, 2, 10, 2,
10, 2, 4, 2, 4, 6, 2, 10, 2, 10, 8, 6, 4, 8, 4, 6,
8, 4, 6, 2, 4, 8, 6, 4, 6, 2, 4, 6, 8, 6, 4, 6,
6, 2, 6, 6, 4, 2, 10, 2, 10, 2, 4, 6, 6, 2, 6, 4,
2, 10, 6, 2, 6, 6, 6, 4, 6, 12, 2, 4, 2, 12, 6, 4,
6, 2, 4, 8, 12, 4, 2, 4, 8, 6, 4, 2, 4, 2, 10, 2,
10, 8, 4, 6, 2, 6, 4, 6, 6, 6, 2, 6, 4, 2, 10, 6,
8, 6, 4, 2, 4, 14, 4, 6, 2, 4, 6, 2, 6, 6, 6, 10,
2, 6, 4, 2, 4, 12, 12, 2, 4, 2, 10, 2, 6, 6, 4, 6,
6, 2, 10, 2, 6, 4, 14, 4, 2, 4, 2, 4, 8, 6, 4, 6,
2, 4, 6, 2, 6, 6, 4, 2, 4, 6, 2, 6, 4, 2, 16, 2,
0};
S_16 *qp;
S_32 NeedInfo, params, fpb, i, j, L, huminp, mastnum,
*lim, affnum = 1, affcou, status=0, WhichWay[2],
WorkSize, prmbar;
U_32 *ip, *is, *it;
size_t fetched, big;
struct affect *afflist = NULL, *affitem, *affprev, **affwalk;
struct scratchpad *LoadMaster, *ModCounter, *TypeSetter,
*RandomSkip, *WorkRegion;
// Note: size_t is a typedef like U_32, and happens to be the
// same as U_32.
//////////////////////////////////////////////////////////////////////
// BEGIN: WinMain is the primary/required starting point of any
// Windows program
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR szCmdLine, int iCmdShow)
{ static char szAppName[] = "Cryption";
MSG msg;
WNDCLASS wndclass;
previnst = hPrevInstance; // Save to global to eliminate compiler
// warning
ucMemBlk1 = malloc(1100000); // Buffer for file input, modification,
// and output
ucMemBlk2 = malloc(525000); // main work area, 65536 scratchpads,
// 8 bytes each
ucMemBlk3 = malloc(600000); // For the Index to the compressed-
// primes data file
ucMemBlk4 = malloc(4200000); // For sorting up to 65536 4-byte
// numbers, quickly
if((ucMemBlk1 == NULL) || (ucMemBlk2 == NULL) ||
(ucMemBlk3 == NULL) || (ucMemBlk4 == NULL))
{ if(ucMemBlk1)
free(ucMemBlk1);
if(ucMemBlk2)
free(ucMemBlk2);
if(ucMemBlk3)
free(ucMemBlk2);
if(ucMemBlk4)
free(ucMemBlk2);
MessageBox(NULL, "NOT ENOUGH MEMORY!", "ERROR",
MB_OK | MB_ICONSTOP);
exit(CRP_ERR_OM); // exit code 1: Out of Memory
}
// Next critical step: ensure data file and index are available
fetched = GetLogicalDrives();// Bitmap of available drives
// 1=A, 2=B, 4=C, 8=D, etc, 0=fail
ch = 'C'; // here and below (i=4) skip Drive A and Drive B
for(big=4; big; big<<=1) // Loop quits when bit shifted off end
// and i becomes Zero
{ if((fetched & big) == big)
{ sprintf(tmpstr, "%c:\\PRIMES\\COMPRESS.PRM", ch);
prmfil = fopen(tmpstr, "rb");
if(prmfil != NULL)
{ fpb = 0; // initialize file-position-byte
sprintf(tmpstr, "%c:\\PRIMES\\CMPRMDEX.QNT", ch);
dskfil = fopen(tmpstr, "rb");
break; // Quit for() loop regardless of existence of file,
// because have other file
} } // Otherwise continue loop; assume compressed-prime file
// does not exist here; try next drive
ch++; // 'D, 'E', etc
}
if((prmfil == NULL) || (dskfil == NULL))
{ MessageBox(NULL, "Files COMPRESS.PRM and CMPRMDEX.QNT are not\n"
"both available in a \\PRIMES directory.",
"ERROR", MB_OK | MB_ICONSTOP);
exit(CRP_ERR_OF); // using exit code 2: Opening File
}
fread(ucMemBlk1, 2, 143023, dskfil); // Load entire primes-data-
// Index file
fclose(dskfil); // close the file
lim = (S_32 *)(ucMemBlk1 + 286046);
ip = (U_32 *)ucMemBlk3; // initialize Index pointer
i = 0;
for(qp=(S_16 *)ucMemBlk1; qp<(S_16 *)lim; )
{ i += *qp++; // accumulate number of primes in this indexed block
*ip++ = i; // save the accumulations for later binary search
}
/*It might be noted that the preceding calculations using the Index
data file (and future calcs using the compressed Primes data file)
are not subject to the Endian controversy. This is because it is
expected that the algorithms in PRMPRESS.C would have been compiled
and executed to generate the files on the same computers that are
going to use those files. A problem is likely if, say, a Little
Endian machine was used to generate the files, and a Big Endian
machine was given a COPY of those files. But that's not likely,
mostly because you now know about it and can avoid it.
*/
// Next up: Set four special scratchpads used to Control
LoadMaster = (struct scratchpad *)(&ucMemBlk2[0]); // the
ModCounter = (struct scratchpad *)(&ucMemBlk2[8]); // CRYPTION
TypeSetter = (struct scratchpad *)(&ucMemBlk2[16]); // algorithm
RandomSkip = (struct scratchpad *)(&ucMemBlk2[24]);
WorkRegion = (struct scratchpad *)(&ucMemBlk2[32]); // Begin 64K
// scratchpads, array-accessed
// Those prior five lines are a way of telling the compiler that the
// simple unorgainzed block of memory allocated for the ucMemBlk2
// pointer (half-a-million bytes +) is going to be accessed as if it
// was a huge array of scratchpad structures (each of which occupies
// 8 bytes). The first four are special-purpose scratchpads that
// deserve their own names, which will control major aspects of the
// CRYPTION process, as described elsewhere in this file.
// "WorkRegion" is the name by which thousands of other scratchpads
// can be accessed (specific examples are WorkRegion[15] and
// WorkRegion[3749]) in that memory-block. One of the best features
// of the C programming language is something called "pointer
// arithmetic". Those references like "ucMemBlk2[24]" are
// specifying particular numbers of bytes from the start of the
// memory block, simply because ucMemBlk2 was declared as a "char"
// pointer, and "char" variables occupy one byte each. However,
// since WorkRegion (and the others) are pointers to 8-byte
// scratchpad structures, the compiler automatically knows to put
// WorkRegion[193] 8 bytes after WorkRegion[192]. Thus while
// WorkRegion[0] is located at ucMemBlk2[32], WorkRegion[1] is
// located at ucMemBlk2[40], and WorkRegion[2] is located at
// ucMemBlk2[48], and so on, for 65536 WorkRegion scratchpads, and
// for 524288 bytes of memory in ucMemBlk2.
tadone[0] = '\0';
NeedInfo = 63; // Binary 00111111 --flag-bits that will be cleared
// as info verified
keyfile[0] = '\0';
del1 = 'N'; // default Don't Delete the Key File
afflist = affitem = NULL;
affwalk = &afflist; // get address of linked-list first-item pointer
// (currently NULL)
affcou = 0; // initialize count of items on linked list
doit = ' ';
proceed = ' ';
szParams = szCmdLine; // prepare to process parameters
for(params=0; szParams[params]; params++) // Use this loop to find
// length of param-string,
szParams[params] = (char)toupper(szParams[params]); // ensuring
// uppercase
//NOTE: Parsing stops at any failure point, or at NULL-terminator.
// Insufficient acceptable parameters will result (later) in waiting
// for human input.
huminp = 1;// Assume missing or faulty params, so human input needed
if(params) // if any length of parameter-info
{ ch = szParams[0];// Windows ensures first character is not a space
if(ch == '"')// Validate required quotation mark for Key File Name
{ p = ++szParams;//Skip the open-quote and save start of File Name
q = strchr(p, '"'); // look for the required close-quote
if(q) // result of seeking must not be NULL
{ L = (int)(uli(q) - uli(p));//compute length of drive\path\name
if(L && (L < 49)) //Key File Name must exist and not
// be too long
{ strncpy(keyfile, p, L);// copy the stuff between quote marks
keyfile[L] = '\0'; // strncpy does not terminate string,
// so do it
szParams += L; // Skip filename (pointer now at close-quote
while(' ' == (ch = *(++szParams))); // 1-line loop: skip
// current character and spaces
if(ch == 'D') // check for optional 'D'
{ del1 = 'Y'; // Set flag to delete Key File after
// successful operation
while(' ' == (ch = *(++szParams))); // Same 1-line loop as
// above; skip spaces
} // If optional 'D' wasn't there, no problem continuing
do // BEGIN LOOP TO PARSE 4 PARAMS FOR EACH FILE TO AFFECT
{ if(isdigit(ch)) // A skip number is now required
{ huminp = 1; // More params to process, assume
// will need human input to fix
affprev = affitem; // Save current item as
// about-to-be-previous
*affwalk = malloc(sizeof(struct affect)); // reserve
// some memory (sets 2 pointers)
if(!*affwalk) // if failed
{ while(afflist) // while an item on this list exists
{ affitem = afflist; // save the current item
afflist = afflist->next; // Get at a possible next
// item (may be NULL)
free(affitem); // delete current item
}
MessageBox(NULL, "NOT ENOUGH MEMORY!", "ERROR",
MB_OK | MB_ICONSTOP);
free(ucMemBlk1);
free(ucMemBlk2);
free(ucMemBlk3);
free(ucMemBlk4);
exit(CRP_ERR_OM); // exit code 1: Out of Memory
}// Program-flow passes this point only after successful
// memory allocation
affcou++; // count number of items on this linked list
affitem = *affwalk;// get easy pointer to this list item
affitem->prev = affprev;//set link to prior item on list
afflist->prev = affitem; // Set FIRST-item link to last
// (current) item on list
affprev = affitem->prev;// Refetch pointer, but at FIRST
// sets to NON-NULL
affitem->next = NULL; // Ensure new structure doesn't
// point anywhere
affwalk = &(affitem->next); // Get address of linklist
// pointer for next loop
affitem->dotloc = -1; // Initialize in case no file
// extension
affitem->kill = 'E'; // Initialize to Error in case loop
// quits early
i = 0; // initialize a number
do // can do this at least once since ch is a digit
{ i = i * 10 + (ch - '0'); // Accumulate Skip Number;
// convert digit's CHARACTER-CODE
} while(isdigit(ch = *(++szParams)) && (i <= 999999));
//NOTE if i==999999,
// then next loop makes i nearly ten million
affitem->skipno = i; // save the skip number
if((ch == ' ') || isdigit(ch)) // Did digit loop end at
// a space or digit?
while((' ' == (ch = *(++szParams))) || isdigit(ch));
// skip spaces or excess digits
if((ch != 'D') && (ch != 'E')) // Did that loop end at
// required algorithm code?
break; // end affwalk loop if not
affitem->algo = ch; // save the algorithm code
while(' ' == (ch = *(++szParams)));//Skip any/all spaces
// that follow it
if(ch != '"') // Should now be at required quoted
// file name
break; // end affwalk loop if not
p = ++szParams; // Skip the open-quote and
// save start of File Name
q = strchr(p, '"'); // look for the required close-quote
if(q == NULL) // if wasn't there
break; // end affwalk loop
L = (int)(uli(q) - uli(p)); // Compute length of
// drive\path\name
if((L < 1) || (L > 48)) // Key File Name must exist and
// not be too long
break; // end affwalk loop
q = affitem->pathname; // Get easy pointer to the copied
// pathfile string
strncpy(q, p, L); // Copy the stuff between quote marks
q[L] = '\0'; // terminate the string
for(i=L-1; i>=0; i--) // Walk backward through the
// filename
{ ch = q[i];
if((ch == '\\') || (ch == ':'))
break; // found marker before filename
// quit looping regardless of finding extension
if((ch == '.') && (affitem->dotloc == -1))
{ affitem->dotloc = (S_16)i; // Save location of
// first-found extension-marker
break; // no need to continue for() loop
} }
if(((i == -1) && (L > 44)) || // MUST HAVE ROOM IN FILE
// NAME STRING TO EITHER
((48 - i) < 4)) // REPLACE EXTENSION WITH,
// OR ADD, .CRP
break; // end affwalk loop
szParams += L; // Skip filename (pointer now at
// close-quote
while(' ' == (ch = *(++szParams)));//Skip any/all spaces
// that follow it
if(!ch || (ch == ' ') || isdigit(ch))// String ended, or
// have space or digit?
affitem->kill = 'N'; // Default to NO KILL;
// valid option is missing
else if(ch != 'D')//anything else MUST be a D for delete
break; // keep the Error-marker and quit affwalk loop
else
affitem->kill = 'Y'; // have a D, so set YES KILL
if(ch && !isdigit(ch)) // if D or space
while(' ' == (ch = *(++szParams))); // Skip it and
// any/all following spaces
huminp = 0; // Params accepted -- no human
// input needed to fix
} // note huminp reset to 1 if another file-to-affect
else
break; // nondigit where a digit was required
} while(*szParams); // need at least one to-affect file
} } } } // continue parsing the parameter-string
affitem = affprev = afflist; // Set pointers to start of linked list
// (if exists)
affwalk = &afflist; // and this one, too
if(!huminp) // so far no errors requiring human input?
doit = 'A'; // prepare for automatic-acceptance processing
// DONE PROCESSING PARAMETERS; TIME TO DO ORDINARY WINDOWS STUFF
// Prepare a couple of fixed fonts,
// only differece is that one is underlined
hfont1 = CreateFont(0, 7, 0, 0, 0, 0, 0, 0, ANSI_CHARSET,
OUT_DEFAULT_PRECIS, CLIP_CHARACTER_PRECIS,
DEFAULT_QUALITY, (FIXED_PITCH | FF_MODERN),
"SimpleFixed"); // NO UNDERLINE
hfont2 = CreateFont(0, 7, 0, 0, 0, 0, 1, 0, ANSI_CHARSET,
OUT_DEFAULT_PRECIS, CLIP_CHARACTER_PRECIS,
DEFAULT_QUALITY, (FIXED_PITCH | FF_MODERN),
"SimpleFixUN"); // UNDERLINED
/* (Copied from Language Reference)
CreateFont
(int nHeight, // logical height of font (0=default)
int nWidth, // logical average character width
// (0=default, using 7)
int nEscapement, // angle of escapement (0=default)
int nOrientation, // base-line orientation angle(0=default)
int fnWeight, // font weight (0=default)
DWORD fdwItalic, // italic attribute flag (0=False)
DWORD fdwUnderline, // underline attribute flag (0=False)
DWORD fdwStrikeOut, // strikeout attribute flag (0=False)
DWORD fdwCharSet, // character set identifier, using
// ANSI_CHARSET
DWORD fdwOutputPrecision,// output precision, using default
DWORD fdwClipPrecision, // clipping precision, using SOME
DWORD fdwQuality, // output quality, using default
DWORD fdwPitchAndFamily, // pitch and family,
// using Fixed and no 'serif"
LPCTSTR lpszFace); // address of typeface name string
*/
// FINALLY READY TO CREATE A WINDOW FOR THIS PROGRAM
//wndclass.Size = sizeof(wndclass);
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH) GetStockObject(LTGRAY_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
//wndclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);// NOT USED
RegisterClass(&wndclass);
hwnd = CreateWindow(szAppName, "PRIMARY CRYPTION",
WS_CAPTION | WS_VISIBLE,
5, 5,
575, 280,
NULL, NULL, hInstance, NULL);
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd);
// Main Windows Program Loop; ends when the Quit message returns zero
for(;;)
{ if(GetMessage(&msg, NULL, 0, 0))
{ TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
break; // GetMessage pulled a WM_QUIT and returned zero; exit!
}
// Windows programming is like a dance; the Operating System is
// willing to spend time executing your program-code as long as you
// let it take the "lead". WINDOWS interacts with the user, and
// Windows will pass messages to your program, telling it what the
// user wants. This tends to make your program a collection of
// disconnected blocks of code, each block waiting for the user to
// tell Windows to pass a message to it, to do its thing. On the
// other hand, as long as Windows knows that your program is doing a
// GetMessage()/TranslateMessage()/DispatchMessage() loop, it doesn't
// matter WHERE your program does it. (See the ProcessMessages()
// subroutine later on in the file.) This means your program can do
// stuff without always waiting for Windows to give the go-ahead, and
// in fact the original author of the CRYPTION program once used such
// a trick to make a large, developed-for-a-decade, multi-megabyte
// pure-DOS program (the kind that wants total control of all
// operations of the computer) co-exist with the Windows paradigm, AS
// IF it had become an ordinary Windows program. (Well, there was
// more to it than just that trick, but 90%+ of the original code did
// not need to be modified.)
// BEGIN ORGANIZED EXIT PROCESS
// CLEAN UP ALL ALLOCATED MEMORY
free(ucMemBlk1); // file I/O buffer
free(ucMemBlk2); // workspace
free(ucMemBlk3); // prime-search/index table
free(ucMemBlk4); // sorting are when preparing Index
// To clean up the linked list, we use the starting "anchor" and
while(afflist) // walk the list: while an item on this list exists
{ affitem = afflist; // save the current item
afflist = afflist->next; // get a possible next item (may be NULL)
free(affitem); // delete current item
}
if(prmfil != NULL)
fclose(prmfil); // close the file
system("DEL .\\TMP*.CRP"); // Clean up after any
// abort-while-affecting-files
return(msg.wParam); // CRYPTION.EXE program terminates here
}; // PERSONAL PREFERENCE QUIRK of original author: The return
// statement in C is not a function, and so the value returned,
// msg.wParam here, does not need to be surrounded by parentheses.
// However, the return statement is LIKE a function-call -- such
// as system() or free() above, where parentheses are mandatory --
// in the sense that return can transmit a value elsewhere.
// Thus, mostly for the sake of "aesthetic consistency" does the
// original author prefer to associate parentheses with any return
// statement that transmits a value. The compiler does not care,
// and in some cases, such as when a complicated expression must be
// evaluated to produce the return-value, parentheses often make
// sense for that reason alone, to clarify something to the
// compiler (or another human). So using parentheses generally for
// aesthetics means being consistent with the special case, also.
//////////////////////////////////////////////////////////////////////
// Critical Window Procedure: Windows passes User-Activity messages to
// it, for processing
LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam,
LPARAM lParam)
{ // A function that uses a lot of local variables is often a function
// that is taking a performance hit. The computer has a special
// place in its memory called the "stack" to which local variables
// are typically added and removed as needed -- which is EVERY time
// a function begins or ends. Adding or removing a lot of local
// variables, when a function is called many many times, can be
// called a "stack-thrashing" time-wasting event. What is an
// alternative, when global variables are frowned-upon these days?
// Well, a special type of local variable is called "static", which
// is stored outside the stack, in a kind-of-global way, and
// persists for as long as the overall program runs -- but it is
// still accessed only by the function that declares it. Static
// variables are useful for remembering data in-between calls to the
// function. While some of that remembrance is needed here, in just
// a few variables, what is needed more is the time savings that
// comes with zero stack-thrashing. So here all the local variables
// are declared as static.
static HDC hdc;
static PAINTSTRUCT ps;
static COLORREF oldcolr;
static struct affect *afftmp;
static S_32 painting = 0, gathlen = 0, skip = -1, gotdat = 0;
static S_32 dot, repaint, juset = 0, working = 0, ret;
static char algo = ' ', del2 = 'N', gather[50] = {' ','\0'},
dofile[50] = {'\0'}, sd1 = ' ',
savskp[50] = {'\0'}, savalg = ' ',
savdo[50] = {'\0'},
sd2 = ' ', d1 = ' ', alg = ' ', d2 = ' ';
// The 'working' variable is set when all data entry has been
// accepted and the program is beginning to do its CRYPTIONing. It
// is used to ignore all code associated with the data-entry
// process, and to limit screen-displays to such things as a
// progress bar.
if(!working && (affitem == NULL))
{ *affwalk = malloc(sizeof(struct affect)); // Reserve some memory
// (sets 2 pointers)
affitem = *affwalk; // set third pointer
if(!affitem) // if failed to allocate new linked-list item
{ MessageBox(NULL, "NOT ENOUGH MEMORY!", "ERROR",
MB_OK | MB_ICONSTOP);
PostQuitMessage(CRP_ERR_OM); // exit code 1: Out of Memory
return(0); // exit program in a way that cleans up memory
} // Below, set ->prev links to go backwards from latest item to
// first -- which points to latest
affitem->prev = affprev; // Link to prior item (initially sets
// FIRST list item to NULL)
afflist->prev = affitem; // Set FIRST item prior-link to last
// (current) list item
affprev = affitem->prev; // Refetch pointer, except at FIRST,
// where sets to NON-NULL
affitem->next = NULL;
affwalk = &(affitem->next);//Get address in case another list-item
// needs creating
affitem->skipno = -1; // Now initialize the ordinary variables
// in the structure
affitem->algo = ' ';
affitem->pathname[0] = '\0';
affitem->dotloc = -1;
affitem->length = 0;
affitem->kill = ' ';
affcou++; // increment total count of these linked-list items
} // Later on, the Up-Arrow and Enter keys let the user to
// sort-of-scroll through many data items, and this next code
// fetches a small group that have been saved on the linked list.
if(!working && (gotdat == 0)) // Pull file-to-affect data saved in
// current linked-list item?
{ skip = affitem->skipno;
algo = affitem->algo;
strcpy(dofile, affitem->pathname);
sd2 = del2 = affitem->kill;
gather[0] = d2 = alg = savalg = ' '; // erase other remembrance
// variables
savskp[0] = savdo[0] = gather[1] = '\0';
gathlen = 0;
gotdat = 1; // no need to repeat these assignments for a while
if(huminp) // force stoppages if human input
{ if(skip > -1)
{ sprintf(gather, "%d", skip);
gathlen = strlen(gather);
strcat(gather, " ");
skip = -1;
}
strcpy(savskp, gather);
savalg = algo;
algo = ' ';
strcpy(gather, dofile);
gathlen = strlen(gather);
strcat(gather, " ");
dofile[0] = '\0';
strcpy(savdo, gather);
del2 = ' ';
} }
GetClientRect(hwnd, &rect); // Get rectangular screen region upon
// which text will be displayed
switch(iMsg)
{/* case WM_CREATE: // All initializations already done;
return(0); // ignore WM_CREATE messages
*/
////
case WM_KEYUP: // Using this Message only to let us handle the
// up-arrow key
case WM_CHAR:
GetKeyNameText(lParam, keynam, 32);
if(!strcmp(keynam, "Esc")) // quit
{ PostQuitMessage(0);
return(0);
} // Next line: Since every keystroke generates both WM_CHAR and
// WM_KEYUP messages...
if(strcmp(keynam, "Up") && (iMsg == WM_KEYUP))
return(0); // Need this to prevent duplicate-display of
// non-up-arrow keys!
ch = (char)toupper((int)wParam);
huminp = 1; // For whatever reason, a keystroke means human
// input has occurred
if(!working && !strcmp(keynam, "Up"))
{ for(i=1; i<64; i<<=1) // Prepare to walk through six lowest
// bits of NeedInfo variable
if(!(NeedInfo & i)) // Each accepted data item is a zero-bit
// in NeedInfo
{ NeedInfo |= i; // Setting that bit forces the item to be
// human-handled
break; // If any bit was set, must be last-accepted
// data item
}
juset = 1; // Have just set some data (flag controls coloring
// of displayed text)
if((i == 16) && (affprev != afflist->prev)) // DO WE NEED TO
// CYCLE TO A PRIOR ITEM?
{ NeedInfo &= 239; // clear that just-set bit-value of 16
affitem = affprev; // point at prior item on linked list
affprev = affitem->prev;
affnum--; // indicate prior list-element number
gotdat = 0; // set flag to gather up that data
SendMessage(hwnd, WM_CHAR, 0x0000000D, 0x001C0001);
// above, send an ENTER (go to algorithm)
SendMessage(hwnd, WM_CHAR, 0x0000000D, 0x001C0001);
// above, send another (go to affect-file)
SendMessage(hwnd, WM_CHAR, 0x0000000D, 0x001C0001);
// above, send another (highlights DeleteFile)
// SendMessage(hwnd, WM_CHAR, 0x00000026, 0xC1480001);
// above, codes for UP-ARROW, if needed
return(0); // exit current call to WndProc()
}
switch(i)
{ case 32:
strcpy(gather, keyfile); // Remember stuff when moving
// up to prior editable item
gathlen = strlen(gather);
strcat(gather, " ");
keyfile[0] = '\0';
break;
case 16:
sd1 = del1;
del1 = ' ';
strcpy(savskp, gather);
break;
case 8:
sprintf(gather, "%d", skip);
gathlen = strlen(gather);
strcat(gather, " ");
skip = -1;
break;
case 4:
savalg = algo;
algo = ' ';
strcpy(savdo, gather);
break;
case 2:
strcpy(gather, dofile);
gathlen = strlen(gather);
strcat(gather, " ");
dofile[0] = '\0';
break;
case 1:
sd2 = del2;
del2 = ' ';
}
}
if(!strcmp(keynam, "Space"))
L = 1;
else
L = strlen(keynam);
if(((L == 1) || !strstr("Enter Up", keynam)) &&
((NeedInfo & 32) || ((NeedInfo & 8) &&
!(NeedInfo & 16))
// 32: bitflag for key file
|| ((NeedInfo & 2) &&
!(NeedInfo & 28))))
// 8: bitflag for skip number
{ if(!strcmp(keynam, "Backspace") ||//2: bitflag, file to affect
((L == 1) && (strchr("1234567890", ch) ||
(((NeedInfo & 32) || !(NeedInfo & 8)) &&
strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ ;:\'`~!@"
"#$%^&()_+-=.,[]{}_\\", ch)))))
{ if(gathlen && !strcmp(keynam, "Backspace"))
{ gather[gathlen--] = '\0';
gather[gathlen] = ' ';
}
else if((L == 1) && ((gathlen < 7) ||
((NeedInfo & 34) && (gathlen < 48))))
{ gather[gathlen++] = ch;
gather[gathlen++] = ' ';
gather[gathlen--] = '\0'; // Terminate string after space,
// backup to replace space later
} } }
else if((L == 1) && ((NeedInfo & 16) ||
((NeedInfo & 1) && !(NeedInfo & 14))))
// 16: bitflag: delete key file
{ if((ch == 'Y') || (ch == 'N')) // 1: bitflag for deletion of
// affected file
if(NeedInfo & 16)
d1 = ch;
else
d2 = ch;
}
else if((NeedInfo & 4) && (L == 1)) // 4: bitflag for
// De/En-cryption
{ if((ch == 'D') || (ch == 'E'))
alg = ch;
}
else if(!working)
{ if(!NeedInfo) // this is for the Accept/More question
{ if((doit == ' ') && (L == 1) &&
((ch == 'M') || ((ch == 'A') && // Accept or More
(affitem->next == NULL))))
doit = ch; // "More" will mean Continue to gather info on
// another file-to-affect
}
else if(!strcmp("Enter", keynam)) // Everything else, except
// ENTER and UP
{ if(NeedInfo & 32)
{ strncpy(keyfile, gather, gathlen);
keyfile[gathlen] = '\0';
}
else if((NeedInfo & 2) && !(NeedInfo & 60))
{ strncpy(dofile, gather, gathlen);
dofile[gathlen] = '\0';
} }
else if(strcmp("Up", keynam)) // everything else, except UP
break; // let default processing handle all other keystrokes
}
else
break; // if CRYPTION is "working" then also ignore keystrokes
InvalidateRect(hwnd, &rect, 0); // Ensure when have a key, the
// window is updated
// return(0); // Every WndProc() "case" that is processed is
// expected to return(0). HOWEVER, this program gets better
// results for Up-Arrow handling by letting each keystroke
// directly flow to the WM_Paint section
////
case WM_PAINT:
if(painting)
return(0);
painting = 1; // multiple calls here will be rejected
repaint = 0; // this will be set only if we WANT to repaint
hdc = BeginPaint (hwnd, &ps);
SetBkColor(hdc, 0x00C0C0C0); // reasonably light gray
oldcolr = SetTextColor(hdc, 0x00000000); // black; DOING ONLY TO
// SET oldcolr
hPriorF = SelectObject(hdc, hfont1); // Fixed font, not
// underlined
SetTextColor(hdc, 0x00008000); // moderately dark green
TextOut(hdc, // device context
1, 2, // LOGICAL (not pixel) X and Y coordinates
"THANK YOU, for choosing this data protection tool.",
50); // length of above string-to-display
SetTextColor(hdc, oldcolr); // black
/*
NOTE: Because this CRYPTION program is a "lower level" program, it
does not have fancy Windows controls like edit boxes, nor does it call
the Windows Choose File dialog. Here the user is expected to know the
standard DOS/Windows syntax for a file name, which is: Drive letter
followed by a colon : followed by a backslash \ followed by a
directory name OR by a file name. If a directory name follows the
backslash, then another backslash must follow the directory name.
THAT backslash is followed by either another directory name (name of a
subdirectory, actually) or a file name. Eventually, possibly after
several sub-subdirectory names and backslashes, the file name is
finally included. Note that the filename itself is frequently in two
parts, with a period . separating the two parts. The first part is
usually called the file name and the second part is called the
extension, but the combination of name and period and extension is
also often called the file name. Fancy stuff like Windows dialogs
belongs to fancier programs, such as the future Controller Program
that will keep track of encryption keys and call this one with a
bunch of parameters. */
SelectObject(hdc, hfont1);
if(!*keyfile)
SetTextColor(hdc, 0x0000FFFF); // yellow
TextOut(hdc, 1, 30, "Drv:\\Pth\\KeyFile (any 8K+)?", 27);
SetTextColor(hdc, oldcolr); // Reset regardless of whether
// was changed
TextOut(hdc, 205, 30, eraser, 49); // Erase and reprint below;
// handles backspaces
SelectObject(hdc, hfont2);
if(NeedInfo & 32)
{ if(*keyfile)
{ strcpy(gather, keyfile);
gathlen = strlen(gather);
strcat(gather, " ");
}
else
del1 = ' ';
TextOut(hdc, 205, 30, gather, strlen(gather));
}
else if(*keyfile)
TextOut(hdc, 205, 30, keyfile, strlen(keyfile));
if(NeedInfo & 32) // begin gathering wanted data
{ if((!strcmp(keynam, "Enter")) || *keyfile)
{ strncpy(keyfile, gather, gathlen);
keyfile[gathlen] = '\0';
dskfil = fopen(keyfile, "rb");
ucp1 = &ucMemBlk1[15000]; // Skip space big enough for any
// .PRM file
if(dskfil && // if to-be-affected file exists
(8300 <= (fetched = fread(ucp1, 1, 1080000, dskfil))) &&
// and is big enough
(0 == keycheck())) // and has some moderately random data
{ fclose(dskfil); // (to be reopened if actually PROCEED)
NeedInfo &= 223; // Clear Bit Five (6th from right),
// value 32
gather[0] = ' '; // Prepare these variables for next data
// to be gathered
gather[1] = '\0';
gathlen = 0;
juset = 1;
}
else
{ if(dskfil)
fclose(dskfil);
sprintf(tmpstr, "This key file either cannot be found,\n"
"or is smaller than 8300 bytes, or\n"
"hasn't enough random data in it:\n%s",
keyfile);
MessageBox(NULL, tmpstr, "ERROR",
MB_OK | MB_ICONEXCLAMATION);
keyfile[0] = '\0';
doit = ' ';
huminp = 1;
repaint = 1;
}
keynam[0] = '\0';
} }
if(!(NeedInfo & 32)) // Only do next data entry after prior line
// is accepted
{ SelectObject(hdc, hfont1);
if(NeedInfo & 16)// Only want yellow question text when asking
// this question
if((((del1 == ' ') && (d1 == ' ') && (sd1 == ' ')) ||
!strcmp(keynam, "Up")) ||
((huminp == 1) && (juset == 1)))
SetTextColor(hdc, 0x0000FFFF); // yellow
TextOut(hdc, 1, 46,
"Delete that file after successful processing (Y/N)?",
51);
SetTextColor(hdc, oldcolr); // Reset regardless of whether
// was changed
SelectObject(hdc, hfont2);
if(sd1 != ' ')
del1 = sd1;
if(d1 != ' ')
del1 = d1;
sprintf(tmpstr, "%c", del1);
TextOut(hdc, 373, 46, tmpstr, 1);
if(NeedInfo & 16) // ok to begin gathering data ?
{ if((del1 != ' ') && (((huminp == 0) || (juset == 0)) ||
!strcmp(keynam, "Enter")))
{ NeedInfo &= 239; // Clear Bit Four (5th from right),
// value 16
sd1 = ' ';
}
else
juset = 0;
d1 = ' ';
keynam[0] = '\0';
} }
if(NeedInfo & 32)
{ SelectObject(hdc, hfont1);
TextOut(hdc, 1, 46, eraser, 80); // Hide this line while prior
// line edited
}
if(!(NeedInfo & 48)) // Only display this data entry after prior
// lines are accepted
{ SelectObject(hdc, hfont1);
sprintf(tmpstr,
"======= INFO FOR FILE TO AFFECT: #%d OF %d =======",
affnum, affcou);
SetTextColor(hdc, 0x00008000); // moderately dark green
TextOut(hdc, 1, 78, tmpstr, strlen(tmpstr));
SetTextColor(hdc, oldcolr);
if(NeedInfo & 8) // Only want yellow question text when asking
// this question
if((skip == -1) &&
(strcmp(keynam, "Enter") || (gathlen == 0)))
SetTextColor(hdc, 0x0000FFFF); // yellow
TextOut(hdc, 1, 94, "Extra randomizer 0-9999999:", 27);
SetTextColor(hdc, oldcolr); // Reset regardless of whether
// was changed
TextOut(hdc, 205, 94, eraser, 80); // Erase & reprint; cleans
// up after Automatic displays
SelectObject(hdc, hfont2);
if(NeedInfo & 8)
{ if(skip > -1)
{ sprintf(gather, "%d", skip);
gathlen = strlen(gather);
strcat(gather, " ");
}
else
{ if(*savskp)
{ strcpy(gather, savskp);
gathlen = strlen(gather) - 1;
savskp[0] = '\0';
} }
TextOut(hdc, 205, 94, gather, strlen(gather));
}
else if(skip > -1)
{ sprintf(tmpstr, "%d", skip);
TextOut(hdc, 205, 94, tmpstr, strlen(tmpstr));
}
if(NeedInfo & 8) // ok to begin gathering data ?
{ i = strtol(gather, NULL, 10);// Get not-necessarily-finished
// numeric value
affitem->skipno = i; // save it on linked list
if((!strcmp(keynam, "Enter") && (gathlen > 0)) ||
(skip > -1))
{ skip = i;
NeedInfo &= 247; // Clear Bit Three (4th from right),
// value 8
gather[0] = ' '; // Prepare these variables for next data
// to be gathered
gather[1] = '\0';
gathlen = 0;
juset = 1;
keynam[0] = '\0';
} } }
if(NeedInfo & 16)
{ SelectObject(hdc, hfont1);
TextOut(hdc, 1, 78, eraser, 69); // Hide this line while prior
// line edited
TextOut(hdc, 1, 94, eraser, 80); // Hide this line while prior
// line edited
}
if(!(NeedInfo & 56)) // Only display this data entry after prior
// lines are accepted
{ SelectObject(hdc, hfont1);
if(NeedInfo & 4) // Only want yellow question text when asking
// this question
if((((algo == ' ') && (alg == ' ') && (savalg == ' ')) ||
!strcmp(keynam, "Up")) ||
((huminp == 1) && (juset == 1)))
SetTextColor(hdc, 0x0000FFFF); // yellow
TextOut(hdc, 1, 110,
"Use \"Encrypt\" or \"Decrypt\" algorithm (E/D)?",
43);
SetTextColor(hdc, oldcolr); // Reset regardless of whether
// was changed
SelectObject(hdc, hfont2); // same fixed font, underlined
if(savalg != ' ')
algo = savalg;
if(alg != ' ') //(alg == 'E') || (alg == 'D')
algo = alg;
sprintf(tmpstr, "%c", algo);
TextOut(hdc, 317, 110, tmpstr, 1);
if(NeedInfo & 4)
{ if((algo != ' ') && (((huminp == 0) || (juset == 0)) ||
!strcmp(keynam, "Enter")))
{ NeedInfo &= 251; // Clear Bit Two (3rd from right),
// value 4
savalg = ' ';
keynam[0] = '\0';
}
else
juset = 0;
alg = ' ';
keynam[0] = '\0';
affitem->algo = algo; // save on linked list
} }
if(NeedInfo & 8)
{ SelectObject(hdc, hfont1);
TextOut(hdc, 1, 110, eraser, 80);// Hide this line while prior
// line edited
}
if(!(NeedInfo & 60)) // Only display this data entry after prior
// lines are accepted
{ SelectObject(hdc, hfont1);
if(!*dofile)
SetTextColor(hdc, 0x0000FFFF); // yellow
TextOut(hdc, 1, 126, "Drive:\\Path\\File to affect?", 27);
SetTextColor(hdc, oldcolr); // Reset regardless of whether
// was changed
TextOut(hdc, 205, 126, eraser, 49); // Erase & reprint; cleans
// up after Automatic displays
SelectObject(hdc, hfont2);
if(*dofile)
{ strcpy(gather, dofile);
gathlen = strlen(gather);
strcat(gather, " ");
}
else
{ del2 = ' ';
if(*savdo)
{ strcpy(gather, savdo);
gathlen = strlen(gather) - 1;
savdo[0] = '\0';
} }
if(!working && gathlen)
{ strncpy(affitem->pathname, gather, gathlen); // Save even
// partial name on linked list
affitem->pathname[gathlen] = '\0'; // Terminate the copied
// string
}
TextOut(hdc, 205, 126, gather, strlen(gather));
if(NeedInfo & 2) // see if ok to begin gathering data
{ if((!strcmp(keynam, "Enter")) || *dofile)
{ strncpy(dofile, gather, gathlen);
dofile[gathlen] = '\0';
for(dot=-1, i=gathlen-1; i>=0; i--)//Walk backward through
// the filename
{ ch = dofile[i];
if((ch == '\\') || (ch == ':'))
break; // found marker before filename; quit looping
if((ch == '.') && (dot == -1))
{ dot = i; // Save location of first-found
// extension-marker
break; // no need to continue for() loop
} }
if(((i == -1) && (gathlen > 44)) || // MUST HAVE ROOM IN
// FILE NAME STRING TO EITHER
((48 - i) < 4)) // REPLACE EXTENSION WITH, OR ADD, .CRP
{ sprintf(tmpstr,
"This File-to-affect has too long a name:\n %s\n"
"There must be space to set the File Extension to .CRP\n"
"-- AND that modified name must be acceptably short,\n"
"or it can't be specified as a file to unscramble!\n"
"Try renaming file, or moving it to another directory.",
dofile);
MessageBox(NULL, tmpstr, "ERROR",
MB_OK | MB_ICONEXCLAMATION);
dofile[0] = '\0';
doit = ' ';
huminp = 1;
}
else // Below, using FindFirstFile() and not fopen()
// because want file length
{ j = 1; // initialize this as a failure flag
fil1 = FindFirstFile(dofile, (LPWIN32_FIND_DATA)&wfd);
// seek file-to-affect
i = GetLastError(); // double-check to be sure it exists
if((fil1 != INVALID_HANDLE_VALUE) &&
(i != ERROR_PATH_NOT_FOUND))
{ affitem->length = wfd.FileSzLo; // Save length of the
// file, 4GB max
j = 0;
} // Below, failed to open file, so
else if((dot > -1) && !strcmp(".CRP", &(dofile[dot])))
// if specified extension is .CRP
{ afftmp = afflist; // Prepare to walk list of
// files-to-affect
i = dot - 1;// Get length of drive\path\name up to the
// extension-marker
while(afftmp != affitem) // As long as current
// linked-list-item not reached
{ j = strncmp(dofile, afftmp->pathname, i); // Compare
// prior ACCEPTED file names
if(!j) // A matching file was specified previously
// and saved?
{ affitem->length = afftmp->length + 87; // Copy
// length of the file, 4GB max, because that one
break; // passed, and here-desired .CRP file will
}// be created from it (so end loop). Note original
// file's length + 87 ("Cryptionite" identifier) is
// roughly the number of scrambled bytes, for that
// currently-nonexistent .CRP file.
afftmp = afftmp->next; // Else see about next
// file-data saved on linked list
} }
FindClose(fil1); // Release memory associated with
// FindFirstFile()
if(!j) // if this file name is OK
{ NeedInfo &= 253; // Clear Bit One (2nd from right),
// value 2
gather[0] = ' '; // Prepare these variables for next
// data to be gathered
gather[1] = '\0';
gathlen = 0;
affitem->dotloc = (S_16)dot;
juset = 1;
}
else
{ sprintf(tmpstr,
"This file-to-affect cannot be found:\n%s",
dofile);
MessageBox(NULL, tmpstr, "ERROR",
MB_OK | MB_ICONEXCLAMATION);
dofile[0] = '\0';
doit = ' ';
huminp = 1;
} }
keynam[0] = '\0';
} } }
if(NeedInfo & 4)
{ SelectObject(hdc, hfont1);
TextOut(hdc, 1, 126, eraser, 80);// Hide this line while prior
// line edited
}
if(!(NeedInfo & 62)) // Only display this data entry after prior
// lines are accepted
{ SelectObject(hdc, hfont1);
if(NeedInfo & 1) // Only want yellow question text when asking
// this question
if((((del2 == ' ') && (d2 == ' ') && (sd2 == ' ')) ||
!strcmp(keynam, "Up")) ||
((huminp == 1) && (juset == 1)))
SetTextColor(hdc, 0x0000FFFF); // yellow
if((dot > -1) && !strcmp(".CRP", &(dofile[dot])))
// if specified extension is .CRP
{ TextOut(hdc, 1, 142, eraser, 99);
TextOut(hdc, 1, 142,
"Every specified .CRP file will always be replaced.",
50);
SetTextColor(hdc, oldcolr);
del2 = 'Y'; // ALWAYS delete "original" .CRP files
}
else
{ TextOut(hdc, 1, 142,
"Delete that file after successful processing (Y/N)?",
51);
SetTextColor(hdc, oldcolr); // black
SelectObject(hdc, hfont2);
if(sd2 != ' ')
del2 = sd2;
if(d2 != ' ')
del2 = d2;
}
sprintf(tmpstr, "%c", del2);
TextOut(hdc, 368, 142, tmpstr, 1);
if(NeedInfo & 1)
{ if((del2 != ' ') && (((huminp == 0) || (juset == 0)) ||
!strcmp(keynam, "Enter")))
{ NeedInfo &= 254; // Clear Bit Four (5th from right),
// value 16
sd2 = ' ';
}
else
juset = 0;
d2 = ' ';
keynam[0] = '\0';
affitem->kill = del2; // save on linked list
} }
if(NeedInfo & 2)
{ SelectObject(hdc, hfont1);
TextOut(hdc, 1, 142, eraser, 80);// Hide this line while prior
// line edited
}
SelectObject(hdc, hfont1);
if(!NeedInfo) // no other file info required?
{ if(!working && (affitem->next != NULL))
{ strcpy(tmpstr, "MORE TO SEE; PRESS M");
doit = 'M';
}
else
{ strcpy(tmpstr, "ACCEPT / MORE (A/M)?");
if(!huminp) // if automatically continuing
doit = 'A';//for when all data has been presented/accepted
}
if(working || (proceed == ' '))
{ if(!working && (doit == ' '))
SetTextColor(hdc, 0x0000FFFF); // yellow
TextOut(hdc, 1, 174, tmpstr, 20); // Display MORE TO SEE or
// ACCEPT / MORE
SetTextColor(hdc, oldcolr);
SelectObject(hdc, hfont2);
sprintf(tmpstr, "%c", doit);
TextOut(hdc, 155, 174, tmpstr, 1);
if(!working && (doit != ' '))// check for a previously-saved
// A or M
proceed = doit;
} }
else
{ SelectObject(hdc, hfont1);
TextOut(hdc, 1, 174, eraser, 25); // Hide while prior line
// edited
}
SelectObject(hdc, hfont1);
tmpstr[0] = '\0';
strcpy(tadone, "Press");
if(proceed != 'A')
{ if(proceed == 'M')
{ affnum++; // prepare new item number
affprev = affitem; // Save current item as previous (becomes
// previous, next line of code)
affitem = affitem->next;
}
if(huminp)
{ if(!(NeedInfo & 32))
{ strcat(tadone, " up-arrow to edit prior entry,");
doit = ' '; // no auto-proceed if human entry required
}
sprintf(tmpstr, "%s ESC to exit program.", tadone);
}
else
TextOut(hdc, 165, 174,
" (autoproceeding with good-looking parameters)",
47);
}
else // We are proceding
{ if(huminp)
TextOut(hdc, 165, 174,
"-- OK, accepted inputs now being applied...", 43);
else // Automatically proceeding ("autoproceeding" duplication
// is for Window re-draws)
TextOut(hdc, 165, 174,
" (autoproceeding with good-looking parameters)",
47);
strcpy(tmpstr,
"Program automatically ends when done; Press ESC to abort.");
}
TextOut(hdc, 100, 238, eraser, 49);
SetTextColor(hdc, 0x00FFFFFF); // white
TextOut(hdc, 1, 238, tmpstr, strlen(tmpstr));
// tmpstr is one of 3 possible strings here:
// Press ESC to exit program.
// Press up-arrow to edit prior entry, ESC to exit program.
// Program automatically ends when done; Press ESC to abort.
if(!NeedInfo && (proceed == 'A') && (status > 0))
{ i = status / 100; // extract type of status message
j = ((status % 100) >> 1) + 1; // Extract progress report:
// 1 to 50
strcpy(tadone,
" ");
// reset to 51 spaces
strnset(tadone, '=', j); // Create progress bar (overwrite up
// to 50 spaces)
tadone[j] = '>'; // Make progress bar look like an arrow
// (may overwrite 51st space)
// note that null-terminator, after strcpy(), is not affected
switch(i)
{ case 1:
sprintf(tmpstr, "Initializing work area: [%s]", tadone);
break;
case 2:
sprintf(tmpstr, "Gathering Control Primes: [%s]", tadone);
break;
case 3:
j = (fetched - 108) >> 3;
if(j > 65536)
j = 65536;
sprintf(tmpstr,
"Please be patient; pulling Primary Cryption Key,"
" up to %d prime numbers", j); // Fetched is at
// least 8300; 8300-108=8192;
// 3 bit-shifts=division-by-8 (8192 bytes from Key File
// yields 1024 primes)
SetTextColor(hdc, 0x0000FFFF); // Yellow
TextOut(hdc, 1, 190, tmpstr, strlen(tmpstr)); // Special
// "be patient" message
SetTextColor(hdc, oldcolr);
sprintf(tmpstr, "Loaded %d Primes: [%s]", prmbar, tadone);
break;
case 4:
TextOut(hdc, 1, 190, eraser, 99); // erase special message
tmpstr[0] = '\0';
sprintf(tmpstr, "Crypting File #%d of %d: [%s]",
affnum, affcou, tadone);
}
TextOut(hdc, 1, 206, eraser, 99); // Erase any prior
// progress bar
TextOut(hdc, 1, 206, tmpstr, strlen(tmpstr)); // (Re)Display
// current message and progress bar
}
SetTextColor(hdc, oldcolr);
SelectObject(hdc, hPriorF);
EndPaint(hwnd, &ps);
painting = 0;
if(proceed == 'M')
{ proceed = ' ';
gotdat = 0;
NeedInfo |= 15;
repaint = 1; // WANT to repaint, to prepare for
// new data entries
}
if((proceed == 'A') && !working)
{ working = 1;
PostMessage(hwnd, WM_COMMAND, 123456789, 987654321);
// **** BEGIN TASK! ****
}
if(repaint)
{ InvalidateRect(hwnd, &rect, 0); // The window must now be
// re-updated
repaint = 0;
}
return(0); // END OF WM_PAINT
////
case WM_COMMAND:
if((wParam == 123456789) && (lParam == 987654321))
{ if(0 < (ret = WorkAreaInit()))
{ PostQuitMessage(ret); // any of several possible errors
return(0);
}
if(0 < (ret = KeyToPrimes()))
{ PostQuitMessage(ret); // any of several possible errors
return(0);
}
if(0 < (ret = AffectFiles()))
{ PostQuitMessage(ret); // any of several possible errors
return(0);
}
PostQuitMessage(0); // QUIT AUTOMATICALLY WHEN ALL
// TESTING/PROCESSING IS DONE
return(0);
}
break; // ignore all other WM_COMMAND messages
////
case WM_DESTROY: // Clean-up work goes here, before ending overall
// conversion program
PostQuitMessage(0); // Note memory allocations freed at end
// of WinMain()
return(0); // Always return Zero from any Windows message
// processed
} // all Windows messages not handled in switch block must be
// processed below
return(DefWindowProc(hwnd, iMsg, wParam, lParam));
}; // END OF WndProc()
/********************************************************************/
// This semi-clone of the Main Windows Message-Handling Loop has the
// purpose of being available anywhere in this program, to process
// anything outstanding in the Message Queue. That is, some of the
// other functions below may take a long time to do their things, and
// Windows might think the program is "Not Responding". By placing
// calls to this function in appropriate places (during Progress-Bar
// handling, most likely), not only will Windows stay happy, but it
// also makes it easier for the user to abort the program by pressing
// the ESCape key. NOTE: When a large Key File is specified, such
// that more than ten thousand Primes will be pulled as the Primary
// CRYPTION Key, it does indeed take time to sort out those thousands
// of data items from random sequence, and then pull the primes from
// the main compressed-data file. (IF YOU WANT REAL SPEED FOR THIS
// PART OF THE PROCESS, GET A "SOLID-STATE DISK" a.k.a. RAM DISK TO
// HOLD THE PRIMES DIRECTORY.) If the user chooses to Abort, several
// seconds may pass before the CRYPTION program acknowledges it by
// quitting. In a way this slow preparation work is good because the
// user is granted a reasonable chance to abort -- since the CRYPTION
// process itself is the very next thing that happens after loading
// all those Primes, and this process does depend on the speed of the
// computer, which can be quite fast. There may not be any other
// equally good place to abort the CRYPTION program after all data has
// been accepted, especially if small files are to be scrambled
// (UNLESS very large Skip Numbers are chosen). IN GENERAL, it may be
// preferable to use large Key Files only when you want to scramble
// very large data files, or perhaps a great many small files. In
// those cases the time to load 65,000 primes for the Primary CRYPTION
// Key is OK because you know it's going to take a while, anyway, for
// the overall scrambling process to be completed.
S_32 ProcessMessages(void)
{ static MSG mesg; // Make this multi-byte structure static so
// doesn't go on the stack.
S_32 ret = 0;//Initialize return-value-variable to Everything-is-OK.
// For the inexperienced, this assigment will happen
// every time this function is called, because ret is
// NOT a "static" type of variable. Assignments to
// static variables (in their declaration statements at
// the start of a function) are only done once, after
// which the assignment-code is ignored in all later
// calls to the function. However, the assigned value
// is remembered in-between function calls, and the
// body of the function can change it if desired (then
// the new value will be remembered, of course).
while(!ret && PeekMessage(&mesg, NULL, 0, 0, PM_NOREMOVE)) // Seek
// anything in the Windows Message queue
{ if(GetMessage(&mesg, NULL, 0, 0)) // We arrive here ONLY if
// PeekMessage() found something
{ TranslateMessage(&mesg);
DispatchMessage(&mesg);
}
else // GetMessage pulled a WM_QUIT
{ PostQuitMessage(0); // Must put it back in Messge Queue for
// primary loop in WinMain()!
ret = 1; // Tell this while() loop AND the calling function
// to quit.
} } // Otherwise loop til processed all accumulated Messages
// in the Queue
return(ret); // return-value ret will be 0 if no messages in Queue
};
S_32 keycheck(void) // Let's try to get 90 bytes of reasonably random
// data from the Key File
{ S_32 k;
U_08 uc, *ucp2 = ucMemBlk1; // Point at start of buffer loaded from
// Key File
// Remember Key File data begins 15000 bytes into the buffer, and
// that ucp1 points at its start
for(i=0; i<100; i++)
ucp2[i] = 0; // Erase the first 100 bytes of the ucMemBlk1
// memory block
for(i=0, j=0, uc=0; (i<(int)fetched)&&(j<90); i++)// Prepare to walk
// length of fetched Key File data
{ uc ^= ucp1[i]; // Get a byte (or combine with another via
// Exclusive-Or)
// This trick with Exclusive-Or means that the 90 bytes we save via
// the ucp2 pointer will be rather unlike the data actually pulled
// from the file. More, since we are seeking ALL DIFFERENT bytes,
// there is no telling what order they will be in. That is, if a
// pulled/combined byte has already been found, we keep pulling/
// combining until something different pops out. So, while a simple
// text file like the U.S. Constitution may not look much like
// random data, multiple Exclusive-Or operations between pulled
// bytes will indeed create reasonable randomness. (Later on, in
// the main algorithm for using the Key File to select primes, bytes
// pulled from the Key File will be combined with pseudorandom
// numbers, so again even minimal randomness in the Key File will be
// acceptable.)
if(uc > 0) // for nonzero values only
{ for(k=0; k<j; k++) // check previously saved values
if(uc == ucp2[k]) // If current nonzero value has already
// been saved
break; // consider it not random enough
if(k == j) // if for() loop ended with no match of current value
{ ucp2[j++] = uc; // save it
uc = 0; // reset Exclusive-Or "accumulator"
} } } // loop through as much of Key file as needed
return(j < 90); // Return value is zero if got 90 different
// byte values
};// Here is a place where parentheses with return make great sense,
// because they delimit a comparison-operation. Parentheses force the
// compiler to code an evaluation of the comparison, and yield a
// guaranteed result of either Zero-for-"False" or One-for-"True".
S_32 WorkAreaInit(void) // fill 4,725,000 bytes with zeros
{ char *r, *s, *t, *u, *v;
S_32 x, y;
p = (char *)(ucMemBlk4 + 525000);// Set pointer partway through this
// memory block
q = p + 525000; // set pointer partway through this memory block
r = q + 525000; // set pointer partway through this memory block
s = r + 525000; // set pointer partway through this memory block
t = s + 525000; // set pointer partway through this memory block
u = t + 525000; // set pointer partway through this memory block
v = u + 525000; // set pointer partway through this memory block
for(status=100, x=0; x<525000; x+=10500, status+=2)
{ InvalidateRect(hwnd, &rect, 0); // make window update-able
PostMessage(hwnd, WM_PAINT, 0, 0); // Progress bar grows, based on
// status
if(ProcessMessages()) // process ALL messages in the queue
return(CRP_WM_QUIT); // assume user abort
for(y=x; y<(x+10500); y++)
{ ucMemBlk2[y] = '\0'; // 50 * 10,500 = 525,000
ucMemBlk4[y] = '\0'; // 50 * 10,500 = 525,000
p[y] = '\0'; // 50 * 10,500 = 525,000
q[y] = '\0'; // 50 * 10,500 = 525,000
r[y] = '\0'; // 50 * 10,500 = 525,000
s[y] = '\0'; // 50 * 10,500 = 525,000
t[y] = '\0'; // 50 * 10,500 = 525,000
u[y] = '\0'; // 50 * 10,500 = 525,000
v[y] = '\0'; // 50 * 10,500 = 525,000; last of 4,200,000 bytes
} } // YES, the preceding could be written more efficiently and run
//faster. But this was also the first chance to experiment with
// progress-bar code....
it = (U_32 *)ucMemBlk3; // Initialize Index Table pointer for
// IndexToPrime() function
WorkSize = 0; // There are no ScratchPads prepared yet in the
// WorkRegion (ucMemBlk2)
WhichWay[0] = 0;// Set for Encryption Algorithm (code for Decryption
// Algorithm is 4)
WhichWay[1] = 1;// Set for Encryption Algorithm (code for Decryption
// Algorithm is -1)
// Now give the Control Scratchpads some default values,
// soon to be changed
LoadMaster->dvd = 0x12345;// Just a number to be divided a few times
// (in hexadecimal)
LoadMaster->dvs = 0x13531; // This is a prime, 79153 in Base Ten;
// not used very long
ModCounter->dvd = 1; // Two bits at a time will be yielded by
// ModCounter
ModCounter->dvs = 0x8000000; // This division produces ZEROS for a
// short time (2 modifications)
TypeSetter->dvd = 30; // Three bits at a time will be yielded by
// TypeSetter
TypeSetter->dvs = 63; // This division produces 011 110 011 110
// 011 110 ...(til dvd/dvs replaced)
RandomSkip->dvd = 1; // Two bits at a time will be yielded by
// RandomSkip
RandomSkip->dvs = 3; // This division produces 01 01 01 01 ...
// (til dvd/dvs replaced)
// Above default values will allow the PseudoRandomize() function to
// be called even while gathering the final data that will replace
// these default values. This lets us scramble the Key File data
// even while fetching it, thereby practically guaranteeing we
// always have pseudorandom Key data. Again, that is the main
// reason why only a minimal randomness test was performed by
// function keycheck().
return(0);
};
/* NOTE REGARDING EFFECTIVENESS OF THE CRYPTION ALGORITHMS: While
someone watching the flow of this code with a debugging tool could
easily use the above initial information to construct a list of
initial "scramble values" (values that affect key-file data), it
remains to be seen if that information can actually help unscramble
a given .CRP file. See, the dilemma faced by a cryptanalyst is
that the scramble-data used to affect that file did NOT directly
come from these easily computed initial scramble values. These
values are merely used, along some of with those 90 "random" bytes
found by the keycheck() function, to replace themselves with all
new values -- and the cryptanalyst does not know what key file was
used to obtain those 90 bytes. THEN, those new values are used to
generate all new pseudorandom numbers which are combined with
(again unknown to the cryptanalyst) straight key-file data to
select a thousand or more primes from the compressed primes data
file -- and it is those primes that generate the scramble-data
used to affect a Message file. It seems likely that without
knowing the key file, and even knowing that the scrambled data in
the affected file always begins with "Data Protected From
Snoopercomputers"..., there is no easy way to decide which
GROUPINGS of primes-as-divisors and which numbers-as-dividends were
used to generate the actually-used scamble-values. */
// Do binary search of Index for location of Nth prime, then fetch it
// from the COMPRESS.PRM file. Parameter is a pointer to N;
// THIS FUNCTION REPLACES N WITH FOUND PRIME
S_32 IndexToPrime(U_32 *dp)
{ static U_32 N, hi, md, lo; // Static variables let the function run
// faster
/*
The Index to the COMPRESS.PRM file treats that file like 143023 blocks
of 720 bytes each (the last block is smaller). The CMPRMDEX.QNT file
holds 143023 quantities of primes, each quantity being the number of
primes represented by the compressed data in one 720-byte block of the
file COMPRESS.PRM. When the Index file was loaded, those quantites
were added up, and each subtotal was saved. Therefore we now have in
memory an ever-increasing sequence of numbers, which we can use to
find the Nth prime. N just needs to be no larger than the last value
in the sequence, which is the grand total number of primes that can be
accessed via COMPRESS.PRM (203,280,221). The first thing this
function will do is ensure N is not too big. Then it will conduct a
fast binary search to determine which 720-byte block inside
COMPRESS.PRM holds the desired Nth prime, and that compressed-data
block is of course immediately fetched. The desired prime is then
quickly constructed using the Index value, N, and some of the elements
in the tbl array.
*/
N = *dp;
while(N > 203280221) // Too big a value entered for finding Nth
// prime?
N >>= 1; // HALVE the value (ignore any odd-bit remainder)
if(N < 7)
*dp = skpd[N]; // These first primes are NOT represented in
// COMPRESS.PRM
else
{ lo = 0; // lowest Index element
hi = 143022; // highest Index element
md = hi >> 1; // Prepare to do a binary search from middle of
// the Index
while (lo != hi)
{ if(N < it[md])
hi = md; // Set new upper bound (this still might be the
// right block!)
else if(N > it[md])
lo = md + 1;// Set new lower bound (block following md might
// be the right one!)
else
break; // Well, well, cand just happened to exactly match
// an Index entry!
md = ((hi - lo) >> 1) + lo; // Get new middle between hi and lo
// (may equal untested lo if hi-lo=1,
// but next loop then finds it or makes lo=md=hi)
}
lo = md * 720; // Compute which block of compressed file holds
// our N
fseek(prmfil, ((S_32)lo - fpb) , SEEK_CUR); // Go to appropriate
// file location
fpb = fread(tmpstr, 1, 720, prmfil); // Load a block of
// compressed-prime data
fpb += lo; // Set file-position-byte to current location for
// next seek
ucp4 = tbl; // initialize table-pointer
if(md > 0) // not the very first block
{ hi = it[(md - 1)]; // Fetch total number of primes preceding the
// fetched block
md *= 30030; // Initial computation for first possible prime in
// that block
md++; // Adjust for exactness: first number represented by
// compressed data
bt = 1; // initialize bit-tester
}
else // very first block...
{ hi = 6; // Compressed-prime data starts at 7th prime;
// ensure loop below works
md = 17;
bt = 2; // initialize bit-tester
ucp4++; // correct table pointer for first block
}
by = (U_08 *)tmpstr; // Set byte-pointer to start of 720 items in
// buffer
for(;;) // this loop guaranteed to break
{ if((*by & bt) == bt) // if this is a prime
hi++; // count towards desired N
if(hi >= N)
break; // exit if N reached
md += *ucp4++; // add a tbl entry to create new possible prime
if(bt == 128) // check for last possible bit-value (in one byte)
{ bt = 1; // reset bit-tester
by++; // advance to next byte in buffer
}
else
bt <<= 1; // double for next bit-to-test
} // when loop ends, variable md holds the desired Nth prime
*dp = md; // Replace N with it
}
return(0); // no error!
};
// Divide using passed scratchpad pointer, to obtain some number of
// bits of result. Since Base 2 is king, each division will yield one
// bit. Most of the time this function will be used to obtain just
// two bytes, 16 bits, and often less. NOTE: ENSURE (0 < .dvd) and
// (.dvd < .dvs) -- WHEN .dvs IS A PRIME>2, NEVER DIVIDES EVENLY!
U_32 Divide(struct scratchpad *sp, S_32 bits)
{ static U_32 ret;
/*
One of the keys to the CRYPTION program is the fact that this
function operates on EXTERNAL data. That modified data can be --
and is -- simply retained until this function is called to operate
on it some more. Thus we do PART of an overall long division now --
just a few bits of it -- some more bits later, some more bits still
later still, and so on. There will be up to 64K division-operations
in progress! That is how this program trades memory usage to obtain
speed. Only a relatively small amount of division is needed to
obtain the pseudo-random numbers used for scrambling data, and it
doesn't have to be re-done to get different pseudo-random numbers --
because all the operations are held in memory, waiting for this: */
ret = 0; // initialize return value
while(sp->dvd > sp->dvs) // While divisor is smaller than
// to-be-divided
sp->dvd >>= 1; // HALVE the dividend. Here we do not care at all
// about keeping the thown-away lowest bit, because
// we expect to be doing this only to convert a random
// 32-bit number down to the same magnitude of the
// divisor-Prime -- and once done, will not need to be
// done again to this ScratchPad.
while(bits > 0)
{ ret <<= 1; // For each bit counted in while() loop, prepare a
// place to hold a One or a Zero (doubles ret by shifting
// all bits higher one position, "appending" a zero)
if(sp->dvs > sp->dvd)//while divisor is greater than to-be-divided
sp->dvd <<= 1; // double/redouble dividend
/* The two main steps in this loop are doing "long division" in
Base Two, totally analogous to familiar Base Ten stuff. Consider
this Base Ten example:
__0.009708_ <-appended zeros retained when can't subtract
103 | 1.000 <-append and keep 2 zeros in quotient, but
_927_ <-subtract after 3rd; nonzero put in quotient
730 <-append a zero (multiply by Ten), but quotient
_721_ <-gets a nonzero because we can subtract again
900 <-append/keep 1 more zero above
_824_ <-but 2nd appended zero allows subtraction
Note in Base Ten each appended Zero means multiply-by-Ten, while
in Base Two it means multiply-by-Two -- doubling, in other words.
Also in Base Two, the only digit (and multiplier) besides Zero is
ONE (not 9, 7 or 8 as above), which is why Base Two division can
subtract without multiplying. */
// Below is the subtraction step; if ok-to-do, the Zero that was
// appended to ret via bit-shift/doubling will be incremented
if(sp->dvs < sp->dvd) // TEST: is divisor smaller than dividend
// (can we subtract)?
{ sp->dvd -= sp->dvs; // Division in Base Two consists of
// subtracting the divisor from the (doubled/redoubled) dividend.
// The remainder becomes new dividend for next division steps.
ret++; // Obviously if no subtraction occurs, appended Zero will
// be unchanged
} // Ret is a partial quotient, the wanted string of bits, mixed
// Zeros and Ones
bits--; /* Each iteration of overall loop represents one bit in
Base-Two division. Since 16 or fewer bits are normally sought,
next test exists for those rare occasions when we want to know
that a prime's reciprocal-PERIOD is very long. For these tests,
dvs is set to the prime P, dvd is set to P-1, and the bits
parameter is set to 100,020. As it happens, when taking a
reciprocal, the last division of a prime's period will always have
a remainder of 1, while the MIDDLE division during the period will
have a remainder of P-1 (this is a consequence of the two halves
of a period adding their digits to a constant value). So, by
starting with a dividend of P-1 for dvd before calling this
function, and seeking a constant of 1 as the current remainder...
if a remainder of 1 appears before we countdown bits from 100,020
to 20, then we know that the HALF-PERIOD is less than 100,000
digits, and this is unacceptable for CRYPTION. (Note 100,000
loops are actually done quite quickly, since bit-shifts and
subtractions are processed FAST.) */
if(bits > 20) // Only do this test when wanting large quantities
// of bits
{ ret = 0; // Prevent accidental return value of 0xFFFFFFFF
// by main loop
if(sp->dvd == 1)
return(0xFFFFFFFF); // Let period-testing routine know the
// divisor is unacceptable!
} }
return(ret); // Note most return values will range 16 bits or less,
}; // max of 0xFFFF
// The function processes one byte at a time only because files are
// measured in bytes. Later, this might be modified to handle 32-bit
// or even 64-bit quantities, in which case the ModType and ModData
// arrays must be adjusted, and some tricky end-of-file stuff will be
// needed, too.
U_08 PseudoRandomize(U_08 uc)
{ static U_08 ch, sv, em, mn, kp; // Static variables speed a function
static S_32 w, x, y, z; // up by not needing to be recreated/
static U_32 dat; // destroyed every time the function is called.
w = Divide(RandomSkip, 2); // get a 2-bit number (0 to 3)
if(w == 3) // Prepare to convert 3 to 1 -- want w to be ONLY
// for 0, 1 or 2 skips, averaging 1 (makes actually-used
w = 1; // cryptioning data less predictable)
kp = uc; // Keep the value-to-randomize for the following
// do/while loop
do // DO-LOOP PURPOSE: pseudorandomly waste cryptioning-numbers;
// w is a tiny internal Skip Number
{ uc = kp; // Restore initial value of data-to-affect if this
// DO-loop repeats
x = Divide(ModCounter, 2) + 1; // Get a 2-bit number (0 to 3) and
// convert it to range 1 to 4
/* Now we reach the heart of the algorithm. Basically, we will
use ModCounter and Divide() to decide how many modifications will
be made to the current byte, from two to five. (If that seems
strange because of above x having range 1 to 4, remember that ZERO
is a counting value and will be used here, to access the first
ModType and ModData array elements. That is, counting both Zero
and One represents two iterations.) So two-to-five pseudo-random
codes will be quantified by ModCounter. Each of those codes will
be generated by sending TypeSetter to Divide() for a 3-bit result,
which will represent one of eight possible modifications to the
data at uc. These codes will be saved in the ModType array.
Also, of course, more data is required DURING a modification, to
actually cause the modification, and so LoadMaster will be sent to
Divide() to obtain a 16-bit number. This number will be used as
an index into the main WorkRegion/array of 1K to 64K ScratchPads
(65536 fits in 16 bits). The selected ScratchPad is sent to
Divide() for a byte of modification data, which will be saved in
the ModData array. Note that depending on Encryption or
Decryption, the data arrays are filled either first-to-last or
last-to-first. Remember, CRYPTION is a NON-COMMUTATIVE process,
so each Decryption step must be exactly the reverse of an
Encryption step. Thus the ModType and ModData arrays: All the
info needed to Encrypt or Decrypt a single byte is GATHERED in the
same order, but STORED in opposite orders (based on WhichWay
info). Then data can be plucked from the arrays in first-to-last
order, but because of the way it was stored, it will be USED in
the correct order for the chosen Algorithm. There is one slight
complication, in that different fractions of the arrays are used,
depending on ModCounter: The last-to-first sequence-of-array-
access has to be adjusted whenever fewer-than-five Modifications
are done. */
for(y=x,// Copy number of modifications, zero-based counting (note
// comma operator) the adjustment to x is necessary because
// the arrays start at element zero
z=min(WhichWay[0],x); // Get initial (first or last) access
// value for ModType and ModData arrays
// Note: Since WhichWay[0] will be either 0 or 4, the min()
// macro ensures the correct initial z for the ModType and
// ModData arrays (range from 0 to 4). Example: if x is 2
// (actually meaning 3 Modifications), then z is set here
// to either zero (Encryption value of WhichWay[0]) or 2
// (when WhichWay[0] is 4).
y>=0; // Check for ending of this gather-mod-data loop
// (in Example, y starts at 2)
z+=WhichWay[1], // modify by +1 or -1, depending on Encryption
// or Decryption Algorithm
// in Example, z is sequence 0, 1, 2 (Encryption)
// or 2, 1, 0 (Decryption)
y--) // (note comma operator above) Loop countdowner. Example
// y sequence is 2, 1, zero, and -1 -- which terminates
// loop due to test for y>=0 above.
{ ModType[z] = (U_08)Divide(TypeSetter, 3); // Get 3-bit
// Modification Type, save in array
dat = Divide(LoadMaster, 16); // Get a 16-bit number because max
// value is 64K ScratchPads
if(WorkSize) // WorkSize is zero during the initial part of the
// prep work, remember
ch = (U_08)Divide(&(WorkRegion[(dat % WorkSize)]), 8);// The %
// (modulus) operation yields real ScratchPad
else // No ScratchPads available yet; and we still need 8
// pseudorandom bits
ch = (U_08)(dat & 0x000000FF); // Keep lowest 8 bits of the
// 16-bit ScratchPad ID
ModData[z] = ch; // Save the data that will be used to modify
// the byte in parameter uc
} // At end of loop we have quantity-x of stored Type and
// Modification info
for(y=0; y<=x; y++) // x ranges from 1 to 4 here, but 5 loops
// possible since counting starts at zero
{ ch = ModData[y];// Get Modification Data from just-filled array,
// for this Modification Step
// Below, (ch % 7) gets the remainder of division-by-7
// (modulus: zero to six)
sv = (U_08)((ch % 7) + 1); // Some Modification Types only need
// values of 1 to 7
em = (U_08)(8 - sv); // The same Modification Types need this
// bit-count, also
mn = ModType[y]; // Now get Type for current Modification from
// appropriate array
if(WhichWay[1] > 0) // Encryption Algorithm chosen
{ switch(mn) // Type Number ranges from 0 to 7
{ case 0: // About to do a LEFT ROTATION; only 7 possible mods
// to original value.
case 1: // (Eight rotations of a byte is full-circle, no
// affect on data. Prevented.)
uc = (U_08)((uc << sv) + (uc >> em)); // Move some bits
// left; this throws the rest away off the left end of the
// byte, so we ALSO move the not-yet-thrown bits right,
// thus letting us combine the two sets of moved bits
break;
case 2:
case 3:
uc += ch; // add; allow byte to odometerize
break;
case 4:
case 5:
uc -= ch; // subtract; byte will odometerize as necessary
break;
case 6:
case 7:
uc ^= ch; // modify using Exclusive-OR (flips a few bits)
}
if(mn & 1) // if Modification Type 1, 3, 5, or 7
uc = (U_08)(~uc); // ALSO FLIP ALL BITS
}
else // Decryption Algorithm chosen
{ if(mn & 1) // if Modification Type 1, 3, 5, or 7
uc = (U_08)(~uc); // FIRST FLIP ALL BITS
switch(mn) // Type Number ranges from 0 to 7
{ case 0:// About to do a RIGHT ROTATION; only 7 possible mods
// to original value.
case 1: // (Eight rotations of a byte is full-circle,
// no affect on data. Prevented.)
uc = (U_08)((uc >> sv) + (uc << em)); // Move some bits
// right; this throws the rest away off the right end of
// the byte, so we ALSO move the not-yet-thrown bits left,
// thus letting us combine the two sets of moved bits
break;
case 2:
case 3:
uc -= ch; // subtract; byte will odometerize as necessary
break;
case 4:
case 5:
uc += ch; // add; allow byte to odometerize
break;
case 6:
case 7:
uc ^= ch; // modify using Exclusive-OR (flips a few bits)
} } }
} while(--w >= 0); // Immediate exit if w was 0, or repeat loop once
// or twice (AVERAGE of one repeat)
return(uc); // Original value now scrambled two-to-five times, each
// time being one of eight ways, and numbers causing those changes
}; // here-and-now were not necessarily obtained as consecutive to the
// numbers that caused changes during the LAST call to
// PseudoRandomize()
/* About the KeyToPrimes() function
Uses the already-loaded KeyFile data in ucMemBlk1, along with
"fetched", to gather anywhere from 1K to 64K primes into the work area
at ucMemBlk2. Two groups of primes will be loaded. The first group
consists of only 4 primes, which are used for overall control of the
CRYPTION process. These 4 primes will be pulled using those 90
different bytes, located at the start of ucMemBlk1, that were found/
saved when giving the key file a minimal test for randomness. All we
need are 16 bytes, for 4 values of N in the Index to the primes -- but
we will actually use 32, to increase the randomness of those values.
These control primes must pass a special test, to ensure that the
reciprocal of each has a "half-period" of at least 100,000 digits.
The program will abort if it can't find 4 such primes using those 90
"random" bytes. But that is expected to be a fairly low-probability
event, and if it happens, it is simple enough to choose a different
key file. The second group of primes gathered will depend on
"fetched", which is basically the size of the key file, up to roughly
half of a megabyte (if no duplicate primes then about 524288 bytes
would suffice). Eight bytes at a time will be pulled from the key
file, to be used to create an N to obtain one one prime for a
scratchpad in ucMemBlk2. The miminum allowed key file size is 8300
bytes, so the "minimum" number of primes used for CRYPTION will be 1K
(loads quickly, and weeded-out duplicates will reduce that quantity).
The maximum is 64K. In-between those quantities, the size of the Key
File will determine the security of the Message(s). AND, the tail-end
of the Key File data is reserved, to allow ANOTHER replacement of the
four Control Primes, after the main prime-key-array has been gathered,
and prior to beginning any scrambling operations on the Message(s).
The only difference the file size makes, with respect to the actual
execution of the CRYPTION process to affect files, is the preparatory
time taken to load the primes (which can be minimized if the \PRIMES
directory is located on a large RAM disk, although some special
sorting is going to be done to more efficiently extract the primes
from COMPRESS.PRM). But that is the only price to pay for greater
security. In addition to specifying a thousand or more N-values for
selecting primes, a third group of numbers will be entirely generated
by one of the first 4 control primes. This group will be equal in
quantity to the group loaded according to "fetched"; they will be the
dividends into which the scratchpad-primes are initially divided (no
reciprocals here, unless pseudorandomly happens!). Finally, in
comparison to other and more standard encryption techniques, in which
the Key is described as being "56-bit" or "128-bit" or even
"2048-bit", the MINIMUM Key here is (if no duplicate primes)
65536-bit. */
S_32 KeyToPrimes(void)
{ U_08 uc, *ucp2 = ucMemBlk1; // Want a pointer with which to walk the
// data buffer
S_32 a, b, c, d, x=-4, y=0, z=1;
U_32 test, hold;
// below, this block for first four control-primes
is = (U_32 *)ucMemBlk4;//Initialize Sorting and Prime Index pointers
status = 200; // for "Gathering Control Primes" progress bar
InvalidateRect(hwnd, &rect, 0); // make window update-able
PostMessage(hwnd, WM_PAINT, 0, 0); // show blank progress bar
if(ProcessMessages()) // process ALL messages in the queue
return(CRP_WM_QUIT); // assume user abort
// Below, if had 90 "random" bytes from ucp2[0] to [89], then last
// 64-bit data starts at [82]
test = 0;
while((z == 1) && (y < 83)) // Prepare to walk "random" numbers
// grouping for values of N
{ for(a=0; ((a<4)&&(y<83)); a++, y++)
{ uc = *ucp2++; // fetch a byte and increment pointer
uc = PseudoRandomize(uc); // "AFFECT" this byte, making it
// somewhat more "random".
#ifdef PROCESSOR_ENDIAN_TYPE_LITTLE
((U_08 *)(&test))[a] = uc; // Save the byte as part of a 32-bit
// number
#endif
#ifdef PROCESSOR_ENDIAN_TYPE_BIG
((U_08 *)(&test))[(3-a)] = uc; // save the byte as part of a
// 32-bit number
#endif
uc = *ucp2++; // fetch another byte and increment pointer
uc = PseudoRandomize(uc); // "AFFECT" this byte, making it
// somewhat more "random". Note that in WorkAreaInit(),
// initializations of LoadMaster, ModCounter, TypeSetter, and
// RandomSkip ScratchPads were done just so these calls to
// PseudoRandomize will work while using the KeyFile here to set
// those ScratchPads! This makes those initializations VERY
// temporary! Below, by taking the address of a variable and
// using that address as an array-pointer, we can set any number
// of bytes without referencing subunits of the variable (say a
// struct). HOWEVER, the order in which the bytes are put
// together, to make a 32-bit number, is a sure sign of a
// potential "Big-Endian/Little-Endian" conflict. BEWARE!
#ifdef PROCESSOR_ENDIAN_TYPE_LITTLE
((U_08 *)(&test))[a] |= uc;// OR-combine with last byte, as part
// of a 32-bit number
#endif
#ifdef PROCESSOR_ENDIAN_TYPE_BIG
((U_08 *)(&test))[(3-a)] |= uc; // OR-combine with last byte, as
// part of a 32-bit number
#endif // note using OR to combine bits to reduce chance of zero total
} // loop for 4 bytes of a 32-bit number
IndexToPrime(&test); // use Index to replace random N with a Prime
// Next, can't have half-period of 100,000 if Prime is less than
// 200,000
if(test > 200000)
{ WorkRegion[0].dvd = WorkRegion[0].dvs = test; // Copy test-Prime
// to tmp dividend & divisor
WorkRegion[0].dvd--; // set dividend to P-1
if(0xFFFFFFFF != Divide(&WorkRegion[0], 100020)) // Seek 100,020
// pseudorandom bits
{ status += 25;//225, 250, 275, 200
if(status == 300) // if got here then prime passed test
status = 299;// ensure Status Message Text remains the same!
InvalidateRect(hwnd, &rect, 0); // make window update-able
PostMessage(hwnd, WM_PAINT, 0, 0);// Increment progress bar in
// four sections
if(ProcessMessages()) // process ALL messages in the queue
return(CRP_WM_QUIT); // assume user abort
WorkRegion[x] = WorkRegion[0]; // Copy both Prime/Divisor and
// current Remainder/Dividend
// Note: Above test done with WorkRegion[0] to avoid affecting
// default values of LoadMaster, ModCounter, TypeSetter, and
// RandomSkip until we know that the new values pass the test
x++; // LoadMaster is set when x=-4; increment for ModCounter,
// TypeSetter and RandomSkip
if(x == 0) // When finished replacing the defaults of all four
// critical Control Primes,
z = 0; // set flag loop-exit; only other way is to fail
// 80-odd attempts to set the four.
}
else // bad prime! Had half-PERIOD of less than 100,000 digits
// in Base Two
{ y -= 7; // Back up to re-use last 7 of 8 "random" bytes of
// those 90 (NOTE that when these are again passed to
ucp2 -= 7; // PseudoRandomize(), very different results are
} } // likely.
else // bad prime! (must be larger than 200,000 to have a
// half-PERIOD of 100,000
{ y -= 7; // Back up to re-use last 7 of 8 "random" bytes of those
// 90 (NOTE that when these are again passed to PseudoRandomize(),
ucp2 -= 7; // very different results are likely.
}
}
if(z == 1) // did not find four acceptable primes?
{ MessageBox(NULL, "QUITTING! Key File (randomly!) didn't contain "
"suitable data for picking one or\nmore of 4 "
"critical Control Primes. (Very rare error -- "
"use another Key File)",
"ERROR", MB_OK | MB_ICONSTOP);
// This is indeed expected to be VERY rare....
return(CRP_ERR_BD); // using 6 as code for Bad Data
} // NEXT UP: Fill the main WorkRegion area!
lim = (S_32 *)(ucMemBlk1 + fetched - 108);
z = (fetched - 108) >> 3; // Key File size divided by
// 8-bytes-per-prime
if(z > 65536)
z = 65536; // Fix upper limit. This is kind of arbitrary. Just
// need more allocated memory for processing the Key File, and for
// scratchpads, to allow an even bigger Key. Of course, a bigger
// Key will also take longer to load....
y = z / 50; // number of primes per progress-bar increment
b = y; // set so initial display of progress bar occurs
c = prmbar = 0; // initialize the bar counter
for(status=300, x=0; ((x<z)&&(ucp1<(U_08*)lim)); x++) // For desired
// primes, depending on Key File size
{ if(b == y) // if time for a progress bar increment
{ InvalidateRect(hwnd, &rect, 0); // make window update-able
PostMessage(hwnd, WM_PAINT, 0, 0); // Progress bar grows, based
// on status
if(ProcessMessages()) // process ALL messages in the queue
return(CRP_WM_QUIT); // Assume user abort if ProcessMessages()
// returned nonzero
b = 0; // reset counter for next increment of progress bar
status += 2; // this is what the next increment will be
if(status >= 400) // ensure progress bar doesn't grow too much
status = 399;
}
// Below, as previously indicated elsewhere, we are about to
// SCRAMBLE bytes taken from the Key File, before using them to
// select prime numbers from COMPRESS.PRM. Here is a bit of extra
// sneakiness, where we semirandomly specify either Encryption or
// Decryption scrambling.
uc = (U_08)Divide(TypeSetter, 1); // Get one bit as Encryption/
//Decryption flag
if(uc == 1)
{ WhichWay[0] = 0; // Set for Encryption Algorithm
WhichWay[1] = 1; // Set for Encryption Algorithm
}
else
{ WhichWay[0] = 4; // Set for Decryption Algorithm
WhichWay[1] = -1; // Set for Decryption Algorithm
}
for(a=0; (a<4); a++) // For 4 pairs of bytes per 32-bit number,
// using current E/D algorithm
{ uc = *ucp1++; // fetch a byte from the Key File
uc = PseudoRandomize(uc); // Make it more "random". Notice that
// inside the PseudoRandomize() function is:
// WorkRegion[(dat %= WorkSize)] --which lets us start using
// the main ScratchPad array to scramble numbers as soon as even
// one ScratchPad has been prepared.
// Below, assemble the value N that will be used to fetch a prime;
// also initialize dividend
#ifdef PROCESSOR_ENDIAN_TYPE_LITTLE
((U_08 *)(&WorkRegion[x].dvs))[a] = uc;//Save as part of divisor
// for this ScratchPad
uc = (U_08)Divide(ModCounter, 8); // get 8 more bits
((U_08 *)(&WorkRegion[x].dvd))[a] = uc; // Save as part of
// dividend for this ScratchPad
#endif
#ifdef PROCESSOR_ENDIAN_TYPE_BIG
((U_08 *)(&WorkRegion[x].dvs))[(3-a)] = uc; // Save as part of
// divisor for this ScratchPad
uc = (U_08)Divide(ModCounter, 8); // get 8 more bits
((U_08 *)(&WorkRegion[x].dvd))[(3-a)] = uc; // Save as part of
// dividend for this ScratchPad
#endif
uc = *ucp1++; // fetch another byte from the Key File
uc = PseudoRandomize(uc); // Make it more "random".
// Below, finish assembling the value N and dividend, using OR
#ifdef PROCESSOR_ENDIAN_TYPE_LITTLE
((U_08 *)(&WorkRegion[x].dvs))[a] |= uc; // Save as part of
// divisor for this ScratchPad
uc = (U_08)Divide(ModCounter, 8); // get 8 more bits
((U_08 *)(&WorkRegion[x].dvd))[a] |= uc; // Save as part of
// dividend for this ScratchPad
#endif // note using OR to combine bits to reduce chance of zero total
#ifdef PROCESSOR_ENDIAN_TYPE_BIG
((U_08 *)(&WorkRegion[x].dvs))[(3-a)] |= uc; // Save as part of
// divisor for this ScratchPad
uc = (U_08)Divide(ModCounter, 8); // get 8 more bits
((U_08 *)(&WorkRegion[x].dvd))[(3-a)] |= uc; // Save as part of
// dividend for this ScratchPad
#endif // note using OR to combine bits to reduce chance of zero total
}
big = WorkRegion[x].dvs; // get N
if(big < 256)
big += 257; // Ignore the lowest primes as having too-short
// periods
while(big > 203280221) // too big a value for finding Nth prime?
big >>= 1; // HALVE the value (ignore any odd-bit remainder)
WorkRegion[x].dvs = big; // put possibly-modified N back
if(status <= 302)// For first 1/50 of scratchpads only, duplicates
// allowed
{ IndexToPrime(&WorkRegion[x].dvs); // Use Index to convert random
// 32-bit number to a Prime divisor
prmbar = WorkSize++; // This ScratchPad is now ready for use!
// Note that the first Divide() that is called to use it will
b++; // cause its .dvd value to be repeatedly halved until it is
} // smaller than the .dvs value (the random .dvd value
// essentially becomes even more random).
else // SAVE IN ucMemBlk4 for sorting! Two mutually exclusive
{ // goals are (1) to access 49/50 of all the desired primes, in
// COMPRESS.PRM as fast as possible (in order), and (2) retain
// ALL the randomness associated with the first 1/50. It can
// be done!
if(!c) // not yet set?
c = x; // SAVE THIS FOR LATER
test = big / 194; // compute a proportionate position
// (203,280,221/1,050,000 = 193.6)
if(!is[++test]) // Bump to avoid Slot Zero in ucMemBlk4 array of
// 4,200,000 bytes; available?
{ is[test] = big; // save N
goto bmp; // Despite being reviled, goto is sometimes the
} // best tool available. GO TO incrementation of progress bar
// ONLY when NO duplicate N is involved. BASIC IDEA IS:
// ucMemBlk4 HAS 1,050,000 SLOTS FOR < 65,000 RANDOM VALUES;
// NOTE THE ABOVE COMBINING |= PUSHES AVERAGE RANDOM NUMBER
// TO UPPER HALF
else // slot full!
{ for(a=d=(S_32)test; (is[a]&&(a>1)); a--); // Find open slot
// preceding proportionate location
for(; (is[d]&&(d<1049999)); d++); // Find open slot following
// proportionate location
for(ip=&is[a]; ((big!=*ip)&&(ip<&is[d])); ip++);
// seek duplicate N if previously saved
if(big != *ip) // for nonduplicate N only
{ if(is[a] || // if no open slot here, OR "here" is the
((((S_32)test - a) >= (d - (S_32)test)) &&// farther slot
// from proportional position AND
!is[d])) // the other slot is open
{ while(big < (hold = is[--d])) // compare current and
// previously-saved N
is[(d+1)] = hold; // Move higher values upward in array
// of slots
is[++d] = big; // save current value in correct place
} // Since slots always outnumber the random values,
// guaranteed to find one unoccupied,
else // and in this case the other side of the proportional
// position is available
{ while((big > (hold = is[++a])) && hold) // compare current
// & prior-saved N
is[(a-1)] = hold; // move lower values (NOT 0) downward
// in array of slots
is[--a] = big; // save current value in correct place
} // now the ucMemBlk4 buffer is becoming an array of sorted
// values interspersed with Zeros
bmp: if((x & 1) == 0) // One bump of prmbar counter for
// every-other N-value
{ prmbar++; // bump count of loaded primes (LIE!)
b++; // bump counter responsible for progress bar
} }
else
x--; // don't count this duplicate
} } }
a = big = 0; // set some counters to start of array
while(a < 1050000) // walk the array of slots
{ if(0 < (hold = is[a++])) // get/test each value
{ is[(a-1)] = 0; // erase the slot from which value was taken
is[big++] = hold; // Condense values so no interspersed Zeros
// remain
} } // now all the Zeros in the array FOLLOW the sorted values
ip = (U_32 *)(ucMemBlk4 + 700000); // This section of the array
// guaranteed to be empty
for(a=0; a<(S_32)big; a++) // walk the sorted N values
{ test = is[a]; // get one
IndexToPrime(&test); // Find Nth prime, steadily forward through
// COMPRESS.PRM
ip[a] = test; // save the prime
if((a & 1) == 1) // for every-other value
{ prmbar++; // one bump of prmbar counter (lie some more)
if(++b == y) // if time for a progress bar increment
{ InvalidateRect(hwnd, &rect, 0); // make window update-able
PostMessage(hwnd, WM_PAINT, 0, 0); // Progress bar grows,
// based on status
if(ProcessMessages()) // process ALL messages in the queue
return(CRP_WM_QUIT); // Assume user abort if
// ProcessMessages() returned nonzero
b = 0; // reset counter for next increment of progress bar
status += 2; // this is what the next increment will be
if(status >= 400) // ensure progress bar doesn't grow too much
status = 399;
} } }
// LAST STEP: Put the primes into the WorkRegion[].dvs variables
// AS IF NO SORTING DONE
big--; // decrement to highest-used slot in array of N-values
for(WorkSize=c; WorkSize<x; WorkSize++)
{ test = WorkRegion[WorkSize].dvs;
a = 0; // location of lowest N in array
d = (S_32)big; // location of highest N in array
c = d >> 1; // Prepare to do a binary search from middle of the
// array
while (a < d)
{ if(test < is[c])
d = c - 1; // Set new upper bound to possible correct array
// item
else if(test > is[c])
a = c + 1;//set new lower bound to possible correct array item
else
break; // Well, well, test just happened to exactly match an
// item!
c = ((d - a) >> 1) + a; // Get new middle between a and d; (may
// equal untested a if d-a=1, but next loop then finds it or
} // makes a=c=d)
WorkRegion[WorkSize].dvs = ip[c]; // Use the array of primes to
// replace N
}
x=-4, y=0, z=1; // AND NOW TO REPLACE THE CONTROL PRIMES AGAIN!
// This code block works with 108 BYTES reserved at the end of the
// block of Key File data associated with the fetched variable,
// much like the 90 bytes previously used.
test = 0;
{ struct scratchpad sp; // One cool thing about C is that wherever
// there is a Left Brace, it is allowed to place a variable-
// declaration there. This can be handy to ensure that variables
// needed for a particular block of code (until the matching Right
// Brace) are available. BUT doing this also has a price. These
// variables are always placed in an area known as The Stack, and
// not only does it take extra time to put them there (and remove
// them when done), accessing them can also be slower than
// accessing global memory variables. More, such variable-
//declarations surrounded by code are not so obvious to the casual
// eye as declarations at the start of a function (or at top of
// prgram, global). It can sometimes be annoying to try to find
// them. THIS declaration is the only one of its kind in this
// program, partly so this comment could be written for the
// benefit of a novice, and partly because this block of code was
// copied from above, where WorkRegion[0] was specified. THEN
// WorkRegion[0] was unoccupied and available for temporary use;
// NOW it has been assigned a value that we want to retain. So a
// new scratchpad is needed -- so declared!
while((z == 1) && (y < 101)) // Prepare to walk "random" numbers
// grouping for values of N
{ // Below, as previously indicated elsewhere, we are about to
// SCRAMBLE bytes taken from the Key File, before using them to
// select prime numbers from COMPRESS.PRM. Here is a bit of
// extra sneakiness, where we semirandomly specify either
// Encryption or Decryption scrambling.
uc = (U_08)Divide(TypeSetter, 1); // Get one bit as Encryption/
//Decryption flag
if(uc == 1)
{ WhichWay[0] = 0; // Set for Encryption Algorithm
WhichWay[1] = 1; // Set for Encryption Algorithm
}
else
{ WhichWay[0] = 4; // Set for Decryption Algorithm
WhichWay[1] = -1; // Set for Decryption Algorithm
}
for(a=0; ((a<4)&&(y<83)); a++, y++)
{ uc = *ucp1++; // Fetch a byte from the Key File and increment
// pointer
uc = PseudoRandomize(uc); // "AFFECT" this byte, making it
// somewhat more "random".
#ifdef PROCESSOR_ENDIAN_TYPE_LITTLE
((U_08 *)(&test))[a] = uc;// Save the byte as part of a 32-bit
// number
#endif
#ifdef PROCESSOR_ENDIAN_TYPE_BIG
((U_08 *)(&test))[(3-a)] = uc; // save the byte as part of a
// 32-bit number
#endif
uc = *ucp1++; // Fetch another Key File byte and increment
// pointer
uc = PseudoRandomize(uc); // "AFFECT" this byte, making it
// somewhat more "random".
// Below, by taking the address of a variable and using that
// address as an array-pointer, we can set any number of bytes
// without referencing subunits of the variable (say a struct).
// HOWEVER, the order in which the bytes are put together, to
// make a 32-bit number, is a sure sign of a potential
// "Big-Endian/Little-Endian" conflict. BEWARE!
#ifdef PROCESSOR_ENDIAN_TYPE_LITTLE
((U_08 *)(&test))[a] |= uc; // OR-combine with last byte, as
// part of a 32-bit number
#endif
#ifdef PROCESSOR_ENDIAN_TYPE_BIG
((U_08 *)(&test))[(3-a)] |= uc; // OR-combine with last byte,
// as part of a 32-bit number
#endif // note using OR to combine bits to reduce chance of zero total
} // loop for 4 bytes of a 32-bit number
IndexToPrime(&test);//use Index to replace random N with a Prime
// Next, can't have half-period of 100,000 if Prime is less than
if(test > 200000) // 200,000
{ sp.dvd = sp.dvs = test; // copy test-Prime to tmp dividend &
// divisor
sp.dvd--; // set dividend to P-1
if(0xFFFFFFFF != Divide(&sp, 100020)) // Seek 100,020
// pseudorandom bits
{ WorkRegion[x] = sp; // Copy both Prime/Divisor and current
// Remainder/Dividend
// Note: Above test done with temporary sp to avoid
// affecting default values of LoadMaster, ModCounter,
// TypeSetter, and RandomSkip until we know that the new
// values pass the test
x++;//LoadMaster is set when x=-4; increment for ModCounter,
// TypeSetter and RandomSkip
if(x == 0)//When finished replacing the defaults of all four
// critical Control Primes,
z = 0;// set flag loop-exit; only other way is to fail 100
} // attempts to set the four.
else // bad prime! Had half-PERIOD of less than 100,000 digits
// in Base Two
{ y -= 7; // Back up to re-use last 7 of 8 bytes of those 108
ucp1 -= 7; // (NOTE that when these are again passed to
} } // PseudoRandomize(), very different results are likely.
else // bad prime! (must be larger than 200,000 to have a
// half-PERIOD of 100,000
{ y -= 7; // Back up to re-use last 7 of 8 bytes of those 108
ucp1 -= 7; // (NOTE that when these are again passed to
} // PseudoRandomize(), very different results are likely.
}
if(z == 1) // did not find four acceptable primes?
{ MessageBox(NULL, "QUITTING! Key File didn't contain suitable "
"data for picking one or\nmore of 4 critical "
"Control Primes. (Very rare error -- use "
"another Key File)",
"ERROR", MB_OK | MB_ICONSTOP);
// This is indeed expected to be VERY rare....
return(CRP_ERR_BD); // using 6 as code for Bad Data
} }
InvalidateRect(hwnd, &rect, 0); // make window update-able
PostMessage(hwnd, WM_PAINT, 0, 0); // Progress bar ends (show final
// count of loaded primes)
if(ProcessMessages()) // process ALL messages in the queue
return(CRP_WM_QUIT); // Sssume user abort if ProcessMessages()
// returned nonzero
return(0); // no error!
};
/*SPECIAL NOTE REGARDING THE LITTLE-ENDIAN AND BIG-ENDIAN CONTROVERSY:
The smallest unit of information is the bit, and the commonest next-
small unit is the byte (a group of 8 bits). Computer memory is
typically organized as a huge array of bytes; for example, a 32-bit
processor can "address" an array of 4,294,967,296 memory locations. A
minor dilemma arises in that to GIVE a 32-bit processor 32 bits of
data, four separate 8-bit bytes are required. The original author
of this document personally thinks that every memory address accessed
by a 32-bit processor should have 32 bits of data sitting there,
instead of 8, but that's a simplistic and controversial way to solve
the "endian" controversy. So, with the standard of only 8 bits per
address, the needed four bytes typically come from 4 adjacent/
consecutive addresses in the memory. One will hold the "Least
Significant" 8 bits -- values that can only range from 0 to 255. And
one byte will hold the "Most Significant" 8 bits -- values that range
from 16,777,216 to 1,073,741,824 (not counting the possibly negative
biggest bit) -- but which? More specifically, imagine four memory
address numbers 10,000 through 10,003 (computers always start counting
from Zero). Will address 10,000 hold the Least Significant Byte, and
10,003 hold the Most Significant Byte -- the Little-Endian way -- or
vice-versa, the Big-Endian way? Because both ways have some logic
behind them, both ways have been implemented in the computer industry,
despite being mutually incompatible.
What are those logical arguments, you ask? Well, Little-Endian
supporters like the simple match-up between data-magnitude and memory-
address. Memory address 10,000, the first and least-magnitude of the
four addresses, would hold the Least Significant Byte, as previously
described. On the other hand, the Big-Endian supporters look at the
CONTENTS of the memory in sequence, and for them, it makes more sense
for the Most Significant Byte to be first. Here is an example of
that; consider the number 1 Billion in 32 binary digits:
00111011100110101100101000000000. This is the conventional way to
represent a large-ish number in Base Two. Broken into a series of
bytes, the same data is 00111011 10011010 11001010 00000000. Suppose
these bytes were placed into the memory in Little Endian format.
Well, if you decided to "read" the contents of that memory in order,
then you would end up with the following sequence: 00000000 11001010
10011010 00111011. It's bad enough that binary is not easy to read,
but now sections of our 1 Billion are backwards (even while the bits
in the individual bytes are still "forwards"!)! Thus the Big-Endians
choose to put the Most Significant Byte in the Least Significant
memory address.
Heh, if "the convential way to represent a number" was REVERSED,
then in common ordinary Base Ten we would write 1 Billion as
000,000,000,1 -- and in binary a similar reversal is
00000000010100110101100111011100 -- so that is also a possible
solution to the controversy (the Little Endian way of putting sections
of the number into memory would be completely consistent). But is
such a change of Convention likely? NOT, any time soon! Anyway,
regardless of anyone's preferences, certain parts of this program do
need to be able to work properly, so that a Message encoded by a
Little Endian processor can be correctly decoded by a Big Endian
processor (or vice-versa). And that's the purpose for #define
PROCESSOR_ENDIAN_TYPE_LITTLE earlier in this program; it lets us use
#ifdef blocks to tell the compiler which sections of code need to be
compiled, to ensure that any Message data gets processed properly.
Should this program be compiled on a big-endian processor, that
#define statement is the ONLY thing in this entire program that must
be edited. (CAUTION -- that claim has not been tested! Other editing
may be needed if some mistake has been made!) */
// HERE is the main function that handles file extensions (including
// .CRP), opens files, works with the "Snoopercomputer/Cryptionite"
// strings, applies Encrypt/Decrypt algorithms via PseudoRandomize(),
// and deletes specified files (including old .CRP files) when done.
// Nonzero return values are error codes.
// Uses global struct affect *afflist --so no parameter needed
S_32 AffectFiles(void)
{ FILE *outfil; // In order to not delete any files until all
// processing is successful, need two FILE pointers here.
// Global dskfil will be used for the input-file.
S_32 a, b;
U_32 x=0, y, z;
char work[60], ext[50];
for(y=0, affitem=afflist, affnum=1; affnum<=affcou;
affnum++, affitem=affitem->next)
{ if(affitem == NULL)
{ sprintf(tmpstr, "QUITTING! Could not access internal "
"Files-to-Affect List!");
MessageBox(NULL, tmpstr, "ERROR", MB_OK | MB_ICONSTOP);
return(CRP_ERR_LL); // using 4 as code for Linked-List failure
}
z = y; // save for comparison
y += affitem->length; // accumulate lengths of files to process
if(z > y) // total rolled over 4 gigabytes?
x++; // carry to 64 bits
z = y;
y += affitem->skipno;//Also accumulate total wasted randomizations
// (large skips take time)
if(z > y)
x++; // Carry any rollover to 64 bits --
// TRY TO KEEP TOTAL WITHIN 100GB
} // Note minor flaw: Total does NOT account for "Snoopercomputer/
// Cryptionite" recognition strings. If completed loop then we know
// afflist is in good shape and we can walk it again without fear
// Now to divide a 64-bit number by 50, for the progress bar -- we
// want result to fit in 32 bits! (Perhaps I'm just being lazy
// here, and should change all the (++x == y) below to 64-bit stuff)
// BEST divide in Assembly Language, but cheap compilers often don't
// support asm stuff, so....
if(x >= 50) // if exceeded 200GB (how likely is this, anyway???)
y = 0xFFFFFFFF; // Set one Progress Bar increment after processing
// every 4GB, but program will keep running even after Progress
// Bar reaches its endpoint, because CRYPTION will try to finish
// all assigned work
else
{ y = (y >> 1) + ((x & 1) << 31); // First divide by two, via
// bit-shifts
x >>= 1; // below, only need to divide by 25 now
z = y; // save, just in case
y /= 25;
if(x > 0) // most of the time probably won't go farther than this
{ // NOTE prior 200GB test, and bit-shift, ensures that x is its
// own remainder-of-division-by-25
x <<= 16; // shift to High Half of 32 bits
x += (z >> 16); // Combine with High Half of former y
// (but make Low Half here)
y = (x / 25) << 16; // Get integer quotient and shift it to High
// Half of 32 bits
x = (x % 25) << 16; // Get remainder of division-by-25; move to
// High Half of 32 bits
x += (z & 0x0000FFFF); // Combine remainder with Low Quarter of
// original 64-bit value
y += (x / 25); // Combine shifted quotient with rest of computed
// quotient
} }
for(x=0, status = 400, affitem=afflist, affnum=1;
(affnum<=affcou); affnum++, affitem=affitem->next)
{ InvalidateRect(hwnd, &rect, 0); // make window update-able
PostMessage(hwnd, WM_PAINT, 0, 0); // Show progress bar and File-
// to-Affect Number info
if(ProcessMessages()) // process ALL messages in the queue
return(CRP_WM_QUIT); // assume user abort
for(a=affitem->skipno; a>0; a--) // Since skipno is supposed to be
// an irregular value, we gain some security by wasting an
{ PseudoRandomize(0);// irregular quantity of pseudorandomizations
if(++x == y) // NOTE x is NOT reset to zero at start of each
// File-to-Affect
{ status += 2; // Progress-bar-incrementation could occur any
// time, since x is constantly
if(status >= 500) // incremented with each pseudorandomization
status = 499; // make sure status stays within allowed range
InvalidateRect(hwnd, &rect, 0); // make window update-able
PostMessage(hwnd, WM_PAINT, 0, 0); // increment progress bar
if(ProcessMessages()) // process ALL messages in the queue
return(CRP_WM_QUIT); // assume user abort
x = 0; // reset counter for next progress bar increment
} }
if(affitem->algo == 'E') // check the Algorithm code for this
// particular file
{ WhichWay[0] = 0; // Set for Encryption Algorithm
WhichWay[1] = 1; // Set for Encryption Algorithm
}
else
{ WhichWay[0] = 4; // Set for Decryption Algorithm
WhichWay[1] = -1; // Set for Decryption Algorithm
}
dskfil = fopen(affitem->pathname, "rb"); // Read Binary
if(dskfil == NULL)
{ sprintf(tmpstr, "QUITTING! Could not open File-to-Affect:\n%s",
affitem->pathname);
MessageBox(NULL, tmpstr, "ERROR", MB_OK | MB_ICONSTOP);
return(CRP_ERR_OF); // using 2 as code for Opening File error
}
sprintf(work, ".\\TMP%d.CRP", affnum); // Create temporary file in
// executable's directory
outfil = fopen(work, "wb"); // Write Binary
if(outfil == NULL)
{ fclose(dskfil);
sprintf(tmpstr,
"QUITTING! Could not open Temporary Work File:\n%s",
work);
MessageBox(NULL, tmpstr, "ERROR", MB_OK | MB_ICONSTOP);
return(CRP_ERR_OF); // using 2 as code for Opening File error
}
ext[0] = '\0'; // initialize a blank file extension
ucp1 = ucMemBlk1; // reset this pointer to start of file buffer
z = min(200, affitem->length); // Set z appropriately, in case of
// very short file
if(z != fread(ucp1, 1, z, dskfil)) // Get file's start, perhaps
// only a Previously-Affected indicator
{ sprintf(tmpstr,
"QUITTING! Could Not Load Initial Part of File:\n%s",
affitem->pathname);
MessageBox(NULL, tmpstr, "ERROR", MB_OK | MB_ICONSTOP);
fclose(dskfil);
fclose(outfil);
return(CRP_ERR_FR); // using 3 as code for File Read error
}
// Below, check to see if file has previously been Affected, and
// do stuff if HASN'T
if(strncmp((char *)ucp1, "DATA PROTECTED FROM SNOOPERCOMPUTERS "
"WITH CRYPTIONITE: ", 56))
{ if(affitem->dotloc > -1) // Did the file actually have an
// Extension? Below, copy it if so
strcpy(ext, &(affitem->pathname[(affitem->dotloc + 1)]));
sprintf(tmpstr, "DATA PROTECTED FROM SNOOPERCOMPUTERS WITH CRYP"
"TIONITE: Data Protected From Snoopercomputers"
" With Cryptionite: The Original File Extensio"
"n is %s. ", ext);
// put in main ID string (replace %s)
// Purpose of the period after %s is to be a marker indicating
// END of File Extension. The original dotloc identification of
// a file extension never includes a period.
a = strlen(tmpstr); // get length of just-constructed string
memmove((ucp1 + a), ucp1, z); // Move the initial file data
// deeper into ucMemBlk1
strncpy((char *)ucp1, tmpstr, a); // Insert with no string-
// terminator
z += a; // get total length to put in outfil after Affecting it
} // Whether had it already, or just added the identifier string,
// Affecting starts at ucp1[56]
strcpy(ext, ".CRP"); // Set to CRYPTION-file extension, even if
// already was .CRP
ucp1 += 56; // Pointer arithmetic: skip DATA PROTECTED ...
// (capitalized text)
for(a=56; a<(int)z; a++) // From Data Protected to end of
// loaded start-of-file
{ *ucp1++ = PseudoRandomize(*ucp1); // Affect appropriate part of
// this block of data
if(y < 10000)
y = 12000;
if(++x == y) // NOTE x is NOT reset to zero at start of each
// File-to-Affect
{ status += 2; // Since x increments across files, progress-bar-
// incrementation could occur any time.
if(status >= 500)
status = 499; // make sure status stays within allowed range
InvalidateRect(hwnd, &rect, 0); // make window update-able
PostMessage(hwnd, WM_PAINT, 0, 0); // increment progress bar
if(ProcessMessages()) // process ALL messages in the queue
{ fclose(dskfil);
fclose(outfil);
return(CRP_WM_QUIT); // assume user abort
}
x = 0; // reset counter for next progress bar increment
} }
// Below, check for RESTORED file (well, first part of it might
// now be restored)
if(!strncmp((char *)ucMemBlk1, "DATA PROTECTED FROM SNOOPERCOMPUT"
"ERS WITH CRYPTIONITE: Data Prote"
"cted From Snoopercomputers With C"
"ryptionite: The Original File Ex"
"tension is ", 143)) // Remember,
// Extension in text is followed by a marker-period and two spaces
{ for(a=143, b=1; ('.'!=(ext[b]=ucMemBlk1[a])); a++, b++);// Fetch
// Original Extension
// Prior line is self-contained loop, exits after copying period
// that follows Extension
ext[b] = '\0'; // replace marker-period with null terminator
ext[0] = '.';//prepend Extension-separator (note b started at 1)
if(affitem->dotloc == -1) // if no Extension in given filename
affitem->dotloc = (S_16)strlen(affitem->pathname); // Set this
// to where it would have been
ucp1 = ucMemBlk1 + 145 + strlen(ext); // Get buffer location of
// start of original file data
// Note about above 145: length up to Original File Extension is
// 143, and 3 characters follow it (a period and two spaces), so
// one might at first think that the 145 should be 146. However,
// variable ext includes an initial dot that was not part of the
// stored Extension and so we have to account for it in the total
// length (most easily by specifying 145).
z -= (ucp1 - ucMemBlk1); // Get length of original data after
// CRYPTION identifier removed
memmove(ucMemBlk1, ucp1, z); // Remove the CRYPTION identifier;
// this is why we sought to load 200 bytes from start of file;
// whole identifier is there
} // Next line either restores original file extension or sets it
// to .CRP
strcpy(affitem->newname, affitem->pathname); // copy current name
strcpy(&(affitem->newname[affitem->dotloc]), ext); // Overwrite or
// append Extension
if(z != fwrite(ucMemBlk1, 1, z, outfil)) // Save the initial data
// to new (temporary) file
{ sprintf(tmpstr,
"QUITTING! Could Not Save Initial Part of Work-File:\n%s",
work);
MessageBox(NULL, tmpstr, "ERROR", MB_OK | MB_ICONSTOP);
fclose(dskfil);
fclose(outfil);
return(CRP_ERR_FW); // using 7 as code for File Write error
} // Below, check to see if file has previously been Affected, and
// do stuff if HASN'T
while(!feof(dskfil)) // Main loop to Affect data until end-of-file
// reached
{ z = fread(ucMemBlk1, 1, 524288, dskfil); // Get huge chunk of
// file, several whole clusters
if((z != 524288) && !feof(dskfil)) // If file not ended and
// didn't fetch what we wanted
{ sprintf(tmpstr, "QUITTING! Could Not Load Part of File:\n%s",
affitem->pathname);
MessageBox(NULL, tmpstr, "ERROR", MB_OK | MB_ICONSTOP);
fclose(dskfil);
fclose(outfil);
return(CRP_ERR_FR); // using 3 as code for File Read error
}
ucp1 = ucMemBlk1; // reset moving pointer
for(a=0; a<(int)z; a++)// From start to end of however-much data
// is in ucMemBlk1
{ *ucp1++ = PseudoRandomize(*ucp1);// Affect appropriate part of
// this block of data
if(++x == y) // NOTE x is NOT reset to zero at start of each
// File-to-Affect
{ status += 2; // Since x increments across files, progress-
// bar-incrementation could occur any time
if(status >= 500)
status = 499;//make sure status stays within allowed range
InvalidateRect(hwnd, &rect, 0); // make window update-able
PostMessage(hwnd, WM_PAINT, 0, 0); // increment progress bar
if(ProcessMessages()) // process ALL messages in the queue
{ fclose(dskfil);
fclose(outfil);
return(CRP_WM_QUIT); // assume user abort
}
x = 0; // reset counter for next progress bar increment
} } // loop til half-a-megabyte processed, or tail end of file
if(z != fwrite(ucMemBlk1, 1, z, outfil)) // Save the processed
// block
{ sprintf(tmpstr,
"QUITTING! Could Not Save Part of Work-File:\n%s",
work);
MessageBox(NULL, tmpstr, "ERROR", MB_OK | MB_ICONSTOP);
fclose(dskfil);
fclose(outfil);
return(CRP_ERR_FW); // using 7 as code for File Write error
} } // loop til done Affecting this file
fclose(dskfil); // Close the current files
fclose(outfil); // in preparation for next loop
rename(work, affitem->newname); // MUST MOVE/RENAME TMP#.CRP FILE
// TO ORIGINAL DIRECTORY NOW. MUST BE AVAILABLE IN CASE ADDITIONAL
// PROCESSING OF THE FILE IS SPECIFIED IN ONE OF THE NEXT LINKED-
// LIST ITEMS. THIS WILL OVERWRITE A PRIOR .CRP FILE IF CURRENT IS
// STILL SCRAMBLED. (But overwriting was described long ago in
// this Document.)
} // loop til done walking linked list, and Affecting all
// specified files
// NOW DELETE SPECIFIED ORIGINAL FILES
if(del1 == 'Y')
remove(keyfile);
for(affitem=afflist, affnum = 1; affnum<=affcou;
affnum++, affitem=affitem->next)
{ if((affitem->kill == 'Y') &&
strcmp(affitem->pathname, affitem->newname)) // If both names
// are the same, then they WILL both have .CRP extensions.
// Above, the rename() has already overwritten the old .CRP
// file, and we certainly don't want to delete the new one.
remove(affitem->pathname); // DO remove() old file if 'Y' and
// filename has changed. In this case either new file is .CRP
} // or old file is restored.
return(0); // no error!
};
// END OF SOURCE CODE FOR CRYPTION.C,
// THE ORIGINAL WORK DONE BY VERNON NEMITZ
/* Final Remarks:
Some people may think a powerful encryption program to be too valuable
to give away, even if only in the shared manner as is described at the
start of this program source file. But -- is this really and truly a
powerful encryption program? The algorithms MUST be published, in
order for experts to make that determination. Well, that means anyone
who learns the algorithms will be in a position to write an equivalent
competing program. It really isn't a terribly complicated thing,
after all. OK, well, certainly algorithms are allowed to be patented,
which would secure some protection against such competition for a
reasonable time (think about the well-known RSA algorithm, which
allowed one company to grow into prominence while enjoying such
protection). However, recall again that this algorithm has not been
proved! -- and patents can be expensive. It would be kind of silly to
invest all that money in something that could turn out to have a
fundamental flaw. So why not give it away, with no warranty of any
kind? Risk-takers who gamble on its security can at least be assured
that their data will be protected UNTIL such a flaw is discovered --
which may take a while. Perhaps even a very long while...especially
after others have a chance of improving the algorighm.
-- Vernon Nemitz -- http://www.nemitz.net/vernon/vernon.htm */
|
|