Mike Noonan
Mike Noonan

Reputation: 9

Convert C++ method to C#

I have a method that decrypts a base64 and I need to convert it to c#, I've tried to use the Convert.FromBase64String of C# and some other tips I've looked here, but the final string doesn't match the result on c++.

Can someone give a hand to understand this peace of code of c++?

Example input: lWXtYpNpwBBXoLmcuktDqg==

Expected output: AC0105E92ED496ACD373D7496B76C1C8

Output using Convert.FromBase64: 95-65-ED-62-93-69-C0-10-57-A0-B9-9C-BA-4B-43-AA

static BYTE BackFrom64[]={
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0x3e,0xff,0xff,0xff,0x3f,
0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,
0x3c,0x3d,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0x00,0x01,0x02,0x03,0x04,0x05,0x06,
0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,
0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,
0x17,0x18,0x19,0xff,0xff,0xff,0xff,0xff,
0xff,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x20,
0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,
0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x30,
0x31,0x32,0x33,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff

};

static BYTE * TabBase64=(BYTE *)"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";


int FromBase64(BYTE *Output,BYTE *Input,int BufSize,int MaxSize) {

    int              xut,txut;
    int              i,t,k,shift;
    BYTE             c;
    dWORD            dr,dr1;

    xut=txut=t=0;
    dr=0L;
    for(i=0; i < BufSize; ++i) {
        c=*Input++;
        if(c == '=') {
            break;
        }
        if((c=BackFrom64[c]) == 0xff) {
            continue;
        }
        dr1=(dWORD)c;
        shift=(32-(6*(t+1)));
        dr1<<=shift;
        dr|=dr1;
        t++;
        if(t == 4) {
            t=0;
            for(k=0; k < 3; ++k) {
                dr1=dr;
                dr1>>=(32-(8*(k+1)));
                dr1&=0xff;
                *Output++=(BYTE) dr1;
                if(++txut >= MaxSize) {
                    return(-1);
                }
            }
            dr=0;
        }
    }
    if(t > 0) {
        for(k=0; k < (t-1); ++k) {
            dr1=dr;
            dr1>>=(32-(8*(k+1)));
            dr1&=0xff;
            *Output++=(BYTE) dr1;
            if(++txut >= MaxSize) {
                return(-1);
            }
        }
    }
    return(txut);
}

Upvotes: 0

Views: 150

Answers (1)

Pete Kirkham
Pete Kirkham

Reputation: 49331

A very naive conversion with minimal work follows.

  • the types have been changed to C# types
  • the pointer increments have been changed to indexes into arrays

The only other change was to calculate the unsupplied BackFrom64 array as the inverse of the TabBase64 array, and to add a unit test for the supplied input and output. If it did something other than the standard algorithm, then I'd tidy up the counters, some of which are redundant (probably both i and iInput could be replaced with foreach(var c in input)

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace SO
{
    [TestClass]
    public class UnitTest1
    {
        private const string TabBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
        private static readonly byte[] BackFrom64 = CreateBackFrom64();

        private static byte[] CreateBackFrom64()
        {
            var backFrom64 = new byte[128];

            for (var i = 0; i < backFrom64.Length; ++i)
                backFrom64[i] = 0xff;

            for (byte i = 0; i < TabBase64.Length; i++)
            {
                var c = TabBase64[i];
                backFrom64[c] = i;
            }

            return backFrom64;
        }

        int FromBase64(byte[] Output, string Input, int BufSize, int MaxSize)
        {
            int xut, txut;
            int i, t, k, shift;
            byte c;
            int dr, dr1;

            xut = txut = t = 0;
            dr = 0;

            var iInput = 0;
            var iOutput = 0;

            for (i = 0; i < BufSize; ++i)
            {
                c = (byte)Input[iInput++];
                if (c == '=')
                {
                    break;
                }
                if ((c = BackFrom64[c]) == 0xff)
                {
                    continue;
                }
                dr1 = c;
                shift = (32 - (6 * (t + 1)));
                dr1 <<= shift;
                dr |= dr1;
                t++;
                if (t == 4)
                {
                    t = 0;
                    for (k = 0; k < 3; ++k)
                    {
                        dr1 = dr;
                        dr1 >>= (32 - (8 * (k + 1)));
                        dr1 &= 0xff;
                        Output[iOutput++] = (byte)dr1;
                        if (++txut >= MaxSize)
                        {
                            return (-1);
                        }
                    }
                    dr = 0;
                }
            }
            if (t > 0)
            {
                for (k = 0; k < (t - 1); ++k)
                {
                    dr1 = dr;
                    dr1 >>= (32 - (8 * (k + 1)));
                    dr1 &= 0xff;
                    Output[iOutput++] = (byte)dr1;
                    if (++txut >= MaxSize)
                    {
                        return (-1);
                    }
                }
            }
            return (txut);
        }
        [TestMethod]
        public void TestMethod1()
        {
            var input = @"lWXtYpNpwBBXoLmcuktDqg==";
            var expectedOutput = "AC0105E92ED496ACD373D7496B76C1C8";

            var converted = new byte[(input.Length * 2) / 3];

            var result = FromBase64(converted, input, input.Length, int.MaxValue);

            Assert.AreNotEqual(-1, result, "result is valid");

            Assert.AreEqual(
                BitConverter.ToString(converted).Replace("-",""),
                BitConverter.ToString(Convert.FromBase64String(input)).Replace("-",""),
                "Supplied algorithm matches standard algorithm" );

            Assert.AreEqual(
                expectedOutput,
                BitConverter.ToString(converted).Replace("-",""),
                "Supplied algorithm matches expected output" );
        }
    }
}

Now, the first assert passes because the ported code returns exactly the same result as Convert.FromBase64String but the second one fails as this not the output you say you get from the C++.

From this I would conclude that the most likely cause of the difference is when the C++ version is being called, the content of BackFrom64 has not been initialised correctly and contains junk values - the values for BackFrom64 supplied in edit to OP result in the same output as the calculated ones, so something else is going on with the expected result. Compiling the C++ version and running with the given input:

int main()
{
    const char* input = "lWXtYpNpwBBXoLmcuktDqg==";

    BYTE Input[100];
    BYTE Output[100];

    int length = strlen(input);

    memcpy(Input, input, length);

    int result = FromBase64(Output, Input, 100, length);

    for (int i = 0; i < result; ++i)
        std::cout << std::hex << (int)Output[i] << " ";

    std::cout << "\n";
}

Generates the output

95 65 ed 62 93 69 c0 10 57 a0 b9 9c ba 4b 43 aa

which again indicates that it's the same as the standard algorithm.

Upvotes: 1

Related Questions