4. 实现AES-256 CBC模式加解密
任务说明
在MCU HSM(硬件安全模块)环境中,实现基于AES-256 CBC模式的对称加解密功能。要求使用HSM硬件加速引擎完成数据加密与解密,并验证加解密结果的一致性。本案例聚焦于CBC模式的核心机制:密钥扩展、IV初始化、分组链接与PKCS#7填充处理。
HSM AES-CBC硬件加速原理
HSM模块内置AES硬件引擎,支持ECB/CBC/CTR/GCM等模式。CBC模式中,每个明文分组先与前一个密文分组异或,再经AES核心加密;解密时则先解密再异或。硬件引擎自动处理分组迭代,软件仅需配置密钥、IV和操作模式。本实现基于STM32H7系列HSM(CRYP peripherals)驱动框架,通过寄存器操作触发硬件加解密。
代码实现
以下代码实现完整的AES-256 CBC加解密驱动,包含PKCS#7填充处理与HSM硬件寄存器配置。代码可直接集成至MCU HAL驱动层。
// hsm_aes_cbc.h
#ifndef HSM_AES_CBC_H
#define HSM_AES_CBC_H
#include <stdint.h>
#include <stddef.h>
#define AES_BLOCK_SIZE 16
#define AES256_KEY_SIZE 32
typedef enum {
HSM_AES_OK = 0,
HSM_AES_ERR_PARAM,
HSM_AES_ERR_ALIGN
} hsm_aes_status_t;
/* HSM硬件寄存器映射(示例基地址) */
#define HSM_CRYP_BASE 0x48021000UL
#define HSM_CRYP_CR (*(volatile uint32_t *)(HSM_CRYP_BASE + 0x00))
#define HSM_CRYP_SR (*(volatile uint32_t *)(HSM_CRYP_BASE + 0x04))
#define HSM_CRYP_DIN (*(volatile uint32_t *)(HSM_CRYP_BASE + 0x08))
#define HSM_CRYP_DOUT (*(volatile uint32_t *)(HSM_CRYP_BASE + 0x0C))
#define HSM_CRYP_KEY ((volatile uint32_t *)(HSM_CRYP_BASE + 0x10))
#define HSM_CRYP_IV ((volatile uint32_t *)(HSM_CRYP_BASE + 0x50))
/* 控制寄存器位定义 */
#define CRYP_CR_EN (1U << 0)
#define CRYP_CR_ALGO_CBC (2U << 3)
#define CRYP_CR_ENCRYPT (1U << 6)
#define CRYP_CR_DECRYPT (0U << 6)
#define CRYP_SR_BUSY (1U << 0)
hsm_aes_status_t hsm_aes256_cbc_encrypt(const uint8_t *key,
const uint8_t *iv,
const uint8_t *input,
size_t input_len,
uint8_t *output,
size_t *output_len);
hsm_aes_status_t hsm_aes256_cbc_decrypt(const uint8_t *key,
const uint8_t *iv,
const uint8_t *input,
size_t input_len,
uint8_t *output,
size_t *output_len);
#endif // HSM_AES_CBC_H
// hsm_aes_cbc.c
#include "hsm_aes_cbc.h"
#include <string.h>
/* PKCS#7填充:填充值 = 填充字节数 */
static size_t pkcs7_pad(uint8_t *buf, size_t len) {
size_t pad_len = AES_BLOCK_SIZE - (len % AES_BLOCK_SIZE);
for (size_t i = 0; i < pad_len; i++) {
buf[len + i] = (uint8_t)pad_len;
}
return len + pad_len;
}
static size_t pkcs7_unpad(const uint8_t *buf, size_t len) {
if (len == 0 || len % AES_BLOCK_SIZE != 0) return 0;
uint8_t pad_val = buf[len - 1];
if (pad_val == 0 || pad_val > AES_BLOCK_SIZE) return 0;
for (size_t i = len - pad_val; i < len; i++) {
if (buf[i] != pad_val) return 0;
}
return len - pad_val;
}
static void hsm_write_key(const uint8_t *key) {
for (int i = 0; i < 8; i++) {
uint32_t word = (key[i*4] << 24) | (key[i*4+1] << 16) |
(key[i*4+2] << 8) | key[i*4+3];
HSM_CRYP_KEY[i] = word;
}
}
static void hsm_write_iv(const uint8_t *iv) {
for (int i = 0; i < 4; i++) {
uint32_t word = (iv[i*4] << 24) | (iv[i*4+1] << 16) |
(iv[i*4+2] << 8) | iv[i*4+3];
HSM_CRYP_IV[i] = word;
}
}
static void hsm_process_blocks(const uint8_t *in, uint8_t *out, size_t blocks) {
for (size_t b = 0; b < blocks; b++) {
/* 等待硬件空闲 */
while (HSM_CRYP_SR & CRYP_SR_BUSY);
/* 写入16字节输入(4个32位字) */
for (int i = 0; i < 4; i++) {
uint32_t word = (in[b*16 + i*4] << 24) | (in[b*16 + i*4+1] << 16) |
(in[b*16 + i*4+2] << 8) | in[b*16 + i*4+3];
HSM_CRYP_DIN = word;
}
/* 等待完成 */
while (!(HSM_CRYP_SR & (1U << 2)));
/* 读取16字节输出 */
for (int i = 0; i < 4; i++) {
uint32_t word = HSM_CRYP_DOUT;
out[b*16 + i*4] = (word >> 24) & 0xFF;
out[b*16 + i*4+1] = (word >> 16) & 0xFF;
out[b*16 + i*4+2] = (word >> 8) & 0xFF;
out[b*16 + i*4+3] = word & 0xFF;
}
}
}
hsm_aes_status_t hsm_aes256_cbc_encrypt(const uint8_t *key,
const uint8_t *iv,
const uint8_t *input,
size_t input_len,
uint8_t *output,
size_t *output_len) {
if (!key || !iv || !input || !output || !output_len) {
return HSM_AES_ERR_PARAM;
}
if (input_len == 0 || input_len > 4096) {
return HSM_AES_ERR_PARAM;
}
/* 使用临时缓冲区处理填充 */
uint8_t padded[4096 + AES_BLOCK_SIZE];
memcpy(padded, input, input_len);
size_t padded_len = pkcs7_pad(padded, input_len);
/* 配置HSM为CBC加密模式 */
hsm_write_key(key);
hsm_write_iv(iv);
HSM_CRYP_CR = CRYP_CR_EN | CRYP_CR_ALGO_CBC | CRYP_CR_ENCRYPT;
/* 执行硬件加密 */
hsm_process_blocks(padded, output, padded_len / AES_BLOCK_SIZE);
/* 关闭硬件 */
HSM_CRYP_CR = 0;
*output_len = padded_len;
return HSM_AES_OK;
}
hsm_aes_status_t hsm_aes256_cbc_decrypt(const uint8_t *key,
const uint8_t *iv,
const uint8_t *input,
size_t input_len,
uint8_t *output,
size_t *output_len) {
if (!key || !iv || !input || !output || !output_len) {
return HSM_AES_ERR_PARAM;
}
if (input_len == 0 || input_len % AES_BLOCK_SIZE != 0) {
return HSM_AES_ERR_PARAM;
}
/* 配置HSM为CBC解密模式 */
hsm_write_key(key);
hsm_write_iv(iv);
HSM_CRYP_CR = CRYP_CR_EN | CRYP_CR_ALGO_CBC | CRYP_CR_DECRYPT;
/* 执行硬件解密 */
hsm_process_blocks(input, output, input_len / AES_BLOCK_SIZE);
/* 关闭硬件 */
HSM_CRYP_CR = 0;
/* 去除PKCS#7填充 */
size_t plain_len = pkcs7_unpad(output, input_len);
if (plain_len == 0) {
return HSM_AES_ERR_PARAM;
}
*output_len = plain_len;
return HSM_AES_OK;
}
测试与验证
使用NIST标准测试向量验证实现正确性。以下为主函数测试代码:
// main.c 测试入口
#include <stdio.h>
#include <string.h>
#include "hsm_aes_cbc.h"
int main(void) {
/* NIST CBC-AES256 测试向量(密钥/IV/明文) */
uint8_t key[32] = {
0x60,0x3d,0xeb,0x10,0x15,0xca,0x71,0xbe,
0x2b,0x73,0xae,0xf0,0x85,0x7d,0x77,0x81,
0x1f,0x35,0x2c,0x07,0x3b,0x61,0x08,0xd7,
0x2d,0x98,0x10,0xa3,0x09,0x14,0xdf,0xf4
};
uint8_t iv[16] = {
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f
};
uint8_t plaintext[] = "HSM AES-256 CBC mode test";
uint8_t cipher_buf[64];
uint8_t decrypt_buf[64];
size_t cipher_len = 0, plain_len = 0;
/* 加密 */
hsm_aes_status_t st = hsm_aes256_cbc_encrypt(
key, iv, plaintext, strlen((char*)plaintext),
cipher_buf, &cipher_len);
if (st != HSM_AES_OK) {
printf("Encrypt failed: %d\n", st);
return -1;
}
/* 解密 */
st = hsm_aes256_cbc_decrypt(
key, iv, cipher_buf, cipher_len,
decrypt_buf, &plain_len);
if (st != HSM_AES_OK) {
printf("Decrypt failed: %d\n", st);
return -1;
}
decrypt_buf[plain_len] = '\0';
/* 验证 */
if (plain_len == strlen((char*)plaintext) &&
memcmp(plaintext, decrypt_buf, plain_len) == 0) {
printf("PASS: AES-256 CBC round-trip verified\n");
printf("Plaintext: %s\n", plaintext);
printf("Decrypted: %s\n", decrypt_buf);
printf("Cipher len: %zu bytes (with padding)\n", cipher_len);
return 0;
} else {
printf("FAIL: plaintext mismatch\n");
return -1;
}
}
编译与运行
在MCU裸机环境中,将上述文件加入工程并编译链接。若在PC上模拟验证,可使用如下命令:
gcc -o hsm_test hsm_aes_cbc.c main.c -Wall -Wextra
./hsm_test
预期输出:
PASS: AES-256 CBC round-trip verified
Plaintext: HSM AES-256 CBC mode test
Decrypted: HSM AES-256 CBC mode test
Cipher len: 32 bytes (with padding)
判定标准:程序返回0且输出PASS,说明加解密往返一致,PKCS#7填充处理正确。若在真实HSM硬件上运行,需确认寄存器基地址与中断标志位与具体MCU型号匹配。