ARMv8架构AES解密异常:解密结果与原输入不匹配求助
问题
我有一台ARMv8 64位架构设备,想利用其指令加速AES运算。找到一段仅实现AES加密的ARM AES代码后,自行编写了解密函数,但解密结果与原输入不匹配,附上完整代码,请求排查问题:
#if defined(__arm__) || defined(__aarch32__) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM) # if defined(__GNUC__) # include <stdint.h> # endif # if defined(__ARM_NEON) || defined(_MSC_VER) # include <arm_neon.h> # endif /* GCC and LLVM Clang, but not Apple Clang */ # if defined(__GNUC__) && !defined(__apple_build_version__) # if defined(__ARM_ACLE) || defined(__ARM_FEATURE_CRYPTO) # include <arm_acle.h> #include <string.h> # endif # endif #endif /* ARM Headers */ void aes_process_arm(const uint8_t key[], const uint8_t subkeys[], uint32_t rounds, const uint8_t input[], uint8_t output[], uint32_t length) { while (length >= 16) { uint8x16_t block = vld1q_u8(input); // AES single round encryption: AddRoundKey + SubBytes + ShiftRows block = vaeseq_u8(block, vld1q_u8(key)); // AES mix columns block = vaesmcq_u8(block); // AES single round encryption block = vaeseq_u8(block, vld1q_u8(subkeys)); // AES mix columns block = vaesmcq_u8(block); for (unsigned int i=1; i<rounds-2; ++i) { // AES single round encryption block = vaeseq_u8(block, vld1q_u8(subkeys+i*16)); // AES mix columns block = vaesmcq_u8(block); } // AES single round encryption (no MixColumns after) block = vaeseq_u8(block, vld1q_u8(subkeys+(rounds-2)*16)); // Final Add (bitwise Xor) block = veorq_u8(block, vld1q_u8(subkeys+(rounds-1)*16)); vst1q_u8(output, block); input += 16; output += 16; length -= 16; } } void aes_decrypt_arm(const uint8_t key[], const uint8_t subkeys[], uint32_t rounds, const uint8_t input[], uint8_t output[], uint32_t length) { // Reverse the order of the subkeys uint8_t reversed_subkeys[rounds*16]; for (unsigned int i=0; i<rounds; ++i) { memcpy(reversed_subkeys+i*16, subkeys+(rounds-i-1)*16, 16); } while (length >= 16) { uint8x16_t block = vld1q_u8(input); // AES Final Add (bitwise Xor) block = veorq_u8(block, vld1q_u8(reversed_subkeys)); for (unsigned int i=rounds-1; i>0; --i) { // AES single round decryption block = vaesdq_u8(block, vld1q_u8(reversed_subkeys+i*16)); // AES inverse mix columns block = vaesimcq_u8(block); } // AES single round decryption block = vaesdq_u8(block, vld1q_u8(reversed_subkeys)); // AES single round decryption block = vaesdq_u8(block, vld1q_u8(reversed_subkeys+(rounds-1)*16)); vst1q_u8(output, block); input += 16; output += 16; length -= 16; } } #include <stdio.h> #include <string.h> int main(int argc, char* argv[]) { /* FIPS 197, Appendix B input */ const uint8_t input[16] = { /* user input, unaligned buffer */ 0x32, 0x43, 0xf6, 0xa8, 0x88, 0x5a, 0x30, 0x8d, 0x31, 0x31, 0x98, 0xa2, 0xe0, 0x37, 0x07, 0x34 }; /* FIPS 197, Appendix B key */ const uint8_t key[16] = { /* user input, unaligned buffer */ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c }; /* FIPS 197, Appendix B expanded subkeys */ __attribute__((aligned(4))) const uint8_t subkeys[10][16] = { /* library controlled, aligned buffer */ {0xA0, 0xFA, 0xFE, 0x17, 0x88, 0x54, 0x2c, 0xb1, 0x23, 0xa3, 0x39, 0x39, 0x2a, 0x6c, 0x76, 0x05}, {0xF2, 0xC2, 0x95, 0xF2, 0x7a, 0x96, 0xb9, 0x43, 0x59, 0x35, 0x80, 0x7a, 0x73, 0x59, 0xf6, 0x7f}, {0x3D, 0x80, 0x47, 0x7D, 0x47, 0x16, 0xFE, 0x3E, 0x1E, 0x23, 0x7E, 0x44, 0x6D, 0x7A, 0x88, 0x3B}, {0xEF, 0x44, 0xA5, 0x41, 0xA8, 0x52, 0x5B, 0x7F, 0xB6, 0x71, 0x25, 0x3B, 0xDB, 0x0B, 0xAD, 0x00}, {0xD4, 0xD1, 0xC6, 0xF8, 0x7C, 0x83, 0x9D, 0x87, 0xCA, 0xF2, 0xB8, 0xBC, 0x11, 0xF9, 0x15, 0xBC}, {0x6D, 0x88, 0xA3, 0x7A, 0x11, 0x0B, 0x3E, 0xFD, 0xDB, 0xF9, 0x86, 0x41, 0xCA, 0x00, 0x93, 0xFD}, {0x4E, 0x54, 0xF7, 0x0E, 0x5F, 0x5F, 0xC9, 0xF3, 0x84, 0xA6, 0x4F, 0xB2, 0x4E, 0xA6, 0xDC, 0x4F}, {0xEA, 0xD2, 0x73, 0x21, 0xB5, 0x8D, 0xBA, 0xD2, 0x31, 0x2B, 0xF5, 0x60, 0x7F, 0x8D, 0x29, 0x2F}, {0xAC, 0x77, 0x66, 0xF3, 0x19, 0xFA, 0xDC, 0x21, 0x28, 0xD1, 0x29, 0x41, 0x57, 0x5c, 0x00, 0x6E}, {0xD0, 0x14, 0xF9, 0xA8, 0xC9, 0xEE, 0x25, 0x89, 0xE1, 0x3F, 0x0c, 0xC8, 0xB6, 0x63, 0x0C, 0xA6} }; /* Result */ uint8_t ciphertext[19] = { 0 }; uint8_t decryption[19]= { 0 }; aes_process_arm((const uint8_t*)key, (const uint8_t*)subkeys, 10, input, ciphertext+3, 16); printf("Input: "); for (unsigned int i=0; i<16; ++i) printf("%02X ", input[i]); printf("\n"); printf("Key: "); for (unsigned int i=0; i<16; ++i) printf("%02X ", key[i]); printf("\n"); printf("Output: "); for (unsigned int i=3; i<19; ++i) printf("%02X ",ciphertext[i]); printf("\n"); aes_decrypt_arm((const uint8_t*)key, (const uint8_t*)subkeys, 10, ciphertext+3, decryption+3, 16); printf("Decipher: "); for (unsigned int i=3; i<19; ++i) printf("%02X ", decryption[i]); printf("\n"); /* FIPS 197, Appendix B output */ const uint8_t exp[16] = { 0x39, 0x25, 0x84, 0x1D, 0x02, 0xDC, 0x09, 0xFB, 0xDC, 0x11, 0x85, 0x97, 0x19, 0x6A, 0x0B, 0x32 }; if (0 == memcmp(ciphertext+3, exp, 16)) printf("SUCCESS!!!\n"); else printf("FAILURE!!!\n"); if (0 == memcmp(decryption+3, input, 16)) printf("Decryption SUCCESS!!!\n"); else printf("Decryption FAILURE!!!\n"); return 0; }
问题排查与修复
核心错误点
子密钥处理逻辑完全错误
AES解密不能直接反转加密子密钥的顺序,需要对加密阶段的子密钥执行逆密钥扩展变换(即对除初始密钥和最后一轮子密钥外的所有加密子密钥应用InvMixColumns操作),才能得到解密所需的子密钥。原代码仅简单反转子密钥顺序,导致每轮使用的密钥完全不符合AES解密规范。解密轮次流程混乱
- 原解密函数的初始异或操作使用了错误的子密钥
- 循环内重复调用
vaesdq_u8,且vaesimcq_u8(逆MixColumns)的位置不符合解密流程 - 解密的最后一步多执行了一次不必要的
vaesdq_u8调用
轮次计数匹配错误
AES-128的轮数是10,对应的扩展密钥是11个16字节块(初始密钥+10轮子密钥),原加密函数将初始密钥单独作为参数传入,子密钥数组包含10个块,解密时需要整合这11个密钥块进行逆扩展。
修正后的解密函数及配套逻辑
// 辅助函数:对16字节块执行InvMixColumns(用于逆密钥扩展) static void inv_mix_columns(uint8_t *block) { uint8x16_t vec = vld1q_u8(block); vec = vaesimcq_u8(vec); vst1q_u8(block, vec); } void aes_decrypt_arm(const uint8_t key[], const uint8_t subkeys[], uint32_t rounds, const uint8_t input[], uint8_t output[], uint32_t length) { // 整合所有加密密钥块:初始密钥 + 10轮子密钥(共11个块) uint8_t enc_keys[(rounds+1)*16]; memcpy(enc_keys, key, 16); memcpy(enc_keys+16, subkeys, rounds*16); // 生成解密用子密钥:反转加密密钥顺序,并对中间块执行InvMixColumns uint8_t dec_keys[(rounds+1)*16]; for (int i = 0; i <= rounds; ++i) { memcpy(dec_keys+i*16, enc_keys+(rounds - i)*16, 16); // 除第一个和最后一个块外,其余执行InvMixColumns if (i != 0 && i != rounds) { inv_mix_columns(dec_keys+i*16); } } while (length >= 16) { uint8x16_t block = vld1q_u8(input); // 初始AddRoundKey(对应加密最后一步的AddRoundKey) block = veorq_u8(block, vld1q_u8(dec_keys)); // 中间轮次:InvSubBytes+InvShiftRows + InvMixColumns for (unsigned int i = 1; i < rounds; ++i) { block = vaesdq_u8(block, vld1q_u8(dec_keys+i*16)); block = vaesimcq_u8(block); } // 最后一轮:仅InvSubBytes+InvShiftRows + AddRoundKey block = vaesdq_u8(block, vld1q_u8(dec_keys+rounds*16)); vst1q_u8(output, block); input += 16; output += 16; length -= 16; } }
修正说明
- 逆密钥扩展:生成解密子密钥时,先反转加密密钥的顺序,再对中间的10个密钥块(除首尾)执行
InvMixColumns,这是AES解密的标准要求。 - 解密流程对齐:解密流程严格对应加密的逆过程:
- 初始异或最后一个加密子密钥
- 中间9轮执行
vaesdq_u8(InvSubBytes+InvShiftRows) +vaesimcq_u8(InvMixColumns) - 最后一轮仅执行
vaesdq_u8,无需InvMixColumns,对应加密的最后一轮无MixColumns
- 移除冗余操作:删除了原解密函数中重复的
vaesdq_u8调用,修正了循环计数逻辑。
替换原解密函数后,运行代码即可得到正确的解密结果,匹配原输入数据。
内容的提问来源于stack exchange,提问作者Per Mertesacker
相关产品推荐
相关产品推荐

