/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "auth.h"
#include <openssl/evp.h>
#include <openssl/objects.h>
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <openssl/sha.h>

using namespace Hdc;
#define BIGNUMTOBIT 32

namespace HdcAuth {
// ---------------------------------------Cheat compiler---------------------------------------------------------
#ifdef HDC_HOST

bool AuthVerify(uint8_t *token, uint8_t *sig, int siglen)
{
    return false;
};
bool PostUIConfirm(string publicKey)
{
    return false;
}

#else  // daemon

bool GenerateKey(const char *file)
{
    return false;
};
int AuthSign(void *rsa, const unsigned char *token, size_t tokenSize, void *sig)
{
    return 0;
};
int GetPublicKeyFileBuf(unsigned char *data, size_t len)
{
    return 0;
}

#endif
// ------------------------------------------------------------------------------------------------

const uint32_t RSANUMBYTES = 512;  // 4096 bit key length
const uint32_t RSANUMWORDS = (RSANUMBYTES / sizeof(uint32_t));
struct RSAPublicKey {
    int wordModulusSize;            // Length of n[] in number of uint32_t */
    uint32_t rsaN0inv;              // -1 / n[0] mod 2^32
    uint32_t modulus[RSANUMWORDS];  // modulus as little endian array
    uint32_t rr[RSANUMWORDS];       // R^2 as little endian array
    BN_ULONG exponent;                   // 3 or 65537
};

#ifdef HDC_HOST
// Convert OpenSSL RSA private key to pre-computed RSAPublicKey format
int RSA2RSAPublicKey(RSA *rsa, RSAPublicKey *publicKey)
{
    int result = 1;
    unsigned int i;
    BN_CTX *ctx = BN_CTX_new();
    BIGNUM *r32 = BN_new();
    BIGNUM *rsaRR = BN_new();
    BIGNUM *rsaR = BN_new();
    BIGNUM *rsaRem = BN_new();
    BIGNUM *rsaN0inv = BN_new();
#ifdef OPENSSL_IS_BORINGSSL
    // boringssl
    BIGNUM *n = BN_new();
    BN_copy(n, rsa->n);
    publicKey->exponent = BN_get_word(rsa->e);
#else
    // openssl
#if OPENSSL_VERSION_NUMBER >= 0x10100005L
    BIGNUM *n = (BIGNUM *)RSA_get0_n(rsa);
    publicKey->exponent = BN_get_word(RSA_get0_e(rsa));
#else
    BIGNUM *n = BN_new();
    BN_copy(n, rsa->n);
    publicKey->exponent = BN_get_word(rsa->e);
#endif

#endif  // OPENSSL_IS_BORINGSSL
    while (true) {
        if (RSA_size(rsa) != RSANUMBYTES) {
            result = 0;
            break;
        }

        BN_set_bit(r32, BIGNUMTOBIT);
        BN_set_bit(rsaR, RSANUMWORDS * BIGNUMTOBIT);
        BN_mod_sqr(rsaRR, rsaR, n, ctx);
        BN_div(nullptr, rsaRem, n, r32, ctx);
        BN_mod_inverse(rsaN0inv, rsaRem, r32, ctx);
        publicKey->wordModulusSize = RSANUMWORDS;
        publicKey->rsaN0inv = 0 - BN_get_word(rsaN0inv);
        for (i = 0; i < RSANUMWORDS; ++i) {
            BN_div(rsaRR, rsaRem, rsaRR, r32, ctx);
            publicKey->rr[i] = BN_get_word(rsaRem);
            BN_div(n, rsaRem, n, r32, ctx);
            publicKey->modulus[i] = BN_get_word(rsaRem);
        }
        break;
    }

    BN_free(rsaR);
    BN_free(rsaRR);
    BN_free(n);
    BN_free(r32);
    BN_free(rsaN0inv);
    BN_free(rsaRem);
    BN_CTX_free(ctx);
    return result;
}

int GetUserInfo(char *buf, size_t len)
{
    char hostname[BUF_SIZE_DEFAULT];
    char username[BUF_SIZE_DEFAULT];
    uv_passwd_t pwd;
    int ret = -1;
    size_t bufSize = sizeof(hostname);
    if (uv_os_gethostname(hostname, &bufSize) < 0 && EOK != strcpy_s(hostname, sizeof(hostname), "unknown")) {
        return ERR_API_FAIL;
    }
    if (!uv_os_get_passwd(&pwd) && !strcpy_s(username, sizeof(username), pwd.username)) {
        ret = 0;
    }
    uv_os_free_passwd(&pwd);
    if (ret < 0 && EOK != strcpy_s(username, sizeof(username), "unknown")) {
        return ERR_API_FAIL;
    }
    if (snprintf_s(buf, len, len - 1, " %s@%s", username, hostname) < 0) {
        return ERR_BUF_OVERFLOW;
    }
    return RET_SUCCESS;
}

int WritePublicKeyfile(RSA *private_key, const char *private_key_path)
{
    RSAPublicKey publicKey;
    char info[BUF_SIZE_DEFAULT];
    int ret = 0;
    string path = private_key_path + string(".pub");

    ret = RSA2RSAPublicKey(private_key, &publicKey);
    if (!ret) {
        WRITE_LOG(LOG_DEBUG, "Failed to convert to publickey\n");
        return 0;
    }
    vector<uint8_t> vec = Base::Base64Encode((const uint8_t *)&publicKey, sizeof(RSAPublicKey));
    if (!vec.size()) {
        return 0;
    }
    GetUserInfo(info, sizeof(info));
    vec.insert(vec.end(), (uint8_t *)info, (uint8_t *)info + strlen(info));
    ret = Base::WriteBinFile(path.c_str(), vec.data(), vec.size(), true);
    return ret >= 0 ? 1 : 0;
}

bool GenerateKey(const char *file)
{
    EVP_PKEY *publicKey = EVP_PKEY_new();
    BIGNUM *exponent = BN_new();
    RSA *rsa = RSA_new();
    int bits = 4096;
    mode_t old_mask;
    FILE *fKey = nullptr;
    bool ret = false;

    while (true) {
        WRITE_LOG(LOG_DEBUG, "generate_key '%s'\n", file);
        if (!publicKey || !exponent || !rsa) {
            WRITE_LOG(LOG_DEBUG, "Failed to allocate key");
            break;
        }

        BN_set_word(exponent, RSA_F4);
        RSA_generate_key_ex(rsa, bits, exponent, nullptr);
        EVP_PKEY_set1_RSA(publicKey, rsa);
        old_mask = umask(077);  // 077:permission

        fKey = fopen(file, "w");
        if (!fKey) {
            WRITE_LOG(LOG_DEBUG, "Failed to open '%s'\n", file);
            umask(old_mask);
            break;
        }
        umask(old_mask);
        if (!PEM_write_PrivateKey(fKey, publicKey, nullptr, nullptr, 0, nullptr, nullptr)) {
            WRITE_LOG(LOG_DEBUG, "Failed to write key");
            break;
        }
        if (!WritePublicKeyfile(rsa, file)) {
            WRITE_LOG(LOG_DEBUG, "Failed to write public key");
            break;
        }
        ret = true;
        break;
    }

    EVP_PKEY_free(publicKey);
    BN_free(exponent);
    if (fKey)
        fclose(fKey);
    return ret;
}

bool ReadKey(const char *file, list<void *> *listPrivateKey)
{
    FILE *f = nullptr;
    bool ret = false;

    if (file == nullptr || listPrivateKey == nullptr) {
        WRITE_LOG(LOG_FATAL, "file or listPrivateKey is null");
        return ret;
    }
    while (true) {
        if (!(f = fopen(file, "r"))) {
            break;
        }
        RSA *rsa = RSA_new();
        if (!PEM_read_RSAPrivateKey(f, &rsa, nullptr, nullptr)) {
            RSA_free(rsa);
            break;
        }
        listPrivateKey->push_back((void *)rsa);
        ret = true;
        break;
    }
    if (f) {
        fclose(f);
    }
    return ret;
}

int GetUserKeyPath(string &path)
{
    struct stat status;
    const char harmoneyPath[] = ".harmony";
    const char hdcKeyFile[] = "hdckey";
    char buf[BUF_SIZE_DEFAULT];
    size_t len = BUF_SIZE_DEFAULT;
    // $home
    if (uv_os_homedir(buf, &len) < 0)
        return false;
    string dir = string(buf) + Base::GetPathSep() + string(harmoneyPath) + Base::GetPathSep();
    path = Base::CanonicalizeSpecPath(dir);
    if (stat(path.c_str(), &status)) {
        uv_fs_t req;
        uv_fs_mkdir(nullptr, &req, path.c_str(), 0750, nullptr);  // 0750:permission
        uv_fs_req_cleanup(&req);
        if (req.result < 0) {
            WRITE_LOG(LOG_DEBUG, "Cannot mkdir '%s'", path.c_str());
            return false;
        }
    }
    path += hdcKeyFile;
    return true;
}

bool LoadHostUserKey(list<void *> *listPrivateKey)
{
    struct stat status;
    string path;
    if (!GetUserKeyPath(path)) {
        return false;
    }
    if (stat(path.c_str(), &status) == -1) {
        if (!GenerateKey(path.c_str())) {
            WRITE_LOG(LOG_DEBUG, "Failed to generate new key");
            return false;
        }
    }
    if (!ReadKey(path.c_str(), listPrivateKey)) {
        return false;
    }
    return true;
}

int AuthSign(void *rsa, const unsigned char *token, size_t tokenSize, void *sig)
{
    unsigned int len;
    if (!RSA_sign(NID_sha256, token, tokenSize, (unsigned char *)sig, &len, (RSA *)rsa)) {
        return 0;
    }
    return static_cast<int>(len);
}

int GetPublicKeyFileBuf(unsigned char *data, size_t len)
{
    string path;
    int ret;

    if (!GetUserKeyPath(path)) {
        return 0;
    }
    path += ".pub";
    ret = Base::ReadBinFile(path.c_str(), (void **)data, len);
    if (ret <= 0) {
        return 0;
    }
    data[ret] = '\0';
    return ret + 1;
}

#else  // daemon

bool RSAPublicKey2RSA(const uint8_t *keyBuf, RSA **key)
{
    const int pubKeyModulusSize = 256;
    const int pubKeyModulusSizeWords = pubKeyModulusSize / 4;

    const RSAPublicKey *keyStruct = reinterpret_cast<const RSAPublicKey *>(keyBuf);
    bool ret = false;
    uint8_t modulusBuffer[pubKeyModulusSize];
    RSA *newKey = RSA_new();
    if (!newKey) {
        goto cleanup;
    }
    if (keyStruct->wordModulusSize != pubKeyModulusSizeWords) {
        goto cleanup;
    }
    if (memcpy_s(modulusBuffer, sizeof(modulusBuffer), keyStruct->modulus, sizeof(modulusBuffer)) != EOK) {
        goto cleanup;
    }
    Base::ReverseBytes(modulusBuffer, sizeof(modulusBuffer));

#ifdef OPENSSL_IS_BORINGSSL
    // boringssl
    newKey->n = BN_bin2bn(modulusBuffer, sizeof(modulusBuffer), nullptr);
    newKey->e = BN_new();
    if (!newKey->e || !BN_set_word(newKey->e, keyStruct->exponent) || !newKey->n) {
        goto cleanup;
    }
#else
    // openssl
#if OPENSSL_VERSION_NUMBER >= 0x10100005L
    RSA_set0_key(newKey, BN_bin2bn(modulusBuffer, sizeof(modulusBuffer), nullptr), BN_new(), BN_new());
#else
    newKey->n = BN_bin2bn(modulusBuffer, sizeof(modulusBuffer), nullptr);
    newKey->e = BN_new();
    if (!newKey->e || !BN_set_word(newKey->e, keyStruct->exponent) || !newKey->n) {
        goto cleanup;
    }
#endif
#endif

    *key = newKey;
    ret = true;

cleanup:
    if (!ret && newKey) {
        RSA_free(newKey);
    }
    return ret;
}

void ReadDaemonKeys(const char *file, list<void *> *listPublicKey)
{
    char buf[BUF_SIZE_DEFAULT2] = { 0 };
    char *sep = nullptr;
    int ret;
    FILE *f = fopen(file, "re");
    if (!f) {
        WRITE_LOG(LOG_DEBUG, "Can't open '%s'", file);
        return;
    }
    while (fgets(buf, sizeof(buf), f)) {
        RSAPublicKey *key = new RSAPublicKey();
        if (!key) {
            break;
        }
        sep = strpbrk(buf, " \t");
        if (sep) {
            *sep = '\0';
        }
        ret = Base::Base64DecodeBuf(reinterpret_cast<uint8_t *>(buf), strlen(buf), reinterpret_cast<uint8_t *>(key));
        if (ret != sizeof(RSAPublicKey)) {
            WRITE_LOG(LOG_DEBUG, "%s: Invalid base64 data ret=%d", file, ret);
            delete key;
            continue;
        }

        if (key->wordModulusSize != RSANUMWORDS) {
            WRITE_LOG(LOG_DEBUG, "%s: Invalid key len %d\n", file, key->wordModulusSize);
            delete key;
            continue;
        }
        listPublicKey->push_back(key);
    }
    fclose(f);
}

bool AuthVerify(uint8_t *token, uint8_t *sig, int siglen)
{
    list<void *> listPublicKey;
    uint8_t authKeyIndex = 0;
    void *ptr = nullptr;
    int ret = 0;
    int childRet = 0;
    while (KeylistIncrement(&listPublicKey, authKeyIndex, &ptr)) {
        RSA *rsa = nullptr;
        if (!RSAPublicKey2RSA((const uint8_t *)ptr, &rsa)) {
            break;
        }
        childRet = RSA_verify(NID_sha256, reinterpret_cast<const unsigned char *>(token),
                              RSA_TOKEN_SIZE, reinterpret_cast<const unsigned char *>(sig),
                              siglen, rsa);
        RSA_free(rsa);
        if (childRet) {
            ret = 1;
            break;
        }
    }
    FreeKey(true, &listPublicKey);
    return ret;
}

void LoadDaemonKey(list<void *> *listPublicKey)
{
#ifdef HDC_PCDEBUG
    char keyPaths[][BUF_SIZE_SMALL] = { "/root/.harmony/hdckey.pub" };
#else
    char keyPaths[][BUF_SIZE_SMALL] = { "/hdc_keys", "/data/misc/hdc/hdc_keys" };
#endif
    int num = sizeof(keyPaths) / sizeof(keyPaths[0]);
    struct stat buf;

    for (int i = 0; i < num; ++i) {
        char *p = keyPaths[i];
        if (!stat(p, &buf)) {
            WRITE_LOG(LOG_DEBUG, "Loading keys from '%s'", p);
            ReadDaemonKeys(p, listPublicKey);
        }
    }
}

bool PostUIConfirm(string publicKey)
{
    // Because the Hi3516 development board has no UI support for the time being, all public keys are received and
    // By default, the system UI will record the public key /data/misc/hdc/hdckey/data/misc/hdc/hdckey
    return true;
}
#endif  // HDC_HOST

// --------------------------------------common code------------------------------------------
bool KeylistIncrement(list<void *> *listKey, uint8_t &authKeyIndex, void **out)
{
    if (!listKey->size()) {
#ifdef HDC_HOST
        LoadHostUserKey(listKey);
#else
        LoadDaemonKey(listKey);
#endif
    }
    if (authKeyIndex == listKey->size()) {
        // all finish
        return false;
    }
    auto listIndex = listKey->begin();
    std::advance(listIndex, ++authKeyIndex);
    *out = *listIndex;
    if (!*out) {
        return false;
    }
    return true;
}

void FreeKey(bool publicOrPrivate, list<void *> *listKey)
{
    for (auto &&v : *listKey) {
        if (publicOrPrivate) {
            delete (RSAPublicKey *)v;
            v = nullptr;
        } else {
            RSA_free((RSA *)v);
            v = nullptr;
        }
    }
    listKey->clear();
}

#ifdef HDC_HOST
EVP_PKEY *GenerateNewKey(void)
{
    bool success = false;
    int bits = RSA_KEY_BITS;
    RSA *rsa = RSA_new();
    BIGNUM *e = BN_new();
    EVP_PKEY *evp = EVP_PKEY_new();

    while (true) {
        if (!evp || !e || !rsa) {
            WRITE_LOG(LOG_FATAL, "allocate key failed");
            break;
        }

        BN_set_word(e, RSA_F4);
        if (!RSA_generate_key_ex(rsa, bits, e, nullptr)) {
            WRITE_LOG(LOG_FATAL, "generate rsa key failed");
            break;
        }
        if (!EVP_PKEY_set1_RSA(evp, rsa)) {
            WRITE_LOG(LOG_FATAL, "evp set rsa failed");
            break;
        }

        WRITE_LOG(LOG_INFO, "generate key pair success");
        success = true;
        break;
    }

    if (e)
        BN_free(e);
    if (success) {
        return evp;
    }

    // if fail, need free rsa and evp
    if (rsa)
        RSA_free(rsa);
    if (evp)
        EVP_PKEY_free(evp);

    return nullptr;
}
bool GenerateKeyPair(const string& prikey_filename, const string& pubkey_filename)
{
    bool ret = false;
    FILE *file_prikey = nullptr;
    FILE *file_pubkey = nullptr;
    EVP_PKEY *evp = nullptr;
    mode_t old_mask = umask(077);  // 077:permission

    while (true) {
        evp = GenerateNewKey();
        if (!evp) {
            WRITE_LOG(LOG_FATAL, "generate new key failed");
            break;
        }

        file_prikey = fopen(prikey_filename.c_str(), "w");
        if (!file_prikey) {
            WRITE_LOG(LOG_FATAL, "open %s failed", prikey_filename.c_str());
            break;
        }
        if (!PEM_write_PrivateKey(file_prikey, evp, nullptr, nullptr, 0, nullptr, nullptr)) {
            WRITE_LOG(LOG_FATAL, "write private key failed");
            break;
        }
        file_pubkey = fopen(pubkey_filename.c_str(), "w");
        if (!file_pubkey) {
            WRITE_LOG(LOG_FATAL, "open %s failed", pubkey_filename.c_str());
            break;
        }
        if (!PEM_write_PUBKEY(file_pubkey, evp)) {
            WRITE_LOG(LOG_FATAL, "write public key file failed");
            break;
        }
        WRITE_LOG(LOG_INFO, "generate key pair success");
        ret = true;
        break;
    }

    if (evp)
        EVP_PKEY_free(evp);
    if (file_prikey)
        fclose(file_prikey);
    if (file_pubkey)
        fclose(file_pubkey);
    umask(old_mask);

    return ret;
}

bool LoadPublicKey(const string& pubkey_filename, string &pubkey)
{
    bool ret = false;
    BIO *bio = nullptr;
    EVP_PKEY *evp = nullptr;
    FILE *file_pubkey = nullptr;

    do {
        file_pubkey = fopen(pubkey_filename.c_str(), "r");
        if (!file_pubkey) {
            WRITE_LOG(LOG_FATAL, "open file %s failed", pubkey_filename.c_str());
            break;
        }
        evp = PEM_read_PUBKEY(file_pubkey, NULL, NULL, NULL);
        if (!evp) {
            WRITE_LOG(LOG_FATAL, "read pubkey from %s failed", pubkey_filename.c_str());
            break;
        }
        bio = BIO_new(BIO_s_mem());
        if (!bio) {
            WRITE_LOG(LOG_FATAL, "alloc bio mem failed");
            break;
        }
        if (!PEM_write_bio_PUBKEY(bio, evp)) {
            WRITE_LOG(LOG_FATAL, "write bio failed");
            break;
        }
        size_t len = 0;
        char buf[RSA_KEY_BITS] = {0};
        if (BIO_read_ex(bio, buf, sizeof(buf), &len) <= 0) {
            WRITE_LOG(LOG_FATAL, "read bio failed");
            break;
        }
        pubkey = string(buf, len);
        ret = true;
        WRITE_LOG(LOG_INFO, "load pubkey from file(%s) success", pubkey_filename.c_str());
    } while (0);

    if (evp) {
        EVP_PKEY_free(evp);
        evp = nullptr;
    }
    if (bio) {
        BIO_free(bio);
        bio = nullptr;
    }
    if (file_pubkey) {
        fclose(file_pubkey);
        file_pubkey = nullptr;
    }
    return ret;
}

bool TryLoadPublicKey(string &pubkey)
{
    string prikey_filename;
    struct stat status;
    if (!GetUserKeyPath(prikey_filename)) {
        WRITE_LOG(LOG_FATAL, "get key path failed");
        return false;
    }
    string pubkey_filename = prikey_filename + ".pub";
    if (stat(prikey_filename.c_str(), &status) == -1) {
        if (!GenerateKeyPair(prikey_filename, pubkey_filename)) {
            WRITE_LOG(LOG_FATAL, "generate new key failed");
            return false;
        }
    }
    if (!LoadPublicKey(pubkey_filename, pubkey)) {
        WRITE_LOG(LOG_FATAL, "load key failed");
        return false;
    }

    WRITE_LOG(LOG_INFO, "load pubkey success");

    return true;
}

bool GetHostName(string &hostname)
{
    int ret;
    char buf[BUF_SIZE_DEFAULT] = {0};
    size_t bufsize = sizeof(buf);

    ret = uv_os_gethostname(buf, &bufsize);
    if (ret != 0) {
        WRITE_LOG(LOG_FATAL, "get hostname failed: %d", ret);
        return false;
    }

    hostname = string(buf, bufsize);

    WRITE_LOG(LOG_INFO, "hostname: %s", hostname.c_str());

    return true;
}

bool GetPublicKeyinfo(string &pubkey_info)
{
    string hostname;
    if (!GetHostName(hostname)) {
        WRITE_LOG(LOG_FATAL, "gethostname failed");
        return false;
    }
    string pubkey;
    if (!HdcAuth::TryLoadPublicKey(pubkey)) {
        WRITE_LOG(LOG_FATAL, "load public key failed");
        return false;
    }
    pubkey_info = hostname;
    pubkey_info.append(HDC_HOST_DAEMON_BUF_SEPARATOR);
    pubkey_info.append(pubkey);

    WRITE_LOG(LOG_INFO, "Get pubkey info success");

    return true;
}

RSA *LoadPrivateKey(const string& prikey_filename)
{
    RSA *rsa = nullptr;
    EVP_PKEY *evp = nullptr;
    FILE *file_prikey = nullptr;

    do {
        file_prikey = fopen(prikey_filename.c_str(), "r");
        if (!file_prikey) {
            WRITE_LOG(LOG_FATAL, "open file %s failed", prikey_filename.c_str());
            break;
        }
        evp = PEM_read_PrivateKey(file_prikey, NULL, NULL, NULL);
        if (!evp) {
            WRITE_LOG(LOG_FATAL, "read prikey from %s failed", prikey_filename.c_str());
            break;
        }
        rsa = EVP_PKEY_get1_RSA(evp);
        WRITE_LOG(LOG_FATAL, "load prikey success");
    } while (0);

    if (evp) {
        EVP_PKEY_free(evp);
        evp = nullptr;
    }
    if (file_prikey) {
        fclose(file_prikey);
        file_prikey = nullptr;
    }

    return rsa;
}

bool RsaSignAndBase64(string &buf)
{
    RSA *rsa = nullptr;
    string prikey_filename;
    int sign_ori_size;
    int sign_out_size;
    unsigned char sign_ori[BUF_SIZE_DEFAULT2] = { 0 };
    unsigned char *sign_out = nullptr;
    int in_size = buf.size();
    const unsigned char *in = reinterpret_cast<const unsigned char *>(buf.c_str());

    WRITE_LOG(LOG_INFO, "plain(%s)", buf.c_str());
    if (!GetUserKeyPath(prikey_filename)) {
        WRITE_LOG(LOG_FATAL, "get key path failed");
        return false;
    }

    rsa = LoadPrivateKey(prikey_filename);
    if (!rsa) {
        WRITE_LOG(LOG_FATAL, "load prikey from file(%s) failed", prikey_filename.c_str());
        return false;
    }
    sign_ori_size = RSA_private_encrypt(in_size, in, sign_ori, rsa, RSA_PKCS1_PADDING);
    if (sign_ori_size <= 0) {
        WRITE_LOG(LOG_FATAL, "encrypt failed");
        return false;
    }
    sign_out = new unsigned char[sign_ori_size * 2];
    if (!sign_out) {
        WRITE_LOG(LOG_FATAL, "alloc mem failed");
        return false;
    }
    sign_out_size = EVP_EncodeBlock(sign_out, sign_ori, sign_ori_size);
    if (sign_out_size <= 0) {
        WRITE_LOG(LOG_FATAL, "encode buf failed");
        delete[] sign_out;
        sign_out = nullptr;
        return false;
    }

    buf = string(reinterpret_cast<char *>(sign_out), sign_out_size);

    WRITE_LOG(LOG_INFO, "sign success");

    delete[] sign_out;
    sign_out = nullptr;

    return true;
}
#endif
}