Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I convert this XOR-encryption function from Delphi to C#?

The following Delphi routine is originally from a long-ago CompuServe posting, and is used to encrypt various information in our database. Below are both the Delphi 2007 and (thanks to some SO help with the Unicode differences) Delphi XE versions.

We have been trying to convert this to C#, and have gotten close-ish, but we're missing something somewhere. Unfortunately, our Delphi guy (me) doesn't know C#, and the C# guy is new to Delphi. C# doesn't (appear to) have the concept of AnsiString, so the solution will probably involve byte or char arrays?

We'd greatly appreciate any help in converting this to C#.

Delphi 2007 Version (ASCII)

function EncodeDecode(Str: string): string;
const
  Hash: string = '^%12hDVjED1~~#29afdmSD`6ZvUY@hbkDBC3fn7Y7euF|R7934093*7a-|-  Q`';
var
  I: Integer;
begin
  for I := 1 to Length (Str) do
    Str[I] := chr (ord (Str[I]) xor not (ord (Hash[I mod Length (Hash) + 1])));
  Result := Str;
end;

Delphi XE Version (Unicode)

function TfrmMain.EncodeDecode(Str: AnsiString): AnsiString;
const
  Hash: string = '^%12hDVjED1~~#29afdmSD`6ZvUY@hbkDBC3fn7Y7euF|R7934093*7a-|-  Q`';
var
  I: Integer;
begin
  Result := Str;
  for I := 1 to Length (Result) do
    Result[I] := AnsiChar (ord (Result[I]) xor not (Ord (Hash[I mod Length (Hash) + 1])));
end;
like image 733
Eric S. Avatar asked Jul 20 '11 19:07

Eric S.


2 Answers

I don't know C# either, so this is probably seriously non-idiomatic.

static string EncodeDecode(string str)
{
    byte[] hash = new byte[63] { 94, 37, 49, 50, 104, 68, 86, 106, 69, 68, 49, 126, 
        126, 35, 50, 57, 97, 102, 100, 109, 83, 68, 96, 54, 90, 118, 85, 89, 64, 
        104, 98, 107, 68, 66, 67, 51, 102, 110, 55, 89, 55, 101, 117, 70, 124, 82, 
        55, 57, 51, 52, 48, 57, 51, 42, 55, 97, 45, 124, 45, 32, 32, 81, 96 };

    Encoding ANSI = Encoding.GetEncoding(1252);
    byte[] input = ANSI.GetBytes(str);
    byte[] output = new byte[input.Length];
    for (int i = 0; i < input.Length; i++)
        output[i] = (byte)(input[i] ^ ~hash[(i + 1) % hash.Length]);
    return ANSI.GetString(output);
}

I have assumed that your ANSI strings are encoded with Windows 1252, but it you happen to have encoded your legacy data with a different code page it is obvious enough how to change it.

Since C# doesn't have the equivalent of Delphi's 8 bit string types, I personally would be sorely tempted to use byte[] rather than string.

Done that way it looks like this:

static byte[] EncodeDecode(byte[] input)
{
    byte[] hash = new byte[63] { 94, 37, 49, 50, 104, 68, 86, 106, 69, 68, 49, 126, 
        126, 35, 50, 57, 97, 102, 100, 109, 83, 68, 96, 54, 90, 118, 85, 89, 64, 
        104, 98, 107, 68, 66, 67, 51, 102, 110, 55, 89, 55, 101, 117, 70, 124, 82, 
        55, 57, 51, 52, 48, 57, 51, 42, 55, 97, 45, 124, 45, 32, 32, 81, 96 };

    byte[] output = new byte[input.Length];
    for (int i = 0; i < input.Length; i++)
        output[i] = (byte)(input[i] ^ ~hash[(i + 1) % hash.Length]);
    return output;
}

@Groo makes the excellent point that the hash can be initialised more cleanly list this:

byte[] hash = ANSI.GetBytes(@"^%12hDVjED1~~#29afdmSD`6ZvUY@hbkDBC3fn7Y7euF|R7934093*7a-|-  Q`");
like image 138
David Heffernan Avatar answered Sep 28 '22 04:09

David Heffernan


String in C# is a sequence of characters encoded in UTF-16, as explained in this article by Jon Skeet. That actually shouldn't concern you at all, until you decide to serialize it binary (i.e. convert it into a byte array). In that case, there is a class called Encoding in System.Text namespace which supports encoding the String to whatever encoding you want.

An AnsiString in Delphi is basically an ASCII string (nope, it's really an ANSI string, like the name says), where each character is guaranteed to have exactly 8 bits. This is a relatively "simple" encoding of encoding to work with, since it is of fixed size, widely accepted and compatible with legacy systems (but it doesn't allow encoding of more than 255 characters).

In other words, both of your versions deal with the same type of Encoding, but the unicode version now explicitly defines legacy strings as AnsiStrings. That means that the latter version doesn't actually support Unicode strings, but the type needed to be changed with the new Delphi version.

What @David did while I was writing this lengthy nonsense is basically what I was going to write, with the exception that I would have used the Encoding.ASCII encoding instead ([edit] and would fail miserably due to the fact that ASCII only uses the lower 7-bits to encode characters, as mentioned by David below). Windows-1252 is the encoding most commonly referred to as "ANSI" (although, if you check that Wiki article, you will find out that it, according to Microsoft, The term ANSI as used to signify Windows code pages is a historical reference, but is nowadays a misnomer that continues to persist in the Windows community).

like image 37
Groo Avatar answered Sep 28 '22 05:09

Groo