is set to the bitwise OR of TLSEXT_nid_unknown (0x1000000) and the id of the
group.
-SSL_get_negotiated_group() returns the NID of the negotiated group on a TLSv1.3
-connection for key exchange. This can be called by either client or server. If
-the NID for the shared group is unknown then the value is set to the bitwise OR
-of TLSEXT_nid_unknown (0x1000000) and the id of the group.
+SSL_get_negotiated_group() returns the NID of the negotiated group used for
+the handshake key exchange process. For TLSv1.3 connections this typically
+reflects the state of the current connection, though in the case of PSK-only
+resumption, the returned value will be from a previous connection. For earlier
+TLS versions, when a session has been resumed, it always reflects the group
+used for key exchange during the initial handshake (otherwise it is from the
+current, non-resumption, connection). This can be called by either client or
+server. If the NID for the shared group is unknown then the value is set to the
+bitwise OR of TLSEXT_nid_unknown (0x1000000) and the id of the group.
All these functions are implemented as macros.
When called on a client B<ssl>, SSL_get_shared_group() has no meaning and
returns -1.
-SSL_get_negotiated_group() returns the NID of the negotiated group on a
-TLSv1.3 connection for key exchange. Or it returns NID_undef if no negotiated
-group.
+SSL_get_negotiated_group() returns the NID of the negotiated group used for
+key exchange, or NID_undef if there was no negotiated group.
=head1 SEE ALSO
return id;
}
case SSL_CTRL_GET_NEGOTIATED_GROUP:
- ret = tls1_group_id2nid(s->s3.group_id, 1);
- break;
+ {
+ unsigned int id;
+ if (SSL_IS_TLS13(s) && s->s3.did_kex)
+ id = s->s3.group_id;
+ else
+ id = s->session->kex_group;
+ ret = tls1_group_id2nid(id, 1);
+ break;
+ }
case SSL_CTRL_SET_SIGALGS:
return tls1_set_sigalgs(s->cert, parg, larg, 0);
ASN1_OCTET_STRING *alpn_selected;
uint32_t tlsext_max_fragment_len_mode;
ASN1_OCTET_STRING *ticket_appdata;
+ uint32_t kex_group;
} SSL_SESSION_ASN1;
ASN1_SEQUENCE(SSL_SESSION_ASN1) = {
ASN1_EXP_OPT_EMBED(SSL_SESSION_ASN1, max_early_data, ZUINT32, 15),
ASN1_EXP_OPT(SSL_SESSION_ASN1, alpn_selected, ASN1_OCTET_STRING, 16),
ASN1_EXP_OPT_EMBED(SSL_SESSION_ASN1, tlsext_max_fragment_len_mode, ZUINT32, 17),
- ASN1_EXP_OPT(SSL_SESSION_ASN1, ticket_appdata, ASN1_OCTET_STRING, 18)
+ ASN1_EXP_OPT(SSL_SESSION_ASN1, ticket_appdata, ASN1_OCTET_STRING, 18),
+ ASN1_EXP_OPT_EMBED(SSL_SESSION_ASN1, kex_group, UINT32, 19)
} static_ASN1_SEQUENCE_END(SSL_SESSION_ASN1)
IMPLEMENT_STATIC_ASN1_ENCODE_FUNCTIONS(SSL_SESSION_ASN1)
as.version = SSL_SESSION_ASN1_VERSION;
as.ssl_version = in->ssl_version;
+ as.kex_group = in->kex_group;
+
if (in->cipher == NULL)
l = in->cipher_id;
else
ret->ssl_version = (int)as->ssl_version;
+ ret->kex_group = as->kex_group;
+
if (as->cipher->length != 2) {
ERR_raise(ERR_LIB_SSL, SSL_R_CIPHER_CODE_WRONG_LENGTH);
goto err;
const SSL_CIPHER *cipher;
unsigned long cipher_id; /* when ASN.1 loaded, this needs to be used to
* load the 'cipher' structure */
+ unsigned int kex_group; /* TLS group from key exchange */
CRYPTO_EX_DATA ex_data; /* application specific data */
/*
* These are used to make removal of session-ids more efficient and to
*/
char is_probably_safari;
+ /*
+ * Track whether we did a key exchange this handshake or not, so
+ * SSL_get_negotiated_group() knows whether to fall back to the
+ * value in the SSL_SESSION.
+ */
+ char did_kex;
/* For clients: peer temporary key */
/* The group_id for the key exchange key */
uint16_t group_id;
SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_R_BAD_KEY_SHARE);
return 0;
}
+ /* Retain this group in the SSL_SESSION */
+ if (!s->hit) {
+ s->session->kex_group = group_id;
+ } else if (group_id != s->session->kex_group) {
+ /*
+ * If this is a resumption but changed what group was used, we need
+ * to record the new group in the session, but the session is not
+ * a new session and could be in use by other threads. So, make
+ * a copy of the session to record the new information so that it's
+ * useful for any sessions resumed from tickets issued on this
+ * connection.
+ */
+ SSL_SESSION *new_sess;
+
+ if ((new_sess = ssl_session_dup(s->session, 0)) == NULL) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_MALLOC_FAILURE);
+ return 0;
+ }
+ SSL_SESSION_free(s->session);
+ s->session = new_sess;
+ s->session->kex_group = group_id;
+ }
if ((ginf = tls1_group_id_lookup(s->ctx, group_id)) == NULL) {
SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_R_BAD_KEY_SHARE);
return 0;
}
}
+ s->s3.did_kex = 1;
#endif
return 1;
}
s->s3.group_id = group_id;
+ /* Cache the selected group ID in the SSL_SESSION */
+ s->session->kex_group = group_id;
if (EVP_PKEY_set1_encoded_public_key(s->s3.peer_tmp,
PACKET_data(&encoded_pt),
return EXT_RETURN_FAIL;
}
}
+ s->s3.did_kex = 1;
return EXT_RETURN_SENT;
#else
return EXT_RETURN_FAIL;
*pkey = X509_get0_pubkey(s->session->peer);
/* else anonymous ECDH, so no certificate or pkey. */
+ /* Cache the agreed upon group in the SSL_SESSION */
+ s->session->kex_group = curve_id;
return 1;
}
SSL_R_UNSUPPORTED_ELLIPTIC_CURVE);
goto err;
}
- s->s3.tmp.pkey = ssl_generate_pkey_group(s, curve_id);
+ /* Cache the group used in the SSL_SESSION */
+ s->session->kex_group = curve_id;
/* Generate a new key for this curve */
+ s->s3.tmp.pkey = ssl_generate_pkey_group(s, curve_id);
if (s->s3.tmp.pkey == NULL) {
/* SSLfatal() already called */
goto err;