3.4 Matrix示例
3.4.1 准备
Matrix 签名为一种简易的校验算法,ICWKEY(PowerWriter) 随机生成一一种组合,来对ID 进行加密,然后将加密的信息在生产时写入到目标芯片,目标芯片启动时,对签名进行校验,从来用来验证,当前芯片是否写入签名信息来进行固件保护的方法,在开始之前,我们需要按照流程核查所有的准备工作都已经完成。
- PowerWriter 项目中使用ICWKEY 签名(或者 ICWKEY 签名锁定模式)。
- 已经设置签名地址(如0x08002000)。
- PowerWriter 端的通讯信息已经同步到ICWKEY 的项目中,并与项目重新建立加密通讯。
- 合理设置可授权次数,比如设置为10000。
- 签名方法:选用Matrix签名,保存到ICWKEY,并导出了源码。
演示如下所示:
如以上步骤均完成,则可以看到ICWKEY 设备的显示信息,以及导出的源码信息,参考如下:
同时ICWKEY 的设备将显示如下的信息:
3.4.2 示例工程
3.4.2.1 准备
示例工程路径ICWKEY 的安装路径下,具体为:
C:\Users\用户名\AppData\Local\ICWKEY\Examples_for_mdk
可通过ICWKEY 桌面图标,快速定位到为止,并拷贝 Matrix 示例工程到指定路径,并解压,参考演示如下:
3.4.2.2 代码结构
3.4.2.2.1 cortex_chipid_binding.c
- 更换ICWKEY 导出的函数
//使用ICWKEY 中的导出的代码进行替换
//The following code may warn in KEIL(MDK), ignore it
static void ChipUIDAlgo(char pUserID[], char pChipID[], char pKey[])
{
pKey[0] = pChipID[8] * pChipID[3] | pUserID[8] & pChipID[10] ;
pKey[1] = pChipID[5] + pChipID[2] - pChipID[7] ^ pChipID[11] ;
pKey[2] = pChipID[9] - pUserID[4] ^ pUserID[2] * pUserID[10] ;
pKey[3] = pChipID[1] | pUserID[0] & pUserID[11] + pUserID[7] ;
pKey[4] = pUserID[1] * pUserID[9] | pUserID[3] & pChipID[6] ;
pKey[5] = pUserID[6] - pChipID[4] + pChipID[0] ^ pUserID[5] ;
pKey[6] = pUserID[2] - pChipID[1] & pChipID[9] * pChipID[0] ;
pKey[7] = pUserID[11] | pUserID[4] ^ pChipID[11] + pUserID[5] ;
pKey[8] = pChipID[7] - pUserID[1] | pUserID[0] + pChipID[10] ;
pKey[9] = pUserID[3] * pUserID[10] & pChipID[4] ^ pChipID[6] ;
pKey[10] = pChipID[5] | pUserID[7] - pUserID[9] + pChipID[8] ;
pKey[11] = pUserID[8] ^ pUserID[6] * pChipID[3] & pChipID[2] ;
}
3.4.2.2.2 cortex_chipid_binding.h
- 填写ID 地址(请看提示信息 。
- 修改签名地址为PowerWriter 中签名地址 。
- 替换为ICWKEY 中导出的UID_USERID_KEYx 。
- 根据实际情况,是否开启占位符。
/* Exported define --------------------------------------------------------*/
/* The following macros are automatically exported by the software supporting the burner.
Please do not modify them to keep them consistent */
#define UID_CHIP_MASK 0x5BD489F0 //Random generation
#define UID_CHIP_SIZE 12 //ChipID Size
/* 目标芯片的ID地址,可根据芯片手册查询 */
#define UID_CHIP_ADDR (0x1FFFF7E8^UID_CHIP_MASK) //ChipID Inner Addr in chip
#define UID_KEY_LENGTH 12 //The password is the same length as the user ID input
//签名信息存储地址,改为PowerWriter项目中签名信息的存储地址 0x08002000
#define UID_KEYADDR_INNER (0x08002000^UID_CHIP_MASK) //Key Store Addr In flash
//替换为 ICWKEY 中导出的 密码
#define UID_USERID_LENGTH UID_KEY_LENGTH //Customize password length
#define UID_USERID_KEY1 0xAD683BDB //UserID 1
#define UID_USERID_KEY2 0xC973505C //UserID 2
#define UID_USERID_KEY3 0x72C3BC02 //UserID 3
#define UID_DATAENDIAN littleEndian //Data Endian
/*
说明:
此定制用于决定是否在固件中对签名信息进行占位(预分配),防止和其他函数地址重叠,PowerWriter 烧写的时候可能会意外擦除代码
而导致固件出问题,此时,需要预先分配,如此标志位为0,则表示不进行占位,这种情况下,需要将签名信息的存储地址,存储在固件之外。
总结:
1:签名地址在实际固件范围之内,必须开启占位符;签名地址在实际固件范围之外,建议不开占位符,避免固件过大。
2:如果地址超过 0.7 芯片容量,则不开启占位符,如果地址小于 0.7 * 芯片容量则开启占位符
*/
#define UID_KEYADDR_PLACEHOLDER_EN 1 //Key Store Addr In Flash Enable/Disable Placeholder
UID_CHIP_ADDR 的地址,可以用过PowerWriter 选择签名模式为 Matrix ,导出源码中可以看到实际的ID地址。
3.4.2.2.3 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 闪烁
printf("Ok:matrix verify passed!\r\n");
HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
HAL_GPIO_TogglePin(LED2_GPIO_Port, LED2_Pin);
HAL_Delay(100);
}
else
{
//false: LED 常亮或者常灭
printf("Error:matrix verify failed!\r\n");
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.4.2.3 编译
编译项目,将在目录 Output\ TargetIC_Example.bin 生成测试固件。
3.4.2.4 验证
重新打开PowerWriter 项目,在Program Memory 页面添加 TargetIC_Example.bin 测试固件,并将项目加载到PowerWriter设备,如下所示:
连接ICWKEY 到PowerWriter,并连接需要编程的MCU 目标PCB板, 并连接电源进行编程,参考接线如下所示:
编程结束后,连接目标PCB 的串口TX 引脚,即可看到输出的签名验证信息,参考如下:
3.4.2.5 调试方法
使用PowerWriter 对目标固件进行签名并编程到目标芯片之后,可通过设定的状态输出,来检查签名是否生效,在复杂的场景下,单纯看工作状态无法判定出现问题的位置,此时,需要对目标芯片进行调试,调试步骤如下:
- 参考编译验证流程,完成编程
- IDE选择:不擦除目标芯片,不编程目标芯片,不校验目标芯片的方式进行。
参考演示如下所示: