4

我第一次使用结构指针,我似乎无法理解这里发生的事情。我的测试应用了 xor 的基本属性,即 x ^ y ^ y = x,但不是在 C 中?

下面的代码在我的主程序中,并准确地恢复了“test”的所有字母(我继续在屏幕上打印,但我省略了很多垃圾,以保持这个问题简短(er))。结构“aes”指的是这个定义:

typedef uint32_t word;

struct aes {

word iv[4];
word key[8];
word state[4];
word schedule[56];

};

正如上下文可能暗示的那样,封装项目是一个 AES 实现(我试图通过尝试新技术来加速我目前的实现)。

在我的测试中, make_string 和 make_state 工作可靠,即使在有问题的函数中,但为了参考:

void make_string (word in[], char out[]) {

for (int i = 0; i < 4; i++) {

    out[(i * 4) + 0] = (char) (in[i] >> 24);
    out[(i * 4) + 1] = (char) (in[i] >> 16);
    out[(i * 4) + 2] = (char) (in[i] >>  8);
    out[(i * 4) + 3] = (char) (in[i]      );

}

}

void make_state(word out[], char in[]) {

for (int i = 0; i < 4; i++) {

    out[i] =    (word) (in[(i * 4) + 0] << 24) ^
                (word) (in[(i * 4) + 1] << 16) ^
                (word) (in[(i * 4) + 2] <<  8) ^
                (word) (in[(i * 4) + 3]      );

}

}

无论如何,这是可以工作的块。这是我试图通过将其存放在一个函数中来模块化的功能:

char test[16] = {
    'a', 'b', 'c', 'd',
    'e', 'f', 'g', 'h',
    'i', 'j', 'k', 'l',
    'm', 'n', 'o', 'p'
};

aes cipher;

struct aes * work;

work = &cipher;

make_state(work->state, test);

work->state[0] ^= 0xbc6378cd;
work->state[0] ^= 0xbc6378cd;

make_string(work->state, test);

虽然这段代码有效,但通过将其传递给函数来做同样的事情不会:

void encipher_block (struct aes * work, char in[]) {

    make_state(work->state, in);

    work->state[0] ^= 0xff00cd00;

    make_string(work->state, in);

}

void decipher_block (struct aes * work, char in[]) {

    make_state(work->state, in);

    work->state[0] ^= 0xff00cd00;

    make_string(work->state, in);

}

然而,通过删除 encipher 和 decipher 中的 make_state 和 make_string 调用,它可以按预期工作!

make_state(work->state, test);

encipher_block(&cipher, test);
decipher_block(&cipher, test);

make_string(work->state, test);

所以澄清一下,我没有问题!我只是想了解这种行为。

4

2 回答 2

2

更改charunsigned charchar可能已签名,并且可能在您的系统上,这在转换为其他整数类型和移位时会导致问题。

在 中的表达式(char) (in[i] >> 24)make_string,一个无符号的 32 位整数被转换为一个有符号的 8 位整数(在您的 C 实现中)。此表达式可以将值转换为 achar中不可表示的 a char,特别是从 128 到 255 的值。根据 C 2011 6.3.1.3 3,结果是实现定义的或引发实现定义的信号。

在表达式(word) (in[(i * 4) + 3] )make_statein[…]是 a char,它是一个有符号的 8 位整数(在您的 C 实现中)。根据 C 2011 6.3.1.1 2 中定义的通常整数提升,这char将转换为。如果为负,则结果为负。然后,当它转换为无符号的 a 时,其效果是符号位被复制到高 24 位中。例如,如果值为 -166 (0x90),则结果将为,但您想要。intcharintwordchar0xffffff900x00000090

在整个代码中更改char为。unsigned char

此外, in make_state,in[(i * 4) + 0]应该word在左移之前强制转换。这是因为它将以 开头,在班次之前unsigned char提升为。int如果它的高位设置了某个值,例如 0x80,则将其左移 24 位会产生一个无法在 中表示的值int,例如 0x80000000。根据 C 2011 6.5.7 4,行为未定义。

这在大多数 C 实现中都不是问题;二进制补码通常用于有符号整数,结果将根据需要换行。此外,我希望这是编译器开发人员设计的模型情况,因为它是一种非常常见的代码结构。但是,为了提高便携性,casting toword会避免溢出的可能性。

于 2012-12-04T20:04:52.283 回答
0

make_state()函数覆盖第一个参数中传递的数组。如果你把encipher_block()anddecipher_block()身体内联,你会得到这个:

/* encipher_block inline */
make_state(work->state, in);
work->state[0] ^= 0xff00cd00;
make_string(work->state, in);

/* decipher_block inline */
make_state(work->state, in);    /* <-- Here's the problem */
work->state[0] ^= 0xff00cd00;
make_string(work->state, in);
于 2012-12-04T19:48:13.583 回答