ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

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

2021-07-09 15:02:20  阅读:193  来源: 互联网

标签:md 认证码 05 ctx mbedtls param 算法 cipher MBEDTLS


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中指定使用的配置头文件:

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

编译、下载、测试结果如图:

三、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中配置:

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

编译、下载,测试结果为:


 

标签:md,认证码,05,ctx,mbedtls,param,算法,cipher,MBEDTLS
来源: https://blog.51cto.com/u_13640625/3027834

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有