一.CW32F030C8T6 数字签名相关寄存器
1.1 概述
数字签名主要用来存放芯片唯一身份标识(UID)、产品型号、FLASH 容量、SRAM 容量、芯片封装引脚数等信息, 可以通过 SWD 或者 CPU 读取。数字签名相关信息在出厂时编程,用户固件或外部设备可通过读取数字签名来对 芯片的合法性进行验证。
1.2 产品唯一身份标识(UID)
寄存器(80bit) UID寄存器存储了芯片的唯一身份标识符,其地址为 0x0001 2660 - 0x0001 2669,共 80bit。UID在芯片生产时写入, 用户无法修改。UID 寄存器支持以单字节 / 半字 / 全字等方式读取,然后使用自定义算法连接起来。 唯一身份标识符典型应用场景:
用作设备序列号
设备合法性验证 , 防止盗版 用户在设备生产时采用私有密钥对 UID 进行加密运算,并将计算结果存放在主 FLASH 存储器或 OTP 存储器, 程序在设备启动后,读取 UID 并采用同样的密钥进行加密运算,并将运算结果和之前存储的计算结果进行比 较,相同则认为该设备是合法的,否则程序不启动,可有效防止用户设备被非法复制(盗版)。
作为安全密钥使用 用户结合 UID 和私有算法,可在用户对 FLASH 编程前进行安全校验,提高 FLASH 内代码的安全性。
激活安全启动流程等
1.3 产品型号寄存器
产品型号寄存器存储了产品型号的 ASCII 码,其地址为 0x0001 2610 - 0x0001 2625,共 22 字节。产品型号不足 22 字节时,用 0x00 进行填充。 如芯片的型号为 CW32x030C8T7-LQFP48,对应存储 ( 从低地址开始 ) 的数据为:0x43
1.4 FLASH 容量寄存器
FLASH 容量寄存器存储了芯片内置 FLASH 存储器的容量大小,其地址为 0x0001 2628 - 0x0001 262B,共 4 字节。 从 FLASH 容量寄存器读出的 FLASH 容量大小以字节为单位,如 0x0001 0000 代表 64KB,0x0000 8000 代表 32KB。
1.5 SRAM 容量寄存器
SRAM 容量寄存器存储了芯片内置 SRAM 存储器的容量大小,其地址为 0x0001 262C - 0x0001 262F,共 4 字节。 从 SRAM 容量寄存器读出的 SRAM 容量大小以字节为单位,如 0x0001 0000 代表 64KB,0x0000 4000 代表 16KB。
1.6 引脚数量寄存器
引脚数量寄存器存储了芯片引脚数,其地址为 0x0001 2626 - 0x0001 2627,共 2 字节。如 0x0020 代表 32Pin, 0x0030 代表 48Pin。
二.数字签名实现方法
2.1 使用加密库
CW32F030C8T6 可通过第三方加密库(如mbedTLS、WolfSSL)实现数字签名。常见算法包括RSA、ECDSA。
2.2 基于SHA和RSA的签名流程
生成哈希值:使用SHA-256对固件计算哈希值。
私钥签名:用开发者私钥对哈希值进行RSA加密,生成签名。
公钥验证:在设备端用预置的公钥解密签名,与重新计算的哈希值比对。
2.3 代码示例 (基于mbedTLS)
使用 CW32F030C8T6 实现 ECDSA 数字签名
2.3.1硬件准备
确保 CW32F030C8T6 开发环境已配置完毕,包括必要的开发工具链(如 Keil MDK 或 IAR Embedded Workbench)和调试工具(如 J-Link 或 ST-Link)。
环境配置
https://blog.csdn.net/weixin_43260261/article/details/148895475?spm=1011.2415.3001.5331
2.3.2软件库选择
使用支持 ECDSA 的加密库,如 Mbed TLS、wolfSSL 或 TinyCrypt。这些库提供 ECDSA 的实现,可以直接集成到项目中
2.3.3密钥生成
ECDSA 需要一对密钥(私钥和公钥)。使用加密库生成 ECC 密钥对。以下(基于 Mbed TLS):
#include "mbedtls/ecdsa.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
mbedtls_ecdsa_context ctx;
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
mbedtls_ecdsa_init(&ctx);
mbedtls_entropy_init(&entropy);
mbedtls_ctr_drbg_init(&ctr_drbg);
// 初始化随机数生成器
mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0);
// 生成 ECC 密钥对(使用 secp256r1 曲线)
mbedtls_ecdsa_genkey(&ctx, MBEDTLS_ECP_DP_SECP256R1, mbedtls_ctr_drbg_random, &ctr_drbg);
2.3.4签名生成
使用私钥对消息进行签名。
unsigned char hash[32]; // 假设消息的哈希值为 32 字节
unsigned char sig[64]; // ECDSA 签名输出
// 计算消息的哈希(此处省略哈希计算步骤)
// ...
// 生成签名
mbedtls_ecdsa_write_signature(&ctx, MBEDTLS_MD_SHA256, hash, sizeof(hash), sig, sizeof(sig), mbedtls_ctr_drbg_random, &ctr_drbg);
2.3.5签名验证
使用公钥验证签名。
int ret = mbedtls_ecdsa_read_signature(&ctx, hash, sizeof(hash), sig, sizeof(sig));
if (ret == 0) {
// 签名验证成功
} else {
// 签名验证失败
}
2.3.6资源清理
完成操作后,释放资源:
mbedtls_ecdsa_free(&ctx);
mbedtls_entropy_free(&entropy);
mbedtls_ctr_drbg_free(&ctr_drbg);
2.3.7优化建议
CW32F030C8T6 的资源有限,建议使用较小的曲线(如 secp192r1)以减少计算负载。如果性能要求较高,可以考虑硬件加速或优化库的实现。
2.4 验证签名的步骤
烧录公钥到CW32F030C8T6的安全存储区(如OTP或Flash保护区)。
设备启动时重新计算固件哈希值。
用公钥解密签名,匹配哈希值。
2.5 注意事项
私钥需离线存储,避免泄露。
推荐使用硬件安全元件(如SE)或TrustZone增强保护。
定期更新密钥对以应对潜在**风险。
确保芯片的硬件标识符(如UID)与官方文档一致,避免使用非原装或篡改过的芯片。CW32F030C8T6的UID通常位于特定内存地址,需通过读取命令验证。
推荐使用ECDSA或RSA算法,密钥长度需符合安全标准(如RSA-2048或ECC-256)。算法实现需通过权威认证(如FIPS 140-2),避免使用自定义或未经验证的加密库。
私钥必须存储在安全环境中(如HSM或安全芯片),禁止硬编码在代码中。公钥可嵌入固件,但需校验其完整性和来源。定期轮换密钥以降低泄露风险。
固件签名应在隔离的构建环境中完成,确保编译与签名环节无缝衔接。签名后需生成哈希值(如SHA-256)并与签名一起存储,供后续验证使用。
在Bootloader中集成签名验证逻辑,确保只有合法签名的固件可被加载。验证失败时立即触发安全异常(如系统复位或警报),并记录错误日志。
量产阶段禁用JTAG/SWD等调试接口,防止未签名代码通过调试端口注入。可通过芯片的选项字节(Option Bytes)配置实现。
参考CW32官方提供的安全手册和ANSI/ISO相关标准,确保签名流程符合行业规范。保留完整的签名记录和审计日志以备审查。
三.案例-读取数字签名相关内容
uint8_t Chip_Type[24]; // 存储芯片型号的数组,长度为24字节
uint16_t Pin_Count; // 存储芯片引脚数量的变量
uint32_t Flash_Size; // 存储芯片Flash大小的变量
uint32_t Ram_Size; // 存储芯片RAM大小的变量
uint8_t Chip_Uid[10]; // 存储芯片唯一标识符的数组,长度为10字节
/**
******************************************************************************
** \brief 项目主函数
**
** \return uint32_t 返回值,如有需要
**
** 该示例用于切换GPIOA.00
**
******************************************************************************/
int32_t main(void)
{
// 初始化GPIOB时钟
RCC_AHBPeriphClk_Enable(RCC_AHB_PERIPH_GPIOB, ENABLE); // 开启GPIOB时钟
// 配置GPIOB引脚6
CW_GPIOB->ANALOG_f.PIN6 = 0U; // 设置PB06为数字模式
CW_GPIOB->BRR_f.BRR6 = 1U; // 复位PB06
CW_GPIOB->DIR_f.PIN6 = 0U; // 设置PB06为输出模式
// 获取芯片信息
DIGITALSIGN_GetChipType(Chip_Type); // 获取芯片型号
Pin_Count = DIGITALSIGN_GetPinCount(); // 获取芯片引脚数量
Flash_Size = DIGITALSIGN_GetFlashSize(); // 获取芯片Flash大小
Ram_Size = DIGITALSIGN_GetRamSize(); // 获取芯片RAM大小
DIGITALSIGN_GetChipUid(Chip_Uid); // 获取芯片唯一标识符
// 主循环
while (1)
{
CW_GPIOB->TOG = bv8; // 切换GPIOB引脚8状态
CW_GPIOB->TOG = bv6; // 切换GPIOB引脚6状态
FirmwareDelay(100000); // 延时
}
}
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/weixin_43260261/article/details/149168909
|