killercode killercode - 1 month ago 15
C Question

XTEA encryption give different output in C and Delphi

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?

Answer

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.

Comments