打印
[应用相关]

单片机开机安全校验系统,MCU和EEPROM绑定启动

[复制链接]
314|4
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
tpgf|  楼主 | 2023-7-10 13:24 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
在物联网产品开发过程中,尤其在量产环节,为了防止固件泄露,抄板等发生,

需要在硬件电路的协助下,让单片机程序做一层安全校验系统。

比如第一次开机可以将mcu的唯一ID保存到空白eeprom中,当再次启动时

会读取eeprom固定地址的ID信息与自身ID进行匹配,匹配不上则让单片机

处于死机状态。更进一步,当单片机校验正常启动以后,再与无线通信模组进行ID信息的交换,

或者不同的应答逻辑,双方达到匹配之后才可正常工作。

enc.c简单的异或加解密

#ifndef  __ENC_H__
#define  __ENC_H__

#include <stdlib.h>
#include "stm32f2xx_hal.h"



void Robot_Code_Init(void);

uint8_t* Robot_Encode_Protocol(uint8_t*datas,uint8_t len,uint8_t pni);

uint8_t* Robot_Decode_Protocol(uint8_t*datas,uint8_t len,uint8_t pni);


#endif




#include "enc.h"


static RNG_HandleTypeDef RngHandle;



//单片机硬件随机数发生器初始化
void Robot_Code_Init(void)
{
          __HAL_RCC_RNG_CLK_ENABLE();
       
          /*##-1- Configure the RNG peripheral #######################################*/
          RngHandle.Instance = RNG;
       
          if(HAL_RNG_Init(&RngHandle) != HAL_OK)
          {
                 /* Initialization Error */
                 return;
          }
       
          srand(HAL_RNG_GetRandomNumber(&RngHandle));
}





/**
简单的异或加密,pni为随机密钥字节存放索引,
返回加密后的内容.
*/
uint8_t* Robot_Encode_Protocol(uint8_t*datas,uint8_t len,uint8_t pni)
{
                uint8_t nr;      
                uint8_t mask     = 0;
                uint8_t i;

                uint8_t nr_index = pni;
          
                nr               = rand() & 0xFF;


            datas[nr_index] = nr;

                for(i=0;i < len; i++)
                {
                        if((i & 0x01) == 1)
                                mask = (uint8_t)(nr + i + 1);
                        else
                                mask = (uint8_t)(nr * (i + 5));
                       
                        if(i != nr_index)
                                        datas ^= mask;
                }

                return datas;
}





/**
简单的异或解密,pni为随机密钥字节存放索引,
返回解密后的内容.
*/
uint8_t* Robot_Decode_Protocol(uint8_t*datas,uint8_t len,uint8_t pni)
{
                uint8_t nr_index    = pni;
                uint8_t mask        = 0;
                uint8_t i;
               
                uint8_t nr          = datas[nr_index];
                datas[nr_index]     = 0x00;
                 
                for(i=0; i < len; i++)
                {
                         if((i & 0x01) == 1)
                                 mask = (uint8_t)(nr + i + 1);
                         else
                                 mask = (uint8_t)(nr * (i + 5));
                       
                         if(i != nr_index)
                                        datas ^= mask;
                }

                return datas;
}


system_verify.c单片机开机执行校验流程


#ifndef __SYSTEM_VERIFY_H__
#define __SYSTEM_VERIFY_H__

#include <string.h>
#include <core_cm3.h>

#include "stm32f2xx_hal.h"
#include "comtypes.h"
#include "boot_act.h"
#include "unique.h"
#include "enc.h"
#include "util.h"
#include "base_data.h"


void System_Verify(void);


#endif






#include "system_verify.h"


#define CPU_PKG_LEN                     (13)
#define SV_NR_INDEX                     (12)


#define POWERON_FLAG_LEN                (2)
#define CPU_ID_SAM_LEN                  (4)


#define POWERON_FLAG                    (0x8899)




//字符串比较
static BOOLEAN Bytes_Cmp(uint8_t*src,uint8_t*target,uint16_t len)
{
     uint8_t i;
       
         for(i=0;i < len;i++)
         {
                        if(*src++ != *target++) return FALSE;
         }

         return TRUE;
}




//关闭中断进入无限循环
static void SV_Failed_OPTS(void)
{
                  
      __disable_irq();
                 
          while(TRUE);
}



/**
全为0x00或者0xFF都表示eeprom为空
*/
static BOOLEAN Is_Correct(uint8_t*buf,uint16_t len,BOOLEAN ignore0)
{
         uint8_t i;

         if(!ignore0 && buf[0] == 0x00)
         {
             for(i=0;i < len;i++)
                 {
                     if(buf != 0x00) return TRUE;
                 }
                 
                 return FALSE;
         }

         for(i=0;i < len;i++)
         {
             if(buf != 0xFF) return TRUE;
         }

         
         return FALSE;
}





/**校验系统*/
void  System_Verify(void)
{
         uint8_t readpkgbuf[CPU_PKG_LEN];
         uint8_t readidsam[CPU_ID_SAM_LEN];

         uint8_t curidbuf[CPU_PKG_LEN];
         uint8_t curidsam[CPU_ID_SAM_LEN];

         uint8_t poweronflag[POWERON_FLAG_LEN];
         uint8_t alldata[POWERON_FLAG_LEN + CPU_ID_SAM_LEN + CPU_PKG_LEN];

         uint8_t*encpkgbuf;
         uint8_t*decpkgbuf;

         uint32_t tmp                         = 0;

         //先清空,防止垃圾数据
         memset(readpkgbuf,0,sizeof(readpkgbuf));

         //读取存储的芯片MCU ID
         Read_Boot(readpkgbuf,IAP_START_ADDRESSS_ID,CPU_PKG_LEN);

         //如果没有保存ID信息
         if(!Is_Correct(readpkgbuf,CPU_ID_LEN,FALSE))
         {
                   //判断是否为第一次上电
                   memset(poweronflag,0,sizeof(poweronflag));
                   Read_Boot(poweronflag,IAP_START_ADDRESS_POWERON_FLAG,POWERON_FLAG_LEN);
                         
                   if(((poweronflag[0] << 8) | poweronflag[1]) == POWERON_FLAG)
                   {
                                //开始做崩溃的事情
                                goto SYSTEM_VERIFY_FAILED;
                   }

                   //将当前芯片ID及校验信息保存起来,并置第一次上电完成.
                   Get_ID(curidbuf);

                   tmp                      = Crc32_Calc(curidbuf,CPU_ID_LEN);
                   curidsam[0]                           = (tmp >> 24) & 0xFF;
                   curidsam[1]              = (tmp >> 16) & 0xFF;
                   curidsam[2]              = (tmp >> 8) & 0xFF;
                   curidsam[3]              = tmp & 0xFF;
                         
               
                   poweronflag[0]           = (POWERON_FLAG >> 8) & 0xFF;
                   poweronflag[1]           = POWERON_FLAG & 0xFF;
               
                   encpkgbuf                = Robot_Encode_Protocol(curidbuf,CPU_PKG_LEN,SV_NR_INDEX);

                   memcpy(alldata,poweronflag,POWERON_FLAG_LEN);
                   memcpy(alldata+POWERON_FLAG_LEN,curidsam,CPU_ID_SAM_LEN);
                   memcpy(alldata+POWERON_FLAG_LEN+CPU_ID_SAM_LEN,encpkgbuf,CPU_PKG_LEN);
               
                   Write_Boot_ACT(TYPE_ADDR_BOOT,IAP_START_ADDRESS,alldata,sizeof(alldata));
                         
                   goto SYSTEM_VERIFY_SUCCESS;
                }

               
                //解密芯片MCU ID
                decpkgbuf                    = Robot_Decode_Protocol(readpkgbuf,CPU_PKG_LEN,SV_NR_INDEX);

                //验证存储的芯片ID校验信息是否正确
                Read_Boot(readidsam,IAP_START_ADDRESS_IDSAM,CPU_ID_SAM_LEN);
                if(((readidsam[0] << 24) | (readidsam[1] << 16) | (readidsam[2] << 8) | readidsam[3]) != Crc32_Calc(decpkgbuf,CPU_ID_LEN))
                {
                         //开始做崩溃的事情
                         goto SYSTEM_VERIFY_FAILED;
                }


                //与当前芯片ID比较
                Get_ID(curidbuf);

         
                if(!Bytes_Cmp(decpkgbuf,curidbuf,CPU_ID_LEN))
                {
                         开始做崩溃的事情
                         goto SYSTEM_VERIFY_FAILED;
                }

               
SYSTEM_VERIFY_SUCCESS:
                return;
               

SYSTEM_VERIFY_FAILED:
         SV_Failed_OPTS();
}





————————————————
版权声明:本文为CSDN博主「麦德泽特」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_44470384/article/details/131262245

使用特权

评论回复
沙发
V853| | 2023-7-10 14:33 | 只看该作者
这个想法挺好,不过也挺容易被**的。

使用特权

评论回复
板凳
天天向善| | 2023-7-10 14:34 | 只看该作者
只要找到那个空白的区域,基本上这个加密方法就被**了。

使用特权

评论回复
地板
软核硬核| | 2023-7-10 14:34 | 只看该作者
这个确实是一个好方法,后面可以试试。

使用特权

评论回复
5
Undshing| | 2023-7-10 22:31 | 只看该作者
那读一下eeprom不就破了嘛

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

2028

主题

15903

帖子

13

粉丝