ikautak.log

C/C++, Python, CUDA, Android, Linux kernel, Network, etc.

暗号理論入門 4.8.2

暗号理論入門 原書第3版

暗号理論入門 原書第3版

4.8.2 CBCモード(cipherblock chaining mode)

ECBモードの欠点を除去するためにCBCモードが考案された。このモードでは、平文ブロックの暗号化は暗号化したい平文ブロックとその鍵だけでなく、先行するブロックにも依存している。同一の平文でも、異なった文脈の中では異なった形で暗号化される。

CBCモードには固定された初期ベクトル(Initialization vector)  IV \in \Sigma^{n} が必要となる。

長さ n の平文ブロック列 m_1,\ \ ...,\ \ m_t を鍵 e で暗号する場合、
  c_0 = IV,\ \ \ \ \ \ c_j = E_e (c_{j-1} \oplus m_j),\ \ \ \ \ \ 1 \le j \le t
とおき暗号文ブロック  c_1,\ \ \ ...,\ \ \ c_t を得る。( c_{j-1} とXORしてから暗号化)

復号化するためには、
  c_0 = IV,\ \ \ \ \ \ m_j = c_{j-1} \oplus D_d(c_j),\ \ \ \ \ \ 1 \le j \le t
とおき平文ブロック列を得る。(復号化してから c_{j-1} とXOR)

C++で書いてみる。ブロック長は8bit、初期ベクトルは10101010b(0xAA)、鍵はbit操作しやすいように  \left( \begin{array}{cccccccc} 7\ \ 6\ \ 5\ \ 4\ \ 3\ \ 2\ \ 1\ \ 0 \\ 6\ \ 5\ \ 4\ \ 3\ \ 2\ \ 1\ \ 0\ \ 7 \end{array} \right) とする。

#include <iostream>

class CBC {
public:
    CBC(int* key, const int block_len, const unsigned char iv)
        : BLOCK_LEN(block_len), IV(iv) {
        mKey = new int[BLOCK_LEN];

        for (int i = 0; i < BLOCK_LEN; ++i) {
            mKey[i] = key[i];
        }
    }

    ~CBC() {
        delete[] mKey;
    }

    std::string Encrypt(std::string& m) {
        std::string c = m;
        unsigned char v = IV;

        int block_num = c.size();
        for (int i = 0; i < block_num; ++i) {
            unsigned char c_i = 0x00;

            // permute from MSB.
            for (int b = 0; b < BLOCK_LEN; ++b) {
                unsigned char m_i = ((v^m[i]) >> mKey[b]) & 0x01;
                c_i |= m_i << (BLOCK_LEN - b - 1);
            }

            c[i] = c_i;
            v = c_i;
        }

        return c;
    }

    std::string Decrypt(std::string& c) {
        std::string m = c;
        unsigned char v = IV;

        int block_num = m.size();
        for (int i = 0; i < block_num; ++i) {
            unsigned char m_i = 0x00;

            // permute from MSB.
            for (int b = 0; b < BLOCK_LEN; ++b) {
                unsigned char c_i = (c[i] >> (BLOCK_LEN - b - 1)) & 0x01;
                m_i |= c_i << mKey[b];
            }

            m[i] = v^m_i;
            v = c[i];
        }

        return m;
    }

private:
    const int BLOCK_LEN;
    const unsigned char IV;
    int* mKey;
};

int main(void) {
    int key[8] = {6, 5, 4, 3, 2, 1, 0, 7};
    unsigned char IV = 0xAA;
    CBC* cbc = new CBC(key, 8, IV);

    std::string p = "Hello World! block cipher CBC mode.";
    std::cout << "p   : " << p << std::endl;

    std::string e = cbc->Encrypt(p);
    std::cout << "enc : " << e << std::endl;

    p = cbc->Decrypt(e);
    std::cout << "dec : " << p << std::endl;

    delete cbc;

    return 0;
}

bit操作がカオス。char単位で鍵の対応しているbitを置換していくが、その際、前の結果とXORすることで同じ文字を暗号化しても結果が異なる。実行結果を見てもわからないが…

実行結果

$ ./cbc
p   : Hello World! block cipher CBC mode.
enc : �AZlL6����9�����[p2��yl^8����s,
dec : Hello World! block cipher CBC mode.

CBCモードは暗号文ブロックの順序を入れ替えたり、1つの暗号文ブロックを書き換えると、復号化できなくなるという長所がある。