本帖最后由 eefocus_3880118 于 2024-3-24 14:07 编辑
STM32H735在安全性上的能力也是很强的,在芯片中有个加密和哈希模块,用于各种加密、哈希计算,在有了高主频和硬件模块的支持,使得加解密和哈希计算在这款芯片上速度可以很快。 那么具体支持哪些加解密与哈希计算呢?那就要看看技术手册的原文
可以看到我们常用的DES/TDES、AES各种算法各种秘钥长度均支持,SHA-1、SHA-2、MD5、HMAC均支持。并且支持DMA。
二、STM32CubeExpansion_Crypto介绍
我们平常想要实现加解密,最快的方法就是找一个加解密库,移植过去,然后调用接口,我平常使用mbedtls,这是一个开源的加解密库,但他是纯软件计算,需要占用CPU,速度肯定是比不上结合硬件加密模块的加密算法的。那么在ST的芯片上如果你想使用芯片的硬件加密模块,应该怎么办呢?
这个问题的答案就是今天本文的主角了,ST的加密库“STM32CubeExpansion_Crypto”,这个加密库ST一直在维护升级,现在已经更新到V4了,还记得我上一次用它还是在2021年,有截图为证
这个加密库功能非常强大,几乎涵盖了我们平常能使用到的绝大部分加解密算法,只要你是使用STM32的MCU,你就可以免费试用,真得很
关于支持哪些芯片,在网页中有写到,我看了一下几乎是涵盖STM32全系MCU了,极个别系列没有,可能是太新了还没支持上,可能是ST没写上,例如H5,网页上没有,但是在加密库的更新说明中是写了,这令我有点困惑
三.实战
好了话不多说,我们开始实战一下,把这个加密库加入到我们的工程中去使用
3.1 加密库的下载及讲解
首先我们要把加密库下载下来,在网页中点击“获取最新版本”,然后再弹出的引导页面选择登陆/注册账号再登陆/以访客身份下载
下载好后我们把压缩包解压,文件夹内容如下
“_htmresc”是html文件需要使用的资源文件
“Drivers”中存放了各个芯片的HAL库文件以及各个开发板的BSP文件,还有CMSIS
“Middlewares”中存放的就是我们待会儿要使用的加密库文件
“Projects”中存放的是ST基于各个系列的开发板做个各种加解密demo工程及代码,如果你手上恰好有以下开发板的话,就可以直接跑demo了(可以H735-DK不在其中,所以待会儿我要来移植到自己创建的工程中)
3.2 CUBEMX配置并创建工程
接下来开始创建一个新的工程 打开cubemx,我们选择从选择MCU开始工程
配置开启SWD调试
选择高速时钟源为晶振
开启串口3(用于待会儿串口打印输出各种信息)
串口的引脚需要重新定义一下,CUBEMX中默认的串口3引脚是PC10、PC11,需要手动去右侧的芯片引脚图上重新定义真正使用的引脚(PD8/PD9),这点在上一篇《CoreMark测试评分》中已经说到了
开启CRC,这个一定要开启,加解密库需要使用它
配置时钟树,还是把频率拉满
起个名字,生成Keil工程
3.3 加密库移植及Keil库配置
把ST加密库复制到工程中
打开Keil
切换AC6、开启MicroLib
重定向printf
- int fputc(int ch, FILE *f)
- {
- HAL_UART_Transmit(&huart3, (uint8_t *)&ch, 1, 0xffff);
- return ch;
- }
复制代码
开始移植加密库,新增一个组,添加加密库的lib和接口文件
cmox_low_level_template.c文件在STM32_Cryptographic\interface中
.a文件在STM32_Cryptographic\lib中,H735是使用的M7核,所以我们选择M7的那个文件。添加时默认的文件类型为.c,需要改为.a或者所有文件,才可以看到
添加完成
然后我们需要对这两个文件做一下处理
cmox_low_level_template.c要去除只读属性,否则我们无法修改其中内容
修改后文件角上的钥匙就不见了
.a文件:右键点击第一个选项,然后把文件类型修改为Library file。不然编译时会有报错
然后修改cmox_low_level_template.c文件内容,这里面就只有2个接口,一个是初始化,另一个是去初始化。
在初始化中需要把CRC进行初始化并启用,因为我们之前在cubemx中已经开启了CRC,所以在main函数中就已经有了CRC的初始化,因此这里我们就不需要做这个动作,直接把图中这两句话注释掉。
去初始化接口中本来是用于关闭CRC的(如果你程序的其他部分不再需要使用CRC的话),我们这儿就也不去动他了
因为没有调用其他的接口,所以头文件部分也不需要去补充hal库的名称,直接让他注释掉就好了
最后需要添加一下加密库头文件路径
到此为止,整个库就移植好了。
这个库使用起来也是非常的简单,只要调用一下初始化,就可以直接调用你想要的加解密接口函数即可
初始化函数如下
添加头文件
- if (cmox_initialize(NULL) != CMOX_INIT_SUCCESS)
- {
- printf("Crypto lib init fail\r\n");
- }
- else
- {
- printf("Crypto lib init success\r\n");
- }
复制代码
如果你不清楚各个加解密接口如何使用,在加密库压缩包中有很多的demo,虽然你手上可能没有一样的板卡可以直接运行代码,但是通过阅读代码并查看对应的注释,也可以侧面了解各个接口是如何使用的
3.4 AES CBC加解密测试
这部分我会测试一下使用AES ECB模式,使用128Bit秘钥和256bit秘钥对数据的加解密功能。
关于数据填充,由于AES从原理上讲是需要按16字节的整数倍进行计算的,从而诞生了很多填充方式,例如PCKS7、ZERO等。这些填充都是需要我们自己去填充好再去进行加密的(真心希望ST后续的加密库可以提供填充方式的参数,这样我就不需要自己在写一个填充函数了),我这里偷个懒,就直接把数据写成16字节的整数倍,这样就不需要填充了
定义加解密所需的变量
- /* 128bit秘钥 */
- const uint8_t Key_128bit[] =
- {
- 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
- };
- /* 256bit秘钥 */
- const uint8_t Key_256bit[] =
- {
- 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
- 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
- };
- /* 128bit秘钥加密前的明文 */
- const uint8_t Plaintext[] =
- {
- 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
- };
- /* 128bit秘钥加密后的秘文 */
- const uint8_t Ciphertext_128bitKey[] =
- {
- 0x27, 0x9F, 0xB7, 0x4A, 0x75, 0x72, 0x13, 0x5E, 0x8F, 0x9B, 0x8E, 0xF6, 0xD1, 0xEE, 0xE0, 0x03,
- };
- /* 256bit秘钥加密后的秘文 */
- const uint8_t Ciphertext_256bitKey[] =
- {
- 0x06, 0x39, 0xE8, 0x7D, 0x0E, 0x36, 0xC0, 0x1C, 0xAF, 0x88, 0x1B, 0x0B, 0x58, 0xF5, 0x00, 0x97,
- };
- /* 计算得到的秘文 */
- uint8_t Computed_Ciphertext[50];
- /* 计算得到的明文 */
- uint8_t Computed_Plaintext[50];
复制代码AES ECB加密测试函数
- void aes_ecb_encrypt_test(void)
- {
- cmox_cipher_retval_t ret;
- size_t computed_size;
- /******************************************************************************/
- /* AES 128bit 加密测试 */
- printf("AES ECB 128Bit encrypt test\r\n");
- ret = cmox_cipher_encrypt(CMOX_AES_ECB_ENC_ALGO,
- Plaintext, sizeof(Plaintext),
- Key_128bit, sizeof(Key_128bit),
- NULL, 0,
- Computed_Ciphertext, &computed_size);
- /* 加密是否成功 */
- if (ret != CMOX_CIPHER_SUCCESS)
- {
- printf("encrypt fail\r\n");
- return;
- }
- /* 打印加密结果 */
- printf("encrypt success\r\n");
- printf("encrypt data = ");
- for(int i = 0; i < computed_size; i++)
- {
- printf("%02X ", Computed_Ciphertext[i]);
- }
- printf("\r\n");
- /******************************************************************************/
- /* AES 256bit 加密测试 */
- printf("AES ECB 256Bit encrypt test\r\n");
- ret = cmox_cipher_encrypt(CMOX_AES_ECB_ENC_ALGO,
- Plaintext, sizeof(Plaintext),
- Key_256bit, sizeof(Key_256bit),
- NULL, 0,
- Computed_Ciphertext, &computed_size);
- /* 加密是否成功 */
- if (ret != CMOX_CIPHER_SUCCESS)
- {
- printf("encrypt fail\r\n");
- return;
- }
- /* 打印加密结果 */
- printf("encrypt success\r\n");
- printf("encrypt data = ");
- for(int i = 0; i < computed_size; i++)
- {
- printf("%02X ", Computed_Ciphertext[i]);
- }
- printf("\r\n");
- }
复制代码AES ECB解密测试函数
- void aes_ecb_dencrypt_test(void)
- {
- cmox_cipher_retval_t ret;
- size_t computed_size;
- /******************************************************************************/
- /* AES 128bit 解密测试 */
- printf("AES ECB 128Bit dencrypt test\r\n");
- ret = cmox_cipher_decrypt(CMOX_AES_ECB_DEC_ALGO,
- Ciphertext_128bitKey, sizeof(Ciphertext_128bitKey),
- Key_128bit, sizeof(Key_128bit),
- NULL, 0,
- Computed_Plaintext, &computed_size);
- /* 解密是否成功 */
- if (ret != CMOX_CIPHER_SUCCESS)
- {
- printf("dencrypt fail\r\n");
- return;
- }
- /* 打印解密结果 */
- printf("dencrypt success\r\n");
- printf("dencrypt data = ");
- for(int i = 0; i < computed_size; i++)
- {
- printf("%02x ", Computed_Plaintext[i]);
- }
- printf("\r\n");
- /******************************************************************************/
- /* AES 256bit 解密测试 */
- printf("AES ECB 256Bit dencrypt test\r\n");
- ret = cmox_cipher_decrypt(CMOX_AES_ECB_DEC_ALGO,
- Ciphertext_256bitKey, sizeof(Ciphertext_256bitKey),
- Key_256bit, sizeof(Key_256bit),
- NULL, 0,
- Computed_Plaintext, &computed_size);
- /* 解密是否成功 */
- if (ret != CMOX_CIPHER_SUCCESS)
- {
- printf("dencrypt fail\r\n");
- return;
- }
- /* 打印解密结果 */
- printf("dencrypt success\r\n");
- printf("dencrypt data = ");
- for(int i = 0; i < computed_size; i++)
- {
- printf("%02x ", Computed_Plaintext[i]);
- }
- printf("\r\n");
- }
复制代码把他们放入main运行一下看看
运行结果
两种秘钥的加解密结果都正确,和我用在线工具计算的一致
128bit秘钥加密结果
256bit秘钥加密结果
总结一下,AES使用的加解密接口分别是cmox_cipher_encrypt、cmox_cipher_decrypt。
他们的入参、返回值、注释如下
- /**
- * @brief Encrypt or decrypt a message using a symmetric cipher
- *
- * @param P_algo Identifier of the cipher algorithm to use for the computation.
- * This parameter can be one of the following:
- * @arg CMOX_AESFAST_ECB_ENC_ALGO
- * @arg CMOX_AESFAST_CBC_ENC_ALGO
- * @arg CMOX_AESFAST_CTR_ENC_ALGO
- * @arg CMOX_AESFAST_CFB_ENC_ALGO
- * @arg CMOX_AESFAST_OFB_ENC_ALGO
- * @arg CMOX_AESFAST_XTS_ENC_ALGO
- * @arg CMOX_AESSMALL_ECB_ENC_ALGO
- * @arg CMOX_AESSMALL_CBC_ENC_ALGO
- * @arg CMOX_AESSMALL_CTR_ENC_ALGO
- * @arg CMOX_AESSMALL_CFB_ENC_ALGO
- * @arg CMOX_AESSMALL_OFB_ENC_ALGO
- * @arg CMOX_AESSMALL_XTS_ENC_ALGO
- * @arg CMOX_AESSMALL_KEYWRAP_ENC_ALGO
- * @arg CMOX_SM4_ECB_ENC_ALGO
- * @arg CMOX_SM4_CBC_ENC_ALGO
- * @arg CMOX_SM4_CTR_ENC_ALGO
- * @arg CMOX_SM4_CFB_ENC_ALGO
- * @arg CMOX_SM4_OFB_ENC_ALGO
- * @param P_pInput Buffer of bytes containing the data to encrypt or decrypt
- * @param P_inputLen Length in bytes of the data to encrypt or decrypt
- * @param P_pKey Buffer of bytes containing the key
- * @param P_keyLen Length in bytes of the key
- * @param P_pIv Buffer of bytes containing the IV/nonce
- * @param P_ivLen Length in bytes of the key
- * @param P_pOutput Buffer of bytes where there will be stored the encrypted or
- * decrypted data
- * @param P_pOutputLen Number of bytes that have been processed by the function.
- * It is an optional parameter and can be set to NULL if not needed.
- * @return cmox_cipher_retval_t Cipher return value
- * @note This single call function cannot be used for AEAD ciphers
- */
- cmox_cipher_retval_t cmox_cipher_encrypt(cmox_cipher_algo_t P_algo,
- const uint8_t *P_pInput,
- size_t P_inputLen,
- const uint8_t *P_pKey,
- cmox_cipher_keyLen_t P_keyLen,
- const uint8_t *P_pIv,
- size_t P_ivLen,
- uint8_t *P_pOutput,
- size_t *P_pOutputLen);
复制代码- /**
- * @brief Decrypt a message using a symmetric cipher
- *
- * @param P_algo Identifier of the cipher algorithm to use for the computation.
- * This parameter can be one of the following:
- * @arg CMOX_AESFAST_ECB_DEC_ALGO
- * @arg CMOX_AESFAST_CBC_DEC_ALGO
- * @arg CMOX_AESFAST_CTR_DEC_ALGO
- * @arg CMOX_AESFAST_CFB_DEC_ALGO
- * @arg CMOX_AESFAST_OFB_DEC_ALGO
- * @arg CMOX_AESFAST_XTS_DEC_ALGO
- * @arg CMOX_AESFAST_KEYWRAP_DEC_ALGO
- * @arg CMOX_AESSMALL_ECB_DEC_ALGO
- * @arg CMOX_AESSMALL_CBC_DEC_ALGO
- * @arg CMOX_AESSMALL_CTR_DEC_ALGO
- * @arg CMOX_AESSMALL_CFB_DEC_ALGO
- * @arg CMOX_AESSMALL_OFB_DEC_ALGO
- * @arg CMOX_AESSMALL_XTS_DEC_ALGO
- * @arg CMOX_AESSMALL_KEYWRAP_DEC_ALGO
- * @arg CMOX_SM4_ECB_DEC_ALGO
- * @arg CMOX_SM4_CBC_DEC_ALGO
- * @arg CMOX_SM4_CTR_DEC_ALGO
- * @arg CMOX_SM4_CFB_DEC_ALGO
- * @arg CMOX_SM4_OFB_DEC_ALGO
- * @param P_pInput Buffer of bytes containing the data to encrypt or decrypt
- * @param P_inputLen Length in bytes of the data to encrypt or decrypt
- * @param P_pKey Buffer of bytes containing the key
- * @param P_keyLen Length in bytes of the key
- * @param P_pIv Buffer of bytes containing the IV/nonce
- * @param P_ivLen Length in bytes of the key
- * @param P_pOutput Buffer of bytes where there will be stored the decrypted
- * data.
- * @param P_pOutputLen Number of bytes that have been processed by the function.
- * It is an optional parameter and can be set to NULL if not needed.
- * @return cmox_cipher_retval_t Cipher return value
- * @note This single call function cannot be used for AEAD ciphers
- */
- cmox_cipher_retval_t cmox_cipher_decrypt(cmox_cipher_algo_t P_algo,
- const uint8_t *P_pInput,
- size_t P_inputLen,
- const uint8_t *P_pKey,
- cmox_cipher_keyLen_t P_keyLen,
- const uint8_t *P_pIv,
- size_t P_ivLen,
- uint8_t *P_pOutput,
- size_t *P_pOutputLen);
复制代码从注释可以看到,这两个函数不光可以给AES用,还可以给SM4用
简单解释一下各个入参,以加密接口为例,解密接口类似
P_algo:需要使用的加密类型,具体可以使用那些枚举,可以看注释
P_pInput:需要加密的内容(完成填充后的数据)
P_inputLen:需要加密的数据的长度(这个长度是已经完成填充后的数据的长度,我试过不填充,且长度不是16的整数倍,加密会返回失败)
P_pKey:秘钥
P_keyLen:秘钥的长度
P_pIv:IV的值(如果你的加密算法不需要使用IV,例如ECB,就填NULL)
P_ivLen:IV的长度(如果你的加密算法不需要使用IV,例如ECB,就填0)
P_pOutput:加密得到的数据
P_pOutputLen:加密得到的数据的长度
3.5 哈希SHA1计算测试 这里测试一下SHA1计算 用于哈希计算的原始数据就借用之前AES加密用的明文 用于测试的变量 - /* 计算得到的SHA1结果 */
- uint8_t Computed_hash[50];
复制代码
测试函数: - void hash_sha1_test(void)
- {
- cmox_hash_retval_t ret;
- size_t computed_size;
- ret = cmox_hash_compute(CMOX_SHA1_ALGO,
- Plaintext, sizeof(Plaintext),
- Computed_hash,
- CMOX_SHA1_SIZE,
- &computed_size);
- if (ret != CMOX_HASH_SUCCESS)
- {
- printf("hash compute fail\r\n");
- return;
- }
- /* 打印哈希计算结果 */
- printf("hash compute success\r\n");
- printf("hash compute result data = ");
- for(int i = 0; i < computed_size; i++)
- {
- printf("%02X ", Computed_hash[i]);
- }
- printf("\r\n");
- }
复制代码
测试结果
在线计算工具的计算结果
结果一致,测试成功
总结一下,哈希计算使用的接口为cmox_hash_compute。 他的入参、返回值、注释如下
- /**
- * @brief Compute the digest of a message using a hash algorithm
- *
- * @param P_algo Identifier of the hash algorithm to use for the computation.
- * This parameter can be one of the following:
- * @arg CMOX_SHA1_ALGO
- * @arg CMOX_SHA224_ALGO
- * @arg CMOX_SHA256_ALGO
- * @arg CMOX_SHA384_ALGO
- * @arg CMOX_SHA512_ALGO
- * @arg CMOX_SHA512_224_ALGO
- * @arg CMOX_SHA512_256_ALGO
- * @arg CMOX_SHA3_224_ALGO
- * @arg CMOX_SHA3_256_ALGO
- * @arg CMOX_SHA3_384_ALGO
- * @arg CMOX_SHA3_512_ALGO
- * @arg CMOX_SHAKE128_ALGO
- * @arg CMOX_SHAKE256_ALGO
- * @arg CMOX_SM3_ALGO
- * @param P_pPlaintext Buffer of bytes containing the message to hash
- * @param P_plaintextLen Size in bytes of the message to hash
- * @param P_pDigest Buffer of bytes that will contain the computed digest
- * @param P_expectedDigestLen Desired size in bytes of the digest to compute
- * @param P_pComputedDigestLen Number of bytes generated by the function.
- * It is an optional parameter and can be set to NULL if not needed.
- * @return cmox_hash_retval_t
- */
- cmox_hash_retval_t cmox_hash_compute(cmox_hash_algo_t P_algo,
- const uint8_t *P_pPlaintext,
- size_t P_plaintextLen,
- uint8_t *P_pDigest,
- const size_t P_expectedDigestLen,
- size_t *P_pComputedDigestLen);
复制代码简单解释一下各个入参
P_algo:哈希计算方式
P_pPlaintext:需要进行哈希计算的原始数据
P_plaintextLen:需要进行哈希计算的原始数据的长度
P_pDigest:哈希计算结果
P_expectedDigestLen:期待的长度(这个参数可以去STM32_Cryptographic/include/hash/cmox_XXXX.h文件中找,你用什么哈希计算就去那个文件找)
P_pComputedDigestLen:计算出的结果的长度
|