回答

收藏

[评测分享] 【STM32H735-DK 测评】③加密测试

#板卡评测 #板卡评测 2497 人阅读 | 0 人回复 | 2024-03-21

本帖最后由 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年,有截图为证


那个时候这个库还不是点击就可以下载,要先写申请,通过后才可以下载,现在进入网址就可以直接下载了(前提是你得注册并登陆,或者以游客身份填写信息下载),网址如下:https://www.st.com/content/st_com/zh/products/embedded-software/mcu-mpu-embedded-software/stm32-embedded-software/stm32cube-expansion-packages/x-cube-cryptolib.html




  这个加密库功能非常强大,几乎涵盖了我们平常能使用到的绝大部分加解密算法,只要你是使用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


  1. #include "stdio.h"
复制代码
  1. int fputc(int ch, FILE *f)
  2. {
  3.   HAL_UART_Transmit(&huart3, (uint8_t *)&ch, 1, 0xffff);
  4.   return ch;
  5. }
复制代码


开始移植加密库,新增一个组,添加加密库的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库的名称,直接让他注释掉就好了


最后需要添加一下加密库头文件路径


到此为止,整个库就移植好了。


这个库使用起来也是非常的简单,只要调用一下初始化,就可以直接调用你想要的加解密接口函数即可


初始化函数如下

添加头文件


  1. #include "cmox_crypto.h"
复制代码
加密库初始化


  1.   if (cmox_initialize(NULL) != CMOX_INIT_SUCCESS)
  2.   {
  3.     printf("Crypto lib init fail\r\n");
  4.   }
  5.   else
  6.   {
  7.     printf("Crypto lib init success\r\n");
  8.   }
复制代码


如果你不清楚各个加解密接口如何使用,在加密库压缩包中有很多的demo,虽然你手上可能没有一样的板卡可以直接运行代码,但是通过阅读代码并查看对应的注释,也可以侧面了解各个接口是如何使用的


3.4 AES CBC加解密测试

这部分我会测试一下使用AES ECB模式,使用128Bit秘钥和256bit秘钥对数据的加解密功能。

关于数据填充,由于AES从原理上讲是需要按16字节的整数倍进行计算的,从而诞生了很多填充方式,例如PCKS7、ZERO等。这些填充都是需要我们自己去填充好再去进行加密的(真心希望ST后续的加密库可以提供填充方式的参数,这样我就不需要自己在写一个填充函数了),我这里偷个懒,就直接把数据写成16字节的整数倍,这样就不需要填充了


定义加解密所需的变量

  1. /* 128bit秘钥 */
  2. const uint8_t Key_128bit[] =
  3. {
  4.   0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
  5. };

  6. /* 256bit秘钥 */
  7. const uint8_t Key_256bit[] =
  8. {
  9.   0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
  10.   0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
  11. };

  12. /* 128bit秘钥加密前的明文 */
  13. const uint8_t Plaintext[] =
  14. {
  15.   0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
  16. };

  17. /* 128bit秘钥加密后的秘文 */
  18. const uint8_t Ciphertext_128bitKey[] =
  19. {
  20.   0x27, 0x9F, 0xB7, 0x4A, 0x75, 0x72, 0x13, 0x5E, 0x8F, 0x9B, 0x8E, 0xF6, 0xD1, 0xEE, 0xE0, 0x03,
  21. };

  22. /* 256bit秘钥加密后的秘文 */
  23. const uint8_t Ciphertext_256bitKey[] =
  24. {
  25.   0x06, 0x39, 0xE8, 0x7D, 0x0E, 0x36, 0xC0, 0x1C, 0xAF, 0x88, 0x1B, 0x0B, 0x58, 0xF5, 0x00, 0x97,
  26. };

  27. /* 计算得到的秘文 */
  28. uint8_t Computed_Ciphertext[50];

  29. /* 计算得到的明文 */
  30. uint8_t Computed_Plaintext[50];
复制代码
AES ECB加密测试函数

  1. void aes_ecb_encrypt_test(void)
  2. {
  3.   cmox_cipher_retval_t ret;
  4.   size_t computed_size;

  5. /******************************************************************************/
  6.   /* AES 128bit 加密测试 */
  7.   printf("AES ECB 128Bit encrypt test\r\n");
  8.   ret = cmox_cipher_encrypt(CMOX_AES_ECB_ENC_ALGO,
  9.                                Plaintext, sizeof(Plaintext),
  10.                                Key_128bit, sizeof(Key_128bit),
  11.                                NULL, 0,
  12.                                Computed_Ciphertext, &computed_size);

  13.   /* 加密是否成功 */
  14.   if (ret != CMOX_CIPHER_SUCCESS)
  15.   {
  16.     printf("encrypt fail\r\n");
  17.     return;
  18.   }

  19.   /* 打印加密结果 */
  20.   printf("encrypt success\r\n");

  21.   printf("encrypt data = ");
  22.   for(int i = 0; i < computed_size; i++)  
  23.   {
  24.     printf("%02X ", Computed_Ciphertext[i]);
  25.   }
  26.   printf("\r\n");

  27. /******************************************************************************/
  28.   /* AES 256bit 加密测试 */
  29.   printf("AES ECB 256Bit encrypt test\r\n");
  30.   ret = cmox_cipher_encrypt(CMOX_AES_ECB_ENC_ALGO,
  31.                                Plaintext, sizeof(Plaintext),
  32.                                Key_256bit, sizeof(Key_256bit),
  33.                                NULL, 0,
  34.                                Computed_Ciphertext, &computed_size);

  35.   /* 加密是否成功 */
  36.   if (ret != CMOX_CIPHER_SUCCESS)
  37.   {
  38.     printf("encrypt fail\r\n");
  39.     return;
  40.   }

  41.   /* 打印加密结果 */
  42.   printf("encrypt success\r\n");

  43.   printf("encrypt data = ");
  44.   for(int i = 0; i < computed_size; i++)  
  45.   {
  46.     printf("%02X ", Computed_Ciphertext[i]);
  47.   }
  48.   printf("\r\n");
  49. }
复制代码
AES ECB解密测试函数

  1. void aes_ecb_dencrypt_test(void)
  2. {
  3.   cmox_cipher_retval_t ret;
  4.   size_t computed_size;

  5. /******************************************************************************/
  6.   /* AES 128bit 解密测试 */
  7.   printf("AES ECB 128Bit dencrypt test\r\n");
  8.   ret = cmox_cipher_decrypt(CMOX_AES_ECB_DEC_ALGO,
  9.                                Ciphertext_128bitKey, sizeof(Ciphertext_128bitKey),
  10.                                Key_128bit, sizeof(Key_128bit),
  11.                                NULL, 0,
  12.                                Computed_Plaintext, &computed_size);

  13.   /* 解密是否成功 */
  14.   if (ret != CMOX_CIPHER_SUCCESS)
  15.   {
  16.     printf("dencrypt fail\r\n");
  17.     return;
  18.   }


  19.   /* 打印解密结果 */
  20.   printf("dencrypt success\r\n");
  21.   printf("dencrypt data = ");
  22.   for(int i = 0; i < computed_size; i++)  
  23.   {
  24.     printf("%02x ", Computed_Plaintext[i]);
  25.   }
  26.   printf("\r\n");

  27. /******************************************************************************/
  28.   /* AES 256bit 解密测试 */
  29.   printf("AES ECB 256Bit dencrypt test\r\n");
  30.   ret = cmox_cipher_decrypt(CMOX_AES_ECB_DEC_ALGO,
  31.                                Ciphertext_256bitKey, sizeof(Ciphertext_256bitKey),
  32.                                Key_256bit, sizeof(Key_256bit),
  33.                                NULL, 0,
  34.                                Computed_Plaintext, &computed_size);

  35.   /* 解密是否成功 */
  36.   if (ret != CMOX_CIPHER_SUCCESS)
  37.   {
  38.     printf("dencrypt fail\r\n");
  39.     return;
  40.   }


  41.   /* 打印解密结果 */
  42.   printf("dencrypt success\r\n");
  43.   printf("dencrypt data = ");
  44.   for(int i = 0; i < computed_size; i++)  
  45.   {
  46.     printf("%02x ", Computed_Plaintext[i]);
  47.   }
  48.   printf("\r\n");

  49. }
复制代码
把他们放入main运行一下看看



运行结果


两种秘钥的加解密结果都正确,和我用在线工具计算的一致

128bit秘钥加密结果


256bit秘钥加密结果


总结一下,AES使用的加解密接口分别是cmox_cipher_encrypt、cmox_cipher_decrypt。

他们的入参、返回值、注释如下

  1. /**
  2.   * @brief Encrypt or decrypt a message using a symmetric cipher
  3.   *
  4.   * @param P_algo Identifier of the cipher algorithm to use for the computation.
  5.   *               This parameter can be one of the following:
  6.   *               @arg CMOX_AESFAST_ECB_ENC_ALGO
  7.   *               @arg CMOX_AESFAST_CBC_ENC_ALGO
  8.   *               @arg CMOX_AESFAST_CTR_ENC_ALGO
  9.   *               @arg CMOX_AESFAST_CFB_ENC_ALGO
  10.   *               @arg CMOX_AESFAST_OFB_ENC_ALGO
  11.   *               @arg CMOX_AESFAST_XTS_ENC_ALGO
  12.   *               @arg CMOX_AESSMALL_ECB_ENC_ALGO
  13.   *               @arg CMOX_AESSMALL_CBC_ENC_ALGO
  14.   *               @arg CMOX_AESSMALL_CTR_ENC_ALGO
  15.   *               @arg CMOX_AESSMALL_CFB_ENC_ALGO
  16.   *               @arg CMOX_AESSMALL_OFB_ENC_ALGO
  17.   *               @arg CMOX_AESSMALL_XTS_ENC_ALGO
  18.   *               @arg CMOX_AESSMALL_KEYWRAP_ENC_ALGO
  19.   *               @arg CMOX_SM4_ECB_ENC_ALGO
  20.   *               @arg CMOX_SM4_CBC_ENC_ALGO
  21.   *               @arg CMOX_SM4_CTR_ENC_ALGO
  22.   *               @arg CMOX_SM4_CFB_ENC_ALGO
  23.   *               @arg CMOX_SM4_OFB_ENC_ALGO
  24.   * @param P_pInput Buffer of bytes containing the data to encrypt or decrypt
  25.   * @param P_inputLen Length in bytes of the data to encrypt or decrypt
  26.   * @param P_pKey Buffer of bytes containing the key
  27.   * @param P_keyLen Length in bytes of the key
  28.   * @param P_pIv Buffer of bytes containing the IV/nonce
  29.   * @param P_ivLen  Length in bytes of the key
  30.   * @param P_pOutput Buffer of bytes where there will be stored the encrypted or
  31.   *                  decrypted data
  32.   * @param P_pOutputLen Number of bytes that have been processed by the function.
  33.   *        It is an optional parameter and can be set to NULL if not needed.
  34.   * @return cmox_cipher_retval_t Cipher return value
  35.   * @note This single call function cannot be used for AEAD ciphers
  36.   */
  37. cmox_cipher_retval_t cmox_cipher_encrypt(cmox_cipher_algo_t P_algo,
  38.                                          const uint8_t *P_pInput,
  39.                                          size_t P_inputLen,
  40.                                          const uint8_t *P_pKey,
  41.                                          cmox_cipher_keyLen_t P_keyLen,
  42.                                          const uint8_t *P_pIv,
  43.                                          size_t P_ivLen,
  44.                                          uint8_t *P_pOutput,
  45.                                          size_t *P_pOutputLen);
复制代码
  1. /**
  2.   * @brief Decrypt a message using a symmetric cipher
  3.   *
  4.   * @param P_algo Identifier of the cipher algorithm to use for the computation.
  5.   *               This parameter can be one of the following:
  6.   *               @arg CMOX_AESFAST_ECB_DEC_ALGO
  7.   *               @arg CMOX_AESFAST_CBC_DEC_ALGO
  8.   *               @arg CMOX_AESFAST_CTR_DEC_ALGO
  9.   *               @arg CMOX_AESFAST_CFB_DEC_ALGO
  10.   *               @arg CMOX_AESFAST_OFB_DEC_ALGO
  11.   *               @arg CMOX_AESFAST_XTS_DEC_ALGO
  12.   *               @arg CMOX_AESFAST_KEYWRAP_DEC_ALGO
  13.   *               @arg CMOX_AESSMALL_ECB_DEC_ALGO
  14.   *               @arg CMOX_AESSMALL_CBC_DEC_ALGO
  15.   *               @arg CMOX_AESSMALL_CTR_DEC_ALGO
  16.   *               @arg CMOX_AESSMALL_CFB_DEC_ALGO
  17.   *               @arg CMOX_AESSMALL_OFB_DEC_ALGO
  18.   *               @arg CMOX_AESSMALL_XTS_DEC_ALGO
  19.   *               @arg CMOX_AESSMALL_KEYWRAP_DEC_ALGO
  20.   *               @arg CMOX_SM4_ECB_DEC_ALGO
  21.   *               @arg CMOX_SM4_CBC_DEC_ALGO
  22.   *               @arg CMOX_SM4_CTR_DEC_ALGO
  23.   *               @arg CMOX_SM4_CFB_DEC_ALGO
  24.   *               @arg CMOX_SM4_OFB_DEC_ALGO
  25.   * @param P_pInput Buffer of bytes containing the data to encrypt or decrypt
  26.   * @param P_inputLen Length in bytes of the data to encrypt or decrypt
  27.   * @param P_pKey Buffer of bytes containing the key
  28.   * @param P_keyLen Length in bytes of the key
  29.   * @param P_pIv Buffer of bytes containing the IV/nonce
  30.   * @param P_ivLen  Length in bytes of the key
  31.   * @param P_pOutput Buffer of bytes where there will be stored the decrypted
  32.   *                  data.
  33.   * @param P_pOutputLen Number of bytes that have been processed by the function.
  34.   *        It is an optional parameter and can be set to NULL if not needed.
  35.   * @return cmox_cipher_retval_t Cipher return value
  36.   * @note This single call function cannot be used for AEAD ciphers
  37.   */
  38. cmox_cipher_retval_t cmox_cipher_decrypt(cmox_cipher_algo_t P_algo,
  39.                                          const uint8_t *P_pInput,
  40.                                          size_t P_inputLen,
  41.                                          const uint8_t *P_pKey,
  42.                                          cmox_cipher_keyLen_t P_keyLen,
  43.                                          const uint8_t *P_pIv,
  44.                                          size_t P_ivLen,
  45.                                          uint8_t *P_pOutput,
  46.                                          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加密用的明文
用于测试的变量
  1. /* 计算得到的SHA1结果 */
  2. uint8_t Computed_hash[50];
复制代码

测试函数:
  1. void hash_sha1_test(void)
  2. {
  3.   cmox_hash_retval_t ret;
  4.   size_t computed_size;

  5.   ret = cmox_hash_compute(CMOX_SHA1_ALGO,
  6.                              Plaintext, sizeof(Plaintext),
  7.                              Computed_hash,
  8.                              CMOX_SHA1_SIZE,
  9.                              &computed_size);

  10.   if (ret != CMOX_HASH_SUCCESS)
  11.   {
  12.     printf("hash compute fail\r\n");
  13.     return;
  14.   }

  15.   /* 打印哈希计算结果 */
  16.   printf("hash compute success\r\n");

  17.   printf("hash compute result data = ");
  18.   for(int i = 0; i < computed_size; i++)  
  19.   {
  20.     printf("%02X ", Computed_hash[i]);
  21.   }
  22.   printf("\r\n");

  23. }
复制代码


测试结果


在线计算工具的计算结果



结果一致,测试成功


总结一下,哈希计算使用的接口为cmox_hash_compute。
他的入参、返回值、注释如下

  1. /**
  2.   * @brief Compute the digest of a message using a hash algorithm
  3.   *
  4.   * @param P_algo Identifier of the hash algorithm to use for the computation.
  5.   *               This parameter can be one of the following:
  6.   *               @arg CMOX_SHA1_ALGO
  7.   *               @arg CMOX_SHA224_ALGO
  8.   *               @arg CMOX_SHA256_ALGO
  9.   *               @arg CMOX_SHA384_ALGO
  10.   *               @arg CMOX_SHA512_ALGO
  11.   *               @arg CMOX_SHA512_224_ALGO
  12.   *               @arg CMOX_SHA512_256_ALGO
  13.   *               @arg CMOX_SHA3_224_ALGO
  14.   *               @arg CMOX_SHA3_256_ALGO
  15.   *               @arg CMOX_SHA3_384_ALGO
  16.   *               @arg CMOX_SHA3_512_ALGO
  17.   *               @arg CMOX_SHAKE128_ALGO
  18.   *               @arg CMOX_SHAKE256_ALGO
  19.   *               @arg CMOX_SM3_ALGO
  20.   * @param P_pPlaintext Buffer of bytes containing the message to hash
  21.   * @param P_plaintextLen Size in bytes of the message to hash
  22.   * @param P_pDigest Buffer of bytes that will contain the computed digest
  23.   * @param P_expectedDigestLen Desired size in bytes of the digest to compute
  24.   * @param P_pComputedDigestLen Number of bytes generated by the function.
  25.   *        It is an optional parameter and can be set to NULL if not needed.
  26.   * @return cmox_hash_retval_t
  27.   */
  28. cmox_hash_retval_t cmox_hash_compute(cmox_hash_algo_t P_algo,
  29.                                      const uint8_t *P_pPlaintext,
  30.                                      size_t P_plaintextLen,
  31.                                      uint8_t *P_pDigest,
  32.                                      const size_t P_expectedDigestLen,
  33.                                      size_t *P_pComputedDigestLen);
复制代码
简单解释一下各个入参

P_algo:哈希计算方式

P_pPlaintext:需要进行哈希计算的原始数据

P_plaintextLen:需要进行哈希计算的原始数据的长度

P_pDigest:哈希计算结果

P_expectedDigestLen:期待的长度(这个参数可以去STM32_Cryptographic/include/hash/cmox_XXXX.h文件中找,你用什么哈希计算就去那个文件找)

P_pComputedDigestLen:计算出的结果的长度

CryptoTest.part03.rar

7.67 MB, 下载次数: 0

CryptoTest.part01.rar

20 MB, 下载次数: 0

CryptoTest.part02.rar

20 MB, 下载次数: 0

分享到:
回复

使用道具 举报

您需要登录后才可以回帖 注册/登录

本版积分规则

关闭

站长推荐上一条 /3 下一条