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.
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
Thanks for your patience.