跳到主要内容
版本:Next

3.3 ECDSA示例

3.3.1 准备

ECDSA 签名为一种非对称加密的电子签名方法,私钥存储到签名设备ICWKEY 中,公钥存储在项目固件中,ICWKEY 通过对目标芯片ID 以及当前的私钥,生成签名信息,然后通过PowerWriter 将签名信息写入到固件指定地址,固件运行时,通过公钥 + ID去验证当前签名信息是否有效,从而判定当前芯片是否已经得到有效的授权,避免固件被直接拷贝使用,在开始之前,我们需要按照流程核查所有的准备工作都已经完成。

  • PowerWriter 项目中使用ICWKEY 签名(或者 ICWKEY 签名锁定模式)。
  • 已经设置签名地址(如0x08002000)。
  • PowerWriter 端的通讯信息已经同步到ICWKEY 的项目中,并与项目重新建立加密通讯。
  • 合理设置可授权次数,比如设置为10000。
  • 签名方法:选用ECDSA 签名,保存到ICWKEY,并导出了源码。

如以上步骤均完成,则可以看到ICWKEY 设备的显示信息,以及导出的源码信息,参考如下:

image-20240423103620274

同时ICWKEY 的设备将显示如下的信息:

微信图片_20240423103742

3.3.2 示例工程

3.3.2.1 准备

示例工程路径ICWKEY 的安装路径下,具体为:

C:\Users\用户名\AppData\Local\ICWKEY\Examples_for_mdk

可通过ICWKEY 桌面图标,快速定位到为止,并拷贝ECDSA 示例工程到指定路径,并解压,参考演示如下:

GIF 2024-4-23 10-43-58

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设备,如下所示:

GIF 2024-4-23 12-10-31

连接ICWKEY 到PowerWriter,并连接需要编程的MCU 目标PCB板, 并连接电源进行编程,参考接线如下所示:

image-20240423144333507

编程结束后,连接目标PCB 的串口TX 引脚,即可看到输出的签名验证信息,参考如下:

GIF 2024-4-23 14-48-39

3.3.2.5 调试方法

使用PowerWriter 对目标固件进行签名并编程到目标芯片之后,可通过设定的状态输出,来检查签名是否生效,在复杂的场景下,单纯看工作状态无法判定出现问题的位置,此时,需要对目标芯片进行调试,调试步骤如下:

  • 参考编译验证流程,完成编程
  • IDE选择:不擦除目标芯片,不编程目标芯片,不校验目标芯片的方式进行。

参考演示如下所示:

GIF 2024-4-23 14-48-39