4

初めて構造体ポインターを使用していますが、ここで何が起こっているのか理解できないようです。私のテストは、x ^ y ^ y = x という xor の基本的なプロパティを適用しますが、C では適用されませんか?

以下のコードは私のメインプログラムにあり、「テスト」のすべての文字を正確に復元します(画面に印刷しますが、この質問を短くするために多くのがらくたを省略しました(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);

}

それでも、暗号化と復号化の両方で 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 charます。char署名されている可能性があり、システム上にある可能性があります。これにより、他の整数型に変換するときやシフトするときに問題が発生します。

の式(char) (in[i] >> 24)make_stringは、符号なし32ビット整数が符号付き8ビット整数に変換されます(C実装の場合)。この式は、値をacharで表現できないchar値、特に128から255の値に変換する場合があります。C20116.3.1.3 3によると、結果は実装定義であるか、実装定義の信号が発生します。

の式(word) (in[(i * 4) + 3] )にはmake_statein[…]がありますchar。これは、符号付き8ビット整数です(C実装の場合)。これは、C 2011 6.3.1.1 2で定義されている通常の整数拡張に従って、にchar変換されます。が負の場合、結果は負になります。次に、符号なしのに変換すると、符号ビットが上位24ビットに複製されます。たとえば、の値が-166(0x90)の場合、結果はになりますが、が必要になります。intcharintwordchar0xffffff900x00000090

このコード全体でに変更charします。unsigned char

さらに、ではmake_state、左シフトの前にin[(i * 4) + 0]キャストする必要があります。wordこれは、シフト前にunsigned char昇格されたとして開始されるためです。int0x80など、上位ビットが設定された値がある場合、それを24ビット左にシフトすると、int0x80000000などので表すことができない値が生成されます。C 2011 6.5.7 4によると、動作は未定義です。

これは、ほとんどのC実装では問題になりません。2の補数は通常、符号付き整数に使用され、結果は必要に応じて折り返されます。さらに、これは非常に一般的なコード構造であるため、コンパイラ開発者が設計するモデルの状況であると思います。ただし、移植性を向上させるために、にキャストするwordとオーバーフローの可能性が回避されます。

于 2012-12-04T20:04:52.283 に答える
0

このmake_state()関数は、最初の引数で渡された配列を上書きします。encipher_block()と本体をインラインで配置すると、次のdecipher_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 に答える