3

I'm coding an application on both C and Delphi, I've XTEA Encryption in both, its exactly the same but the problem is the output is different, even though the delta and N is the same. I don't really know whats wrong

Here is the Delphi code

type
  TTeaMsgBlock = array[0..1] of LongWord;
  TTeaKeyBlock = array[0..3] of LongWord;

const   
  DELTA = $9e3779b9;
  N = 32;

procedure XTeaCrypt(var V: TTeaMsgBlock; const K: TTeaKeyBlock);
var
  I: LongWord;
  S: Int64;
begin
  S := 0;
  for I := 0 to N - 1 do begin
    Inc(V[0], (((V[1] shl 4) xor (V[1] shr 5)) + V[1]) xor (S + K[S and 3]));
    Inc(S, DELTA);
    Inc(V[1], (((V[0] shl 4) xor (V[0] shr 5)) + V[0]) xor (S + K[(S shr 11) and 3]));
  end;
end;

function XTeaCryptStr(const Msg, Pwd: string): string;
var
  V: TTeaMsgBlock;
  K: TTeaKeyBlock;
  I, L, N: Integer;
begin
  L := Length(Pwd); if L > SizeOf(K) then L := SizeOf(K);
  K[0] := 0; K[1] := 0; K[2] := 0; K[3] := 0; Move(Pwd[1], K[0], L);

  I := 1; L := Length(Msg);
  if L > 0 then SetLength(Result, ((L - 1) div SizeOf(V) + 1) * SizeOf(V))
           else SetLength(Result, 0);
  while I <= L do begin
    V[0] := 0; V[1] := 0;
    N := L - I + 1; if N > SizeOf(V) then N := SizeOf(V);
    Move(Msg[I], V[0], N);
    XTeaCrypt(V, K);
    Move(V[0], Result[I], SizeOf(V));
    Inc(I, SizeOf(V))
  end;
end;

//Test
const Key: array [0..15] of char = (char($00), char($01), char($02), char($03), char($04), char($05), 
                                char($06), char($07), char($08), char($09), char($0a), char($0b), 
                                char($0c), char($0d), char($0e), char($0f));
const Msg: string = 'This Is#';

begin
 WriteLn('Encrypted: ' + pChar(XTeaCryptStr(Msg, Key)));
end.

and thats the C code (Taken From PolarSSL)

typedef struct
{
    uint32_t k[4];       /*!< key */
} xtea_context;

#ifndef GET_ULONG_BE
#define GET_ULONG_BE(n,b,i)                             \
{                                                       \
    (n) = ( (unsigned long) (b)[(i)    ] << 24 )        \
        | ( (unsigned long) (b)[(i) + 1] << 16 )        \
        | ( (unsigned long) (b)[(i) + 2] <<  8 )        \
        | ( (unsigned long) (b)[(i) + 3]       );       \
}
#endif

#ifndef PUT_ULONG_BE
#define PUT_ULONG_BE(n,b,i)                             \
{                                                       \
    (b)[(i)    ] = (unsigned char) ( (n) >> 24 );       \
    (b)[(i) + 1] = (unsigned char) ( (n) >> 16 );       \
    (b)[(i) + 2] = (unsigned char) ( (n) >>  8 );       \
    (b)[(i) + 3] = (unsigned char) ( (n)       );       \
}
#endif

/*
 * XTEA key schedule
 */
void xtea_setup( xtea_context *ctx, unsigned char key[16] )
{
    int i;

    memset(ctx, 0, sizeof(xtea_context));

    for( i = 0; i < 4; i++ )
    {
        GET_ULONG_BE( ctx->k[i], key, i << 2 );
    }
}

/*
 * XTEA encrypt function
 */
int xtea_crypt_ecb( xtea_context *ctx, int mode, unsigned char input[8],
                     unsigned char output[8])
{
    uint32_t *k, v0, v1, i;

    k = ctx->k;

    GET_ULONG_BE( v0, input, 0 );
    GET_ULONG_BE( v1, input, 4 );

    if( mode == XTEA_ENCRYPT )
    {
        uint32_t sum = 0, delta = 0x9E3779B9;

        for( i = 0; i < 32; i++ )
        {
            v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + k[sum & 3]);
            sum += delta;
            v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + k[(sum>>11) & 3]);
        }
    }
    else /* XTEA_DECRYPT */
    {
        uint32_t delta = 0x9E3779B9, sum = delta * 32;

        for( i = 0; i < 32; i++ )
        {
            v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + k[(sum>>11) & 3]);
            sum -= delta;
            v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + k[sum & 3]);
        }
    }

    PUT_ULONG_BE( v0, output, 0 );
    PUT_ULONG_BE( v1, output, 4 );
    output[8] = '\0';

    return( 0 );
}

//test
int main()
{
    int i;
    unsigned char buf[8] = "This Is#";
    xtea_context ctx;
    xtea_setup( &ctx, (unsigned char *) xtea_test_key);
    xtea_crypt_ecb( &ctx, XTEA_ENCRYPT, buf, buf );
    printf("Encrypted = %s\n", buf);
    return 0;
}

but the output is totally different :/ What am I doing wrong?

Johan
  • 74,508
  • 24
  • 191
  • 319
killercode
  • 1,666
  • 5
  • 29
  • 42

4 Answers4

5

Firstly, I have not tried to execute your code: my comments are based purely on inspection.

I suspect the root of your problem is confusion between big-endian and little-endian data storage.

The macros in the C code (GET_ULONG_BE and PUT_ULONG_BE) are extracting the raw data and converting to uint32 in BIG-endian format.

In Delphi, you are copying the raw data from bytes to LongWord format without doing a little-endian to big-endian conversion.

Apart from that, I'm not sure about the declaration of S as Int64, but that is probably minor in comparison to the big-end/little-end problem.

Edit: You need to do the big-endian conversion in your XTeaCryptStr() routine. You need to apply it to the 4 elements of the key structure, and to your data block - both before (the data going in) and after (the result coming out) the call to the main encryption routine.

Edit: Started trying to run the Delphi code. First problem is that your test password starts with a nul character. You need to use a different password (without nul characters).

Yet another edit:

I inserted calls to SwapEndian() as below:

function XTeaCryptStr(const Msg, Pwd: string): string;
    var
      V: TTeaMsgBlock;
      K: TTeaKeyBlock;
      I, L, N: Integer;
  begin
  L := Length(Pwd);
  if L > SizeOf(K) then
    L := SizeOf(K);
  K[0] := 0; K[1] := 0; K[2] := 0; K[3] := 0;
  Move(Pwd[1], K[0], L);
  for i := 0 to 3 do
    K[i] := SwapEndian( K[i] );

  I := 1; L := Length(Msg);
  if L > 0 then
    SetLength(Result, ((L - 1) div SizeOf(V) + 1) * SizeOf(V))
  else
    SetLength(Result, 0);
  while I <= L do
    begin
    V[0] := 0; V[1] := 0;
    N := L - I + 1;
    if N > SizeOf(V) then
      N := SizeOf(V);
    Move(Msg[I], V[0], N);

    V[0] := SwapEndian( V[0] );
    V[1] := SwapEndian( V[1] );

    XTeaCrypt(V, K);

    V[0] := SwapEndian( V[0] );
    V[1] := SwapEndian( V[1] );

    Move(V[0], Result[I], SizeOf(V));
    Inc(I, SizeOf(V))
    end;
  end;

With this (and a password that doesn't contain nul characters), I got the same result from the C and Delphi code.

Alistair Ward
  • 1,093
  • 9
  • 20
  • im not that good with big endian and little endian thing, can u suggest how can i remove that conversion from the C code without breaking it? thanks – killercode Feb 27 '11 at 11:46
  • no, you don't want to remove the conversion from the C code. Instead, you want to *add* the conversion to the Delphi code. See question http://stackoverflow.com/questions/3065335/how-to-convert-big-endian-numbers-to-native-numbers-delphi for a routine to do this. – Alistair Ward Feb 27 '11 at 12:00
  • In function XteaCryptStr(). After Move(Pwd[1],Key[0],L) you need to to change the endianness of each element of the Key structure, and you need to change the endianness of your datablock (V[0] and V[1]) immediately before and immediately after the call to XTeaCrypt – Alistair Ward Feb 27 '11 at 12:19
  • thats much harder than it seemed, i tried with a loop and did what u said, but sometimes it crashes and sometimes it displays a totally diffrent output...can u give me a hand? – killercode Feb 27 '11 at 12:57
  • thanks alot :), so everything now is big endian? i should remove the calls to big endian if the pc running the delphi code is big endian? – killercode Mar 02 '11 at 11:40
  • The chances of Delphi running on a big-endian processor are fairly slim :-) The better method (if you were really concerned) would be to write a pair of Delphi functions that duplicate the GET_ and PUT_ macros - the code would then become processor independent. – Alistair Ward Mar 03 '11 at 02:17
  • ```First problem is that your test password starts with a nul character. You need to use a different password (without nul characters).``` - wrong, xtea passwords can contain null bytes. in fact, 16 null bytes is a fully valid XTEA password. (if his delphi implementation cannot handle null bytes in the password, then that is an implementation bug, because the XTEA algorithm has no problems with it, given that the key is 4x uint32_t) – hanshenrik Mar 06 '19 at 17:41
4

You have failed copying the code from PolarSSL.

There are several bugs in the C source that were the tip-off. :)

First, no ECB encryption code would EVER include:

output[8] = '\0';

The whole point of ECB mode is to change one block of input into another block of input. This line has no place in an ECB routine -- it is obviously related to string-output. (In fact, this is also writing beyond the end of the buffer; the last writable position in array[n] is array[n-1]. So you're just scribbling on unrelated memory. This line was probably put into place because the test array buf in main is too short to hold its own ASCII NUL -- change that to buf[9].)

I would strongly suggest testing both your C and Delphi code against test vectors (sorry about the horrible colors on that page, nothing to be done about it except get the data and close the window again quickly! :) to discover which, if either, of your programs are correct.

PLEASE do not use ECB mode directly. When used improperly, it is too easy to provide zero privacy and integrity assurances. Building proper protocols and modes of operation out of ECB is very difficult work; please use OFB, CFB, CTR, CBC, PCBC or one of the newer authenticated encryption modes instead.

EDIT: This in your Delphi looks different: (V[1] xor S). Take another look at the lines from C; I think you've mis-translated the Delphi version in the two key lines of the routine. (I misread. Sorry.)

sarnold
  • 102,305
  • 22
  • 181
  • 238
  • i added the '\0' since i was testing on Console (cmd.exe) so it displays correctly, and this is just for test, also this isn't Really ECB or CBC or anything, its only 1 Block, i cant do CBC with 1 block :D, i got it as small as possible to test it against the delphi one. so any idea why the output isnt the same? thanks. – killercode Feb 27 '11 at 10:47
  • @killercode, I strongly recommend putting the `'\0'` in the correct location in the code right now; if you don't, you're just setting yourself up for impossible-to-find memory corruption down the road. :) – sarnold Feb 27 '11 at 10:49
  • @killercode thanks ;) That makes me feel better. I noticed `V[1] xor S` in the Delphi code, but couldn't figure out how you derived that from the C code -- I suggest taking another look at the core lines of the algorithm specifically. – sarnold Feb 27 '11 at 10:54
  • i didnt clone the brackets exactly as they were, in the c code, but i did now, diffrent result than the old delphi one, but still not the same as the c one – killercode Feb 27 '11 at 11:02
  • @killercode, my apologies, I managed to completely botch reading the Delphi -- the code looks like it corresponds quite well --definitely bed time now. :) Sorry to send you hunting down that wrong path. – sarnold Feb 27 '11 at 11:08
  • all good, going some path is better than standing still, i still dunno whats wrong :/ – killercode Feb 27 '11 at 11:10
1

I don't know why my previous answer was downvotedm then deleted.

Let's try again.

If you use the same C code in your Windows app and on the microprocessor then any standard encryption should work Just choose one.

See

http://www.drbob42.com/examines/examin92.htm
http://www.hflib.gov.cn/e_book/e_book_file/bcb/ch06.htm (Using C++ Code in Delphi)
http://edn.embarcadero.com/article/10156#H11

It looks like you need to use a DLL, but you can statically link it if you don't want to distribute it

  • Looks like my bounty was already awarded by the system, but this is the only answer that matches my bounty (as opposed to the original question), so I think that is what I will do - put the C code into a DLL on the PC and statically link it with my Delphi app and just use it as-is on the microprocessor. – Mawg says reinstate Monica Dec 20 '12 at 03:34
0

Here is the working code. I've converted from the C++ code. cheers.. ;)

unit SuatXTEA;

interface

uses SysUtils;

function EncryptXTEA(Data, Key:AnsiString):AnsiString;
function DecryptXTEA(Data, Key:AnsiString):AnsiString;

implementation

function FourCharsToLong(const V: AnsiString):LongWord;
var j, k:Integer;
begin
 Result:=0;
 for j:=0 to Length(V)-1 do
 begin
  k:=(3-(j mod 4))*8;
  Result:=Result or ((Ord(V[j+1]) shl k));
 end;//for j...
end;//FourCharsToLong

function LongToFourChars(const V: Longword):AnsiString;
var j, k:Integer;
begin
 Result:='';
 for j:=0 to 3 do
 begin
  k:=(3-(j mod 4))*8;
  Result:=Result+AnsiChar(V shr k);
 end;//for j...
end;//LongToFourChars

function HexToDec(Hex:AnsiString):LongWord;
 var j:Integer;
     k:Byte;
     Base:LongWord;
begin
 Result:=0;
 Hex:=UpperCase(Hex);
 Base:=1;
 for j:=Length(Hex) downto 1 do
 begin
  k:=Ord(Hex[j])-48;
  if k>9 then k:=k-7;
  Result:=Result+k*Base;
  Base:=Base*16;
 end;
end;//HexToDec

function EncryptXTEA(Data, Key:AnsiString):AnsiString;
const
 Delta = $9e3779b9;
 NumRounds=32;
var j:Integer;
 B_Key:array [0..3] of LongWord;
 Sum, v0, v1: LongWord;
 B_Result:AnsiString;
begin
 B_Result:='';
 //--- Set Key --------------
 FillChar(B_Key, SizeOf(B_Key), 0);
 j:=0;
 while Key<>'' do
 begin
  B_Key[j]:=FourCharsToLong(Copy(Key, 1, 4));
  Key:=Copy(Key, 5, 1024);
  inc(j);
 end;//while
 //-----
 while Data<>'' do
 begin
  v0:=FourCharsToLong(Copy(Data, 1, 4));
  v1:=FourCharsToLong(Copy(Data, 5, 4));
  Data:=Copy(Data, 9, 1024);
  //---
  Sum:=0;
  for j:= 0 to NumRounds-1 do
  begin
   Inc(v0, (((v1 shl 4) xor (v1 shr 5)) + v1) xor (Sum + B_Key[Sum and 3]));
   Inc(Sum, Delta);
   Inc(v1, (((v0 shl 4) xor (v0 shr 5)) + v0) xor (Sum + B_Key[Sum shr 11 and 3]));
  end;//for j...
  B_Result:=B_Result+LongToFourChars(v0)+LongToFourChars(v1);
 end;//while Data<>''
 //---
 Result:='';
 for j:=0 to Length(B_Result)-1 do
 begin
  Result:=Result+IntToHex(ord(B_Result[j+1]), 2);
 end;
 Result:=Lowercase(Result);
end;//EncryptXTEA

function DecryptXTEA(Data, Key:AnsiString):AnsiString;
const
 Delta = $9e3779b9;
 NumRounds=32;
var j:Integer;
 B_Key:array [0..3] of LongWord;
 Sum, v0, v1: LongWord;
 B_Result:AnsiString;
begin
 //--- Set Key --------------
 FillChar(B_Key, SizeOf(B_Key), 0);
 j:=0;
 while Key<>'' do
 begin
  B_Key[j]:=FourCharsToLong(Copy(Key, 1, 4));
  Key:=Copy(Key, 5, 1024);
  inc(j);
 end;//while
 //-----
 B_Result:='';
 for j:=0 to (Length(Data) div 2)-1 do
 begin
  B_Result:=B_Result+AnsiChar(HexToDec(Copy(Data, (j*2)+1, 2)));
 end;
 //----
 Result:='';
 while B_Result<>'' do
 begin
  v0:=FourCharsToLong(Copy(B_Result, 1, 4));
  v1:=FourCharsToLong(Copy(B_Result, 5, 4));
  B_Result:=Copy(B_Result, 9, 1024);
  //---
  Sum:=$C6EF3720;//Delta*NumRounds = Delta shl 5
  for j:=0 to NumRounds-1 do
  begin
   Dec(v1, (((v0 shl 4) xor (v0 shr 5)) + v0) xor (Sum + B_Key[(Sum shr 11) and 3]));
   Dec(Sum, Delta);
   Dec(v0, (((v1 shl 4) xor (v1 shr 5)) + v1) xor (Sum + B_Key[Sum and 3]));
  end;//for j...
  Result:=Result+LongToFourChars(v0)+LongToFourChars(v1);
 end;//while Data<>''
end;//DecryptXTEA

end.
SuatDmk
  • 1
  • 1