AES-128 加解密算法實現

本文中的代碼僅實現了AES-128單個分組的加密算法和解密算法,不涉及填充和分組加密的工作模式(如CBC、CTR)

編寫環境和工具

OS:Windows 10
IDE:Clion
mingw64(x86_64-8.1.0-posix-seh-rt_v6-rev0)

加解密算法實現

實際上代碼都是C風格的,稍微改一改頭文件就是C了

AES.hpp

#ifndef AES_H_INCLUDED
#define AES_H_INCLUDED

#include <cinttypes>

/**
 * 編寫日期:2020-6-10
 * 編寫內容:AES-128單個分組加解密相關的
 * 主要函數、涉及常量和數據類型的定義
 * 修改日期:2020-6-13
 */

/// ! 僅實現AES-128 ! ///


/// 類型定義和常量

const uint8_t BLOCKSIZE = 16;
const uint8_t KEYSIZE = 16;
const uint8_t MAXROUND = 10;

typedef uint8_t round;
typedef uint8_t state[4][4];
typedef uint32_t exkey[44];
typedef char orikey[KEYSIZE + 1];
typedef char block[BLOCKSIZE + 1];

const uint8_t SBOX[16][16] = {
        {0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76},
        {0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0},
        {0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15},
        {0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75},
        {0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84},
        {0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF},
        {0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8},
        {0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2},
        {0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73},
        {0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB},
        {0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79},
        {0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08},
        {0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A},
        {0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E},
        {0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF},
        {0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16}
};

const uint8_t INV_SBOX[16][16] = {
        {0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb},
        {0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb},
        {0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e},
        {0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25},
        {0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92},
        {0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84},
        {0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06},
        {0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b},
        {0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73},
        {0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e},
        {0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b},
        {0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4},
        {0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f},
        {0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef},
        {0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61},
        {0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d}
};


/// AES分組的加解密

void AES_BlockEncrypt(block pt, exkey w, block ct);

void AES_BlockDecrypt(block ct, exkey w, block pt);

void KeyExpansion(const orikey k, exkey w); /// 密鑰擴展(生成輪密鑰)


/// AES分組加解密中共同用到的操作


void block2state(block blc, state s); /// 將字符串(明文/密文)轉爲狀態矩陣

void state2block(state s, block blc); /// 將狀態矩陣 轉爲字符串(明文/密文)

void AddRoundKey(state s, exkey w, round r); /// 輪密鑰加


/// AES分組加密中用到的其他操作


void SubBytes(state s);  /// 經過S盒

void ShiftRows(state s);  /// 行移位

void MixColumns(state s);  /// 列混淆 (最後一輪這裏是輪密鑰加)


/// AES分組解密中用到的其他操作


void InvShiftRows(state s);  /// 逆行移位

void InvSubBytes(state s);    /// 經過逆S盒

void InvMixColumns(state s);  /// 逆列混淆 (最後一輪這裏是輪密鑰加)


#endif // AES_H_INCLUDED

AES.cpp

#include "AES.hpp"

/**
 * 編寫日期:2020-6-12
 * 編寫內容:AES-128分組加解密實現
 * 修改日期:2020-6-15
 */


void AES_BlockEncrypt(block pt, exkey w, block ct) {
    state s;

    block2state(pt, s);

    AddRoundKey(s, w, 0);

    // 第一輪到第九輪
    for (int round = 1; round < 10; ++round) {
        SubBytes(s);
        ShiftRows(s);
        MixColumns(s);
        AddRoundKey(s, w, round);
    }

    // 第十輪
    SubBytes(s);
    ShiftRows(s);
    AddRoundKey(s, w, 10);

    state2block(s, ct);
}

void AES_BlockDecrypt(block ct, exkey w, block pt) {
    state s;

    block2state(ct, s);

    AddRoundKey(s, w, 10);
    for (int round = 9; round > 0; --round){
        InvShiftRows(s);

        InvSubBytes(s);
        AddRoundKey(s, w, round);
        InvMixColumns(s);
    }

    InvShiftRows(s);
    InvSubBytes(s);
    AddRoundKey(s, w, 0);

    state2block(s, pt);
}

/// 用於密鑰擴展
uint32_t SubWord(uint32_t in);
uint32_t RotWord(uint32_t in);
static const uint32_t Rcon[11] = {0x0, 0x01000000, 0x02000000,  // 0x0 unused here
                              0x04000000, 0x08000000,
                              0x10000000, 0x20000000,
                              0x40000000, 0x80000000,
                              0x1b000000, 0x36000000 };
/// 密鑰擴展(生成輪密鑰)
void KeyExpansion(const orikey key, exkey w) {
    uint32_t tmp;
    int i = 0;
    // AES-128 ,Nk = 4
    while (i < 4) {
        uint32_t one = ((uint32_t)key[4*i] & 0xFF) << 24;
        uint32_t two = ((uint32_t)key[4*i+1] & 0xFF) << 16;
        uint32_t three = ((uint32_t)key[4*i+2] & 0xFF) << 8;
        uint32_t four = (uint32_t)key[4*i+3] & 0xFF;
        w[i] = one | two | three | four;
        i++;
    }
    i = 4;
    // AES-128
    while (i < 44) {
        tmp = w[i-1];
        if (i % 4 == 0){
            tmp = SubWord(RotWord(tmp));
            tmp ^=  Rcon[i/4];
        }
        // For AES-128 only here
        w[i] = w[i - 4] ^ tmp;
        i++;
    }
    i = 4;
}

uint32_t SubWord(uint32_t in) {
    uint8_t tmp[4];
    uint8_t row,col;
    uint32_t res = 0;

    for (int i = 0; i < 4; ++i) {
        tmp[i] = (in >> (3 - i) * 8) & 0xFF;
        row = (tmp[i] >> 4) & 0x0F;
        col = tmp[i] & 0x0F;
        tmp[i] = SBOX[row][col];
    }

    uint32_t one = ((uint32_t)tmp[0] & 0xFF) << 24;
    uint32_t two = ((uint32_t)tmp[1] & 0xFF) << 16;
    uint32_t three = ((uint32_t)tmp[2] & 0xFF) << 8;
    uint32_t four = (uint32_t)tmp[3] & 0xFF;
    res = one | two | three | four;

    return res;
}

uint32_t RotWord(uint32_t in){
    return (in << 8) | (in >> 24);
}


/// AES分組加解密中共同用到的操作


/// 輪密鑰加
void AddRoundKey(state s, exkey w, round r) {
    uint8_t tmp;

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

        tmp = (uint8_t) ((w[r * 4 + c] & 0xFF000000) >> 24);
        s[0][c] ^= tmp;

        tmp = (uint8_t) ((w[r * 4 + c] & 0x00FF0000) >> 16);
        s[1][c] ^= tmp;

        tmp = (uint8_t) ((w[r * 4 + c] & 0x0000FF00) >> 8);
        s[2][c] ^= tmp;

        tmp = (uint8_t)(w[r * 4 + c] & 0x000000FF);
        s[3][c] ^= tmp;
    }
}

/// 將字符串(明文/密文)轉爲狀態矩陣
void block2state(block blc, state s) {
    for (int row = 0; row < 4; row++) {
        for (int col = 0; col < 4; col++) {
            s[row][col] = blc[4 * col + row];
        }
    }
}

/// 將狀態矩陣 轉爲字符串(明文/密文)
void state2block(state s, block blc) {
    for (int row = 0; row < 4; row++) {
        for (int col = 0; col < 4; col++) {
            blc[4 * col + row] = s[row][col];
        }
    }
    blc[BLOCKSIZE] = '\0';
}


/// AES分組加密中用到的其他操作


/// 經過S盒
void SubBytes(state s) {
    uint8_t row, col;

    for (int i = 0; i < 4; ++i) {
        for (int j = 0; j < 4; ++j) {
            row = (s[i][j] >> 4) & 0x0F;
            col = s[i][j] & 0x0F;
            s[i][j] = SBOX[row][col];
        }
    }
}

/// 行移位
void ShiftRows(state s) {
    uint32_t tmp;
    // 第0行不用移
    for (int i = 1; i < 4; ++i) {
        uint32_t one = ((uint32_t)s[i][0] & 0xFF) << 24;
        uint32_t two = ((uint32_t)s[i][1] & 0xFF) << 16;
        uint32_t three = ((uint32_t)s[i][2] & 0xFF) << 8;
        uint32_t four = (uint32_t)s[i][3] & 0xFF;
        tmp = one | two | three | four;
        // 循環左移 8r 位
        tmp = (tmp << (8 * i)) | (tmp >> (32 - 8 * i));
        // 取數
        s[i][0] = (uint8_t)(tmp >> 24) & 0xFF;
        s[i][1] = (uint8_t)(tmp >> 16) & 0xFF;
        s[i][2] = (uint8_t)(tmp >> 8) & 0xFF;
        s[i][3] = (uint8_t)(tmp & 0xFF);
    }
}

/// GF(2^8)上的乘法,用於列混淆和逆列混淆
/// 選取不可約多項式 p(x) 爲 x^8 + x^4 + x^3 + x + 1.
uint8_t GMul(uint8_t a, uint8_t b) {
    uint8_t res = 0;
    uint8_t tmp;

    res ^= (a & 0x1) ? b : 0;
    for (int i = 1; i < 8; ++i) {
        // 計算a(x)的i次項乘以b(x)的結果,並與當前結果相加(異或)
        if ((a >> i) & 0x1) {
            tmp = b;
            for (int timeMulx = 1; timeMulx <= i; ++timeMulx) {
                // 計算 x乘以b(x)的結果
                if (tmp & 0x80) {            // 若deg(tmp(x)) = 7 則 x·tmp(x) 爲8次多項式,需要模一下(減一下)p(x)
                    tmp = (tmp << 1) ^ 0x1B; // 而減法就是加法,因此 加(XOR)0x1B即爲減去p(x)
                } else { tmp = tmp << 1; }
            }
            res ^= tmp;
        }
    }
    return res;
    //    for (int i = 0; i < 8; ++i) {
//        if (a & 0x01) {
//            res ^= b;
//        }
//        /// if deg(f) < 7 , then x·f(x) = byte[f(x)] << 1
//        /// if deg(f) = 7 , then x·f(x) = x·f(x) - p(x) = x·f(x) - (x^4 + x^3 + x + 1) = (byte[f(x)] << 1) ^ 0x1B
//        uint8_t flag = (b & 0x80);
//        b <<= 1;
//        if (flag) {
//            b ^= 0x1B;
//        }
//
//        a >>= 1;
//    }
//    return res;
}



/// 列混淆
void MixColumns(state s) {
    uint8_t tmp[4];
    for (int c = 0; c < 4; ++c) {
        for (int i = 0; i < 4; ++i) {
           tmp[i] = s[i][c];
        }
        s[0][c] = GMul(0x02, tmp[0]) ^ GMul(0x03, tmp[1]) ^ tmp[2] ^ tmp[3];
        s[1][c] = tmp[0] ^ GMul(0x02, tmp[1]) ^ GMul(0x03, tmp[2]) ^ tmp[3];
        s[2][c] = tmp[0] ^ tmp[1] ^ GMul(0x02, tmp[2]) ^ GMul(0x03, tmp[3]);
        s[3][c] = GMul(0x03, tmp[0]) ^ tmp[1] ^ tmp[2] ^ GMul(0x02, tmp[3]);
    }
}


/// AES分組解密中用到的其他操作


/// 逆行移位
void InvShiftRows(state s) {
    uint32_t tmp;
    // 第0行不用移
    for (int i = 1; i < 4; ++i) {
        uint32_t one = ((uint32_t)s[i][0] & 0xFF) << 24;
        uint32_t two = ((uint32_t)s[i][1] & 0xFF) << 16;
        uint32_t three = ((uint32_t)s[i][2] & 0xFF) << 8;
        uint32_t four = (uint32_t)s[i][3] & 0xFF;
        tmp = one | two | three | four;
        // 循環右移 8r 位
        tmp = (tmp >> (8 * i)) | (tmp << (32 - 8 * i));
        // 取數
        s[i][0] = (uint8_t)(tmp >> 24) & 0xFF;
        s[i][1] = (uint8_t)(tmp >> 16) & 0xFF;
        s[i][2] = (uint8_t)(tmp >> 8) & 0xFF;
        s[i][3] = (uint8_t)(tmp & 0xFF);
    }
}

/// 經過逆S盒
void InvSubBytes(state s) {
    uint8_t row, col;
    // TODO: debug
    for (int i = 0; i < 4; ++i) {
        for (int j = 0; j < 4; ++j) {
            row = (s[i][j] >> 4) & 0x0F;
            col = s[i][j] & 0x0F;
            s[i][j] = INV_SBOX[row][col];
        }
    }
}

/// 逆列混淆
void InvMixColumns(state s) {
    uint8_t tmp[4];

    for (int c = 0; c < 4; ++c) {
        for (int i = 0; i < 4; ++i) {
            tmp[i] = s[i][c];
        }
        s[0][c] = GMul(0x0E, tmp[0]) ^ GMul(0x0B, tmp[1]) ^ GMul(0x0D, tmp[2]) ^ GMul(0x09, tmp[3]);
        s[1][c] = GMul(0x09, tmp[0]) ^ GMul(0x0E, tmp[1]) ^ GMul(0x0B, tmp[2]) ^ GMul(0x0D, tmp[3]);
        s[2][c] = GMul(0x0D, tmp[0]) ^ GMul(0x09, tmp[1]) ^ GMul(0x0E, tmp[2]) ^ GMul(0x0B, tmp[3]);
        s[3][c] = GMul(0x0B, tmp[0]) ^ GMul(0x0D, tmp[1]) ^ GMul(0x09, tmp[2]) ^ GMul(0x0E, tmp[3]);
    }
}

測試用例

test_AES.hpp

#ifndef AES_DES_CBC_TEST_AES_HPP
#define AES_DES_CBC_TEST_AES_HPP

void test_AES_BlockEncrypt();
void test_AES_BlockDecrypt();

#endif //AES_DES_CBC_TEST_AES_HPP

test_AES.cpp

#include <cstdio>
#include "AES.hpp"
#include "test_AES.hpp"
#include "AES.cpp"

void test_AES_BlockEncrypt() {
/**
 * plainText: 3243f6a8885a308d313198a2e0370734
 * key: 2b7e151628aed2a6abf7158809cf4f3c
 * expect cipherText:3925841d02dc09fbdc118597196a0b32
 */
    block pt = "\x32\x43\xf6\xa8\x88\x5a\x30\x8d\x31\x31\x98\xa2\xe0\x37\x07\x34";
    block ct = "\0";
    orikey key = "\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c";
    exkey w = {0};
    uint8_t *p;

    KeyExpansion(key, w);
    AES_BlockEncrypt(pt, w, ct);

    printf("The cipherText is:\n");
    p = (uint8_t*)ct;
    for (int i = 0; i < 16; ++i) {
        printf("%02x ", *p);
        p++;
    }
    printf("\n");
}

void test_AES_BlockDecrypt() {
/**
 * cipherText: 39 25 84 1d 02 dc 09 fb dc 11 85 97 19 6a 0b 32
 * key: 2b 7e 15 16 28 ae d2 a6 ab f7 15 88 09 cf 4f 3c
 * expect plainText: 32 43 f6 a8 88 5a 30 8d 31 31 98 a2 e0 37 07 34
 */
    block ct = "\x39\x25\x84\x1d\x02\xdc\x09\xfb\xdc\x11\x85\x97\x19\x6a\x0b\x32";
    block pt = "\0";
    orikey key = "\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c";
    exkey w = {0};
    uint8_t *p;

    KeyExpansion(key, w);
    AES_BlockDecrypt(ct, w, pt);

    printf("The plainText is:\n");
    p = (uint8_t*)pt;
    for (int i = 0; i < 16; ++i) {
        printf("%02x ", *p);
        p++;
    }
    printf("\n");
}

main.cpp

#define TEST_AES


#ifdef TEST_AES
#include "test_AES.hpp"

int main(){
    test_AES_BlockEncrypt();
    test_AES_BlockDecrypt();
    return 0;
}

#endif

參考資料

後記

這次實驗並沒有我想象的那麼簡單,遇到了很多坑,也發現了自己的問題(比如說位運算各種寫錯)。爲了解決鏈接時的一個奇怪的問題,代碼中使用了一些邪惡的手段(沒錯,就是 #include “AES.cpp”)

犯了一些難以發現的低級錯誤,調試調吐了,發現錯誤之後飽了。很菜,等考研結束,再好好地提升一下自己的編程水平。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章