+
+/*
+ * For TLS 1.2 servers check if we have a certificate which can be used
+ * with the signature algorithm "lu" and return index of certificate.
+ */
+
+static int tls12_get_cert_sigalg_idx(const SSL *s, const SIGALG_LOOKUP *lu)
+{
+ int sig_idx = lu->sig_idx;
+ const SSL_CERT_LOOKUP *clu = ssl_cert_lookup_by_idx(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) == 0
+ || (clu->nid == EVP_PKEY_RSA_PSS
+ && (s->s3.tmp.new_cipher->algorithm_mkey & SSL_kRSA) != 0))
+ return -1;
+
+ return s->s3.tmp.valid_flags[sig_idx] & CERT_PKEY_VALID ? sig_idx : -1;
+}
+
+/*
+ * Checks the given cert against signature_algorithm_cert restrictions sent by
+ * the peer (if any) as well as whether the hash from the sigalg is usable with
+ * the key.
+ * Returns true if the cert is usable and false otherwise.
+ */
+static int check_cert_usable(SSL *s, const SIGALG_LOOKUP *sig, X509 *x,
+ EVP_PKEY *pkey)
+{
+ const SIGALG_LOOKUP *lu;
+ int mdnid, pknid, supported;
+ size_t i;
+
+ /*
+ * If the given EVP_PKEY cannot supporting signing with this sigalg,
+ * the answer is simply 'no'.
+ */
+ ERR_set_mark();
+ supported = EVP_PKEY_supports_digest_nid(pkey, sig->hash);
+ ERR_pop_to_mark();
+ if (supported == 0)
+ return 0;
+
+ /*
+ * The TLS 1.3 signature_algorithms_cert extension places restrictions
+ * on the sigalg with which the certificate was signed (by its issuer).
+ */
+ if (s->s3.tmp.peer_cert_sigalgs != NULL) {
+ if (!X509_get_signature_info(x, &mdnid, &pknid, NULL, NULL))
+ return 0;
+ for (i = 0; i < s->s3.tmp.peer_cert_sigalgslen; i++) {
+ lu = tls1_lookup_sigalg(s->s3.tmp.peer_cert_sigalgs[i]);
+ if (lu == NULL)
+ continue;
+
+ /*
+ * TODO this does not differentiate between the
+ * rsa_pss_pss_* and rsa_pss_rsae_* schemes since we do not
+ * have a chain here that lets us look at the key OID in the
+ * signing certificate.
+ */
+ if (mdnid == lu->hash && pknid == lu->sig)
+ return 1;
+ }
+ return 0;
+ }
+
+ /*
+ * Without signat_algorithms_cert, any certificate for which we have
+ * a viable public key is permitted.
+ */
+ return 1;
+}
+
+/*
+ * Returns true if |s| has a usable certificate configured for use
+ * with signature scheme |sig|.
+ * "Usable" includes a check for presence as well as applying
+ * the signature_algorithm_cert restrictions sent by the peer (if any).
+ * Returns false if no usable certificate is found.
+ */
+static int has_usable_cert(SSL *s, const SIGALG_LOOKUP *sig, int idx)
+{
+ /* TLS 1.2 callers can override sig->sig_idx, but not TLS 1.3 callers. */
+ if (idx == -1)
+ idx = sig->sig_idx;
+ if (!ssl_has_cert(s, idx))
+ return 0;
+
+ return check_cert_usable(s, sig, s->cert->pkeys[idx].x509,
+ s->cert->pkeys[idx].privatekey);
+}
+
+/*
+ * Returns true if the supplied cert |x| and key |pkey| is usable with the
+ * specified signature scheme |sig|, or false otherwise.
+ */
+static int is_cert_usable(SSL *s, const SIGALG_LOOKUP *sig, X509 *x,
+ EVP_PKEY *pkey)
+{
+ size_t idx;
+
+ if (ssl_cert_lookup_by_pkey(pkey, &idx) == NULL)
+ return 0;
+
+ /* Check the key is consistent with the sig alg */
+ if ((int)idx != sig->sig_idx)
+ return 0;
+
+ return check_cert_usable(s, sig, x, pkey);
+}
+
+/*
+ * Find a signature scheme that works with the supplied certificate |x| and key
+ * |pkey|. |x| and |pkey| may be NULL in which case we additionally look at our
+ * available certs/keys to find one that works.
+ */
+static const SIGALG_LOOKUP *find_sig_alg(SSL *s, X509 *x, EVP_PKEY *pkey)
+{
+ const SIGALG_LOOKUP *lu = NULL;
+ size_t i;
+#ifndef OPENSSL_NO_EC
+ int curve = -1;
+#endif
+ EVP_PKEY *tmppkey;
+
+ /* Look for a shared sigalgs matching possible certificates */
+ for (i = 0; i < s->shared_sigalgslen; i++) {
+ lu = s->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;
+ /* Check that we have a cert, and signature_algorithms_cert */
+ if (!tls1_lookup_md(s->ctx, lu, NULL))
+ continue;
+ if ((pkey == NULL && !has_usable_cert(s, lu, -1))
+ || (pkey != NULL && !is_cert_usable(s, lu, x, pkey)))
+ continue;
+
+ tmppkey = (pkey != NULL) ? pkey
+ : s->cert->pkeys[lu->sig_idx].privatekey;
+
+ if (lu->sig == EVP_PKEY_EC) {
+#ifndef OPENSSL_NO_EC
+ if (curve == -1)
+ curve = evp_pkey_get_EC_KEY_curve_nid(tmppkey);
+ if (lu->curve != NID_undef && curve != lu->curve)
+ continue;
+#else
+ continue;
+#endif
+ } else if (lu->sig == EVP_PKEY_RSA_PSS) {
+ /* validate that key is large enough for the signature algorithm */
+ if (!rsa_pss_check_min_key_size(s->ctx, tmppkey, lu))
+ continue;
+ }
+ break;
+ }
+
+ if (i == s->shared_sigalgslen)
+ return NULL;
+
+ return lu;
+}
+
+/*
+ * 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,
+ * an appropriate error code is set and a TLS alert is sent.
+ *
+ * For clients fatalerrs is set to 0. 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 fatalerrs)
+{
+ const SIGALG_LOOKUP *lu = NULL;
+ int sig_idx = -1;
+
+ s->s3.tmp.cert = NULL;
+ s->s3.tmp.sigalg = NULL;
+
+ if (SSL_IS_TLS13(s)) {
+ lu = find_sig_alg(s, NULL, NULL);
+ if (lu == NULL) {
+ if (!fatalerrs)
+ return 1;
+ SSLfatal(s, SSL_AD_HANDSHAKE_FAILURE, 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)) {
+ size_t i;
+ if (s->s3.tmp.peer_sigalgs != NULL) {
+#ifndef OPENSSL_NO_EC
+ int curve = -1;
+
+ /* For Suite B need to match signature algorithm to curve */
+ if (tls1_suiteb(s))
+ curve =
+ evp_pkey_get_EC_KEY_curve_nid(s->cert->pkeys[SSL_PKEY_ECC]
+ .privatekey);
+#endif
+
+ /*
+ * Find highest preference signature algorithm matching
+ * cert type
+ */
+ for (i = 0; i < s->shared_sigalgslen; i++) {
+ lu = s->shared_sigalgs[i];
+
+ if (s->server) {
+ if ((sig_idx = tls12_get_cert_sigalg_idx(s, lu)) == -1)
+ continue;
+ } else {
+ int cc_idx = s->cert->key - s->cert->pkeys;
+
+ sig_idx = lu->sig_idx;
+ if (cc_idx != sig_idx)
+ continue;
+ }
+ /* Check that we have a cert, and sig_algs_cert */
+ if (!has_usable_cert(s, lu, sig_idx))
+ continue;
+ if (lu->sig == EVP_PKEY_RSA_PSS) {
+ /* validate that key is large enough for the signature algorithm */
+ EVP_PKEY *pkey = s->cert->pkeys[sig_idx].privatekey;
+
+ if (!rsa_pss_check_min_key_size(s->ctx, pkey, lu))
+ continue;
+ }
+#ifndef OPENSSL_NO_EC
+ if (curve == -1 || lu->curve == curve)
+#endif
+ break;
+ }
+#ifndef OPENSSL_NO_GOST
+ /*
+ * Some Windows-based implementations do not send GOST algorithms indication
+ * in supported_algorithms extension, so when we have GOST-based ciphersuite,
+ * we have to assume GOST support.
+ */
+ if (i == s->shared_sigalgslen && s->s3.tmp.new_cipher->algorithm_auth & (SSL_aGOST01 | SSL_aGOST12)) {
+ if ((lu = tls1_get_legacy_sigalg(s, -1)) == NULL) {
+ if (!fatalerrs)
+ return 1;
+ SSLfatal(s, SSL_AD_HANDSHAKE_FAILURE,
+ SSL_F_TLS_CHOOSE_SIGALG,
+ SSL_R_NO_SUITABLE_SIGNATURE_ALGORITHM);
+ return 0;
+ } else {
+ i = 0;
+ sig_idx = lu->sig_idx;
+ }
+ }
+#endif
+ if (i == s->shared_sigalgslen) {
+ if (!fatalerrs)
+ return 1;
+ SSLfatal(s, SSL_AD_HANDSHAKE_FAILURE,
+ SSL_F_TLS_CHOOSE_SIGALG,
+ SSL_R_NO_SUITABLE_SIGNATURE_ALGORITHM);
+ return 0;
+ }
+ } else {
+ /*
+ * If we have no sigalg use defaults
+ */
+ const uint16_t *sent_sigs;
+ size_t sent_sigslen;
+
+ if ((lu = tls1_get_legacy_sigalg(s, -1)) == NULL) {
+ if (!fatalerrs)
+ return 1;
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CHOOSE_SIGALG,
+ SSL_R_NO_SUITABLE_SIGNATURE_ALGORITHM);
+ 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
+ && has_usable_cert(s, lu, lu->sig_idx))
+ break;
+ }
+ if (i == sent_sigslen) {
+ if (!fatalerrs)
+ return 1;
+ SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER,
+ SSL_F_TLS_CHOOSE_SIGALG,
+ SSL_R_WRONG_SIGNATURE_TYPE);
+ return 0;
+ }
+ }
+ } else {
+ if ((lu = tls1_get_legacy_sigalg(s, -1)) == NULL) {
+ if (!fatalerrs)
+ return 1;
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CHOOSE_SIGALG,
+ SSL_R_NO_SUITABLE_SIGNATURE_ALGORITHM);
+ return 0;
+ }
+ }
+ }
+ if (sig_idx == -1)
+ sig_idx = lu->sig_idx;
+ s->s3.tmp.cert = &s->cert->pkeys[sig_idx];
+ s->cert->key = s->s3.tmp.cert;
+ s->s3.tmp.sigalg = lu;
+ return 1;
+}
+
+int SSL_CTX_set_tlsext_max_fragment_length(SSL_CTX *ctx, uint8_t mode)
+{
+ if (mode != TLSEXT_max_fragment_length_DISABLED
+ && !IS_MAX_FRAGMENT_LENGTH_EXT_VALID(mode)) {
+ SSLerr(SSL_F_SSL_CTX_SET_TLSEXT_MAX_FRAGMENT_LENGTH,
+ SSL_R_SSL3_EXT_INVALID_MAX_FRAGMENT_LENGTH);
+ return 0;
+ }
+
+ ctx->ext.max_fragment_len_mode = mode;
+ return 1;
+}
+
+int SSL_set_tlsext_max_fragment_length(SSL *ssl, uint8_t mode)
+{
+ if (mode != TLSEXT_max_fragment_length_DISABLED
+ && !IS_MAX_FRAGMENT_LENGTH_EXT_VALID(mode)) {
+ SSLerr(SSL_F_SSL_SET_TLSEXT_MAX_FRAGMENT_LENGTH,
+ SSL_R_SSL3_EXT_INVALID_MAX_FRAGMENT_LENGTH);
+ return 0;
+ }
+
+ ssl->ext.max_fragment_len_mode = mode;
+ return 1;
+}
+
+uint8_t SSL_SESSION_get_max_fragment_length(const SSL_SESSION *session)
+{
+ return session->ext.max_fragment_len_mode;
+}
+
+/*
+ * Helper functions for HMAC access with legacy support included.
+ */
+SSL_HMAC *ssl_hmac_new(const SSL_CTX *ctx)
+{
+ SSL_HMAC *ret = OPENSSL_zalloc(sizeof(*ret));
+ EVP_MAC *mac = NULL;
+
+ if (ret == NULL)
+ return NULL;
+#ifndef OPENSSL_NO_DEPRECATED_3_0
+ if (ctx->ext.ticket_key_evp_cb == NULL
+ && ctx->ext.ticket_key_cb != NULL) {
+ ret->old_ctx = HMAC_CTX_new();
+ if (ret->old_ctx == NULL)
+ goto err;
+ return ret;
+ }
+#endif
+ mac = EVP_MAC_fetch(ctx->libctx, "HMAC", NULL);
+ if (mac == NULL || (ret->ctx = EVP_MAC_CTX_new(mac)) == NULL)
+ goto err;
+ EVP_MAC_free(mac);
+ return ret;
+ err:
+ EVP_MAC_CTX_free(ret->ctx);
+ EVP_MAC_free(mac);
+ OPENSSL_free(ret);
+ return NULL;
+}
+
+void ssl_hmac_free(SSL_HMAC *ctx)
+{
+ if (ctx != NULL) {
+ EVP_MAC_CTX_free(ctx->ctx);
+#ifndef OPENSSL_NO_DEPRECATED_3_0
+ HMAC_CTX_free(ctx->old_ctx);
+#endif
+ OPENSSL_free(ctx);
+ }
+}
+
+#ifndef OPENSSL_NO_DEPRECATED_3_0
+HMAC_CTX *ssl_hmac_get0_HMAC_CTX(SSL_HMAC *ctx)
+{
+ return ctx->old_ctx;
+}
+#endif
+
+EVP_MAC_CTX *ssl_hmac_get0_EVP_MAC_CTX(SSL_HMAC *ctx)
+{
+ return ctx->ctx;
+}
+
+int ssl_hmac_init(SSL_HMAC *ctx, void *key, size_t len, char *md)
+{
+ OSSL_PARAM params[3], *p = params;
+
+ if (ctx->ctx != NULL) {
+ *p++ = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_DIGEST, md, 0);
+ *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY, key, len);
+ *p = OSSL_PARAM_construct_end();
+ if (EVP_MAC_CTX_set_params(ctx->ctx, params) && EVP_MAC_init(ctx->ctx))
+ return 1;
+ }
+#ifndef OPENSSL_NO_DEPRECATED_3_0
+ if (ctx->old_ctx != NULL)
+ return HMAC_Init_ex(ctx->old_ctx, key, len,
+ EVP_get_digestbyname(md), NULL);
+#endif
+ return 0;
+}
+
+int ssl_hmac_update(SSL_HMAC *ctx, const unsigned char *data, size_t len)
+{
+ if (ctx->ctx != NULL)
+ return EVP_MAC_update(ctx->ctx, data, len);
+#ifndef OPENSSL_NO_DEPRECATED_3_0
+ if (ctx->old_ctx != NULL)
+ return HMAC_Update(ctx->old_ctx, data, len);
+#endif
+ return 0;
+}
+
+int ssl_hmac_final(SSL_HMAC *ctx, unsigned char *md, size_t *len,
+ size_t max_size)
+{
+ if (ctx->ctx != NULL)
+ return EVP_MAC_final(ctx->ctx, md, len, max_size);
+#ifndef OPENSSL_NO_DEPRECATED_3_0
+ if (ctx->old_ctx != NULL) {
+ unsigned int l;
+
+ if (HMAC_Final(ctx->old_ctx, md, &l) > 0) {
+ if (len != NULL)
+ *len = l;
+ return 1;
+ }
+ }
+#endif
+ return 0;
+}
+
+size_t ssl_hmac_size(const SSL_HMAC *ctx)
+{
+ if (ctx->ctx != NULL)
+ return EVP_MAC_size(ctx->ctx);
+#ifndef OPENSSL_NO_DEPRECATED_3_0
+ if (ctx->old_ctx != NULL)
+ return HMAC_size(ctx->old_ctx);
+#endif
+ return 0;
+}
+