'-------------------------------------------------------------------------------
/*
'-------------------------------------------------------------------------------

'PROGRAM = CRYPT.EBA
'EB CONSOLE PROGRAM
'ENCRYPTS AND DECRYPTS FILES.

'-------------------------------------------------------------------------------
'START PROGRAM OPERATION
'-------------------------------------------------------------------------------

All you need to do in order to try the program, is to compile it as type
"Console EXE", and run it. The program will ask you for the three items
of input it requires.

The program functions by using a pseudo-random number generator.

(The program only reads files to be encrypted or decrypted, it does not
alter them. Instead, it writes new files.)

Let's refer to the random number generator, as the "RNG".

Say, you want to encrypt a file named "SECRET.XYZ" (the program doesn't
care about the file type, or what the file name is).

When the program starts, it will first ask you for a pass code string.
That string is used to seed the RNG.

Say, for the pass code string, you enter, "down by the river" (without
the quotation marks).

Next, the program will ask you for the name of the file to be encrypted
or decrypted. You will enter, "SECRET.XYZ" (without the quotation marks).
(Note that if the file is in the current directory, then you only need
to enter its name. Otherwise, you must enter the entire path.)

Next, the program will ask you to choose whether you want to encrypt or
decrypt the file. You will choose, "encrypt".

Now, the program will do the actual work. It will open "SECRET.XYZ",
copy it into an array, and close it. Then, it will generate the
encrypted file, "SECRET.XYZ.E" (the program adds the ".E" suffix, to
indicate that the file is the encrypted version).

Next, if you want, you can open and look at the contents of
"SECRET.XYZ.E". It should bear no resemblance to "SECRET.XYZ".

After "SECRET.XYZ.E" is generated, the program will give you the
opportunity to restart it, or to quit.

Let's say, that now you want to decrypt "SECRET.XYZ.E" (you can do it by
either restarting the program, or by running it again).

When the program asks you for the pass code string, you would again
enter "down by the river".

When the program asks you for the file name, you would enter,
"SECRET.XYZ.E".

When the program asks you whether you want to encrypt, or decrypt, you
would choose, "decrypt".

Now, the program will open "SECRET.XYZ.E", copy it into an array, and
close it. Then, it will generate the decrypted file, "SECRET.XYZ.E.D"
(the program adds the ".D" suffix, to indicate that the file is the
decrypted version).

If you check, the contents of "SECRET.XYZ.E.D", should be identical to
the contents of "SECRET.XYZ".

'-------------------------------------------------------------------------------
'END PROGRAM OPERATION
'-------------------------------------------------------------------------------
'START PROGRAM EXPLANATION
'-------------------------------------------------------------------------------

*** FILE ENCRYPTION ***

Let's call the contents of the file to be encrypted, the "message". When
the file is read, the message is copied into the MESSAGEARRAY[].

The program uses the RNG to make a new ASCII table. You know that the
ASCII table has 256 values, 0 - 255. It is therefore able to represent
256 different characters. The program makes an array,

INT NEWASCIITABLE[256]

So, the indexes of NEWASCIITABLE[] are, 0 - 255. It then uses the RNG to
fill the array with the values 0 - 255, in a random order. So,
NEWASCIITABLE[] will contain all of the values, 0,1,2,3, .. ,255. The
program makes sure that no value is equal to its index. For instance, it
will not allow NEWASCIITABLE[65] to equal 65.

Then, for each CHAR, "C", in the MESSAGEARRAY[], it substitutes
NEWASCIITABLE[C], for C. (The program actually uses an INT, "I",
substitute value for C, because arrays cannot be indexed with CHARs.)

Here is the actual subroutine:

SUB ENCRYPTMESSAGE()
INT I, J
IF CRYPT = DECRYPT THEN RETURN
FOR I = 1 TO ENCRYPTMESSAGELENGTH
J = #<CHAR>MESSAGEARRAY[I]
#<CHAR>MESSAGEARRAY[I] = NEWASCIITABLE[J]
NEXT I
ENDSUB

Therefore, after ENCRYTMESSAGE() executes, the character set of the
message (MESSAGEARRAY[]), will have been replaced with a completely
different character set. Every character in the original message, will
be replaced by another character.

For instance, maybe,

'a' -> '$'
'b' -> 'f'
'c' -> '+'
'.' -> 'Q'

etc.

There are two arrays associated with with writing an encrypted file, the
MESSAGEARRAY[], and the DOCUMENTARRAY[]. Both, are one-dimensional
arrays, of type CHAR. The contents of MESSAGEARRAY[], are inserted into
DOCUMENTARRAY[], and then the contents of DOCUMENTARRAY[], are written
to, (become), the encrypted file.

There are two CONSTs in the program, whose values can be altered by the
user (when they are changed, the program must be re-compiled). Here they
are, as I have set them in the program.

CONST JUNKMESSAGERATIO = 1
CONST EXTRAJUNKBYTES = 1303

The reason that these two constants exist, is because they permit the
sizes (lengths of the index ranges) of MESSAGEARRAY[] and
DOCUMENTARRAY[], to be different. In the program, the size of
MESSAGEARRAY[], must be less than or equal to that of DOCUMENTARRAY[]
(because MESSAGEARRAY[] is inserted into DOCUMENTARRAY[]).

(Actually, MESSAGEARRAY[] and DOCUMENTARRAY[] are dynamic, because the
size of the file to be encrypted, is unknown during program compilation.
But, just to simplify showing what I mean, let's assume here, that they
are static.)

Let's say we are encrypting the file, "SECRET.XYZ". Let's say its
length is 3714 bytes. The contents of SECRET.XYZ will be copied into
MESSAGEARRAY[]. The length of MESSAGEARRAY[] will be 3715, i.e., its
index range will be 0 - 3714 (the extra byte is because I like to start
indexing from 1, not 0; I don't use MESSAGEARRAY[0]).

Then, DOCUMENTARRAY[] must be at least 3715 bytes (remember that 1 CHAR,
requires 1 byte). But, it may in fact be bigger. That is where the two
constants mentioned above are used.

Both of the constants, JUNKMESSAGERATIO, and EXTRAJUNKBYTES, should be
non-negative INTs. The program enforces this, by using the constants
indirectly. There are two global variables,

INT JMR
INT EJB

There are two lines in the program,

JMR = ABS((JUNKMESSAGERATIO))
EJB = ABS((EXTRAJUNKBYTES))

So, JMR and EJB are INTs, and are non-negative. They are the regulated
versions of JUNKMESSAGERATIO and EXTRAJUNKBYTES, and are what the
program uses in its calculations.

There is a line in the program, which determines the document length,

ENCRYPTDOCUMENTLENGTH = ENCRYPTMESSAGELENGTH * (JMR + 1) + EJB

Using the numbers from above, for SECRET.XYZ, the document length will
be

ENCRYPTDOCUMENTLENGTH = 3714 * (1 + 1) + 1303, = 7731 bytes.

(Like the MESSAGEARRAY[], the DOCUMENTARRAY[] will be dimensioned to have
1 extra byte, so that indexing can begin at 1 instead of 0.)

So now we have a MESSAGEARRAY[] of 3714 CHARs, to be inserted into a
DOCUMENTARRAY[] of 7731 CHARs.

Now what happens, is that all 7731 locations in DOCUMENTARRAY[] are
filled with random CHARs, using the RNG.

Remember, that the character set of MESSAGEARRAY[] has already been
replaced using NEWASCIITABLE[], as described in the previous section.

Now, again using the RNG, the contents of MESSAGEARRAY[] are inserted
into DOCUMENTARRAY[], in random locations (actually, the DOCUMENTARRAY[]
values of locations determined by the RNG, are overwritten).

We now have our finished DOCUMENTARRAY[] which is written to the
encryption file.

In the case we are describing, of the 7731 CHARs in DOCUMENTARRAY[],
3714 will comprise the message, and 4017 will be "junk", like
white-noise.

Additionally, the 3714 message CHARs will be distributed in random
order, and in random locations.

Additionally, the message CHARs will have been replaced by a different
character set.

To finish this section, let's mention the intended meanings of
JUNKMESSAGERATIO and EXTRAJUNKBYTES.

JUNKMESSAGERATIO, means the ratio of junk CHARs to message CHARs, in
DOCUMENTARRAY[].

For instance, if EXTRAJUNKBYTES = 0, and JUNKMESSAGERATIO = 7, then
within DOCUMENTARRAY[], for every message CHAR, there will be 7 junk
CHARs.

The primary purpose of EXTRAJUNKBYTES, is for the instances when the
message to be encrypted is very small.

Say, for instance, JUNKMESSAGERATIO = 1, EXTRAJUNKBYTES = 0, and the
length of the message is only 1 CHAR. In that case, DOCUMENTARRAY[] will
only contain 2 CHARs. To me, this circumstance does not seem like very
good encryption. But, if EXTRAJUNKBYTES = 657, for instance, then
DOCUMENTARRAY[] will contain 659 CHARs, which maybe is better.

My guess is that the bigger JUNKMESSAGERATIO and EXTRAJUNKBYTES are, the
harder it would be for someone to somehow decrypt the message.

Notice that if JUNKMESSAGERATIO = 0, and EXTRAJUNKBYTES = 0, then
DOCUMENTARRAY[] and MESSAGEARRAY[] will be the same size; there will be
no junk CHARs in DOCUMENTARRAY[].

*** FILE DECRYPTION ***

In order to decrypt a file, you need the same pass code string with
which the file was encrypted. Also, JUNKMESSAGERATIO and EXTRAJUNKBYTES
must have the same values as for the encryption.

Decryption works, because the RNG generates the same sequence of numbers,
when started using the same seed.

Decryption uses the RNG to undo what was done during encryption, and
writes the results to the decryption file.

'-------------------------------------------------------------------------------
'END PROGRAM EXPLANATION
'-------------------------------------------------------------------------------
'START MISCELLANEOUS INFORMATION
'-------------------------------------------------------------------------------

1.

I had one problem in writing the program, which I could not fix.

The program has three items of input, the pass code string, the input
file name, and the execution mode.

After the user inputs all three items, he is shown the values of all
three items together, and asked to verify that they are all correct. As
the program is currently constructed, if one of the items is incorrect,
then the user must re-enter all three items. Then, he is again shown the
values of all three items together, and asked to verify that they are
correct. This cycle continues, until the user makes the verification.

Originally, during the verification, if the value of one of the items
was incorrect, the user could indicate that item, and then be given the
opportunity to change the value of only that item.

But, I could not make the original process function properly.

Below, I have put a partial simplified version of the code, that I could
not get to work correctly. When the program (as shown below) begins, the
value of the variable "VERIFY", is FALSE. That is so that the first time
through, the user will be asked to enter all three items of input. When
the function VERIFYALL() executes, the value of VERIFY, becomes TRUE.

Within VERIFYALL(), if the user wants to change the value of one of the
items, he presses the key corresponding to that item's number. Then,
using a GOTO statement, execution should jump out of the function, and
back to the location of the corresponding label, within the main
program.

So, for instance, if the user wants to change the value of the pass code
string, he presses key, "1". Then, execution jumps to LABEL P1. The
function, GETPASSCODE(), is called again. Then, because VERIFY is now
TRUE, execution jumps to LABEL P4, and VERIFYALL() is called again. Now,
suppose the user wants to change the value of the execution mode, so he
presses key, "3". Then execution jumps to LABEL P3. The function,
GETEXECUTIONMODE() is called again. Then, the next statement after
GETEXECUTIONMODE, is VERIFYALL(), so it called once more. The dilemma is
that, once the user has jumped from VERIFYALL() back to the main program
in order to change an item, he will be stuck forever jumping back and
forth from VERIFYALL() to the main program. When he is in VERIFYALL()
and decides that all of the input is now correct, and so presses a key
other than "1", "2", or "3", instead of execution continuing to the end
of VERIFYALL(), and then returning to the main program, and resuming at
the first statement after the VERIFYALL() statement, execution jumps to
the LABEL in the main program, of the GOTO statement that was last
executed from VERIFYALL().

I also tried having VERIFYALL() return an INT value to the main program,
and then executing a GOTO statement from the main program, depending on
the value that VERIFYALL() returned.  But the problem was still the
same. If the user chose to change a value during the first time
VERIFYALL() executed, then he could never reach the statement in the
main program after VERIFYALL(). When he tried to, execution would again
jump to the main program LABEL of the last GOTO statement executed.


ENUM CRYPTTYPE
ENCRYPT
DECRYPT
ENDENUM

INT VERIFY = FALSE
LABEL P1
GETPASSCODE()
IF VERIFY THEN GOTO P4
LABEL P2
GETINPUTFILE()
IF VERIFY THEN GOTO P4
LABEL P3
GETEXECUTIONMODE()
LABEL P4
VERIFYALL()
END

SUB VERIFYALL()
STRING S
VERIFY = TRUE
IF CRYPT = ENCRYPT
S = "encryption"
ELSE
S  = "decryption"
ENDIF
PRINT "Verify input."
PRINT
PRINT "1) pass code string = ", PASSCODESTRING
PRINT "2) input file       = ", INPUTFILENAME
PRINT "3) execution mode   = ", S
PRINT
PRINT
PRINT "To change a value, press the key corresponding to its number."
PRINT "If all the values are correct, press (almost) any other key, to continue."
PRINT
WHILE TRUE
S = INKEY$
IF S <> "" THEN GOTO VA
ENDWHILE
LABEL VA
IF S = "1" THEN GOTO P1
IF S = "2" THEN GOTO P2
IF S = "3" THEN GOTO P3
ENDSUB

--------------------------------------------------------------------

2.

I think it is true, that the program will encrypt any type of file. But,
I don't think it is true, that it will encrypt any size file. The arrays
that the program uses, are all dynamic (POINTERs).

If the file is too big for the memory available to be allocated, then,
in my opinion, what will happen, will not be good.

When I compile the following program, as type "Console EXE", and run it,
the output is, "1234.00". The memory allocation would be 800,000,000,000
(eight hundred billion) bytes. The RAM on my machine, is only 2
gigabytes (2,147,483,648 bytes), approximately two billion bytes.

POINTER P
STRING S
INT ONEHUNDREDBILLION = 100000000000
P = NEW(DOUBLE, ONEHUNDREDBILLION)
IF P = NULL
PRINT "NULL"
END
ENDIF
#<DOUBLE>P[ONEHUNDREDBILLION - 1] = 1234
PRINT #<DOUBLE>P[ONEHUNDREDBILLION - 1]
INPUT S
END

--------------------------------------------------------------------

3.

Say, you encrypt a file on Machine A, and you decrypt it on Machine B.
Say, that Machines A and B have different versions of Windows, or
different processors. In that case, will the file decrypt correctly? My
answer is, "I don't know for certain.".

--------------------------------------------------------------------

4.

Say, I encrypt the file, "HELLO.TXT". The output of the encryption, will
be the file, "HELLO.TXT.E". Say, I then want to decrypt. In the program,
for the input file, I would then enter, "HELLO.TXT.E". And the output of
the decryption, would be the file, "HELLO.TXT.E.D". So, although I
started the process with only one file, "HELLO.TXT", after I have
finished encrypting and decrypting, I will have three files,
"HELLO.TXT", "HELLO.TXT.E", and "HELLO.TXT.E.D".

Therefore, it is actually incorrect to say I am encrypting or decrypting
a file. In actuality, I am creating a new file, which is either an
encrypted or decrypted version of the original file. (However, the
contents of the decrypted version, should be identical to the contents
of the original file.)

--------------------------------------------------------------------

5.

The program outputs encrypted files with the suffix, ".E", and decrypted
files with the suffix, ".D". It does that to differentiate the original
file, the encrypted copy, and the decrypted copy. But, you can then
change the file names to anything you want. For instance, if you change
the name of the encrypted file, "HELLO.TXT.E", to "BLOB", you will still
be able to decrypt "BLOB". In other words, the program does not check
the file type before it encrypts or decrypts.

--------------------------------------------------------------------

6.

After you encrypt a file, if you want to, you can open and look in the
encrypted copy, to see if it resembles the original file. Hopefully, it
won't.

--------------------------------------------------------------------

7.

If you encrypt a file with one pass code string, and then decrypt it
with another, then (obviously) the decryption will be incorrect.

If you encrypt a file, and then change either of the constants
JUNKMESSAGERATIO, or EXTRAJUNKBYTES, you will not be able to correctly
decrypt the file (and you may not even be able to incorrectly decrypt
it).

--------------------------------------------------------------------

8.

The constants, JUNKMESSAGERATIO and EXTRAJUNKBYTES, appear just below.
They are the only parameters which are intended to be potentially
altered. Hopefully, everything else is OK, as it is.

--------------------------------------------------------------------

9.

For super strength security, try encrypting a file twice!

'-------------------------------------------------------------------------------
'END MISCELLANEOUS INFORMATION
'-------------------------------------------------------------------------------
*/
'-------------------------------------------------------------------------------
'START GLOBAL CONSTANTS
'-------------------------------------------------------------------------------

'BOTH OF THESE CONSTANTS MUST BE NON-NEGATIVE INTEGERS.
'IF YOU ENCRYPT A FILE, AND THEN CHANGE EITHER OF THEM,
'YOU WILL NOT BE ABLE TO (PROPERLY) DECRYPT IT.

CONST JUNKMESSAGERATIO = 1
CONST EXTRAJUNKBYTES   = 1303

'-------------------------------------------------------------------------------
'END GLOBAL CONSTANTS
'-------------------------------------------------------------------------------
'START RANDOM NUMBER GENERATOR STATIC VARIABLES
'-------------------------------------------------------------------------------

CONST IM1       = 2147483563
CONST IM2       = 2147483399
CONST IMM1      = IM1 - 1
CONST IA1       = 40014
CONST IA2       = 40692
CONST IQ1       = 53668
CONST IQ2       = 52774
CONST IR1       = 12211
CONST IR2       = 3791
CONST NTAB      = 32
CONST NDIV      = 1 + IMM1 / NTAB
CONST DEPS      = 1.0E-16
INT   RAN2SEED
INT   IDUM2
INT   IY
CHAR  RAN2FLAG
INT   IV[NTAB]

'-------------------------------------------------------------------------------
'END RANDOM NUMBER GENERATOR STATIC VARIABLES
'-------------------------------------------------------------------------------
'START GLOBAL ENUMERATIONS
'-------------------------------------------------------------------------------

ENUM CRYPTTYPE
ENCRYPT
DECRYPT
ENDENUM

ENUM MESSAGETYPE
MIFN
MPCS
MOM
MDONE
ENDENUM

ENUM LOCATIONTYPE
LOM
LGPCS
LIPCS
LGIFN
LIIFN
LEOD
LCD
LCE
LVA
ENDENUM

ENUM REPLYTYPE
YES
NO
GOOD
BAD
OK
ERROR
QUIT
RESTART
ENDENUM

'-------------------------------------------------------------------------------
'END GLOBAL ENUMERATIONS
'-------------------------------------------------------------------------------
'START GLOBAL VARIABLES
'-------------------------------------------------------------------------------

STRING  PASSCODESTRING
INT     PASSCODE
INT     NEWASCIITABLE[256]
POINTER INPUTFILEARRAY
POINTER MESSAGEARRAY
POINTER DOCUMENTARRAY
POINTER DOCUMENTFLAGARRAY
INT     ENCRYPTMESSAGELENGTH
INT     ENCRYPTDOCUMENTLENGTH
INT     DECRYPTMESSAGELENGTH
INT     JMR
INT     EJB
BFILE   INFILE
BFILE   OUTFILE
STRING  INPUTFILENAME
STRING  OUTPUTFILENAME
INT     INPUTFILELENGTH
INT     ANSWER

CRYPTTYPE    CRYPT
MESSAGETYPE  MESSAGE
LOCATIONTYPE LOCATION
REPLYTYPE    REPLY

'-------------------------------------------------------------------------------
'END GLOBAL VARIABLES
'-------------------------------------------------------------------------------
'START PROGRAM
'-------------------------------------------------------------------------------

OPENCONSOLE

LABEL PRESTART
LABEL POM

SETGLOBALVARIABLES()
DELETEMEMORY()
CLOSEINPUTFILE()

CLS
DEFAULTCOLOR()
PUTSEPARATOR()

OPENINGMESSAGE()
BRANCH()

LABEL PGPCS
PUTSEPARATOR()

GETPASSCODESTRING()
PUTSEPARATOR()
VERIFYPASSCODESTRING()
BRANCH()

CALCPASSCODE(PASSCODESTRING)
IF REPLY = BAD
PUTSEPARATOR()
INVALIDPASSCODESTRING()
BRANCH()
ENDIF
SETRAN2SEED(PASSCODE)

LABEL PGIFN
PUTSEPARATOR()
GETINPUTFILENAME()
PUTSEPARATOR()
VERIFYINPUTFILENAME()
BRANCH()
OPENINPUTFILE()
IF REPLY = ERROR
PUTSEPARATOR()
INVALIDINPUTFILE()
BRANCH()
ENDIF

LABEL PEOD
PUTSEPARATOR()
ENCRYPTORDECRYPT()
PUTSEPARATOR()
VERIFYENCRYPTORDECRYPT()
BRANCH()

PUTSEPARATOR()
VERIFYALL()
BRANCH()

PUTSEPARATOR()
CALCJMR()
CALCEJB()
GETINPUTFILELENGTH()
SETENCRYPTMESSAGELENGTH()
CALCENCRYPTDOCUMENTLENGTH()
SETDECRYPTMESSAGELENGTH()
ANSWER = CHECKIFCANENCRYPT()
IF ANSWER = FALSE THEN CANNOTENCRYPT()
ANSWER = CHECKIFCANDECRYPT()
IF ANSWER = FALSE THEN CANNOTDECRYPT()
BRANCH()

LASTCHANCE()
BRANCH()
PUTSEPARATOR()
WORKING()

SETOUTPUTFILENAME()
ALLOCATEDOCUMENTARRAY()
ALLOCATEMESSAGEARRAY()
READINPUTFILE()
CLOSEINPUTFILE()
FILLENCRYPTMESSAGEARRAY()
FILLNEWASCIITABLE()
ENCRYPTMESSAGE()
FILLDOCUMENTARRAY()
INSERTMESSAGEINDOCUMENT()
DUMPDOCUMENTARRAYRANDOMVALUES()
EXTRACTMESSAGEFROMDOCUMENT()
DECRYPTMESSAGE()

OPENOUTPUTFILE()
WRITEOUTPUTFILE()
CLOSEOUTPUTFILE()

PUTSEPARATOR()
DONE()
BRANCH()

LABEL PQUIT
CLOSEINPUTFILE()
CLOSECONSOLE

END

'-------------------------------------------------------------------------------
'END PROGRAM
'-------------------------------------------------------------------------------
'START I/0 FUNCTIONS
'-------------------------------------------------------------------------------

SUB OPENINGMESSAGE()
LOCATION = LOM
MESSAGE = MOM
PRINTMESSAGE()
YESNOQUITRESTART()
ENDSUB

'-------------------------------------------------------------------------------

SUB GETPASSCODESTRING()
LOCATION = LGPCS
MESSAGE = MPCS
PRINTMESSAGE()
INPUT PASSCODESTRING
ENDSUB

'-------------------------------------------------------------------------------

SUB VERIFYPASSCODESTRING()
PRINT "Your pass code string is \"",PASSCODESTRING,"\"?"
YESNOQUITRESTART()
ENDSUB

'-------------------------------------------------------------------------------

SUB INVALIDPASSCODESTRING()
LOCATION = LIPCS
ERRORCOLOR()
PRINT "Invalid pass code string."
PRINT "Do you want to continue?"
DEFAULTCOLOR()
YESNOQUITRESTART()
ENDSUB

'-------------------------------------------------------------------------------

SUB GETINPUTFILENAME()
LOCATION  = LGIFN
MESSAGE = MIFN
PRINTMESSAGE()
INPUT INPUTFILENAME
ENDSUB

'-------------------------------------------------------------------------------

SUB VERIFYINPUTFILENAME()
PRINT "The input file is \"",INPUTFILENAME,"\"?"
YESNOQUITRESTART()
ENDSUB

'-------------------------------------------------------------------------------

SUB INVALIDINPUTFILE()
LOCATION = LIIFN
ERRORCOLOR()
PRINT "Cannot open input file."
PRINT "Do you want to continue?"
DEFAULTCOLOR()
YESNOQUITRESTART()
ENDSUB

'-------------------------------------------------------------------------------

SUB ENCRYPTORDECRYPT()
STRING S

LOCATION = LEOD
PRINT "Do you want to encrypt or decrypt?"
PRINT
PRINT "Press a key."
PRINT "\"e\" = encrypt"
PRINT "\"d\" = decrypt"
PRINT
LABEL EOD1
WHILE TRUE
S = INKEY$
IF S <> "" THEN GOTO EOD2
ENDWHILE
LABEL EOD2
S = UCASE$(S)
IF S = "E"
CRYPT = ENCRYPT
RETURN
ENDIF
IF S = "D"
CRYPT = DECRYPT
RETURN
ENDIF
GOTO EOD1
ENDSUB

'-------------------------------------------------------------------------------

SUB VERIFYENCRYPTORDECRYPT()
IF CRYPT = ENCRYPT
PRINT "You want to encrypt?"
ELSE
PRINT "You want to decrypt?"
ENDIF
YESNOQUITRESTART()
ENDSUB

'-------------------------------------------------------------------------------

SUB VERIFYALL()
LOCATION = LVA
IF CRYPT = ENCRYPT
S = "encryption"
ELSE
S  = "decryption"
ENDIF
PRINT "Verify input."
PRINT
PRINT "pass code string = ", PASSCODESTRING
PRINT "input file       = ", INPUTFILENAME
PRINT "execution mode   = ", S
PRINT
PRINT
PRINT "Are these values all correct?"
YESNOQUITRESTART()
ENDSUB

'-------------------------------------------------------------------------------

SUB LASTCHANCE()
PRINT "Write output file now?"
YESNOQUITRESTART()
ENDSUB

'-------------------------------------------------------------------------------

SUB CANNOTENCRYPT()
LOCATION = LCE
ERRORCOLOR()
PRINT "The input file, \"", INPUTFILENAME, "\", cannot be encrypted."
PRINT "The file is empty, it contains zero bytes."
PRINT "Do you want to continue?"
DEFAULTCOLOR()
YESNOQUITRESTART()
ENDSUB

'-------------------------------------------------------------------------------

SUB CANNOTDECRYPT()
LOCATION = LCD
ERRORCOLOR()
PRINT "The input file, \"", INPUTFILENAME, "\", cannot be decrypted."
PRINT
PRINT "The program operates as follows."
PRINT "JUNKMESSAGERATIO = a program constant"
PRINT "EXTRAJUNKBYTES   = a program constant"
PRINT "JMR = ABS(INT(JUNKMESSAGERATIO))"
PRINT "EJB = ABS(INT(EXTRAJUNKBYTES))"
PRINT "DECRYPTMESSAGELENGTH = (INPUTFILELENGTH - EJB) / (JMR + 1)"
PRINT
PRINT "In this instance,"
PRINT "JUNKMESSAGERATIO     = ", JUNKMESSAGERATIO
PRINT "EXTRAJUNKBYTES       = ", EXTRAJUNKBYTES
PRINT "JMR                  = ", JMR
PRINT "EJB                  = ", EJB
PRINT "INPUTFILELENGTH      = ", INPUTFILELENGTH
PRINT "DECRYPTMESSAGELENGTH = ", DECRYPTMESSAGELENGTH
PRINT
PRINT "DECRYPTMESSAGELENGTH must be greater than 0."
PRINT "Do you want to continue?"
DEFAULTCOLOR()
YESNOQUITRESTART()
ENDSUB

'-------------------------------------------------------------------------------

SUB WORKING()
COLOR 9,0
PRINT "  working..."
PRINT
DEFAULTCOLOR()
ENDSUB

'-------------------------------------------------------------------------------

SUB DONE()
MESSAGE = MDONE
PRINTMESSAGE()
QUITRESTART()
ENDSUB

'-------------------------------------------------------------------------------

SUB PRINTMESSAGE()
SELECT MESSAGE

CASE MOM
PRINT "This program will encrypt any file."
PRINT "It will decrypt any file that has been encrypted."
PRINT
PRINT "It does not alter the file to be encrypted or decrypted."
PRINT
PRINT "It creates a new file, with either, \".E\" (for encrypted),"
PRINT "or \".D\" (for decrypted), appended to the name of the"
PRINT "input file (the file to be encrypted, or decrypted)."
PRINT
PRINT "(It is probably best, to maximize the console window.)"
PRINT
PRINT "Do you want to continue?"
PRINT

CASE MPCS
PRINT "Enter the pass code string (PCS)."
PRINT
PRINT "This is the string required to decrypt the file."
PRINT "The string can only contain 'A'-'Z', 'a'-'z', '0'-'9', and 'SPC' (space)."
PRINT "The string is case sensitive."
PRINT "The string length must be in the range of 1 to 254 characters."
PRINT
PRINT "a valid PCS = \"Mary had a little lamb\""
PRINT "a valid PCS = \"2AcGb4\""
PRINT "a valid PCS = \"2aCgB4\""
PRINT "a valid PCS = \"North Dakota\""
PRINT "a valid PCS = \"207869573    EgYUjnW\""
PRINT "a valid PCS = \"Way down upon 4587GDB the Swanee River\""
PRINT "a valid PCS = \"bubbles\""
PRINT "a valid PCS = \"007\""

PRINT
PRINT "Enter the PCS."
PRINT "(Do not enclose it in quotation marks.)"
PRINT

CASE MIFN
PRINT "Enter the name of (or the path to) the input file"
PRINT "(the file to be encrypted or decrypted)."
PRINT
PRINT "If the file is in the current directory, then just enter the file's name."
PRINT "If the file is not in the current directory, then enter the entire path."
PRINT
PRINT "(Do not enclose the entry in quotation marks.)"
PRINT

CASE MDONE
PRINT "Program execution is complete."
PRINT

ENDSELECT
ENDSUB

'-------------------------------------------------------------------------------

SUB PUTSEPARATOR()
INT I
COLOR 14,0
PRINT
FOR I = 1 TO 80
PRINT "-",
NEXT I
PRINT
PRINT
DEFAULTCOLOR()
ENDSUB

'-------------------------------------------------------------------------------

SUB DEFAULTCOLOR()
COLOR 11,0
ENDSUB

'-------------------------------------------------------------------------------

SUB ERRORCOLOR()
COLOR 12,0
ENDSUB

'-------------------------------------------------------------------------------
'END I/O FUNCTIONS
'-------------------------------------------------------------------------------
'START USER CHOICE FUNCTIONS
'-------------------------------------------------------------------------------

SUB YESNOQUITRESTART()
STRING S
PRINT
PRINT "Press a key."
PRINT "\"y\" = yes"
PRINT "\"n\" = no"
PRINT "\"q\" = quit"
PRINT "\"r\" = restart"
PRINT
LABEL YNQR1
WHILE TRUE
S = INKEY$
IF S <> "" THEN GOTO YNQR2
ENDWHILE
LABEL YNQR2
S = UCASE$(S)
IF S = "Y"
REPLY = YES
ELSEIF S = "N"
REPLY = NO
ELSEIF S = "Q"
REPLY = QUIT
ELSEIF S = "R"
REPLY = RESTART
ELSE
GOTO YNQR1
ENDIF
ENDSUB

'-------------------------------------------------------------------------------

SUB QUITRESTART()
STRING S
PRINT
PRINT "Press a key."
PRINT "\"q\" = quit"
PRINT "\"r\" = restart"
PRINT
LABEL QR1
WHILE TRUE
S = INKEY$
IF S <> "" THEN GOTO QR2
ENDWHILE
LABEL QR2
S = UCASE$(S)
IF S = "Q"
REPLY = QUIT
RETURN
ENDIF
IF S = "R"
REPLY = RESTART
RETURN
ENDIF
GOTO QR1
ENDSUB

'-------------------------------------------------------------------------------

SUB BRANCH()
'SPECIAL CASES FOR QUESTION, "Do you want to continue?".
IF ((REPLY = NO)  AND (LOCATION = LOM))   THEN GOTO PQUIT
IF ((REPLY = YES) AND (LOCATION = LIIFN)) THEN GOTO PGIFN
IF ((REPLY = NO)  AND (LOCATION = LIIFN)) THEN GOTO PQUIT
IF ((REPLY = YES) AND (LOCATION = LIPCS)) THEN GOTO PGPCS
IF ((REPLY = NO)  AND (LOCATION = LIPCS)) THEN GOTO PQUIT
IF ((REPLY = YES) AND (LOCATION = LCE))   THEN GOTO PRESTART
IF ((REPLY = NO)  AND (LOCATION = LCE))   THEN GOTO PQUIT
IF ((REPLY = YES) AND (LOCATION = LCD))   THEN GOTO PRESTART
IF ((REPLY = NO)  AND (LOCATION = LCD))   THEN GOTO PQUIT

IF  REPLY = YES     THEN RETURN
IF  REPLY = QUIT    THEN GOTO PQUIT
IF  REPLY = RESTART THEN GOTO PRESTART

'IF REPLY = NO (BELOW)
IF LOCATION = LVA   THEN GOTO PRESTART
IF LOCATION = LGPCS THEN GOTO PGPCS
IF LOCATION = LGIFN THEN GOTO PGIFN
IF LOCATION = LEOD  THEN GOTO PEOD
GOTO PQUIT
ENDSUB

'-------------------------------------------------------------------------------
'END USER CHOICE FUNCTIONS
'-------------------------------------------------------------------------------
'START FILE FUNCTIONS
'-------------------------------------------------------------------------------

SUB OPENINPUTFILE()
INT RESULT
RESULT = OPENFILE(INFILE, INPUTFILENAME,"R")
IF RESULT = 0
REPLY = OK
ELSE
REPLY = ERROR
ENDIF
ENDSUB
'-------------------------------------------------------------------------------

SUB GETINPUTFILELENGTH()
INPUTFILELENGTH = LEN(INFILE)
ENDSUB

'-------------------------------------------------------------------------------

SUB READINPUTFILE()
INT I
REPLY = OK
INPUTFILEARRAY=NEW(CHAR,INPUTFILELENGTH+1)
FOR I=1 TO INPUTFILELENGTH
IF READ(INFILE,#<CHAR>INPUTFILEARRAY[I]) <> 0
REPLY = ERROR
GOTO RIF
ENDIF
NEXT I
LABEL RIF
CLOSEFILE INFILE
ENDSUB

'-------------------------------------------------------------------------------

SUB CLOSEINPUTFILE()
CLOSEFILE(INFILE)
ENDSUB

'-------------------------------------------------------------------------------

SUB SETOUTPUTFILENAME()
IF CRYPT = ENCRYPT
OUTPUTFILENAME = APPEND$(INPUTFILENAME, ".E")
ELSE
OUTPUTFILENAME = APPEND$(INPUTFILENAME, ".D")
ENDIF
ENDSUB

'-------------------------------------------------------------------------------

SUB OPENOUTPUTFILE()
INT RESULT
RESULT = OPENFILE(OUTFILE, OUTPUTFILENAME,"W")
IF RESULT = 0
REPLY = OK
ELSE
REPLY = ERROR
ENDIF
ENDSUB

'-------------------------------------------------------------------------------

SUB WRITEOUTPUTFILE()
INT I
IF CRYPT = ENCRYPT
FOR I = 1 TO ENCRYPTDOCUMENTLENGTH
WRITE(OUTFILE, #<CHAR>DOCUMENTARRAY[I])
NEXT I
ELSE
FOR I = 1 TO DECRYPTMESSAGELENGTH
WRITE(OUTFILE, #<CHAR>MESSAGEARRAY[I])
NEXT I
ENDIF
ENDSUB

'-------------------------------------------------------------------------------

SUB CLOSEOUTPUTFILE()
CLOSEFILE(OUTFILE)
ENDSUB

'-------------------------------------------------------------------------------
'END FILE FUNCTIONS
'-------------------------------------------------------------------------------
'START MEMORY FUNCTIONS
'-------------------------------------------------------------------------------

SUB ALLOCATEDOCUMENTARRAY()
INT I
IF CRYPT = ENCRYPT
DOCUMENTARRAY     = NEW(CHAR, ENCRYPTDOCUMENTLENGTH + 1)
DOCUMENTFLAGARRAY = NEW(CHAR, ENCRYPTDOCUMENTLENGTH + 1)
FOR I = 1 TO ENCRYPTDOCUMENTLENGTH
#<CHAR>DOCUMENTFLAGARRAY[I] = 0
NEXT I
ELSE
DOCUMENTARRAY     = NEW(CHAR, INPUTFILELENGTH + 1)
DOCUMENTFLAGARRAY = NEW(CHAR, INPUTFILELENGTH + 1)
FOR I = 1 TO INPUTFILELENGTH
#<CHAR>DOCUMENTFLAGARRAY[I] = 0
NEXT I
ENDIF
ENDSUB

'-------------------------------------------------------------------------------

SUB ALLOCATEMESSAGEARRAY()
IF CRYPT = ENCRYPT
MESSAGEARRAY = NEW(CHAR, ENCRYPTMESSAGELENGTH + 1)
ELSE
MESSAGEARRAY = NEW(CHAR, DECRYPTMESSAGELENGTH + 1)
ENDIF
ENDSUB

'-------------------------------------------------------------------------------

SUB DELETEMEMORY()
DELETE(MESSAGEARRAY)
DELETE(DOCUMENTARRAY)
DELETE(DOCUMENTFLAGARRAY)
ENDSUB

'-------------------------------------------------------------------------------
'END MEMORY FUNCTIONS
'-------------------------------------------------------------------------------
'START ENCRYPTION/DECRYPTION FUNCTIONS
'-------------------------------------------------------------------------------

SUB CALCJMR()
JMR = ABS((JUNKMESSAGERATIO))
ENDSUB

'-------------------------------------------------------------------------------

SUB CALCEJB()
EJB = ABS((EXTRAJUNKBYTES))
ENDSUB

'-------------------------------------------------------------------------------

SUB CHECKIFCANENCRYPT(), INT
IF CRYPT = DECRYPT THEN RETURN TRUE
IF INPUTFILELENGTH = 0 THEN RETURN FALSE
RETURN TRUE
ENDSUB

'-------------------------------------------------------------------------------

SUB CHECKIFCANDECRYPT(), INT

IF CRYPT = ENCRYPT THEN RETURN TRUE
IF DECRYPTMESSAGELENGTH < 1 THEN RETURN FALSE
RETURN TRUE
ENDSUB

'-------------------------------------------------------------------------------

SUB SETENCRYPTMESSAGELENGTH()
IF CRYPT = DECRYPT THEN RETURN
ENCRYPTMESSAGELENGTH = INPUTFILELENGTH
ENDSUB

'-------------------------------------------------------------------------------

SUB CALCENCRYPTDOCUMENTLENGTH()
IF CRYPT = DECRYPT THEN RETURN
ENCRYPTDOCUMENTLENGTH = ENCRYPTMESSAGELENGTH * (JMR + 1) + EJB
ENDSUB

'-------------------------------------------------------------------------------

SUB SETDECRYPTMESSAGELENGTH()
IF CRYPT = ENCRYPT THEN RETURN
DECRYPTMESSAGELENGTH = (INPUTFILELENGTH - EJB)/(JMR + 1)
ENDSUB
'-------------------------------------------------------------------------------

SUB INSERTMESSAGEINDOCUMENT()
INT I, J, R, COUNT
IF CRYPT = DECRYPT THEN RETURN
FOR I = 1 TO ENCRYPTMESSAGELENGTH
R =  GETRANDOMVALUE(1, ENCRYPTDOCUMENTLENGTH + 1 - I)
COUNT =  0
FOR J = 1 TO ENCRYPTDOCUMENTLENGTH
IF #<CHAR>DOCUMENTFLAGARRAY[J] = 0 THEN COUNT += 1
IF COUNT = R
#<CHAR>DOCUMENTARRAY[J] = #<CHAR>MESSAGEARRAY[I]
#<CHAR>DOCUMENTFLAGARRAY[J] = 1
BREAKFOR
ENDIF
NEXT J
NEXT I
ENDSUB

'-------------------------------------------------------------------------------

SUB EXTRACTMESSAGEFROMDOCUMENT()
INT I, J, R, COUNT
IF CRYPT = ENCRYPT THEN RETURN
FOR I = 1 TO DECRYPTMESSAGELENGTH
R =  GETRANDOMVALUE(1, INPUTFILELENGTH + 1 - I)
COUNT =  0
FOR J = 1 TO INPUTFILELENGTH
IF #<CHAR>DOCUMENTFLAGARRAY[J] = 0 THEN COUNT += 1
IF COUNT = R
#<CHAR>MESSAGEARRAY[I] = #<CHAR>DOCUMENTARRAY[J]
#<CHAR>DOCUMENTFLAGARRAY[J] = 1
BREAKFOR
ENDIF
NEXT J
NEXT I
ENDSUB

'-------------------------------------------------------------------------------

SUB ENCRYPTMESSAGE()
INT I, J
IF CRYPT = DECRYPT THEN RETURN
FOR I = 1 TO ENCRYPTMESSAGELENGTH
J = #<CHAR>MESSAGEARRAY[I]
#<CHAR>MESSAGEARRAY[I] = NEWASCIITABLE[J]
NEXT I
ENDSUB

'-------------------------------------------------------------------------------

SUB DECRYPTMESSAGE()
INT I, J
IF CRYPT = ENCRYPT THEN RETURN
FOR I = 1 TO DECRYPTMESSAGELENGTH
J = #<CHAR>MESSAGEARRAY[I]
#<CHAR>MESSAGEARRAY[I] = GETINDEXOFNEWASCIITABLEVAL(J)
NEXT I
ENDSUB

'-------------------------------------------------------------------------------

SUB FILLDOCUMENTARRAY()
INT I
IF CRYPT = ENCRYPT
FOR I =  1 TO ENCRYPTDOCUMENTLENGTH
#<CHAR>DOCUMENTARRAY[I] = GETRANDOMVALUE(0, 255)
NEXT I
ELSE
FOR I =  1 TO INPUTFILELENGTH
#<CHAR>DOCUMENTARRAY[I] = #<CHAR>INPUTFILEARRAY[I]
NEXT I
ENDIF
ENDSUB

'-------------------------------------------------------------------------------

SUB FILLENCRYPTMESSAGEARRAY()
INT I
IF CRYPT = DECRYPT THEN RETURN
FOR I = 1 TO ENCRYPTMESSAGELENGTH
#<CHAR>MESSAGEARRAY[I] = #<CHAR>INPUTFILEARRAY[I]
NEXT I
ENDSUB

'-------------------------------------------------------------------------------

SUB DUMPDOCUMENTARRAYRANDOMVALUES()
CHAR C
INT I
IF CRYPT = ENCRYPT THEN RETURN
FOR I =  1 TO INPUTFILELENGTH
C = GETRANDOMVALUE(0, 255)
NEXT I
ENDSUB

'-------------------------------------------------------------------------------

SUB FILLNEWASCIITABLE()
INT I, J, R, COUNT
FOR I = 0 TO 255
NEWASCIITABLE[I] = -1
NEXT I
FOR I = 0 TO 255
LABEL FNAT
R =  GETRANDOMVALUE(1, 256 - I)
COUNT =  0
FOR J = 0 TO 255
IF NEWASCIITABLE[J] = -1 THEN COUNT += 1
IF COUNT = R
IF I = J
IF I = 255
NEWASCIITABLE[255] = NEWASCIITABLE[254]
NEWASCIITABLE[254] = 255
RETURN
ENDIF
GOTO FNAT
ENDIF
NEWASCIITABLE[J] = I
BREAKFOR
ENDIF
NEXT J
NEXT I
ENDSUB

'-------------------------------------------------------------------------------

SUB GETINDEXOFNEWASCIITABLEVAL(INT V), INT
INT I
FOR I = 0 TO 255
IF NEWASCIITABLE[I] = V
RETURN I
ENDIF
NEXT I
RETURN -1
ENDSUB

'-------------------------------------------------------------------------------
'END ENCRYPTION/DECRYPTION FUNCTIONS
'-------------------------------------------------------------------------------
'START MISCELLANEOUS FUNCTIONS
'-------------------------------------------------------------------------------

SUB SETGLOBALVARIABLES()
RAN2SEED  = 0
IDUM2     = 0
IY        = 0
RAN2FLAG  = 0
ENDSUB

'-------------------------------------------------------------------------------
'END MISCELLANEOUS FUNCTIONS
'-------------------------------------------------------------------------------
'START PASS CODE FUNCTIONS
'-------------------------------------------------------------------------------

SUB CALCPASSCODE(STRING PW)
INT I, N, P
DOUBLE SUM
INT MAXINT = 2147483647
INT PWL = LEN(PW)
IF PWL = 0
REPLY = BAD
RETURN
ENDIF
SUM = 0
FOR I = 1 TO PWL
P = PWL - I
N = GETCHARCODE(I, MID$(PW, I, 1))
IF N = -1
REPLY = BAD
RETURN
ENDIF
SUM += N * (10 ^ P)
NEXT I
IF SUM > MAXINT
WHILE SUM > MAXINT
SUM /= 2
ENDWHILE
ENDIF
PASSCODE = SUM
REPLY = GOOD
ENDSUB

'-------------------------------------------------------------------------------

SUB GETCHARCODE(INT I, STRING C),INT
INT DIGIT[10]
INT UPPER[26]
INT LOWER[26]
INT NUM = ASC(C)
DIGIT = 0,1,2,3,4,5,6,7,8,9
UPPER = 0,5,0,5,7,2,0,7,1,9,9,4,6,9,1,7,1,4,3,8,8,2,3,3,6,5
LOWER = 6,0,2,9,6,8,7,3,9,0,5,0,6,7,5,8,1,4,8,1,3,2,1,4,2,4

IF I = 1
IF NUM =  48 THEN RETURN  3
IF NUM =  65 THEN RETURN  5
IF NUM =  67 THEN RETURN  7
IF NUM =  71 THEN RETURN  9
IF NUM =  98 THEN RETURN  4
IF NUM = 106 THEN RETURN  6
IF NUM = 108 THEN RETURN  8
ENDIF

IF NUM = 32 THEN RETURN 2

IF ((NUM >= 48) AND (NUM <=  57)) THEN RETURN DIGIT[NUM - 48]
IF ((NUM >= 65) AND (NUM <=  90)) THEN RETURN UPPER[NUM - 65]
IF ((NUM >= 97) AND (NUM <= 122)) THEN RETURN LOWER[NUM - 97]

RETURN -1
ENDSUB

'-------------------------------------------------------------------------------
'END PASS CODE FUNCTIONS
'-------------------------------------------------------------------------------
'START RANDOM NUMBER GENERATOR FUNCTIONS
'-------------------------------------------------------------------------------

SUB RAN2(), DOUBLE
'random double generator
'from "numerical Recipes in C", 2nd edition, p.282.
'0 < RAN2() < 1

DOUBLE AM   = 1.0 / IM1
DOUBLE RNMX = 1.0 - DEPS
INT J, K
DOUBLE TEMP

IF RAN2FLAG = 0
RAN2SEED = ABS(RAN2SEED)
IF RAN2SEED < 1 THEN RAN2SEED = 1
IDUM2 = RAN2SEED
FOR J = NTAB + 7 TO 0 STEP -1
K = RAN2SEED / IQ1
RAN2SEED = IA1 * (RAN2SEED - K * IQ1) - K * IR1
IF RAN2SEED < 0 THEN RAN2SEED += IM1
IF J < NTAB THEN IV[J] = RAN2SEED
NEXT J
IY = IV[0]
RAN2FLAG = 1
ENDIF

K = RAN2SEED / IQ1
RAN2SEED = IA1 * (RAN2SEED - K * IQ1) - K * IR1
IF RAN2SEED < 0 THEN RAN2SEED += IM1
K = IDUM2 / IQ2
IDUM2 = IA2 * (IDUM2 - K * IQ2) - K * IR2
IF IDUM2 < 0 THEN IDUM2 += IM2
J = IY / NDIV
IY = IV[J] - IDUM2
IV[J] = RAN2SEED
IF IY < 1 THEN IY += IMM1
TEMP = AM * IY
IF TEMP > RNMX
RETURN RNMX
ELSE
RETURN TEMP
ENDIF

'This statement never executes.
'Added, so compiler doesn't warn that a return value is expected.
RETURN 0

ENDSUB

'----------------------------------------------------------------------------------

SUB GETRANDOMVALUE(INT MIN, INT MAX),INT
'returns uniformly distributed values, MIN <= VALUE <= MAX

RETURN RAN2() * (MAX - MIN) + MIN
ENDSUB

'----------------------------------------------------------------------------------

SUB GETNUMSECONDS(), INT
'returns total number of seconds, since midnight of current day

INT TOTSECS = 0
STRING TIMENOW = TIME$
STRING TEMP
TEMP = MID$(TIMENOW, 1, 2)
TOTSECS += VAL(TEMP) * 3600
TEMP = MID$(TIMENOW, 4, 2)
TOTSECS += VAL(TEMP) * 60
TEMP = MID$(TIMENOW, 7, 2)
TOTSECS += VAL(TEMP)
RETURN TOTSECS

ENDSUB
'----------------------------------------------------------------------------------

SUB SETRAN2SEED(INT I)
RAN2SEED = I
ENDSUB

'----------------------------------------------------------------------------------
'END RANDOM NUMBER GENERATOR FUNCTIONS
'-------------------------------------------------------------------------------

