/*
 * Copyright (C) 2011-2012 Free Software Foundation, Inc.
 * Copyright (C) 2017 Red Hat, Inc.
 *
 * Author: Nikos Mavrogiannopoulos
 *
 * This file is part of GnuTLS.
 *
 * The GnuTLS is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>
 *
 */

#include "gnutls_int.h"
#include "algorithms.h"
#include "errors.h"
#include "x509/common.h"
#include <assert.h>
#include "c-strcase.h"
#include "pk.h"

/* signature algorithms;
 */

#ifdef ALLOW_SHA1
#define SHA1_SECURE_VAL _SECURE
#else
#define SHA1_SECURE_VAL _INSECURE_FOR_CERTS
#endif

static SYSTEM_CONFIG_OR_CONST gnutls_sign_entry_st sign_algorithms[] = {
	/* RSA-PKCS#1 1.5: must be before PSS,
	 * so that gnutls_pk_to_sign() will return
	 * these first for backwards compatibility. */
	{ .name = "RSA-SHA256",
	  .oid = SIG_RSA_SHA256_OID,
	  .id = GNUTLS_SIGN_RSA_SHA256,
	  .pk = GNUTLS_PK_RSA,
	  .hash = GNUTLS_DIG_SHA256,
	  .aid = { { 4, 1 }, SIG_SEM_DEFAULT } },
	{ .name = "RSA-SHA384",
	  .oid = SIG_RSA_SHA384_OID,
	  .id = GNUTLS_SIGN_RSA_SHA384,
	  .pk = GNUTLS_PK_RSA,
	  .hash = GNUTLS_DIG_SHA384,
	  .aid = { { 5, 1 }, SIG_SEM_DEFAULT } },
	{ .name = "RSA-SHA512",
	  .oid = SIG_RSA_SHA512_OID,
	  .id = GNUTLS_SIGN_RSA_SHA512,
	  .pk = GNUTLS_PK_RSA,
	  .hash = GNUTLS_DIG_SHA512,
	  .aid = { { 6, 1 }, SIG_SEM_DEFAULT } },

	/* RSA-PSS */
	{ .name = "RSA-PSS-SHA256",
	  .oid = PK_PKIX1_RSA_PSS_OID,
	  .id = GNUTLS_SIGN_RSA_PSS_SHA256,
	  .pk = GNUTLS_PK_RSA_PSS,
	  .priv_pk =
		  GNUTLS_PK_RSA, /* PKCS#11 doesn't separate RSA from RSA-PSS privkeys */
	  .hash = GNUTLS_DIG_SHA256,
	  .flags = GNUTLS_SIGN_FLAG_TLS13_OK,
	  .aid = { { 8, 9 }, SIG_SEM_DEFAULT } },
	{ .name = "RSA-PSS-RSAE-SHA256",
	  .oid = PK_PKIX1_RSA_PSS_OID,
	  .id = GNUTLS_SIGN_RSA_PSS_RSAE_SHA256,
	  .pk = GNUTLS_PK_RSA_PSS,
	  .cert_pk = GNUTLS_PK_RSA,
	  .priv_pk = GNUTLS_PK_RSA,
	  .hash = GNUTLS_DIG_SHA256,
	  .flags = GNUTLS_SIGN_FLAG_TLS13_OK,
	  .aid = { { 8, 4 }, SIG_SEM_DEFAULT } },
	{ .name = "RSA-PSS-SHA384",
	  .oid = PK_PKIX1_RSA_PSS_OID,
	  .id = GNUTLS_SIGN_RSA_PSS_SHA384,
	  .pk = GNUTLS_PK_RSA_PSS,
	  .priv_pk = GNUTLS_PK_RSA,
	  .hash = GNUTLS_DIG_SHA384,
	  .flags = GNUTLS_SIGN_FLAG_TLS13_OK,
	  .aid = { { 8, 0x0A }, SIG_SEM_DEFAULT } },
	{ .name = "RSA-PSS-RSAE-SHA384",
	  .oid = PK_PKIX1_RSA_PSS_OID,
	  .id = GNUTLS_SIGN_RSA_PSS_RSAE_SHA384,
	  .pk = GNUTLS_PK_RSA_PSS,
	  .cert_pk = GNUTLS_PK_RSA,
	  .priv_pk = GNUTLS_PK_RSA,
	  .hash = GNUTLS_DIG_SHA384,
	  .flags = GNUTLS_SIGN_FLAG_TLS13_OK,
	  .aid = { { 8, 5 }, SIG_SEM_DEFAULT } },
	{ .name = "RSA-PSS-SHA512",
	  .oid = PK_PKIX1_RSA_PSS_OID,
	  .id = GNUTLS_SIGN_RSA_PSS_SHA512,
	  .pk = GNUTLS_PK_RSA_PSS,
	  .priv_pk = GNUTLS_PK_RSA,
	  .hash = GNUTLS_DIG_SHA512,
	  .flags = GNUTLS_SIGN_FLAG_TLS13_OK,
	  .aid = { { 8, 0x0B }, SIG_SEM_DEFAULT } },
	{ .name = "RSA-PSS-RSAE-SHA512",
	  .oid = PK_PKIX1_RSA_PSS_OID,
	  .id = GNUTLS_SIGN_RSA_PSS_RSAE_SHA512,
	  .pk = GNUTLS_PK_RSA_PSS,
	  .cert_pk = GNUTLS_PK_RSA,
	  .priv_pk = GNUTLS_PK_RSA,
	  .hash = GNUTLS_DIG_SHA512,
	  .flags = GNUTLS_SIGN_FLAG_TLS13_OK,
	  .aid = { { 8, 6 }, SIG_SEM_DEFAULT } },

	/* Ed25519: The hash algorithm here is set to be SHA512, although that is
	 * an internal detail of Ed25519; we set it, because CMS/PKCS#7 requires
	 * that mapping. */
	{ .name = "EdDSA-Ed25519",
	  .oid = SIG_EDDSA_SHA512_OID,
	  .id = GNUTLS_SIGN_EDDSA_ED25519,
	  .pk = GNUTLS_PK_EDDSA_ED25519,
	  .hash = GNUTLS_DIG_SHA512,
	  .flags = GNUTLS_SIGN_FLAG_TLS13_OK,
	  .aid = { { 8, 7 }, SIG_SEM_DEFAULT } },

	/* Ed448: The hash algorithm here is set to be SHAKE256, although that is
	 * an internal detail of Ed448; we set it, because CMS/PKCS#7 requires
	 * that mapping. */
	{ .name = "EdDSA-Ed448",
	  .oid = SIG_ED448_OID,
	  .id = GNUTLS_SIGN_EDDSA_ED448,
	  .pk = GNUTLS_PK_EDDSA_ED448,
	  .hash = GNUTLS_DIG_SHAKE_256,
	  .flags = GNUTLS_SIGN_FLAG_TLS13_OK,
	  .aid = { { 8, 8 }, SIG_SEM_DEFAULT },
	  .hash_output_size = 114 },

	/* ECDSA */
	/* The following three signature algorithms
	 * have different semantics when used under TLS 1.2
	 * or TLS 1.3. Under the former they behave as the
	 * as ECDSA signed by SHAXXX by any curve, but under the
	 * latter they are restricted to a single curve.
	 * For this reason the ECDSA-SHAXXX algorithms act
	 * as an alias to them. */
	/* we have intentionally the ECDSA-SHAXXX algorithms first
	 * so that gnutls_pk_to_sign() will return these. */
	{ .name = "ECDSA-SHA256",
	  .oid = "1.2.840.10045.4.3.2",
	  .id = GNUTLS_SIGN_ECDSA_SHA256,
	  .pk = GNUTLS_PK_ECDSA,
	  .hash = GNUTLS_DIG_SHA256,
	  .aid = { { 4, 3 }, SIG_SEM_PRE_TLS12 } },
	{ .name = "ECDSA-SHA384",
	  .oid = "1.2.840.10045.4.3.3",
	  .id = GNUTLS_SIGN_ECDSA_SHA384,
	  .pk = GNUTLS_PK_ECDSA,
	  .hash = GNUTLS_DIG_SHA384,
	  .aid = { { 5, 3 }, SIG_SEM_PRE_TLS12 } },
	{ .name = "ECDSA-SHA512",
	  .oid = "1.2.840.10045.4.3.4",
	  .id = GNUTLS_SIGN_ECDSA_SHA512,
	  .pk = GNUTLS_PK_ECDSA,
	  .hash = GNUTLS_DIG_SHA512,
	  .aid = { { 6, 3 }, SIG_SEM_PRE_TLS12 } },

	{ .name = "ECDSA-SECP256R1-SHA256",
	  .id = GNUTLS_SIGN_ECDSA_SECP256R1_SHA256,
	  .pk = GNUTLS_PK_ECDSA,
	  .curve = GNUTLS_ECC_CURVE_SECP256R1,
	  .hash = GNUTLS_DIG_SHA256,
	  .flags = GNUTLS_SIGN_FLAG_TLS13_OK,
	  .aid = { { 4, 3 }, SIG_SEM_TLS13 } },
	{ .name = "ECDSA-SECP384R1-SHA384",
	  .id = GNUTLS_SIGN_ECDSA_SECP384R1_SHA384,
	  .pk = GNUTLS_PK_ECDSA,
	  .curve = GNUTLS_ECC_CURVE_SECP384R1,
	  .hash = GNUTLS_DIG_SHA384,
	  .flags = GNUTLS_SIGN_FLAG_TLS13_OK,
	  .aid = { { 5, 3 }, SIG_SEM_TLS13 } },
	{ .name = "ECDSA-SECP521R1-SHA512",
	  .id = GNUTLS_SIGN_ECDSA_SECP521R1_SHA512,
	  .pk = GNUTLS_PK_ECDSA,
	  .curve = GNUTLS_ECC_CURVE_SECP521R1,
	  .hash = GNUTLS_DIG_SHA512,
	  .flags = GNUTLS_SIGN_FLAG_TLS13_OK,
	  .aid = { { 6, 3 }, SIG_SEM_TLS13 } },

	/* ECDSA-SHA3 */
	{ .name = "ECDSA-SHA3-224",
	  .oid = SIG_ECDSA_SHA3_224_OID,
	  .id = GNUTLS_SIGN_ECDSA_SHA3_224,
	  .pk = GNUTLS_PK_EC,
	  .hash = GNUTLS_DIG_SHA3_224,
	  .aid = TLS_SIGN_AID_UNKNOWN },
	{ .name = "ECDSA-SHA3-256",
	  .oid = SIG_ECDSA_SHA3_256_OID,
	  .id = GNUTLS_SIGN_ECDSA_SHA3_256,
	  .pk = GNUTLS_PK_EC,
	  .hash = GNUTLS_DIG_SHA3_256,
	  .aid = TLS_SIGN_AID_UNKNOWN },
	{ .name = "ECDSA-SHA3-384",
	  .oid = SIG_ECDSA_SHA3_384_OID,
	  .id = GNUTLS_SIGN_ECDSA_SHA3_384,
	  .pk = GNUTLS_PK_EC,
	  .hash = GNUTLS_DIG_SHA3_384,
	  .aid = TLS_SIGN_AID_UNKNOWN },
	{ .name = "ECDSA-SHA3-512",
	  .oid = SIG_ECDSA_SHA3_512_OID,
	  .id = GNUTLS_SIGN_ECDSA_SHA3_512,
	  .pk = GNUTLS_PK_EC,
	  .hash = GNUTLS_DIG_SHA3_512,
	  .aid = TLS_SIGN_AID_UNKNOWN },
	{ .name = "RSA-SHA3-224",
	  .oid = SIG_RSA_SHA3_224_OID,
	  .id = GNUTLS_SIGN_RSA_SHA3_224,
	  .pk = GNUTLS_PK_RSA,
	  .hash = GNUTLS_DIG_SHA3_224,
	  .aid = TLS_SIGN_AID_UNKNOWN },
	{ .name = "RSA-SHA3-256",
	  .oid = SIG_RSA_SHA3_256_OID,
	  .id = GNUTLS_SIGN_RSA_SHA3_256,
	  .pk = GNUTLS_PK_RSA,
	  .hash = GNUTLS_DIG_SHA3_256,
	  .aid = TLS_SIGN_AID_UNKNOWN },
	{ .name = "RSA-SHA3-384",
	  .oid = SIG_RSA_SHA3_384_OID,
	  .id = GNUTLS_SIGN_RSA_SHA3_384,
	  .pk = GNUTLS_PK_RSA,
	  .hash = GNUTLS_DIG_SHA3_384,
	  .aid = TLS_SIGN_AID_UNKNOWN },
	{ .name = "RSA-SHA3-512",
	  .oid = SIG_RSA_SHA3_512_OID,
	  .id = GNUTLS_SIGN_RSA_SHA3_512,
	  .pk = GNUTLS_PK_RSA,
	  .hash = GNUTLS_DIG_SHA3_512,
	  .aid = TLS_SIGN_AID_UNKNOWN },

	/* DSA-SHA3 */
	{ .name = "DSA-SHA3-224",
	  .oid = SIG_DSA_SHA3_224_OID,
	  .id = GNUTLS_SIGN_DSA_SHA3_224,
	  .pk = GNUTLS_PK_DSA,
	  .hash = GNUTLS_DIG_SHA3_224,
	  .aid = TLS_SIGN_AID_UNKNOWN },
	{ .name = "DSA-SHA3-256",
	  .oid = SIG_DSA_SHA3_256_OID,
	  .id = GNUTLS_SIGN_DSA_SHA3_256,
	  .pk = GNUTLS_PK_DSA,
	  .hash = GNUTLS_DIG_SHA3_256,
	  .aid = TLS_SIGN_AID_UNKNOWN },
	{ .name = "DSA-SHA3-384",
	  .oid = SIG_DSA_SHA3_384_OID,
	  .id = GNUTLS_SIGN_DSA_SHA3_384,
	  .pk = GNUTLS_PK_DSA,
	  .hash = GNUTLS_DIG_SHA3_384,
	  .aid = TLS_SIGN_AID_UNKNOWN },
	{ .name = "DSA-SHA3-512",
	  .oid = SIG_DSA_SHA3_512_OID,
	  .id = GNUTLS_SIGN_DSA_SHA3_512,
	  .pk = GNUTLS_PK_DSA,
	  .hash = GNUTLS_DIG_SHA3_512,
	  .aid = TLS_SIGN_AID_UNKNOWN },

	/* legacy */
	{ .name = "RSA-RAW",
	  .oid = NULL,
	  .id = GNUTLS_SIGN_RSA_RAW,
	  .pk = GNUTLS_PK_RSA,
	  .hash = GNUTLS_DIG_UNKNOWN,
	  .aid = TLS_SIGN_AID_UNKNOWN },
	{ .name = "RSA-SHA1",
	  .oid = SIG_RSA_SHA1_OID,
	  .id = GNUTLS_SIGN_RSA_SHA1,
	  .pk = GNUTLS_PK_RSA,
	  .hash = GNUTLS_DIG_SHA1,
	  .slevel = SHA1_SECURE_VAL,
	  .aid = { { 2, 1 }, SIG_SEM_DEFAULT } },
	{ .name = "RSA-SHA1",
	  .oid = ISO_SIG_RSA_SHA1_OID,
	  .id = GNUTLS_SIGN_RSA_SHA1,
	  .pk = GNUTLS_PK_RSA,
	  .slevel = SHA1_SECURE_VAL,
	  .hash = GNUTLS_DIG_SHA1,
	  .aid = { { 2, 1 }, SIG_SEM_DEFAULT } },
	{ .name = "RSA-SHA224",
	  .oid = SIG_RSA_SHA224_OID,
	  .id = GNUTLS_SIGN_RSA_SHA224,
	  .pk = GNUTLS_PK_RSA,
	  .hash = GNUTLS_DIG_SHA224,
	  .aid = TLS_SIGN_AID_UNKNOWN },
	{ .name = "RSA-RMD160",
	  .oid = SIG_RSA_RMD160_OID,
	  .id = GNUTLS_SIGN_RSA_RMD160,
	  .pk = GNUTLS_PK_RSA,
	  .hash = GNUTLS_DIG_RMD160,
	  .slevel = _INSECURE_FOR_CERTS,
	  .aid = TLS_SIGN_AID_UNKNOWN },
	{ .name = "DSA-SHA1",
	  .oid = SIG_DSA_SHA1_OID,
	  .id = GNUTLS_SIGN_DSA_SHA1,
	  .pk = GNUTLS_PK_DSA,
	  .slevel = SHA1_SECURE_VAL,
	  .hash = GNUTLS_DIG_SHA1,
	  .aid = { { 2, 2 }, SIG_SEM_PRE_TLS12 } },
	{ .name = "DSA-SHA1",
	  .oid = "1.3.14.3.2.27",
	  .id = GNUTLS_SIGN_DSA_SHA1,
	  .pk = GNUTLS_PK_DSA,
	  .hash = GNUTLS_DIG_SHA1,
	  .slevel = SHA1_SECURE_VAL,
	  .aid = { { 2, 2 }, SIG_SEM_PRE_TLS12 } },
	{ .name = "DSA-SHA224",
	  .oid = SIG_DSA_SHA224_OID,
	  .id = GNUTLS_SIGN_DSA_SHA224,
	  .pk = GNUTLS_PK_DSA,
	  .hash = GNUTLS_DIG_SHA224,
	  .aid = TLS_SIGN_AID_UNKNOWN },
	{ .name = "DSA-SHA256",
	  .oid = SIG_DSA_SHA256_OID,
	  .id = GNUTLS_SIGN_DSA_SHA256,
	  .pk = GNUTLS_PK_DSA,
	  .hash = GNUTLS_DIG_SHA256,
	  .aid = TLS_SIGN_AID_UNKNOWN },
	{ .name = "RSA-MD5",
	  .oid = SIG_RSA_MD5_OID,
	  .id = GNUTLS_SIGN_RSA_MD5,
	  .pk = GNUTLS_PK_RSA,
	  .hash = GNUTLS_DIG_MD5,
	  .slevel = _INSECURE,
	  .aid = TLS_SIGN_AID_UNKNOWN },
	{ .name = "RSA-MD5",
	  .oid = "1.3.14.3.2.25",
	  .id = GNUTLS_SIGN_RSA_MD5,
	  .pk = GNUTLS_PK_RSA,
	  .hash = GNUTLS_DIG_MD5,
	  .slevel = _INSECURE,
	  .aid = TLS_SIGN_AID_UNKNOWN },
	{ .name = "RSA-MD2",
	  .oid = SIG_RSA_MD2_OID,
	  .id = GNUTLS_SIGN_RSA_MD2,
	  .pk = GNUTLS_PK_RSA,
	  .hash = GNUTLS_DIG_MD2,
	  .slevel = _INSECURE,
	  .aid = TLS_SIGN_AID_UNKNOWN },
	{ .name = "ECDSA-SHA1",
	  .oid = "1.2.840.10045.4.1",
	  .id = GNUTLS_SIGN_ECDSA_SHA1,
	  .pk = GNUTLS_PK_EC,
	  .slevel = SHA1_SECURE_VAL,
	  .hash = GNUTLS_DIG_SHA1,
	  .aid = { { 2, 3 }, SIG_SEM_DEFAULT } },
	{ .name = "ECDSA-SHA224",
	  .oid = "1.2.840.10045.4.3.1",
	  .id = GNUTLS_SIGN_ECDSA_SHA224,
	  .pk = GNUTLS_PK_EC,
	  .hash = GNUTLS_DIG_SHA224,
	  .aid = TLS_SIGN_AID_UNKNOWN },
	/* GOST R 34.10-2012-512 */
	{ .name = "GOSTR341012-512",
	  .oid = SIG_GOST_R3410_2012_512_OID,
	  .id = GNUTLS_SIGN_GOST_512,
	  .pk = GNUTLS_PK_GOST_12_512,
	  .hash = GNUTLS_DIG_STREEBOG_512,
	  .flags = GNUTLS_SIGN_FLAG_CRT_VRFY_REVERSE,
	  .aid = { { 8, 65 }, SIG_SEM_PRE_TLS12 } },
	/* GOST R 34.10-2012-256 */
	{ .name = "GOSTR341012-256",
	  .oid = SIG_GOST_R3410_2012_256_OID,
	  .id = GNUTLS_SIGN_GOST_256,
	  .pk = GNUTLS_PK_GOST_12_256,
	  .hash = GNUTLS_DIG_STREEBOG_256,
	  .flags = GNUTLS_SIGN_FLAG_CRT_VRFY_REVERSE,
	  .aid = { { 8, 64 }, SIG_SEM_PRE_TLS12 } },
	/* GOST R 34.10-2001 */
	{ .name = "GOSTR341001",
	  .oid = SIG_GOST_R3410_2001_OID,
	  .id = GNUTLS_SIGN_GOST_94,
	  .pk = GNUTLS_PK_GOST_01,
	  .hash = GNUTLS_DIG_GOSTR_94,
	  .flags = GNUTLS_SIGN_FLAG_CRT_VRFY_REVERSE,
	  .aid = TLS_SIGN_AID_UNKNOWN },
	/* GOST R 34.10-94 */
	{ .name = "GOSTR341094",
	  .oid = SIG_GOST_R3410_94_OID,
	  .id = 0,
	  .pk = 0,
	  .hash = 0,
	  .aid = TLS_SIGN_AID_UNKNOWN },
	{ .name = "DSA-SHA384",
	  .oid = SIG_DSA_SHA384_OID,
	  .id = GNUTLS_SIGN_DSA_SHA384,
	  .pk = GNUTLS_PK_DSA,
	  .hash = GNUTLS_DIG_SHA384,
	  .aid = TLS_SIGN_AID_UNKNOWN },
	{ .name = "DSA-SHA512",
	  .oid = SIG_DSA_SHA512_OID,
	  .id = GNUTLS_SIGN_DSA_SHA512,
	  .pk = GNUTLS_PK_DSA,
	  .hash = GNUTLS_DIG_SHA512,
	  .aid = TLS_SIGN_AID_UNKNOWN },

	{ .name = 0,
	  .oid = 0,
	  .id = 0,
	  .pk = 0,
	  .hash = 0,
	  .aid = TLS_SIGN_AID_UNKNOWN }
};

#define GNUTLS_SIGN_LOOP(b)                                       \
	do {                                                      \
		const gnutls_sign_entry_st *p;                    \
		for (p = sign_algorithms; p->name != NULL; p++) { \
			b;                                        \
		}                                                 \
	} while (0)

#define GNUTLS_SIGN_ALG_LOOP(a)                        \
	GNUTLS_SIGN_LOOP(if (p->id && p->id == sign) { \
		a;                                     \
		break;                                 \
	})

/**
 * gnutls_sign_get_name:
 * @algorithm: is a sign algorithm
 *
 * Convert a #gnutls_sign_algorithm_t value to a string.
 *
 * Returns: a string that contains the name of the specified sign
 *   algorithm, or %NULL.
 **/
const char *gnutls_sign_get_name(gnutls_sign_algorithm_t algorithm)
{
	gnutls_sign_algorithm_t sign = algorithm;
	const char *ret = NULL;

	/* avoid prefix */
	GNUTLS_SIGN_ALG_LOOP(ret = p->name);

	return ret;
}

/**
 * gnutls_sign_is_secure:
 * @algorithm: is a sign algorithm
 *
 * Returns: Non-zero if the provided signature algorithm is considered to be secure.
 **/
unsigned gnutls_sign_is_secure(gnutls_sign_algorithm_t algorithm)
{
	return gnutls_sign_is_secure2(algorithm, 0);
}

bool _gnutls_sign_is_secure2(const gnutls_sign_entry_st *se, unsigned int flags)
{
	if (se->hash != GNUTLS_DIG_UNKNOWN &&
	    _gnutls_digest_is_insecure2(
		    se->hash,
		    flags & GNUTLS_SIGN_FLAG_ALLOW_INSECURE_REVERTIBLE ?
			    GNUTLS_MAC_FLAG_ALLOW_INSECURE_REVERTIBLE :
			    0)) {
		return gnutls_assert_val(false);
	}

	return (flags & GNUTLS_SIGN_FLAG_SECURE_FOR_CERTS ?
			se->slevel == _SECURE :
			(se->slevel == _SECURE ||
			 se->slevel == _INSECURE_FOR_CERTS)) ||
	       (flags & GNUTLS_SIGN_FLAG_ALLOW_INSECURE_REVERTIBLE &&
		se->flags & GNUTLS_SIGN_FLAG_INSECURE_REVERTIBLE);
}

/* This is only called by cfg_apply in priority.c, in blocklisting mode. */
int _gnutls_sign_mark_insecure(gnutls_sign_algorithm_t sign,
			       hash_security_level_t level)
{
#ifndef DISABLE_SYSTEM_CONFIG
	gnutls_sign_entry_st *p;

	if (unlikely(level == _SECURE))
		return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);

	for (p = sign_algorithms; p->name != NULL; p++) {
		if (p->id && p->id == sign) {
			if (p->slevel < level)
				p->slevel = level;
			return 0;
		}
	}
#endif
	return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
}

/* This is only called by cfg_apply in priority.c, in allowlisting mode. */
void _gnutls_sign_mark_insecure_all(hash_security_level_t level)
{
#ifndef DISABLE_SYSTEM_CONFIG
	gnutls_sign_entry_st *p;

	for (p = sign_algorithms; p->name != NULL; p++) {
		if (p->slevel < level)
			p->slevel = level;
		p->flags |= GNUTLS_SIGN_FLAG_INSECURE_REVERTIBLE;
	}
#endif
}

int _gnutls_sign_set_secure(gnutls_sign_algorithm_t sign,
			    hash_security_level_t slevel)
{
#ifndef DISABLE_SYSTEM_CONFIG
	gnutls_sign_entry_st *p;

	for (p = sign_algorithms; p->name != NULL; p++) {
		if (p->id && p->id == sign) {
			if (!(p->flags &
			      GNUTLS_SIGN_FLAG_INSECURE_REVERTIBLE)) {
				return gnutls_assert_val(
					GNUTLS_E_INVALID_REQUEST);
			}
			p->slevel = slevel;
			return 0;
		}
	}
#endif
	return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
}

/**
 * gnutls_sign_is_secure2:
 * @algorithm: is a sign algorithm
 * @flags: zero or %GNUTLS_SIGN_FLAG_SECURE_FOR_CERTS
 *
 * Returns: Non-zero if the provided signature algorithm is considered to be secure.
 **/
unsigned gnutls_sign_is_secure2(gnutls_sign_algorithm_t algorithm,
				unsigned int flags)
{
	const gnutls_sign_entry_st *se;

	se = _gnutls_sign_to_entry(algorithm);
	if (se == NULL)
		return 0;

	return _gnutls_sign_is_secure2(se, flags);
}

/**
 * gnutls_sign_list:
 *
 * Get a list of supported public key signature algorithms.
 * This function is not thread safe.
 *
 * Returns: a (0)-terminated list of #gnutls_sign_algorithm_t
 *   integers indicating the available ciphers.
 *
 **/
const gnutls_sign_algorithm_t *gnutls_sign_list(void)
{
	static gnutls_sign_algorithm_t supported_sign[MAX_ALGOS + 1] = { 0 };

	if (supported_sign[0] == 0) {
		int i = 0;

		GNUTLS_SIGN_LOOP(
			/* list all algorithms, but not duplicates */
			if (supported_sign[i] != p->id &&
			    _gnutls_pk_sign_exists(p->id)) {
				assert(i + 1 < MAX_ALGOS);
				supported_sign[i++] = p->id;
				supported_sign[i + 1] = 0;
			});
	}

	return supported_sign;
}

/**
 * gnutls_sign_get_id:
 * @name: is a sign algorithm name
 *
 * The names are compared in a case insensitive way.
 *
 * Returns: return a #gnutls_sign_algorithm_t value corresponding to
 *   the specified algorithm, or %GNUTLS_SIGN_UNKNOWN on error.
 **/
gnutls_sign_algorithm_t gnutls_sign_get_id(const char *name)
{
	gnutls_sign_algorithm_t ret = GNUTLS_SIGN_UNKNOWN;

	GNUTLS_SIGN_LOOP(if (c_strcasecmp(p->name, name) == 0) {
		ret = p->id;
		break;
	});

	return ret;
}

const gnutls_sign_entry_st *_gnutls_oid_to_sign_entry(const char *oid)
{
	GNUTLS_SIGN_LOOP(if (p->oid && strcmp(oid, p->oid) == 0) { return p; });
	return NULL;
}

/**
 * gnutls_oid_to_sign:
 * @oid: is an object identifier
 *
 * Converts a textual object identifier to a #gnutls_sign_algorithm_t value.
 *
 * Returns: a #gnutls_sign_algorithm_t id of the specified digest
 *   algorithm, or %GNUTLS_SIGN_UNKNOWN on failure.
 *
 * Since: 3.4.3
 **/
gnutls_sign_algorithm_t gnutls_oid_to_sign(const char *oid)
{
	const gnutls_sign_entry_st *se;

	se = _gnutls_oid_to_sign_entry(oid);
	if (se == NULL) {
		_gnutls_debug_log("Unknown SIGN OID: '%s'\n", oid);
		return GNUTLS_SIGN_UNKNOWN;
	}
	return se->id;
}

const gnutls_sign_entry_st *
_gnutls_pk_to_sign_entry(gnutls_pk_algorithm_t pk,
			 gnutls_digest_algorithm_t hash)
{
	GNUTLS_SIGN_LOOP(if (pk == p->pk && hash == p->hash) { return p; });

	return NULL;
}

/**
 * gnutls_pk_to_sign:
 * @pk: is a public key algorithm
 * @hash: a hash algorithm
 *
 * This function maps public key and hash algorithms combinations
 * to signature algorithms.
 *
 * Returns: return a #gnutls_sign_algorithm_t value, or %GNUTLS_SIGN_UNKNOWN on error.
 **/
gnutls_sign_algorithm_t gnutls_pk_to_sign(gnutls_pk_algorithm_t pk,
					  gnutls_digest_algorithm_t hash)
{
	const gnutls_sign_entry_st *e;

	e = _gnutls_pk_to_sign_entry(pk, hash);
	if (e == NULL)
		return GNUTLS_SIGN_UNKNOWN;
	return e->id;
}

/**
 * gnutls_sign_get_oid:
 * @sign: is a sign algorithm
 *
 * Convert a #gnutls_sign_algorithm_t value to its object identifier.
 *
 * Returns: a string that contains the object identifier of the specified sign
 *   algorithm, or %NULL.
 *
 * Since: 3.4.3
 **/
const char *gnutls_sign_get_oid(gnutls_sign_algorithm_t sign)
{
	const char *ret = NULL;

	GNUTLS_SIGN_ALG_LOOP(ret = p->oid);

	return ret;
}

/**
 * gnutls_sign_get_hash_algorithm:
 * @sign: is a signature algorithm
 *
 * This function returns the digest algorithm corresponding to
 * the given signature algorithms.
 *
 * Since: 3.1.1
 *
 * Returns: return a #gnutls_digest_algorithm_t value, or %GNUTLS_DIG_UNKNOWN on error.
 **/
gnutls_digest_algorithm_t
gnutls_sign_get_hash_algorithm(gnutls_sign_algorithm_t sign)
{
	gnutls_digest_algorithm_t ret = GNUTLS_DIG_UNKNOWN;

	GNUTLS_SIGN_ALG_LOOP(ret = p->hash);

	return ret;
}

/**
 * gnutls_sign_get_pk_algorithm:
 * @sign: is a signature algorithm
 *
 * This function returns the public key algorithm corresponding to
 * the given signature algorithms. Note that there may be multiple
 * public key algorithms supporting a particular signature type;
 * when dealing with such algorithms use instead gnutls_sign_supports_pk_algorithm().
 *
 * Since: 3.1.1
 *
 * Returns: return a #gnutls_pk_algorithm_t value, or %GNUTLS_PK_UNKNOWN on error.
 **/
gnutls_pk_algorithm_t gnutls_sign_get_pk_algorithm(gnutls_sign_algorithm_t sign)
{
	gnutls_pk_algorithm_t ret = GNUTLS_PK_UNKNOWN;

	GNUTLS_SIGN_ALG_LOOP(ret = p->pk);

	return ret;
}

/**
 * gnutls_sign_supports_pk_algorithm:
 * @sign: is a signature algorithm
 * @pk: is a public key algorithm
 *
 * This function returns non-zero if the public key algorithm corresponds to
 * the given signature algorithm. That is, if that signature can be generated
 * from the given private key algorithm.
 *
 * Since: 3.6.0
 *
 * Returns: return non-zero when the provided algorithms are compatible.
 **/
unsigned gnutls_sign_supports_pk_algorithm(gnutls_sign_algorithm_t sign,
					   gnutls_pk_algorithm_t pk)
{
	const gnutls_sign_entry_st *p;
	unsigned r;

	for (p = sign_algorithms; p->name != NULL; p++) {
		if (p->id && p->id == sign) {
			r = sign_supports_priv_pk_algorithm(p, pk);
			if (r != 0)
				return r;
		}
	}

	return 0;
}

gnutls_sign_algorithm_t _gnutls_tls_aid_to_sign(uint8_t id0, uint8_t id1,
						const version_entry_st *ver)
{
	gnutls_sign_algorithm_t ret = GNUTLS_SIGN_UNKNOWN;

	if (id0 == 255 && id1 == 255)
		return ret;

	GNUTLS_SIGN_LOOP(if (p->aid.id[0] == id0 && p->aid.id[1] == id1 &&
			     ((p->aid.tls_sem & ver->tls_sig_sem) != 0)) {
		ret = p->id;
		break;
	});

	return ret;
}

/* Returns NULL if a valid AID is not found
 */
const sign_algorithm_st *_gnutls_sign_to_tls_aid(gnutls_sign_algorithm_t sign)
{
	const sign_algorithm_st *ret = NULL;

	GNUTLS_SIGN_ALG_LOOP(ret = &p->aid);

	if (ret != NULL && HAVE_UNKNOWN_SIGAID(ret))
		return NULL;

	return ret;
}

const gnutls_sign_entry_st *_gnutls_sign_to_entry(gnutls_sign_algorithm_t sign)
{
	const gnutls_sign_entry_st *ret = NULL;

	GNUTLS_SIGN_ALG_LOOP(ret = p);

	return ret;
}

const gnutls_sign_entry_st *
_gnutls_tls_aid_to_sign_entry(uint8_t id0, uint8_t id1,
			      const version_entry_st *ver)
{
	if (id0 == 255 && id1 == 255)
		return NULL;

	GNUTLS_SIGN_LOOP(
		if (p->aid.id[0] == id0 && p->aid.id[1] == id1 &&
		    ((p->aid.tls_sem & ver->tls_sig_sem) != 0)) { return p; });

	return NULL;
}

const gnutls_sign_entry_st *
_gnutls13_sign_get_compatible_with_privkey(gnutls_privkey_t privkey)
{
	GNUTLS_SIGN_LOOP(if ((p->flags & GNUTLS_SIGN_FLAG_TLS13_OK) &&
			     _gnutls_privkey_compatible_with_sig(
				     privkey, p->id)) { return p; });

	return NULL;
}

unsigned _gnutls_sign_get_hash_strength(gnutls_sign_algorithm_t sign)
{
	const gnutls_sign_entry_st *se = _gnutls_sign_to_entry(sign);
	const mac_entry_st *me;
	unsigned hash_output_size;

	if (unlikely(se == NULL))
		return 0;

	me = hash_to_entry(se->hash);
	if (unlikely(me == NULL))
		return 0;

	if (se->hash_output_size > 0)
		hash_output_size = se->hash_output_size;
	else
		hash_output_size = _gnutls_mac_get_algo_len(me);

	if (me->id == GNUTLS_MAC_SHAKE_128)
		return MIN(hash_output_size * 8 / 2, 128);
	else if (me->id == GNUTLS_MAC_SHAKE_256)
		return MIN(hash_output_size * 8 / 2, 256);

	return hash_output_size * 8 / 2;
}
