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);
}
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With