/* signature.c
 *
 * Copyright (C) 2006-2026 wolfSSL Inc.
 *
 * This file is part of wolfSSL.
 *
 * wolfSSL is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * wolfSSL is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
 */

#include <wolfssl/wolfcrypt/libwolfssl_sources.h>

#include <wolfssl/wolfcrypt/signature.h>
#ifndef NO_ASN
#include <wolfssl/wolfcrypt/asn.h>
#endif
#ifdef HAVE_ECC
#include <wolfssl/wolfcrypt/ecc.h>
#endif
#ifndef NO_RSA
#include <wolfssl/wolfcrypt/rsa.h>
#endif

/* If ECC and RSA are disabled then disable signature wrapper */
#if (!defined(HAVE_ECC) || (defined(HAVE_ECC) && !defined(HAVE_ECC_SIGN) \
    && !defined(HAVE_ECC_VERIFY))) && defined(NO_RSA)
    #undef NO_SIG_WRAPPER
    #define NO_SIG_WRAPPER
#endif

/* Signature wrapper disabled check */
#ifndef NO_SIG_WRAPPER

#if !defined(NO_RSA) && defined(NO_ASN)
    #ifndef MAX_DER_DIGEST_ASN_SZ
        #define MAX_DER_DIGEST_ASN_SZ 36
    #endif
    #ifndef MAX_ENCODED_SIG_SZ
        #define MAX_ENCODED_SIG_SZ 1024 /* Supports 8192 bit keys */
    #endif
#endif

/* Minimum hash strength accepted by the wc_SignatureVerify/Generate
 * convenience APIs. Default is SHA-256 to keep MD5 and SHA-1 (both with
 * known collision attacks) out of new code. Define WC_SIG_MIN_HASH_TYPE
 * to a weaker wc_HashType (e.g. WC_HASH_TYPE_SHA) to opt back into legacy
 * behavior. The lower-level wc_SignatureVerifyHash/wc_SignatureGenerateHash
 * APIs are unaffected. */
#ifndef WC_SIG_MIN_HASH_TYPE
    #define WC_SIG_MIN_HASH_TYPE WC_HASH_TYPE_SHA256
#endif

static int wc_SignatureCheckHashStrength(enum wc_HashType hash_type)
{
    int min_sz, this_sz;

    min_sz = wc_HashGetDigestSize(WC_SIG_MIN_HASH_TYPE);
    if (min_sz < 0) {
        /* configured floor not compiled in - skip enforcement */
        return 0;
    }
    this_sz = wc_HashGetDigestSize(hash_type);
    if (this_sz < 0) {
        return this_sz;
    }
    if (this_sz < min_sz) {
        WOLFSSL_MSG("wc_Signature*: hash weaker than WC_SIG_MIN_HASH_TYPE");
        return BAD_FUNC_ARG;
    }
    return 0;
}


#if !defined(NO_RSA) && defined(WOLFSSL_CRYPTOCELL)
    extern int cc310_RsaSSL_Verify(const byte* in, word32 inLen, byte* sig,
                                RsaKey* key, CRYS_RSA_HASH_OpMode_t mode);
    extern int cc310_RsaSSL_Sign(const byte* in, word32 inLen, byte* out,
                    word32 outLen, RsaKey* key, CRYS_RSA_HASH_OpMode_t mode);
#endif

#if !defined(NO_RSA) && !defined(NO_ASN)
static int wc_SignatureDerEncode(enum wc_HashType hash_type, byte* hash_data,
    word32 hash_len, word32* hash_enc_len)
{
    int ret, oid;

    ret = wc_HashGetOID(hash_type);
    if (ret < 0) {
        return ret;
    }
    oid = ret;

    ret = (int)wc_EncodeSignature(hash_data, hash_data, hash_len, oid);
    if (ret > 0) {
        *hash_enc_len = (word32)ret;
        ret = 0;
    }

    return ret;
}
#endif /* !NO_RSA && !NO_ASN */

int wc_SignatureGetSize(enum wc_SignatureType sig_type,
    const void* key, word32 key_len)
{
    int sig_len = WC_NO_ERR_TRACE(BAD_FUNC_ARG);

    /* Suppress possible unused args if all signature types are disabled */
    (void)key;
    (void)key_len;

    switch(sig_type) {
        case WC_SIGNATURE_TYPE_ECC:
#ifdef HAVE_ECC
            /* Verify that key_len matches exactly sizeof(ecc_key).
             * This is a necessary but not sufficient type check:
             * the const void* API cannot verify the actual runtime
             * type of the pointed-to object.
             * Callers must pass a valid ecc_key* cast to const void*. */
            if ((size_t)key_len == sizeof(ecc_key)) {
#if defined(HAVE_SELFTEST) || (defined(HAVE_FIPS) && FIPS_VERSION3_LT(5,0,0))
                sig_len = wc_ecc_sig_size((ecc_key*)(wc_ptr_t)key);
#else
                sig_len = wc_ecc_sig_size((const ecc_key*)key);
#endif
            }
            else {
                WOLFSSL_MSG("wc_SignatureGetSize: Invalid ECC key size");
            }
#else
            sig_len = SIG_TYPE_E;
#endif
            break;

        case WC_SIGNATURE_TYPE_RSA_W_ENC:
        case WC_SIGNATURE_TYPE_RSA:
#ifndef NO_RSA
            /* Verify that key_len matches exactly sizeof(RsaKey).
             * Same caveat as the ECC case above: size equality is necessary
             * but not sufficient; the caller must pass a valid RsaKey*. */
            if ((size_t)key_len == sizeof(RsaKey)) {
#if defined(HAVE_SELFTEST) || (defined(HAVE_FIPS) && FIPS_VERSION3_LT(5,0,0))
                sig_len = wc_RsaEncryptSize((RsaKey*)(wc_ptr_t)key);
#else
                sig_len = wc_RsaEncryptSize((const RsaKey*)key);
#endif
#if defined(WOLFSSL_MICROCHIP_TA100)
                if (sig_len <= 0) {
                    const RsaKey* r = (const RsaKey*)key;
                    /* TA100 stores hardware-backed RSA public keys outside
                     * the software mp_int fields, so use the backend's fixed
                     * public-key buffer size when handles are present. */
                    if (r->rKeyH != 0 || r->uKeyH != 0) {
                        sig_len = WOLFSSL_TA_KEY_TYPE_RSA_SIZE;
                    }
                }
#endif
            }
            else {
                WOLFSSL_MSG("wc_SignatureGetSize: Invalid RsaKey key size");
            }
#else
            sig_len = SIG_TYPE_E;
#endif
            break;

        case WC_SIGNATURE_TYPE_NONE:
        default:
            sig_len = BAD_FUNC_ARG;
            break;
    }
    return sig_len;
}

int wc_SignatureVerifyHash(
    enum wc_HashType hash_type, enum wc_SignatureType sig_type,
    const byte* hash_data, word32 hash_len,
    const byte* sig, word32 sig_len,
    void* key, word32 key_len)
{
    int ret;

    /* Check arguments */
    if (hash_data == NULL || hash_len == 0 ||
        sig == NULL || sig_len == 0 ||
        key == NULL || key_len == 0) {
        return BAD_FUNC_ARG;
    }

    /* Validate signature len (1 to max is okay) */
    if ((int)sig_len > wc_SignatureGetSize(sig_type, key, key_len)) {
        WOLFSSL_MSG("wc_SignatureVerify: Invalid sig type/len");
        return BAD_FUNC_ARG;
    }

    /* Validate hash size */
    ret = wc_HashGetDigestSize(hash_type);
    if (ret < 0) {
        WOLFSSL_MSG("wc_SignatureVerify: Invalid hash type/len");
        return ret;
    }

#if !defined(NO_RSA) && !defined(WOLFSSL_RSA_PUBLIC_ONLY)
    /* For WC_SIGNATURE_TYPE_RSA_W_ENC, we need to extract the actual size of
     * the ASN.1-encoded hash.
     */
    if (sig_type == WC_SIGNATURE_TYPE_RSA_W_ENC) {
        int hash_dec_len;
        word32 idx = 0;
        if (GetSequence(hash_data, &idx, &hash_dec_len, hash_len) < 0)
            return ASN_PARSE_E;
        /* skip the AlgorithmIdentifier */
        if (GetSequence(hash_data, &idx, &hash_dec_len, hash_len) < 0)
            return ASN_PARSE_E;
        idx += (word32)hash_dec_len;
        /* now sitting at the OCTET STRING containing the digest */
        if (GetOctetString(hash_data, &idx, &hash_dec_len, hash_len) < 0)
            return ASN_PARSE_E;
        if (hash_dec_len != ret)
            return BAD_LENGTH_E;
    }
    else
#endif
    {
        if (hash_len != (word32)ret) {
            WOLFSSL_MSG("wc_SignatureVerify: Invalid hash size");
            return BAD_LENGTH_E;
        }
    }

    ret = 0;

    /* Verify signature using hash */
    switch (sig_type) {
        case WC_SIGNATURE_TYPE_ECC:
        {
#if defined(HAVE_ECC) && defined(HAVE_ECC_VERIFY)
            int is_valid_sig = 0;

            /* Perform verification of signature using provided ECC key */
            do {
            #ifdef WOLFSSL_ASYNC_CRYPT
                ret = wc_AsyncWait(ret, &((ecc_key*)key)->asyncDev,
                    WC_ASYNC_FLAG_CALL_AGAIN);
            #endif
            if (ret >= 0)
                ret = wc_ecc_verify_hash(sig, sig_len, hash_data, hash_len,
                    &is_valid_sig, (ecc_key*)key);
            } while (ret == WC_NO_ERR_TRACE(WC_PENDING_E));
            if (ret != 0 || is_valid_sig != 1) {
                ret = SIG_VERIFY_E;
            }
#else
            ret = SIG_TYPE_E;
#endif
            break;
        }

        case WC_SIGNATURE_TYPE_RSA_W_ENC:
        case WC_SIGNATURE_TYPE_RSA:
        {
#ifndef NO_RSA
    #ifdef WOLFSSL_CRYPTOCELL
        if (sig_type == WC_SIGNATURE_TYPE_RSA_W_ENC) {
            ret = cc310_RsaSSL_Verify(hash_data, hash_len, (byte*)sig,
                (RsaKey*)key, cc310_hashModeRSA(hash_type, 0));
        }
        else {
            ret = cc310_RsaSSL_Verify(hash_data, hash_len, (byte*)sig,
                (RsaKey*)key, cc310_hashModeRSA(hash_type, 1));
        }
        if (ret != 0) {
            ret = SIG_VERIFY_E;
        }
    #else

            word32 plain_len = hash_len;
        #if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
            byte *plain_data;
        #else
            ALIGN64 byte plain_data[MAX_ENCODED_SIG_SZ];
        #endif

            /* Make sure the plain text output is at least key size */
            if (plain_len < sig_len) {
                plain_len = sig_len;
            }
        #if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
            plain_data = (byte*)XMALLOC(plain_len, NULL, DYNAMIC_TYPE_TMP_BUFFER);
            if (plain_data)
        #else
            if (plain_len <= sizeof(plain_data))
        #endif
            {
                byte* plain_ptr = NULL;
                XMEMSET(plain_data, 0, plain_len);
                XMEMCPY(plain_data, sig, sig_len);
                /* Perform verification of signature using provided RSA key */
                do {
                #ifdef WOLFSSL_ASYNC_CRYPT
                    ret = wc_AsyncWait(ret, &((RsaKey*)key)->asyncDev,
                        WC_ASYNC_FLAG_CALL_AGAIN);
                #endif
                if (ret >= 0)
                        ret = wc_RsaSSL_VerifyInline(plain_data, sig_len,
                            &plain_ptr, (RsaKey*)key);
                } while (ret == WC_NO_ERR_TRACE(WC_PENDING_E));
                if (ret >= 0 && plain_ptr) {
                    if ((word32)ret == hash_len &&
                            XMEMCMP(plain_ptr, hash_data, hash_len) == 0) {
                        ret = 0; /* Success */
                    }
                    else {
                        ret = SIG_VERIFY_E;
                    }
                }
            #if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
                XFREE(plain_data, NULL, DYNAMIC_TYPE_TMP_BUFFER);
            #endif
            }
            else {
                ret = MEMORY_E;
            }
    #endif /* WOLFSSL_CRYPTOCELL */
            if (ret != 0) {
                WOLFSSL_MSG("RSA Signature Verify failed!");
            }
#else
            ret = SIG_TYPE_E;
#endif
            break;
        }

        case WC_SIGNATURE_TYPE_NONE:
        default:
            ret = BAD_FUNC_ARG;
            break;
    }

    return ret;
}

int wc_SignatureVerify(
    enum wc_HashType hash_type, enum wc_SignatureType sig_type,
    const byte* data, word32 data_len,
    const byte* sig, word32 sig_len,
    void* key, word32 key_len)
{
    int ret;
    word32 hash_len, hash_enc_len;
#if defined(WOLFSSL_SMALL_STACK) || defined(NO_ASN)
    byte *hash_data;
#else
    byte hash_data[MAX_DER_DIGEST_SZ];
#endif

    /* Check arguments */
    if (data == NULL || data_len == 0 ||
        sig == NULL || sig_len == 0 ||
        key == NULL || key_len == 0) {
        return BAD_FUNC_ARG;
    }

    /* Validate signature len (1 to max is okay) */
    if ((int)sig_len > wc_SignatureGetSize(sig_type, key, key_len)) {
        WOLFSSL_MSG("wc_SignatureVerify: Invalid sig type/len");
        return BAD_FUNC_ARG;
    }

    /* Validate hash size */
    ret = wc_HashGetDigestSize(hash_type);
    if (ret < 0) {
        WOLFSSL_MSG("wc_SignatureVerify: Invalid hash type/len");
        return ret;
    }
    hash_enc_len = hash_len = (word32)ret;

    /* Reject hashes weaker than WC_SIG_MIN_HASH_TYPE (default SHA-256) */
    ret = wc_SignatureCheckHashStrength(hash_type);
    if (ret != 0) {
        return ret;
    }

#ifndef NO_RSA
    if (sig_type == WC_SIGNATURE_TYPE_RSA_W_ENC) {
        /* For RSA with ASN.1 encoding include room */
        hash_enc_len += MAX_DER_DIGEST_ASN_SZ;
    }
#endif

#if defined(WOLFSSL_SMALL_STACK) || defined(NO_ASN)
    /* Allocate temporary buffer for hash data */
    hash_data = (byte*)XMALLOC(hash_enc_len, NULL, DYNAMIC_TYPE_TMP_BUFFER);
    if (hash_data == NULL) {
        return MEMORY_E;
    }
#endif

    /* Perform hash of data */
    ret = wc_Hash(hash_type, data, data_len, hash_data, hash_len);
    if (ret == 0) {
        /* Handle RSA with DER encoding */
        if (sig_type == WC_SIGNATURE_TYPE_RSA_W_ENC) {
        #if defined(NO_RSA) || defined(NO_ASN)
            ret = SIG_TYPE_E;
        #else
            ret = wc_SignatureDerEncode(hash_type, hash_data, hash_len,
                &hash_enc_len);
        #endif
        }

        if (ret == 0) {
            /* Verify signature using hash */
            ret = wc_SignatureVerifyHash(hash_type, sig_type,
                hash_data, hash_enc_len, sig, sig_len, key, key_len);
        }
    }

#if defined(WOLFSSL_SMALL_STACK) || defined(NO_ASN)
    XFREE(hash_data, NULL, DYNAMIC_TYPE_TMP_BUFFER);
#endif

    return ret;
}


int wc_SignatureGenerateHash(
    enum wc_HashType hash_type, enum wc_SignatureType sig_type,
    const byte* hash_data, word32 hash_len,
    byte* sig, word32 *sig_len,
    void* key, word32 key_len, WC_RNG* rng)
{
    return wc_SignatureGenerateHash_ex(hash_type, sig_type, hash_data, hash_len,
        sig, sig_len, key, key_len, rng, 1);
}

int wc_SignatureGenerateHash_ex(
    enum wc_HashType hash_type, enum wc_SignatureType sig_type,
    const byte* hash_data, word32 hash_len,
    byte* sig, word32 *sig_len,
    void* key, word32 key_len, WC_RNG* rng, int verify)
{
    int ret;

    /* Suppress possible unused arg if all signature types are disabled */
    (void)rng;

    /* Check arguments */
    if (hash_data == NULL || hash_len == 0 ||
        sig == NULL || sig_len == NULL || *sig_len == 0 ||
        key == NULL || key_len == 0) {
        return BAD_FUNC_ARG;
    }

    /* Validate signature len (needs to be at least max) */
    if ((int)*sig_len < wc_SignatureGetSize(sig_type, key, key_len)) {
        WOLFSSL_MSG("wc_SignatureGenerate: Invalid sig type/len");
        return BAD_FUNC_ARG;
    }

    /* Validate hash size */
    ret = wc_HashGetDigestSize(hash_type);
    if (ret < 0) {
        WOLFSSL_MSG("wc_SignatureGenerate: Invalid hash type/len");
        return ret;
    }
    ret = 0;

    /* Create signature using hash as data */
    switch (sig_type) {
        case WC_SIGNATURE_TYPE_ECC:
#if defined(HAVE_ECC) && defined(HAVE_ECC_SIGN)
            /* Create signature using provided ECC key */
            do {
            #ifdef WOLFSSL_ASYNC_CRYPT
                ret = wc_AsyncWait(ret, &((ecc_key*)key)->asyncDev,
                    WC_ASYNC_FLAG_CALL_AGAIN);
            #endif
            if (ret >= 0)
                ret = wc_ecc_sign_hash(hash_data, hash_len, sig, sig_len,
                    rng, (ecc_key*)key);
            } while (ret == WC_NO_ERR_TRACE(WC_PENDING_E));
#else
            ret = SIG_TYPE_E;
#endif
            break;

        case WC_SIGNATURE_TYPE_RSA_W_ENC:
        case WC_SIGNATURE_TYPE_RSA:
#if !defined(NO_RSA) && !defined(WOLFSSL_RSA_PUBLIC_ONLY) && \
    !defined(WOLFSSL_RSA_VERIFY_ONLY)
    #ifdef WOLFSSL_CRYPTOCELL
            /* use expected signature size (incoming sig_len could be larger buffer */
            *sig_len = wc_SignatureGetSize(sig_type, key, key_len);
            if (sig_type == WC_SIGNATURE_TYPE_RSA_W_ENC) {
                ret = cc310_RsaSSL_Sign(hash_data, hash_len, sig, *sig_len,
                    (RsaKey*)key, cc310_hashModeRSA(hash_type, 0));
            }
            else {
                ret = cc310_RsaSSL_Sign(hash_data, hash_len, sig, *sig_len,
                    (RsaKey*)key, cc310_hashModeRSA(hash_type, 1));
           }
    #else
            /* Create signature using provided RSA key */
            do {
            #ifdef WOLFSSL_ASYNC_CRYPT
                ret = wc_AsyncWait(ret, &((RsaKey*)key)->asyncDev,
                    WC_ASYNC_FLAG_CALL_AGAIN);
            #endif
                if (ret >= 0)
                    ret = wc_RsaSSL_Sign(hash_data, hash_len, sig, *sig_len,
                        (RsaKey*)key, rng);
            } while (ret == WC_NO_ERR_TRACE(WC_PENDING_E));
    #endif /* WOLFSSL_CRYPTOCELL */
            if (ret >= 0) {
                *sig_len = (word32)ret;
                ret = 0; /* Success */
            }
#else
            ret = SIG_TYPE_E;
#endif
            break;

        case WC_SIGNATURE_TYPE_NONE:
        default:
            ret = BAD_FUNC_ARG;
            break;
    }

    if (ret == 0 && verify) {
        ret = wc_SignatureVerifyHash(hash_type, sig_type, hash_data, hash_len,
            sig, *sig_len, key, key_len);
    }

    return ret;
}

int wc_SignatureGenerate(
    enum wc_HashType hash_type, enum wc_SignatureType sig_type,
    const byte* data, word32 data_len,
    byte* sig, word32 *sig_len,
    void* key, word32 key_len, WC_RNG* rng)
{
    return wc_SignatureGenerate_ex(hash_type, sig_type, data, data_len, sig,
        sig_len, key, key_len, rng, 1);
}

int wc_SignatureGenerate_ex(
    enum wc_HashType hash_type, enum wc_SignatureType sig_type,
    const byte* data, word32 data_len,
    byte* sig, word32 *sig_len,
    void* key, word32 key_len, WC_RNG* rng, int verify)
{
    int ret;
    word32 hash_len, hash_enc_len;
#if defined(WOLFSSL_SMALL_STACK) || defined(NO_ASN)
    byte *hash_data;
#else
    byte hash_data[MAX_DER_DIGEST_SZ];
#endif

    /* Check arguments */
    if (data == NULL || data_len == 0 ||
        sig == NULL || sig_len == NULL || *sig_len == 0 ||
        key == NULL || key_len == 0) {
        return BAD_FUNC_ARG;
    }

    /* Validate signature len (needs to be at least max) */
    if ((int)*sig_len < wc_SignatureGetSize(sig_type, key, key_len)) {
        WOLFSSL_MSG("wc_SignatureGenerate: Invalid sig type/len");
        return BAD_FUNC_ARG;
    }

    /* Validate hash size */
    ret = wc_HashGetDigestSize(hash_type);
    if (ret < 0) {
        WOLFSSL_MSG("wc_SignatureGenerate: Invalid hash type/len");
        return ret;
    }
    hash_enc_len = hash_len = (word32)ret;

    /* Reject hashes weaker than WC_SIG_MIN_HASH_TYPE (default SHA-256) */
    ret = wc_SignatureCheckHashStrength(hash_type);
    if (ret != 0) {
        return ret;
    }

#if !defined(NO_RSA) && !defined(WOLFSSL_RSA_PUBLIC_ONLY)
    if (sig_type == WC_SIGNATURE_TYPE_RSA_W_ENC) {
        /* For RSA with ASN.1 encoding include room */
        hash_enc_len += MAX_DER_DIGEST_ASN_SZ;
    }
#endif

#if defined(WOLFSSL_SMALL_STACK) || defined(NO_ASN)
    /* Allocate temporary buffer for hash data */
    hash_data = (byte*)XMALLOC(hash_enc_len, NULL, DYNAMIC_TYPE_TMP_BUFFER);
    if (hash_data == NULL) {
        return MEMORY_E;
    }
#endif

    /* Perform hash of data */
    ret = wc_Hash(hash_type, data, data_len, hash_data, hash_len);
    if (ret == 0) {
        /* Handle RSA with DER encoding */
        if (sig_type == WC_SIGNATURE_TYPE_RSA_W_ENC) {
        #if defined(NO_RSA) || defined(NO_ASN) || \
                                                defined(WOLFSSL_RSA_PUBLIC_ONLY)
            ret = SIG_TYPE_E;
        #else
            ret = wc_SignatureDerEncode(hash_type, hash_data, hash_len,
                &hash_enc_len);
        #endif
        }
        if (ret == 0) {
            /* Generate signature using hash (also handles verify) */
            ret = wc_SignatureGenerateHash_ex(hash_type, sig_type, hash_data,
                hash_enc_len, sig, sig_len, key, key_len, rng, verify);
        }
    }

#if defined(WOLFSSL_SMALL_STACK) || defined(NO_ASN)
    XFREE(hash_data, NULL, DYNAMIC_TYPE_TMP_BUFFER);
#endif

    return ret;
}

#endif /* NO_SIG_WRAPPER */
