为什么我的字节在加密算法的这个 C# 端口的第四轮不同?
Why are my bytes different on the fourth round of this C# port of an encryption algorithm?
我正在尝试将 C++ 代码移植到 C# 并且在大多数情况下它都可以工作,但仅适用于循环的前 3 轮。在第四轮,输入块的字节开始不同,我不明白为什么。如果我们假设 C++ 版本是正确的实现,为什么 C# 代码在第四轮给出不同的结果。下面是我的结果和代码(C++/CLR 和 C# 的控制台应用程序)
我认为输入块在传递给 AES 之前在每一轮中创建的方式有所不同(在 C++ 中有一种方法可以转换为基数 256,to_base_256 和 from_base_256) 但在 C# 中,我将基本 256 字节数组直接转换为 BigInteger,然后再转换回字节数组。我只是不知道为什么每个人都会在前 3 轮中产生相同的输入块值,但在第四轮中却不会。
编辑:
经过更多调试后,我缩小了问题开始出现在 for 循环中这一行的位置,当 i = 2 (Round 3)
BigInteger AESResult = new BigInteger(t);
我的字节数组t包含的块经过AES加密后
23 , 111 , 30 , 144 , 117 , 161 , 87 , 113 , 157 , 52 , 215 , 157 , 130 , 135 , 20 , 184
但是当我使用上面的行将这些字节转换为 BigInteger 时,值上的符号突然变成负数,并且从那里开始走下坡路。该值甚至不会像前几轮那样显示在 Locals windows 中。
输出结果
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 控制台应用程序
// 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# 控制台应用程序
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);
}
}
BigInteger constructor 的文档明确指出:
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.
有几种方法可以解决这个问题,其中最简单的方法是简单地将零字节附加到字节数组,否则它会被解释为负数。这是一个简单的方法。
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);
}
}
在您的代码中,如果您将 new BigInteger(byte[])
的所有实例替换为调用
BuildPositiveBigInteger
它应该 运行 符合预期。
我正在尝试将 C++ 代码移植到 C# 并且在大多数情况下它都可以工作,但仅适用于循环的前 3 轮。在第四轮,输入块的字节开始不同,我不明白为什么。如果我们假设 C++ 版本是正确的实现,为什么 C# 代码在第四轮给出不同的结果。下面是我的结果和代码(C++/CLR 和 C# 的控制台应用程序)
我认为输入块在传递给 AES 之前在每一轮中创建的方式有所不同(在 C++ 中有一种方法可以转换为基数 256,to_base_256 和 from_base_256) 但在 C# 中,我将基本 256 字节数组直接转换为 BigInteger,然后再转换回字节数组。我只是不知道为什么每个人都会在前 3 轮中产生相同的输入块值,但在第四轮中却不会。
编辑: 经过更多调试后,我缩小了问题开始出现在 for 循环中这一行的位置,当 i = 2 (Round 3)
BigInteger AESResult = new BigInteger(t);
我的字节数组t包含的块经过AES加密后
23 , 111 , 30 , 144 , 117 , 161 , 87 , 113 , 157 , 52 , 215 , 157 , 130 , 135 , 20 , 184
但是当我使用上面的行将这些字节转换为 BigInteger 时,值上的符号突然变成负数,并且从那里开始走下坡路。该值甚至不会像前几轮那样显示在 Locals windows 中。
输出结果
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 控制台应用程序
// 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# 控制台应用程序
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);
}
}
BigInteger constructor 的文档明确指出:
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.
有几种方法可以解决这个问题,其中最简单的方法是简单地将零字节附加到字节数组,否则它会被解释为负数。这是一个简单的方法。
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);
}
}
在您的代码中,如果您将 new BigInteger(byte[])
的所有实例替换为调用
BuildPositiveBigInteger
它应该 运行 符合预期。