+
+/*
+ * For TLS 1.2 servers check if we have a certificate which can be used
+ * with the signature algorithm "lu".
+ */
+
+static int tls12_check_cert_sigalg(const SSL *s, const SIGALG_LOOKUP *lu)
+{
+ const SSL_CERT_LOOKUP *clu = ssl_cert_lookup_by_idx(lu->sig_idx);
+
+ /* If not recognised or not supported by cipher mask it is not suitable */
+ if (clu == NULL || !(clu->amask & s->s3->tmp.new_cipher->algorithm_auth))
+ return 0;
+
+ return s->s3->tmp.valid_flags[lu->sig_idx] & CERT_PKEY_VALID ? 1 : 0;
+}
+
+/*
+ * 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)
+{
+ 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, SHA224, DSA and RSA if not PSS */
+ if (lu->hash == NID_sha1
+ || lu->hash == NID_sha224
+ || lu->sig == EVP_PKEY_DSA
+ || lu->sig == EVP_PKEY_RSA)
+ continue;
+ if (!tls1_lookup_md(lu, NULL))
+ continue;
+ if (!ssl_has_cert(s, lu->sig_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[SSL_PKEY_ECC].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 ciphersuite doesn't require a cert nothing to do */
+ if (!(s->s3->tmp.new_cipher->algorithm_auth & SSL_aCERT))
+ return 1;
+ if (!s->server && !ssl_has_cert(s, s->cert->key - s->cert->pkeys))
+ 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[SSL_PKEY_ECC].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];
+
+ if (s->server) {
+ if (!tls12_check_cert_sigalg(s, lu))
+ continue;
+ } else if (lu->sig_idx != s->cert->key - s->cert->pkeys) {
+ continue;
+ }
+#ifndef OPENSSL_NO_EC
+ if (curve == -1 || lu->curve == curve)
+#endif
+ 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, -1)) == 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, -1)) == NULL) {
+ if (al == NULL)
+ return 1;
+ *al = SSL_AD_INTERNAL_ERROR;
+ SSLerr(SSL_F_TLS_CHOOSE_SIGALG, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+ }
+ }
+ s->s3->tmp.cert = &s->cert->pkeys[lu->sig_idx];
+ s->cert->key = s->s3->tmp.cert;
+ s->s3->tmp.sigalg = lu;
+ return 1;
+}