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 加密流程 明文块 P₁ IV ⊕ P₁ AES-256加密 密文块 C₁ C₁ 作为下一分组加密输入 AES-256 CBC 解密流程 密文块 C₁ AES-256解密 ⊕ IV 明文块 P₁

代码实现

以下代码实现完整的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型号匹配。