QB64.org Forum

Active Forums => QB64 Discussion => Topic started by: NOVARSEG on December 06, 2020, 11:48:23 pm

Title: File encryption / decryption
Post by: NOVARSEG on December 06, 2020, 11:48:23 pm
Basic file encryptor
Don't try this on important files as it has not been tested

**********************

DIM string1(16384) AS STRING 
DIM string2(16384) AS STRING
DIM LFILE AS _UNSIGNED LONG
DIM FPOS AS _UNSIGNED LONG
DIM NBYTES  AS _UNSIGNED LONG
FPOS = 1
string1 = string$(16384,"a") 'string1 could randomized for better security
OPEN TEST.TXT FOR BINARY AS #1

LFILE = LOF (1)

DO
IF LFILE  = 0  THEN EXIT DO
 
  IF LFILE  < 16384  THEN
  GET #1, ,string2(LFILE) 'file read
  FPOS = FPOS  + LFILE    'FPOS = file position after GET
  nbytes = LFILE 
  GOSUB  XORstring2      'encrypt or decrypt
  SEEK 1, FPOS - LFILE    'adjust file pointer 
  PUT #1, ,string2(LFILE) 'file write
  LFILE = 0
  END IF 

  IF LFILE =>16384  THEN
  GET #1, ,string2(16384) 'file read
  FPOS = FPOS  + 16384    'FPOS = file position after GET 
  nbytes = 16384 
  GOSUB XORstring2        'encrypt or decrypt
  SEEK 1, FPOS - 16384    'adjust file pointer
  PUT #1, ,string2(16384) 'file write
  LFILE = LFILE - 16384
  END IF 

LOOP
CLOSE 1
END

XORstring2:
FOR x% = 1 to nbytes
a$ = MID$(string1),x%,1)
b$ = MID$(string2),x%,1)
b$ xor a$
MID$(string2),x%,1) = b$
NEXT x%
RETURN

*****
Question
In the above code is:
DIM string1(16384) AS STRING
equivalent to
DIM string1 AS STRING * 16384
for the application as shown?
****
Title: Re: File encryption / decryption
Post by: bplus on December 08, 2020, 12:35:34 pm
Quote
*****
Question
In the above code is:
DIM string1(16384) AS STRING
equivalent to
DIM string1 AS STRING * 16384
for the application as shown?
****

Oh man, I just saw this question when I started playing around with a file Encryption of my own.
Sorry but you had it buried in what I thought was code. I saw the XOR code method in quick glance and passed at looking at until I decided to test my encryptor with a copy of your post put in a text file, when decrypting my description I saw the question. Well better late that never...

No, those 2 things are not equivalent:
DIM string1(16384) AS STRING > is a string array with 16384 items
DIM string1 AS STRING * 16384 > is a Fixed string of fixed length 16384

Do you know how to post code in code tabs?  [ code ]  your code here   [ /code ]

  [ This attachment cannot be displayed inline in 'Print Page' view ]


Put your code in editor, highlight it all, and then click the QB64 icon button.

or click the button and paste the code between    code] x[/code  where x is (not much room).
Title: Re: File encryption / decryption
Post by: Bert22306 on December 08, 2020, 06:20:31 pm
Interesting. In your scheme, however, you will need to input two long strings, in order to encrypt or decrypt the actual plaintext file. If I read this correctly. The plaintext string1 and this string2. Or perhaps, both strings make up the plaintext?

Another way to do this, create one string, consisting of the plaintext, and then XOR each byte with a pseudo-random byte from the QB64 RANDOMIZE function.

Then, all you have to send to the receiver is the seed used in the encryption step. This has a formal name: "symmetric key stream cipher." Pretty hard to crack, as long as the random number sequence is at least as long as the plaintext.

Or, instead of XORing, another possibility is multiplication in encryption, and division in decryption. But that will create a longer ciphertext than plaintext.

Fun stuff.
Title: Re: File encryption / decryption
Post by: NOVARSEG on December 08, 2020, 07:29:28 pm
Hi bplus
so they are not the same exactly?

DIM string1(16384) AS STRING
DIM string1 AS STRING * 16384
GET or PUT seems to like the array type. 

****
Hi Bert22306

Quote
Interesting. In your scheme, however, you will need to input two long strings, in order to encrypt or decrypt the actual plaintext file. If I read this correctly. The plaintext string1 and this string2. Or perhaps, both strings make up the plaintext?

string2 is the buffer for read from file and write to file.

string1 is the random string. It is shown as all "a" for simplicity

basically the code does:  string2 XOR string1
Quote
Another way to do this, create one string, consisting of the plaintext, and then XOR each byte with a pseudo-random byte from the QB64 RANDOMIZE function.

That is what the code does. string1 is the random string, just have to use the RANDOMIZE function

Quote
Then, all you have to send to the receiver is the seed used in the encryption step. This has a formal name: "symmetric key stream cipher." Pretty hard to crack, as long as the random number sequence is at least as long as the plaintext.

There are various methods. The one I like is the long password system. A long password, say, 512 bytes is hashed and then that is used as the password.  The hash function has to be written correctly to obliterate any repetitions etc in the hash itself.

Quote
Or, instead of XORing, another possibility is multiplication in encryption, and division in decryption. But that will create a longer ciphertext than plaintext.

There are other ways but XOR works just fine if done right.

The code overwrites the plain text file with XORed bytes to create cipher text
and when run again
 the code overwrites the cipher text file with XORed bytes to create plain text
Title: Re: File encryption / decryption
Post by: bplus on December 08, 2020, 07:58:54 pm
Quote
Hi bplus
so they are not the same exactly?

DIM string1(16384) AS STRING
DIM string1 AS STRING * 16384
GET or PUT seems to like the array type.

I will show a difference:
Code: QB64: [Select]
  1. DIM string1(16384) AS STRING
  2. DIM string2 AS STRING * 16384
  3.  
  4. FOR i = 1 TO 16384
  5.     string1(i) = STR$(i)
  6. ' no error
  7.  
  8. FOR i = 1 TO 16384
  9.     string2(i) = STR$(i)   ' <<<<<<<<<<<<< red lined!!!
  10.  

Here's another:
Code: QB64: [Select]
  1. DIM string1(16384) AS STRING
  2. DIM string2 AS STRING * 16384
  3.  
  4. PRINT LEN(string1(1))
  5. PRINT LEN(string2)
  6.  
  7.  
  8.  
Title: Re: File encryption / decryption
Post by: NOVARSEG on December 08, 2020, 08:15:26 pm
bplus
Interesting
whoop made a mistake i had 2 string1
****

DIM string1(16384) AS STRING
DIM string2 AS STRING * 16384
 
PRINT LEN(string1())
PRINT LEN(string2)

are they the same length?
 
Title: Re: File encryption / decryption
Post by: bplus on December 08, 2020, 08:29:15 pm
bplus
Interesting
whoop made a mistake i had 2 string1
****

DIM string1(16384) AS STRING
DIM string2 AS STRING * 16384
 
PRINT LEN(string1())
PRINT LEN(string2)

are they the same length?

;-)) well you could run the code but yes! string2 is a fixed length string always it will have length 16384 because that's how you DIM'd it.

String1 is an array of variable length strings we have 16384 of those dimensioned for that array of strings, so at the moment it has a length of 0 because it hasn't been assigned a value yet.

Title: Re: File encryption / decryption
Post by: bplus on December 08, 2020, 08:49:22 pm
@NOVARSEG

I just put your code in the IDE, no offense but it's a mess, which tells me you hadn't tried it yourself.

Not good.

Not only should we not try it on important files, we shouldn't try it at all, not safe.
But we couldn't run this one, as is, so there's that ;-))

I suggest trying XOR on 2 strings before moving up to files. It's a nice idea if it works.
Title: Re: File encryption / decryption
Post by: SMcNeill on December 08, 2020, 09:48:40 pm
Interesting. In your scheme, however, you will need to input two long strings, in order to encrypt or decrypt the actual plaintext file. If I read this correctly. The plaintext string1 and this string2. Or perhaps, both strings make up the plaintext?

Another way to do this, create one string, consisting of the plaintext, and then XOR each byte with a pseudo-random byte from the QB64 RANDOMIZE function.

Then, all you have to send to the receiver is the seed used in the encryption step. This has a formal name: "symmetric key stream cipher." Pretty hard to crack, as long as the random number sequence is at least as long as the plaintext.

Or, instead of XORing, another possibility is multiplication in encryption, and division in decryption. But that will create a longer ciphertext than plaintext.

Fun stuff.

As Bert said, this isn't that complicated to do.   Here's a quick example of a normal string, encrypted, and then decrypted, using a simple seed and RND with XOR:

Code: QB64: [Select]
  1. SCREEN _NEWIMAGE(720, 1024, 32)
  2.  
  3. _TITLE "String Encryption"
  4. a$ = "The Ass and the Lapdog.    A Farmer one day came to the stables to see to his beasts of burden: among them was his favourite Ass, that was always well fed and often carried his master.  With the Farmer came his Lapdog, who danced about and licked his hand and frisked about as happy as could be.  The Farmer felt in his pocket, gave the Lapdog some dainty food, and sat down while he gave his orders to his servants.  The Lapdog jumped into his master's lap, and lay there blinking while the Farmer stroked his ears.  The Ass, seeing this, broke loose from his halter and commenced prancing about in imitation of the Lapdog.  The Farmer could not hold his sides with laughter, so the Ass went up to him, and putting his feet upon the Farmer's shoulder attempted to climb into his lap.  The Farmer's servants rushed up with sticks and pitchforks and soon taught the Ass that Clumsy jesting is no joke."
  5.  
  6. RANDOMIZE USING -1973 'a nice random seed
  7. FOR i = 1 TO LEN(a$)
  8.     b$ = b$ + CHR$(ASC(a$, i) XOR INT(RND * 255))
  9. PRINT "-----------------------------------------------------------------------------------------"
  10.  
  11. RANDOMIZE USING -1973 'the same seed as above nice random seed
  12. FOR i = 1 TO LEN(b$)
  13.     c$ = c$ + CHR$(ASC(b$, i) XOR INT(RND * 255))
  14. PRINT "-----------------------------------------------------------------------------------------"

Title: Re: File encryption / decryption
Post by: SMcNeill on December 08, 2020, 09:57:57 pm
Personally, an even easier way I find to encrypt things is to just do something really simple with a compressed file:

Code: QB64: [Select]
  1. SCREEN _NEWIMAGE(1024, 720, 32)
  2.  
  3. _TITLE "String Encryption"
  4. a$ = "The Ass and the Lapdog.    A Farmer one day came to the stables to see to his beasts of burden: among them was his favourite Ass, that was always well fed and often carried his master.  With the Farmer came his Lapdog, who danced about and licked his hand and frisked about as happy as could be.  The Farmer felt in his pocket, gave the Lapdog some dainty food, and sat down while he gave his orders to his servants.  The Lapdog jumped into his master's lap, and lay there blinking while the Farmer stroked his ears.  The Ass, seeing this, broke loose from his halter and commenced prancing about in imitation of the Lapdog.  The Farmer could not hold his sides with laughter, so the Ass went up to him, and putting his feet upon the Farmer's shoulder attempted to climb into his lap.  The Farmer's servants rushed up with sticks and pitchforks and soon taught the Ass that Clumsy jesting is no joke."
  5. b$ = _DEFLATE$(a$)
  6. FOR i = LEN(b$) TO 1 STEP -1
  7.     c$ = c$ + MID$(b$, i, 1) 'A simple reverse order of the compressed file, to "encrypt it".
  8. PRINT "--------------------------------"
  9. PRINT "--------------------------------"
  10.  
  11. d$ = _INFLATE$(c$)
  12. PRINT "--------------------------------"
  13. PRINT d$; "Notice this is blank?  It's not a valid compressed file, so it fails on decompression and returns nothing."
  14. FOR i = LEN(c$) TO 1 STEP -1
  15.     e$ = e$ + MID$(c$, i, 1) 'A simple reverse order of the compressed file, to "decrypt it".
  16. f$ = _INFLATE$(e$)
  17. PRINT "--------------------------------"

All I'm doing here is compressing my string, and then reversing it.  That's it, and yet, it'd be hard as heck for anyone to sort out what type of file it is, or what the contents are.  You could NOT each byte, or XOR each byte, or do whatever the heck else you want to do with it, but unless the other person knows the same process, they're not going to have an easy time decoding whatever the contents are.

File encryption/decryption doesn't have to be that hard of a process to implement -- it's just BASIC programming, in the end.  :P
Title: Re: File encryption / decryption
Post by: bplus on December 08, 2020, 11:12:31 pm
Thanks @SMcNeill

Your simple examples put the kibosh on the effort I was struggling with today. ;-))

Those 2 examples are simple. So that's how to do XOR! Cool.
Title: Re: File encryption / decryption
Post by: NOVARSEG on December 08, 2020, 11:53:35 pm
Quote
I just put your code in the IDE, no offense but it's a mess, which tells me you hadn't tried it yourself.

Not good.

Not only should we not try it on important files, we shouldn't try it at all, not safe.
But we couldn't run this one, as is, so there's that ;-))

I expected it to have syntax errors but it is essentially correct.
I don't have any IDE at present

One error was
CLOSE 1
it should be CLOSE #1

OK I should try out my own code.

SMcNeill

compression method that works too

Title: Re: File encryption / decryption
Post by: bplus on December 09, 2020, 12:16:42 am
Quote
I don't have any IDE at present

@NOVARSEG   sorry to read, what's up with that?
Title: Re: File encryption / decryption
Post by: bplus on December 09, 2020, 12:25:03 am
Here is a peak what the IDE is red-lining, I commented out the first to get to 2nd... and repeat comment 2nd to get 3rd...

The middle section was left clear but I confess I don't understand what it's doing.

Code: QB64: [Select]
  1.  
  2. DIM string1(16384) AS STRING
  3. DIM string2(16384) AS STRING
  4. FPOS = 1
  5. '''''''''' string1 is an array ' string1 = string$(16384,"a") 'string1 could randomized for better security
  6. '''''''''' missing quotes '' OPEN TEST.TXT FOR BINARY AS #1
  7.  
  8. LFILE = LOF(1)
  9.  
  10.     IF LFILE = 0 THEN EXIT DO
  11.  
  12.     IF LFILE < 16384 THEN
  13.         GET #1, , string2(LFILE) 'file read
  14.         FPOS = FPOS + LFILE 'FPOS = file position after GET
  15.         NBYTES = LFILE
  16.         GOSUB XORstring2 'encrypt or decrypt
  17.         SEEK 1, FPOS - LFILE 'adjust file pointer
  18.         PUT #1, , string2(LFILE) 'file write
  19.         LFILE = 0
  20.     END IF
  21.  
  22.     IF LFILE >= 16384 THEN
  23.         GET #1, , string2(16384) 'file read
  24.         FPOS = FPOS + 16384 'FPOS = file position after GET
  25.         NBYTES = 16384
  26.         GOSUB XORstring2 'encrypt or decrypt
  27.         SEEK 1, FPOS - 16384 'adjust file pointer
  28.         PUT #1, , string2(16384) 'file write
  29.         LFILE = LFILE - 16384
  30.     END IF
  31.  
  32.  
  33. XORstring2:
  34. FOR x% = 1 TO NBYTES
  35.     ''''''''''''''string1 is array '''''''''''''''''a$ = MID$(string1),x%,1)
  36.     ''''''''''''' string2 is array '''''''''''''''''b$ = MID$(string2),x%,1)
  37.     ''''''''''''' XOR need numbers '''''''''''''''''b$ xor a$
  38.     ''''''''''''' string2 is array '''''''''''''''''MID$(string2),x%,1) = b$
  39. NEXT x%
  40.  
  41.  
Title: Re: File encryption / decryption
Post by: SMcNeill on December 09, 2020, 01:02:30 am
Quote
The middle section was left clear but I confess I don't understand what it's doing.

What the middle is an attempt to do, is to read a file in chunks of data of 16384 bytes (though I have no idea why such a number was chosen), XOR those chunks, and then put them back into the file.

OPEN “TEMP.TXT” FOR BINARY AS #1
l = LOF(1)
a$ = SPACE$(l)
GET #1, 1, a$
FOR i = 1 TO l
   b$ = b$ + CHR$(ASC(a$,i) XOR 97)
NEXT
PUT #1, 1, b$
CLOSE

There’s the exact same program, simplified down for usability and understandability.

Since string1 = string$(16384,"a"), it’s nothing more than a repetitive set of “a” characters, which is CHR$(97).  Since XOR works with numbers, I simply hardcoded that XOR 97 above and was done with it.

Read a file into a string.
XOR the contents of the string.
Write the string back to “encrypt” the file.

It’s that simple, unless folks just want to try and complicate things to obfuscate their code/process.
Title: Re: File encryption / decryption
Post by: bplus on December 09, 2020, 01:20:40 am
Here's the source of my obfuscation:
Code: QB64: [Select]
  1. DIM string1(16384) AS STRING
  2. DIM string2(16384) AS STRING
  3.  

Probably meant
Code: QB64: [Select]
  1. DIM string1 as STRING * 16384
Title: Re: File encryption / decryption
Post by: SMcNeill on December 09, 2020, 01:37:19 am
Or perhaps:

DIM string1(16384) AS STRING * 1

An array of 16384 characters to process, rather than a single string?

(Though if that’s the case, why not just make it an array AS _BYTE?  No need to get ASC values then.)
Title: Re: File encryption / decryption
Post by: NOVARSEG on December 09, 2020, 01:44:37 am
gotta problem with that see latest thread "Having trouble with PUT"
Title: Re: File encryption / decryption
Post by: NOVARSEG on December 09, 2020, 04:00:14 am
Quote
What the middle is an attempt to do, is to read a file in chunks of data of 16384 bytes (though I have no idea why such a number was chosen), XOR those chunks, and then put them back into the file.
16384 is a binary number. It was Peter Norton's favorite size when working with files

Quote
OPEN “TEMP.TXT” FOR BINARY AS #1
l = LOF(1)
a$ = SPACE$(l)
GET #1, 1, a$
FOR i = 1 TO l
   b$ = b$ + CHR$(ASC(a$,i) XOR 97)
NEXT
PUT #1, 1, b$
CLOSE

There’s the exact same program, simplified down for usability and understandability.

OK I got to be careful here. I did try my own code and it crashed . Maybe  I really don't understand how PUT and GET works. Im thinking in terms of the functionality of the DOS interrupts for file read and file write.  The inputs for those functions are location of a file buffer, number of bytes read / write and the file handle.

When  the file is read , the file pointer advances. So just before the NeXT write we gotta decrement  the  file pointer by the number of bytes that were read (using SEEK).  That is how the file is processed.

Quote
FOR i = 1 TO l
   b$ = b$ + CHR$(ASC(a$,i) XOR 97)
NEXT
PUT #1, 1, b$
where b$ is variable length
but
DIM string2 AS STRING * 16384
PUT #1, 1, string2
probably won't work the same.

Title: Re: File encryption / decryption
Post by: SMcNeill on December 09, 2020, 07:36:35 am
PUT works the same in QB64 as it has in all other versions of GWBASIC, QBASIC, and QB45.  It doesn’t matter if it’s a variable length, or set length string.  The commandis consistent.  ;)
Title: Re: File encryption / decryption
Post by: FellippeHeitor on December 09, 2020, 08:20:56 am
Not for arrays, as that behavior is QB64-exclusive. One would have to BSAVE back in the day.
Title: Re: File encryption / decryption
Post by: Bert22306 on December 09, 2020, 06:16:15 pm
There’s the exact same program, simplified down for usability and understandability.

Since string1 = string$(16384,"a"), it’s nothing more than a repetitive set of “a” characters, which is CHR$(97).  Since XOR works with numbers, I simply hardcoded that XOR 97 above and was done with it.

Read a file into a string.
XOR the contents of the string.
Write the string back to “encrypt” the file.

It’s that simple, unless folks just want to try and complicate things to obfuscate their code/process.

In other words, your "secret key" would be 97. Give that secret key "out of band," securely, to the receiver, and send the ciphertext to the receiver over an unsecured channel, and the receiver can easily recover the plaintext.

But, the problem with this scheme is that a dedicated intruder, so-called man-in-the-middle, could try to figure out what that secret key is. For example, find out what are the most repeated cipher characters. Should be encrypted forms of <space> or "e," typically. Once you have those few most-frequent examples, you can easily determine what the "secret key" was. You'll only have a short list of candidates. XOR those tentative secret keys with the ciphertext, and you'll get gibberish a couple of times maybe, then very soon recover the plaintext.

Instead, if you use 8-bit random bytes, from a PRNG, with RANDOMIZE USING (which repeats the sequence from the beginning, for any given seed value), you'll create a ciphertext that is really difficult to crack. Get that seed value to the receiver over some secure channel, and you're golden. The pseudo-random sequence has to be at least as long as the plaintext.

Another tangential point. I tried concatenating two XOR computations, using a different PRNG seed value for each set of computations. This did not improve anything. The ciphertext actually became less "random," statistically, than the single XOR method. And it could still be converted back to plaintext with only one XOR operation by the receiver.
Title: Re: File encryption / decryption
Post by: bplus on December 09, 2020, 08:30:39 pm
Wouldn't it take the "man in the middle" quite some time to figure which encryption scheme is being employed?

To me, this could be as nasty as actually trying to crack the scheme.
Title: Re: File encryption / decryption
Post by: NOVARSEG on December 09, 2020, 08:59:47 pm
Got it working..

Quote
DIM string2 AS STRING 'variable length string
DIM LFILE AS _UNSIGNED LONG
DIM FPOS AS _UNSIGNED LONG
DIM NBYTES AS _UNSIGNED LONG
FPOS = 1

OPEN "TEST.TXT" FOR BINARY AS #1

LFILE = LOF(1): PRINT LOF(1)


DO
    IF LFILE = 0 THEN EXIT DO

    IF LFILE < 16384 THEN
        string2 = SPACE$(LFILE) 'gives string2 the correct length for GET and PUT
        GET #1, , string2 'file read
        FPOS = FPOS + LFILE 'FPOS = file position after GET
        NBYTES = LFILE
        GOSUB XORstring2 'encrypt or decrypt
        SEEK 1, FPOS - LFILE 'adjust file pointer
        PUT #1, , string2 'file write
        LFILE = 0
    END IF

    IF LFILE >= 16384 THEN
        string2 = SPACE$(16384) 'gives string2 the correct length for GET and PUT
        GET #1, , string2 'file read
        FPOS = FPOS + 16384 'FPOS = file position after GET
        NBYTES = 16384
        GOSUB XORstring2 'encrypt or decrypt
        SEEK 1, FPOS - 16384 'adjust file pointer
        PUT #1, , string2 'file write
        LFILE = LFILE - 16384
    END IF

LOOP

PRINT "DONE"

CLOSE #1

END

XORstring2:
FOR x% = 1 TO NBYTES
    a$ = MID$(string2, x%, 1)
    MID$(string2, x%, 1) = CHR$(ASC(a$) XOR 97)
NEXT x%
RETURN
Title: Re: File encryption / decryption
Post by: Bert22306 on December 09, 2020, 09:14:31 pm
Wouldn't it take the "man in the middle" quite some time to figure which encryption scheme is being employed?

To me, this could be as nasty as actually trying to crack the scheme.

True enough, but that technique, also known as "security through obscurity," has a bad reputation among cipher geeks. The "proper" way to design a crypto protocol, according to this community of geeks, is to make it "impossible" to crack, even if the bad guys know the protocol. This describes all the popular encryption protocols today.

So, in the scheme where the keystream is generated by a PRNG, this man-in-the-middle, even if he knows how the scheme works, will be required to test a huge number of possible seed values. Trial and error, but requiring a prohibitively long series of trials. Compare this with XORing always with one ASCII character, such as "a" and you can see, the series of trials will be very limited. It will probably take only two attempts, at most.

Symmetric key stream ciphers are perhaps the fastest of all encryption and decryption protocols. A variation of this is to use a block code, and then create the keystream out of the entries in each block. Another scheme is to create blocks of plaintext, but that's not as fast. Thing is, in all of these, the XOR operation is super common.

So, anything you try to devise, which ends up being repetitive, in any "security through obscurity" technique, a dedicated hacker will figure out, sooner or later.