/*
 * This file is part of the openHiTLS project.
 *
 * openHiTLS is licensed under the Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *
 *     http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
#include "app_utils.h"
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include "bsl_sal.h"
#include "bsl_ui.h"
#include "bsl_errno.h"
#include "bsl_buffer.h"
#include "bsl_pem_internal.h"
#include "sal_file.h"
#include "crypt_errno.h"
#include "crypt_eal_codecs.h"
#include "crypt_eal_pkey.h"
#include "crypt_eal_cipher.h"
#include "crypt_eal_md.h"
#include "crypt_eal_rand.h"
#include "crypt_codecskey.h"
#include "app_print.h"
#include "app_errno.h"
#include "app_opt.h"
#include "app_list.h"
#include "app_sm.h"

#define DEFAULT_PEM_FILE_SIZE 1024U
#define RSA_PRV_CTX_LEN 8

#define APP_HEX_HEAD "0x"
#define APP_LINESIZE 255
#define PEM_BEGIN_STR "-----BEGIN "
#define PEM_END_STR "-----END "
#define PEM_TAIL_STR "-----\n"
#define PEM_TAIL_KEY_STR "KEY-----\n"

#define PEM_BEGIN_STR_LEN ((int)(sizeof(PEM_BEGIN_STR) - 1))
#define PEM_END_STR_LEN ((int)(sizeof(PEM_END_STR) - 1))
#define PEM_TAIL_STR_LEN ((int)(sizeof(PEM_TAIL_STR) - 1))
#define PEM_TAIL_KEY_STR_LEN ((int)(sizeof(PEM_TAIL_KEY_STR) - 1))

#define PEM_RSA_PRIVATEKEY_STR "RSA PRIVATE KEY"
#define PEM_RSA_PUBLIC_STR "RSA PUBLIC KEY"
#define PEM_EC_PRIVATEKEY_STR "EC PRIVATE KEY"
#define PEM_PKCS8_PRIVATEKEY_STR "PRIVATE KEY"
#define PEM_PKCS8_PUBLIC_STR "PUBLIC KEY"
#define PEM_ENCRYPTED_PKCS8_PRIVATEKEY_STR "ENCRYPTED PRIVATE KEY"

#define PEM_PROC_TYPE_STR "Proc-Type:"
#define PEM_PROC_TYPE_NUM_STR "4,"
#define PEM_ENCRYPTED_STR "ENCRYPTED"

#define APP_PASS_ARG_STR "pass:"
#define APP_PASS_ARG_STR_LEN ((int)(sizeof(APP_PASS_ARG_STR) - 1))

#define APP_PASS_STDIN_STR "stdin"
#define APP_PASS_STDIN_STR_LEN ((int)(sizeof(APP_PASS_STDIN_STR) - 1))

#define APP_PASS_FILE_STR "file:"
#define APP_PASS_FILE_STR_LEN ((int)(sizeof(APP_PASS_FILE_STR) - 1))

#ifdef HITLS_APP_SM_MODE
#ifndef HITLS_PROVIDER_LIB_NAME
#error "HITLS_PROVIDER_LIB_NAME must be defined by build system"
#endif
#define APP_SM_PROVIDER_NAME HITLS_PROVIDER_LIB_NAME
#define APP_SM_PROVIDER_ATTR "provider=sm"
#endif

#define APP_DEFAULT_PROVIDER_NAME "default"
#define APP_DEFAULT_PROVIDER_ATTR "provider=default"

typedef struct defaultPassCBData {
    uint32_t maxLen;
    uint32_t minLen;
} APP_DefaultPassCBData;

int32_t HITLS_APP_CheckPasswd(const uint8_t *password, const uint32_t passwordLen)
{
    for (uint32_t i = 0; i < passwordLen; ++i) {
        if (password[i] < '!' || password[i] > '~') {
            AppPrintError("The password can contain only the following characters:\n");
            AppPrintError("a~z A~Z 0~9 ! \" # $ %% & ' ( ) * + , - . / : ; < = > ? @ [ \\ ] ^ _ ` { | } ~\n");
            return HITLS_APP_PASSWD_FAIL;
        }
    }
    return HITLS_APP_SUCCESS;
}

static int32_t CheckFileSizeByPath(const char *inFilePath, uint32_t *fileSize)
{
    size_t getFileSize = 0;
    int32_t ret = BSL_SAL_FileLength(inFilePath, &getFileSize);
    if (ret != BSL_SUCCESS) {
        AppPrintError("Failed to get the file size: %d.\n", ret);
        return HITLS_APP_UIO_FAIL;
    }
    if (getFileSize > APP_FILE_MAX_SIZE) {
        AppPrintError("File size exceed limit %zukb.\n", APP_FILE_MAX_SIZE_KB);
        return HITLS_APP_UIO_FAIL;
    }
    if (fileSize != NULL) {
        *fileSize = (uint32_t)getFileSize;
    }
    return ret;
}

static char *GetPemKeyFileName(const char *buf, size_t readLen)
{
    // -----BEGIN *** KEY-----
    if ((strncmp(buf, PEM_BEGIN_STR, PEM_BEGIN_STR_LEN) != 0) || (readLen < PEM_TAIL_KEY_STR_LEN) ||
        (strncmp(buf + readLen - PEM_TAIL_KEY_STR_LEN, PEM_TAIL_KEY_STR, PEM_TAIL_KEY_STR_LEN) != 0)) {
        return NULL;
    }

    int32_t len = readLen - PEM_BEGIN_STR_LEN - PEM_TAIL_STR_LEN;
    char *name = BSL_SAL_Calloc(len + 1, 1);
    if (name == NULL) {
        return name;
    }
    memcpy(name, buf + PEM_BEGIN_STR_LEN, len);
    name[len] = '\0';
    return name;
}

static bool IsNeedEncryped(const char *name, const char *header, uint32_t headerLen)
{
    // PKCS8
    if (strcmp(name, PEM_ENCRYPTED_PKCS8_PRIVATEKEY_STR) == 0) {
        return true;
    }
    // PKCS1
    // Proc-Type: 4, ENCRYPTED
    uint32_t offset = 0;
    uint32_t len = strlen(PEM_PROC_TYPE_STR);
    if (strncmp(header + offset, PEM_PROC_TYPE_STR, len) != 0) {
        return false;
    }
    offset += len + strspn(header + offset + len, " \t");
    len = strlen(PEM_PROC_TYPE_NUM_STR);
    if ((offset >= headerLen) || (strncmp(header + offset, PEM_PROC_TYPE_NUM_STR, len) != 0)) {
        return false;
    }
    offset += len + strspn(header + offset + len, " \t");
    len = strlen(PEM_ENCRYPTED_STR);
    if ((offset >= headerLen) || (strncmp(header + offset, PEM_ENCRYPTED_STR, len) != 0)) {
        return false;
    }
    offset += len + strspn(header + offset + len, " \t");
    if ((offset >= headerLen) || header[offset] != '\n') {
        return false;
    }
    return true;
}

static void PrintFileOrStdinError(const char *filePath, const char *errStr)
{
    if (filePath == NULL) {
        AppPrintError("%s.\n", errStr);
    } else {
        AppPrintError("%s from \"%s\".\n", errStr, filePath);
    }
}

static int32_t ReadPemKeyFile(const char *inFilePath, uint8_t **inData, uint32_t *inDataSize, char **name,
    bool *isEncrypted)
{
    if ((inData == NULL) || (inDataSize == NULL) || (name == NULL)) {
        return HITLS_APP_INVALID_ARG;
    }

    // End after reading the following two strings in sequence:
    // -----BEGIN XXX-----
    // -----END XXX-----
    bool isParseHeader = false;
    BSL_Buffer dataBuff = {0};
    BSL_PEM_Symbol symbol = {PEM_BEGIN_STR, PEM_END_STR};
    int32_t ret = HITLS_APP_ReadData(inFilePath, &symbol, "key", &dataBuff);
    if (ret != HITLS_APP_SUCCESS) {
        return ret;
    }
    uint32_t fileSize = dataBuff.dataLen;
    char *tmp = (char *)dataBuff.data;
    uint32_t readLen = 0;
    while (readLen < fileSize) {
        uint32_t remain = fileSize - readLen;
        char *nextline = memchr(tmp, '\n', remain);
        uint32_t curLineLen = (nextline != NULL) ? (uint32_t)(nextline - tmp + 1) : remain;

        if (*name == NULL) {
            *name = GetPemKeyFileName(tmp, curLineLen);
        } else if (curLineLen < PEM_END_STR_LEN && strncmp(tmp, PEM_END_STR, PEM_END_STR_LEN) == 0) {
            break; // Read the end of the pem.
        } else if (!isParseHeader) {
            *isEncrypted = IsNeedEncryped(*name, tmp, curLineLen);
            isParseHeader = true;
        }
        tmp += curLineLen;
        readLen += curLineLen;
    }
    if (readLen == 0 || *name == NULL) {
        AppPrintError("Failed to read the pem file.\n");
        BSL_SAL_FREE(dataBuff.data);
        BSL_SAL_FREE(*name);
        return HITLS_APP_STDIN_FAIL;
    }
    *inData = dataBuff.data;
    *inDataSize = readLen;
    return HITLS_APP_SUCCESS;
}

static int32_t GetPasswdByFile(const char *passwdArg, size_t passwdArgLen, char **pass)
{
    if (passwdArgLen <= APP_PASS_FILE_STR_LEN) {
        AppPrintError("Failed to read passwd from file.\n");
        return HITLS_APP_INVALID_ARG;
    }
    // Apply for a new memory and copy the unprocessed character string.
    char filePath[PATH_MAX] = {0};
    size_t pathLen = strlen(passwdArg + APP_PASS_FILE_STR_LEN);
    if (pathLen >= PATH_MAX) {
        AppPrintError("Failed to read passwd from file.\n");
        return HITLS_APP_INTERNAL_EXCEPTION;
    }
    memcpy(filePath, passwdArg + APP_PASS_FILE_STR_LEN, pathLen + 1);
    // Binding the password file UIO.
    BSL_UIO *passUio = BSL_UIO_New(BSL_UIO_FileMethod());
    if (passUio == NULL) {
        AppPrintError("Failed to read passwd from file.\n");
        return HITLS_APP_UIO_FAIL;
    }
    BSL_UIO_SetIsUnderlyingClosedByUio(passUio, true);
    if (BSL_UIO_Ctrl(passUio, BSL_UIO_FILE_OPEN, BSL_UIO_FILE_READ, filePath) != BSL_SUCCESS) {
        AppPrintError("Failed to set infile mode for passwd.\n");
        BSL_UIO_Free(passUio);
        return HITLS_APP_UIO_FAIL;
    }
    char *passBuf = (char *)BSL_SAL_Calloc(APP_MAX_PASS_LENGTH + 1 + 1, 1);
    if (passBuf == NULL) {
        BSL_UIO_Free(passUio);
        AppPrintError("Failed to read passwd from file.\n");
        return HITLS_APP_MEM_ALLOC_FAIL;
    }
    // When the number of bytes exceeds 1024 bytes, only one more bit is read.
    uint32_t passLen = APP_MAX_PASS_LENGTH + 1 + 1;
    if (BSL_UIO_Gets(passUio, passBuf, &passLen) != BSL_SUCCESS) {
        AppPrintError("Failed to read passwd from file.\n");
        BSL_UIO_Free(passUio);
        BSL_SAL_FREE(passBuf);
        return HITLS_APP_UIO_FAIL;
    }
    BSL_UIO_Free(passUio);

    if (passLen <= 0) {
        passBuf[0] = '\0';
    } else if (passBuf[passLen - 1] == '\n') {
        passBuf[passLen - 1] = '\0';
    }
    *pass = passBuf;
    return HITLS_APP_SUCCESS;
}

static char *GetPasswdByStdin(BSL_UI_ReadPwdParam *param)
{
    uint32_t passLen = APP_MAX_PASS_LENGTH + 1;
    char *pass = (char *)BSL_SAL_Calloc(APP_MAX_PASS_LENGTH + 1, 1);
    if (pass == NULL) {
        AppPrintError("Fail to alloc memory.\n");
        return NULL;
    }
    if (BSL_UI_ReadPwdUtil(param, pass, &passLen, NULL, NULL) != BSL_SUCCESS) {
        BSL_SAL_FREE(pass);
        AppPrintError("Fail to read pwd from stdin.\n");
        return NULL;
    }
    return pass;
}

static char *GetStrAfterPreFix(const char *inputArg, uint32_t inputArgLen, uint32_t prefixLen)
{
    if (prefixLen > inputArgLen) {
        return NULL;
    }
    uint32_t len = inputArgLen - prefixLen;
    char *str = (char *)BSL_SAL_Calloc(len + 1, 1);
    if (str == NULL) {
        return NULL;
    }
    memcpy(str, inputArg + prefixLen, len);
    str[len] = '\0';
    return str;
}

int32_t HITLS_APP_ParsePasswd(const char *passArg, char **pass)
{
    if (passArg == NULL) {
        return HITLS_APP_SUCCESS;
    }
    if (strncmp(passArg, APP_PASS_ARG_STR, APP_PASS_ARG_STR_LEN) == 0) {
        *pass = GetStrAfterPreFix(passArg, strlen(passArg), APP_PASS_ARG_STR_LEN);
    } else if (strncmp(passArg, APP_PASS_STDIN_STR, APP_PASS_STDIN_STR_LEN) == 0) {
        BSL_UI_ReadPwdParam passParam = { "passwd", NULL, false };
        *pass = GetPasswdByStdin(&passParam);
    } else if (strncmp(passArg, APP_PASS_FILE_STR, APP_PASS_FILE_STR_LEN) == 0) {
        return GetPasswdByFile(passArg, strlen(passArg), pass);
    } else {
        AppPrintError("Unsupported password source. Use pass:, stdin, or file:.\n");
        return HITLS_APP_PASSWD_FAIL;
    }
    if (*pass == NULL) {
        return HITLS_APP_PASSWD_FAIL;
    }
    return HITLS_APP_SUCCESS;
}

static CRYPT_EAL_PkeyCtx *ReadPemPrvKey(BSL_Buffer *encode, const char *name, uint8_t *pass, uint32_t passLen)
{
    int32_t type = CRYPT_ENCDEC_UNKNOW;

    if (strcmp(name, PEM_RSA_PRIVATEKEY_STR) == 0) {
        type = CRYPT_PRIKEY_RSA;
    } else if (strcmp(name, PEM_EC_PRIVATEKEY_STR) == 0) {
        type = CRYPT_PRIKEY_ECC;
    } else if (strcmp(name, PEM_PKCS8_PRIVATEKEY_STR) == 0) {
        type = CRYPT_PRIKEY_PKCS8_UNENCRYPT;
    } else if (strcmp(name, PEM_ENCRYPTED_PKCS8_PRIVATEKEY_STR) == 0) {
        type = CRYPT_PRIKEY_PKCS8_ENCRYPT;
    }

    CRYPT_EAL_PkeyCtx *pkey = NULL;
    if (CRYPT_EAL_DecodeBuffKey(BSL_FORMAT_PEM, type, encode, pass, passLen, &pkey) != CRYPT_SUCCESS) {
        return NULL;
    }
    return pkey;
}

static CRYPT_EAL_PkeyCtx *ReadPemPubKey(BSL_Buffer *encode, const char *name)
{
    int32_t type = CRYPT_ENCDEC_UNKNOW;

    if (strcmp(name, PEM_RSA_PUBLIC_STR) == 0) {
        type = CRYPT_PUBKEY_RSA;
    } else if (strcmp(name, PEM_PKCS8_PUBLIC_STR) == 0) {
        type = CRYPT_PUBKEY_SUBKEY;
    }

    CRYPT_EAL_PkeyCtx *pkey = NULL;
    if (CRYPT_EAL_DecodeBuffKey(BSL_FORMAT_PEM, type, encode, NULL, 0, &pkey) != CRYPT_SUCCESS) {
        return NULL;
    }
    return pkey;
}

int32_t HITLS_APP_GetPasswd(BSL_UI_ReadPwdParam *param, char **passin, uint32_t *passLen)
{
    if (*passin == NULL) {
        *passin = GetPasswdByStdin(param);
    }
    if ((*passin == NULL) || (strlen(*passin) > APP_MAX_PASS_LENGTH) || (strlen(*passin) < APP_MIN_PASS_LENGTH)) {
        HITLS_APP_PrintPassErrlog();
        return HITLS_APP_PASSWD_FAIL;
    }
    *passLen = strlen(*passin);
    return HITLS_APP_SUCCESS;
}

static bool CheckFilePath(const char *filePath)
{
    if (filePath == NULL) {
        return true;
    }
    if (strlen(filePath) > PATH_MAX) {
        AppPrintError("The maximum length of the file path is %d.\n", PATH_MAX);
        return false;
    }
    return true;
}

static CRYPT_EAL_PkeyCtx *LoadPrvDerKey(const char *inFilePath)
{
    static CRYPT_ENCDEC_TYPE encodeType[] = {CRYPT_PRIKEY_ECC, CRYPT_PRIKEY_RSA, CRYPT_PRIKEY_PKCS8_UNENCRYPT};

    CRYPT_EAL_PkeyCtx *pkey = NULL;
    for (uint32_t i = 0; i < sizeof(encodeType) / sizeof(CRYPT_ENCDEC_TYPE); ++i) {
        if (CRYPT_EAL_DecodeFileKey(BSL_FORMAT_ASN1, encodeType[i], inFilePath, NULL, 0, &pkey) == CRYPT_SUCCESS) {
            break;
        }
    }

    if (pkey == NULL) {
        AppPrintError("Failed to read the private key from \"%s\".\n", inFilePath);
        return NULL;
    }

    return pkey;
}

static CRYPT_EAL_PkeyCtx *ProviderLoadPrvDerKey(CRYPT_EAL_LibCtx *libCtx, const char *attrName, const char *inFilePath)
{
    static CRYPT_PKEY_AlgId encodeType[] = {CRYPT_PKEY_SM2, CRYPT_PKEY_RSA};
    const char *encodeTypeStr[] = {"PRIKEY_ECC", "PRIKEY_RSA"};
    CRYPT_EAL_PkeyCtx *pkey = NULL;
    for (uint32_t i = 0; i < sizeof(encodeType) / sizeof(CRYPT_ENCDEC_TYPE); ++i) {
        if (CRYPT_EAL_ProviderDecodeFileKey(libCtx, attrName, encodeType[i], "ASN1", encodeTypeStr[i], inFilePath,
            NULL, &pkey) == CRYPT_SUCCESS) {
            break;
        }
    }

    if (pkey == NULL) {
        AppPrintError("Failed to read the private key from \"%s\".\n", inFilePath);
        return NULL;
    }
    return pkey;
}

static CRYPT_EAL_PkeyCtx *ProviderReadPemPrvKey(CRYPT_EAL_LibCtx *libCtx, const char *attrName, BSL_Buffer *encode,
    uint8_t *pass, uint32_t passLen)
{
    CRYPT_EAL_PkeyCtx *pkey = NULL;
    BSL_Buffer passBuf = { pass, passLen };
    if (CRYPT_EAL_ProviderDecodeBuffKey(libCtx, attrName, BSL_CID_UNKNOWN, "PEM", NULL,
        encode, &passBuf, &pkey) != CRYPT_SUCCESS) {
        return NULL;
    }
    return pkey;
}

CRYPT_EAL_PkeyCtx *HITLS_APP_ProviderLoadPrvKey(CRYPT_EAL_LibCtx *libCtx, const char *attrName,
    const char *inFilePath, BSL_ParseFormat informat, char **passin)
{
    if (inFilePath == NULL && informat == BSL_FORMAT_ASN1) {
        AppPrintError("The \"-inform DER or -keyform DER\" requires using the \"-in\" option.\n");
        return NULL;
    }
    if (!CheckFilePath(inFilePath)) {
        return NULL;
    }
    if (informat == BSL_FORMAT_ASN1) {
        return ProviderLoadPrvDerKey(libCtx, attrName, inFilePath);
    }
    char *prvkeyName = NULL;
    bool isEncrypted = false;
    uint8_t *data = NULL;
    uint32_t dataLen = 0;
    if (ReadPemKeyFile(inFilePath, &data, &dataLen, &prvkeyName, &isEncrypted) != HITLS_APP_SUCCESS) {
        PrintFileOrStdinError(inFilePath, "Failed to read the private key");
        return NULL;
    }

    uint8_t *pass = NULL;
    uint32_t passLen = 0;
    BSL_UI_ReadPwdParam passParam = { "passwd", inFilePath, false };
    if (isEncrypted && (HITLS_APP_GetPasswd(&passParam, passin, &passLen) != HITLS_APP_SUCCESS)) {
        BSL_SAL_ClearFree(data, dataLen);
        BSL_SAL_FREE(prvkeyName);
        return NULL;
    }
    pass = (uint8_t *)*passin;
    BSL_Buffer encode = { data, dataLen };
    CRYPT_EAL_PkeyCtx *pkey = ProviderReadPemPrvKey(libCtx, attrName, &encode, pass, passLen);
    if (pkey == NULL) {
        PrintFileOrStdinError(inFilePath, "Failed to read the private key");
    }
    BSL_SAL_CleanseData(pass, passLen);
    BSL_SAL_ClearFree(data, dataLen);
    BSL_SAL_FREE(prvkeyName);
    return pkey;
}

CRYPT_EAL_PkeyCtx *HITLS_APP_LoadPrvKey(const char *inFilePath, BSL_ParseFormat informat, char **passin)
{
    if (inFilePath == NULL && informat == BSL_FORMAT_ASN1) {
        AppPrintError("The \"-inform DER or -keyform DER\" requires using the \"-in\" option.\n");
        return NULL;
    }
    if (!CheckFilePath(inFilePath)) {
        return NULL;
    }
    if (informat == BSL_FORMAT_ASN1) {
        return LoadPrvDerKey(inFilePath);
    }
    char *prvkeyName = NULL;
    bool isEncrypted = false;
    uint8_t *data = NULL;
    uint32_t dataLen = 0;
    if (ReadPemKeyFile(inFilePath, &data, &dataLen, &prvkeyName, &isEncrypted) != HITLS_APP_SUCCESS) {
        PrintFileOrStdinError(inFilePath, "Failed to read the private key");
        return NULL;
    }

    uint8_t *pass = NULL;
    uint32_t passLen = 0;
    BSL_UI_ReadPwdParam passParam = { "passwd", inFilePath, false };
    if (isEncrypted && (HITLS_APP_GetPasswd(&passParam, passin, &passLen) != HITLS_APP_SUCCESS)) {
        BSL_SAL_ClearFree(data, dataLen);
        BSL_SAL_FREE(prvkeyName);
        return NULL;
    }
    pass = (uint8_t *)*passin;
    BSL_Buffer encode = { data, dataLen };
    CRYPT_EAL_PkeyCtx *pkey = ReadPemPrvKey(&encode, prvkeyName, pass, passLen);
    if (pkey == NULL) {
        PrintFileOrStdinError(inFilePath, "Failed to read the private key");
    }
    BSL_SAL_CleanseData(pass, passLen);
    BSL_SAL_ClearFree(data, dataLen);
    BSL_SAL_FREE(prvkeyName);
    return pkey;
}

CRYPT_EAL_PkeyCtx *HITLS_APP_LoadPubKey(const char *inFilePath, BSL_ParseFormat informat)
{
    if (informat != BSL_FORMAT_PEM) {
        AppPrintError("Reading public key from non-PEM files is not supported.\n");
        return NULL;
    }
    char *pubKeyName = NULL;
    uint8_t *data = NULL;
    uint32_t dataLen = 0;
    bool isEncrypted = false;
    if (!CheckFilePath(inFilePath) ||
        (ReadPemKeyFile(inFilePath, &data, &dataLen, &pubKeyName, &isEncrypted) != HITLS_APP_SUCCESS)) {
        PrintFileOrStdinError(inFilePath, "Failed to read the public key");
        return NULL;
    }
    BSL_Buffer encode = { data, dataLen };
    CRYPT_EAL_PkeyCtx *pkey = ReadPemPubKey(&encode, pubKeyName);
    if (pkey == NULL) {
        PrintFileOrStdinError(inFilePath, "Failed to read the public key");
    }
    BSL_SAL_FREE(data);
    BSL_SAL_FREE(pubKeyName);
    return pkey;
}

int32_t HITLS_APP_PrintPubKey(CRYPT_EAL_PkeyCtx *pkey, const char *outFilePath, BSL_ParseFormat outformat)
{
    if (!CheckFilePath(outFilePath)) {
        return HITLS_APP_INVALID_ARG;
    }

    BSL_Buffer pubKeyBuf = { 0 };
    if (CRYPT_EAL_EncodeBuffKey(pkey, NULL, outformat, CRYPT_PUBKEY_SUBKEY, &pubKeyBuf) != CRYPT_SUCCESS) {
        AppPrintError("Failed to export the public key.\n");
        return HITLS_APP_ENCODE_KEY_FAIL;
    }
    BSL_UIO *wPubKeyUio = HITLS_APP_UioOpen(outFilePath, 'w', outFilePath != NULL ? 1 : 0);
    if (wPubKeyUio == NULL) {
        BSL_SAL_FREE(pubKeyBuf.data);
        return HITLS_APP_UIO_FAIL;
    }
    int32_t ret = HITLS_APP_OptWriteUio(wPubKeyUio, pubKeyBuf.data, pubKeyBuf.dataLen, HITLS_APP_FORMAT_PEM);
    BSL_SAL_FREE(pubKeyBuf.data);
    BSL_UIO_Free(wPubKeyUio);
    return ret;
}

int32_t HITLS_APP_PrintPrvKey(CRYPT_EAL_PkeyCtx *pkey, const char *outFilePath, BSL_ParseFormat outformat,
    int32_t cipherAlgCid, char **passout)
{
    if (!CheckFilePath(outFilePath)) {
        return HITLS_APP_INVALID_ARG;
    }

    BSL_UIO *wPrvUio = HITLS_APP_UioOpen(outFilePath, 'w', outFilePath != NULL ? 1 : 0);
    if (wPrvUio == NULL) {
        return HITLS_APP_UIO_FAIL;
    }
    AppKeyPrintParam param = { outFilePath, outformat, cipherAlgCid, false, false};
    int32_t ret = HITLS_APP_PrintPrvKeyByUio(wPrvUio, pkey, &param, passout);
    BSL_UIO_Free(wPrvUio);
    return ret;
}

int32_t HITLS_APP_PrintPrvKeyByUio(BSL_UIO *uio, CRYPT_EAL_PkeyCtx *pkey, AppKeyPrintParam *printKeyParam,
    char **passout)
{
    int32_t ret = printKeyParam->text ? CRYPT_EAL_PrintPrikey(0, pkey, uio) : HITLS_APP_SUCCESS;
    if (ret != HITLS_APP_SUCCESS) {
        AppPrintError("Failed to print the private key text, errCode = 0x%x.\n", ret);
        return HITLS_APP_CRYPTO_FAIL;
    }
    if (printKeyParam->noout) {
        return HITLS_APP_SUCCESS;
    }
    int32_t type =
        printKeyParam->cipherAlgCid != CRYPT_CIPHER_MAX ? CRYPT_PRIKEY_PKCS8_ENCRYPT : CRYPT_PRIKEY_PKCS8_UNENCRYPT;
    uint8_t *pass = NULL;
    uint32_t passLen = 0;
    BSL_UI_ReadPwdParam passParam = { "passwd", printKeyParam->name, true };
    if ((type == CRYPT_PRIKEY_PKCS8_ENCRYPT) &&
        (HITLS_APP_GetPasswd(&passParam, passout, &passLen) != HITLS_APP_SUCCESS)) {
        return HITLS_APP_PASSWD_FAIL;
    }
    pass = (uint8_t *)*passout;
    CRYPT_Pbkdf2Param param = { 0 };
    param.pbesId = BSL_CID_PBES2;
    param.pbkdfId = BSL_CID_PBKDF2;
    param.hmacId = CRYPT_MAC_HMAC_SHA256;
    param.symId = printKeyParam->cipherAlgCid;
    param.pwd = pass;
    param.saltLen = DEFAULT_SALTLEN;
    param.pwdLen = passLen;
    param.itCnt = DEFAULT_ITCNT;
    CRYPT_EncodeParam paramEx = { CRYPT_DERIVE_PBKDF2, &param };
    BSL_Buffer prvKeyBuf = { 0 };
    if (CRYPT_EAL_EncodeBuffKey(pkey, &paramEx, printKeyParam->outformat, type, &prvKeyBuf) != CRYPT_SUCCESS) {
        AppPrintError("Failed to export the private key.\n");
        return HITLS_APP_ENCODE_KEY_FAIL;
    }
    ret = HITLS_APP_OptWriteUio(uio, prvKeyBuf.data, prvKeyBuf.dataLen, HITLS_APP_FORMAT_PEM);
    BSL_SAL_ClearFree(prvKeyBuf.data, prvKeyBuf.dataLen);
    return ret;
}

int32_t HITLS_APP_GetAndCheckCipherOpt(const char *name, int32_t *symId)
{
    if (symId == NULL) {
        return HITLS_APP_INVALID_ARG;
    }
    uint32_t cid = (uint32_t)HITLS_APP_GetCidByName(name, HITLS_APP_LIST_OPT_CIPHER_ALG);
    if (cid == CRYPT_CIPHER_MAX) {
        AppPrintError("%s: Use the [list -cipher-algorithms] command to view supported encryption algorithms.\n",
            HITLS_APP_GetProgName());
        return HITLS_APP_OPT_UNKOWN;
    }
    if (!CRYPT_EAL_CipherIsValidAlgId(cid)) {
        AppPrintError("%s: %s ciphers not supported.\n", HITLS_APP_GetProgName(), name);
        return HITLS_APP_OPT_UNKOWN;
    }
    uint32_t isAeadId = 1;

    if (CRYPT_EAL_CipherGetInfo((CRYPT_CIPHER_AlgId)cid, CRYPT_INFO_IS_AEAD, &isAeadId) != CRYPT_SUCCESS) {
        AppPrintError("%s: The encryption algorithm is not supported\n", HITLS_APP_GetProgName());
        return HITLS_APP_INVALID_ARG;
    }

    if (isAeadId == 1) {
        AppPrintError("%s: The AEAD encryption algorithm is not supported\n", HITLS_APP_GetProgName());
        return HITLS_APP_INVALID_ARG;
    }
    *symId = cid;
    return HITLS_APP_SUCCESS;
}

static int32_t ReadPemByUioSymbol(BSL_UIO *memUio, BSL_UIO *rUio, BSL_PEM_Symbol *symbol)
{
    int32_t ret = HITLS_APP_UIO_FAIL;
    char buf[APP_LINESIZE + 1 + 1] = {0}; // +1+1 for '\n' '\0'
    uint32_t lineLen;
    bool hasHead = false;
    uint32_t writeMemLen;
    int64_t dataLen = 0;

    while (true) {
        lineLen = APP_LINESIZE + 1;
        memset(buf, 0, lineLen);

        // Reads a row of data.
        if ((BSL_UIO_Gets(rUio, buf, &lineLen) != BSL_SUCCESS) || (lineLen == 0)) {
            break;
        }
        int32_t ctrlRet = BSL_UIO_Ctrl(rUio, BSL_UIO_GET_READ_NUM, sizeof(int64_t), &dataLen);
        if (ctrlRet != BSL_SUCCESS || dataLen > APP_FILE_MAX_SIZE) {
            AppPrintError("The maximum file size is %zukb.\n", APP_FILE_MAX_SIZE_KB);
            break;
        }
        if (!hasHead) {
            // Check whether it is the head.
            if (strncmp(buf, symbol->head, strlen(symbol->head)) != 0) {
                continue;
            }
            if (BSL_UIO_Puts(memUio, (const char *)buf, &writeMemLen) != BSL_SUCCESS || writeMemLen != lineLen) {
                break;
            }
            hasHead = true;
            continue;
        }
        // Check whether it is the tail.
        if (strncmp(buf, symbol->tail, strlen(symbol->tail)) == 0) {
            if (BSL_UIO_Write(memUio, (const uint8_t *)buf, lineLen + 1, &writeMemLen) == BSL_SUCCESS &&
                writeMemLen == lineLen + 1) {
                ret = HITLS_APP_SUCCESS;
            }
            break;
        }
        // Copy the intermediate content.
        if (BSL_UIO_Puts(memUio, (const char *)buf, &writeMemLen) != BSL_SUCCESS || writeMemLen != lineLen) {
            break;
        }
    }
    return ret;
}

static int32_t ReadBlockFromStdin(BSL_UIO *memUio, BSL_UIO *rUio, uint32_t maxSize)
{
    int32_t ret = HITLS_APP_UIO_FAIL;
    uint32_t readLen;
    uint32_t writeLen;
    uint64_t totalLen = 0;
    uint8_t *buf = (uint8_t *)BSL_SAL_Calloc(MAX_DIGEST_SIZE + 1, 1);
    if (buf == NULL) {
        return HITLS_APP_MEM_ALLOC_FAIL;
    }

    while (true) {
        readLen = MAX_DIGEST_SIZE;
        if (BSL_UIO_Read(rUio, buf, MAX_DIGEST_SIZE, &readLen) != BSL_SUCCESS) {
            break;
        }
        if (readLen == 0) {
            /* EOF */
            ret = HITLS_APP_SUCCESS;
            break;
        }
        if (totalLen + readLen > maxSize) {
            AppPrintError("The maximum file size is %ukb.\n", maxSize / 1024);
            break;
        }
        if (BSL_UIO_Write(memUio, buf, readLen, &writeLen) != BSL_SUCCESS || writeLen != readLen) {
            break;
        }
        totalLen += readLen;
    }
    BSL_SAL_FREE(buf);
    return ret;
}

static int32_t ReadPemFromStdin(BSL_BufMem **data, BSL_PEM_Symbol *symbol, uint32_t maxSize)
{
    int32_t ret = HITLS_APP_UIO_FAIL;
    BSL_UIO *memUio = BSL_UIO_New(BSL_UIO_MemMethod());
    if (memUio == NULL) {
        return ret;
    }

    // Read from stdin or file.
    BSL_UIO *rUio = HITLS_APP_UioOpen(NULL, 'r', 0);
    if (rUio == NULL) {
        BSL_UIO_Free(memUio);
        return ret;
    }
    if (symbol == NULL || symbol->head == NULL || symbol->tail == NULL) {
        ret = ReadBlockFromStdin(memUio, rUio, maxSize);
    } else {
        ret = ReadPemByUioSymbol(memUio, rUio, symbol);
    }
    BSL_UIO_Free(rUio);
    if (ret == HITLS_APP_SUCCESS) {
        if (BSL_UIO_Ctrl(memUio, BSL_UIO_MEM_GET_PTR, sizeof(BSL_BufMem *), data) == BSL_SUCCESS) {
            BSL_UIO_SetIsUnderlyingClosedByUio(memUio, false);
            BSL_SAL_Free(BSL_UIO_GetCtx(memUio));
            BSL_UIO_SetCtx(memUio, NULL);
        } else {
            ret = HITLS_APP_UIO_FAIL;
        }
    }
    BSL_UIO_Free(memUio);
    return ret;
}

static int32_t ReadFileData(const char *path, BSL_Buffer *data)
{
    int32_t ret = CheckFileSizeByPath(path, NULL);
    if (ret != HITLS_APP_SUCCESS) {
        return ret;
    }
    ret = BSL_SAL_ReadFile(path, &data->data, &data->dataLen);
    if (ret != BSL_SUCCESS) {
        AppPrintError("Read file failed: %s.\n", path);
    }
    return ret;
}

int32_t HITLS_APP_ReadData(const char *path, BSL_PEM_Symbol *symbol, char *fileName, BSL_Buffer *data)
{
    if (data == NULL || fileName == NULL) {
        return HITLS_APP_INVALID_ARG;
    }
    if (path == NULL) {
        BSL_BufMem *buf = NULL;
        if (ReadPemFromStdin(&buf, symbol, APP_FILE_MAX_SIZE) != HITLS_APP_SUCCESS) {
            AppPrintError("Failed to read %s from stdin.\n", fileName);
            return HITLS_APP_UIO_FAIL;
        }
        data->data = (uint8_t *)buf->data;
        data->dataLen = buf->length;
        BSL_SAL_Free(buf);
        return HITLS_APP_SUCCESS;
    } else {
        return ReadFileData(path, data);
    }
}

HITLS_X509_Cert *HITLS_APP_LoadCert(const char *inPath, BSL_ParseFormat inform)
{
    if (inPath == NULL && inform == BSL_FORMAT_ASN1) {
        AppPrintError("Reading DER files from stdin is not supported.\n");
        return NULL;
    }
    if (!CheckFilePath(inPath)) {
        AppPrintError("Invalid cert path: \"%s\".", inPath == NULL ? "" : inPath);
        return NULL;
    }
    BSL_Buffer data = { 0 };
    BSL_PEM_Symbol symbol = { BSL_PEM_CERT_BEGIN_STR, BSL_PEM_CERT_END_STR };
    int32_t ret = HITLS_APP_ReadData(inPath, &symbol, "cert", &data);
    if (ret != HITLS_APP_SUCCESS) {
        return NULL;
    }

    HITLS_X509_Cert *cert = NULL;
    if (HITLS_X509_CertParseBuff(inform, &data, &cert) != 0) {
        AppPrintError("Failed to parse cert: \"%s\".\n", inPath == NULL ? "stdin" : inPath);
        BSL_SAL_Free(data.data);
        return NULL;
    }
    BSL_SAL_Free(data.data);
    return cert;
}

HITLS_X509_Csr *HITLS_APP_LoadCsr(const char *inPath, BSL_ParseFormat inform)
{
    if (inPath == NULL && inform == BSL_FORMAT_ASN1) {
        AppPrintError("Reading DER files from stdin is not supported.\n");
        return NULL;
    }
    if (!CheckFilePath(inPath)) {
        AppPrintError("Invalid csr path: \"%s\".", inPath == NULL ? "" : inPath);
        return NULL;
    }

    BSL_Buffer data = { 0 };
    BSL_PEM_Symbol symbol = { BSL_PEM_CERT_REQ_BEGIN_STR, BSL_PEM_CERT_REQ_END_STR };
    int32_t ret = HITLS_APP_ReadData(inPath, &symbol, "csr", &data);
    if (ret != HITLS_APP_SUCCESS) {
        return NULL;
    }

    HITLS_X509_Csr *csr = NULL;
    if (HITLS_X509_CsrParseBuff(inform, &data, &csr) != 0) {
        AppPrintError("Failed to parse csr: \"%s\".\n", inPath == NULL ? "stdin" : inPath);
        BSL_SAL_Free(data.data);
        return NULL;
    }
    BSL_SAL_Free(data.data);
    return csr;
}

HITLS_X509_Crl *HITLS_APP_LoadCrl(const char *inPath, BSL_ParseFormat inform)
{
    if (inPath == NULL && inform == BSL_FORMAT_ASN1) {
        AppPrintError("Reading DER files from stdin is not supported.\n");
        return NULL;
    }
    if (!CheckFilePath(inPath)) {
        AppPrintError("Invalid crl path: \"%s\".", inPath == NULL ? "" : inPath);
        return NULL;
    }

    BSL_Buffer data = { 0 };
    BSL_PEM_Symbol symbol = { BSL_PEM_CRL_BEGIN_STR, BSL_PEM_CRL_END_STR };
    int32_t ret = HITLS_APP_ReadData(inPath, &symbol, "crl", &data);
    if (ret != HITLS_APP_SUCCESS) {
        return NULL;
    }

    HITLS_X509_Crl *crl = NULL;
    if (HITLS_X509_CrlParseBuff(inform, &data, &crl) != 0) {
        AppPrintError("Failed to parse crl: \"%s\".\n", inPath == NULL ? "stdin" : inPath);
        BSL_SAL_Free(data.data);
        return NULL;
    }
    BSL_SAL_Free(data.data);
    return crl;
}

int32_t HITLS_APP_GetAndCheckHashOpt(const char *name, int32_t *hashId)
{
    if (hashId == NULL) {
        return HITLS_APP_INVALID_ARG;
    }
    uint32_t cid = (uint32_t)HITLS_APP_GetCidByName(name, HITLS_APP_LIST_OPT_DGST_ALG);
    if (cid == BSL_CID_UNKNOWN) {
        AppPrintError("%s: Use the [list -digest-algorithms] command to view supported digest algorithms.\n",
            HITLS_APP_GetProgName());
        return HITLS_APP_OPT_UNKOWN;
    }
    if (!CRYPT_EAL_MdIsValidAlgId(cid)) {
        AppPrintError("%s: %s digest not supported.\n", HITLS_APP_GetProgName(), name);
        return HITLS_APP_OPT_UNKOWN;
    }
    *hashId = cid;
    return HITLS_APP_SUCCESS;
}

int32_t HITLS_APP_PrintText(const BSL_Buffer *csrBuf, const char *outFileName)
{
    BSL_UIO *wCsrUio = HITLS_APP_UioOpen(outFileName, 'w', outFileName != NULL ? 1 : 0);
    if (wCsrUio == NULL) {
        return HITLS_APP_UIO_FAIL;
    }
    int32_t ret = HITLS_APP_OptWriteUio(wCsrUio, csrBuf->data, csrBuf->dataLen, HITLS_APP_FORMAT_TEXT);
    BSL_UIO_Free(wCsrUio);
    return ret;
}

CRYPT_EAL_PkeyCtx *HITLS_APP_GenRsaPkeyCtx(uint32_t bits)
{
    CRYPT_EAL_PkeyCtx *pkey = CRYPT_EAL_ProviderPkeyNewCtx(NULL, CRYPT_PKEY_RSA,
        CRYPT_EAL_PKEY_UNKNOWN_OPERATE, "provider=default");
    if (pkey == NULL) {
        AppPrintError("%s: Failed to initialize the RSA private key.\n", HITLS_APP_GetProgName());
        return NULL;
    }
    CRYPT_EAL_PkeyPara *para = BSL_SAL_Calloc(sizeof(CRYPT_EAL_PkeyPara), 1);
    if (para == NULL) {
        CRYPT_EAL_PkeyFreeCtx(pkey);
        return NULL;
    }
    static uint8_t e[] = {0x01, 0x00, 0x01}; // Default E value
    para->id = CRYPT_PKEY_RSA;
    para->para.rsaPara.bits = bits;
    para->para.rsaPara.e = e;
    para->para.rsaPara.eLen = sizeof(e);
    if (CRYPT_EAL_PkeySetPara(pkey, para) != CRYPT_SUCCESS) {
        AppPrintError("%s: Failed to set RSA parameters.\n", HITLS_APP_GetProgName());
        BSL_SAL_FREE(para);
        CRYPT_EAL_PkeyFreeCtx(pkey);
        return NULL;
    }
    BSL_SAL_FREE(para);
    if (CRYPT_EAL_PkeyGen(pkey) != CRYPT_SUCCESS) {
        AppPrintError("%s: Failed to generate the RSA private key.\n", HITLS_APP_GetProgName());
        CRYPT_EAL_PkeyFreeCtx(pkey);
        return NULL;
    }

    int32_t padType = CRYPT_EMSA_PKCSV15;
    if (CRYPT_EAL_PkeyCtrl(pkey, CRYPT_CTRL_SET_RSA_PADDING, &padType, sizeof(padType)) != CRYPT_SUCCESS) {
        AppPrintError("%s: Failed to set rsa padding.\n", HITLS_APP_GetProgName());
        CRYPT_EAL_PkeyFreeCtx(pkey);
        return NULL;
    }
    return pkey;
}

void HITLS_APP_PrintPassErrlog(void)
{
    AppPrintError("The password length is incorrect. It should be in the range of %d to %d.\n", APP_MIN_PASS_LENGTH,
        APP_MAX_PASS_LENGTH);
}

int32_t HITLS_APP_ParseHex(const char *hexStr, bool expectPrefix, uint8_t **bytes, uint32_t *bytesLen)
{
    if (hexStr == NULL || bytes == NULL || bytesLen == NULL) {
        AppPrintError("Invalid input parameters.\n");
        return HITLS_APP_INVALID_ARG;
    }

    uint32_t prefixLen = strlen(APP_HEX_HEAD);
    if (expectPrefix && (strlen(hexStr) <= prefixLen || strncmp(hexStr, APP_HEX_HEAD, prefixLen) != 0)) {
        AppPrintError("Invalid hex value, should start with '0x'.\n");
        return HITLS_APP_OPT_VALUE_INVALID;
    }
    const char *num = hexStr;
    if (expectPrefix) {
        num += prefixLen;
    }
    uint32_t hexLen = strlen(num);
    // Skip the preceding zeros.
    for (uint32_t i = 0; i < hexLen; ++i) {
        if (num[i] != '0' && (i + 1) != hexLen) {
            num += i;
            hexLen -= i;
            break;
        }
    }
    *bytesLen = (hexLen + 1) / APP_HEX_TO_BYTE;
    uint8_t *res = BSL_SAL_Malloc(*bytesLen);
    if (res == NULL) {
        AppPrintError("Allocate memory failed.\n");
        return HITLS_APP_MEM_ALLOC_FAIL;
    }

    int32_t ret = HITLS_APP_SUCCESS;
    if (hexLen % APP_HEX_TO_BYTE == 1) {
        char *tmp = BSL_SAL_Malloc(hexLen + 2); // 2: '0' + '\0'
        if (tmp == NULL) {
            AppPrintError("Allocate memory failed.\n");
            BSL_SAL_Free(res);
            return HITLS_APP_MEM_ALLOC_FAIL;
        }
        tmp[0] = '0';
        memcpy(tmp + 1, num, hexLen);
        tmp[hexLen + 1] = '\0';
        ret = HITLS_APP_HexToBytes(tmp, res, bytesLen);
        BSL_SAL_Free(tmp);
    } else {
        ret = HITLS_APP_HexToBytes(num, res, bytesLen);
    }
    if (ret != HITLS_APP_SUCCESS) {
        BSL_SAL_Free(res);
        return ret;
    }
    *bytes = res;
    return HITLS_APP_SUCCESS;
}

int32_t HITLS_APP_HexToBytes(const char *hexStr, uint8_t *bytes, uint32_t *bytesLen)
{
    if (hexStr == NULL || bytes == NULL || bytesLen == NULL) {
        AppPrintError("Invalid input buffer or output buffer.\n");
        return HITLS_APP_INVALID_ARG;
    }
    size_t inLen = strlen(hexStr);
    if (inLen == 0 || inLen % 2 != 0 || *bytesLen < inLen / 2) { // 2: one byte to two hex chars.
        return HITLS_APP_INVALID_ARG;
    }

    // A group of 2 bytes
    for (size_t i = 0; i < inLen; i += 2) {
        if (!((hexStr[i] >= '0' && hexStr[i] <= '9') || (hexStr[i] >= 'a' && hexStr[i] <= 'f') ||
            (hexStr[i] >= 'A' && hexStr[i] <= 'F'))) {
            AppPrintError("Input string is not a valid hex string.\n");
            return HITLS_APP_OPT_VALUE_INVALID;
        }
        if (!((hexStr[i + 1] >= '0' && hexStr[i + 1] <= '9') || (hexStr[i + 1] >= 'a' && hexStr[i + 1] <= 'f') ||
            (hexStr[i + 1] >= 'A' && hexStr[i + 1] <= 'F'))) {
            AppPrintError("Input string is not a valid hex string.\n");
            return HITLS_APP_OPT_VALUE_INVALID;
        }
        // Formula for converting hex to int: (Hex% 32 + 9)% 25 = int, hexadecimal, 16: high 4 bits.
        bytes[i / 2] = ((uint8_t)hexStr[i] % 32 + 9) % 25 * 16 + ((uint8_t)hexStr[i + 1] % 32 + 9) % 25;
    }
    *bytesLen = inLen / 2; // 2: one byte to two hex chars.
    return HITLS_APP_SUCCESS;
}

int32_t HITLS_APP_ReadFileOrStdin(uint8_t **buf, uint64_t *bufLen, const char *inFile,
                                   uint32_t maxSize, const char *module)
{
    if (buf == NULL || bufLen == NULL || module == NULL) {
        AppPrintError("%s: Invalid parameters for ReadFileOrStdin\n", module);
        return HITLS_APP_INVALID_ARG;
    }

    // Validate bufLen to prevent overflow
    if (*bufLen > UINT32_MAX) {
        AppPrintError("%s: Buffer length exceeds maximum allowed size\n", module);
        return HITLS_APP_INVALID_ARG;
    }

    // Special handling for stdin
    if (inFile == NULL) {
        BSL_BufMem *data = NULL;
        int32_t ret = ReadPemFromStdin(&data, NULL, maxSize);
        if (ret != HITLS_APP_SUCCESS) {
            AppPrintError("%s: Failed to read content from stdin\n", module);
            return ret;
        }
        *buf = (uint8_t *)data->data;
        *bufLen = data->length;
        BSL_SAL_Free(data);
        return HITLS_APP_SUCCESS;
    }

    // Read from file
    BSL_UIO *readUio = HITLS_APP_UioOpen(inFile, 'r', inFile != NULL ? 1 : 0);
    if (readUio == NULL) {
        AppPrintError("%s: Failed to open file <%s>, no such file or directory\n", module, inFile);
        return HITLS_APP_UIO_FAIL;
    }

    int32_t ret = HITLS_APP_OptReadUio(readUio, buf, bufLen, maxSize);
    if (ret != HITLS_APP_SUCCESS) {
        AppPrintError("%s: Failed to read content from file <%s>\n", module, inFile);
    }
    BSL_UIO_Free(readUio);
    return ret;
}

static int32_t InitRand(AppInitParam *param)
{
#ifdef HITLS_APP_SM_MODE
    pid_t pid = getpid();
    char str[32] = {0};
    int len = (uint32_t)snprintf(str, sizeof(str), "%d", pid);
    if (len < 0 || (size_t)len >= sizeof(str)) {
        AppPrintError("Failed to set pid, pid = %d.\n", pid);
        return HITLS_APP_INVALID_ARG;
    }
    if (param->smParam->smTag == 1 && param->randAlgId == CRYPT_RAND_SHA256) {
        param->randAlgId = CRYPT_RAND_SM4_CTR_DF;
    }
    int32_t ret = CRYPT_EAL_ProviderRandInitCtx(APP_GetCurrent_LibCtx(), param->randAlgId,
        param->provider->providerAttr, (const uint8_t *)str, (uint32_t)len, NULL);
#else
    int32_t ret = CRYPT_EAL_ProviderRandInitCtx(APP_GetCurrent_LibCtx(), param->randAlgId,
        param->provider->providerAttr, NULL, 0, NULL);
#endif
    if (ret == CRYPT_EAL_ERR_DRBG_REPEAT_INIT) {
        return HITLS_APP_SUCCESS;
    }
    if (ret != CRYPT_SUCCESS) {
        AppPrintError("Failed to init rand ctx, ret: 0x%x.\n", ret);
        return HITLS_APP_CRYPTO_FAIL;
    }
    return HITLS_APP_SUCCESS;
}

#ifdef HITLS_APP_SM_MODE
static char *g_smProviderPath = NULL;
static int32_t GetSmProviderPath(void)
{
    char *path = HITLS_APP_GetAppPath();
    if (path == NULL) {
        return HITLS_APP_INVALID_ARG;
    }
    char *lastSlash = strrchr(path, '/');
    if (lastSlash == NULL) {
        BSL_SAL_Free(path);
        return HITLS_APP_INVALID_ARG;
    }
    lastSlash[0] = '\0';
    g_smProviderPath = path;
    return HITLS_APP_SUCCESS;
}
#endif

static int32_t GetProviderParam(AppInitParam *param)
{
    if (param->provider->providerName != NULL || param->provider->providerAttr != NULL ||
        param->provider->providerPath != NULL) {
        return HITLS_APP_SUCCESS;
    }
#ifdef HITLS_APP_SM_MODE
    if (param->smParam->smTag == 1) {
        int32_t ret = GetSmProviderPath();
        if (ret != HITLS_APP_SUCCESS) {
            return ret;
        }
        param->provider->providerName = APP_SM_PROVIDER_NAME;
        param->provider->providerPath = g_smProviderPath;
        param->provider->providerAttr = APP_SM_PROVIDER_ATTR;
        return HITLS_APP_SUCCESS;
    }
#endif
    param->provider->providerName = APP_DEFAULT_PROVIDER_NAME;
    param->provider->providerPath = NULL;
    param->provider->providerAttr = APP_DEFAULT_PROVIDER_ATTR;
    return HITLS_APP_SUCCESS;
}

static void PrintSelfTestErrlog(AppInitParam *param, int32_t ret)
{
#ifdef HITLS_APP_SM_MODE
    if (param->smParam->smTag == 1) {
        HITLS_APP_SM_PrintLog(ret);
    }
#else
    (void)param;
    (void)ret;
#endif
}

int32_t HITLS_APP_Init(AppInitParam *param)
{
    if (param == NULL) {
        return HITLS_APP_INVALID_ARG;
    }
#ifdef HITLS_APP_SM_MODE
    if (param->smParam->smTag == 1) {
        param->smParam->status = HITLS_APP_SM_STATUS_INIT;
    }
#endif
    int32_t ret = GetProviderParam(param);
    if (ret != HITLS_APP_SUCCESS) {
        return ret;
    }
    ret = HITLS_APP_LoadProvider(param->provider->providerPath, param->provider->providerName);
    if (ret != HITLS_APP_SUCCESS) {
        PrintSelfTestErrlog(param, ret);
        return ret;
    }
    ret = InitRand(param);
    if (ret != HITLS_APP_SUCCESS) {
        return ret;
    }
#ifdef HITLS_APP_SM_MODE
    if (param->smParam->smTag == 1) {
        ret = HITLS_APP_SM_Init(param->provider, param->smParam->workPath, (char **)&param->smParam->password,
            &param->smParam->status);
        if (ret != HITLS_APP_SUCCESS) {
            AppPrintError("Failed to init sm, errCode: 0x%x.\n", ret);
            CRYPT_EAL_RandDeinitEx(APP_GetCurrent_LibCtx());
            return ret;
        }
        param->smParam->passwordLen = strlen((const char *)param->smParam->password);
        param->smParam->status = HITLS_APP_SM_STATUS_KEY_PARAMETER_INPUT;
        if (g_smProviderPath != NULL) {
            BSL_SAL_FREE(g_smProviderPath);
            param->provider->providerPath = NULL;
        }
    }
#endif
    return HITLS_APP_SUCCESS;
}

void HITLS_APP_Deinit(AppInitParam *param, int32_t ret)
{
#ifdef HITLS_APP_SM_MODE
    if (param != NULL && param->smParam != NULL && param->smParam->smTag == 1) {
        if (ret != HITLS_APP_SUCCESS) {
            param->smParam->status = HITLS_APP_SM_STATUS_ERROR;
        }
        if (param->smParam->password != NULL) {
            BSL_SAL_ClearFree(param->smParam->password, param->smParam->passwordLen);
            param->smParam->password = NULL;
            param->smParam->passwordLen = 0;
        }
        param->smParam->status = HITLS_APP_SM_STATUS_CLOSE;
    }
#else
    (void)param;
    (void)ret;
#endif
    CRYPT_EAL_RandDeinitEx(APP_GetCurrent_LibCtx());
}

int32_t HITLS_APP_GetTime(int64_t *time)
{
    if (time == NULL) {
        AppPrintError("Invalid time pointer.\n");
        return HITLS_APP_INVALID_ARG;
    }
    BSL_TIME sysTime = {0};
    int32_t ret = BSL_SAL_SysTimeGet(&sysTime);
    if (ret != BSL_SUCCESS) {
        AppPrintError("Failed to get system time, errCode: 0x%x.\n", ret);
        return HITLS_APP_SAL_FAIL;
    }

    int64_t utcTime = 0;
    ret = BSL_SAL_DateToUtcTimeConvert(&sysTime, &utcTime);
    if (ret != BSL_SUCCESS) {
        AppPrintError("Failed to convert system time to utc time, errCode: 0x%x.\n", ret);
        return HITLS_APP_SAL_FAIL;
    }
    *time = utcTime;
    return HITLS_APP_SUCCESS;
}