mbedtls | 05 - 消息认证码的配置与使用(HMAC算法、GCM算法)

mbedtls系列文章
  • mbedtls | 01 - 移植mbedtls库到STM32的两种方法
  • mbedtls | 02 - 伪随机数生成器(ctr_drbg)的配置与使用
  • mbedtls | 03 - 单向散列算法的配置与使用(MD5、SHA1、SHA256、SHA512)
  • mbedtls | 04 - 对称加密算法的配置与使用(AES算法)
Demo工程源码
  • 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中指定使用的配置头文件:
mbedtls | 05 - 消息认证码的配置与使用(HMAC算法、GCM算法)

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);

编译、下载、测试结果如图:
mbedtls | 05 - 消息认证码的配置与使用(HMAC算法、GCM算法)

三、GCM功能模块的配置与使用

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中配置:
mbedtls | 05 - 消息认证码的配置与使用(HMAC算法、GCM算法)

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);

编译、下载,测试结果为:
mbedtls | 05 - 消息认证码的配置与使用(HMAC算法、GCM算法)

mbedtls | 05 - 消息认证码的配置与使用(HMAC算法、GCM算法)

 

上一篇:用友NCC产品API使用指南


下一篇:年会摇奖小程序