Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are my bytes different on the fourth round of this C# port of an encryption algorithm?

I'm trying to port the C++ code to C# and for the most part it is working, however only for the first 3 round of the loop. On the fourth round, the bytes for the input block begin to differ and I don't understand why. If we assume the C++ version is the correct implementation, why is the C# code giving a different result at the fourth round. Below are my results and the code (both console applications for C++/CLR and C#)

I think something is different with the way the input block is being created at each round before it is passed to AES (in C++ there is a method to convert to and from base 256, to_base_256 and from_base_256) but in C# I'm converting the base 256 byte array directly to BigInteger and then back to byte array. I just don't know why each would result in identical input block values for the first 3 rounds but not for the fourth.

EDIT: After more debugging I narrowed down where the problem starts to show up to this line in the for loop, when i = 2 (Round 3)

BigInteger AESResult = new BigInteger(t);

After the AES encryption of the block my byte array t contains

23 , 111 , 30 , 144 , 117 , 161 , 87 , 113 , 157 , 52 , 215 , 157 , 130 , 135 , 20 , 184

BUT when I convert these bytes to BigInteger using the above line, all of a sudden the sign on the value flips to negative and it all goes downhill from there. The value doesn't even display in the Locals windows like in the previous rounds.

OUTPUT RESULTS

ROUND 1

INPUT
C++ 224,144,103,1,0,0,0,0,0,0,0,0,0,0,0,0,
C# 224,144,103,1,0,0,0,0,0,0,0,0,0,0,0,0,
AES ENCRYPTED
C++ 175,19,208,16,98,242,219,41,136,137,124,214,117,242,222,20,
C# 175,19,208,16,98,242,219,41,136,137,124,214,117,242,222,20,

ROUND 2

INPUT
C++ 168,68,153,2,0,0,0,0,0,0,0,0,1,0,0,0,
C# 168,68,153,2,0,0,0,0,0,0,0,0,1,0,0,0,
AES ENCRYPTED
C++ 182,186,181,102,204,102,32,32,232,213,226,133,59,128,225,109,
C# 182,186,181,102,204,102,32,32,232,213,226,133,59,128,225,109,

ROUND 3

INPUT
C++ 150,126,97,5,0,0,0,0,0,0,0,0,2,0,0,0,
C# 150,126,97,5,0,0,0,0,0,0,0,0,2,0,0,0,
AES ENCRYPTED
C++ 23,111,30,144,117,161,87,113,157,52,215,157,130,135,20,184,
C# 23,111,30,144,117,161,87,113,157,52,215,157,130,135,20,184,

ROUND 4

INPUT
C++ 191,210,191,0,0,0,0,0,0,0,0,0,3,0,0,0,
C# 191,255,174,252,0,0,0,0,0,0,0,0,3,0,0,0,
AES ENCRYPTED
C++ 130,187,182,115,251,12,63,157,109,110,234,35,137,208,172,203,
C# 248,197,125,177,46,103,91,217,246,8,202,219,115,4,213,37,

C++ CLR Console application

// ConsoleApplication2.cpp : main project file.

#include "stdafx.h"

using namespace System;
using namespace System::Security::Cryptography;


void unpack(unsigned int a, unsigned char *b)
{ /* unpack bytes from a word */
    b[0] = unsigned char(a);
    b[1] = unsigned char(a >> 8);
    b[2] = unsigned char(a >> 16);
    b[3] = unsigned char(a >> 24);

}

array<unsigned char>^ AES_encrypt_block(array<unsigned char>^ plainText)
{
    array<unsigned char>^ key = gcnew array<unsigned char>(16);
    key[0] = 0x01; key[1] = 0x01; key[2] = 0x01; key[3] = 0x01; key[4] = 0x01; key[5] = 0x01; key[6] = 0x01; key[7] = 0x01;
    key[8] = 0x01; key[9] = 0x01; key[10] = 0x01; key[11] = 0x01; key[12] = 0x01; key[13] = 0x01; key[14] = 0x01; key[15] = 0x01;

    AesManaged^ AES = gcnew AesManaged();
    AES->BlockSize = 128;
    AES->KeySize = 128;
    AES->Key = key;
    AES->Mode = CipherMode::ECB;
    AES->Padding = PaddingMode::None;

    array<unsigned char>^ output_buffer = gcnew array<unsigned char>(16);

    ICryptoTransform^ encryptor = AES->CreateEncryptor(AES->Key, AES->IV);
    encryptor->TransformBlock(plainText, 0, plainText->Length, output_buffer, 0);

    return output_buffer;
}

void from_base_256(unsigned char *y, int len, int s, char *x)
{
    int i, m, n;
    unsigned int c, d;

    m = 16;
    n = 0; c = 0;
    for (;;)
    {
        while (m>0 && y[m - 1] == 0) m--;
        d = 0;

        for (i = m - 1; i >= 0; i--)
        {
            d = (d << 8) + y[i];
            y[i] = d / s;
            d %= s;
        }


        d += c + x[n]; c = 0;
        if ((int)d >= s)
        {
            c = 1; x[n] = d - s;
        }
        else x[n] = d;

        n++;
        if (n >= len) break;
    }
}

int to_base_256(char *x, int len, int s, unsigned char *y)
{
    int i, j, m;
    unsigned int c;

    for (i = 0; i<16; i++)
        y[i] = 0;
    if (len == 0) return 0;

    m = 1; y[0] = x[len - 1];
    for (j = len - 2; j >= 0; j--)
    { 
        c = x[j];
        for (i = 0; i<m; i++)
        {
            c += (unsigned int)y[i] * s;
            y[i] = c & 0xff;
            c >>= 8;
        }
        if (c>0) { m++; y[m - 1] = c; }
    }

    return m;
}



int main(array<System::String ^> ^args)
{


    int i, n;

    //PLAINTEXT
    char x[256]; 
    x[0] = 1; x[1] = 4; x[2] = 2; x[3] = 5; x[4] = 6; x[5] = 9; x[6] = 8; x[7] = 7;
    x[8] = 2; x[9] = 1; x[10] = 5; x[11] = 4; x[12] = 6; x[13] = 5; x[14] = 3; x[15] = 2;

    unsigned int TL, TR;

    TR = 0;     
    TL = 0;

    int j;
    char *left, *right;
    unsigned char buff[16];
    int l, r;
    l = r = 16 / 2;
    if (16 % 2 == 1) l++;

    left = &x[0]; right = &x[l];

    for (j = 0; j < 8; j++)
    {
        System::Diagnostics::Debug::WriteLine("");
        System::Diagnostics::Debug::WriteLine("ROUND " + (j+1));

        if (j % 2 == 0)
        {
            to_base_256(right, r, 10, buff);

            unpack(TR^j, &buff[12]);

            int size = sizeof(buff) / sizeof(*buff);
            array<unsigned char>^ inputPlaintext = gcnew array<unsigned char>(size);
            for (int i = 0; i < size; i++)
                inputPlaintext[i] = buff[i];

            System::Diagnostics::Debug::WriteLine("");
            System::Diagnostics::Debug::WriteLine("INPUT");
            for (int z = 0; z < size; z++)
                System::Diagnostics::Debug::Write(inputPlaintext[z] + ",");

            array<unsigned char>^ result = AES_encrypt_block(inputPlaintext);

            System::Diagnostics::Debug::WriteLine("");
            System::Diagnostics::Debug::WriteLine("AES ENCRYPTED");
            for (int z = 0; z < size; z++)
                System::Diagnostics::Debug::Write(result[z] + ",");

            pin_ptr<unsigned char>buff = &result[0];

            from_base_256( buff, l, 10, left);

            System::Diagnostics::Debug::WriteLine("");
            System::Diagnostics::Debug::WriteLine("afterFromBase256 - left");
            for (int z = 0; z < sizeof(left) / sizeof(*left); z++)
                System::Diagnostics::Debug::Write(left[z] + " , ");
        }
        else
        {
            to_base_256(left, l, 10, buff);

            unpack(TL^j, &buff[12]);

            int size = sizeof(buff) / sizeof(*buff);
            array<unsigned char>^ inputPlaintext = gcnew array<unsigned char>(size);
            for (int i = 0; i < size; i++)
                inputPlaintext[i] = buff[i];

            System::Diagnostics::Debug::WriteLine("");
            System::Diagnostics::Debug::WriteLine("INPUT");
            for (int z = 0; z < size; z++)
                System::Diagnostics::Debug::Write(inputPlaintext[z] + ",");

            array<unsigned char>^ result = AES_encrypt_block(inputPlaintext);

            System::Diagnostics::Debug::WriteLine("");
            System::Diagnostics::Debug::WriteLine("AES ENCRYPTED");
            for (int z = 0; z < size; z++)
                System::Diagnostics::Debug::Write(result[z] + ",");

            pin_ptr<unsigned char>buff = &result[0];

            from_base_256( buff, r, 10, right);

            System::Diagnostics::Debug::WriteLine("");
            System::Diagnostics::Debug::WriteLine("afterFromBase256 - right");
            for (int z = 0; z < sizeof(right) / sizeof(*right); z++)
                System::Diagnostics::Debug::Write(right[z] + " , ");
        }
    }

    return 0;
}

C# Console application

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.IO;
using System.Numerics;

namespace BPS_ConsoleTest
{
    class Program
    {
        static void Main(string[] args)
        {

            //integer array to hold the bytes of plaintext 
            int[] plaintext = new int[16] { 1, 4, 2, 5, 6, 9, 8, 7, 2, 1, 5, 4, 6, 5, 3, 2 };

            byte[] key = new byte[16];
            key[0] = 0x01; key[1] = 0x01; key[2] = 0x01; key[3] = 0x01; key[4] = 0x01; key[5] = 0x01; key[6] = 0x01; key[7] = 0x01;
            key[8] = 0x01; key[9] = 0x01; key[10] = 0x01; key[11] = 0x01; key[12] = 0x01; key[13] = 0x01; key[14] = 0x01; key[15] = 0x01;

            //Block Cipher - AES-128
            AES AESEncrypt = new AES(key);

            int bits = 128 - 32; //128 block for AES-128

            //tweak
            int tweak = 0; //64 bit user provided tweak value
            int TR = 0; //left side of tweak
            int TL = 0; //right side of tweak

            int s = 10;

            int w = 8; //recommended number of rounds

            int BLOCK_SIZE = 16; //block size in bytes (16 = 128 bits for AES-128)
            int b = 0; //s-integer input length


            b = plaintext.Length;

            //Split the tweak into right and left
            //

            TR = tweak % (1 << 32);
            TL = (tweak - TR) / (1 << 32);

            //Split the plaintext into left and right substrings
            //
            int j = 0;
            int[] XR; //right substring
            int[] XL; //left substring

            int l; //length of left substring
            int r; //length of right substring

            if (b % 2 == 1) //b is odd
            {
                l = (b + 1) / 2;
                r = (b - 1) / 2;
            }
            else //b is even
            {
                l = r = b / 2;
            }

            XL = new int[l];
            XR = new int[r];

            for (int i = 0; i < l; i++)
                XL[i] = plaintext[i];

            j = 0;
            for (int i = l; i <= l + r - 1; i++, j++)
                XR[j] = plaintext[i];


            //initialize left and right branches

            BigInteger L = 0;

            for (int i = 0; i < l; i++)
            {
                L += XL[i] * BigInteger.Pow(s, i);
            }

            BigInteger R = 0;

            for (int i = 0; i < l; i++)
            {
                R += XR[i] * BigInteger.Pow(s, i);
            }


            byte[] initial_Lbytes = L.ToByteArray();
            byte[] initial_Rbytes = R.ToByteArray();

            int[] intitial_L = new int[l];
            int[] intitial_R = new int[r];

            foreach (byte bL in initial_Lbytes)
            {
                BigInteger num = new BigInteger(new byte[] { bL });
            }

            //8 Rounds
            for (int i = 0; i < 8; i++)
            {
                System.Diagnostics.Debug.WriteLine("");
                System.Diagnostics.Debug.WriteLine("ROUND " + (i + 1));

                if (i % 2 == 0) //even
                {
                    byte[] RBytes = R.ToByteArray();

                    byte[] inputPlaintext = new byte[16];
                    for (int k = 0; k < RBytes.Length; k++)
                        inputPlaintext[k] = RBytes[k];

                    inputPlaintext = INT2LE(TR ^ i, inputPlaintext);

                    System.Diagnostics.Debug.WriteLine("INPUT");
                    foreach (byte bb in inputPlaintext)
                        System.Diagnostics.Debug.Write(bb + ",");

                    byte[] t = AESEncrypt.Encrypt(inputPlaintext);

                    System.Diagnostics.Debug.WriteLine("");
                    System.Diagnostics.Debug.WriteLine("AES ENCRYPTED");
                    foreach (byte bb in t)
                        System.Diagnostics.Debug.Write(bb + ",");

                    BigInteger AESResult = new BigInteger(t);

                    BigInteger res = (L + AESResult) % BigInteger.Pow(s, l);

                    L = res;

                }
                else //odd
                {

                    byte[] LBytes = L.ToByteArray();


                    byte[] inputPlaintext = new byte[16];
                    for (int k = 0; k < LBytes.Length; k++)
                        inputPlaintext[k] = LBytes[k];

                    inputPlaintext = INT2LE(TL ^ i, inputPlaintext);

                    System.Diagnostics.Debug.WriteLine("INPUT");
                    foreach (byte bb in inputPlaintext)
                        System.Diagnostics.Debug.Write(bb + ",");

                    byte[] t = AESEncrypt.Encrypt(inputPlaintext);

                    System.Diagnostics.Debug.WriteLine("");
                    System.Diagnostics.Debug.WriteLine("AES ENCRYPTED");
                    foreach (byte bb in t)
                        System.Diagnostics.Debug.Write(bb + ",");

                    BigInteger AESResult = new BigInteger(t);

                    BigInteger res = (R + AESResult) % BigInteger.Pow(s, r);

                    R = res;
                }
            }

            BigInteger FINAL_R = R;
            BigInteger FINAL_L = L;

        }

        public static byte[] INT2LE(Int32 data, byte[] arr)
        {
            byte[] b = arr;
            b[12] = (byte)data;
            b[13] = (byte)(((uint)data >> 8) & 0xFF);
            b[14] = (byte)(((uint)data >> 16) & 0xFF);
            b[15] = (byte)(((uint)data >> 24) & 0xFF);
            return b;
        }
    }


    public class AES : IBlockCipher
    {
        private byte[] _key;
        public AES(byte[] key)
        {
            _key = key;
        }
        public byte[] Encrypt(byte[] input)
        {
            byte[] output_buffer = new byte[16];
            using (AesManaged E = new AesManaged())
            {
                E.BlockSize = 128;
                E.KeySize = 128;
                E.Mode = CipherMode.ECB;
                E.Key = _key;
                E.Padding = PaddingMode.None;
                //E.IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

                // Create a decrytor to perform the stream transform.
                ICryptoTransform encryptor = E.CreateEncryptor(E.Key, E.IV);
                encryptor.TransformBlock(input, 0, 16, output_buffer, 0);

            }

            //return encrypted;
            return output_buffer;
        }
    }

    interface IBlockCipher
    {
        byte[] Encrypt(byte[] input);
    }


}
like image 209
erotavlas Avatar asked Oct 17 '15 16:10

erotavlas


1 Answers

The documentation for that BigInteger constructor clearly states:

The constructor expects positive values in the byte array to use sign-and-magnitude representation, and negative values to use two's complement representation. In other words, if the highest-order bit of the highest-order byte in value is set, the resulting BigInteger value is negative. Depending on the source of the byte array, this may cause a positive value to be misinterpreted as a negative value.

There are several ways to fix this, the easiest of which is to simply append a zero byte to the byte array if it would otherwise be interpreted as a negative number. Here is a simple method to do this.

public static BigInteger BuildPositiveBigInteger(byte [] littleEndianBytes) {
    if (littleEndianBytes[littleEndianBytes.Length-1] >= 0x80) {
        byte[] newBytes = new byte[littleEndianBytes.Length + 1];
        littleEndianBytes.CopyTo (newBytes, 0);
        return new BigInteger (newBytes);
    } else {
        return new BigInteger (littleEndianBytes);
    }
}

In your code, if you replace all instances of new BigInteger(byte[]) with calls to BuildPositiveBigInteger it should run as expected.

like image 154
President James K. Polk Avatar answered Oct 22 '22 02:10

President James K. Polk