STM32利用AES加密数据、解密数据

STM32利用AES加密数据、解密数据


MD5在线工具

https://www.md5ma.com/md5-generator

Chapter1 STM32利用AES加密数据、解密数据

原文链接

一、头文件

c 复制代码
#ifndef _AES_H
#define _AES_H


// 以bit为单位的密钥长度,只能为 128,192 和 256 三种
#define AES_KEY_LENGTH	128

// 加解密模式
#define AES_MODE_ECB	0				// 电子密码本模式(一般模式)
#define AES_MODE_CBC	1				// 密码分组链接模式
#define AES_MODE		AES_MODE_CBC


///
//	函数名:	aes_init
//	描述:		初始化,在此执行扩展密钥操作。
//	输入参数:	pKey -- 原始密钥,其长度必须为 AES_KEY_LENGTH/8 字节。
//	输出参数:	无。
//	返回值:	无。
///
void aes_init(const void *pKey);

//
//	函数名:	aes_encrypt
//	描述:		加密数据
//	输入参数:	pPlainText	-- 明文,即需加密的数据,其长度为nDataLen字节。
//				nDataLen	-- 数据长度,以字节为单位,必须为AES_KEY_LENGTH/8的整倍数。
//				pIV			-- 初始化向量,如果使用ECB模式,可设为NULL。
//	输出参数:	pCipherText	-- 密文,即由明文加密后的数据,可以与pPlainText相同。
//	返回值:	无。
//
void aes_encrypt(const unsigned char *pPlainText, unsigned char *pCipherText, 
				 unsigned int nDataLen, const unsigned char *pIV);

//
//	函数名:	aes_decrypt
//	描述:		解密数据
//	输入参数:	pCipherText -- 密文,即需解密的数据,其长度为nDataLen字节。
//				nDataLen	-- 数据长度,以字节为单位,必须为AES_KEY_LENGTH/8的整倍数。
//				pIV			-- 初始化向量,如果使用ECB模式,可设为NULL。
//	输出参数:	pPlainText  -- 明文,即由密文解密后的数据,可以与pCipherText相同。
//	返回值:	无。
//
void aes_decrypt( const unsigned char *pCipherText,unsigned char *pPlainText, 
				 unsigned int nDataLen, const unsigned char *pIV);
#endif  /* _AES_H */

二、源文件

c 复制代码
#include "aes.h"
#include "string.h"

    
// 为了能针对C51进行优化,并且又使代码可用于ARM和PC等环境,
// 在非C51环境(没有定义__C51__)下需要把C51特定的关键字定义为空
#ifndef __C51__
	#define code
	#define data
	#define idata
	#define xdata
	#define pdata
	typedef unsigned char BOOL;
#else
	typedef bit BOOL;
#endif

#define Nk	(AES_KEY_LENGTH / 32)		// 以"字"(4字节)为单位的密钥长度
#define Nb	4							// 以"字"(4字节)为单位的加解密数据块大小,固定为4

// Nr:加密的轮数
#if   AES_KEY_LENGTH == 128
	#define Nr	10
#elif AES_KEY_LENGTH == 192
	#define Nr	12
#elif AES_KEY_LENGTH == 256
	#define Nr	14
#else
	#error AES_KEY_LENGTH must be 128, 192 or 256 BOOLs!
#endif

// GF(28) 多项式
#define BPOLY 0x1B // Lower 8 BOOLs of (x^8 + x^4 + x^3 + x + 1), ie. (x^4 + x^3 + x + 1).

/*****************************************************************************
*  Typedef  Enum                        枚举定义
*****************************************************************************/

/*****************************************************************************
*  Struct                               数据结构定义
******************************************************************************/


/*****************************************************************************
*  Local variable                       局部变量
*****************************************************************************/

// AES子密钥表,当密钥长度为128位时,占用176字节空间
static xdata unsigned char g_roundKeyTable[4*Nb*(Nr+1)];

// 加密用的SBox
static code const unsigned char sBox[256] = 
{
	0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
	0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
	0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
	0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
	0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
	0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
	0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
	0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
	0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
	0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
	0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
	0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
	0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
	0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
	0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
	0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 
};

// 解密用的SBox
static code const unsigned char invSBox[256] = 
{
	0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
	0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
	0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
	0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
	0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
	0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
	0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
	0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
	0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
	0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
	0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
	0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
	0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
	0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
	0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
	0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d	
};


///
//	函数名:	rotation_word
//	描述:		对一个"字"数据进行循环右移。
//	输入参数:	pWord -- 要右移的4字节数据。
//	输出参数:	pWord -- 右移后的4字节数据。
//	返回值:	无。
///
static void rotation_word(unsigned char *pWord)
{
	unsigned char temp = pWord[0];
	pWord[0]  = pWord[1];
	pWord[1]  = pWord[2];
	pWord[2]  = pWord[3];
	pWord[3]  = temp;
}

///
//	函数名:	xor_bytes
//	描述:		批量异或两组数据。
//	输入参数:	pData1 -- 要异或的第一组数据。
//				pData1 -- 要异或的第二组数据。
//				nCount -- 要异或的数据长度。
//	输出参数:	pData1 -- 异或后的结果。
//	返回值:	无。
///
static void xor_bytes(unsigned char *pData1, const unsigned char *pData2, unsigned char nCount)
{
	unsigned char i;
	
	for (i = 0; i < nCount; i++)
	{
		pData1[i] ^= pData2[i];
	}
}

///
//	函数名:	AddRoundKey
//	描述:		把 中间状态数据 加上(异或)子密钥,数据长度为16字节。
//	输入参数:	pState	  -- 状态数据。
//				pRoundKey -- 子密钥数据。
//	输出参数:	pState	  -- 加上子密钥后的状态数据。
//	返回值:	无。
///
// static void AddRoundKey(unsigned char *pState, const unsigned char *pRoundKey)
// {
// 	xor_bytes(pState, pRoundKey, 4*Nb);
// }

// AddRoundKey的宏形式,比函数形式可以节省4字节的data数据
#define AddRoundKey(pState, pRoundKey) \
	xor_bytes((pState), (pRoundKey), 4*Nb)


///
//	函数名:	sub_bytes
//	描述:		通过S盒子置换状态数据。
//	输入参数:	pState	-- 状态数据。
//				nCount  -- 状态数据长度。
//				bInvert	-- 是否使用反向S盒子(解密时使用)。
//	输出参数:	pState	-- 置换后的状态数据。
//	返回值:	无。
///
static void sub_bytes(unsigned char *pState, unsigned char nCount, BOOL bInvert)
{
	unsigned char i;
	const unsigned char code *pSBox = bInvert ? invSBox : sBox;
	
	for (i = 0; i < nCount; i++)
	{
		pState[i] = pSBox[pState[i]];
	}
}

///
//	函数名:	shift_rows
//	描述:		把状态数据移行。
//	输入参数:	pState	-- 状态数据。
//				bInvert	-- 是否反向移行(解密时使用)。
//	输出参数:	pState	-- 移行后的状态数据。
//	返回值:	无。
///
static void shift_rows(unsigned char *pState, BOOL bInvert)
{
	// 注意:状态数据以列形式存放!

	unsigned char r;	// row,   行
	unsigned char c;	// column,列
	unsigned char temp;
	unsigned char rowData[4];
	
	for (r = 1; r < 4; r++)
	{
		// 备份一行数据
		for (c = 0; c < 4; c++)
		{
			rowData[c] = pState[r + 4*c];
		}
		
		temp = bInvert ? (4 - r) : r;
		for (c = 0; c < 4; c++)
		{
			pState[r + 4*c] = rowData[(c + temp) % 4];
		}
	}
}

///
//	函数名:	gf_mult_by02
//	描述:		在GF(28)域的 乘2 运算。
//	输入参数:	num	-- 乘数。
//	输出参数:	无。
//	返回值:	num乘以2的结果。
///
static unsigned char gf_mult_by02(unsigned char num)
{
	if ((num & 0x80) == 0)
	{
		num = num << 1;
	}
	else
	{
		num = (num << 1) ^ BPOLY;
	}
	
	return num;
}

///
//	函数名:	mix_columns
//	描述:		混合状态各列数据。
//	输入参数:	pState	-- 状态数据。
//				bInvert	-- 是否反向混合(解密时使用)。
//	输出参数:	pState	-- 混合列后的状态数据。
//	返回值:	无。
///
static void mix_columns(unsigned char *pState, BOOL bInvert)
{
	unsigned char i;
	unsigned char temp;
	unsigned char a0Pa2_M4;	// 4(a0 + a2)
	unsigned char a1Pa3_M4;	// 4(a1 + a3)
	unsigned char result[4];

	for (i = 0; i < 4; i++, pState += 4)
	{
		// b0 = 2a0 + 3a1 + a2 + a3 
		//    = (a0 + a1 + a2 + a3) + 2(a0 + a1) + a0

		temp = pState[0] ^ pState[1] ^ pState[2] ^ pState[3];
		result[0] = temp ^ pState[0] ^ gf_mult_by02((unsigned char) (pState[0] ^ pState[1]));
		result[1] = temp ^ pState[1] ^ gf_mult_by02((unsigned char) (pState[1] ^ pState[2]));
		result[2] = temp ^ pState[2] ^ gf_mult_by02((unsigned char) (pState[2] ^ pState[3]));
		result[3] = temp ^ pState[3] ^ gf_mult_by02((unsigned char) (pState[3] ^ pState[0]));

		if (bInvert)
		{
		// b0' = 14a0 + 11a1 + 13a2 + 9a3 
		//     = (a0 + a1 + a2 + a3) + 2(a0 + a1) + a0	(这部分为b0)
		//       + 2(4(a0 + a2) + 4(a1 + a3))
		//       +   4(a0 + a2)

			a0Pa2_M4 = gf_mult_by02(gf_mult_by02((unsigned char) (pState[0] ^ pState[2])));
			a1Pa3_M4 = gf_mult_by02(gf_mult_by02((unsigned char) (pState[1] ^ pState[3])));
			temp	 = gf_mult_by02((unsigned char) (a0Pa2_M4 ^ a1Pa3_M4));
			result[0] ^= temp ^ a0Pa2_M4;
			result[1] ^= temp ^ a1Pa3_M4;
			result[2] ^= temp ^ a0Pa2_M4;
			result[3] ^= temp ^ a1Pa3_M4;
		}

		memcpy(pState, result, 4);
	}
}

///
//	函数名:	block_encrypt
//	描述:		对单块数据加密。
//	输入参数:	pState -- 状态数据。
//	输出参数:	pState -- 加密后的状态数据。
//	返回值:	无。
///
static void block_encrypt(unsigned char *pState)
{
	unsigned char i;
	
	AddRoundKey(pState, g_roundKeyTable);
	
	for (i = 1; i <= Nr; i++)	// i = [1, Nr]
	{
		sub_bytes(pState, 4*Nb, 0);
		shift_rows(pState, 0);

		if (i != Nr)
		{
			mix_columns(pState, 0);
		}

		AddRoundKey(pState, &g_roundKeyTable[4*Nb*i]);
	}
	
// 为了节省代码,合并到循化执行
// 	sub_bytes(pState, 4*Nb);
//	shift_rows(pState, 0);
// 	AddRoundKey(pState, &g_roundKeyTable[4*Nb*Nr]);
}

///
//	函数名:	block_decrypt
//	描述:		对单块数据解密。
//	输入参数:	pState -- 状态数据。
//	输出参数:	pState -- 解密后的状态数据。
//	返回值:	无。
///
static void block_decrypt(unsigned char *pState)
{
	unsigned char i;
	
	AddRoundKey(pState, &g_roundKeyTable[4*Nb*Nr]);
	
	for (i = Nr; i > 0; i--)	// i = [Nr, 1]
	{
		shift_rows(pState, 1);
		sub_bytes(pState, 4*Nb, 1);
		AddRoundKey(pState, &g_roundKeyTable[4*Nb*(i-1)]);

		if (i != 1)
		{
			mix_columns(pState, 1);
		}
	}
	
// 为了节省代码,合并到循化执行
//  shift_rows(pState, 1);
//  sub_bytes(pState, 4*Nb, 1);
//  AddRoundKey(pState, g_roundKeyTable);
}

/*****************************************************************************
*  Global Functions                     全局函数
******************************************************************************/


///
//	函数名:	aes_init
//	描述:		初始化,在此执行扩展密钥操作。
//	输入参数:	pKey -- 原始密钥,其长度必须为 AES_KEY_LENGTH/8 字节。
//	输出参数:	无。
//	返回值:	无。
///
void aes_init(const void *pKey)
{
	// 扩展密钥
	unsigned char i;
	unsigned char *pRoundKey;
	unsigned char Rcon[4] = {0x01, 0x00, 0x00, 0x00};

	memcpy(g_roundKeyTable, pKey, 4*Nk);

	pRoundKey = &g_roundKeyTable[4*Nk];

	for (i = Nk; i < Nb*(Nr+1); pRoundKey += 4, i++)
	{
		memcpy(pRoundKey, pRoundKey - 4, 4);

		if (i % Nk == 0)
		{
			rotation_word(pRoundKey);
			sub_bytes(pRoundKey, 4, 0);
			xor_bytes(pRoundKey, Rcon, 4);

			Rcon[0] = gf_mult_by02(Rcon[0]);
		}
		else if (Nk > 6 && i % Nk == Nb)
		{
			sub_bytes(pRoundKey, 4, 0);
		}

		xor_bytes(pRoundKey, pRoundKey - 4*Nk, 4);
	}
}

//
//	函数名:	aes_encrypt
//	描述:		加密数据
//	输入参数:	pPlainText	-- 明文,即需加密的数据,其长度为nDataLen字节。
//				nDataLen	-- 数据长度,以字节为单位,必须为AES_KEY_LENGTH/8的整倍数。
//				pIV			-- 初始化向量,如果使用ECB模式,可设为NULL。
//	输出参数:	pCipherText	-- 密文,即由明文加密后的数据,可以与pPlainText相同。
//	返回值:	无。
//
void aes_encrypt(const unsigned char *pPlainText, unsigned char *pCipherText, 
				 unsigned int nDataLen, const unsigned char *pIV)
{
	unsigned int i;

	if (pPlainText != pCipherText)
	{
		memcpy(pCipherText, pPlainText, nDataLen);
	}

	for (i = nDataLen/(4*Nb); i > 0 ; i--, pCipherText += 4*Nb)
	{
		#if AES_MODE == AES_MODE_CBC
			xor_bytes(pCipherText, pIV, 4*Nb);
		#endif

		block_encrypt(pCipherText);
		pIV = pCipherText;
	}
}

//
//	函数名:	aes_decrypt
//	描述:		解密数据
//	输入参数:	pCipherText -- 密文,即需解密的数据,其长度为nDataLen字节。
//				nDataLen	-- 数据长度,以字节为单位,必须为AES_KEY_LENGTH/8的整倍数。
//				pIV			-- 初始化向量,如果使用ECB模式,可设为NULL。
//	输出参数:	pPlainText  -- 明文,即由密文解密后的数据,可以与pCipherText相同。
//	返回值:	无。
//
void aes_decrypt( const unsigned char *pCipherText,unsigned char *pPlainText, 
				 unsigned int nDataLen, const unsigned char *pIV)
{
	unsigned int i;

	if (pPlainText != pCipherText)
	{
		memcpy(pPlainText, pCipherText, nDataLen);
	}

	// 从最后一块数据开始解密,这样不用开辟空间来保存IV
	pPlainText += nDataLen - 4*Nb;
	for (i = nDataLen/(4*Nb); i > 0 ; i--, pPlainText -= 4*Nb)
	{
		block_decrypt(pPlainText);

		#if AES_MODE == AES_MODE_CBC
			if (i == 1)
			{// 最后一块数据
				xor_bytes(pPlainText, pIV, 4*Nb);
			}
			else
			{
				xor_bytes(pPlainText, pPlainText - 4*Nb, 4*Nb);
			}
		#endif
	}
}

三、使用

使用注意点:

1、AES_KEY_LENGTH取值只能是128,192 和 256

2、密钥和向量表长度为AES_KEY_LENGTH/8个字节

3、加密、解密数据长度为AES_KEY_LENGTH/8的整数倍字节

c 复制代码
int main(void)
{ 
	u8 buf[16],saveBuf[16],descryptBuf[16];
	u16 i;
	unsigned char AES128key[16] = "123456789abcdefa";//秘钥
	unsigned char AES_IV[16]= "0102030405123456";//向量表
	delay_init(168);		  //初始化延时函数
	LED_Init();		        //初始化LED端口

	aes_init(AES128key);//AES初始化
	
	for(i=0;i<sizeof(buf);i++)
	{
		buf[i]=i;
	}
	while(1)
	{
		aes_encrypt(buf,saveBuf,sizeof(buf), AES_IV);
		
		
		aes_decrypt(saveBuf,descryptBuf, sizeof(buf), AES_IV);
		
		
		GPIO_ResetBits(GPIOF,GPIO_Pin_9);  //LED0对应引脚GPIOF.9拉低,亮  等同LED0=0;
		GPIO_SetBits(GPIOF,GPIO_Pin_10);   //LED1对应引脚GPIOF.10拉高,灭 等同LED1=1;
		delay_ms(500);  		   //延时300ms
		GPIO_SetBits(GPIOF,GPIO_Pin_9);	   //LED0对应引脚GPIOF.0拉高,灭  等同LED0=1;
		GPIO_ResetBits(GPIOF,GPIO_Pin_10); //LED1对应引脚GPIOF.10拉低,亮 等同LED1=0;
		delay_ms(500);                     //延时300ms
	}
}

Chapter2 stm32 的 md5计算函数

原文链接:https://blog.csdn.net/joyopirate/article/details/122006598

假如你用的是stm32cubeide,可以考虑使用官方的库。使用方法可以看我写的这个【加密库使用方法】

参考:

https://www.cnblogs.com/xuning/p/4686021.html

根据上面那个网站提供方法稍加改造得到的

c 复制代码
#define ROTATELEFT(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))

/**
 * @desc: convert message and mes_bkp string into integer array and store them in w
 */
static void md5_process_part1(uint32_t *w, unsigned char *message, uint32_t *pos, uint32_t mes_len, const unsigned char *mes_bkp)
{
    uint32_t i; // used in for loop

    for(i = 0; i <= 15; i++)
    {
        int32_t count = 0;
        while(*pos < mes_len && count <= 24)
        {
            w[i] += (((uint32_t)message[*pos]) << count);
            (*pos)++;
            count += 8;
        }
        while(count <= 24)
        {
            w[i] += (((uint32_t)mes_bkp[*pos - mes_len]) << count);
            (*pos)++;
            count += 8;
        }
    }
}

/**
 * @desc: start encryption based on w
 */
static void md5_process_part2(uint32_t abcd[4], uint32_t *w, const uint32_t k[64], const uint32_t s[64])
{
    uint32_t i; // used in for loop

    uint32_t a = abcd[0];
    uint32_t b = abcd[1];
    uint32_t c = abcd[2];
    uint32_t d = abcd[3];
    uint32_t f = 0;
    uint32_t g = 0;

    for(i = 0; i < 64; i++)
    {
        if(i >= 0 && i <= 15)
        {
            f = (b & c) | ((~b) & d);
            g = i;
        }else if(i >= 16 && i <= 31)
        {
            f = (d & b) | ((~d) & c);
            g = (5 * i + 1) % 16;
        }else if(i >= 32 && i <= 47)
        {
            f = b ^ c ^ d;
            g = (3 * i + 5) % 16;
        }else if(i >= 48 && i <= 63)
        {
            f = c ^ (b | (~d));
            g = (7 * i) % 16;
        }
        uint32_t temp = d;
        d = c;
        c = b;
        b = ROTATELEFT((a + f + k[i] + w[g]), s[i]) + b;
        a = temp;
    }

    abcd[0] += a;
    abcd[1] += b;
    abcd[2] += c;
    abcd[3] += d;
}

static const uint32_t k_table[]={
    0xd76aa478,0xe8c7b756,0x242070db,0xc1bdceee,
    0xf57c0faf,0x4787c62a,0xa8304613,0xfd469501,0x698098d8,
    0x8b44f7af,0xffff5bb1,0x895cd7be,0x6b901122,0xfd987193,
    0xa679438e,0x49b40821,0xf61e2562,0xc040b340,0x265e5a51,
    0xe9b6c7aa,0xd62f105d,0x02441453,0xd8a1e681,0xe7d3fbc8,
    0x21e1cde6,0xc33707d6,0xf4d50d87,0x455a14ed,0xa9e3e905,
    0xfcefa3f8,0x676f02d9,0x8d2a4c8a,0xfffa3942,0x8771f681,
    0x6d9d6122,0xfde5380c,0xa4beea44,0x4bdecfa9,0xf6bb4b60,
    0xbebfbc70,0x289b7ec6,0xeaa127fa,0xd4ef3085,0x04881d05,
    0xd9d4d039,0xe6db99e5,0x1fa27cf8,0xc4ac5665,0xf4292244,
    0x432aff97,0xab9423a7,0xfc93a039,0x655b59c3,0x8f0ccc92,
    0xffeff47d,0x85845dd1,0x6fa87e4f,0xfe2ce6e0,0xa3014314,
    0x4e0811a1,0xf7537e82,0xbd3af235,0x2ad7d2bb,0xeb86d391
};

static const uint32_t s_table[]={
    7,12,17,22,7,12,17,22,7,12,17,22,7,
    12,17,22,5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20,
    4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,6,10,
    15,21,6,10,15,21,6,10,15,21,6,10,15,21
};

int32_t cal_md5(unsigned char *result, unsigned char *data, int length){
    if (result == NULL)
    {
        return 1;
    }

    uint32_t w[16];

    uint32_t i; // used in for loop

    uint32_t mes_len = length;
    uint32_t looptimes = (mes_len + 8) / 64 + 1;
    uint32_t abcd[] = {0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476};

    uint32_t pos = 0; // position pointer for message
    uint32_t bkp_len = 64 * looptimes - mes_len; // 经过计算发现不超过72

//    unsigned char *bkp_mes = (unsigned char *)calloc(1, bkp_len);
    unsigned char bkp_mes[80];
    for(int i = 0; i < 80; i++) //初始化
    {
        bkp_mes[i] = 0;
    }

    bkp_mes[0] = (unsigned char)(0x80);
    uint64_t mes_bit_len = ((uint64_t)mes_len) * 8;
    for(i = 0; i < 8; i++)
    {
        bkp_mes[bkp_len-i-1] = (unsigned char)((mes_bit_len & (0x00000000000000FF << (8 * (7 - i)))) >> (8 * (7 - i)));
    }

    for(i = 0; i < looptimes; i++)
    {
        for(int j = 0; j < 16; j++) //初始化
        {
            w[j] = 0x00000000;
        }

        md5_process_part1(w, data, &pos, mes_len, bkp_mes); // compute w

        md5_process_part2(abcd, w, k_table, s_table); // calculate md5 and store the result in abcd
    }

    for(int i = 0; i < 16; i++)
    {
        result[i] = ((unsigned char*)abcd)[i];
    }

    return 0;
}

Chapter3 STM32 应用程序加密的一种设计方案

原文链接:https://blog.csdn.net/u010058695/article/details/101014672

前言

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。

c 复制代码
#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进一步简单加密。

c 复制代码
//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单向散列值。

c 复制代码
//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,采用随机数扩充。

c 复制代码
//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格式就可以。

个人总结

头文件AppCryptography.h

c 复制代码
#ifndef  __BSP_APPCRYPTOGRAPHY_H__
#define  __BSP_APPCRYPTOGRAPHY_H__

#include <stdint.h>

/**
  * 函数功能: 计算一组数据的MD5值
  * 输入参数: data:指向待计算数据的首地址;length:待计算数据的长度;
  * 返 回 值: result:MD5结果返回值,16字节,128位
  * 说    明: 无
  */
int32_t cal_md5(unsigned char *result, unsigned char *data, int length);
void Getuid(void);


#endif
c 复制代码
#include "AppCryptography/AppCryptography.h"
#include <stdio.h>
#include <stdlib.h>
#include <stm32f410tx.h>

#define ROTATELEFT(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))

#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};
uint32_t uid[3] = {0x00,0x00,0x00};

//get uid, by calc uidbase
//uid: ptr, 96byte length
void Getuid(void)
//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);

	HAL_GetUID(uid); //STM32不同系列,UID_BASE不一样
}

源文件AppCryptography.c
/**
 * @desc: convert message and mes_bkp string into integer array and store them in w
 */
static void md5_process_part1(uint32_t *w, unsigned char *message, uint32_t *pos, uint32_t mes_len, const unsigned char *mes_bkp)
{
    uint32_t i; // used in for loop

    for(i = 0; i <= 15; i++)
    {
        int32_t count = 0;
        while(*pos < mes_len && count <= 24)
        {
            w[i] += (((uint32_t)message[*pos]) << count);
            (*pos)++;
            count += 8;
        }
        while(count <= 24)
        {
            w[i] += (((uint32_t)mes_bkp[*pos - mes_len]) << count);
            (*pos)++;
            count += 8;
        }
    }
}

/**
 * @desc: start encryption based on w
 */
static void md5_process_part2(uint32_t abcd[4], uint32_t *w, const uint32_t k[64], const uint32_t s[64])
{
    uint32_t i; // used in for loop

    uint32_t a = abcd[0];
    uint32_t b = abcd[1];
    uint32_t c = abcd[2];
    uint32_t d = abcd[3];
    uint32_t f = 0;
    uint32_t g = 0;

    for(i = 0; i < 64; i++)
    {
        if(i >= 0 && i <= 15)
        {
            f = (b & c) | ((~b) & d);
            g = i;
        }else if(i >= 16 && i <= 31)
        {
            f = (d & b) | ((~d) & c);
            g = (5 * i + 1) % 16;
        }else if(i >= 32 && i <= 47)
        {
            f = b ^ c ^ d;
            g = (3 * i + 5) % 16;
        }else if(i >= 48 && i <= 63)
        {
            f = c ^ (b | (~d));
            g = (7 * i) % 16;
        }
        uint32_t temp = d;
        d = c;
        c = b;
        b = ROTATELEFT((a + f + k[i] + w[g]), s[i]) + b;
        a = temp;
    }

    abcd[0] += a;
    abcd[1] += b;
    abcd[2] += c;
    abcd[3] += d;
}

static const uint32_t k_table[]={
    0xd76aa478,0xe8c7b756,0x242070db,0xc1bdceee,
    0xf57c0faf,0x4787c62a,0xa8304613,0xfd469501,0x698098d8,
    0x8b44f7af,0xffff5bb1,0x895cd7be,0x6b901122,0xfd987193,
    0xa679438e,0x49b40821,0xf61e2562,0xc040b340,0x265e5a51,
    0xe9b6c7aa,0xd62f105d,0x02441453,0xd8a1e681,0xe7d3fbc8,
    0x21e1cde6,0xc33707d6,0xf4d50d87,0x455a14ed,0xa9e3e905,
    0xfcefa3f8,0x676f02d9,0x8d2a4c8a,0xfffa3942,0x8771f681,
    0x6d9d6122,0xfde5380c,0xa4beea44,0x4bdecfa9,0xf6bb4b60,
    0xbebfbc70,0x289b7ec6,0xeaa127fa,0xd4ef3085,0x04881d05,
    0xd9d4d039,0xe6db99e5,0x1fa27cf8,0xc4ac5665,0xf4292244,
    0x432aff97,0xab9423a7,0xfc93a039,0x655b59c3,0x8f0ccc92,
    0xffeff47d,0x85845dd1,0x6fa87e4f,0xfe2ce6e0,0xa3014314,
    0x4e0811a1,0xf7537e82,0xbd3af235,0x2ad7d2bb,0xeb86d391
};

static const uint32_t s_table[]={
    7,12,17,22,7,12,17,22,7,12,17,22,7,
    12,17,22,5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20,
    4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,6,10,
    15,21,6,10,15,21,6,10,15,21,6,10,15,21
};


/* 函数体 --------------------------------------------------------------------*/
/**
  * 函数功能: 计算一组数据的MD5值
  * 输入参数: data:指向待计算数据的首地址;length:待计算数据的长度;
  * 返 回 值: result:MD5结果返回值,16字节,128位
  * 说    明: 无
  */
int32_t cal_md5(unsigned char *result, unsigned char *data, int length){
    if (result == NULL)
    {
        return 1;
    }

    uint32_t w[16];

    uint32_t i; // used in for loop

    uint32_t mes_len = length;
    uint32_t looptimes = (mes_len + 8) / 64 + 1;
    uint32_t abcd[] = {0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476};

    uint32_t pos = 0; // position pointer for message
    uint32_t bkp_len = 64 * looptimes - mes_len; // 经过计算发现不超过72

//    unsigned char *bkp_mes = (unsigned char *)calloc(1, bkp_len);
    unsigned char bkp_mes[80];
    for(int i = 0; i < 80; i++) //初始化
    {
        bkp_mes[i] = 0;
    }

    bkp_mes[0] = (unsigned char)(0x80);
    uint64_t mes_bit_len = ((uint64_t)mes_len) * 8;
    for(i = 0; i < 8; i++)
    {
        bkp_mes[bkp_len-i-1] = (unsigned char)((mes_bit_len & (0x00000000000000FF << (8 * (7 - i)))) >> (8 * (7 - i)));
    }

    for(i = 0; i < looptimes; i++)
    {
        for(int j = 0; j < 16; j++) //初始化
        {
            w[j] = 0x00000000;
        }

        md5_process_part1(w, data, &pos, mes_len, bkp_mes); // compute w

        md5_process_part2(abcd, w, k_table, s_table); // calculate md5 and store the result in abcd
    }

    for(int i = 0; i < 16; i++)
    {
        result[i] = ((unsigned char*)abcd)[i];
    }

    return 0;
}

具体使用方法

c 复制代码
uint8_t  data1[16] = "abcdefg012345678";
uint8_t  data_result[16] ={0};

int k = sizeof(data1);
cal_md5(data_result, data1, k);
相关推荐
Zw-awa4 小时前
从 CPU 搬运到自动搬运:DMA、空闲中断和两块缓冲
stm32
揽星河..4 小时前
【C语言】扫雷游戏
c语言·算法·游戏
丘山录4 小时前
本地部署 LongCat-2.5,要 7 台机器我只有 4 台
人工智能·算法
Allen_LVyingbo4 小时前
NVIDIA CUDA Tile 在医疗计算中的算子形式化、算法重构与内核实现
大数据·python·算法·重构·知识图谱·健康医疗·数据库架构
无敌贵点大王4 小时前
从寄存器层面重学STM32(6)——串口USART实战部分
stm32·单片机·嵌入式硬件
庙堂龙吟奈我何4 小时前
二进制和十六进制
stm32·单片机
冻柠檬飞冰走茶5 小时前
PTA刷题集之Airline Routes(C语言实现)
c语言·开发语言·数据结构·算法
宗旨飞翔5 小时前
不同的二叉搜索树
算法
736c5 小时前
固件开发岗算法
数据结构·算法
青少儿编程课堂5 小时前
蓝桥杯·全国青少年信息素养大赛·电子学会Python/C++考级:倍增法求最近公共祖先LCA解析
c++·python·算法·bfs·信息学竞赛