/* Skip the CVE-2025-9230 fix in case the version being livepatched already have a fix for it.  */

#ifndef _DISABLE_CVE_2025_9230

#include <string.h>
#include <stdint.h>

#define OPENSSL_FILE __FILE__
#define OPENSSL_LINE __LINE__
#define OPENSSL_FUNC __func__
#define OSSL_CRYPTO_ALLOC __attribute__((__malloc__))
/** clang-extract: from include/openssl/types.h:57:1 */
typedef struct asn1_string_st ASN1_INTEGER;

/** clang-extract: from include/openssl/types.h:58:1 */
typedef struct asn1_string_st ASN1_ENUMERATED;

/** clang-extract: from include/openssl/types.h:59:1 */
typedef struct asn1_string_st ASN1_BIT_STRING;

/** clang-extract: from include/openssl/types.h:60:1 */
typedef struct asn1_string_st ASN1_OCTET_STRING;

/** clang-extract: from include/openssl/types.h:61:1 */
typedef struct asn1_string_st ASN1_PRINTABLESTRING;

/** clang-extract: from include/openssl/types.h:62:1 */
typedef struct asn1_string_st ASN1_T61STRING;

/** clang-extract: from include/openssl/types.h:63:1 */
typedef struct asn1_string_st ASN1_IA5STRING;

/** clang-extract: from include/openssl/types.h:64:1 */
typedef struct asn1_string_st ASN1_GENERALSTRING;

/** clang-extract: from include/openssl/types.h:65:1 */
typedef struct asn1_string_st ASN1_UNIVERSALSTRING;

/** clang-extract: from include/openssl/types.h:66:1 */
typedef struct asn1_string_st ASN1_BMPSTRING;

/** clang-extract: from include/openssl/types.h:67:1 */
typedef struct asn1_string_st ASN1_UTCTIME;

/** clang-extract: from include/openssl/types.h:69:1 */
typedef struct asn1_string_st ASN1_GENERALIZEDTIME;

/** clang-extract: from include/openssl/types.h:70:1 */
typedef struct asn1_string_st ASN1_VISIBLESTRING;

/** clang-extract: from include/openssl/types.h:71:1 */
typedef struct asn1_string_st ASN1_UTF8STRING;

/** clang-extract: from include/openssl/types.h:72:1 */
typedef struct asn1_string_st ASN1_STRING;

/** clang-extract: from include/openssl/types.h:73:1 */
typedef int ASN1_BOOLEAN;

/** clang-extract: from include/openssl/types.h:77:1 */
typedef struct asn1_type_st ASN1_TYPE;

/** clang-extract: from include/openssl/types.h:78:1 */
typedef struct asn1_object_st ASN1_OBJECT;

/** clang-extract: from include/openssl/types.h:81:1 */
typedef struct ASN1_ITEM_st ASN1_ITEM;

/** clang-extract: from include/openssl/types.h:104:1 */
typedef struct evp_cipher_st EVP_CIPHER;

/** clang-extract: from include/openssl/types.h:105:1 */
typedef struct evp_cipher_ctx_st EVP_CIPHER_CTX;

/** clang-extract: from include/openssl/types.h:161:1 */
typedef struct X509_algor_st X509_ALGOR;

/** clang-extract: from include/openssl/types.h:186:1 */
typedef struct engine_st ENGINE;

/** clang-extract: from include/openssl/types.h:218:1 */
typedef struct ossl_lib_ctx_st OSSL_LIB_CTX;

#define OPENSSL_malloc(num) \
        CRYPTO_malloc(num, OPENSSL_FILE, OPENSSL_LINE)
#define OPENSSL_clear_free(addr, num) \
        CRYPTO_clear_free(addr, num, OPENSSL_FILE, OPENSSL_LINE)
#define OPENSSL_free(addr) \
        CRYPTO_free(addr, OPENSSL_FILE, OPENSSL_LINE)
/** clang-extract: from include/openssl/crypto.h:344:1 */
OSSL_CRYPTO_ALLOC void *CRYPTO_malloc(size_t num, const char *file, int line);

/** clang-extract: from include/openssl/crypto.h:349:1 */
void CRYPTO_free(void *ptr, const char *file, int line);

/** clang-extract: from include/openssl/crypto.h:350:1 */
void CRYPTO_clear_free(void *ptr, size_t num, const char *file, int line);

/** clang-extract: from include/openssl/asn1.h:186:1 */
struct asn1_string_st {
    int length;
    int type;
    unsigned char *data;
    /*
     * The value of the following field depends on the type being held.  It
     * is mostly being used for BIT_STRING so if the input data has a
     * non-zero 'unused bits' value, it will be handled correctly
     */
    long flags;
};

/** clang-extract: from include/openssl/asn1.h:279:1 */
typedef struct ASN1_VALUE_st ASN1_VALUE;

#define ASN1_ITEM_rptr(ref) (ref##_it())
/** clang-extract: from include/openssl/asn1.h:523:1 */
struct asn1_type_st;/* Full definition was removed.  */

/** clang-extract: from include/openssl/asn1.h:632:1 */
void *ASN1_TYPE_unpack_sequence(const ASN1_ITEM *it, const ASN1_TYPE *t);

#define ERR_LIB_EVP             6
#define ERR_LIB_CMS             46
#define ERR_RFLAGS_OFFSET              18L
#define ERR_RFLAG_FATAL                (0x1 << ERR_RFLAGS_OFFSET)
#define ERR_RFLAG_COMMON               (0x2 << ERR_RFLAGS_OFFSET)
#define ERR_R_EVP_LIB          (ERR_LIB_EVP/* 6 */ | ERR_RFLAG_COMMON)
#define ERR_R_FATAL                             (ERR_RFLAG_FATAL|ERR_RFLAG_COMMON)
#define ERR_R_MALLOC_FAILURE                    (256|ERR_R_FATAL)
/** clang-extract: from include/openssl/err.h:395:1 */
void ERR_new(void);

/** clang-extract: from include/openssl/err.h:396:1 */
void ERR_set_debug(const char *file, int line, const char *func);

/** clang-extract: from include/openssl/err.h:397:1 */
void ERR_set_error(int lib, int reason, const char *fmt, ...);

#define ERR_raise(lib, reason) ERR_raise_data((lib),(reason),NULL)
#define ERR_raise_data                                         \
    (ERR_new(),                                                 \
     ERR_set_debug(OPENSSL_FILE,OPENSSL_LINE,OPENSSL_FUNC),     \
     ERR_set_error)
#define NID_id_alg_PWRI_KEK             893
/** clang-extract: from include/openssl/objects.h:70:1 */
int OBJ_obj2nid(const ASN1_OBJECT *o);

/** clang-extract: from include/openssl/objects.h:72:1 */
int OBJ_obj2txt(char *buf, int buf_len, const ASN1_OBJECT *a, int no_name);

/** clang-extract: from include/openssl/evp.h:605:1 */
EVP_CIPHER *EVP_CIPHER_fetch(OSSL_LIB_CTX *ctx, const char *algorithm,
                             const char *properties);

/** clang-extract: from include/openssl/evp.h:608:1 */
void EVP_CIPHER_free(EVP_CIPHER *cipher);

/** clang-extract: from include/openssl/evp.h:616:1 */
int EVP_CIPHER_CTX_get_block_size(const EVP_CIPHER_CTX *ctx);

/** clang-extract: from include/openssl/evp.h:767:12 */
int EVP_EncryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out,
                                 int *outl, const unsigned char *in, int inl);

/** clang-extract: from include/openssl/evp.h:776:12 */
int EVP_DecryptInit_ex(EVP_CIPHER_CTX *ctx,
                                  const EVP_CIPHER *cipher, ENGINE *impl,
                                  const unsigned char *key,
                                  const unsigned char *iv);

/** clang-extract: from include/openssl/evp.h:784:12 */
int EVP_DecryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out,
                                 int *outl, const unsigned char *in, int inl);

/** clang-extract: from include/openssl/evp.h:794:12 */
int EVP_CipherInit_ex(EVP_CIPHER_CTX *ctx,
                                 const EVP_CIPHER *cipher, ENGINE *impl,
                                 const unsigned char *key,
                                 const unsigned char *iv, int enc);

/** clang-extract: from include/openssl/evp.h:885:1 */
EVP_CIPHER_CTX *EVP_CIPHER_CTX_new(void);

/** clang-extract: from include/openssl/evp.h:887:1 */
void EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *c);

/** clang-extract: from include/openssl/evp.h:889:1 */
int EVP_CIPHER_CTX_set_padding(EVP_CIPHER_CTX *c, int pad);

/** clang-extract: from include/openssl/evp.h:1483:1 */
int EVP_CIPHER_asn1_to_param(EVP_CIPHER_CTX *c, ASN1_TYPE *type);

/** clang-extract: from include/openssl/evp.h:1533:1 */
int EVP_PBE_CipherInit(ASN1_OBJECT *pbe_obj, const char *pass, int passlen,
                       ASN1_TYPE *param, EVP_CIPHER_CTX *ctx, int en_de);

/** clang-extract: from include/openssl/x509.h:179:1 */
struct X509_algor_st {
    ASN1_OBJECT *algorithm;
    ASN1_TYPE *parameter;
};

/** clang-extract: from include/openssl/x509.h:700:1 */
extern void X509_ALGOR_free(X509_ALGOR *a);

/** clang-extract: from include/openssl/x509.h:700:1 */
extern const ASN1_ITEM *X509_ALGOR_it(void);

#define CMS_R_CIPHER_PARAMETER_INITIALISATION_ERROR      102
#define CMS_R_INVALID_KEY_ENCRYPTION_PARAMETER           176
#define CMS_R_NO_PASSWORD                                178
#define CMS_R_UNKNOWN_CIPHER                             148
#define CMS_R_UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM       179
#define CMS_R_UNWRAP_FAILURE                             180
/** clang-extract: from include/openssl/cms.h:34:1 */
typedef struct CMS_ContentInfo_st CMS_ContentInfo;

/** clang-extract: from include/openssl/cms.h:38:1 */
typedef struct CMS_RecipientInfo_st CMS_RecipientInfo;

/** clang-extract: from include/openssl/rand.h:75:1 */
int RAND_bytes_ex(OSSL_LIB_CTX *ctx, unsigned char *buf, size_t num,
                  unsigned int strength);

#define OSSL_MAX_NAME_SIZE           50
/** clang-extract: from crypto/cms/cms_local.h:27:1 */
typedef struct CMS_EncryptedContentInfo_st CMS_EncryptedContentInfo;

/** clang-extract: from crypto/cms/cms_local.h:35:1 */
typedef struct CMS_KeyTransRecipientInfo_st CMS_KeyTransRecipientInfo;

/** clang-extract: from crypto/cms/cms_local.h:38:1 */
typedef struct CMS_KeyAgreeRecipientInfo_st CMS_KeyAgreeRecipientInfo;

/** clang-extract: from crypto/cms/cms_local.h:43:1 */
typedef struct CMS_KEKRecipientInfo_st CMS_KEKRecipientInfo;

/** clang-extract: from crypto/cms/cms_local.h:44:1 */
typedef struct CMS_PasswordRecipientInfo_st CMS_PasswordRecipientInfo;

/** clang-extract: from crypto/cms/cms_local.h:45:1 */
typedef struct CMS_OtherRecipientInfo_st CMS_OtherRecipientInfo;

/** clang-extract: from crypto/cms/cms_local.h:47:1 */
typedef struct CMS_CTX_st CMS_CTX;

/** clang-extract: from crypto/cms/cms_local.h:49:1 */
struct CMS_CTX_st;/* Full definition was removed.  */

/** clang-extract: from crypto/cms/cms_local.h:128:1 */
struct CMS_EncryptedContentInfo_st {
    ASN1_OBJECT *contentType;
    X509_ALGOR *contentEncryptionAlgorithm;
    ASN1_OCTET_STRING *encryptedContent;
    /* Content encryption algorithm, key and tag */
    const EVP_CIPHER *cipher;
    unsigned char *key;
    size_t keylen;
    unsigned char *tag;
    size_t taglen;
    /* Set to 1 if we are debugging decrypt and don't fake keys for MMA */
    int debug;
    /* Set to 1 if we have no cert and need extra safety measures for MMA */
    int havenocert;
};

/** clang-extract: from crypto/cms/cms_local.h:144:1 */
struct CMS_RecipientInfo_st {
    int type;
    union {
        CMS_KeyTransRecipientInfo *ktri;
        CMS_KeyAgreeRecipientInfo *kari;
        CMS_KEKRecipientInfo *kekri;
        CMS_PasswordRecipientInfo *pwri;
        CMS_OtherRecipientInfo *ori;
    } d;
};

/** clang-extract: from crypto/cms/cms_local.h:235:1 */
struct CMS_PasswordRecipientInfo_st {
    int32_t version;
    X509_ALGOR *keyDerivationAlgorithm;
    X509_ALGOR *keyEncryptionAlgorithm;
    ASN1_OCTET_STRING *encryptedKey;
    /* Extra info: password to use */
    unsigned char *pass;
    size_t passlen;
    const CMS_CTX *cms_ctx;
};

/** clang-extract: from crypto/cms/cms_local.h:392:1 */
const CMS_CTX *ossl_cms_get0_cmsctx(const CMS_ContentInfo *cms);

/** clang-extract: from crypto/cms/cms_local.h:393:1 */
OSSL_LIB_CTX *ossl_cms_ctx_get0_libctx(const CMS_CTX *ctx);

/** clang-extract: from crypto/cms/cms_local.h:394:1 */
const char *ossl_cms_ctx_get0_propq(const CMS_CTX *ctx);

/** clang-extract: from crypto/cms/cms_local.h:450:1 */
CMS_EncryptedContentInfo *ossl_cms_get0_env_enc_content(const CMS_ContentInfo *cms);

/** clang-extract: from crypto/cms/cms_local.h:469:1 */
int ossl_cms_RecipientInfo_pwri_crypt(const CMS_ContentInfo *cms,
                                      CMS_RecipientInfo *ri, int en_de);

/** clang-extract: from crypto/cms/cms_pwri.c:189:1 */
static int kek_unwrap_key(unsigned char *out, size_t *outlen,
                          const unsigned char *in, size_t inlen,
                          EVP_CIPHER_CTX *ctx)
{
    size_t blocklen = EVP_CIPHER_CTX_get_block_size(ctx);
    unsigned char *tmp;
    int outl, rv = 0;
    if (inlen < 2 * blocklen) {
        /* too small */
        return 0;
    }
    if (inlen % blocklen) {
        /* Invalid size */
        return 0;
    }
    if ((tmp = OPENSSL_malloc(inlen)) == NULL) {
        ERR_raise(ERR_LIB_CMS, ERR_R_MALLOC_FAILURE);
        return 0;
    }
    /* setup IV by decrypting last two blocks */
    if (!EVP_DecryptUpdate(ctx, tmp + inlen - 2 * blocklen, &outl,
                           in + inlen - 2 * blocklen, blocklen * 2)
        /*
         * Do a decrypt of last decrypted block to set IV to correct value
         * output it to start of buffer so we don't corrupt decrypted block
         * this works because buffer is at least two block lengths long.
         */
        || !EVP_DecryptUpdate(ctx, tmp, &outl,
                              tmp + inlen - blocklen, blocklen)
        /* Can now decrypt first n - 1 blocks */
        || !EVP_DecryptUpdate(ctx, tmp, &outl, in, inlen - blocklen)

        /* Reset IV to original value */
        || !EVP_DecryptInit_ex(ctx, NULL, NULL, NULL, NULL)
        /* Decrypt again */
        || !EVP_DecryptUpdate(ctx, tmp, &outl, tmp, inlen))
        goto err;
    /* Check check bytes */
    if (((tmp[1] ^ tmp[4]) & (tmp[2] ^ tmp[5]) & (tmp[3] ^ tmp[6])) != 0xff) {
        /* Check byte failure */
        goto err;
    }
    if (inlen < 4 + (size_t)tmp[0]) {
        /* Invalid length value */
        goto err;
    }
    *outlen = (size_t)tmp[0];
    memcpy(out, tmp + 4, *outlen);
    rv = 1;
 err:
    OPENSSL_clear_free(tmp, inlen);
    return rv;

}

/** clang-extract: from crypto/cms/cms_pwri.c:244:1 */
static int kek_wrap_key(unsigned char *out, size_t *outlen,
                        const unsigned char *in, size_t inlen,
                        EVP_CIPHER_CTX *ctx, const CMS_CTX *cms_ctx)
{
    size_t blocklen = EVP_CIPHER_CTX_get_block_size(ctx);
    size_t olen;
    int dummy;
    /*
     * First decide length of output buffer: need header and round up to
     * multiple of block length.
     */
    olen = (inlen + 4 + blocklen - 1) / blocklen;
    olen *= blocklen;
    if (olen < 2 * blocklen) {
        /* Key too small */
        return 0;
    }
    if (inlen > 0xFF) {
        /* Key too large */
        return 0;
    }
    if (out) {
        /* Set header */
        out[0] = (unsigned char)inlen;
        out[1] = in[0] ^ 0xFF;
        out[2] = in[1] ^ 0xFF;
        out[3] = in[2] ^ 0xFF;
        memcpy(out + 4, in, inlen);
        /* Add random padding to end */
        if (olen > inlen + 4
            && RAND_bytes_ex(ossl_cms_ctx_get0_libctx(cms_ctx), out + 4 + inlen,
                             olen - 4 - inlen, 0) <= 0)
            return 0;
        /* Encrypt twice */
        if (!EVP_EncryptUpdate(ctx, out, &dummy, out, olen)
            || !EVP_EncryptUpdate(ctx, out, &dummy, out, olen))
            return 0;
    }

    *outlen = olen;

    return 1;
}

/** clang-extract: from crypto/cms/cms_pwri.c:290:1 */
__attribute__((visibility ("default")))
int ossl_cms_RecipientInfo_pwri_crypt_lp(const CMS_ContentInfo *cms,
                                         CMS_RecipientInfo *ri, int en_de)
{
    CMS_EncryptedContentInfo *ec;
    CMS_PasswordRecipientInfo *pwri;
    int r = 0;
    X509_ALGOR *algtmp, *kekalg = NULL;
    EVP_CIPHER_CTX *kekctx = NULL;
    char name[OSSL_MAX_NAME_SIZE];
    EVP_CIPHER *kekcipher;
    unsigned char *key = NULL;
    size_t keylen;
    const CMS_CTX *cms_ctx = ossl_cms_get0_cmsctx(cms);

    ec = ossl_cms_get0_env_enc_content(cms);

    pwri = ri->d.pwri;

    if (pwri->pass == NULL) {
        ERR_raise(ERR_LIB_CMS, CMS_R_NO_PASSWORD);
        return 0;
    }
    algtmp = pwri->keyEncryptionAlgorithm;

    if (!algtmp || OBJ_obj2nid(algtmp->algorithm) != NID_id_alg_PWRI_KEK) {
        ERR_raise(ERR_LIB_CMS, CMS_R_UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM);
        return 0;
    }

    kekalg = ASN1_TYPE_unpack_sequence(ASN1_ITEM_rptr(X509_ALGOR),
                                       algtmp->parameter);

    if (kekalg == NULL) {
        ERR_raise(ERR_LIB_CMS, CMS_R_INVALID_KEY_ENCRYPTION_PARAMETER);
        return 0;
    }

    OBJ_obj2txt(name, sizeof(name), kekalg->algorithm, 0);
    kekcipher = EVP_CIPHER_fetch(ossl_cms_ctx_get0_libctx(cms_ctx), name,
                                 ossl_cms_ctx_get0_propq(cms_ctx));

    if (kekcipher == NULL) {
        ERR_raise(ERR_LIB_CMS, CMS_R_UNKNOWN_CIPHER);
        goto err;
    }

    kekctx = EVP_CIPHER_CTX_new();
    if (kekctx == NULL) {
        ERR_raise(ERR_LIB_CMS, ERR_R_MALLOC_FAILURE);
        goto err;
    }
    /* Fixup cipher based on AlgorithmIdentifier to set IV etc */
    if (!EVP_CipherInit_ex(kekctx, kekcipher, NULL, NULL, NULL, en_de))
        goto err;
    EVP_CIPHER_CTX_set_padding(kekctx, 0);
    if (EVP_CIPHER_asn1_to_param(kekctx, kekalg->parameter) <= 0) {
        ERR_raise(ERR_LIB_CMS, CMS_R_CIPHER_PARAMETER_INITIALISATION_ERROR);
        goto err;
    }

    algtmp = pwri->keyDerivationAlgorithm;

    /* Finish password based key derivation to setup key in "ctx" */

    if (EVP_PBE_CipherInit(algtmp->algorithm,
                           (char *)pwri->pass, pwri->passlen,
                           algtmp->parameter, kekctx, en_de) < 0) {
        ERR_raise(ERR_LIB_CMS, ERR_R_EVP_LIB);
        goto err;
    }

    /* Finally wrap/unwrap the key */

    if (en_de) {

        if (!kek_wrap_key(NULL, &keylen, ec->key, ec->keylen, kekctx, cms_ctx))
            goto err;

        key = OPENSSL_malloc(keylen);

        if (key == NULL)
            goto err;

        if (!kek_wrap_key(key, &keylen, ec->key, ec->keylen, kekctx, cms_ctx))
            goto err;
        pwri->encryptedKey->data = key;
        pwri->encryptedKey->length = keylen;
    } else {
        key = OPENSSL_malloc(pwri->encryptedKey->length);

        if (key == NULL) {
            ERR_raise(ERR_LIB_CMS, ERR_R_MALLOC_FAILURE);
            goto err;
        }
        if (!kek_unwrap_key(key, &keylen,
                            pwri->encryptedKey->data,
                            pwri->encryptedKey->length, kekctx)) {
            ERR_raise(ERR_LIB_CMS, CMS_R_UNWRAP_FAILURE);
            goto err;
        }

        OPENSSL_clear_free(ec->key, ec->keylen);
        ec->key = key;
        ec->keylen = keylen;

    }

    r = 1;

 err:
    EVP_CIPHER_free(kekcipher);
    EVP_CIPHER_CTX_free(kekctx);

    if (!r)
        OPENSSL_free(key);
    X509_ALGOR_free(kekalg);

    return r;

}
#endif
