3.3 ECDSA示例
3.3.1 准备
ECDSA 签名为一种非对称加密的电子签名方法,私钥存储到签名设备ICWKEY 中,公钥存储在项目固件中,ICWKEY 通过对目标芯片ID 以及当前的私钥,生成签名信息,然后通过PowerWriter 将签名信息写入到固件指定地址,固件运行时,通过公钥 + ID去验证当前签名信息是否有效,从而判定当前芯片是否已经得到有效的授权,避免固件被直接拷贝使用,在开始之前,我们需要按照流程核查所有的准备工作都已经完成。
- PowerWriter 项目中使用ICWKEY 签名(或者 ICWKEY 签名锁定模式)。
- 已经设置签名地址(如0x08002000)。
- PowerWriter 端的通讯信息已经同步到ICWKEY 的项目中,并与项目重新建立加密通讯。
- 合理设置可授权次数,比如设置为10000。
- 签名方法:选用ECDSA 签名,保存到ICWKEY,并导出了源码。
如以上步骤均完成,则可以看到ICWKEY 设备的显示信息,以及导出的源码信息,参考如下:
同时ICWKEY 的设备将显示如下的信息:
3.3.2 示例工程
3.3.2.1 准备
示例工程路径ICWKEY 的安装路径下,具体为:
C:\Users\用户名\AppData\Local\ICWKEY\Examples_for_mdk
可通过ICWKEY 桌面图标,快速定位到为止,并拷贝ECDSA 示例工程到指定路径,并解压,参考演示如下:
3.3.2.2 代码结构
3.3.2.2.1 startup_stm32f103xg.s
- 调整堆大小 > 0x1300
- 挑战栈大小 > 0x800
Stack_Size EQU 0x1000 ;请将堆栈调大一些,ECDSA 签名校验需要较大的栈空间
AREA STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem SPACE Stack_Size
__initial_sp
; <h> Heap Configuration
; <o> Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>
Heap_Size EQU 0x2000 ;调整堆大小 > 0x1300,用于动态内存分配
AREA HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem SPACE Heap_Size
__heap_limit
PRESERVE8
THUMB
请特别注意堆栈大小,要调大,否则将无法执行签名校验,返回内存不足的错误信息。
3.3.2.2.2 cortex_chipid_binding.c
- 更换公钥为ICWKEY 导出的公钥
//使用ICWKEY 中的 公钥进行替换
const static uint8_t PUBLIC_KEY[49]={
0x04,0x00,0x7F,0xFE,0xF3,0x5A,0xFB,0x48,0xC3,0xEB,0xE8,0xE5,0x41,0xDE,0xAF,0x99,
0x89,0x48,0x8C,0x31,0x93,0x2A,0x91,0x81,0xD1,0x17,0x62,0xA5,0x89,0xA6,0x77,0x02,
0x14,0x60,0xC7,0x79,0x1E,0x33,0xDF,0x8F,0xE0,0xF0,0xC2,0x47,0x03,0x49,0x7B,0x5F,
0xF7
};
3.3.2.2.3 cortex_chipid_binding.h
- 填写ID 地址(请看提示信息)
- 修改签名地址为PowerWriter 中签名地址
- 根据实际情况,是否开启占位符。
/* Exported define --------------------------------------------------------*/
/* The following parameter definitions must be consistent with the actual chip and burner settings */
#define UID_CHIP_MASK 0xEF2A7BF1 //Random generation
#define UID_CHIP_SIZE 12 //ChipID Size //ChipID Size
/* 目标芯片的ID地址,可根据芯片手册查询 */
#define UID_CHIP_ADDR (0x1FFFF7E8^UID_CHIP_MASK) //ChipID Inner Addr in chip
//签名信息存储地址,改为PowerWriter项目中签名信息的存储地址 0x08002000
#define LICENCE_ADDR (0x08002000^UID_CHIP_MASK) //Licence Store Addr In flash
/*
说明:
此定制用于决定是否在固件中对签名信息进行占位(预分配),防止和其他函数地址重叠,PowerWriter 烧写的时候可能会意外擦除代码
而导致固件出问题,此时,需要预先分配,如此标志位为0,则表示不进行占位,这种情况下,需要将签名信息的存储地址,存储在固件之外。
总结:
1:签名地址在实际固件范围之内,必须开启占位符;签名地址在实际固件范围之外,建议不开占位符,避免固件过大。
2:如果地址超过 0.7 芯片容量,则不开启占位符,如果地址小于 0.7 * 芯片容量则开启占位符
*/
#define UID_LICENCEADDR_PLACEHOLDER_EN 1 //Licnece Store Addr In Flash Enable/Disable Placeholder
UID_CHIP_ADDR 的地址,可以用过PowerWriter 选择签名模式为 Matrix ,导出源码中可以看到实际的ID地址。
3.3.2.2.4 main.c
- 初始化ID
- 验证签名
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
//用于串口打印日志信息
int fputc(int ch, FILE *f)
{
uint8_t ch8 = (uint8_t)ch;
HAL_UART_Transmit(&huart2,&ch8,sizeof(ch8),5);
return (ch);
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_TIM1_Init();
MX_USART2_UART_Init(); //串口初始化,用于打印日志
/* USER CODE BEGIN 2 */
//Initial Chip
ChipUIDInitial(); //签名初始化,用于初始化chipid,ID 不连续的芯片,请连续拷贝,请查看代码
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
//Check in your code
if(ChipUIDAlgo_Check()) //验证签名
{
//ok:LED 闪烁
HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
HAL_GPIO_TogglePin(LED2_GPIO_Port, LED2_Pin);
HAL_Delay(100);
}
else
{
//false: LED 常亮或者常灭
HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_RESET);
}
}
/* USER CODE END 3 */
}
示例代码只是演示,为了更加安全,请注意隐藏代码,可提高安全性,有必要时,联系我们获取 MCU 通用安全保护库,来进一步提升固件安全,防止固件被逆向反编译、破解、和修改。
3.3.2.3 编译
#define SISSDK_LOG_ENABLE //disbale /Enable #warning You have to implement fput functions to use log print function
验证时可开启日志,方便查看结果,编译项目,将在目录 Output\ TargetIC_Example.bin 生成测试固件。
3.3.2.4 验证
重新打开PowerWriter 项目,在Program Memory 页面添加 TargetIC_Example.bin 测试固件,并将项目加载到PowerWriter设备,如下所示:
连接ICWKEY 到PowerWriter,并连接需要编程的MCU 目标PCB板, 并连接电源进行编程,参考接线如下所示:
编程结束后,连接目标PCB 的串口TX 引脚,即可看到输出的签名验证信息,参考如下:
3.3.2.5 调试方法
使用PowerWriter 对目标固件进行签名并编程到目标芯片之后,可通过设定的状态输出,来检查签名是否生效,在复杂的场景下,单纯看工作状态无法判定出现问题的位置,此时,需要对目标芯片进行调试,调试步骤如下:
- 参考编译验证流程,完成编程
- IDE选择:不擦除目标芯片,不编程目标芯片,不校验目标芯片的方式进行。
参考演示如下所示: