mbedtls系列文章
  • mbedtls | 01 - 移植mbedtls库到STM32的两种方法
  • mbedtls | 02 - 伪随机数生成器(ctr_drbg)的配置与使用
  • mbedtls | 03 - 单向散列算法的配置与使用(MD5、SHA1、SHA256、SHA512)
  • mbedtls | 04 - 对称加密算法的配置与使用(AES算法)
  • mbedtls | 05 - 消息认证码的配置与使用(HMAC算法、GCM算法)
  • mbedtls | 06 - 非对称加密算法的配置与使用(RSA算法)
  • mbedtls | 07 - DH秘钥协商算法的配置与使用
  • mbedtls | 08 - ECDH秘钥协商算法的配置与使用
Demo工程源码
  • https://github.com/Mculover666/mbedtls-study-demo

本工程基于STM32L41RCT6开发板,包含了本系列文章中所编写的所有Demo,持续更新……


文章目录

  • mbedtls系列文章
  • Demo工程源码
  • 一、数字签名算法
    • 1. 什么是数字签名
    • 2. 数字签名算法
  • 二、RSA数字签名功能模块的配置与使用
    • 1. 配置宏
    • 2. RSA数字签名功能模块API说明
    • 3. 编写测试函数
    • 4. 测试结果
  • 三、ECDSA数字签名功能模块的配置与使用
    • 1. 配置宏
    • 2. ECDSA签名功能API说明
    • 3. 编写测试函数
    • 4. 测试结果


一、数字签名算法

1. 什么是数字签名

数字签名类似于盖章和签字,数字签名可以检查消息是否被篡改、并验证消息的可靠性,因为私钥只有签名者持有,所以还可以防止否认。

2. 数字签名算法

RSA数字签名

RSA数字签名算法基于RSA非对称加密算法,在用于数字签名时公钥和私钥的用法刚好相反:

  • 发送方使用私钥对消息执行加密操作 = 对消息进行签名;
  • 接收方使用公钥对签名进行解密 = 对签名进行认证;

RSA签名算法还需要包括填充方法,有两种:PKCS1-V1.5和PSS(使用随机数填充,推荐使用)。

DSA算法

ECDSA算法

在相同的安全等级下,ECDSA算法具有秘钥短、执行效率高的特点,更适合物联网应用。

二、RSA数字签名功能模块的配置与使用

1. 配置宏

使用RSA功能需要提前开启伪随机数生成器(依赖AES算法、SHA256算法、MD通用接口),其中伪随机数生成器的硬件适配移植实现已经讲述,请参考对应的第二篇博客,不再赘述。

综合上述,本实验中需要开启的宏如下表:

宏定义 功能
MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES 不使用默认熵源(如果已有、要屏蔽该宏)
MBEDTLS_NO_PLATFORM_ENTROPY 不使用系统内置熵源
MBEDTLS_AES_C 使用AES算法
MBEDTLS_ENTROPY_C 使能熵源模块
MBEDTLS_CTR_DRBG_C 使能随机数模块
MBEDTLS_SHA256_C 使能SHA256算法
MBEDTLS_MD_C 开启MD通用接口
MBEDTLS_AES_ROM_TABLES 使能预定义S盒(节约内存空间)
MBEDTLS_BIGNUM_C 开启大数运算
MBEDTLS_GENPRIME 开启生成素数
MBEDTLS_OID_C 开启OID数据结构模块
MBEDTLS_RSA_C 开启RSA算法
MBEDTLS_PKCS1_V21 开启PKCS#1 v2.1填充方案(OAEP)

新建配置文件mbedtls_config_rsa_sign.h,编写以下内容:

/**
 * @brief   Minimal configuration for RSA Sign Function
 * @author  mculover666
 * @date    2020/10/03
*/

#ifndef _MBEDTLS_CONFIG_RSA_SIGN_H_
#define _MBEDTLS_CONFIG_RSA_SIGN_H_

/* System support */
#define MBEDTLS_HAVE_ASM
//#define MBEDTLS_HAVE_TIME

/* mbed feature support */
#define MBEDTLS_ENTROPY_HARDWARE_ALT
//#define MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES
#define MBEDTLS_NO_PLATFORM_ENTROPY

/* mbed modules */
#define MBEDTLS_AES_C
#define MBEDTLS_AES_ROM_TABLES
#define MBEDTLS_CTR_DRBG_C
#define MBEDTLS_ENTROPY_C
#define MBEDTLS_SHA256_C
#define MBEDTLS_MD_C
#define MBEDTLS_BIGNUM_C
#define MBEDTLS_GENPRIME
#define MBEDTLS_OID_C
#define MBEDTLS_RSA_C
#define MBEDTLS_PKCS1_V21

#include "mbedtls/check_config.h"

#endif /* _MBEDTLS_CONFIG_RSA_SIGN_H_ */

在MDK中设置使用该配置文件:
mbedtls | 09 - 数字签名算法的配置与使用(RSA数字签名算法、ECDSA数字签名算法)_mbedtls

2. RSA数字签名功能模块API说明

使用该功能模块需要包含头文件:

#include "mbedtls/rsa.h"

① RSA签名接口

/**
 * \brief          This function performs a private RSA operation to sign
 *                 a message digest using PKCS#1.
 *
 *                 It is the generic wrapper for performing a PKCS#1
 *                 signature using the \p mode from the context.
 *
 * \note           The \p sig buffer must be as large as the size
 *                 of \p ctx->N. For example, 128 Bytes if RSA-1024 is used.
 *
 * \note           For PKCS#1 v2.1 encoding, see comments on
 *                 mbedtls_rsa_rsassa_pss_sign() for details on
 *                 \p md_alg and \p hash_id.
 *
 * \deprecated     It is deprecated and discouraged to call this function
 *                 in #MBEDTLS_RSA_PUBLIC mode. Future versions of the library
 *                 are likely to remove the \p mode argument and have it
 *                 implicitly set to #MBEDTLS_RSA_PRIVATE.
 *
 * \note           Alternative implementations of RSA need not support
 *                 mode being set to #MBEDTLS_RSA_PUBLIC and might instead
 *                 return #MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED.
 *
 * \param ctx      The initialized RSA context to use.
 * \param f_rng    The RNG function to use. If the padding mode is PKCS#1 v2.1,
 *                 this must be provided. If the padding mode is PKCS#1 v1.5 and
 *                 \p mode is #MBEDTLS_RSA_PRIVATE, it is used for blinding
 *                 and should be provided; see mbedtls_rsa_private() for more
 *                 more. It is ignored otherwise.
 * \param p_rng    The RNG context to be passed to \p f_rng. This may be \c NULL
 *                 if \p f_rng is \c NULL or doesn't need a context argument.
 * \param mode     The mode of operation. This must be either
 *                 #MBEDTLS_RSA_PRIVATE or #MBEDTLS_RSA_PUBLIC (deprecated).
 * \param md_alg   The message-digest algorithm used to hash the original data.
 *                 Use #MBEDTLS_MD_NONE for signing raw data.
 * \param hashlen  The length of the message digest.
 *                 Ths is only used if \p md_alg is #MBEDTLS_MD_NONE.
 * \param hash     The buffer holding the message digest or raw data.
 *                 If \p md_alg is #MBEDTLS_MD_NONE, this must be a readable
 *                 buffer of length \p hashlen Bytes. If \p md_alg is not
 *                 #MBEDTLS_MD_NONE, it must be a readable buffer of length
 *                 the size of the hash corresponding to \p md_alg.
 * \param sig      The buffer to hold the signature. This must be a writable
 *                 buffer of length \c ctx->len Bytes. For example, \c 256 Bytes
 *                 for an 2048-bit RSA modulus. A buffer length of
 *                 #MBEDTLS_MPI_MAX_SIZE is always safe.
 *
 * \return         \c 0 if the signing operation was successful.
 * \return         An \c MBEDTLS_ERR_RSA_XXX error code on failure.
 */
int mbedtls_rsa_pkcs1_sign( mbedtls_rsa_context *ctx,
                    int (*f_rng)(void *, unsigned char *, size_t),
                    void *p_rng,
                    int mode,
                    mbedtls_md_type_t md_alg,
                    unsigned int hashlen,
                    const unsigned char *hash,
                    unsigned char *sig );

② RSA验证签名接口

/**
 * \brief          This function performs a public RSA operation and checks
 *                 the message digest.
 *
 *                 This is the generic wrapper for performing a PKCS#1
 *                 verification using the mode from the context.
 *
 * \note           For PKCS#1 v2.1 encoding, see comments on
 *                 mbedtls_rsa_rsassa_pss_verify() about \p md_alg and
 *                 \p hash_id.
 *
 * \deprecated     It is deprecated and discouraged to call this function
 *                 in #MBEDTLS_RSA_PRIVATE mode. Future versions of the library
 *                 are likely to remove the \p mode argument and have it
 *                 set to #MBEDTLS_RSA_PUBLIC.
 *
 * \note           Alternative implementations of RSA need not support
 *                 mode being set to #MBEDTLS_RSA_PRIVATE and might instead
 *                 return #MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED.
 *
 * \param ctx      The initialized RSA public key context to use.
 * \param f_rng    The RNG function to use. If \p mode is #MBEDTLS_RSA_PRIVATE,
 *                 this is used for blinding and should be provided; see
 *                 mbedtls_rsa_private() for more. Otherwise, it is ignored.
 * \param p_rng    The RNG context to be passed to \p f_rng. This may be
 *                 \c NULL if \p f_rng is \c NULL or doesn't need a context.
 * \param mode     The mode of operation. This must be either
 *                 #MBEDTLS_RSA_PUBLIC or #MBEDTLS_RSA_PRIVATE (deprecated).
 * \param md_alg   The message-digest algorithm used to hash the original data.
 *                 Use #MBEDTLS_MD_NONE for signing raw data.
 * \param hashlen  The length of the message digest.
 *                 This is only used if \p md_alg is #MBEDTLS_MD_NONE.
 * \param hash     The buffer holding the message digest or raw data.
 *                 If \p md_alg is #MBEDTLS_MD_NONE, this must be a readable
 *                 buffer of length \p hashlen Bytes. If \p md_alg is not
 *                 #MBEDTLS_MD_NONE, it must be a readable buffer of length
 *                 the size of the hash corresponding to \p md_alg.
 * \param sig      The buffer holding the signature. This must be a readable
 *                 buffer of length \c ctx->len Bytes. For example, \c 256 Bytes
 *                 for an 2048-bit RSA modulus.
 *
 * \return         \c 0 if the verify operation was successful.
 * \return         An \c MBEDTLS_ERR_RSA_XXX error code on failure.
 */
int mbedtls_rsa_pkcs1_verify( mbedtls_rsa_context *ctx,
                      int (*f_rng)(void *, unsigned char *, size_t),
                      void *p_rng,
                      int mode,
                      mbedtls_md_type_t md_alg,
                      unsigned int hashlen,
                      const unsigned char *hash,
                      const unsigned char *sig );

3. 编写测试函数

新建文件mbedtls_rsa_sign_test.c,编写以下测试内容:

/**
 * @brief   RSA Sign Function demo
 * @author  mculover666
 * @date    2020/10/03
*/

#if !defined(MBEDTLS_CONFIG_FILE)
#include "mbedtls/config.h"
#else
#include MBEDTLS_CONFIG_FILE
#endif

#if defined(MBEDTLS_RSA_C)

#include <stdio.h>
#include "string.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/rsa.h"

static char buf[516];

static void dump_rsa_key(mbedtls_rsa_context *ctx)
{
    size_t olen;
    
    printf("\n  +++++++++++++++++ rsa keypair +++++++++++++++++\n\n");
    mbedtls_mpi_write_string(&ctx->N , 16, buf, sizeof(buf), &olen);
    printf("N: %s\n", buf); 

    mbedtls_mpi_write_string(&ctx->E , 16, buf, sizeof(buf), &olen);
    printf("E: %s\n", buf);

    mbedtls_mpi_write_string(&ctx->D , 16, buf, sizeof(buf), &olen);
    printf("D: %s\n", buf);

    mbedtls_mpi_write_string(&ctx->P , 16, buf, sizeof(buf), &olen);
    printf("P: %s\n", buf);

    mbedtls_mpi_write_string(&ctx->Q , 16, buf, sizeof(buf), &olen);
    printf("Q: %s\n", buf);

    mbedtls_mpi_write_string(&ctx->DP, 16, buf, sizeof(buf), &olen);
    printf("DP: %s\n", buf);

    mbedtls_mpi_write_string(&ctx->DQ, 16, buf, sizeof(buf), &olen);
    printf("DQ: %s\n", buf);

    mbedtls_mpi_write_string(&ctx->QP, 16, buf, sizeof(buf), &olen);
    printf("QP: %s\n", buf);
    printf("\n  +++++++++++++++++ rsa keypair +++++++++++++++++\n\n");
}

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" : "");
    }
}

static uint8_t output_buf[2048/8];

int mbedtls_rsa_sign_test(void)
{
    int ret;
    const char* msg = "HelloWorld";
   
    const char *pers = "rsa_sign_test";
    mbedtls_entropy_context entropy;
    mbedtls_ctr_drbg_context ctr_drbg;
    mbedtls_rsa_context ctx;

    /* 1. init structure */
    mbedtls_entropy_init(&entropy);
    mbedtls_ctr_drbg_init(&ctr_drbg);
    mbedtls_rsa_init(&ctx, MBEDTLS_RSA_PKCS_V21, MBEDTLS_MD_SHA256);
    
    /* 2. update seed with we own interface ported */
    printf( "\n  . Seeding the random number generator..." );
    
    ret = mbedtls_ctr_drbg_seed( &ctr_drbg, mbedtls_entropy_func, &entropy,
                               (const unsigned char *) pers,
                               strlen(pers));
    if(ret != 0) {
        printf( " failed\n  ! mbedtls_ctr_drbg_seed returned %d(-0x%04x)\n", ret, -ret);
        goto exit;
    }
    printf( " ok\n" );

    /* 3. generate an RSA keypair */
    printf( "\n  . Generate RSA keypair..." );
    
    ret = mbedtls_rsa_gen_key(&ctx, mbedtls_ctr_drbg_random, &ctr_drbg, 2048, 65537);
    if(ret != 0) {
        printf( " failed\n  ! mbedtls_rsa_gen_key returned %d(-0x%04x)\n", ret, -ret);
        goto exit;
    }
    printf( " ok\n" );
    
    /* shwo RSA keypair */
    dump_rsa_key(&ctx);
    
    /* 4. sign */
    printf( "\n  . RSA pkcs1 sign..." );
    
    ret = mbedtls_rsa_pkcs1_sign(&ctx, mbedtls_ctr_drbg_random, &ctr_drbg, MBEDTLS_RSA_PRIVATE, MBEDTLS_MD_SHA256, strlen(msg), (uint8_t *)msg, output_buf);
    if(ret != 0) {
        printf( " failed\n  ! mbedtls_rsa_pkcs1_sign returned %d(-0x%04x)\n", ret, -ret);
        goto exit;
    }
    printf( " ok\n" );
    
    /* show sign result */
    dump_buf(output_buf, sizeof(output_buf));
    
    /* 5. verify sign*/
    printf( "\n  . RSA pkcs1 verify..." );
    
    ret = mbedtls_rsa_pkcs1_verify(&ctx, mbedtls_ctr_drbg_random, &ctr_drbg, MBEDTLS_RSA_PUBLIC, MBEDTLS_MD_SHA256, strlen(msg), (uint8_t *)msg, output_buf);
    
    if(ret != 0) {
        printf( " failed\n  ! mbedtls_rsa_pkcs1_encrypt returned %d(-0x%04x)\n", ret, -ret);
        goto exit;
    }
    printf( " ok\n" );
    
    exit:
    
    /* 5. release structure */
    mbedtls_ctr_drbg_free(&ctr_drbg);
    mbedtls_entropy_free(&entropy);
    mbedtls_rsa_free(&ctx);
    
    return ret;
}

#endif /* MBEDTLS_RSA_C */

4. 测试结果

在main.c中声明该测试函数:

extern int mbedtls_rsa_sign_test(void);

在main函数中调用该测试函数:

/* 8. rsa sign test */
mbedtls_rsa_sign_test();

编译、下载,在串口助手中查看测试结果:

① 生成秘钥对成功(需要几min的时间):
mbedtls | 09 - 数字签名算法的配置与使用(RSA数字签名算法、ECDSA数字签名算法)_#define_02
② 生成签名和验证签名成功:
mbedtls | 09 - 数字签名算法的配置与使用(RSA数字签名算法、ECDSA数字签名算法)_#define_03

三、ECDSA数字签名功能模块的配置与使用

1. 配置宏

使用ECDSA秘钥协商功能需要提前开启伪随机数生成器(依赖AES算法、SHA256算法、MD通用接口),其中伪随机数生成器的硬件适配移植实现已经讲述,请参考对应的第二篇博客,不再赘述。

综合上述,本实验中需要开启的宏如下表:

宏定义 功能
MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES 不使用默认熵源(如果已有、要屏蔽该宏)
MBEDTLS_NO_PLATFORM_ENTROPY 不使用系统内置熵源
MBEDTLS_AES_C 使用AES算法
MBEDTLS_ENTROPY_C 使能熵源模块
MBEDTLS_CTR_DRBG_C 使能随机数模块
MBEDTLS_SHA256_C 使能SHA256算法
MBEDTLS_MD_C 开启MD通用接口
MBEDTLS_AES_ROM_TABLES 使能预定义S盒(节约内存空间)
MBEDTLS_BIGNUM_C 开启大数运算
MBEDTLS_ECP_C 开启椭圆曲线基础运算
MBEDTLS_ECP_DP_SECP256R1_ENABLED 选择secp256r1曲线参数
MBEDTLS_ECDSA_C 开启ECDSA算法
MBEDTLS_ASN1_WRITE_C 开启ASN1格式写入
MBEDTLS_ASN1_PARSE_C 开启ASN1结构解析

下面补充几个一个第一次出现宏的定义。

MBEDTLS_ECDSA_C

/**
 * \def MBEDTLS_ECDSA_C
 *
 * Enable the elliptic curve DSA library.
 *
 * Module:  library/ecdsa.c
 * Caller:
 *
 * This module is used by the following key exchanges:
 *      ECDHE-ECDSA
 *
 * Requires: MBEDTLS_ECP_C, MBEDTLS_ASN1_WRITE_C, MBEDTLS_ASN1_PARSE_C
 */
#define MBEDTLS_ECDSA_C

MBEDTLS_ASN1_WRITE_C

/**
 * \def MBEDTLS_ASN1_WRITE_C
 *
 * Enable the generic ASN1 writer.
 *
 * Module:  library/asn1write.c
 * Caller:  library/ecdsa.c
 *          library/pkwrite.c
 *          library/x509_create.c
 *          library/x509write_crt.c
 *          library/x509write_csr.c
 */
#define MBEDTLS_ASN1_WRITE_C

MBEDTLS_ASN1_PARSE_C

/**
 * \def MBEDTLS_ASN1_PARSE_C
 *
 * Enable the generic ASN1 parser.
 *
 * Module:  library/asn1.c
 * Caller:  library/x509.c
 *          library/dhm.c
 *          library/pkcs12.c
 *          library/pkcs5.c
 *          library/pkparse.c
 */
#define MBEDTLS_ASN1_PARSE_C

新建配置文件mbedtls_config_ecdsa.h,编写以下内容:

/**
 * @brief   Minimal configuration for ECDSA Function
 * @author  mculover666
 * @date    2020/10/03
*/

#ifndef _MBEDTLS_CONFIG_ECDSA_H_
#define _MBEDTLS_CONFIG_ECDSA_H_

/* System support */
#define MBEDTLS_HAVE_ASM
//#define MBEDTLS_HAVE_TIME

/* mbed feature support */
#define MBEDTLS_ENTROPY_HARDWARE_ALT
//#define MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES
#define MBEDTLS_NO_PLATFORM_ENTROPY

/* mbed modules */
#define MBEDTLS_AES_C
#define MBEDTLS_AES_ROM_TABLES
#define MBEDTLS_CTR_DRBG_C
#define MBEDTLS_ENTROPY_C
#define MBEDTLS_SHA256_C
#define MBEDTLS_MD_C
#define MBEDTLS_BIGNUM_C
#define MBEDTLS_ECP_C
#define MBEDTLS_ECP_DP_SECP256R1_ENABLED
#define MBEDTLS_ECDSA_C
#define MBEDTLS_ASN1_WRITE_C
#define MBEDTLS_ASN1_PARSE_C

#include "mbedtls/check_config.h"

#endif /* _MBEDTLS_CONFIG_ECDSA_H_ */

在MDK中配置使用该配置文件:mbedtls | 09 - 数字签名算法的配置与使用(RSA数字签名算法、ECDSA数字签名算法)_sed_04

2. ECDSA签名功能API说明

① 初始化ECDSA结构体:

/**
 * \brief           This function initializes an ECDSA context.
 *
 * \param ctx       The ECDSA context to initialize.
 *                  This must not be \c NULL.
 */
void mbedtls_ecdsa_init( mbedtls_ecdsa_context *ctx );

② ECDSA生成秘钥接口:

/**
 * \brief          This function generates an ECDSA keypair on the given curve.
 *
 * \see            ecp.h
 *
 * \param ctx      The ECDSA context to store the keypair in.
 *                 This must be initialized.
 * \param gid      The elliptic curve to use. One of the various
 *                 \c MBEDTLS_ECP_DP_XXX macros depending on configuration.
 * \param f_rng    The RNG function to use. This must not be \c NULL.
 * \param p_rng    The RNG context to be passed to \p f_rng. This may be
 *                 \c NULL if \p f_rng doesn't need a context argument.
 *
 * \return         \c 0 on success.
 * \return         An \c MBEDTLS_ERR_ECP_XXX code on failure.
 */
int mbedtls_ecdsa_genkey( mbedtls_ecdsa_context *ctx, mbedtls_ecp_group_id gid,
                  int (*f_rng)(void *, unsigned char *, size_t), void *p_rng );

③ ECDSA签名接口:

/**
 * \brief           This function computes the ECDSA signature of a
 *                  previously-hashed message.
 *
 * \note            The deterministic version implemented in
 *                  mbedtls_ecdsa_sign_det() is usually preferred.
 *
 * \note            If the bitlength of the message hash is larger than the
 *                  bitlength of the group order, then the hash is truncated
 *                  as defined in <em>Standards for Efficient Cryptography Group
 *                  (SECG): SEC1 Elliptic Curve Cryptography</em>, section
 *                  4.1.3, step 5.
 *
 * \see             ecp.h
 *
 * \param grp       The context for the elliptic curve to use.
 *                  This must be initialized and have group parameters
 *                  set, for example through mbedtls_ecp_group_load().
 * \param r         The MPI context in which to store the first part
 *                  the signature. This must be initialized.
 * \param s         The MPI context in which to store the second part
 *                  the signature. This must be initialized.
 * \param d         The private signing key. This must be initialized.
 * \param buf       The content to be signed. This is usually the hash of
 *                  the original data to be signed. This must be a readable
 *                  buffer of length \p blen Bytes. It may be \c NULL if
 *                  \p blen is zero.
 * \param blen      The length of \p buf in Bytes.
 * \param f_rng     The RNG function. This must not be \c NULL.
 * \param p_rng     The RNG context to be passed to \p f_rng. This may be
 *                  \c NULL if \p f_rng doesn't need a context parameter.
 *
 * \return          \c 0 on success.
 * \return          An \c MBEDTLS_ERR_ECP_XXX
 *                  or \c MBEDTLS_MPI_XXX error code on failure.
 */
int mbedtls_ecdsa_sign( mbedtls_ecp_group *grp, mbedtls_mpi *r, mbedtls_mpi *s,
                const mbedtls_mpi *d, const unsigned char *buf, size_t blen,
                int (*f_rng)(void *, unsigned char *, size_t), void *p_rng );

④ ECDSA验证签名接口

/**
 * \brief           This function verifies the ECDSA signature of a
 *                  previously-hashed message.
 *
 * \note            If the bitlength of the message hash is larger than the
 *                  bitlength of the group order, then the hash is truncated as
 *                  defined in <em>Standards for Efficient Cryptography Group
 *                  (SECG): SEC1 Elliptic Curve Cryptography</em>, section
 *                  4.1.4, step 3.
 *
 * \see             ecp.h
 *
 * \param grp       The ECP group to use.
 *                  This must be initialized and have group parameters
 *                  set, for example through mbedtls_ecp_group_load().
 * \param buf       The hashed content that was signed. This must be a readable
 *                  buffer of length \p blen Bytes. It may be \c NULL if
 *                  \p blen is zero.
 * \param blen      The length of \p buf in Bytes.
 * \param Q         The public key to use for verification. This must be
 *                  initialized and setup.
 * \param r         The first integer of the signature.
 *                  This must be initialized.
 * \param s         The second integer of the signature.
 *                  This must be initialized.
 *
 * \return          \c 0 on success.
 * \return          #MBEDTLS_ERR_ECP_BAD_INPUT_DATA if the signature
 *                  is invalid.
 * \return          An \c MBEDTLS_ERR_ECP_XXX or \c MBEDTLS_MPI_XXX
 *                  error code on failure for any other reason.
 */
int mbedtls_ecdsa_verify( mbedtls_ecp_group *grp,
                          const unsigned char *buf, size_t blen,
                          const mbedtls_ecp_point *Q, const mbedtls_mpi *r,
                          const mbedtls_mpi *s);

⑤ 释放ECDSA结构体:

/**
 * \brief           This function frees an ECDSA context.
 *
 * \param ctx       The ECDSA context to free. This may be \c NULL,
 *                  in which case this function does nothing. If it
 *                  is not \c NULL, it must be initialized.
 */
void mbedtls_ecdsa_free( mbedtls_ecdsa_context *ctx );

3. 编写测试函数

新建文件mbedtls_ecdsa_test,编写以下测试函数:

/**
 * @brief   ECDSA Function demo
 * @author  mculover666
 * @date    2020/10/03
*/

#if !defined(MBEDTLS_CONFIG_FILE)
#include "mbedtls/config.h"
#else
#include MBEDTLS_CONFIG_FILE
#endif

#if defined(MBEDTLS_ECDSA_C)

#include <stdio.h>
#include "string.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/ecdsa.h"

uint8_t buf[97];

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 mbedtls_ecdsa_test(void)
{
    int ret;
    size_t qlen, dlen;
    size_t rlen, slen;
    uint8_t hash[32];
   
    const char *msg  = "HelloWorld";
    const char *pers = "ecdsa_test";
    mbedtls_entropy_context entropy;
    mbedtls_ctr_drbg_context ctr_drbg;
    mbedtls_mpi r, s;
    mbedtls_ecdsa_context ctx;
    mbedtls_md_context_t md_ctx;
        
    /* 1. init structure */
    mbedtls_md_init(&md_ctx);
    mbedtls_entropy_init(&entropy);
    mbedtls_ctr_drbg_init(&ctr_drbg);
    mbedtls_mpi_init(&r);
    mbedtls_mpi_init(&s);
    
    /* 2. update seed with we own interface ported */
    printf( "\n  . Seeding the random number generator..." );
    
    ret = mbedtls_ctr_drbg_seed( &ctr_drbg, mbedtls_entropy_func, &entropy,
                               (const unsigned char *) pers,
                               strlen(pers));
    if(ret != 0) {
        printf( " failed\n  ! mbedtls_ctr_drbg_seed returned %d(-0x%04x)\n", ret, -ret);
        goto exit;
    }
    printf( " ok\n" );
    
    /* 3. hash message */
    printf( "\n  . Hash message..." );
    
    ret = mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), (uint8_t *)msg, strlen(msg), hash);
    if(ret != 0) {
        printf( " failed\n  ! mbedtls_md returned %d(-0x%04x)\n", ret, -ret);
        goto exit;
    }
    printf( " ok\n" );
    
    /* show hash */
    dump_buf(hash, sizeof(hash));
    
    /* 4. generate keypair */
    printf( "\n  . Generate ecdsa keypair..." );
    
    ret = mbedtls_ecdsa_genkey(&ctx, MBEDTLS_ECP_DP_SECP256R1, mbedtls_ctr_drbg_random, &ctr_drbg);
    if(ret != 0) {
        printf( " failed\n  ! mbedtls_ecdsa_genkey returned %d(-0x%04x)\n", ret, -ret);
        goto exit;
    }
    printf( " ok\n" );
    
    /* show keypair */
    mbedtls_ecp_point_write_binary(&ctx.grp, &ctx.Q, MBEDTLS_ECP_PF_UNCOMPRESSED, &qlen, buf, sizeof(buf));
    dlen = mbedtls_mpi_size(&ctx.d);
    mbedtls_mpi_write_binary(&ctx.d, buf + qlen, dlen);
    dump_buf(buf, qlen + dlen);
    
    /* 5. ecdsa sign */
    printf( "\n  . ECDSA sign..." );
    
    ret = mbedtls_ecdsa_sign(&ctx.grp, &r, &s, &ctx.d, hash, sizeof(hash), mbedtls_ctr_drbg_random, &ctr_drbg);
    if(ret != 0) {
        printf( " failed\n  ! mbedtls_ecdsa_sign returned %d(-0x%04x)\n", ret, -ret);
        goto exit;
    }
    printf( " ok\n" );
    
    /* show sign */
    rlen = mbedtls_mpi_size(&r);
    slen = mbedtls_mpi_size(&s);
    mbedtls_mpi_write_binary(&r, buf, rlen);
    mbedtls_mpi_write_binary(&s, buf + rlen, slen);
    dump_buf(buf, rlen + slen);
    
    /* 6. ecdsa verify */
    printf( "\n  . ECDSA verify..." );
    
    ret = mbedtls_ecdsa_verify(&ctx.grp, hash, sizeof(hash), &ctx.Q, &r, &s);
    if(ret != 0) {
        printf( " failed\n  ! mbedtls_ecdsa_verify returned %d(-0x%04x)\n", ret, -ret);
        goto exit;
    }
    printf( " ok\n" );

    exit:
    
    /* 7. release structure */
    mbedtls_ctr_drbg_free(&ctr_drbg);
    mbedtls_entropy_free(&entropy);
    mbedtls_mpi_free(&r);
    mbedtls_mpi_free(&s);
    mbedtls_md_free(&md_ctx);
    mbedtls_ecdsa_free(&ctx);
    
    return ret;
}

#endif /* MBEDTLS_DHM_C */

4. 测试结果

在main.c中声明该函数:

extern int mbedtls_ecdsa_test(void);

在main函数中调用该函数:

/* 9. ecdsa sign test */
mbedtls_ecdsa_test();

编译、下载,在串口助手中查看结果:
mbedtls | 09 - 数字签名算法的配置与使用(RSA数字签名算法、ECDSA数字签名算法)_sed_05
接收精彩文章及资源推送,请订阅我的微信公众号:『mculover666』
mbedtls | 09 - 数字签名算法的配置与使用(RSA数字签名算法、ECDSA数字签名算法)_#include_06