Page 2 of 3 FirstFirst 123 LastLast
Results 11 to 20 of 21

Thread: Difficulties translating XTea encryption to Pascal

  1. #11

    Difficulties translating XTea encryption to Pascal

    I've been following this thread with interest. I have a suggestion. If this algorithm will work fine on stream data then why not just make is encrpt and decrypt streams only? The Turbo Power LockBox functions do this, they mainly work on streams and have a wrapper function to convert the string to a memory stream (using TStreamStream and a copy) to handle the actual encryption.

    I have found in the past with compression that dealing with strings directly can always be a bit tricky.

    That is an interesting point about the C >> operator, I have had problems converting code which uses >> before but I can never remember how I get round it each time
    <A HREF="http://www.myhpf.co.uk/banner.asp?friend=139328">
    <br /><IMG SRC="http://www.myhpf.co.uk/banners/60x468.gif" BORDER="0">
    <br /></A>

  2. #12

    Difficulties translating XTea encryption to Pascal

    Here's another suggestion if using string is a must:
    - First test if 1 of the 8 byte numbers in v block contains 0. This is easy when converting
    array[0..1] of longword -> array[0..7] of byte
    For example: b8:=TBlock8(v);

    - Then split 8 characters in 4 character parts and add another 4 characters after/before both 2 parts that are easy to detect and delete when decrypting.

    - A bit trickier here makes the selection of added characters. Obsiously that 4 char sequence could not then be allowed to exist in plain text, so use characters that are not used when writing text.

    -Also it now requires that both blocks are tested for #0 again. Change the 4 before/after characters until there is no #0 in outcome :roll:
    Example: default 4 chars + #1#1#1#1
    if #0 found in any of 8 outcome, then #1#1#1#2 has high propability of not contain one. this #1#1#1 will still be detectable no matter how large you grow the latter char.

    Too worky?

  3. #13

    Difficulties translating XTea encryption to Pascal

    Here is a working version using streams for the encrypted data. The only code that has changed is the Encrypt and Decrypt methods.

    xtea.pas
    Code:
    unit xtea;
    
    interface
    
    uses
      SysUtils, Classes;
    
    type
      PKey128 = ^TKey128;
      TKey128 = array &#91;0..3&#93; of longword;
    
      PBlock64 = ^TBlock64;
      TBlock64 = array &#91;0..1&#93; of longword;
    
      TXTea = class&#40;TObject&#41;
      private
        FKey&#58; TKey128;
        FInitialized&#58; Boolean;
      public
        property Initialized&#58; boolean read FInitialized default false;
        procedure Burn;
        procedure InitKey&#40;const Key&#58; string&#41;;
        procedure Encrypt&#40;const Input&#58; string; Stream&#58; TStream&#41;;
        function Decrypt&#40;Stream&#58; TStream; Count&#58; Integer&#41;&#58; string;
        destructor Destroy; override;
        constructor Create;
      end;
    
    implementation
    
    const
      DELTA = $9e3779b9;
      XTEA_BLANK&#58; TBlock64 = &#40;0,0&#41;;
      XTEA_BURN&#58; TKey128 =&#40;0,0,0,0&#41;;
    
    destructor TXTea.Destroy;
    begin
      Burn;
      inherited;
    end;
    
    constructor TXTea.Create;
    begin
      inherited;
      Burn;
    end;
    
    procedure TXTea.Burn;
    begin
      FKey &#58;= XTEA_BURN;
      FInitialized &#58;= False;
    end;
    
    procedure TXTea.InitKey&#40;const Key&#58; string&#41;;
    var L&#58; integer;
    begin
      if Length&#40;Key&#41; <= SizeOf&#40;FKey&#41; then
        L &#58;= Length&#40;Key&#41;
      else
        L &#58;= SizeOf&#40;FKey&#41;;
    
      Move&#40;Key&#91;1&#93;,FKey&#91;0&#93;,L&#41;;
    
      FInitialized &#58;= True;
    end;
    
    procedure TXTea.Encrypt&#40;const Input&#58; string; Stream&#58; TStream&#41;;
    var i,l&#58; integer;
        v&#58; TBlock64;
    
      procedure CipherXTea&#40;v&#58; PBlock64; key&#58; PKey128&#41;;
      var sum&#58; longword;
          i&#58; integer;
      begin
        Sum&#58;= 0;
    
        for i&#58;= 0 to 31 do
        begin
         Inc&#40;v&#91;0&#93;, &#40;&#40;v&#91;1&#93; shl 4 xor v&#91;1&#93; shr 5&#41; + v&#91;1&#93;&#41; xor &#40;Sum + Key&#91;Sum and 3&#93;&#41;&#41;;
         Inc&#40;Sum, Delta&#41;;
         Inc&#40;v&#91;1&#93;, &#40;&#40;v&#91;0&#93; shl 4 xor v&#91;0&#93; shr 5&#41; + v&#91;0&#93;&#41; xor &#40;Sum + Key&#91;Sum shr 11 and 3&#93;&#41;&#41;;
        end;
      end;
    
    begin
      if not Initialized then
        raise Exception.Create&#40;'Error&#58; You must define a password.'&#41;;
    
      l &#58;= &#40;Length&#40;Input&#41; + 1 + &#40;SizeOf&#40;v&#41; - 1&#41;&#41; and &#40;not &#40;SizeOf&#40;v&#41; - 1&#41;&#41;;
    
      i &#58;= 1;
      while i < l do
      try
        v &#58;= XTEA_BLANK;
        if l - &#40;i - 1&#41; < SizeOf&#40;v&#41; then
          Move&#40;Input&#91;i&#93;, v, l - &#40;i - 1&#41;&#41;
        else
          Move&#40;Input&#91;i&#93;, v, SizeOf&#40;v&#41;&#41;;
        CipherXTea&#40;@v, @FKey&#41;;
        Stream.Write&#40;v, SizeOf&#40;v&#41;&#41;;
        Inc&#40;i, SizeOf&#40;v&#41;&#41;;
      except
        raise;
      end;
    end;
    
    function TXTea.Decrypt&#40;Stream&#58; TStream; Count&#58; Integer&#41;&#58; string;
    var i&#58; integer;
        v&#58; TBlock64;
    
      procedure DecipherXTea&#40;v&#58; PBlock64; Key&#58; PKey128&#41;;
      var
         i&#58; Integer;
         Sum&#58; Longword;
      begin
        Sum&#58;= $C6EF3720;
    
        for i&#58;= 0 to 31 do
        begin
          Dec&#40;v&#91;1&#93;, &#40;&#40;v&#91;0&#93; shl 4 xor v&#91;0&#93; shr 5&#41; + v&#91;0&#93;&#41; xor &#40;Sum + Key&#91;Sum shr 11 and 3&#93;&#41;&#41;;
          Dec&#40;Sum, Delta&#41;;
          Dec&#40;v&#91;0&#93;, &#40;&#40;v&#91;1&#93; shl 4 xor v&#91;1&#93; shr 5&#41; + v&#91;1&#93;&#41; xor &#40;Sum + Key&#91;Sum and 3&#93;&#41;&#41;;
        end;
      end;
    begin
      if not Initialized then
        raise Exception.Create&#40;'Error&#58; You must define a password.'&#41;;
    
      SetLength&#40;Result, Count&#41;;
    
      i &#58;= 1;
      while i < Count do
      try
        v &#58;= XTEA_BLANK;
        Stream.Read&#40;v, SizeOf&#40;v&#41;&#41;;
        DecipherXTea&#40;@v, @FKey&#41;;
        Move&#40;v, Result&#91;i&#93;, SizeOf&#40;v&#41;&#41;;
        Inc&#40;i, SizeOf&#40;v&#41;&#41;;
      except
        raise;
      end;
    end;
    
    end.
    Sample app
    Code:
    unit Unit4;
    
    interface
    
    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls, xtea;
    
    type
      TForm4 = class&#40;TForm&#41;
        Memo1&#58; TMemo;
        Memo2&#58; TMemo;
        Button1&#58; TButton;
        Button2&#58; TButton;
        procedure Button2Click&#40;Sender&#58; TObject&#41;;
        procedure FormDestroy&#40;Sender&#58; TObject&#41;;
        procedure Button1Click&#40;Sender&#58; TObject&#41;;
        procedure FormCreate&#40;Sender&#58; TObject&#41;;
      private
        FXTea&#58; TXTea;
        FStream&#58; TMemoryStream;
      public
        &#123; Public declarations &#125;
      end;
    
    var
      Form4&#58; TForm4;
    
    implementation
    
    &#123;$R *.dfm&#125;
    
    procedure TForm4.FormCreate&#40;Sender&#58; TObject&#41;;
    begin
      FXTea &#58;= TXTea.Create;
      FXTea.InitKey&#40;'0123456789abcdef'&#41;;
      FStream &#58;= TMemoryStream.Create;
    end;
    
    procedure TForm4.FormDestroy&#40;Sender&#58; TObject&#41;;
    begin
      FStream.Free;
      FXTea.Free;
    end;
    
    procedure TForm4.Button1Click&#40;Sender&#58; TObject&#41;;
    var
      Input&#58; String;
    begin
      Input &#58;= Memo1.Lines.Text;
      &#40;* Set the size and position of the stream outside the method so that
        we could append to an existing stream if we wanted to. *&#41;
      &#40;* Add one to allow for the NUL terminator on the string *&#41;
      FStream.SetSize&#40;Length&#40;Input&#41; + 1&#41;;
      FStream.Position &#58;= 0;
      FXTea.Encrypt&#40;Input, FStream&#41;;
    end;
    
    procedure TForm4.Button2Click&#40;Sender&#58; TObject&#41;;
    begin
      &#40;* This sample assumes that the stream contains only the encrypted data,
        but it could be part of a much larger stream. *&#41;
      FStream.Position &#58;= 0;
      Memo2.Lines.Text &#58;= FXTea.Decrypt&#40;FStream, FStream.Size&#41;;
    end;
    
    end.
    Press Button1 to encrypt the text in Memo1 into a TMemoryStream. Press Button2 to decrypt the stream into Memo2.

  4. #14

    Difficulties translating XTea encryption to Pascal

    Thanks, and I was actually going to write it as Stream to Stream encryption and decryption, with String overloads of each. And I'll probably recover the code by the MPL license, rather than my own. I'm not very good with legalese.

    So I'll upload a final unit shortly, after I rewrite the functions real quick.

    So there is a way around Delphi's implementation of binary shifts? I hope so, since I'm certain that it's our culprit. Or I could write a C* DLL or use the compiled .OBJs created for all this...

  5. #15

    Difficulties translating XTea encryption to Pascal

    Quote Originally Posted by Robert Kosek
    Thanks, and I was actually going to write it as Stream to Stream encryption and decryption, with String overloads of each. And I'll probably recover the code by the MPL license, rather than my own. I'm not very good with legalese.

    So I'll upload a final unit shortly, after I rewrite the functions real quick.

    So there is a way around Delphi's implementation of binary shifts? I hope so, since I'm certain that it's our culprit. Or I could write a C* DLL or use the compiled .OBJs created for all this...
    Sure there is a way around Delphi's implementation of Shifts. Simply write your own methods to do it for you (fair bit of warning, I'm doing this from the cuff with no IDE, so I'll guarntee its wrong):

    [pascal]function RotateBitsRight(Param, Amount:Byte): Byte; // Note you could also do this as a var param
    asm
    ror Param, Amount // Rotate right
    mov Amount, result
    end;

    function RotateBitsRight(Param, Amount:Byte): Byte; // Note you could also do this as a var param
    asm
    rol Param, Amount // Rotate left
    mov Amount, result
    end;[/pascal]

    Anyways my point is, do it in assembly. Good reference guides can be found all over, but I use:
    http://courses.ece.uiuc.edu/ece390/b.../inst-ref.html
    http://www.intel.com/design/pentium4.../index_new.htm
    http://www.nondot.org/sabre/os/files...imization.html
    http://www.website.masmforum.com/tut...ute/index.html

    Pretty much in that order. Looking at my code above, I know its wrong but it gets the idea across. You really would have to look into BASM and samples on the b.p.n.basm group for proper answers.

  6. #16

    Difficulties translating XTea encryption to Pascal

    Yes, but I believe that is what Delphi is already doing. You need the shr and shl opcodes.

  7. #17

    Difficulties translating XTea encryption to Pascal

    Sorry for a bit late reply, I'm quite out of time lately... :?

    Quote Originally Posted by Sly
    In CipherDataXTEA, the loop will not execute at all for strings with less than 8 characters. It will also not encrypt any remaining bytes after the last eight-byte block. The same goes for DecipherDataXTEA.
    Yes, I'm not using padding at all. The code assumes that data block is a multiple of 8 bytes.

    Quote Originally Posted by Sly
    Also, and this might affect both, I believe that the C >> operator is a logical shift (bits fall off the end) and the Delphi shr operator is an arithmetic shift (bits wrap around). I seem to remember we found this when porting Quake 2 to Delphi.
    If you look at C source code posted on Wikipedia web page, you will notice that they use UNSIGNED LONG. In that case, ">>" operator is translated to unsigned binary shift, which equals to "shr" in Delphi. The Pascal code is correct.

    Anywhere else: if you need to use SIGNED SHIFT in Delphi, you may try to use power-of-two division instead, which will be replaced by signed binary shift (with Optimizations ON). It's not exactly equivalent though, since Delphi adds bias before shift. E.g.:
    Code:
    var
     Test, Test2&#58; Integer;
    begin
     Test &#58;= $12345678;
     // Test2&#58;= Test sar 4;  -> this is not possible
     Test2&#58;= Test div 16;
    end;
    The above code gets translated into:
    Code:
     mov eax, $12345678
     mov esi, eax
     test esi, esi
     jns @NoBias
     add esi,  $0F
    @NoBias&#58;
     sar esi, 4
    See Art of Assembly: Chapter Six-3 for more information about how SHR/SAR work and their differences.

  8. #18

    Difficulties translating XTea encryption to Pascal

    Greetings,

    As others have mentioned, under normal circumstances, encryption/decryption routines should work on binary data types as opposed to strings; in many cases, using untyped data cast as the needed data structures for the cipher. The issue with strings not liking nulls (#0) is a notable example of this problem.

    Further, when using block ciphers, care must be taken to pad out the final block when the data doesn't fill it to a block boundary. In some ciphers, improper padding can introduce weaknesses in the encoded message that cryptanalysis can exploit to recover the plaintext easier.

    Outside of those two things, it looks like your encryption object is close to being 100% functional.

    Addendum - You also have to make sure that your encrypted output is the full blocksize in the last block. IE, if you use this 8-byte block cipher to encrypt a stream of plaintext bytes with length of 21, you have to keep all 24 bytes of the encrypted message for decryption; you can't just toss those extra 3 bytes at the end, because they will contain some portion of the last 5 plaintext bytes. Output data length of a block cipher encode message function is always a multiple of the block size.

  9. #19

    Difficulties translating XTea encryption to Pascal

    Murmandamus, that is exactly what my modifications do. It just took a little while to work out the rules.

  10. #20

    Difficulties translating XTea encryption to Pascal

    Here is an updated xtea.pas that uses streams by default with overloaded methods for strings. The formatting and class style has also been Delphi'ed. The sample code hasn't changed except the call to InitKey is now a write to the Key property.

    Tested and all works fine. This has been an interesting challenge.

    [pascal]unit xtea;

    interface

    uses
    Classes, SysUtils;

    type
    TKey128 = array [0..3] of LongWord;
    TBlock64 = array [0..1] of LongWord;

    TXTea = class(TObject)
    private
    FKey: TKey128;
    procedure CipherXTea(var Block: TBlock64);
    procedure DecipherXTea(var Block: TBlock64);
    function GetKey: String;
    procedure SetKey(const Value: String);
    public
    constructor Create;
    destructor Destroy; override;
    procedure Burn;
    function Encrypt(Input, Output: TStream; InputSize: LongWord): LongWord; overload;
    function Encrypt(const Input: String; Output: TStream): LongWord; overload;
    procedure Decrypt(Input, Output: TStream; InputSize: LongWord); overload;
    function Decrypt(Input: TStream; Count: LongWord): String; overload;
    function PaddedSize(Size: LongWord): LongWord;
    property Key: String read GetKey write SetKey;
    end;

    implementation

    const
    DELTA = $9e3779b9;
    XTEA_BLANK: TBlock64 = (0, 0);
    XTEA_BURN: TKey128 =(0, 0, 0, 0);

    function Min(A, B: Integer): Integer;
    begin
    Result := A;
    if B < A then
    Result := B;
    end;

    constructor TXTea.Create;
    begin
    inherited;
    Burn;
    end;

    destructor TXTea.Destroy;
    begin
    Burn;
    inherited;
    end;

    procedure TXTea.Burn;
    begin
    FKey := XTEA_BURN;
    end;

    procedure TXTea.DecipherXTea(var Block: TBlock64);
    var
    Index: Integer;
    Sum: Longword;
    begin
    Sum := $C6EF3720;

    for Index := 0 to 31 do
    begin
    Dec(Block[1], ((Block[0] shl 4 xor Block[0] shr 5) + Block[0]) xor (Sum + FKey[Sum shr 11 and 3]));
    Dec(Sum, DELTA);
    Dec(Block[0], ((Block[1] shl 4 xor Block[1] shr 5) + Block[1]) xor (Sum + FKey[Sum and 3]));
    end;
    end;

    procedure TXTea.Decrypt(Input, Output: TStream; InputSize: LongWord);
    var
    Index: LongWord;
    Block: TBlock64;
    begin
    if Key = '' then
    raise Exception.Create('Error: You must set a key');

    Index := 0;
    while Index < InputSize do
    try
    Block := XTEA_BLANK;
    Input.Read(Block, SizeOf(Block));
    DecipherXTea(Block);
    Output.Write(Block, SizeOf(Block));
    Inc(Index, SizeOf(Block));
    except
    raise;
    end;
    end;

    function TXTea.Decrypt(Input: TStream; Count: LongWord): String;
    var
    Stream: TMemoryStream;
    begin
    Stream := TMemoryStream.Create;
    try
    Decrypt(Input, Stream, Count);
    Result := PChar(Stream.Memory);
    finally
    Stream.Free;
    end;
    end;

    function TXTea.PaddedSize(Size: LongWord): LongWord;
    begin
    Result := (Size + (SizeOf(TBlock64) - 1)) and (not (SizeOf(TBlock64) - 1));
    end;

    procedure TXTea.CipherXTea(var Block: TBlock64);
    var
    Sum: LongWord;
    Index: Integer;
    begin
    Sum := 0;

    for Index := 0 to 31 do
    begin
    Inc(Block[0], ((Block[1] shl 4 xor Block[1] shr 5) + Block[1]) xor (Sum + FKey[Sum and 3]));
    Inc(Sum, DELTA);
    Inc(Block[1], ((Block[0] shl 4 xor Block[0] shr 5) + Block[0]) xor (Sum + FKey[Sum shr 11 and 3]));
    end;
    end;

    function TXTea.Encrypt(Input, Output: TStream; InputSize: LongWord): LongWord;
    var
    Index: LongWord;
    Block: TBlock64;
    begin
    if Key = '' then
    raise Exception.Create('Error: You must set a key');

    Result := PaddedSize(InputSize);

    Index := 0;
    while Index < InputSize do
    try
    Block := XTEA_BLANK;
    Input.Read(Block, Min(SizeOf(Block), InputSize - Index));
    CipherXTea(Block);
    Output.Write(Block, SizeOf(Block));
    Inc(Index, SizeOf(Block));
    except
    raise;
    end;
    end;

    function TXTea.Encrypt(const Input: String; Output: TStream): LongWord;
    var
    Stream: TMemoryStream;
    begin
    Stream := TMemoryStream.Create;
    try
    Stream.Write(Input[1], Length(Input) + 1);
    Stream.Position := 0;
    Result := Encrypt(Stream, Output, Stream.Size);
    finally
    Stream.Free;
    end;
    end;

    function TXTea.GetKey: String;
    begin
    Result := PChar(@FKey);
    end;

    procedure TXTea.SetKey(const Value: String);
    begin
    Burn;
    Move(Value[1], FKey, Min(Length(Value), SizeOf(FKey)));
    end;

    end.[/pascal]

Page 2 of 3 FirstFirst 123 LastLast

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •