- mbedtls | 01 - 移植mbedtls库到STM32的两种方法
- mbedtls | 02 - 伪随机数生成器(ctr_drbg)的配置与使用
- mbedtls | 03 - 单向散列算法的配置与使用(MD5、SHA1、SHA256、SHA512)
- mbedtls | 04 - 对称加密算法的配置与使用(AES算法)
- https://github.com/Mculover666/mbedtls-study-demo
本工程基于STM32L41RCT6开发板,包含了本系列文章中所编写的所有Demo,持续更新……
文章目录
- mbedtls系列文章
- Demo工程源码
- 一、消息认证码
-
- 1. 什么是消息认证码
- 2. 消息认证码的实现方式
-
- 2.1. 基于单向散列算法实现
- 2.2. 基于分组加密算法实现
- 2.3. 认证加密算法实现
- 3. mbedtls中的消息认证码实现
- 二、HMAC功能模块的配置与使用
-
- 1. 配置宏
- 2. HMAC功能模块API说明
- 3. 编写测试函数
- 4. 测试结果
- 三、GCM功能模块的配置与使用
-
- 1. 配置宏
- 2. GCM功能模块相关的API说明
- 3. 编写测试代码
- 4. 测试结果
一、消息认证码
1. 什么是消息认证码
消息认证码(Message Authentication Code)可用来检查消息的完整性和消息来源的可靠性。
消息认证码算法的输入为任意长度的消息和通信双方共享的秘钥,消息认证码的输出为固定长度的数据,该输出数据称为MAC值、Tag值、T值。
2. 消息认证码的实现方式
2.1. 基于单向散列算法实现
这类实现称为HMAC,比如HMAC-SHA1、HMAC-SHA256等。因为单向散列算法无法验证消息来源的可靠性,所以不能直接用于生成消息认证码。
另外,在HMAC算法中,通信双方共享秘钥没有长度限制,明文也没有长度限制,但是最后生成的消息认证码长度是固定的。
比如基于SHA256算法的HAMC算法,因为SHA256计算出的消息摘要是256字节,32个字节,所以最后生成的消息认证码长度也是32个字节固定长度。
2.2. 基于分组加密算法实现
比如将CBC分组加密结果的最后一个分组作为消息认证码的CBC-MAC算法。
还有通过共享秘钥派生出两个中间秘钥的CMAC算法,安全性更高。
2.3. 认证加密算法实现
认证加密算法在通信过程中提供数据机密性和完整性的密码算法,是对称加密算法(eg. AES)和消息认证码的结合,典型实现包括GCM、CCM等。
CCM认证加密过程对明文进行两次处理,第一次使用CBC-MAC计算消息认证码,第二次使用CRT模式将消息认证码(明文)加密。
GCM认证加密过程和CCM类似,只不过第一次计算使用的是GHASH算法,第二次计算使用的是GCTR算法。
另外,GCM的消息认证码长度只能为16字节,而CCM模式的消息认证码长度最小为4字节、最大为16字节。
3. mbedtls中的消息认证码实现
mbedtls中提供了HMAC计算接口和GCM计算接口。
二、HMAC功能模块的配置与使用1. 配置宏
开启一种单向散列函数功能和MD通用接口即可:
宏定义 | 说明 |
---|---|
MBEDTLS_MD_C | 开启MD通用接口 |
MBEDTLS_SHA256_C | 开启SHA256单向散列算法 |
新建一个针对本实验的配置文件mbedtls_config_hmac.h:
/** * @brief Minimal configuration for HMAC Function * @author mculover666 * @date 2020/09/26 */ #ifndef _MBEDTLS_CONFIG_SHA_X_H_ #define _MBEDTLS_CONFIG_SHA_X_H_ /* System support */ #define MBEDTLS_HAVE_ASM //#define MBEDTLS_HAVE_TIME /* mbed feature support */ #define MBEDTLS_NO_PLATFORM_ENTROPY /* mbed modules */ //#define MBEDTLS_MD2_C //#define MBEDTLS_MD4_C //#define MBEDTLS_MD5_C //#define MBEDTLS_SHA1_C //#define MBEDTLS_SHA224_C #define MBEDTLS_SHA256_C //#define MBEDTLS_SHA384_C //#define MBEDTLS_SHA512_C /* enable generic message digest wrappers */ #define MBEDTLS_MD_C #include "mbedtls/check_config.h" #endif /* _MBEDTLS_CONFIG_SHA_X_H_ */
在MDK中指定使用的配置头文件:
2. HMAC功能模块API说明
① hmac启动接口(完成秘钥、ipad、opad计算):
/** * \brief This function sets the HMAC key and prepares to * authenticate a new message. * * Call this function after mbedtls_md_setup(), to use * the MD context for an HMAC calculation, then call * mbedtls_md_hmac_update() to provide the input data, and * mbedtls_md_hmac_finish() to get the HMAC value. * * \param ctx The message digest context containing an embedded HMAC * context. * \param key The HMAC secret key. * \param keylen The length of the HMAC key in Bytes. * * \return \c 0 on success. * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter-verification * failure. */ int mbedtls_md_hmac_starts( mbedtls_md_context_t *ctx, const unsigned char *key, size_t keylen );
② hmac更新接口(处理输入数据):
/** * \brief This function feeds an input buffer into an ongoing HMAC * computation. * * Call mbedtls_md_hmac_starts() or mbedtls_md_hmac_reset() * before calling this function. * You may call this function multiple times to pass the * input piecewise. * Afterwards, call mbedtls_md_hmac_finish(). * * \param ctx The message digest context containing an embedded HMAC * context. * \param input The buffer holding the input data. * \param ilen The length of the input data. * * \return \c 0 on success. * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter-verification * failure. */ int mbedtls_md_hmac_update( mbedtls_md_context_t *ctx, const unsigned char *input, size_t ilen );
③ hmac完成接口(输出消息认证码):
/** * \brief This function finishes the HMAC operation, and writes * the result to the output buffer. * * Call this function after mbedtls_md_hmac_starts() and * mbedtls_md_hmac_update() to get the HMAC value. Afterwards * you may either call mbedtls_md_free() to clear the context, * or call mbedtls_md_hmac_reset() to reuse the context with * the same HMAC key. * * \param ctx The message digest context containing an embedded HMAC * context. * \param output The generic HMAC checksum result. * * \return \c 0 on success. * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter-verification * failure. */ int mbedtls_md_hmac_finish( mbedtls_md_context_t *ctx, unsigned char *output);
④ 错误码:
#define MBEDTLS_ERR_MD_FEATURE_UNAVAILABLE -0x5080 /**< The selected feature is not available. */ #define MBEDTLS_ERR_MD_BAD_INPUT_DATA -0x5100 /**< Bad input parameters to function. */ #define MBEDTLS_ERR_MD_ALLOC_FAILED -0x5180 /**< Failed to allocate memory. */ #define MBEDTLS_ERR_MD_FILE_IO_ERROR -0x5200 /**< Opening or reading of file failed. */ /* MBEDTLS_ERR_MD_HW_ACCEL_FAILED is deprecated and should not be used. */ #define MBEDTLS_ERR_MD_HW_ACCEL_FAILED -0x5280 /**< MD hardware accelerator failed. */
特别注意,与计算单向散列值不同,在计算消息认证码的时候要在设置时表明使用HMAC,也就是 mbedtls_md_setup 函数的第三个参数要设置为非零值,通常设为1即可:
/** * \brief This function selects the message digest algorithm to use, * and allocates internal structures. * * It should be called after mbedtls_md_init() or * mbedtls_md_free(). Makes it necessary to call * mbedtls_md_free() later. * * \param ctx The context to set up. * \param md_info The information structure of the message-digest algorithm * to use. * \param hmac Defines if HMAC is used. 0: HMAC is not used (saves some memory), * or non-zero: HMAC is used with this context. * * \return \c 0 on success. * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter-verification * failure. * \return #MBEDTLS_ERR_MD_ALLOC_FAILED on memory-allocation failure. */ int mbedtls_md_setup( mbedtls_md_context_t *ctx, const mbedtls_md_info_t *md_info, int hmac );
3. 编写测试函数
新建文件mbedtls_hmac_test.c,编写如下测试函数:
/** * @brief HMAC-SHA256 Function demo * @author mculover666 * @date 2020/09/26 */ #if !defined(MBEDTLS_CONFIG_FILE) #include "mbedtls/config.h" #else #include MBEDTLS_CONFIG_FILE #endif #if defined(MBEDTLS_MD_C) #include <stdio.h> #include "string.h" #include "mbedtls/md.h" int mbedtls_hmac_test(mbedtls_md_type_t md_type) { int len, i; int ret; const char *key = "mculover666"; const char *message = "hello world"; unsigned char hmac[32]; mbedtls_md_context_t ctx; const mbedtls_md_info_t *info; printf("message is:%s\r\n", message); /* 1. init mbedtls_md_context_t structure */ mbedtls_md_init(&ctx); /* 2. get md info structure pointer */ info = mbedtls_md_info_from_type(md_type); /* 3. setup md info structure */ ret = mbedtls_md_setup(&ctx, info, 1); if (ret != 0) { goto exit; } /* 4. start */ ret = mbedtls_md_hmac_starts(&ctx, (unsigned char *)key, strlen(key)); if (ret != 0) { goto exit; } /* 5. update */ ret = mbedtls_md_hmac_update(&ctx, (unsigned char *)message, strlen(message)); if (ret != 0) { goto exit; } /* 6. finish */ ret = mbedtls_md_hmac_finish(&ctx, hmac); if (ret != 0) { goto exit; } /* show */ printf("%s hmac context is:[", mbedtls_md_get_name(info)); len= mbedtls_md_get_size(info); for (i = 0; i < len; i++) { printf("%02x", hmac[i]); } printf("]\r\n"); exit: /* 7. free */ mbedtls_md_free(&ctx); return ret; } #endif /* MBEDTLS_MD_C */
4. 测试结果
在main.c中声明该测试函数:
/* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ #include "mbedtls/md.h" int mbedtls_hmac_test(mbedtls_md_type_t md_type); /* USER CODE END 0 */
然后在main函数中调用:
/* 3. hamc test */ mbedtls_hmac_test(MBEDTLS_MD_SHA256);
编译、下载、测试结果如图:
1. 配置宏
开启AES算法功能模块、cipher通用接口、GCM模式即可:
宏定义 | 说明 |
---|---|
MBEDTLS_AES_C | 开启AES算法 |
MBEDTLS_AES_ROM_TABLES | 开启预定义S盒 |
MBEDTLS_GCM_C | 开启GCM模式 |
其它三个宏在讲解AES算法时已详细说明,下面看看GCM模式相关的宏:
/** * \def MBEDTLS_GCM_C * * Enable the Galois/Counter Mode (GCM) for AES. * * Module: library/gcm.c * * Requires: MBEDTLS_AES_C or MBEDTLS_CAMELLIA_C * * This module enables the AES-GCM and CAMELLIA-GCM ciphersuites, if other * requisites are enabled as well. */ #define MBEDTLS_GCM_C
编写针对本实验的配置文件mbedtls_config_gcm.h:
/** * @brief Minimal configuration for GCM Function * @author mculover666 * @date 2020/09/26 */ #ifndef _MBEDTLS_CONFIG_GCM_H_ #define _MBEDTLS_CONFIG_GCM_H_ /* System support */ #define MBEDTLS_HAVE_ASM //#define MBEDTLS_HAVE_TIME /* mbed feature support */ #define MBEDTLS_NO_PLATFORM_ENTROPY /* mbed modules */ #define MBEDTLS_AES_C #define MBEDTLS_AES_ROM_TABLES #define MBEDTLS_CIPHER_C #define MBEDTLS_GCM_C #include "mbedtls/check_config.h" #endif /* _MBEDTLS_CONFIG_GCM_H_ */
在MDK中配置:
2. GCM功能模块相关的API说明
① cipher认证加密:
/** * \brief The generic autenticated encryption (AEAD) function. * * \param ctx The generic cipher context. This must be initialized and * bound to a key. * \param iv The IV to use, or NONCE_COUNTER for CTR-mode ciphers. * This must be a readable buffer of at least \p iv_len * Bytes. * \param iv_len The IV length for ciphers with variable-size IV. * This parameter is discarded by ciphers with fixed-size IV. * \param ad The additional data to authenticate. This must be a * readable buffer of at least \p ad_len Bytes. * \param ad_len The length of \p ad. * \param input The buffer holding the input data. This must be a * readable buffer of at least \p ilen Bytes. * \param ilen The length of the input data. * \param output The buffer for the output data. This must be able to * hold at least \p ilen Bytes. * \param olen The length of the output data, to be updated with the * actual number of Bytes written. This must not be * \c NULL. * \param tag The buffer for the authentication tag. This must be a * writable buffer of at least \p tag_len Bytes. * \param tag_len The desired length of the authentication tag. * * \return \c 0 on success. * \return #MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA on * parameter-verification failure. * \return A cipher-specific error code on failure. */ int mbedtls_cipher_auth_encrypt( mbedtls_cipher_context_t *ctx, const unsigned char *iv, size_t iv_len, const unsigned char *ad, size_t ad_len, const unsigned char *input, size_t ilen, unsigned char *output, size_t *olen, unsigned char *tag, size_t tag_len );
② cipher认证解密
/** * \brief The generic autenticated decryption (AEAD) function. * * \note If the data is not authentic, then the output buffer * is zeroed out to prevent the unauthentic plaintext being * used, making this interface safer. * * \param ctx The generic cipher context. This must be initialized and * and bound to a key. * \param iv The IV to use, or NONCE_COUNTER for CTR-mode ciphers. * This must be a readable buffer of at least \p iv_len * Bytes. * \param iv_len The IV length for ciphers with variable-size IV. * This parameter is discarded by ciphers with fixed-size IV. * \param ad The additional data to be authenticated. This must be a * readable buffer of at least \p ad_len Bytes. * \param ad_len The length of \p ad. * \param input The buffer holding the input data. This must be a * readable buffer of at least \p ilen Bytes. * \param ilen The length of the input data. * \param output The buffer for the output data. * This must be able to hold at least \p ilen Bytes. * \param olen The length of the output data, to be updated with the * actual number of Bytes written. This must not be * \c NULL. * \param tag The buffer holding the authentication tag. This must be * a readable buffer of at least \p tag_len Bytes. * \param tag_len The length of the authentication tag. * * \return \c 0 on success. * \return #MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA on * parameter-verification failure. * \return #MBEDTLS_ERR_CIPHER_AUTH_FAILED if data is not authentic. * \return A cipher-specific error code on failure. */ int mbedtls_cipher_auth_decrypt( mbedtls_cipher_context_t *ctx, const unsigned char *iv, size_t iv_len, const unsigned char *ad, size_t ad_len, const unsigned char *input, size_t ilen, unsigned char *output, size_t *olen, const unsigned char *tag, size_t tag_len );
3. 编写测试代码
新建文件mbedtls_config_gcm.c,编写测试代码:
/** * @brief GCM Function demo * @author mculover666 * @date 2020/09/26 */ #if !defined(MBEDTLS_CONFIG_FILE) #include "mbedtls/config.h" #else #include MBEDTLS_CONFIG_FILE #endif #if defined(MBEDTLS_GCM_C) #include <stdio.h> #include <string.h> #include <stdint.h> #include "mbedtls/cipher.h" /* source context */ static const char input[16] = { 0xc3, 0xb3, 0xc4, 0x1f, 0x11, 0x3a, 0x31, 0xb7, 0x3d, 0x9a, 0x5c, 0xd4, 0x32, 0x10, 0x30, 0x69 }; /* Private Key */ static uint8_t key[16] = { 0xc9, 0x39, 0xcc, 0x13, 0x39, 0x7c, 0x1d, 0x37, 0xde, 0x6a, 0xe0, 0xe1, 0xcb, 0x7c, 0x42, 0x3c }; /* Intialization Vector */ static uint8_t iv[12] = { 0xb3, 0xd8, 0xcc, 0x01, 0x7c, 0xbb, 0x89, 0xb3, 0x9e, 0x0f, 0x67, 0xe2 }; /* The additional data to authenticate */ uint8_t add[16] = { 0x24, 0x82, 0x56, 0x02, 0xbd, 0x12, 0xa9, 0x84, 0xe0, 0x09, 0x2d, 0x3e, 0x44, 0x8e, 0xda, 0x5f }; static void dump_buf(uint8_t *buf, uint32_t len) { int i; for (i = 0; i < len; i++) { printf("%s%02X%s", i % 16 == 0 ? "\r\n\t" : " ", buf[i], i == len - 1 ? "\r\n" : ""); } } int gcm_test(mbedtls_cipher_type_t cipher_type) { int ret; size_t len; int olen = 0; uint8_t output_buf[16]; uint8_t tag_buf[16]; uint8_t decrypt_out_buf[16]; mbedtls_cipher_context_t ctx; const mbedtls_cipher_info_t *info; /* 1. init cipher structuer */ mbedtls_cipher_init(&ctx); /* 2. get info structuer from type */ info = mbedtls_cipher_info_from_type(cipher_type); /* 3. setup cipher structuer */ ret = mbedtls_cipher_setup(&ctx, info); if (ret != 0) { goto exit; } /* 4. set encrypt key */ ret = mbedtls_cipher_setkey(&ctx, key, sizeof(key) * 8, MBEDTLS_ENCRYPT); if (ret != 0) { goto exit; } /* 5. auth encrypt */ ret = mbedtls_cipher_auth_encrypt(&ctx, iv, sizeof(iv), add, sizeof(add), (unsigned char *)input, sizeof(input), output_buf, &len, tag_buf, sizeof(tag_buf)); if (ret != 0) { goto exit; } olen += len; /* show */ printf("cipher name:%s block size is:%d\r\n", mbedtls_cipher_get_name(&ctx), mbedtls_cipher_get_block_size(&ctx)); printf("\r\noutput_buf:\r\n"); dump_buf((uint8_t *)output_buf, olen); printf("\r\ntag_buf:\r\n"); dump_buf(tag_buf, sizeof(tag_buf)); /* 6. set decrypt key */ ret = mbedtls_cipher_setkey(&ctx, key, sizeof(key) * 8, MBEDTLS_DECRYPT); if (ret != 0) { goto exit; } /* 7. auth decrypt */ olen = 0; len = 0; ret = mbedtls_cipher_auth_decrypt(&ctx, iv, sizeof(iv), add, sizeof(add), (unsigned char *)output_buf, sizeof(output_buf), decrypt_out_buf, &len, tag_buf, sizeof(tag_buf)); if (ret != 0) { goto exit; } olen += len; /* show */ printf("cipher name:%s block size is:%d\r\n", mbedtls_cipher_get_name(&ctx), mbedtls_cipher_get_block_size(&ctx)); printf("\r\ndecrypt_out_buf:\r\n"); dump_buf((uint8_t *)decrypt_out_buf, olen); printf("\r\ntag_buf:\r\n"); dump_buf(tag_buf, sizeof(tag_buf)); exit: /* 8. free cipher structure */ mbedtls_cipher_free(&ctx); return ret; } #endif /* MBEDTLS_CIPHER_C */
4. 测试结果
在main.c 中声明该外部函数:
/* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ #include "mbedtls/cipher.h" int gcm_test(mbedtls_cipher_type_t cipher_type); /* USER CODE END 0 */
接着在main函数中调用测试函数:
/* 4.gcm test */ gcm_test(MBEDTLS_CIPHER_AES_128_GCM);
编译、下载,测试结果为: