+
+/*
+ * Choose an appropriate signature algorithm based on available certificates
+ * Sets chosen certificate and signature algorithm.
+ *
+ * For servers if we fail to find a required certificate it is a fatal error
+ * and an appropriate error code is set and the TLS alert set in *al.
+ *
+ * For clients al is set to NULL. If a certificate is not suitable it is not
+ * a fatal error: we will either try another certificate or not present one
+ * to the server. In this case no error is set.
+ */
+int tls_choose_sigalg(SSL *s, int *al)
+{
+ int idx = -1;
+ const SIGALG_LOOKUP *lu = NULL;
+
+ s->s3->tmp.cert = NULL;
+ s->s3->tmp.sigalg = NULL;
+
+ if (SSL_IS_TLS13(s)) {
+ size_t i;
+#ifndef OPENSSL_NO_EC
+ int curve = -1, skip_ec = 0;
+#endif
+
+ /* Look for a certificate matching shared sigalgs */
+ for (i = 0; i < s->cert->shared_sigalgslen; i++) {
+ lu = s->cert->shared_sigalgs[i];
+
+ /* Skip SHA1, DSA and RSA if not PSS */
+ if (lu->hash == NID_sha1 || lu->sig == EVP_PKEY_DSA
+ || lu->sig == EVP_PKEY_RSA)
+ continue;
+ if (ssl_md(lu->hash_idx) == NULL)
+ continue;
+ idx = lu->sig_idx;
+ if (!ssl_has_cert(s, idx))
+ continue;
+ if (lu->sig == EVP_PKEY_EC) {
+#ifndef OPENSSL_NO_EC
+ if (curve == -1) {
+ EC_KEY *ec = EVP_PKEY_get0_EC_KEY(s->cert->pkeys[idx].privatekey);
+
+ curve = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec));
+ if (EC_KEY_get_conv_form(ec)
+ != POINT_CONVERSION_UNCOMPRESSED)
+ skip_ec = 1;
+ }
+ if (skip_ec || (lu->curve != NID_undef && curve != lu->curve))
+ continue;
+#else
+ continue;
+#endif
+ }
+ break;
+ }
+ if (i == s->cert->shared_sigalgslen) {
+ if (al == NULL)
+ return 1;
+ *al = SSL_AD_HANDSHAKE_FAILURE;
+ SSLerr(SSL_F_TLS_CHOOSE_SIGALG,
+ SSL_R_NO_SUITABLE_SIGNATURE_ALGORITHM);
+ return 0;
+ }
+ } else {
+ if (s->server) {
+ /* Find index corresponding to ciphersuite */
+ idx = ssl_cipher_get_cert_index(s->s3->tmp.new_cipher);
+ /* If no certificate for ciphersuite return */
+ if (idx == -1)
+ return 1;
+ if (idx == SSL_PKEY_GOST_EC) {
+ /* Work out which GOST certificate is available */
+ if (ssl_has_cert(s, SSL_PKEY_GOST12_512)) {
+ idx = SSL_PKEY_GOST12_512;
+ } else if (ssl_has_cert(s, SSL_PKEY_GOST12_256)) {
+ idx = SSL_PKEY_GOST12_256;
+ } else if (ssl_has_cert(s, SSL_PKEY_GOST01)) {
+ idx = SSL_PKEY_GOST01;
+ } else {
+ if (al == NULL)
+ return 1;
+ *al = SSL_AD_INTERNAL_ERROR;
+ SSLerr(SSL_F_TLS_CHOOSE_SIGALG, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+ } else if (!ssl_has_cert(s, idx)) {
+ if (al == NULL)
+ return 1;
+ *al = SSL_AD_INTERNAL_ERROR;
+ SSLerr(SSL_F_TLS_CHOOSE_SIGALG, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+ } else {
+ /* Find index for client certificate */
+ idx = s->cert->key - s->cert->pkeys;
+ if (!ssl_has_cert(s, idx))
+ return 1;
+ }
+
+ if (SSL_USE_SIGALGS(s)) {
+ if (s->s3->tmp.peer_sigalgs != NULL) {
+ size_t i;
+#ifndef OPENSSL_NO_EC
+ int curve;
+
+ /* For Suite B need to match signature algorithm to curve */
+ if (tls1_suiteb(s)) {
+ EC_KEY *ec = EVP_PKEY_get0_EC_KEY(s->cert->pkeys[idx].privatekey);
+ curve = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec));
+ } else {
+ curve = -1;
+ }
+#endif
+
+ /*
+ * Find highest preference signature algorithm matching
+ * cert type
+ */
+ for (i = 0; i < s->cert->shared_sigalgslen; i++) {
+ lu = s->cert->shared_sigalgs[i];
+#ifdef OPENSSL_NO_EC
+ if (lu->sig_idx == idx)
+ break;
+#else
+ if (lu->sig_idx == idx
+ && (curve == -1 || lu->curve == curve))
+ break;
+#endif
+ if (idx == SSL_PKEY_RSA && lu->sig == EVP_PKEY_RSA_PSS)
+ break;
+ }
+ if (i == s->cert->shared_sigalgslen) {
+ if (al == NULL)
+ return 1;
+ *al = SSL_AD_INTERNAL_ERROR;
+ SSLerr(SSL_F_TLS_CHOOSE_SIGALG, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+ } else {
+ /*
+ * If we have no sigalg use defaults
+ */
+ const uint16_t *sent_sigs;
+ size_t sent_sigslen, i;
+
+ if ((lu = tls1_get_legacy_sigalg(s, idx)) == NULL) {
+ if (al == NULL)
+ return 1;
+ *al = SSL_AD_INTERNAL_ERROR;
+ SSLerr(SSL_F_TLS_CHOOSE_SIGALG, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+
+ /* Check signature matches a type we sent */
+ sent_sigslen = tls12_get_psigalgs(s, 1, &sent_sigs);
+ for (i = 0; i < sent_sigslen; i++, sent_sigs++) {
+ if (lu->sigalg == *sent_sigs)
+ break;
+ }
+ if (i == sent_sigslen) {
+ if (al == NULL)
+ return 1;
+ SSLerr(SSL_F_TLS_CHOOSE_SIGALG, SSL_R_WRONG_SIGNATURE_TYPE);
+ *al = SSL_AD_ILLEGAL_PARAMETER;
+ return 0;
+ }
+ }
+ } else {
+ if ((lu = tls1_get_legacy_sigalg(s, idx)) == NULL) {
+ if (al == NULL)
+ return 1;
+ *al = SSL_AD_INTERNAL_ERROR;
+ SSLerr(SSL_F_TLS_CHOOSE_SIGALG, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+ }
+ }
+ if (idx == -1) {
+ if (al != NULL) {
+ *al = SSL_AD_INTERNAL_ERROR;
+ SSLerr(SSL_F_TLS_CHOOSE_SIGALG, ERR_R_INTERNAL_ERROR);
+ }
+ return 0;
+ }
+ s->s3->tmp.cert = &s->cert->pkeys[idx];
+ s->cert->key = s->s3->tmp.cert;
+ s->s3->tmp.sigalg = lu;
+ return 1;
+}