My encryption method just wont work

General FreeBASIC programming questions.
Post Reply
datwill310
Posts: 355
Joined: May 29, 2015 20:37

My encryption method just wont work

Post by datwill310 »

There was a change needed, so below.

Hi guys!

Recently I've been designing a piece of software which encrypts a file based on a key. One can decrypt the file by applying the same key.

Trouble is, it just wont work. Another thing odd is that sometimes it does work, but other times it doesn't. And I don't know where I'm going wrong.

8-bit encryption works fine. However, it's just too slow for practical use. Therefore I tried to devise 16/32/and64-bit encryption using this method.

I was wondering if you could help me fix the issues I'm having.

I've had to rewrite this thing multiple times. At first, it works (i.e. all of it). But as I begin to test it out more, I notice that:
  • The decrypted version of the original file comes out different: either by a one byte difference, or just everything doesn't work out.
  • The key likes to change itself sometimes when decrypting: NO idea what that's about.
  • At first, if a file's size wasn't a multiple of the bytes used for the encryption key (e.g. if a file was 65 bytes and the key was 8 bytes [64-bit]) redundant NULL characters would spawn at the end of the file. I had fixed this problem by treating the remaining bytes individually.
My Method
I'm not trying to invent the next leading encryption method or anything. It was just a little project (and I have other reasons why I'm crafting my own encryption program, but it doesn't have to be super secure or anything). I'm not going to release the project and I'm going to make it for personal uses only.

Basically, all I do is offset bytes' values with the key's value.

E.g. 12 byte file, 8-bit encryption. Key value limits: 1 to 255. Let's say the key is 117
Each byte would be offset by 117 (i.e. a byte with value 4 would become 121). Thankfully if the byte value should go over 255, FB/GCC/the computer resets the value to 0 to stop overflow (I had assumed this didn't happen, which caused most of the more earlier problems...).
To decrypt, you simply perform the reverse operation (i.e. 121-117=4, the original value of our example byte).

8-bit encryption works just fine. >8-bit gets a whole lot more compilcated:

I have to make sure I encrypt groups of bytes instead of individual bytes, except at the end of the file, if there should be any extra bytes due to lof mod sizeof(key) > 0. My program below only adds the last byte of the key in such a case to each remaining byte.

E.g. 12 byte file, 64-bit encryption. Key value limits: 1 to 18446744073709551615. Let's say the key is 1000 (usually the randomly generated key is much larger than this, however).
XXXXXXXXXXXX
The first 8 X's (bytes) would be offset by the 1000 value. However, the remaining 4 bytes would be offset by... Because of the way my program is at the moment, 0... I intend to change this, though, and use the byte out of those 8 bytes with the largest value.

I realise there are a few flaws with this:
1. Often, there are a few bytes which aren't encrypted at all, within each group. Think of it as: you can see a few words out of this post, and others are "gibberish". This shouldn't really happen, and I intend to introduce a fancy encryption method which incorporates both the speed of 64-bit encryption along with the security of the 8-bit encryption. But obviously, right now, I'm having issues.
2. Remaining bytes problem has already been noted. Though usually the keys are so large, the last byte is bound to a have a suitable value anyway. This is suitable for now.
I intend to provide solutions to these problems.

This is my expectation. This expectation has been fulfilled, on occasions... Other times, as I have said, the new decrypted file just looks like more gibberish (in the case of the text file), sometimes the key changes (Idek how!), and other times nothing happened at all (when encrypting or decrypting: though hopefully that issue's long gone)!

Notes on Compiling and Running
There are a few things you have to know about compiling and running:

1. The program takes in command-line arguments, so you're going to have to run it using command prompt. You can use "binc h" (binc is the name I've given the code file) for some help, otherwise here are the details:

2. Syntax: binc e/d file key
Provide e for the first argument to encrypt, d to decrypt. The path of the file to encrypt/decrypt should be given at parameter 2, and parameter 3 gives the key.
With encrypting, if this parameter is blank, a random key is generated. Else you can specify your own key to apply.
With decrypting, the key parameter is compulsory.

3. Important!: the key is a special value. It isn't a denary value, nor is it hex. It uses a 36-base system using all the letters of the English alphabet: A = 10 and F = 15, like hex, though G = 16, H = 17, etc. and Z = 35, 10 = 36. There are functions from my personal library which deal with this system (not exclusively: quite contrary to usual, "right" methods, these functions treat A and a different. This is why upper case is obligatory: though the program turns all letters upper case anyway.). Keep this in mind if you want to test the software.

4. My personal library functions work. I have tested and used numerous times with my personal projects, and they all work (well, except the newer ones ;D - but none of these my program uses). I have ported the functions my program uses to the code file so that I can post the code.

List:
char() simply grabs a character from a string. It is used only in my other ported functions.
isbase() checks if a number is of a particular base. Only used with my other ported functions.
cbase() converts an any-base number to a base-10 number. Used in my actual program.
cden() converts a base-10 numbers to an any-base number. Used in my actual program. The base I use with these functions is 36, provided in the constant KEYBASE in case I want to change it later.
rndbet() courtesy to the FB doc ;) generates a random number between two values. Used with key generation.

5. My program, because of the way it has been designed, can not run 8/16/32/64 all at once in the same executable. You will have to uncomment one of the lines:
'dim shared as ubyte buff, key <- uncomment for 8-bit
'dim shared as ushort buff, key <- 16-bit
'dim shared as ulong buff, key <- 32-bit
'dim shared as ulongint buff, key <- 64-bit
I've been trying to find a way I can declare variables in a function according to a parameter, i.e.
if param = "byte" then dim as byte variable
But I've been having issues with that, mainly to do with scope.

Another note: it seems 64-bit encryption gives me the most hassle. Idk why.

Would appreciate feedback on how to fix this darn program :).

Oh, I almost forgot, the program:

Code: Select all

/'SOME NOTES FOR YOU GUYS
To compile, uncomment only one of the lines below. These declare the variables needed for the
process within the bincryption function.
Why do they have global scope?
	a. I wanted to make it so that there was only one function. This was the solution I came up with
	after many attempts.
	b. I can can get away with this because this program will only run once per time. I'm not
	designing a function, I'm designing a program.
Other notes will be in my post.'/

'DIFFERENT VARIABLES FOR DIFFERENT BIT-LENGTH SETTINGS:
'comment these out as appropriate to compile each version of binc
'dim shared as ubyte buff, key
'dim shared as ushort buff, key
'dim shared as ulong buff, key
'dim shared as ulongint buff, key

#include "file.bi"
'functions from my personal library, ported over
function char(byval mstr as string, byval i as ulong = 0) as string
	if i > len(mstr) then i = len(mstr)
	if i = 0 then i = 1
	return mid(mstr, i, 1)
end function
function isbase(byval stringOfNumbers as string, byval baseNumber as ubyte = 10) as boolean
	if baseNumber = 0 then baseNumber = 1
	if baseNumber > 62 then baseNumber = 62
	dim digits(1 to 62) as string = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"}
	dim flag as boolean = true
	dim count as ubyte
	for loopVar as ulongint = 1 to len(stringOfNumbers)
		count = 0
		for innerVar as ubyte = 1 to baseNumber
			if char(stringOfNumbers, loopVar) <> digits(innerVar) then count += 1
		next innerVar
		if count = baseNumber then flag = false: exit for
	next loopVar
	return flag
end function
function cbase(byval stringOfNumbers as string, byval baseNumberOfInput as ubyte = 16) as ulongint
	if stringOfNumbers = "0" then return 0
	if baseNumberOfInput = 0 then baseNumberOfInput = 1
	if baseNumberOfInput > 62 then baseNumberOfInput = 62
	if isbase(stringOfNumbers, baseNumberOfInput) = 0 then return 0
	if baseNumberOfInput = 10 then return val(stringOfNumbers)
	dim as string tempChr
	dim as ubyte tempNumber
	dim as ulongint digits(1 to len(stringOfNumbers))
	for loopVar as ulongint = len(stringOfNumbers) to 1 step -1
		tempChr = char(stringOfNumbers, loopVar)
		select case tempChr
			case "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"
				tempNumber = val(tempChr)
			case else
				if asc(tempChr) >= 65 and asc(tempChr) <= 90 then
					tempNumber = asc(tempChr)-55
				elseif asc(tempChr) >= 97 and asc(tempChr) <= 122 then
					tempNumber = asc(tempChr)-61
				endif
		end select
		digits(loopVar) = tempNumber*baseNumberOfInput^(len(stringOfNumbers)-loopVar)
	next
	dim as ulongint finalAmount
	for loopVar as ulongint = 1 to len(stringOfNumbers) 'note that order doesn't matter: we are only adding every number together
		finalAmount += digits(loopVar)
	next
	return finalAmount
end function
function cden(byval denary as ulongint, byval baseOfOutput as ubyte = 16) as string
	dim as string*1 alphabet(1 to 26) => {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"}
	if denary = 0 then return "0"
	if baseOfOutput = 0 then baseOfOutput = 1
	if baseOfOutput > 62 then baseOfOutput = 62
	redim as ubyte remainders(1 to 1)
	dim as ulongint number = denary, count = 1
	while number > 0
		redim preserve remainders(1 to count)
		remainders(count) = number mod baseOfOutput
		number = int(number/baseOfOutput)
		count += 1
	wend
	dim ret as string
	for loopVar as ulongint = count to 1 step -1
		if remainders(loopVar) >= 0 and remainders(loopVar) <= 9 then
			ret += str(remainders(loopVar))
		elseif remainders(loopVar) >= 10 and remainders(loopVar) <= 35 then
			ret += ucase(alphabet(remainders(loopVar)-9))
		elseif remainders(loopVar) >= 36 and remainders(loopVar) <= 62 then
			ret += alphabet(remainders(loopVar)-35)
		endif
	next
	return right(ret, len(ret)-1)
end function
function rndbet(byval first as double, byval last as double) as double
	return rnd * (last - first) + first
end function

randomize
chdir(exepath) 'this is here because running using NppExec on Notepad++ does screwy things with curdir :(. You can remove it with no harm.
const KEYBASE = 36
const BAD_FILE = "invalid-file"
const EMPTY_FILE = "empty-file"

'true = encrypt, false = decrypt
function bincryption(byval file as string, byval operation as boolean, byval keyinp as string, byval minval as ulongint = 1, byval maxval as ulongint = 0) as string
	dim as ubyte bytebuff
	if maxval = 0 then
		'and yes, I know you can do: maxval = 256^sizeof(key)-1, but that was giving me issues with 64-bit encryption
		if sizeof(key) = 1 then maxval = 255
		if sizeof(key) = 2 then maxval = 65535
		if sizeof(key) = 4 then maxval = 4294967295
		if sizeof(key) = 8 then maxval = 18446744073709551615
	endif
	if maxval < minval then swap maxval, minval
	
	if operation then
		if keyinp = "" then key = rndbet(minval, maxval) else key = cbase(keyinp, KEYBASE)
	else
		if keyinp = "" then return "" else key = cbase(keyinp, KEYBASE)
	endif
	
	if fileexists(file) = 0 then return BAD_FILE
	if open(file for binary as #1) then return BAD_FILE
	if lof(1) = 0 then close #1: return EMPTY_FILE
	
	if lof(1) < sizeof(buff) or sizeof(buff) = 1 then
		for i as ulongint = 1 to lof(1)
			get #1, i, bytebuff
			if operation then bytebuff += cbase(left(bin(key), 8), 2) else bytebuff -= cbase(left(bin(key), 8), 2)
			put #1, i, bytebuff
		next
	else
		for i as ulongint = 1 to lof(1)-(lof(1) mod sizeof(buff)) step sizeof(buff)
			get #1, i, buff
			if operation then buff += key else buff -= key
			put #1, i, buff
		next
		if lof(1) mod sizeof(buff) > 0 then
			for i as ulongint = (lof(1) mod sizeof(buff))+1 to lof(1)
				get #1, i, bytebuff
				if operation then bytebuff += cbase(left(bin(key), 8), 2) else bytebuff -= cbase(left(bin(key), 8), 2)
				put #1, i, bytebuff
			next
		endif
	endif
	
	close #1
	return cden(key, KEYBASE)
end function

#define SHUTDOWN color loword(curcol), hiword(curcol): system
dim as uinteger curcol = color
color 12, 0
print
dim as boolean opin
dim as string optesting = lcase(command(1))
if optesting = "enc" or optesting = "e" or optesting = "encrypt" or optesting = "true" or optesting = "t" or optesting = "1" or optesting = "+" then
	opin = true
	color 11, 0
elseif optesting = "dec" or optesting = "d" or optesting = "decrypt" or optesting = "false" or optesting = "f" or optesting = "0" or optesting = "-" then
	opin = false
	color 10, 0
elseif optesting = "help" or optesting = "h" or optesting = "?" or optesting = "/help" or optesting = "/h" or optesting = "/?" then
	print "syntax: binc e/d/h file key":print
	print "Provide e to encrypt file with key, or with random key if key is blank."
	print "Provide d to decrypt file with key."
	print "Provide h for this help."
	print "PLEASE RUN THIS IN THE COMMAND PROMPT."
	sleep
	SHUTDOWN
else
	print "invalid-operation"
	print "In command(1), type e to encrypt, or d to decrypt.":print
	print "Type 'binc h' for help."
	SHUTDOWN
endif

dim as string keyin = ucase(command(3))
if (cbool(keyin <> "") or opin = false) and cbool(cbase(keyin, KEYBASE) = 0) then
	print "invalid-key"
	print "command(3) should be:"
	print "	encrypt:"
	print "		BLANK to choose random key, or you can provide a valid key to apply."
	print "	decrypt:"
	print "		Must provide a key to apply to the encrypted file. Cannot be blank."
	print "A valid key cannot have any other character than a number or letter character. You provided: "+command(3)
	print "Upper case is compulsory, though the program automatically causes any lower-case letters to be upper-case."
	SHUTDOWN
endif

dim as string ret = bincryption(command(2), opin, keyin)
print "Return value: "+ret
if ret = BAD_FILE then
	if command(2) = "" then print "You did not provide a file to encrypt or decrypt." else print "The provided file doesn't exist or could otherwise not be opened.": print "Make sure no other program has this file opened and try again."
elseif ret = EMPTY_FILE then
	print "The provided file was empty and so could not be encrypted/decrypted."
else
	if opin then
		print "Please copy this key! If you lose it, you wont be able to decrypt your data!"
		print "After copying, please press enter to shut the process down."
		sleep
	else
		print "That key was used for decryption."
		print "Decryption seemed to work. Decryption will only be fully successful if you provided the right key."
	endif
endif
SHUTDOWN
There is no particular file I test on: pretty much anything will do.

Thanks for your patience.
Last edited by datwill310 on Mar 13, 2017 16:55, edited 1 time in total.
MrSwiss
Posts: 3910
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: My encryption method just wont work

Post by MrSwiss »

Just some comments:
1) I'd start small, e.g. a UByte at a time (the 8 bit stuff)
2) for starters, use a fixed value as *key* + a direction flag (for en-/de- crypting likewise)
3) You'll have to use a *round robin algo* to overcome limitation of 255:
-- if result > 255 then result -= 255
-- if result < 0 then result += 255 (use at least a Short variable, during conversion!)
This only applies a simple shift, e.g. KEY = 10 then:
direction up :
"A" = chr(65) --> chr(75) etc.
direction down :
"A" = chr(65) --> chr(55) etc.

Just ideas, if you later on, want to use larger KEY's, you'll have to use padding:
' assuming a U-/Long
if len(string) mod 4 <> 0 then ' pad to achieve a mod of: 0/zero ...
datwill310
Posts: 355
Joined: May 29, 2015 20:37

Re: My encryption method just wont work

Post by datwill310 »

MrSwiss wrote:Just some comments:
1) I'd start small, e.g. a UByte at a time (the 8 bit stuff)
2) for starters, use a fixed value as *key* + a direction flag (for en-/de- crypting likewise)
3) You'll have to use a *round robin algo* to overcome limitation of 255:
-- if result > 255 then result -= 255
-- if result < 0 then result += 255 (use at least a Short variable, during conversion!)
This only applies a simple shift, e.g. KEY = 10 then:
direction up :
"A" = chr(65) --> chr(75) etc.
direction down :
"A" = chr(65) --> chr(55) etc.

Just ideas, if you later on, want to use larger KEY's, you'll have to use padding:
' assuming a U-/Long
if len(string) mod 4 <> 0 then ' pad to achieve a mod of: 0/zero ...
Thanks for your reply.

1. Yup, this works no problem in my program. The problem lies within >8-bit.
2. This is also in the form of the operation flag, is this what you mean? I originally allowed for both negative and positive values, but I can't really incorporate this as easily due to my personal number-base functions not dealing with negative values (I can think of ways to incorporate negative values into the encryption function, but I just don't think I need to).

Concerning larger keys: this actually automatically happened when I didn't check if lof mod sizeof(key): random NULL characters would spawn in the decrypted file at the end. Yes: your idea to pad makes sense, though I'd have to record the extra NULL bytes in the key (something like this: KEY-EXTRAS, eg 1L5-1), because, in decryption, how would the process know that there were or were not those ending NULL characters in the original file?

Thanks for your input, though I'm not quite sure what you mean in point 3 (eg "to overcome limitation of 255": do you mean about applying >8-bit encryption, or about overflow?).
MrSwiss
Posts: 3910
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: My encryption method just wont work

Post by MrSwiss »

datwill310 wrote:do you mean about applying >8-bit encryption, or about overflow?).
Not > 8 bit, overflow processing in 8 bit. E.g. if shift by 10, *up* > 255 then result -= 255 (Dim As Short result).

No padding of *KEY*, the lenght of the String (to encrypt) needs padding, if KEY-size > UByte.
datwill310
Posts: 355
Joined: May 29, 2015 20:37

Re: My encryption method just wont work

Post by datwill310 »

MrSwiss wrote:
datwill310 wrote:do you mean about applying >8-bit encryption, or about overflow?).
Not > 8 bit, overflow processing in 8 bit. E.g. if shift by 10, *up* > 255 then result -= 255 (Dim As Short result).
Ah.

I was think more along the lines of:

if shift up 250 by 10 is > 255, then result:
250 + 5 = 255
10 turns into 5
255 -> 0
5 turns into 4
0 + 4 = 4
4 turns into 0: encrypted.

As what if shift up by, let's say 255, is > 255 (which it will be if byte to encrypt != 0): So we shift down instead of up, but THAT would turn out < 0 (unless byte to encrypt = 255), causing you to be kinda stuck ;)

Does current overflow prevention mechanics work like this? Should I be dependant on this feature?
datwill310
Posts: 355
Joined: May 29, 2015 20:37

Re: My encryption method just wont work

Post by datwill310 »

MrSwiss wrote:No padding of *KEY*, the lenght of the String (to encrypt) needs padding, if KEY-size > UByte.
Missed this: must have edited.

Sorry about that, I meant the length of the file to encrypt (i.e. string), but the extra bytes caused by padding are to be recorded in the key so that we can get the original file byte-perfect, so to speak.
MrSwiss
Posts: 3910
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: My encryption method just wont work

Post by MrSwiss »

datwill310 wrote:if shift up 250 by 10 is > 255, then result:
250 + 10 = 260, next step: 260 - 256 = 4 (=whatever you have, will be
within: 0 to 255, overflow causes 0 to 9 to be returned).
Changing processing otherwise, needs a far more complex routine ...
Last edited by MrSwiss on Mar 13, 2017 16:51, edited 3 times in total.
datwill310
Posts: 355
Joined: May 29, 2015 20:37

Re: My encryption method just wont work

Post by datwill310 »

MrSwiss wrote:
datwill310 wrote:if shift up 250 by 10 is > 255, then result:
250 + 10 = 260, next step: 260 - 255 = 5 (=whatever you have, will be within: 0 to 255).
Changing processing otherwise, needs a far more complex routine ...
Ah, that makes sense. Will use this over my current method. Maybe my old method was the problem?
MrSwiss
Posts: 3910
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: My encryption method just wont work

Post by MrSwiss »

Attention: there was a change needed ... see below!
datwill310
Posts: 355
Joined: May 29, 2015 20:37

Re: My encryption method just wont work

Post by datwill310 »

MrSwiss wrote:Attention: there was a change needed ... see below!
Wait a second... I'd said I wouldn't release this project, and I went ahead and released the code. Am I stupid lol. I put the message on my original post (?). Anyways thanks for the suggestions.
Post Reply