4. 安全启动链验证

任务目标

在RTOS OTA固件更新场景中,启动过程必须防止恶意或损坏固件被执行。本案例实现一个最小化的安全启动链:Bootloader验证应用固件头部的签名与哈希,验证通过后才跳转执行;若验证失败则进入错误处理,不执行应用。

实现方案

采用两级验证模型:哈希校验确保固件完整性,RSA签名验证确保固件来源可信。Bootloader内置公钥,应用固件头部携带签名与固件哈希。启动时Bootloader依次完成:读取固件头→验证签名→计算并比对哈希→跳转。

关键完整代码


#include <stdint.h>
#include <string.h>
#include <stdbool.h>

/* ---- 最小SHA-256实现(仅用于演示,实际应使用成熟库) ---- */
#define SHA256_BLOCK_SIZE 64
typedef struct {
    uint8_t data[SHA256_BLOCK_SIZE];
    uint32_t datalen;
    uint64_t bitlen;
    uint32_t state[8];
} SHA256_CTX;

static const uint32_t K[64] = {
    0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,
    0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,
    0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,
    0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,
    0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,
    0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,
    0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,
    0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
};

#define ROTLEFT(a,b) (((a) << (b)) | ((a) >> (32-(b))))
#define ROTRIGHT(a,b) (((a) >> (b)) | ((a) << (32-(b))))

#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z)))
#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
#define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22))
#define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25))
#define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3))
#define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10))

static void sha256_transform(SHA256_CTX *ctx, const uint8_t data[]) {
    uint32_t m[64], w[64];
    for (int i = 0, j = 0; i < 16; ++i, j += 4)
        m[i] = (data[j] << 24) | (data[j+1] << 16) | (data[j+2] << 8) | data[j+3];
    for (int i = 16; i < 64; ++i)
        w[i] = SIG1(w[i-2]) + w[i-7] + SIG0(w[i-15]) + w[i-16];
    uint32_t a = ctx->state[0], b = ctx->state[1], c = ctx->state[2], d = ctx->state[3];
    uint32_t e = ctx->state[4], f = ctx->state[5], g = ctx->state[6], h = ctx->state[7];
    for (int i = 0; i < 64; ++i) {
        uint32_t t1 = h + EP1(e) + CH(e,f,g) + K[i] + w[i];
        uint32_t t2 = EP0(a) + MAJ(a,b,c);
        h = g; g = f; f = e; e = d + t1;
        d = c; c = b; b = a; a = t1 + t2;
    }
    ctx->state[0] += a; ctx->state[1] += b; ctx->state[2] += c; ctx->state[3] += d;
    ctx->state[4] += e; ctx->state[5] += f; ctx->state[6] += g; ctx->state[7] += h;
}

static void sha256_init(SHA256_CTX *ctx) {
    ctx->datalen = 0; ctx->bitlen = 0;
    ctx->state[0] = 0x6a09e667; ctx->state[1] = 0xbb67ae85;
    ctx->state[2] = 0x3c6ef372; ctx->state[3] = 0xa54ff53a;
    ctx->state[4] = 0x510e527f; ctx->state[5] = 0x9b05688c;
    ctx->state[6] = 0x1f83d9ab; ctx->state[7] = 0x5be0cd19;
}

static void sha256_update(SHA256_CTX *ctx, const uint8_t data[], size_t len) {
    for (size_t i = 0; i < len; ++i) {
        ctx->data[ctx->datalen++] = data[i];
        if (ctx->datalen == SHA256_BLOCK_SIZE) {
            sha256_transform(ctx, ctx->data);
            ctx->bitlen += 512;
            ctx->datalen = 0;
        }
    }
}

static void sha256_final(SHA256_CTX *ctx, uint8_t hash[]) {
    uint32_t i = ctx->datalen;
    if (ctx->datalen < 56) {
        ctx->data[i++] = 0x80;
        while (i < 56) ctx->data[i++] = 0x00;
    } else {
        ctx->data[i++] = 0x80;
        while (i < 64) ctx->data[i++] = 0x00;
        sha256_transform(ctx, ctx->data);
        memset(ctx->data, 0, 56);
    }
    ctx->bitlen += ctx->datalen * 8;
    ctx->data[63] = ctx->bitlen; ctx->data[62] = ctx->bitlen >> 8;
    ctx->data[61] = ctx->bitlen >> 16; ctx->data[60] = ctx->bitlen >> 24;
    ctx->data[59] = ctx->bitlen >> 32; ctx->data[58] = ctx->bitlen >> 40;
    ctx->data[57] = ctx->bitlen >> 48; ctx->data[56] = ctx->bitlen >> 56;
    sha256_transform(ctx, ctx->data);
    for (i = 0; i < 4; ++i) {
        hash[i] = (ctx->state[0] >> (24 - i*8)) & 0xff;
        hash[i+4] = (ctx->state[1] >> (24 - i*8)) & 0xff;
        hash[i+8] = (ctx->state[2] >> (24 - i*8)) & 0xff;
        hash[i+12] = (ctx->state[3] >> (24 - i*8)) & 0xff;
        hash[i+16] = (ctx->state[4] >> (24 - i*8)) & 0xff;
        hash[i+20] = (ctx->state[5] >> (24 - i*8)) & 0xff;
        hash[i+24] = (ctx->state[6] >> (24 - i*8)) & 0xff;
        hash[i+28] = (ctx->state[7] >> (24 - i*8)) & 0xff;
    }
}

/* ---- 固件头与安全启动验证 ---- */
#define FW_MAX_SIZE 4096
#define SIGNATURE_SIZE 256   /* RSA-2048 */
#define HASH_SIZE 32

typedef struct {
    uint32_t magic;          /* 0xA5A5A5A5 */
    uint32_t fw_size;        /* 固件实际字节数 */
    uint8_t  hash[HASH_SIZE];    /* 固件内容SHA-256 */
    uint8_t  signature[SIGNATURE_SIZE]; /* RSA签名(对hash签名) */
} fw_header_t;

/* Bootloader内置公钥(演示用,实际为RSA公钥N和e) */
static const uint8_t boot_pubkey[16] = {
    0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,
    0x99,0xaa,0xbb,0xcc,0xdd,0xee,0xff,0x00
};

/* 演示用RSA验证:实际应调用mbedTLS等库的rsa_verify() */
static bool rsa_verify(const uint8_t *sig, const uint8_t *hash, const uint8_t *pubkey) {
    /* 简化:比较签名前16字节与公钥异或结果,仅用于演示流程 */
    for (int i = 0; i < 16; i++) {
        if ((sig[i] ^ pubkey[i]) != hash[i % HASH_SIZE])
            return false;
    }
    return true;
}

/* 固件存储区(模拟Flash) */
static uint8_t fw_storage[FW_MAX_SIZE + sizeof(fw_header_t)];
static uint32_t fw_storage_size = 0;

/* 应用固件入口 */
typedef void (*app_entry_t)(void);
static void dummy_app(void) { /* 应用固件实际代码 */ }

/* 安全启动主流程 */
int secure_boot_verify(void) {
    if (fw_storage_size < sizeof(fw_header_t))
        return -1;  /* 无固件 */

    fw_header_t *hdr = (fw_header_t *)fw_storage;
    if (hdr->magic != 0xA5A5A5A5)
        return -2;  /* 魔数错误 */

    if (hdr->fw_size > FW_MAX_SIZE)
        return -3;  /* 大小非法 */

    /* 1. 验证签名(先验证hash的签名) */
    if (!rsa_verify(hdr->signature, hdr->hash, boot_pubkey))
        return -4;  /* 签名无效 */

    /* 2. 计算固件实际哈希并比对 */
    uint8_t calc_hash[HASH_SIZE];
    SHA256_CTX ctx;
    sha256_init(&ctx);
    sha256_update(&ctx, fw_storage + sizeof(fw_header_t), hdr->fw_size);
    sha256_final(&ctx, calc_hash);

    if (memcmp(calc_hash, hdr->hash, HASH_SIZE) != 0)
        return -5;  /* 哈希不匹配 */

    /* 3. 验证通过,跳转到应用入口 */
    app_entry_t app = (app_entry_t)(fw_storage + sizeof(fw_header_t));
    app();
    return 0;  /* 正常不会返回 */
}

/* ---- 测试辅助:模拟固件烧录与验证 ---- */
void test_secure_boot(void) {
    /* 构造测试固件 */
    fw_header_t *hdr = (fw_header_t *)fw_storage;
    uint8_t *fw_data = fw_storage + sizeof(fw_header_t);
    const char *test_fw = "RTOS_OTA_FIRMWARE_V1.0";
    size_t fw_len = strlen(test_fw) + 1;

    hdr->magic = 0xA5A5A5A5;
    hdr->fw_size = fw_len;
    memcpy(fw_data, test_fw, fw_len);

    /* 计算哈希 */
    SHA256_CTX ctx;
    sha256_init(&ctx);
    sha256_update(&ctx, fw_data, fw_len);
    sha256_final(&ctx, hdr->hash);

    /* 生成签名(演示:用公钥异或hash) */
    for (int i = 0; i < SIGNATURE_SIZE; i++)
        hdr->signature[i] = boot_pubkey[i % 16] ^ hdr->hash[i % HASH_SIZE];

    fw_storage_size = sizeof(fw_header_t) + fw_len;

    /* 执行安全启动验证 */
    int ret = secure_boot_verify();
    if (ret == 0)
        /* 验证通过,已跳转执行 */
        ;
    else
        /* 验证失败,错误码ret */ 
        ;
}

运行与验证

编译:本代码无外部依赖,使用任意C编译器(如gcc)直接编译:


gcc -o secure_boot secure_boot.c

执行:运行编译产物,调用test_secure_boot()即可触发完整验证流程。

验证方法:

  • 正常路径:在test_secure_boot()中构造合法固件,调用secure_boot_verify()应返回0,并跳转执行dummy_app()。
  • 篡改检测:修改fw_data[0](模拟固件被篡改),重新调用验证函数,应返回-5(哈希不匹配)。
  • 签名验证:修改hdr->signature[0](模拟非法签名),应返回-4(签名无效)。

通过条件:上述三种场景返回值与预期一致,即证明安全启动链正确实现了逐级验证:先验签、再验哈希,全部通过才执行固件。

关键说明

  • 本案例的RSA验证为演示逻辑,实际产品应使用mbedTLS或wolfSSL的rsa_verify()接口,并确保公钥存储在Bootloader的OTP区域或写保护Flash中。
  • SHA-256实现为教学简化版,生产环境建议直接使用经过验证的加密库,避免自行实现。
  • 实际嵌入式系统中,Bootloader与App通常位于不同Flash分区,通过链接脚本或地址映射跳转;本案例用内存数组模拟该分区布局。