本文中的代碼僅實現了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”)
犯了一些難以發現的低級錯誤,調試調吐了,發現錯誤之後飽了。很菜,等考研結束,再好好地提升一下自己的編程水平。