好的,我是C语言的新手,我已经用C#编程了大约10年了,所以仍然习惯于整个语言。我在学习方面一直做得很好,但是我仍然有一些建议,目前我正在尝试编写使用的RC4实现在Xbox 360上加密KeyVault /帐户数据。
但是我遇到了麻烦,代码可以工作,但是输出的数据不正确,我提供了我正在使用的原始c#代码,我知道可以工作,并且提供了C项目中的代码段,所有帮助/指针将不胜感激:)
原始C#代码:
public struct RC4Session
{
public byte[] Key;
public int SBoxLen;
public byte[] SBox;
public int I;
public int J;
}
public static RC4Session RC4CreateSession(byte[] key)
{
RC4Session session = new RC4Session
{
Key = key,
I = 0,
J = 0,
SBoxLen = 0x100,
SBox = new byte[0x100]
};
for (int i = 0; i < session.SBoxLen; i++)
{
session.SBox[i] = (byte)i;
}
int index = 0;
for (int j = 0; j < session.SBoxLen; j++)
{
index = ((index + session.SBox[j]) + key[j % key.Length]) % session.SBoxLen;
byte num4 = session.SBox[index];
session.SBox[index] = session.SBox[j];
session.SBox[j] = num4;
}
return session;
}
public static void RC4Encrypt(ref RC4Session session, byte[] data, int index, int count)
{
int num = index;
do
{
session.I = (session.I + 1) % 0x100;
session.J = (session.J + session.SBox[session.I]) % 0x100;
byte num2 = session.SBox[session.I];
session.SBox[session.I] = session.SBox[session.J];
session.SBox[session.J] = num2;
byte num3 = data[num];
byte num4 = session.SBox[(session.SBox[session.I] + session.SBox[session.J]) % 0x100];
data[num] = (byte)(num3 ^ num4);
num++;
}
while (num != (index + count));
}
现在这是我自己的c版本:
typedef struct rc4_state {
int s_box_len;
uint8_t* sbox;
int i;
int j;
} rc4_state_t;
unsigned char* HMAC_SHA1(const char* cpukey, const unsigned char* hmac_key) {
unsigned char* digest = malloc(20);
digest = HMAC(EVP_sha1(), cpukey, 16, hmac_key, 16, NULL, NULL);
return digest;
}
void rc4_init(rc4_state_t* state, const uint8_t *key, int keylen)
{
state->i = 0;
state->j = 0;
state->s_box_len = 0x100;
state->sbox = malloc(0x100);
// Init sbox.
int i = 0, index = 0, j = 0;
uint8_t buf;
while(i < state->s_box_len) {
state->sbox[i] = (uint8_t)i;
i++;
}
while(j < state->s_box_len) {
index = ((index + state->sbox[j]) + key[j % keylen]) % state->s_box_len;
buf = state->sbox[index];
state->sbox[index] = (uint8_t)state->sbox[j];
state->sbox[j] = (uint8_t)buf;
j++;
}
}
void rc4_crypt(rc4_state_t* state, const uint8_t *inbuf, uint8_t **outbuf, int buflen)
{
int idx = 0;
uint8_t num, num2, num3;
*outbuf = malloc(buflen);
if (*outbuf) { // do not forget to test for failed allocation
while(idx != buflen) {
state->i = (int)(state->i + 1) % 0x100;
state->j = (int)(state->j + state->sbox[state->i]) % 0x100;
num = (uint8_t)state->sbox[state->i];
state->sbox[state->i] = (uint8_t)state->sbox[state->j];
state->sbox[state->j] = (uint8_t)num;
num2 = (uint8_t)inbuf[idx];
num3 = (uint8_t)state->sbox[(state->sbox[state->i] + (uint8_t)state->sbox[state->j]) % 0x100];
(*outbuf)[idx] = (uint8_t)(num2 ^ num3);
printf("%02X", (*outbuf)[idx]);
idx++;
}
}
printf("\n");
}
用法(c#):
byte[] cpukey = new byte[16]
{
...
};
byte[] hmac_key = new byte[16]
{
...
};
byte[] buf = new System.Security.Cryptography.HMACSHA1(cpukey).ComputeHash(hmac_key);
MessageBox.Show(BitConverter.ToString(buf).Replace("-", ""), "");
用法(c):
const char cpu_key[16] = { 0xXX, 0xXX, 0xXX };
const unsigned char hmac_key[16] = { ... };
unsigned char* buf = HMAC_SHA1(cpu_key, hmac_key);
uint8_t buf2[20];
uint8_t buf3[8] = { 0x1E, 0xF7, 0x94, 0x48, 0x22, 0x26, 0x89, 0x8E }; // Encrypted Xbox 360 data
uint8_t* buf4;
// Allocated 8 bytes out.
buf4 = malloc(8);
int num = 0;
while(num < 20) {
buf2[num] = (uint8_t)buf[num]; // convert const char
num++;
}
rc4_state_t* rc4 = malloc(sizeof(rc4_state_t));
rc4_init(rc4, buf2, 20);
rc4_crypt(rc4, buf3, &buf4, 8);
现在我已经弄清楚了HMACsha1,我为此使用了openssl,并且我确认我得到了正确的hmac /解密密钥,只是rc4不起作用,我试图解密应该==“ Xbox 360”的Kyevault的一部分。 “ 58626F7820333630”
当前输出为:“ 0000008108020000”我在编译中未收到任何错误,再次提供任何帮助将是非常棒的^。^
多亏了约翰的帮助,我才得以解决它,这在C#版本中是一个错误,谢谢约翰!
最佳答案
正如我在评论中指出的那样,您的主要问题似乎涉及如何管理输出缓冲区。此后,您已经修改了该问题以解决此问题,但是无论如何我还是在这里进行描述,并提供其他一些解决方案。最后讨论剩余的问题。
函数rc4_crypt()
为其自身分配输出缓冲区,但是它没有机制将指向已分配空间的指针传递回其调用方。在预期如何管理输出缓冲区方面,您修改后的用法还与rc4_crypt()
不一致。
解决问题的方法主要有三种。
函数rc4_crypt()
目前不返回任何内容,因此您可以让它继续分配缓冲区本身,并对其进行修改以返回指向分配的输出缓冲区的指针。
您可以将outbuf
参数的类型修改为uint8_t **
以使rc4_crypt()
间接设置调用者的指针值。
您可以依靠调用方来管理输出缓冲区,并使rc4_crypt()
只是通过传递给它的指针写入输出。
可能对您来说棘手的只有#2;它看起来像这样:
void rc4_crypt(rc4_state_t* state, const uint8_t *inbuf, uint8_t **outbuf, int buflen) {
*outbuf = malloc(buflen);
if (*outbuf) { // do not forget to test for failed allocation
// ...
(*outbuf)[idx] = (uint8_t)(num2 ^ num3);
// ...
}
}
您将像这样使用它:
rc4_crypt(rc4, buf3, &buf4, 8);
...无需为
buf4
分配任何内存。在任何情况下,调用方都有责任在不再需要输出缓冲区时将其释放。当它自己执行分配时,这一点更加清楚。如果
rc4_crypt()
将负责分配,则应记录该要求。剩下的问题似乎严格是输出问题。您显然是依靠
rc4_crypt()
中的打印语句来报告加密数据。无论通过打印语句进行调试,我都没有问题,但是您确实需要小心打印实际要检查的数据。在这种情况下,您不需要。在从输出缓冲区打印字节之前,请在加密循环的末尾更新联合缓冲区索引idx
。结果,在每次迭代中,您不会打印刚刚计算出的加密字节值,而是打印一个不确定值,该值恰好位于输出缓冲区的下一个位置。将
idx++
移至循环的最末端以解决此问题,或将其从while
循环更改为for
循环,并在循环控制语句的第三项中递增idx
。实际上,我强烈建议for
循环而不是while
循环,在这种情况下,前者非常适合代码的结构(如下所示);我敢说,如果以这种方式构造循环,就不会犯这个错误。关于c - 弄清楚为什么我的RC4实现无法产生正确的结果,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/41025926/