mbedtls | 02 - 伪随机数生成器(ctr_drbg)的配置与使用

mbedtls系列文章



一、真随机数和伪随机数

1. 区别

随机数在安全技术中通常被用于生成随机序列(eg. 秘钥),对于一个随机数的生成而言,是否真正的做到“随机”是最重要的。

真随机数通常来源于硬件随机数生成器,每次生成的随机数都是真正的随机数,但是因为物理因素,生成的时间较慢。比如STM32中提供的RNG硬件外设。

伪随机数通常来源于某个生成算法,每次生成的随机数虽然是随机的,但还是遵循生成算法的规则,优点是生成速度较快。比如C库中提供的rand函数,在相同的种子下,其生成的随机数序列相同,所以称之为伪随机数。

所以一般情况下,有一种比较巧妙的办法:将两者结合起来,先使用真随机数生成种子,然后使用伪随机数生成算法,这样既保证了生成速度,又保证了生成的序列是真正的随机数。

对应到 mbedtls 中,将产生真随机数的模块称为真随机数生成器(TRNG),将产生伪随机数的模块称为伪随机数发生器(PRNG)(也叫做确定性随机数生成器,DRBG),其中伪随机数所使用的种子称为熵源(熵池)

2. 伪随机数生成算法

NIST SP 800-90A规范中描述了三种产生伪随机数的算法:

  • Hash_DRBG:使用单向散列算法作为伪随机数生成的基础算法;
  • HMAC_DRBG:使用消息认证码算法作为随机数生成的基础算法;
  • CRT_DRBG:使用分组密码算法的计数器模式作为随机数生成的基础算法(重点)。

CRT_DRBG 可以理解为一个AES加密过程,加密结果为期望随机数序列。

二、自定义熵源接口

mbedtls中可以通过开启宏定义 MBEDTLS_ENTROPY_HARDWARE_ALT 来开启熵源接口,用户可以在熵源接口中实现自己的硬件获取真随机数代码。

1. 开启宏定义

/**
 * \def MBEDTLS_ENTROPY_HARDWARE_ALT
 *
 * Uncomment this macro to let mbed TLS use your own implementation of a
 * hardware entropy collector.
 *
 * Your function must be called \c mbedtls_hardware_poll(), have the same
 * prototype as declared in entropy_poll.h, and accept NULL as first argument.
 *
 * Uncomment to use your own hardware entropy collector.
 */
#define MBEDTLS_ENTROPY_HARDWARE_ALT

2. 自定义实现mbedtls_hardware_poll函数

创建一个新文件用于存放我们编写的该函数实现,这里我创建文件entropy_hardware_alt.c

实现时需要包含头文件entropy_poll.h,其中包含了 mbedtls_hardware_poll() 函数的原型定义:

#if defined(MBEDTLS_ENTROPY_HARDWARE_ALT)
/**
 * \brief           Entropy poll callback for a hardware source
 *
 * \warning         This is not provided by mbed TLS!
 *                  See \c MBEDTLS_ENTROPY_HARDWARE_ALT in config.h.
 *
 * \note            This must accept NULL as its first argument.
 */
int mbedtls_hardware_poll( void *data,
                           unsigned char *output, size_t len, size_t *olen );
#endif

这里我使用之前移植到小熊派开发板的工程,先开启RNG外设,然后使用硬件RNG外设实现该函数。

① 开启硬件RNG外设:

② 编辑文件entropy_hardware_alt.c,实现函数

#include "mbedtls/entropy_poll.h"

#ifdef MBEDTLS_ENTROPY_HARDWARE_ALT

#include "main.h"
#include "string.h"
#include "stm32l4xx_hal.h"
#include "mbedtls/entropy_poll.h"

extern RNG_HandleTypeDef hrng;

int mbedtls_hardware_poll( void *Data, unsigned char *Output, size_t Len, size_t *oLen )
{
  uint32_t index;
  uint32_t randomValue;
		
  for (index = 0; index < Len/4; index++)
  {
    if (HAL_RNG_GenerateRandomNumber(&hrng, &randomValue) == HAL_OK)
    {
      *oLen += 4;
      memset(&(Output[index * 4]), (int)randomValue, 4);
    }
    else
    {
      Error_Handler();
    }
  }
  
  return 0;
}

#endif /*MBEDTLS_ENTROPY_HARDWARE_ALT*/

三、使用mbedtls CTR_DRBG接口生成随机数

1. 宏配置

使用mbedtls随机数生成功能需要开启以下宏:

宏定义功能
MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES不使用默认熵源(如果已有、要屏蔽该宏)
MBEDTLS_NO_PLATFORM_ENTROPY不使用系统内置熵源
MBEDTLS_AES_C使用AES算法
MBEDTLS_ENTROPY_C使能熵源模块
MBEDTLS_CTR_DRBG_C使能随机数模块
MBEDTLS_ENTROPY_FORCE_SHA256使能SHA256算法
MBEDTLS_AES_ROM_TABLES使能预定义S盒(节约内存空间)

MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES(如果已有、要屏蔽该宏)

/**
 * \def MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES
 *
 * Do not add default entropy sources. These are the platform specific,
 * mbedtls_timing_hardclock and HAVEGE based poll functions.
 *
 * This is useful to have more control over the added entropy sources in an
 * application.
 *
 * Uncomment this macro to prevent loading of default entropy functions.
 */
#define MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES

MBEDTLS_NO_PLATFORM_ENTROPY

/**
 * \def MBEDTLS_NO_PLATFORM_ENTROPY
 *
 * Do not use built-in platform entropy functions.
 * This is useful if your platform does not support
 * standards like the /dev/urandom or Windows CryptoAPI.
 *
 * Uncomment this macro to disable the built-in platform entropy functions.
 */
#define MBEDTLS_NO_PLATFORM_ENTROPY

MBEDTLS_AES_C

/**
 * \def MBEDTLS_AES_C
 *
 * Enable the AES block cipher.
 *
 * Module:  library/aes.c
 * Caller:  library/cipher.c
 *          library/pem.c
 *          library/ctr_drbg.c
 *
 * This module enables the following ciphersuites (if other requisites are
 * enabled as well):
 *      MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA
 *      MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA
 *      MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA
 *      MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA
 *      MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256
 *      MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384
 *      MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256
 *      MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384
 *      MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256
 *      MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384
 *      MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256
 *      MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384
 *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
 *      MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
 *      MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
 *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
 *      MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
 *      MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
 *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
 *      MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
 *      MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA
 *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
 *      MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
 *      MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
 *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
 *      MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
 *      MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
 *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
 *      MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
 *      MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA
 *      MBEDTLS_TLS_DHE_PSK_WITH_AES_256_GCM_SHA384
 *      MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384
 *      MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA384
 *      MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA
 *      MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA
 *      MBEDTLS_TLS_DHE_PSK_WITH_AES_128_GCM_SHA256
 *      MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256
 *      MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA256
 *      MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA
 *      MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA
 *      MBEDTLS_TLS_RSA_WITH_AES_256_GCM_SHA384
 *      MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA256
 *      MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA
 *      MBEDTLS_TLS_RSA_WITH_AES_128_GCM_SHA256
 *      MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA256
 *      MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA
 *      MBEDTLS_TLS_RSA_PSK_WITH_AES_256_GCM_SHA384
 *      MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA384
 *      MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA
 *      MBEDTLS_TLS_RSA_PSK_WITH_AES_128_GCM_SHA256
 *      MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA256
 *      MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA
 *      MBEDTLS_TLS_PSK_WITH_AES_256_GCM_SHA384
 *      MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA384
 *      MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA
 *      MBEDTLS_TLS_PSK_WITH_AES_128_GCM_SHA256
 *      MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA256
 *      MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA
 *
 * PEM_PARSE uses AES for decrypting encrypted keys.
 */
#define MBEDTLS_AES_C

MBEDTLS_ENTROPY_C

/**
 * \def MBEDTLS_ENTROPY_C
 *
 * Enable the platform-specific entropy code.
 *
 * Module:  library/entropy.c
 * Caller:
 *
 * Requires: MBEDTLS_SHA512_C or MBEDTLS_SHA256_C
 *
 * This module provides a generic entropy pool
 */
#define MBEDTLS_ENTROPY_C

MBEDTLS_CTR_DRBG_C

/**
 * \def MBEDTLS_CTR_DRBG_C
 *
 * Enable the CTR_DRBG AES-based random generator.
 * The CTR_DRBG generator uses AES-256 by default.
 * To use AES-128 instead, enable MBEDTLS_CTR_DRBG_USE_128_BIT_KEY below.
 *
 * Module:  library/ctr_drbg.c
 * Caller:
 *
 * Requires: MBEDTLS_AES_C
 *
 * This module provides the CTR_DRBG AES random number generator.
 */
#define MBEDTLS_CTR_DRBG_C

MBEDTLS_SHA256_C

/**
 * \def MBEDTLS_SHA256_C
 *
 * Enable the SHA-224 and SHA-256 cryptographic hash algorithms.
 *
 * Module:  library/sha256.c
 * Caller:  library/entropy.c
 *          library/md.c
 *          library/ssl_cli.c
 *          library/ssl_srv.c
 *          library/ssl_tls.c
 *
 * This module adds support for SHA-224 and SHA-256.
 * This module is required for the SSL/TLS 1.2 PRF function.
 */
#define MBEDTLS_SHA256_C

MBEDTLS_AES_ROM_TABLES

/**
 * \def MBEDTLS_AES_ROM_TABLES
 *
 * Use precomputed AES tables stored in ROM.
 *
 * Uncomment this macro to use precomputed AES tables stored in ROM.
 * Comment this macro to generate AES tables in RAM at runtime.
 *
 * Tradeoff: Using precomputed ROM tables reduces RAM usage by ~8kb
 * (or ~2kb if \c MBEDTLS_AES_FEWER_TABLES is used) and reduces the
 * initialization time before the first AES operation can be performed.
 * It comes at the cost of additional ~8kb ROM use (resp. ~2kb if \c
 * MBEDTLS_AES_FEWER_TABLES below is used), and potentially degraded
 * performance if ROM access is slower than RAM access.
 *
 * This option is independent of \c MBEDTLS_AES_FEWER_TABLES.
 *
 */
#define MBEDTLS_AES_ROM_TABLES

根据以上代码,编写针对本实验的配置文件mbedtls_config_ctr_drbg.h

/**
 * @brief   Minimal configuration for CTR_DRBG Function
 * @author  mculover666
 * @date    2020/09/22
*/

#ifndef _MBEDTLS_CONFIG_CTR_DRBG_H_
#define _MBEDTLS_CONFIG_CTR_DRBG_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

#include "mbedtls/check_config.h"

#endif /* _MBEDTLS_CONFIG_CTR_DRBG_H_ */

将配置文件所在目录加入到路径:

选择使用刚刚编写的本实验配置文件:

2. API说明

2.1. 熵相关(entropy)

① 初始化熵结构体:

/**
 * \brief           Initialize the context
 *
 * \param ctx       Entropy context to initialize
 */
void mbedtls_entropy_init( mbedtls_entropy_context *ctx );

② 释放熵结构体:

/**
 * \brief           Free the data in the context
 *
 * \param ctx       Entropy context to free
 */
void mbedtls_entropy_free( mbedtls_entropy_context *ctx );

③ 错误码说明(用于根据返回值判定错误):

#define MBEDTLS_ERR_ENTROPY_SOURCE_FAILED                 -0x003C  /**< Critical entropy source failure. */
#define MBEDTLS_ERR_ENTROPY_MAX_SOURCES                   -0x003E  /**< No more sources can be added. */
#define MBEDTLS_ERR_ENTROPY_NO_SOURCES_DEFINED            -0x0040  /**< No sources have been added to poll. */
#define MBEDTLS_ERR_ENTROPY_NO_STRONG_SOURCE              -0x003D  /**< No strong sources have been added to poll. */
#define MBEDTLS_ERR_ENTROPY_FILE_IO_ERROR                 -0x003F  /**< Read/write error in file. */

2.2. ctr_drbg相关

① 初始化ctr_drbg结构体:

/**
 * \brief               This function initializes the CTR_DRBG context,
 *                      and prepares it for mbedtls_ctr_drbg_seed()
 *                      or mbedtls_ctr_drbg_free().
 *
 * \param ctx           The CTR_DRBG context to initialize.
 */
void mbedtls_ctr_drbg_init( mbedtls_ctr_drbg_context *ctx );

② 根据个性化字符更新种子:

/**
 * - The \p custom string.
 *
 * \note                To achieve the nominal security strength permitted
 *                      by CTR_DRBG, the entropy length must be:
 *                      - at least 16 bytes for a 128-bit strength
 *                      (maximum achievable strength when using AES-128);
 *                      - at least 32 bytes for a 256-bit strength
 *                      (maximum achievable strength when using AES-256).
 *
 *                      In addition, if you do not pass a nonce in \p custom,
 *                      the sum of the entropy length
 *                      and the entropy nonce length must be:
 *                      - at least 24 bytes for a 128-bit strength
 *                      (maximum achievable strength when using AES-128);
 *                      - at least 48 bytes for a 256-bit strength
 *                      (maximum achievable strength when using AES-256).
 *
 * \param ctx           The CTR_DRBG context to seed.
 *                      It must have been initialized with
 *                      mbedtls_ctr_drbg_init().
 *                      After a successful call to mbedtls_ctr_drbg_seed(),
 *                      you may not call mbedtls_ctr_drbg_seed() again on
 *                      the same context unless you call
 *                      mbedtls_ctr_drbg_free() and mbedtls_ctr_drbg_init()
 *                      again first.
 * \param f_entropy     The entropy callback, taking as arguments the
 *                      \p p_entropy context, the buffer to fill, and the
 *                      length of the buffer.
 *                      \p f_entropy is always called with a buffer size
 *                      less than or equal to the entropy length.
 * \param p_entropy     The entropy context to pass to \p f_entropy.
 * \param custom        The personalization string.
 *                      This can be \c NULL, in which case the personalization
 *                      string is empty regardless of the value of \p len.
 * \param len           The length of the personalization string.
 *                      This must be at most
 *                      #MBEDTLS_CTR_DRBG_MAX_SEED_INPUT
 *                      - #MBEDTLS_CTR_DRBG_ENTROPY_LEN.
 *
 * \return              \c 0 on success.
 * \return              #MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED on failure.
 */
int mbedtls_ctr_drbg_seed( mbedtls_ctr_drbg_context *ctx,
                   int (*f_entropy)(void *, unsigned char *, size_t),
                   void *p_entropy,
                   const unsigned char *custom,
                   size_t len );

③ 生成指定长度随机数

/**
 * \brief   This function uses CTR_DRBG to generate random data.
 *
 * This function automatically reseeds if the reseed counter is exceeded
 * or prediction resistance is enabled.
 *
 *
 * \param p_rng         The CTR_DRBG context. This must be a pointer to a
 *                      #mbedtls_ctr_drbg_context structure.
 * \param output        The buffer to fill.
 * \param output_len    The length of the buffer in bytes.
 *
 * \return              \c 0 on success.
 * \return              #MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED or
 *                      #MBEDTLS_ERR_CTR_DRBG_REQUEST_TOO_BIG on failure.
 */
int mbedtls_ctr_drbg_random( void *p_rng,
                     unsigned char *output, size_t output_len );

最大生成长度是:MBEDTLS_CTR_DRBG_MAX_REQUEST(1024),如果需要修改的话可以在配置文件中定义该宏。

④ 释放ctr_drbg结构体:

/**
 * \brief               This function clears CTR_CRBG context data.
 *
 * \param ctx           The CTR_DRBG context to clear.
 */
void mbedtls_ctr_drbg_free( mbedtls_ctr_drbg_context *ctx );

⑤ 错误码:

#define MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED        -0x0034  /**< The entropy source failed. */
#define MBEDTLS_ERR_CTR_DRBG_REQUEST_TOO_BIG              -0x0036  /**< The requested random buffer length is too big. */
#define MBEDTLS_ERR_CTR_DRBG_INPUT_TOO_BIG                -0x0038  /**< The input (entropy + additional data) is too large. */
#define MBEDTLS_ERR_CTR_DRBG_FILE_IO_ERROR                -0x003A  /**< Read or write error in file. */

3. 编写测试代码

编辑文件mbedtls_ctr_drbg_test.c,编写一个测试随机数生成的函数:

/**
 * @brief   CTR_DRBG Function demo
 * @author  mculover666
 * @date    2020/09/22
*/

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

#if defined(MBEDTLS_CTR_DRBG_C)

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

int mbedtls_ctr_drbg_test(void)
{
    int ret;
    uint8_t data_buf[10];
   
    const char *pers = "crbg_test";
    mbedtls_entropy_context entropy;
    mbedtls_ctr_drbg_context ctr_drbg;

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

    printf( " ok\n" );

    /* 4. generate random data */
    printf( "\n  . generate random data..." );
    
    
    if ( ( ret = mbedtls_ctr_drbg_random(&ctr_drbg, data_buf, sizeof(data_buf) ) ) != 0) {
        printf( " failed\n  ! mbedtls_ctr_drbg_random returned %d\n", ret );
        goto exit;
    }
    printf( " ok\n" );
    
    printf("random data:[");
    for (int i = 0; i < sizeof(data_buf); i++) {
        printf("%02x ", data_buf[i]);
    }
    printf("]\r\n");
    
    exit:
    
    /* 5. release ctr drbg structure */
    mbedtls_ctr_drbg_free(&ctr_drbg);
    
    /* 6. release entropy structure*/
    mbedtls_entropy_free(&entropy);
    
    return ret;
}

#endif /* MBEDTLS_CTR_DRBG_C */

4. 调用测试代码

在main.c中声明测试函数定义在外部:

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
extern int mbedtls_ctr_drbg_test(void);
/* USER CODE END 0 */

在main函数中调用drgb生成随机数的测试代码:

/* USER CODE BEGIN 2 */
printf("mbedtls port on BearPi-STM32L431RC board by mculover666\r\n");

/* 1. crt_drbg test */
mbedtls_ctr_drbg_test();

/* USER CODE END 2 */

5. 测试结果

编译,下载,在串口助手中查看运行结果:

按下复位再次运行,比较生成的随机数:

接收精彩文章及资源推送,请订阅我的微信公众号:『mculover666』

Mculover666 CSDN认证博客专家 嵌入式软件开发 IoT全栈开发
CSDN博客专家,微信公众号mculover666,凭借与生俱来的热爱专注于嵌入式领域,在自己折腾的同时,以文字的方式分享所玩、所思、所想、所悟,作为一个技术人,我们一起前进~
©️2020 CSDN 皮肤主题: 成长之路 设计师:Amelia_0503 返回首页