static int sskdf_derive(void *vctx, unsigned char *key, size_t keylen)
{
    KDF_SSKDF *ctx = (KDF_SSKDF *)vctx;
    const EVP_MD *md = ossl_prov_digest_md(&ctx->digest);

    if (ctx->secret == NULL) {
        ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_SECRET);
        return 0;
    }

    if (ctx->mac != NULL) {
        /* H(x) = KMAC or H(x) = HMAC */
        int ret;
        const unsigned char *custom = NULL;
        size_t custom_len = 0;
        const char *macname;
        int default_salt_len;

        /*
         * TODO(3.0) investigate the necessity to have all these controls.
         * Why does KMAC require a salt length that's shorter than the MD
         * block size?
         */
        macname = EVP_MAC_name(ctx->mac);
        if (strcmp(macname, OSSL_MAC_NAME_HMAC) == 0) {
            /* H(x) = HMAC(x, salt, hash) */
            if (md == NULL) {
                ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_MESSAGE_DIGEST);
                return 0;
            }
            default_salt_len = EVP_MD_block_size(md);
            if (default_salt_len <= 0)
                return 0;
        } else if (strcmp(macname, OSSL_MAC_NAME_KMAC128) == 0
                   || strcmp(macname, OSSL_MAC_NAME_KMAC256) == 0) {
            /* H(x) = KMACzzz(x, salt, custom) */
            custom = kmac_custom_str;
            custom_len = sizeof(kmac_custom_str);
            if (strcmp(macname, OSSL_MAC_NAME_KMAC128) == 0)
                default_salt_len = SSKDF_KMAC128_DEFAULT_SALT_SIZE;
            else
                default_salt_len = SSKDF_KMAC256_DEFAULT_SALT_SIZE;
        } else {
            ERR_raise(ERR_LIB_PROV, PROV_R_UNSUPPORTED_MAC_TYPE);
            return 0;
        }
        /* If no salt is set then use a default_salt of zeros */
        if (ctx->salt == NULL || ctx->salt_len <= 0) {
            ctx->salt = OPENSSL_zalloc(default_salt_len);
            if (ctx->salt == NULL) {
                ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE);
                return 0;
            }
            ctx->salt_len = default_salt_len;
        }
        ret = SSKDF_mac_kdm(ctx->mac, md,
                            custom, custom_len, ctx->out_len,
                            ctx->salt, ctx->salt_len,
                            ctx->secret, ctx->secret_len,
                            ctx->info, ctx->info_len, key, keylen);
        return ret;
    } else {
        /* H(x) = hash */
        if (md == NULL) {
            ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_MESSAGE_DIGEST);
            return 0;
        }
        return SSKDF_hash_kdm(md, ctx->secret, ctx->secret_len,
                              ctx->info, ctx->info_len, 0, key, keylen);
    }
}