11
\$\begingroup\$

I have a bit of Python source code that I want to recreate in C#. The code is about reading and decrypting a binary file. I have tested the function on an existing file and it runs without errors; while the resulting string is not garbled or anything, it does not appear to be useful to me.

But that is outside the scope of this question. I only want to know if I have translated the function correctly to C# so it does the same as in Python.

The Python code:

    filename = os.path.basename(path_and_filename)
    key = 0
    for char in filename:
        key = key + ord(char)
    f = open(path_and_filename, 'rb')
    results = ''
    tick = 1
    while True:
        byte = f.read(2)
        if byte:
            if tick:
                key = key + 2
            else:
                key = key - 2
            tick = not tick
            res = struct.unpack('h', byte)[0] ^ key
            results = results + chr(res)
            continue
        break
    f.close()
    return results

path_and_filename is an absolute path to the encrypted file.

Here is the C# code I wrote:

        string path = _path;
        string fileName = _fileName;
        char[] chars = fileName.ToCharArray();
        int key = 0;
        foreach (char c in chars)
        {
            key += c;
        }
        StringBuilder builder = new StringBuilder();


        using (FileStream file = new FileStream(path, FileMode.Open))
        {
            bool tick = true;

            while (true)
            {
                int i = file.ReadByte();
                if (i == -1)
                    break;
                if (tick)
                    key += 2;
                else
                    key -= 2;
                tick = !tick;


                //The next 2 lines are for parsing the short, equivalent(?) to struct.unpack('h', byte)
                i <<= 8;
                i += file.ReadByte();

                i ^= key;
                byte b = (byte)i;
                char c = (char)b;
                builder.Append(c);
            }
        }
        string result = builder.ToString();

Never mind the dirtiness of the code. Will those 2 snippets give the same output to a certain input?

\$\endgroup\$
  • 3
    \$\begingroup\$ This is an interesting question, because unlike most translation questions (which are bad), you've actually tried to do it. This comment is designed to guard against thoughtless downvotes. I can in no way protect you from thoughtful ones. \$\endgroup\$ – Magus Mar 25 '14 at 23:17
  • \$\begingroup\$ Re. "certain input", we're supposed to assume but are you sure that os.path.basename(path_and_filename) is "c28556fb686c8d07066419601a2bdd83"? \$\endgroup\$ – ChrisW Mar 26 '14 at 0:15
  • \$\begingroup\$ @ChrisW I go by the official documentation here: docs.python.org/2/library/os.path.html The specification says that the last folder name is the return value of that basename method. Besides, that folder name is the key for the decryption operation, so it does make sense that it would be the input for the key. \$\endgroup\$ – Hackworth Mar 26 '14 at 0:48
  • \$\begingroup\$ I fear that "path" includes the filename as well as the folder: see stackoverflow.com/q/1112545/49942 for example, and this which says it returns the filename. \$\endgroup\$ – ChrisW Mar 26 '14 at 0:54
  • \$\begingroup\$ @ChrisW Thank you, that was indeed part of the problem. basename does return "Game_Keywords.dat". There were other msitakes as well. \$\endgroup\$ – Hackworth Mar 26 '14 at 8:21
4
\$\begingroup\$

This section seems wrong:

//The next 2 lines are for parsing the short, equivalent(?) to struct.unpack('h', byte)
i <<= 8;
i += file.ReadByte();

you're shifting the value in i 8 bits left and then reading another byte. I'm guessing because the original code read it in 2 bytes at a time. If your file isn't the right length then this will break it here. what about:

i <<= 8;
int secondByte = file.ReadByte()
if(secondByte != -1)
{
   i += secondByte ;
}

Whats with this? It is casting an int to a byte to a char and it is truncating.

byte b = (byte)i;
char c = (char)b;

so you're better off ignoring the first byte and instead everything after tick = !tick; could be:

i = file.ReadByte();

char c = (char)(i ^ (key & 255));
builder.Append(c);

In Python chr() will throw an exception if the value is greater than 255 anyway.

And IMHO it is a bad idea to roll your own encryption/decryption like this. Use a trusted known solution instead.

Question: I only want to know if I have translated the function correctly to C# so it does the same as in Python

Answer Its more of a transliteration rather than a translation but they should give the same output. If I were to re-write the python into c# it would look different as it seems inefficient to me.

\$\endgroup\$
  • 1
    \$\begingroup\$ I know about the dangers of rolling my own encryption. The python is not mine, however, I'm just trying to translate/transliterate it. \$\endgroup\$ – Hackworth Mar 26 '14 at 8:46
4
\$\begingroup\$

After a half-nighter, I finally found the solution. Running the code via Iron Python told me that os.path.basename(path_and_filename) in my case does return the file name, as pointed out in the comments, not the name of the last folder. That was the first problem, so I've been working with the wrong key.

The second issue was how I read the bytes to XOR with the key. Apparently, unpacking the two bytes read reverses their order in python, so in C# I have to read them in reverse order as well.

So my code would actually look something like this:

    string path = _path;
    string filename = _fileName;

    //python os.path.basename()
    char[] chars = filename.ToCharArray();
    int key = 0;
    foreach (char c in chars)
    {
        key += c;
    }

    StringBuilder builder = new StringBuilder();

    using (FileStream file = new FileStream(path + filename, FileMode.Open))
    {
        bool tick = true;

        while (true)
        {
            int i = file.ReadByte();
            //it's ok to break here because if the first byte is present, then the second byte will also be present, because the data files are well formed.
            if (i == -1)
                break;
            //we can skip the 2nd byte because the algorithm ignores it anyway
            file.ReadByte();

            if (tick)
                key += 2;
            else
                key -= 2;
            tick = !tick;

            i ^= key;

            char c = (char)(i & 255);
            builder.Append(c);
        }
    }
    string result = builder.ToString();
\$\endgroup\$
  • \$\begingroup\$ I don't think (char) is doing the truncating (byte) was. \$\endgroup\$ – James Khoury Mar 26 '14 at 23:13
  • \$\begingroup\$ @JamesKhoury I don't see why you made that edit; it doesn't look good to me. Note the @" at the start of the string. \$\endgroup\$ – ChrisW Mar 26 '14 at 23:34
  • \$\begingroup\$ Sorry I should have mentioned it. The StackExchange syntax highlighter doesn't handle @" properly. Atleast on my machine. By putting the extra space it shows the syntax for future viewers.The other alternative is use an extra \ instead. \$\endgroup\$ – James Khoury Mar 27 '14 at 1:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.