ARM64嵌入式安全实战:从密钥生成到U-Boot验签的全链路实现

在智能网联设备和工业控制领域,嵌入式系统的启动安全已成为防御供应链攻击的第一道防线。当一块基于NXP S32G274A的开发板从QSPI闪存启动时,如何确保加载的Linux内核未被篡改?本文将揭示如何通过FIT镜像签名机制,构建从开发环境到目标硬件的完整信任链。

1. 密码学基础与密钥管理

1.1 密钥生成的最佳实践

在~/secure_boot目录下执行以下命令生成符合PKCS#1标准的RSA密钥对:

openssl genpkey -algorithm RSA -out secure.key \
  -pkeyopt rsa_keygen_bits:4096 \
  -pkeyopt rsa_keygen_pubexp:65537
openssl req -x509 -new -nodes -key secure.key -sha256 -days 1825 \
  -out secure.crt -subj "/CN=SecureBoot/O=IoTDevice"

关键参数说明:

  • rsa_keygen_bits: 2048位密钥已逐渐被淘汰,推荐使用4096位
  • days: 证书有效期建议设置为3-5年(1095-1825天)
  • sha256: 必须使用SHA-2系列算法,避免已破解的SHA1

生产环境必须将私钥存储在HSM或安全芯片中,禁止明文存储在开发主机

1.2 密钥部署策略

典型的安全密钥存储方案对比:

存储方式安全性等级成本开发便利性适用场景
文件系统存储★☆☆☆☆低高原型开发阶段
TPM/HSM模块★★★★☆高中工业级设备
安全启动芯片★★★★★较高低车规级/金融设备
eFuse一次性烧录★★★★★中低量产固件签名

2. FIT镜像描述文件深度解析

2.1 ITS文件结构剖析

创建kernel.its文件时需特别注意地址对齐和版本控制:

/dts-v1/;
/ {
    description = "Secure Kernel Image";
    #address-cells = <1>;

    images {
        kernel@1 {
            data = /incbin/("./Image");
            type = "kernel";
            arch = "arm64";
            os = "linux";
            compression = "none";
            load = <0x80080000>;
            entry = <0x80080000>;
            hash@1 {
                algo = "sha512";
            };
        };
        fdt@1 {
            data = /incbin/("./s32g274a.dtb");
            type = "flat_dt";
            arch = "arm64";
            compression = "none";
            hash@1 {
                algo = "sha512";
            };
        };
    };

    configurations {
        default = "conf@1";
        conf@1 {
            kernel = "kernel@1";
            fdt = "fdt@1";
            signature@1 {
                algo = "sha512,rsa4096";
                key-name-hint = "secure";
            };
        };
    };
};

常见配置陷阱:

  1. load/entry地址:必须与内核链接脚本中的CONFIG_TEXT_BASE一致
  2. hash算法:必须与U-Boot配置的CONFIG_FIT_SIGNATURE_HASH匹配
  3. 版本控制:建议使用version字段实现OTA回滚保护

2.2 设备树签名扩展

在s32g274a-uboot.dts中添加验签节点:

signature {
    key-secure {
        required = "conf";
        algo = "sha512,rsa4096";
        rsa,r-squared = <0x...>;
        rsa,modulus = <0x...>;
        rsa,exponent = <0x...>;
        rsa,n0-inverse = <0x...>;
    };
};

通过dtc -I dts -O dtb -o secure.dtb s32g274a-uboot.dts编译时,确保开启-@保留符号引用

3. U-Boot配置与编译陷阱

3.1 关键配置选项

执行make menuconfig时必须验证以下配置:

Boot options  --->
    [*] Enable FIT image support
    [*] Enable signature verification of FIT images
    [*] Enable RSA4096 algorithm support
    [*] Require verified images for booting

Security options  --->
    [*] Enable chain of trust
    [*] Allow unsigned images (DEBUG ONLY)

常见编译错误解决方案:

  1. CONFIG_OF_SEPARATE冲突:当使用独立DTB时需禁用CONFIG_OF_EMBED
  2. RSA库缺失:需在lib/rsa/目录添加rsa-mod-exp驱动
  3. 堆栈溢出:增大CONFIG_SYS_MALLOC_LEN至(16MB+)

3.2 交叉编译技巧

针对ARM64的优化编译命令:

export CROSS_COMPILE=aarch64-linux-gnu-
make BL31=bl31.bin SCP=scp.bin \
    EXT_DTB=secure.dtb \
    BOOT_DEVICE=qspi \
    BUILD_BASE=../build \
    -j$(nproc)

关键产物验证:

aarch64-linux-gnu-objdump -D u-boot | grep "fit_image_verify"
strings u-boot | grep "Verifying Hash Integrity"

4. 验签流程实战验证

4.1 镜像打包与签名

使用mkimage的进阶参数:

mkimage -f kernel.its -K secure.dtb \
    -k ~/secure_keys -c "Revoke-2024" \
    -r -E -N sha512,rsa4096 \
    signed_kernel.fit

参数说明:

  • -c:添加版权声明到签名头
  • -E:强制大端格式存储签名
  • -N:覆盖ITS文件中的默认算法

4.2 目标板验证流程

在U-Boot命令行中触发验签:

=> setenv verify 1
=> setenv bootargs console=ttyLF0,115200 earlycon
=> load mmc 0:1 0x90000000 signed_kernel.fit
=> bootm 0x90000000

验签失败时的调试技巧:

  1. 错误码解析:

    • -EBADMSG:签名不匹配
    • -EKEYREJECTED:证书过期
    • -ENOKEY:公钥未注册
  2. 调试信息开启:

    => setenv verify_debug 1
    => setenv trace on
    
  3. 内存诊断:

    => md 0x90000000 0x100
    => crc32 0x90000000 0x100000
    

5. 生产环境增强方案

5.1 安全启动链扩展

实现二级引导验证的DTB配置示例:

/ {
    secure-boot {
        compatible = "nxp,secure-boot";
        trusted-key {
            nxp,primary-key = <&key_secure>;
            nxp,backup-key = <&key_recovery>;
        };
        stages {
            stage1 {
                load = <0x80000000>;
                verify = <&verify_uboot>;
            };
            stage2 {
                load = <0x81000000>;
                verify = <&verify_kernel>;
            };
        };
    };
};

5.2 抗物理攻击措施

  1. 时钟毛刺检测:在drivers/crypto/fsl_sec.c中添加:

    if (readl(SCU_BASE + SCU_CLK_STAT) & CLK_GLITCH) {
        panic("Clock glitch detected!");
    }
    
  2. 关键函数保护:修改链接脚本保护验签函数:

    .secure_text : {
        KEEP(*(.fit_verify))
        KEEP(*(.rsa_verify))
    } > secure_ram
    
  3. 运行时完整性检查:

    => crc32 0x80080000 0x100000
    => if test "$?" != "0"; then reset; fi
    

在完成基础验签功能后,建议在U-Boot中实现安全计数器(secure counter)来防御重放攻击。通过efuse write命令将成功启动次数写入OTP区域,每次启动时校验计数器值是否符合预期增长模式。

更多推荐