STM32利用UID和OTP进行固件加密

资料准备:对应模块的参考手册(RM)
注:本文章基于STM32G030,文中所说的“本芯片”等,都代表STM32G030
在这里插入图片描述

一、OTP介绍

OTP:one-time programmable,只允许一次编程,也就是只能从1写0,不能从0写1。这里可能有人要问,这不是flash的特性么?需要注意的是,flash是允许擦除的,是允许从0写1的。而OTP不允许擦除,就算在ICP烧录代码时,也不会丢。而且,如果只有一位是0,整个双字(64位)都不能再写了,甚至也不能写0x0000 0000 0000 0000。这里的双字节根据不同的单片机有差距,由于我所使用的每次只能写双字节,因此,这里便是双字节。
“The OTP area cannot be read when RDP level is 1 and boot source is not the
Main Flash memory area.”当读保护级别位1时,并且BOOT源不是主程序区域时,OTP是不允许读的。

在不通的系列上,OTP的起始地址有所不同,需要自行去参考手册的flash章节查找,本芯片中OTP起始地址是0x1FFF7000

二、UID介绍

UID是芯片厂商为每个芯片出厂写入的,是一个96位的全球唯一编码。就相当于人类的身份证。HAL库中提供了获取UID的接口

/**
  * @brief Return the unique device identifier (UID based on 96 bits)
  * @param UID: pointer to 3 words array.
  * @retval Device identifier
  */
void HAL_GetUID(uint32_t *UID)
{
  UID[0] = (uint32_t)(READ_REG(*((uint32_t *)UID_BASE)));
  UID[1] = (uint32_t)(READ_REG(*((uint32_t *)(UID_BASE + 4U))));
  UID[2] = (uint32_t)(READ_REG(*((uint32_t *)(UID_BASE + 8U))));
}

也可以根据UID_BASE地址直接获取,在我用的板子上,UID_BASE地址是:0x1FFF7590UL在这里插入图片描述
因此可以按如下办法直接获取UID。

unsigned long *id = (unsigned long *)0x1FFF7590;
id[0]
id[1]
id[2]

三、加密思想

有了可以区分单片机的UID,又有了写入之后永远也不会丢的OTP,接下来的操作只需要将我们的UID使用我们自己的算法进行加密,然后将加密之后的值保存到OTP之中,每次上电之前去比对以下,OTP中的数据是否正确。进而来判断该设备是否已经被解密过了。

void Flash_OtpWrite()
{
	int i;
	uint64_t *p;//本芯片仅支持双字节写,读者需要按个人需求修改
	
	HAL_FLASH_Unlock(); //unlock
	
	p = (uint64_t *)&encrypt_code;//encrypt_code保存的是对UID加密之后的值

	for ( i = 0; i < DECRYPT_PARAM_SIZE/8; i ++ )//由于是双字节,因此是8位读者按需求更改
	{
		HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, DECRYPT_PARAM_START_ADDRESS+i*8, *(p+i) );
	}

	HAL_FLASH_Lock(); //lock again
}

猜你喜欢

转载自blog.csdn.net/qq_43448742/article/details/106917836