We have developed an application in Delphi, and there the small database can be Encrypted.  We new that the encryption is no fool proof, but we were in an impression that it is not easily breakable. To our horror we discovered that the crypting of this database is actually nothing. The password can be encrypted and found in less than a minute. There are even tools available im web to do just that.

The Encrypting and Decrypted is done in this way:

procedure EncryptBuffer(var Buffer; NumBytes: Integer;
                        Key1: Byte; Key2: Byte);
begin
  asm
    PUSH    EDI
    PUSH    EBX
    XOR        ECX,ECX
    MOV        ECX,NumBytes
    JCXZ    @@Done
    XOR        EBX,EBX
    MOV      BL,Key1
    MOV        BH,Key2
    MOV      EDI,Buffer
  @@Loop1:
    XOR        EAX,EAX
    MOV      AL,
    XOR        AL,BL
    XOR        AL,BH
    MOV      ,AL
    INC        BL
    DEC        BH
    INC      EDI
    LOOP      @@Loop1
  @@Done:
    POP        EBX
    POP        EDI
  end;
end;

procedure DecryptBuffer(var Buffer; NumBytes: Integer;
                        Key1: Byte; Key2: Byte);
begin
  asm
    PUSH    EDI
    PUSH    EBX
    XOR        ECX,ECX
    MOV      ECX,NumBytes
    JCXZ    @@Done
    XOR        EBX,EBX
    MOV      BL,Key1
    MOV      BH,Key2
    MOV        EDI,Buffer
  @@Loop1:
    XOR        EAX,EAX
    MOV      AL,
    XOR        AL,BH
    XOR        AL,BL
    MOV        ,AL
    INC        BL
    DEC        BH
    INC        EDI
    LOOP      @@Loop1
  @@Done:
    POP        EBX
    POP        EDI
  end;
end;

So my question is, is there anything we could quite fast and simply do to alter this code in some way, so at least those ready made tools could not break it? Maybe one more assembler line somewhere there in between?
Of course the right way to do would be to switch to some other database. But the conversion will take time, and some instant, temporary help would be great.

In the old days I used to know something about CHASM assembler. But the time has passed, and I am not able to change anything in that code.

Any suggestions, please?
Mark
Posted on 2011-08-27 11:23:02 by Mark Laitila
Making the code more convoluted will not help, as the implementation/approach is obviously flawed.

If you want real encryption, look into provable encryption algorithm such as Blowfish. Find an open-source implementation, link to its encryption/decryption functions, and be done with it.

The other -- more intrusive -- option is to utilize an alternative database software such as SQLite, which have encryption capabilities.
Posted on 2011-08-27 12:42:33 by SpooK
For AES library see Brian Gladman's Cryptographic Technology website.  Good stuff there.
Posted on 2011-08-27 13:32:43 by p1ranha

Making the code more convoluted

Those existing "password recovery" tools know and have the original ASM-code and that way they have had easy way in. My idea was not to try to make the code and logic more messy. Just a bit different would be fine, so the recovery utility would not work.
I completely understand the remaining weakness.

If you want real encryption, look into provable encryption algorithm
Of course, there are tons of crypting routines. Take a week or two, and you will know much more about encryption and the use of those libraries than you knew earlier:)

The other -- more intrusive -- option is to utilize an alternative database

I also did mention in the original question that it would be an real option

At this point I really was after a quick hack. Maybe one more XOR:ing with some value, to get some modification to the existing logic.

Thanks for the suggestions anyway
Mark
Posted on 2011-08-27 16:25:53 by Mark Laitila
Yes, there is something quick and easy.

Random Number Generator algorithms are always predictable, given an initial known seed value. That is to say, they spew out the same sequence of 'random' values, given the same starting value.

Pick one, give it a known seed, and then xor your data with generated randoms - have the decoder do the same thing, starting with the same known seed.

Now you have a private key encryption, using an unknown cypher.
Posted on 2011-08-27 22:05:48 by Homer
Random Number Generator algorithms are always predictable, given an initial known seed value.
They are predictable, but once you do not have the ASM code nor the 20 char long password, it should not be childrens play. Once someone unassembles our application code and finds the logic, then they will get through.

My original thought was, that maybe there is some 2 lines of change to the ASM code, that would prevent the existing cracker apps to get through.
And one would at least have to unassembe our app. Is this kind of simple ASM change possible?

That is to say, they spew out the same sequence of 'random' values, given the same starting value.
I have used for years string crypting so, that I first add 10 chars of truly randomized data to the beginning. And after that I add the actual string encrypted, with simple crypt algorithm. Every crypted character feeds new seed value to the next char to be crypted.

I am not quite sure if that included ASM snippet uses this logic or not? With my encryption, those those 10 random chars at the beginning seem to be enough to keep 99.5% of snoopers away.

By reading the ASM snippet, I think it is not possible to use this kind of approach at all? Add let's say 2 byte to the beginning? As the routine logic takes f.ex 20 bytes in and it has to put exactly 20 bytes out too.

Pick one, give it a known seed, and then xor your data with generated randoms - have the decoder do the same thing, starting with the same known seed.
If this was a real suggestion, then my first problem I can't do that in ASM.

Maybe there is no quick hack available at all? 

I thank for all the comments I have got this far.
Mark
Posted on 2011-08-28 04:12:40 by Mark Laitila

If this was a real suggestion, then my first problem I can't do that in ASM.


Then don't do it in asm?
A bit of random and xoring bytes together can be done in most languages.
I really don't see why your encryption code was written in ASM in the first place.
It doesn't make it more secure.
Posted on 2011-08-28 04:46:48 by Scali
Then don't do it in asm?
All right, this may be a good advice, with modern compute force.


I really don't see why your encryption code was written in ASM in the first place.
It doesn't make it more secure.
According to those third party developers, security has not originally (8..10 yrs ago) played the major role with that language choice. But the speed has.
SQL query scanning through some 200+ MByte database, there he fractions of a second start to matter.

After all, the advice on the first line may be the one I really should follow now.

Thanks
Mark
Posted on 2011-08-28 06:29:23 by Mark Laitila

According to those third party developers, security has not originally (8..10 yrs ago) played the major role with that language choice. But the speed has.
SQL query scanning through some 200+ MByte database, there he fractions of a second start to matter.


The code you posted was extremely trivial asm, not something that a reasonbly decent compiler cannot do.
Sure, if it was highly optimized SSE code, then it may be out of reach of a compiler... But this was just vanilla 386 code, nothing fancy.
Writing something in ASM doesn't automatically make it faster.
On the contrary, most programmers cannot optimize as well as a modern compiler.
For example, your code uses the 'loop' instruction, which is considerably slower than just sub ecx, 1 jnz. Something every compiler knows, but apparently not the person who wrote that asm.

I could write a C-version and compile it, and benchmark to prove the point... but I don't think I have to.
Posted on 2011-08-28 07:40:22 by Scali
Okay, I had some time to kill, so I decided to write a C version.
I hope you realize that the algo is fully reversible, and EncryptBuffer is exactly the same as DecryptBuffer().

In C, the entire algo can be put on one line, it is THAT simple.
Here is my full test program:

#include "stdafx.h"
#include <Windows.h>
#include <string>

void EncryptBuffer(char* Buffer, unsigned int NumBytes, char Key1, char Key2)
{
  __asm
  {
PUSH    EDI
PUSH    EBX
XOR        ECX,ECX
MOV        ECX,NumBytes
JCXZ    Done
XOR        EBX,EBX
MOV      BL,Key1
MOV        BH,Key2
MOV      EDI,Buffer
  Loop1:
XOR        EAX,EAX
MOV      AL,
XOR        AL,BL
XOR        AL,BH
MOV      ,AL
INC        BL
DEC        BH
INC      EDI
LOOP      Loop1
  Done:
POP        EBX
POP        EDI
  }
}

void DecryptBuffer(char* Buffer, unsigned int NumBytes, char Key1, char Key2)
{
__asm
{
PUSH    EDI
PUSH    EBX
XOR        ECX,ECX
MOV      ECX,NumBytes
JCXZ    Done
XOR        EBX,EBX
MOV      BL,Key1
MOV      BH,Key2
MOV        EDI,Buffer
  Loop1:
XOR        EAX,EAX
MOV      AL,
XOR        AL,BH
XOR        AL,BL
MOV        ,AL
INC        BL
DEC        BH
INC        EDI
LOOP      Loop1
  Done:
POP        EBX
POP        EDI
}
}

void EncryptBufferC(char* pBuffer, unsigned int length, char key1, char key2)
{
for (unsigned int i = 0; i < length; i++)
pBuffer ^= (key1++ ^ key2--);
}

int _tmain(int argc, _TCHAR* argv[])
{
char Key1 = 0x34;
char Key2 = 0x73;

char testString[] = "Testing testing";

LARGE_INTEGER frequency, startTime, endTime;

QueryPerformanceFrequency(&frequency);

QueryPerformanceCounter(&startTime);

int len = strlen(testString);

for (int i = 0; i < 1000000; i++)
EncryptBuffer( testString, len, Key1, Key2 );

QueryPerformanceCounter(&endTime);

printf("ASM time: %f\n", (endTime.QuadPart-startTime.QuadPart)/(double)frequency.QuadPart);

QueryPerformanceCounter(&startTime);

for (int i = 0; i < 1000000; i++)
EncryptBufferC( testString, len, Key1, Key2 );

QueryPerformanceCounter(&endTime);

printf("C time: %f\n", (endTime.QuadPart-startTime.QuadPart)/(double)frequency.QuadPart);

getchar();

return 0;
}


The results on my Core2 Duo laptop speak volumes:
ASM time: 0.073059
C time: 0.032137

The simple C version is more than twice as fast.
Posted on 2011-08-30 07:55:06 by Scali
The simple C version is more than twice as fast.
Thanks, not bad at all, for a voluntary work:) I thought that ASM would do it better especially with this kind of tasks. But the Compiler Optimizations, they really seem to be quite good now a days.

> void EncryptBufferC(char* pBuffer, unsigned int length, char key1,
>char  key2)

I believe I am able to convert that to Delphi/Pascal.  I'll have to go a bit further anyway, and use algorithm that feeds the new seed after every character, so the crypting will not be quite as simple to break . I again hope this fix will not choke the DB engine.

I really should switch to a new DB engine, but that would have tons of side effects and code re-writing. I'll have to go this way right now, and with simple fixes try to make the crypting a bit tighter.

Thanks for the benchmark sample.
Mark
Posted on 2011-08-30 11:28:04 by Mark Laitila
Hello,
If you want a simple modification you can for example insert ROL and ROR instructions like this:
...
    XOR      AL,BL
    ROL AL,3
    XOR      AL,BH
...
    XOR        AL,BH
    ROR AL,3
    XOR        AL,BL
...
This of course means old passwords will not work,  but then you might as well use completely different algorithm?!

Now another question is:

The password can be encrypted and found in less than a minute

What is the relation between password and key1 key2. How is the password stored and converted to key1 and key2?
If the password is entered in plain text by the user (Posted on 2011-08-30 11:37:28 by drizz
Well, it's been a while since I've done Delphi, but I think it will be something like this:


for i := 0 to NumBytes
begin
  Buffer := Buffer xor Key1 xor Key2;
  Key1 := Key1 + 1;
  Key2 := Key2 - 1;
end



I thought that ASM would do it better especially with this kind of tasks. But the Compiler Optimizations, they really seem to be quite good now a days.


Well, it's not assembly's fault, it's the fault of whoever wrote that code.
They didn't do a good job of optimizing. Not just 'now a days' either. 'loop' has been a slow instruction at least since the Pentium, back in 1992. So I bet that even some 20 years ago, the compiler would have beaten this assembly code.

It is possible to come up with code that is as good or better than the compiler. But not every programmer can do that. And a lot of these programmers either don't even know that they can't, or they refuse to admit it.
Posted on 2011-08-30 11:40:10 by Scali