0、前言
STM32编译后的代码存在FLASH中,通过外部工具可以读出来全部数据,一旦硬件抄板一样,再将FLASH数据全部拷贝至抄板单片机中,既可以完全实现硬件和软件功能抄袭。因此,需要对自己的应用程序加密,即使被抄板和读取FLASH数据,拷贝过后仍然无法执行相应的功能。【ps:没有解不开的单片机,主要看解密的经济效益】
主要思路:利用每个芯片的96位唯一ID,进行一定的计算和单向加密,得到唯一的应用AppKey并保存至FLASH,运行APP时读取该FLASH位置的AppKey,并与计算得到的AppKey比较是否相同,如果相同则正常运行,不相同则退出。
因此,在正确运行APP之前,需要保证FLASH保存有AppKey,可以在设备出厂前提前通过代码方式写入FLASH,再烧写正常的APP代码。为了减小代码烧写的工作量,采用IAP的方式实现自动出厂配置,方案包括三个工程:BootLoader,Encrypt,APP。三个工程在FLASH中的内存分配与OTA-IAP相同。
一、计算AppKey
step1 :为避免出现UID_BASE的明文,在读取UID时,对其地址进行一定的简单运算,再读取UID。
#define ADDRSEED (0x20170620)
#define METHORDADDR (ADDRSEED^1+1)
#define METHORDEUID (ADDRSEED|0x12345678)volatile const uint32_t uidaddr[3] = {UID_BASE+METHORDADDR,UID_BASE+4-METHORDADDR,UID_BASE+8+METHORDADDR};//get uid, by calc uidbase
//uid: ptr, 96byte length
static void Getuid(volatile uint32_t *uid)
{volatile uint32_t addrtemp;addrtemp = uidaddr[0] - METHORDADDR;uid[0] = *(volatile uint32_t*)(addrtemp);addrtemp = uidaddr[1] + METHORDADDR;uid[1] = *(volatile uint32_t*)(addrtemp);addrtemp = uidaddr[2] - METHORDADDR;uid[2] = *(volatile uint32_t*)(addrtemp);
}
step2 :对UID进一步简单加密。
//encrypt uid
//euid: ptr, 96byte length
static void Encryptuid(volatile uint32_t *euid)
{uint32_t uid[3];Getuid(uid);euid[0] = uid[0] + METHORDEUID;euid[0] ^= ADDRSEED;euid[1] = uid[1] + METHORDEUID;euid[1] ^= ADDRSEED;euid[2] = uid[2] + METHORDEUID;euid[2] ^= ADDRSEED;
}
step3 :对EncryptUID计算MD5,计算128bit单向散列值。
//generate md5 by euid
//key: ptr, 16byte length
void GenerateMD5(uint8_t *md5)
{uint32_t *euid;Encryptuid(euid);MD5_Init(&Context);MD5_Update(&Context,(uint8_t *)euid,96);MD5_Final(&Context,md5);
}
step4 :根据FLASH页容量大小,将MD5扩充至1024Byte,采用随机数扩充。
//generate key
//key: ptr, 1024 length, md5 extend to 1024
void GenerateKey(uint8_t *key)
{uint16_t i,j;uint8_t md5val[16];GenerateMD5(md5val);j = 0;for(i = 0; i < 1024; i++){if(i%64 == 0){key[i] = md5val[j++];}else{srand1(HAL_GetTick());key[i] = randr(0,0xFF);}}
}
二、自动配置流程
Encrypt工程代码只在出厂时运行一次,目的是利用UID产生AppKey,并将其提前写入指定FLASH中,因此,该段代码执行一次后将擦除。可设置Encrypt在APPA区中运行,利用IAP功能,将APPB区的APP固件在执行完Encrypt固件后,搬移至APPA区。
具体地,出厂自动配置密钥流程如下:
Step1:上电启动单片机,首先执行BootLoader程序。
Step2:BootLoader读取Parameter参数区,此时参数区设置为无升级任务,BootLoader执行APPA区的代码,进入 Step3 。存在升级任务时,执行 Step6, Step7 。
Step3:BootLoader跳转至APPA,执行Encrypt代码,根据UID计算AppKey,执行 Step4 和 Step5 。
Step4:将计算的AppKey存入Parameter区指定的位置,并写入升级标志和APPB区相关固件参数。
Step5:重启单片机,进入BootLoader。
Step6:清除升级任务标志。
Step7:拷贝APPB代码至APPA区,即擦除Encrypt代码,最后执行重启进入 Step5 ,重启后运行真正的APP代码。
三、出厂固件合并
出厂前需要在STM32中烧写BootLoader、Encrypt和APP三份Hex固件,加大了时间开销,三份Hex固件存在FlASH的不同位置,地址容易出错。因此,可将三份Hex进行合并成一个Hex,进行一次烧写,按照自动配置流程完成加密和代码搬运工作。
BootLoader工程在程序一开始运行,其地址和空间大小分配仍然按照0x08000000和20kB分配,编译生成boot.hex。Encrypt工程和APP都在APPA区运行,因此,两者地址和空间大小分配为相同的0x08005000和50kB。APP.Hex只是保存在APPB区,带加密执行完成后通知Bootloader进行搬移。
出厂固件按照boot.Hex,encrypt.Hex,app.Hex的顺序合并,打开Hex文件,分别用后一个文件的全部内容替换前一个文件的最后一行,保存为hex格式就可以。