好的我是C的新手,我已经用C#编程了大约10年了,所以仍然习惯了整个语言,我一直在学习上很棒,但我仍然有一些hickup,目前我正试图编写一个实现在Xbox 360上用于加密KeyVault /帐户数据的RC4。
但是我遇到了麻烦,代码有效,但它输出的数据不正确,我提供了我正在使用的原始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不工作,我试图解密部分Kyevault应该==“Xbox 360 “||”58626F7820333630“
输出目前是:“0000008108020000”我在编译中没有任何错误,再次任何帮助都会很棒^。^
感谢John的帮助,我能够修复它,这是c#版本中的错误,感谢John!
答案 0 :(得分:1)
正如我在评论中所说,你的主要问题似乎涉及如何管理输出缓冲区。你已经修改了问题以解决这个问题,但我在这里描述它,以及修复它的其他一些替代方法。最后讨论了剩下的问题。
函数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()
中的打印语句来报告加密数据。通过print语句调试我没有任何问题,但是你做需要小心打印你想要检查的数据。在这种情况下,你没有。在从输出缓冲区打印一个字节之前,在加密循环结束时更新联合缓冲区索引idx
。因此,在每次迭代时,您不会打印刚刚计算的加密字节值,而是打印在输出缓冲区的 next 位置的不确定值。
将idx++
移至循环的最后以解决此问题,或将其从while
循环更改为for
循环并在idx
中增加for
循环控制语句的第三项。事实上,我强烈建议在while
循环上git checkout -m cpanfile
循环,其中前者非常适合代码结构(如此处所示);我敢说如果你的循环结构那样,你不会犯这个错误。