弄清楚为什么我的RC4实现会产生正确的结果

时间:2016-12-07 19:39:21

标签: c encryption

好的我是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!

1 个答案:

答案 0 :(得分:1)

正如我在评论中所说,你的主要问题似乎涉及如何管理输出缓冲区。你已经修改了问题以解决这个问题,但我在这里描述它,以及修复它的其他一些替代方法。最后讨论了剩下的问题。

函数rc4_crypt()为自己分配输出缓冲区,但它没有机制将指向已分配空间的指针传递回其调用者。对于预期如何管理输出缓冲区,您的修订用法还与rc4_crypt()存在一些不一致。

有三种主要方法可以解决这个问题。

  1. 函数rc4_crypt()目前不返回任何内容,因此您可以让它继续分配缓冲区本身,并修改它以返回指向已分配输出缓冲区的指针。

  2. 您可以将outbuf参数的类型修改为uint8_t **,以便rc4_crypt()间接设置来电者的指针值。

  3. 您可以依赖调用者来管理输出缓冲区,并使rc4_crypt()只通过传递给它的指针来写输出。

  4. 对你来说唯一可能很棘手的是#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循环,其中前者非常适合代码结构(如此处所示);我敢说如果你的循环结构那样,你不会犯这个错误。