/*
 * Copyright (c) 2025 Huawei Technologies Co., Ltd.
 * openUBMC is licensed under 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 <openssl/pem.h>
#include <openssl/cms.h>
#include <openssl/err.h>
#include <openssl/crypto.h>
#include <getopt.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <curl/curl.h>
#include "public.h"

#define TS_FILE ".temp.cms.timestamp.der"
#define TAS_FILE ".temp.cms.hash.tsa"
#define CMS_HASH_FILE ".temp.cms.hash.der"

const char     *signer_file = "signer.pem";
const char     *in_file     = "sign.txt";
const char     *out_file    = "signed.cms";
const char     *ts_signer   = "ts_signer.pem";
const char     *tsa_cnf     = "tsa.cnf";
const char     *password    = NULL;
static int      verbose     = 0;
static BUF_MEM *cms_tmp_buf = NULL;
// 锚点：SignerInfo.signature 对应的 OCTET STRING 节点
static struct asn_data *g_sig_octet_node = NULL;

// —— 厂商开关（默认关闭）：开启后才允许使用云签名/云时间戳
static int keyfactory_enabled = 0;

static const char *cms_url             = NULL; // SignServer CMS endpoint (multipart/form-data)
static const char *cms_worker_id       = NULL; // optional: form workerId
static const char *cms_worker_name     = NULL; // optional: form workerName
static const char *cms_client_hash_alg = NULL; // e.g. "SHA-256", "SHA-384", "SHA-512"

// 全局参数（给 SignServer 用）
static const char *tsa_url     = NULL; // 有值则走 SignServer
static int tsa_insecure        = 0;    // 1=跳过 TLS 校验（仅测试用）
static const char *curl_cafile = NULL; // 指定 CA 根证书（生产建议用）

#define SALT_FLAG "Salted__"

static void unlink_file(const char *name)
{
    if (verbose == 0) {
        unlink(name);
    }
}

static void print_help(const char *exec_name)
{
    log_debug("Usage: %s [options]", exec_name);
    log_debug("Valid options are:");
    log_debug(" %-32s Signer certificate file\n%*c  > default: signer.pem", "-s, --signer=FILE", 32, ' ');
    log_debug(" %-32s Input file\n%*c  > default: sign.txt", "-i, --input=FILE", 32, ' ');
    log_debug(" %-32s Output file\n%*c  > default: signed.cms", "-o, --output=FILE", 32, ' ');
    log_debug(" %-32s Timestamp signer certificate file\n%*c  > default: ts_signer.pem", "-t, --ts_signer", 32, ' ');
    log_debug(" %-32s Timestamp config file\n%*c  > default: tsa.cnf", "-T, --tsa_cnf", 32, ' ');
    log_debug(" %-32s aes-256 encrypted password, also support environment variable $SIGN_PASSWORD\n%*c  > default:",
              "-p, --password", 32, ' ');
    log_debug(" %-32s Print debug messages", "-v, --verbose");
    log_debug(" %-32s Display this summary", "-h, --help");
    log_debug(" %-32s Use SignServer TSA endpoint (if set, -t/-T ignored)\n%*c  e.g. "
              "https://host/signserver/process?workerId=3",
              "-U, --tsa-url=URL", 32, ' ');
    log_debug(" %-32s Insecure TLS (like curl -k), for testing only", "-k, --insecure");
    log_debug(" %-32s Use custom CA file to verify server TLS", "--curl-cafile=FILE");
    log_debug(" %-32s Use SignServer CMS endpoint (multipart/form-data)", "-S, --cms-url=URL");
    log_debug(" %-32s Optional workerId (sent as form field)", "--cms-worker-id=ID");
    log_debug(" %-32s Optional workerName (sent as form field)", "--cms-worker-name=NAME");
    log_debug(" %-32s Enable vendor (Keyfactor) cloud signing. If not set, --cms-url/--tsa-url are ignored for safety.", "--keyfactory");
}

static const char         *opt_string = "s:i:o:t:T:p:vhkU:S:";
static const struct option opts[]     = {
    {"signer", required_argument, NULL, 's'},  {"in", required_argument, NULL, 'i'},
    {"out", required_argument, NULL, 'o'},     {"ts_signer", required_argument, NULL, 't'},
    {"tsa_cnf", required_argument, NULL, 'T'}, {"verbose", no_argument, NULL, 'v'},
    {"help", no_argument, NULL, 'h'},
    {"insecure", no_argument, NULL, 'k'},
    {"tsa-url", required_argument, NULL, 'U'},
    {"cms-url", required_argument, NULL, 'S'},
    {"cms-worker-id", required_argument, NULL, 1004},
    {"cms-worker-name", required_argument, NULL, 1005},
    {"curl-cafile", required_argument, NULL, 1003},
    {"cms-client-hash", required_argument, NULL, 1006},
    {"keyfactory", no_argument, NULL, 1007},        // 厂商开关：开启云签
    {NULL},

};

static unsigned char *read_file(const char *filename, int *read_len, unsigned char *salt, size_t salt_len)
{
    unsigned char *buffer = NULL;
    unsigned char *output = NULL;
    *read_len             = 0;

    FILE *fp = fopen(filename, "r");
    if (!fp) {
        log_error("Open file %s failed, error: %s", filename, strerror(errno));
        return NULL;
    }
    do {
        int ret = fseek(fp, 0, SEEK_END);
        if (ret != 0) {
            log_error("Seek file %s failed, error: %s", filename, strerror(errno));
            break;
        }
        size_t pos = (size_t)ftell(fp);
        (void)fseek(fp, 0, SEEK_SET);
        if (ret != 0) {
            log_error("Seek file %s failed, error: %s", filename, strerror(errno));
            break;
        }
        buffer      = malloc(pos + 1);
        if (buffer == NULL) {
            log_error("malloc memory failed");
            break;
        }
        buffer[pos] = '\0';
        ret         = (int)fread(buffer, 1, pos, fp);
        if (ret != (int)pos) {
            log_error("Read file %s failed, error: %s", signer_file, strerror(errno));
            break;
        }
        if (pos >= (strlen(SALT_FLAG) + salt_len) && memcmp(buffer, SALT_FLAG, strlen(SALT_FLAG)) == 0) {
            (void)memcpy(salt, buffer + strlen(SALT_FLAG), salt_len);
            *read_len = (int)(pos - strlen(SALT_FLAG) - salt_len);
            memmove(buffer, buffer + strlen(SALT_FLAG) + salt_len, (size_t)(*read_len));
        } else {
            *read_len = (int)pos;
        }
        output = buffer;
        buffer = NULL;
        log_debug("Read data len: %d", *read_len);
    } while (0);
    (void)fclose(fp);
    if (buffer) {
        free(buffer);
    }
    return output;
}

static BIO *read_signer_file(const char *filename)
{
    EVP_CIPHER_CTX *ctx;
    unsigned char   salt[8]                                    = {0};
    unsigned char   kv[EVP_MAX_KEY_LENGTH + EVP_MAX_IV_LENGTH] = {0};
    int             plain_len                                  = 0;
    int             len                                        = 0;
    BIO            *bio                                        = NULL;
    unsigned char  *plain_buffer                               = NULL;

    int            read_len    = 0;
    unsigned char *read_buffer = read_file(filename, &read_len, salt, sizeof(salt));
    if (!read_buffer) {
        return NULL;
    }
    log_debug("read file %s, return len: %d", filename, read_len);
    do {
        ctx = EVP_CIPHER_CTX_new();
        if (!password) {
            bio = BIO_new(BIO_s_mem());
            (void)BIO_write(bio, read_buffer, read_len);
            break;
        }
        const EVP_CIPHER *cipher = EVP_aes_256_cbc();
        int               klen   = EVP_CIPHER_key_length(cipher);
        int               vlen   = EVP_CIPHER_iv_length(cipher);
        PKCS5_PBKDF2_HMAC(password, -1, salt, sizeof(salt), 10000, EVP_sha256(), klen + vlen, kv);
        int ret = EVP_DecryptInit_ex(ctx, cipher, NULL, kv, kv + klen);
        if (ret <= 0) {
            log_error("Call EVP_DecryptInit_ex with error");
            break;
        }
        plain_buffer = malloc((size_t)read_len + 1);
        memset(plain_buffer, 0, (size_t)read_len + 1);
        ret = EVP_DecryptUpdate(ctx, plain_buffer, &plain_len, read_buffer, read_len);
        if (ret <= 0) {
            log_error("Call EVP_DecryptUpdate with error");
            break;
        }
        ret = EVP_DecryptFinal_ex(ctx, plain_buffer + plain_len, &len);
        if (ret <= 0) {
            log_error("Call EVP_DecryptFinal_ex with error");
            break;
        }
        plain_len += len;
        bio = BIO_new(BIO_s_mem());
        (void)BIO_write(bio, plain_buffer, plain_len);
        log_debug("Decrypt data len: %d", plain_len);
    } while (0);
    free(read_buffer);
    EVP_CIPHER_CTX_reset(ctx);
    EVP_CIPHER_CTX_free(ctx);
    if (plain_buffer) {
        free(plain_buffer);
    }
    return bio;
}

static int cms_sign(void)
{
    BIO             *in = NULL, *out = NULL, *tbio = NULL;
    X509            *scert = NULL;
    EVP_PKEY        *skey  = NULL;
    CMS_ContentInfo *cms   = NULL;
    int              ret   = -1;

    log_info("Sign %s start", in_file);
    int flags = CMS_DETACHED | CMS_NOSMIMECAP | CMS_BINARY;

    OpenSSL_add_all_algorithms();
    ERR_load_crypto_strings();

    do {
        log_debug("Open signer certificate: %s", signer_file);
        tbio = read_signer_file(signer_file);
        if (!tbio) {
            break;
        }

        log_debug("Read signer certificate: %s", signer_file);
        scert = PEM_read_bio_X509(tbio, NULL, 0, NULL);
        log_debug("Read private key: %s", signer_file);
        skey = PEM_read_bio_PrivateKey(tbio, NULL, 0, NULL);
        BIO_reset(tbio);
        if (!scert || !skey) {
            break;
        }

        log_debug("Open content being signed: %s", in_file);
        in = BIO_new_file(in_file, "r");
        if (!in) {
            log_error("Open content file %s failed", in_file);
            break;
        }

        out = BIO_new(BIO_s_mem());
        if (!out) {
            log_error("Create cms output buffer failed");
            break;
        }

        log_debug("Sign content");
        cms = CMS_sign(scert, skey, NULL, in, (unsigned int)flags);
        if (!cms) {
            log_error("Sign content failed");
            break;
        }

        log_debug("Write signed content");
        if (i2d_CMS_bio_stream(out, cms, in, flags) == 0) {
            log_error("Write signed content failed");
            break;
        }
        BIO_get_mem_ptr(out, &cms_tmp_buf);
        BIO_set_close(out, BIO_NOCLOSE); /* So BIO_free() leaves BUF_MEM alone */
        log_info("Sign %s successfully(need timestamp sign more)", in_file);
        ret = 0;
    } while (0);

    if (ret != 0) {
        log_error("Error Signing Data:");
        print_ssl_error();
    }

    CMS_ContentInfo_free(cms);
    X509_free(scert);
    EVP_PKEY_free(skey);
    BIO_free(in);
    BIO_free(out);
    BIO_free(tbio);
    return ret;
}

struct asn_data {
    struct asn1_string_st asn;
    int                   real_length;  // asn剔除了子节点的数据后的长度
    int                   cap;
    int                   n;
    struct asn_data     **next;
    struct asn_data      *prev;
};

static int calc_asn_head_size(int in_length)
{
    int length = in_length;
    int size = 2;  // type+length至少2个字节
    // 不足1字节的使用短表示
    if (length <= 0x7f) {
        return size;
    }
    // 多于1字节的使用长表示
    while (length > 0) {
        size += 1;
        length = length / 256;  // 每字节表示256个数
    }
    return size;
}

static struct asn_data cms_ad;

/**
 * 新增下层节点时递归修改父节点的长度
 */
static void add_data_length_recurse(struct asn_data *tmp_ad, int in_length)
{
    int length = in_length;
    if (tmp_ad == (&cms_ad)) {
        return;
    }
    do {
        int old_length    = tmp_ad->asn.length;
        int old_head_size = calc_asn_head_size(tmp_ad->asn.length);
        tmp_ad->asn.length += length;
        int new_head_size = calc_asn_head_size(tmp_ad->asn.length);
        length += (new_head_size - old_head_size);
        tmp_ad = tmp_ad->prev;
    } while (tmp_ad && tmp_ad != (&cms_ad));
}

/**
 * 新增下层节点
 */
static struct asn_data *append_asn_data(struct asn_data *ad, const struct asn1_string_st *asn, int add_parent_length)
{
    if (ad->n >= ad->cap) {
        ad->cap += 1;
        if (ad->next != NULL) {
            size_t len = sizeof(struct asn_data *) * ad->cap;
            struct asn_data **newNext = (struct asn_data **)malloc(len);
            memset(newNext, 0, len);
            memcpy(newNext, ad->next, sizeof(struct asn_data *) * (ad->cap - 1));
            free(ad->next);
            ad->next = newNext;
        } else {
            ad->next = (struct asn_data **)malloc((size_t)ad->cap);
        }
        if (ad->next == NULL) {
            log_error("malloc memory failed");
            return NULL;
        }
    }
    struct asn_data *next = (struct asn_data *)malloc(sizeof(struct asn_data));
    if (next == NULL) {
        log_error("malloc memory failed");
        return NULL;
    }
    ad->next[ad->n]       = next;
    memset(next, 0, sizeof(*next));
    memcpy(&next->asn, asn, sizeof(*asn));
    next->prev = ad;
    // 节点的真实大小默认先设置为内容大小，加载完成后需要使用fixed_asn_data_length调整
    next->real_length = asn->length;
    ad->n++;
    if (add_parent_length != 0) {
        add_data_length_recurse(ad, (asn->length + calc_asn_head_size(asn->length)));
    }
    return next;
}

/**
 * 调整节点自身数据真实大小
 */
static struct asn_data *fixed_asn_real_length(struct asn_data *ad)
{
    for (int i = 0; i < ad->n; i++) {
        struct asn_data *next = ad->next[i];
        fixed_asn_real_length(next);
        ad->real_length -= (next->asn.length + calc_asn_head_size(next->asn.length));
    }
}

/**
 * 打印ASA数据
 */
static struct asn_data *show_asn_info(int in_ident, struct asn_data *ad)
{
    int ident = in_ident;
    ident += 4;  // 显示缩进设置为4
    log_debug_ident(ident, "ASN INFO, type: 0x%02x, length: 0x%04x, self data lenght: 0x%04x",
                    (unsigned int)ad->asn.type, (unsigned int)ad->asn.length, ad->real_length);
    for (int i = 0; i < ad->n; i++) {
        struct asn_data *next = ad->next[i];
        // 已经以是最后一个节点
        show_asn_info(ident, next);
    }
}

/**
 * 从内存中加载asn数据结构
 */
static int asn_parse(int ident, unsigned char *buf, size_t read_len, struct asn_data *ad)
{
    int                   ret = 0;
    struct asn1_string_st asn;
    for (int i = 0; i < (int)read_len;) {
        memset(&asn, 0, sizeof(asn));
        asn.type = (int)buf[i];
        int j    = 0;
        if ((buf[i + 1] & 0x80) != 0) {
            for (j = 0; j < (buf[i + 1] & 0x7f); j++) {
                // 右移8位
                asn.length = (int)(((unsigned int)asn.length << 8) | buf[i + 2 + j]);
            }
        } else {
            asn.length = (int)buf[i + 1];
        }
        if (asn.length + 2 + j > (int)read_len) {
            log_debug_ident(ident, "content length overlay, need: %d, length: %lu", asn.length + 2 + j, read_len);
            ret = -1;
            break;
        }
        asn.data = buf + i + 2 + j;
        (void)append_asn_data(ad, &asn, 0);
        if (asn.type == 0x30 || asn.type == 0xa0 || asn.type == 0x31) {
            ret = asn_parse(ident + 2, buf + i + 2 + j, (size_t)asn.length, ad->next[ad->n - 1]);
            if (ret != 0) {
                break;
            }
        }
        i += asn.length + 2 + j;
    }

    if (ident == 0) {
        fixed_asn_real_length(ad);
        show_asn_info(0, ad->next[0]);
    }
    return ret;
}

/**
 * 获取签名hash
 */
static unsigned char *get_rsa_encryption(struct asn_data *ad, int *len)
{
    unsigned char *buf = NULL;
    if (ad->asn.type == 0x04 && ad->asn.length == 0x200) {
        buf = (unsigned char *)malloc(0x200);
        if (buf == NULL) {
            log_error("malloc memory failed");
            return NULL;
        }

        memcpy(buf, ad->asn.data, 0x200);
        *len = 0x200;
        return buf;
    }
    for (int i = 0; i < ad->n; i++) {
        struct asn_data *next = ad->next[i];
        buf                   = get_rsa_encryption(next, len);
        if (buf != NULL) {
            return buf;
        }
    }
    return NULL;
}

static unsigned char *get_rsa_encryption_remote(struct asn_data *ad, int *len)
{
    unsigned char *buf = NULL;
    /* 签名值：SignerInfo.signature 是 OCTET STRING，长度随算法/钥长而变：
     * - SM2/ECDSA(P-256) ~70B；RSA-2048=256B(0x100)、RSA-3072=384B(0x180)、RSA-4096=512B(0x200)
     * 用区间判断替代固定 0x200，并使用“实际长度 L”。 */
    if (ad->asn.type == 0x04 && ad->asn.length >= 0x40 && ad->asn.length <= 0x400) {
        size_t L = ad->asn.length;
        if (!g_sig_octet_node)     /* 单 signer：只记录第一次命中的那个作为锚 */
            g_sig_octet_node = ad; /* 作为后续 unsignedAttrs 追加的定位点（SignerInfo） */
        buf = (unsigned char *)malloc(L);
        if (buf == NULL) {
            log_error("malloc memory failed");
            return NULL;
        }
        memcpy(buf, ad->asn.data, L);
        *len = (int)L;
        return buf;
    }
    for (int i = 0; i < ad->n; i++) {
        struct asn_data *next = ad->next[i];
        buf                   = get_rsa_encryption_remote(next, len);
        if (buf != NULL) {
            return buf;
        }
    }
    return NULL;
}

/**
 * 生成时间戳签名请求
 */
static int tsa_request(const char *cms_hash_file, const char *tsa_file)
{
    char cmd[4096] = {0};
    log_info("Generate tsa request file");
    int ret = snprintf(cmd, sizeof(cmd) - 1,
        "openssl ts -query -data %s -cert -sha256 -no_nonce -out %s", cms_hash_file, tsa_file);
    if (ret <= 0) {
        log_error("Call snprintf failed");
        return -1;
    }
    ret   = system(cmd);
    if (ret != 0) {
        log_error("Call command %s failed", cmd);
        return -1;
    }
    return 0;
}

/**
 * 执行时间戳签名
 */
static int tsa_signed(const char *tsa_file, const char *signed_file)
{
    char cmd[4096]  = {0};
    char template[] = "tmp.29dd2162b9f8-XXXXXX";
    int fd          = mkstemp(template);
    if (fd <= 0) {
        log_error("Create temp file failed, error: %s", strerror(errno));
        return -1;
    }
    BIO *tbio = read_signer_file(ts_signer);
    if (!tbio) {
        close(fd);
        return -1;
    }
    char *mem_ptr = NULL;
    long  size    = BIO_get_mem_data(tbio, &mem_ptr);
    log_debug("Decrypt data : %s", mem_ptr);
    write(fd, mem_ptr, (size_t)size);
    BIO_reset(tbio);
    BIO_free(tbio);
    log_info("Signed tsa request file");
    int ret = snprintf(cmd,  sizeof(cmd) - 1,
                       "openssl ts -config %s -reply -config %s -queryfile %s -signer %s -inkey %s -out %s", tsa_cnf,
                       tsa_cnf, tsa_file, template, template, signed_file);
    close(fd);
    if (ret <= 0) {
        unlink(template);
        log_error("Call snprintf failed");
        return -1;
    }
    ret = system(cmd);
    unlink(template);
    if (ret != 0) {
        log_error("Call command %s failed", cmd);
        return -1;
    }
    return 0;
}

// —— libcurl 一次性初始化
static void curl_global_init_once(void)
{
    static int inited = 0;
    if (!inited) {
        curl_global_init(CURL_GLOBAL_DEFAULT);
        atexit(curl_global_cleanup);
        inited = 1;
    }
}

// —— 写回调：把响应写入文件
static size_t write_to_file_cb(void *ptr, size_t size, size_t nmemb, void *userdata)
{
    FILE *fp = (FILE *)userdata;
    return fwrite(ptr, size, nmemb, fp);
}

// —— 写回调：把响应写入内存（给 CMS 远程签名使用） —— //
struct mem_buf {
    unsigned char *data;
    size_t size;
};
static size_t write_to_mem_cb(void *ptr, size_t size, size_t nmemb, void *userdata)
{
    size_t n           = size * nmemb;
    struct mem_buf *mb = (struct mem_buf *)userdata;
    unsigned char *p   = (unsigned char *)realloc(mb->data, mb->size + n);
    if (!p)
        return 0;
    mb->data = p;
    memcpy(mb->data + mb->size, ptr, n);
    mb->size += n;
    return n;
}

static unsigned char *read_content(const char *cms_file, size_t *read_len);
// —— 通过 SignServer 用 TSQ 换 TSR：tsq_path -> tsr_out_path
static int tsa_signed_signserver_curl(const char *tsq_path, const char *tsr_out_path)
{
    if (!tsa_url) {
        log_error("tsa_url not set");
        return -1;
    }

    curl_global_init_once();

    int ret                    = -1;
    CURL *curl                 = NULL;
    FILE *fout                 = NULL;
    struct curl_slist *headers = NULL;

    // 读入 TSQ（二进制）—— 复用现有的 read_content
    size_t tsq_len         = 0;
    unsigned char *tsq_buf = read_content(tsq_path, &tsq_len);
    if (!tsq_buf) {
        log_error("Failed to read TSQ: %s", tsq_path);
        return -1;
    }

    fout = fopen(tsr_out_path, "wb");
    if (!fout) {
        log_error("Failed to open TSR output: %s", tsr_out_path);
        free(tsq_buf);
        return -1;
    }

    curl = curl_easy_init();
    if (!curl) {
        log_error("curl_easy_init failed");
        fclose(fout);
        free(tsq_buf);
        return -1;
    }

    headers = curl_slist_append(headers, "Content-Type: application/timestamp-query");
    curl_easy_setopt(curl, CURLOPT_URL, tsa_url);
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
    curl_easy_setopt(curl, CURLOPT_POST, 1L);
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, (const char *)tsq_buf);
    curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)tsq_len);

    // TLS 校验策略
    if (tsa_insecure) {
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
    } else {
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L);
        if (curl_cafile) {
            curl_easy_setopt(curl, CURLOPT_CAINFO, curl_cafile);
        }
    }

    // 响应直接写入文件
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_to_file_cb);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, fout);

    // 执行
    {
        CURLcode cc = curl_easy_perform(curl);
        if (cc != CURLE_OK) {
            log_error("curl_easy_perform failed: %s", curl_easy_strerror(cc));
            goto cleanup;
        }
        long http_code = 0;
        curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
        if (http_code != 200) {
            log_error("SignServer HTTP status: %ld", http_code);
            goto cleanup;
        }
    }

    ret = 0; // success

cleanup:
    if (headers)
        curl_slist_free_all(headers);
    if (curl)
        curl_easy_cleanup(curl);
    if (fout)
        fclose(fout);
    if (tsq_buf)
        free(tsq_buf);
    return ret;
}

// 计算文件哈希：支持 SHA-256 / SHA-384 / SHA-512
static int compute_file_digest(const char *path, const char *alg, unsigned char **out, size_t *outlen)
{
    const EVP_MD *md = NULL;
    if (!alg || strcasecmp(alg, "SHA-256") == 0)
        md = EVP_sha256();
    else if (strcasecmp(alg, "SHA-384") == 0)
        md = EVP_sha384();
    else if (strcasecmp(alg, "SHA-512") == 0)
        md = EVP_sha512();
    else {
        log_error("Unsupported hash algorithm: %s", alg);
        return -1;
    }

    FILE *fp = fopen(path, "rb");
    if (!fp) {
        log_error("Open file %s failed: %s", path, strerror(errno));
        return -1;
    }

    EVP_MD_CTX *ctx = EVP_MD_CTX_new();
    if (!ctx) {
        fclose(fp);
        return -1;
    }

    unsigned char buf[65536];
    int ok = (EVP_DigestInit_ex(ctx, md, NULL) == 1);
    while (ok) {
        size_t n = fread(buf, 1, sizeof(buf), fp);
        if (n == 0)
            break;
        ok = (EVP_DigestUpdate(ctx, buf, n) == 1);
    }

    unsigned int dlen     = EVP_MD_size(md);
    unsigned char *digest = (unsigned char *)OPENSSL_malloc(dlen);
    if (!digest)
        ok = 0;

    if (ok)
        ok = (EVP_DigestFinal_ex(ctx, digest, &dlen) == 1);

    EVP_MD_CTX_free(ctx);
    fclose(fp);

    if (!ok) {
        if (digest)
            OPENSSL_free(digest);
        log_error("Digest compute failed for %s", path);
        return -1;
    }
    *out    = digest;
    *outlen = dlen; // 32 / 48 / 64
    return 0;
}

// —— CMS 远程签名（SignServer 表单）：将 in_file 作为 file 字段上传，得到 DER CMS 到 cms_tmp_buf ——
// //
static int cms_signed_signserver_curl(void)
{
    if (!cms_url) {
        log_error("cms_url not set");
        return -1;
    }

    curl_global_init_once();

    int ret                    = -1;
    CURL *curl                 = NULL;
    struct curl_slist *headers = NULL;
    curl_mime *mime            = NULL;
    curl_mimepart *part        = NULL;

    // 用于把响应读入内存，再塞进 cms_tmp_buf
    struct mem_buf {
        unsigned char *data;
        size_t size;
    } mb = {0};

    curl = curl_easy_init();
    if (!curl) {
        log_error("curl_easy_init failed");
        goto cleanup;
    }

    // 关闭 100-continue，兼容部分网关
    headers = curl_slist_append(headers, "Expect:");
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
    curl_easy_setopt(curl, CURLOPT_URL, cms_url);

    // TLS 验证策略沿用 TSA 的开关
    if (tsa_insecure) {
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
    } else {
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L);
        if (curl_cafile) {
            curl_easy_setopt(curl, CURLOPT_CAINFO, curl_cafile);
        }
    }

    // multipart/form-data 表单
    mime = curl_mime_init(curl);

    if (cms_worker_id) {
        part = curl_mime_addpart(mime);
        curl_mime_name(part, "workerId");
        curl_mime_data(part, cms_worker_id, CURL_ZERO_TERMINATED);
    }
    if (cms_worker_name) {
        part = curl_mime_addpart(mime);
        curl_mime_name(part, "workerName");
        curl_mime_data(part, cms_worker_name, CURL_ZERO_TERMINATED);
    }

    // 必填：file 字段 —— 如果启用了 client-side hash，则只上传“哈希值（binary）”
    part = curl_mime_addpart(mime);
    curl_mime_name(part, "file");

    unsigned char *digest = NULL;
    size_t digest_len     = 0;

    if (cms_client_hash_alg) {
        if (compute_file_digest(in_file, cms_client_hash_alg, &digest, &digest_len) != 0) {
            log_error("compute_file_digest failed");
            goto cleanup;
        }
        // 直接上传哈希的二进制
        curl_mime_data(part, (const char *)digest, digest_len);
        curl_mime_type(part, "application/octet-stream");
        curl_mime_filename(part, "digest.bin"); // <-- 新增：补个文件名

        // 告诉 SignServer：客户端预哈希 + 哈希算法（官方文档示例）
        part = curl_mime_addpart(mime);
        curl_mime_name(part, "REQUEST_METADATA.USING_CLIENTSUPPLIED_HASH");
        curl_mime_data(part, "true", CURL_ZERO_TERMINATED);

        part = curl_mime_addpart(mime);
        curl_mime_name(part, "REQUEST_METADATA.CLIENTSIDE_HASHDIGESTALGORITHM");
        curl_mime_data(part, cms_client_hash_alg, CURL_ZERO_TERMINATED);

        part = curl_mime_addpart(mime);
        curl_mime_name(part, "REQUEST_METADATA.CLIENTSIDEHASHING");
        curl_mime_data(part, "true", CURL_ZERO_TERMINATED);

        // （可选兜底）确保 Detached
        part = curl_mime_addpart(mime);
        curl_mime_name(part, "REQUEST_METADATA.DETACHEDSIGNATURE");
        curl_mime_data(part, "true", CURL_ZERO_TERMINATED);
    } else {
        // 经典路径：上传原文件
        if (curl_mime_filedata(part, in_file) != CURLE_OK) {
            log_error("Failed to attach file: %s", in_file);
            goto cleanup;
        }
    }

    // 显式声明 processType
    part = curl_mime_addpart(mime);
    curl_mime_name(part, "processType");
    curl_mime_data(part, "signDocument", CURL_ZERO_TERMINATED);

    curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime);

    // 把响应写入内存
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_to_mem_cb);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &mb);

    // 超时
    curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10L);
    curl_easy_setopt(curl, CURLOPT_TIMEOUT, 120L);

    // 执行
    {
        CURLcode cc = curl_easy_perform(curl);
        if (cc != CURLE_OK) {
            log_error("curl_easy_perform failed: %s", curl_easy_strerror(cc));
            goto cleanup;
        }
        long http_code = 0;
        curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
        if (http_code != 200) {
            log_error("SignServer HTTP status: %ld", http_code);
            goto cleanup;
        }
    }

    if (mb.size < 2) {
        log_error("CMS response too small: %zu bytes", mb.size);
        goto cleanup;
    }
    if (mb.data[0] == 0x30 && mb.data[1] == 0x82) {
        log_info("CMS response starts with 0x30 0x82 (DER definite length)");
    } else {
        log_debug("CMS response not starting with 0x30 0x82 (got 0x%02X 0x%02X)", mb.data[0], mb.data[1]);
    }

    // 把内存响应塞进 cms_tmp_buf，后续 asn_parse() 与 timestamp_sign() 直接复用
    cms_tmp_buf = BUF_MEM_new();
    if (!cms_tmp_buf) {
        log_error("BUF_MEM_new failed");
        goto cleanup;
    }
    cms_tmp_buf->data = OPENSSL_malloc(mb.size);
    if (!cms_tmp_buf->data) {
        log_error("OPENSSL_malloc failed");
        goto cleanup;
    }
    memcpy(cms_tmp_buf->data, mb.data, mb.size);
    cms_tmp_buf->length = mb.size;

    ret = 0;

    if (digest)
        OPENSSL_free(digest);
cleanup:
    if (ret != 0 && cms_tmp_buf) {
        if (cms_tmp_buf->data)
            OPENSSL_free(cms_tmp_buf->data);
        BUF_MEM_free(cms_tmp_buf);
        cms_tmp_buf = NULL;
    }
    if (mime)
        curl_mime_free(mime);
    if (headers)
        curl_slist_free_all(headers);
    if (curl)
        curl_easy_cleanup(curl);
    if (mb.data)
        free(mb.data);
    return ret;
}

/**
 * 读取文件内容
 */
static unsigned char *read_content(const char *cms_file, size_t *read_len)
{
    unsigned char        *buf = NULL;
    struct asn1_string_st asn;
    FILE                 *fp = fopen(cms_file, "rb");
    if (fp == NULL) {
        log_error("Open file %s failed, error: %s", cms_file, strerror(errno));
        return NULL;
    }
    (void)fseek(fp, 0L, SEEK_END);
    size_t pos = (size_t)ftell(fp);
    (void)fseek(fp, 0L, SEEK_SET);
    buf       = (unsigned char *)malloc(pos);
    if (buf == NULL) {
        log_error("malloc memory failed");
        (void)fclose(fp);
        return NULL;
    }
    *read_len = fread(buf, 1, pos, fp);
    (void)fclose(fp);
    if (*read_len != pos) {
        log_error("Read file %s content not finished, broken", cms_file);
        free(buf);
        return NULL;
    }
    return buf;
}

/**
 * 记录OID信息，用于asn中查找path
 */
struct oid_info {
    const char          *name;      // 名称
    const unsigned char *oid;       // oid数组
    size_t               length;    // oid长度
    struct asn_data     *ad;        // asn
    unsigned int         path[32];  // 32个长度足够
    int                  idx;       // 查找第几个oid
};
#define OID_LAST_IDX 0xffff  // 表示获取最后一个OID 65535个足够

/**
 * 从asn中查找指定oid的path
 * path规则为首字符表示path的长度（从1计数，0表示不存在）
 */
static void find_path_by_oid(struct asn_data *ad, struct oid_info *oi, unsigned int *tmp_path, int tmp_len,
                             size_t parent_idx)
{
    tmp_path[tmp_path[0]] = (unsigned int)parent_idx;
    if (ad->asn.type == 0x06 && ad->asn.length == (int)oi->length) {
        if (memcmp(oi->oid, ad->asn.data, oi->length) == 0) {
            if (oi->idx == OID_LAST_IDX) {
                // 获取最后一个匹配的OID，则每次都复制
                memcpy(oi->path, tmp_path, (size_t)tmp_len);
                oi->ad = ad;
            } else {
                oi->idx--;
                if (oi->idx == 0) {
                    memcpy(oi->path, tmp_path, (size_t)tmp_len);
                    oi->ad = ad;
                }
            }
        }
    }
    for (int i = 0; i < ad->n; i++) {
        struct asn_data *next = ad->next[i];
        tmp_path[0]           = tmp_path[0] + 1;
        find_path_by_oid(next, oi, tmp_path, tmp_len, (size_t)i);
        // 未找到，长度减一
        tmp_path[0] = tmp_path[0] - 1;
    }
}

/**
 * 打印path
 */
static void show_path(const struct oid_info *oi)
{
    char str[256] = {0};
    for (unsigned int i = 2; i <= oi->path[0]; i++) {
        size_t str_length = sizeof(str) - strlen(str);
        if (i != 2) {
            snprintf(str + strlen(str), str_length, "/%u", oi->path[i]);
        } else {
            snprintf(str + strlen(str), str_length, "%u", oi->path[i]);
        }
    }
    log_debug("Oid info, %s: %s", oi->name, str);
}

// cms签名的加密数据
static const unsigned char rsa_encryption_oid[] = {0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01};
// ts的签名数据
static const unsigned char signed_data_oid[] = {0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02};
// timestamp oid
static const unsigned char timestamp_oid[] = {0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x10, 0x02, 0x0e};

static unsigned char *dump_memory(const unsigned char *data, size_t len)
{
    unsigned char *tmp = malloc(len);
    if (tmp == NULL) {
        log_error("malloc memory failed");
        return NULL;
    }
    memcpy(tmp, data, len);
    return tmp;
}

static void free_ts_asn(struct asn1_string_st *ctx_specific, struct asn1_string_st *sequence_asn,
    struct asn1_string_st *oid_asn, struct asn1_string_st *set_asn)
{
    if (ctx_specific != NULL) {
        free(ctx_specific);
        ctx_specific = NULL;
    }

    if (sequence_asn != NULL) {
        free(sequence_asn);
        sequence_asn = NULL;
    }

    if (oid_asn != NULL) {
        free(oid_asn);
        oid_asn = NULL;
    }

    if (set_asn != NULL) {
        free(set_asn);
        set_asn = NULL;
    }
}

/**
 * 向cms签名文件中追加时间戳签名
 */
static void append_ts_asn(struct asn_data *cms_context_ad, struct asn_data *ts_context_ad)
{
    struct asn1_string_st *ctx_specific = NULL;
    struct asn1_string_st *sequence_asn = NULL;
    struct asn1_string_st *oid_asn = NULL;
    struct asn1_string_st *set_asn = NULL;
    // 0xa0
    do {
        ctx_specific = (struct asn1_string_st *)malloc(sizeof(struct asn1_string_st));
        if (ctx_specific == NULL) {
            log_error("ctx_specific malloc mem failed");
            break;
        }
        memset(ctx_specific, 0, sizeof(struct asn1_string_st));
        ctx_specific->type               = 0xA1;
        ctx_specific->length             = 0;
        struct asn_data *ctx_specific_ad = append_asn_data(cms_context_ad, ctx_specific, 1);

        // 0xa0 -> 0x30
        sequence_asn = (struct asn1_string_st *)malloc(sizeof(struct asn1_string_st));
        if (sequence_asn == NULL) {
            log_error("sequence_asn malloc mem failed");
            break;
        }
        memset(sequence_asn, 0, sizeof(struct asn1_string_st));
        sequence_asn->type               = 0x30;
        sequence_asn->length             = 0;
        struct asn_data *sequence_asn_ad = append_asn_data(ctx_specific_ad, sequence_asn, 1);

        // 0xa0 -> 0x30 -> 0x06
        oid_asn = (struct asn1_string_st *)malloc(sizeof(struct asn1_string_st));
        if (oid_asn == NULL) {
            log_error("oid_asn malloc mem failed");
            break;
        }
        memset(oid_asn, 0, sizeof(struct asn1_string_st));
        oid_asn->type   = 0x06;
        oid_asn->length = (int)sizeof(timestamp_oid);
        oid_asn->data   = dump_memory(timestamp_oid, sizeof(timestamp_oid));
        (void)append_asn_data(sequence_asn_ad, oid_asn, 1);

        // 0xa0 -> 0x30 -> 0x06
        //      \_ 0x31
        set_asn = (struct asn1_string_st *)malloc(sizeof(struct asn1_string_st));
        if (set_asn == NULL) {
            log_error("set_asn malloc mem failed");
            break;
        }
        memset(set_asn, 0, sizeof(struct asn1_string_st));
        set_asn->type               = 0x31;
        set_asn->length             = 0;
        struct asn_data *set_asn_ad = append_asn_data(sequence_asn_ad, set_asn, 1);

        // 0xa0 -> 0x30 -> 0x06
        //      \_ 0x31 -> 0x30
        int tmp_len                = ts_context_ad->real_length;
        ts_context_ad->real_length = ts_context_ad->asn.length;
        (void)append_asn_data(set_asn_ad, &ts_context_ad->asn, 1);
        ts_context_ad->real_length = tmp_len;
    } while (0);

    free_ts_asn(ctx_specific, sequence_asn, oid_asn, set_asn);
}

/**
 * 合并CMS签名和时间戳签名
 */
static int timestamp_sign_merge(struct asn_data *tsa_ad)
{
    log_info("Merge timestamp signed data");
    unsigned int    path[32] = {0};
    struct oid_info cms_oi   = {
        .name   = "rspEncryption",
        .oid    = rsa_encryption_oid,
        .length = sizeof(rsa_encryption_oid),
        .idx    = OID_LAST_IDX,
    };
    find_path_by_oid(&cms_ad, &cms_oi, path, sizeof(path), 0);
    if (cms_oi.ad == NULL) {
        log_error("Can't find the rsa_encryption_oid path");
        return -1;
    }
    show_path(&cms_oi);
    struct asn_data *cms_context_ad = cms_oi.ad->prev->prev;  // 指向父对象的下一个对象
    log_debug("Cms append position, type: 0x%02x, length: 0x%04x", cms_context_ad->asn.type,
              cms_context_ad->asn.length);
    struct oid_info ts_oi = {
        .name   = "Timestamp signedData",
        .oid    = signed_data_oid,
        .length = sizeof(signed_data_oid),
        .idx    = 1,
    };
    memset(path, 0, sizeof(path));
    find_path_by_oid(tsa_ad, &ts_oi, path, sizeof(path), 0);
    if (ts_oi.ad == NULL) {
        log_error("Can't find the signed_data_oid path");
        return -1;
    }
    show_path(&ts_oi);
    struct asn_data *ts_context_ad = ts_oi.ad->prev;  // 时间戳签名内容为signed_data_oid的父对象
    log_debug("Timestamp sign data, type: 0x%02x, length: 0x%04x", ts_context_ad->asn.type, ts_context_ad->asn.length);

    // 追加时间戳签名
    append_ts_asn(cms_context_ad, ts_context_ad);
    return 0;
}

static int timestamp_sign_merge_remote(struct asn_data *tsa_ad)
{
    log_info("Merge timestamp signed data");

    unsigned int path[32] = {0};

    // 用签名 OCTET STRING 的父节点（SignerInfo）作为追加 unsignedAttrs 的准确锚点
    if (g_sig_octet_node == NULL || g_sig_octet_node->prev == NULL) {
        log_error("Signature OCTET anchor not found");
        return -1;
    }
    struct asn_data *cms_context_ad = g_sig_octet_node->prev; // SignerInfo
    log_debug("Cms append position, type: 0x%02x, length: 0x%04x", cms_context_ad->asn.type,
              cms_context_ad->asn.length);

    struct oid_info ts_oi = {
        .name   = "Timestamp signedData",
        .oid    = signed_data_oid,
        .length = sizeof(signed_data_oid),
        .idx    = 1,
    };
    memset(path, 0, sizeof(path));
    find_path_by_oid(tsa_ad, &ts_oi, path, sizeof(path), 0);
    if (ts_oi.ad == NULL) {
        log_error("Can't find the signed_data_oid path");
        return -1;
    }
    show_path(&ts_oi);
    struct asn_data *ts_context_ad = ts_oi.ad->prev; // 时间戳签名内容为signed_data_oid的父对象
    log_debug("Timestamp sign data, type: 0x%02x, length: 0x%04x", ts_context_ad->asn.type, ts_context_ad->asn.length);

    // 追加时间戳签名
    append_ts_asn(cms_context_ad, ts_context_ad);
    return 0;
}

/**
 * 将asn数据写入文件
 */
static void as_write_st(int ident, FILE *fp, const struct asn_data *ad)
{
    const struct asn1_string_st *asn = &ad->asn;
    // 写类型
    unsigned char buf[4] = {0};  // 4个字符足够存储长度
    int  size   = 0;
    (void)fwrite(&asn->type, 1, 1, fp);
    if (asn->length <= 127) {
        buf[size] = (unsigned char)asn->length;
        size++;
        log_debug_ident(ident, "write type: 0x%02x, length: 0x%04x, real length: 0x%04x", asn->type, asn->length,
                        ad->real_length);
    } else {
        int length = asn->length;
        while (length > 0) {
            buf[size] = length % 256;  // 低字节在前
            length    = length / 256;  // 除256
            size++;
        }
        unsigned char pos0 = 0x80 | (unsigned char)size;
        fwrite(&pos0, 1, 1, fp);
        log_debug_ident(ident, "write type: 0x%02x, length: 0x%02x%04x, real length: 0x%04x", asn->type, pos0,
                        asn->length, ad->real_length);
    }
    for (int i = size - 1; i >= 0; i--) {
        (void)fwrite(buf + i, 1, 1, fp);
    }
    if (ad->real_length > 0) {
        fwrite(asn->data, (size_t)ad->real_length, 1, fp);
    }
}

/**
 * 将asn_data写入文件后递归写入子节点
 */
static void asn_write(int in_ident, FILE *fp, const struct asn_data *ad)
{
    int ident = in_ident;
    ident += 4;  // 显示缩进设置为4
    as_write_st(ident, fp, ad);
    for (int i = 0; i < ad->n; i++) {
        struct asn_data *next = ad->next[i];
        asn_write(ident, fp, next);
    }
}

/**
 * 将cms写入文件
 */
static int cms_write(const char *file)
{
    FILE *fp = fopen(file, "w");
    log_info("Write cms data to %s start", file);
    if (fp == NULL) {
        log_error("Open file %s failed, error: %s", file, strerror(errno));
        return -1;
    }
    asn_write(0, fp, cms_ad.next[0]);
    (void)fclose(fp);
    log_info("Write cms data to %s successfully", file);
    return 0;
}

static int read_tsa(void)
{
    // 读取tsa内容，无需释放
    size_t tsa_len = 0;
    unsigned char *tsa_data = read_content(TS_FILE, &tsa_len);
    unlink_file(TS_FILE);
    if (tsa_data == NULL) {
        return -1;
    }
    struct asn_data tsa_ad;
    memset(&tsa_ad, 0, sizeof(tsa_ad));
    log_info("Parse timestamp signed data");
    if (asn_parse(0, tsa_data, tsa_len, &tsa_ad) != 0) {
        return -1;
    }
    if (timestamp_sign_merge(&tsa_ad) != 0) {
        return -1;
    }

    return 0;
}

static int read_tsa_remote(void)
{
    // 读取tsa内容，无需释放
    size_t tsa_len          = 0;
    unsigned char *tsa_data = read_content(TS_FILE, &tsa_len);
    unlink_file(TS_FILE);
    if (tsa_data == NULL) {
        return -1;
    }
    struct asn_data tsa_ad;
    memset(&tsa_ad, 0, sizeof(tsa_ad));
    log_info("Parse timestamp signed data");
    if (asn_parse(0, tsa_data, tsa_len, &tsa_ad) != 0) {
        return -1;
    }
    if (timestamp_sign_merge_remote(&tsa_ad) != 0) {
        return -1;
    }

    return 0;
}

/**
 * 时间戳签名流程
 */
static int timestamp_sign(void)
{
    int            len  = 0;
    unsigned char *hash = get_rsa_encryption(&cms_ad, &len);
    if (hash == NULL) {
        log_error("Get rspEncryption data failed");
        return -1;
    }

    log_info("Found rsaEncryption data");
    int flag = -1;
    do {
        FILE  *fp = fopen(CMS_HASH_FILE, "w");
        if (fp == NULL) {
            log_error("open cms_hash_file failed");
            break;
        }
        size_t write_len = fwrite(hash, 1, (size_t)len, fp);
        (void)fclose(fp);
        if ((int)write_len != len) {
            log_error("Write %s failed, error: %s", CMS_HASH_FILE, strerror(errno));
            break;
        }
        // 创建tsa签名请求
        int ret = tsa_request(CMS_HASH_FILE, TAS_FILE);
        unlink_file(CMS_HASH_FILE);
        if (ret != 0) {
            break;
        }
        // 时间戳签名
        ret = tsa_signed(TAS_FILE, TS_FILE);
        unlink_file(TAS_FILE);
        if (ret != 0) {
            break;
        }

        if (read_tsa() != 0) {
            break;
        }

        flag = 0;
    } while (0);

    free(hash);
    hash = NULL;
    if (flag != 0) {
        return flag;
    }
    return cms_write(out_file);
}

static int timestamp_sign_remote(void)
{
    int            len  = 0;
    unsigned char *hash = get_rsa_encryption_remote(&cms_ad, &len);
    if (hash == NULL) {
        log_error("Get rspEncryption data failed");
        return -1;
    }

    log_info("Found rsaEncryption data");
    int flag = -1;
    do {
        FILE  *fp = fopen(CMS_HASH_FILE, "w");
        if (fp == NULL) {
            log_error("open cms_hash_file failed");
            break;
        }
        size_t write_len = fwrite(hash, 1, (size_t)len, fp);
        (void)fclose(fp);
        if ((int)write_len != len) {
            log_error("Write %s failed, error: %s", CMS_HASH_FILE, strerror(errno));
            break;
        }
        // 创建tsa签名请求
        int ret = tsa_request(CMS_HASH_FILE, TAS_FILE);
        unlink_file(CMS_HASH_FILE);
        if (ret != 0) {
            break;
        }
        // 时间戳签名
        ret = tsa_signed_signserver_curl(TAS_FILE, TS_FILE);
        unlink_file(TAS_FILE);
        if (ret != 0) {
            break;
        }

        if (read_tsa_remote() != 0) {
            break;
        }

        flag = 0;
    } while (0);

    free(hash);
    hash = NULL;
    if (flag != 0) {
        return flag;
    }
    return cms_write(out_file);
}

static int arg_parse(int argc, char **argv)
{
    int opt = 0;
    while ((opt = getopt_long(argc, argv, opt_string, opts, NULL)) != -1) {
        switch (opt) {
            case 's':
                signer_file = optarg;
                break;
            case 'i':
                in_file = optarg;
                break;
            case 'o':
                out_file = optarg;
                break;
            case 't':
                ts_signer = optarg;
                break;
            case 'T':
                tsa_cnf = optarg;
                break;
            case 'p':
                password = optarg;
                break;
            case 'v':
                verbose = 1;
                break;
            case 'U':
                tsa_url = optarg;
                break;
            case 'k':
                tsa_insecure = 1;
                break;
            case 1003: // --curl-cafile=FILE
                curl_cafile = optarg;
                break;
            case 'S': // --cms-url
                cms_url = optarg;
                break;
            case 1004: // --cms-worker-id
                cms_worker_id = optarg;
                break;
            case 1005: // --cms-worker-name
                cms_worker_name = optarg;
                break;
            case 1006:                        // --cms-client-hash
                cms_client_hash_alg = optarg; // 例： "SHA-256"
                break;
            case 1007: 
                keyfactory_enabled = 1; 
                break;   // 打开厂商开关
            default:
                verbose = 1;
                print_help(argv[0]);
                return -1;
        }
    }

    if (!password) {
        password = getenv("SIGN_PASSWORD");
    }
    if (password && strlen(password) != 32) {
        log_error("password length error, must 32 bytes, get %zu bytes", strlen(password));
        return -1;
    }
    if (verbose != 0) {
        log_debug("Parameters:");
        log_debug("  Signer certificate:             %s", signer_file);
        log_debug("  Input:                          %s", in_file);
        log_debug("  Output:                         %s", out_file);
        if (tsa_url)
            log_debug("  TSA URL (SignServer):           %s", tsa_url);
        if (curl_cafile)
            log_debug("  CAfile:                          %s", curl_cafile);
        if (tsa_insecure)
            log_debug("  TLS verify:                      INSECURE (-k)");
        if (cms_url)
            log_debug("  CMS URL (SignServer):           %s", cms_url);
        if (cms_worker_id)
            log_debug("  CMS workerId (form):            %s", cms_worker_id);
        if (cms_worker_name)
            log_debug("  CMS workerName (form):          %s", cms_worker_name);
        if (cms_client_hash_alg)
            log_debug("  CMS client-side hash:           %s", cms_client_hash_alg);
    }
    return 0;
}

int main(int argc, char **argv)
{
    int ret = 0;
    ret     = arg_parse(argc, argv);
    if (ret != 0) {
        return ret;
    }

    // 正常签名后会生成cms_tmp_buf数据，该数据无需释放
    // CMS：厂商开关未打开 → 强制本地；打开且配置 URL → 走云端
    if (!keyfactory_enabled && cms_url != NULL) {
        log_debug("--cms-url ignored because --keyfactory not set (fallback to local CMS).");
    }
    ret = (keyfactory_enabled && cms_url != NULL) ? cms_signed_signserver_curl() : cms_sign();
    if (ret != 0) {
        return ret;
    }
    log_info("Parse cms data start");
    ret = asn_parse(0, (unsigned char *)cms_tmp_buf->data, cms_tmp_buf->length, &cms_ad);
    if (ret != 0) {
        return ret;
    }

    // TSA：同上，未开关→强制本地；开关+URL→云端
    if (!keyfactory_enabled && tsa_url != NULL) {
        log_debug("--tsa-url ignored because --keyfactory not set (fallback to local TSA).");
    }
    ret = (keyfactory_enabled && tsa_url != NULL) ? timestamp_sign_remote() : timestamp_sign();
    if (ret != 0) {
        return ret;
    }
    log_info("Finished");
    return 0;
}
