Fix ext_pre_shared_key_clienthello_length calculation.
If we're dropping the PSK extension due to an HRR with mismatched hash
(looking back at that, we could easily have avoided that nuisance...
I've filed [0] on rfc8446bis), we don't predict the length correctly.
The consequence is we don't pad the second ClientHello to the desired
range. Fix this and add an assert.
[0] https://github.com/tlswg/tls13-spec/issues/1227
Change-Id: I47d880b6bdafa95840f7513b6b7718851f22554d
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/47998
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/t1_lib.cc b/ssl/t1_lib.cc
index da89afb..33621a3 100644
--- a/ssl/t1_lib.cc
+++ b/ssl/t1_lib.cc
@@ -1937,10 +1937,27 @@
//
// https://tools.ietf.org/html/rfc8446#section-4.2.11
-static size_t ext_pre_shared_key_clienthello_length(SSL_HANDSHAKE *hs) {
- SSL *const ssl = hs->ssl;
+static bool should_offer_psk(const SSL_HANDSHAKE *hs) {
+ const SSL *const ssl = hs->ssl;
if (hs->max_version < TLS1_3_VERSION || ssl->session == nullptr ||
ssl_session_protocol_version(ssl->session.get()) < TLS1_3_VERSION) {
+ return false;
+ }
+
+ // Per RFC 8446 section 4.1.4, skip offering the session if the selected
+ // cipher in HelloRetryRequest does not match. This avoids performing the
+ // transcript hash transformation for multiple hashes.
+ if (ssl->s3->used_hello_retry_request &&
+ ssl->session->cipher->algorithm_prf != hs->new_cipher->algorithm_prf) {
+ return false;
+ }
+
+ return true;
+}
+
+static size_t ext_pre_shared_key_clienthello_length(const SSL_HANDSHAKE *hs) {
+ const SSL *const ssl = hs->ssl;
+ if (!should_offer_psk(hs)) {
return 0;
}
@@ -1953,16 +1970,7 @@
bool *out_needs_binder) {
const SSL *const ssl = hs->ssl;
*out_needs_binder = false;
- if (hs->max_version < TLS1_3_VERSION || ssl->session == nullptr ||
- ssl_session_protocol_version(ssl->session.get()) < TLS1_3_VERSION) {
- return true;
- }
-
- // Per RFC 8446 section 4.1.4, skip offering the session if the selected
- // cipher in HelloRetryRequest does not match. This avoids performing the
- // transcript hash transformation for multiple hashes.
- if (ssl->s3->used_hello_retry_request &&
- ssl->session->cipher->algorithm_prf != hs->new_cipher->algorithm_prf) {
+ if (!should_offer_psk(hs)) {
return true;
}
@@ -3309,8 +3317,8 @@
last_was_empty = false;
}
+ size_t psk_extension_len = ext_pre_shared_key_clienthello_length(hs);
if (!SSL_is_dtls(ssl) && !ssl->quic_method) {
- size_t psk_extension_len = ext_pre_shared_key_clienthello_length(hs);
header_len +=
SSL3_HM_HEADER_LENGTH + 2 + CBB_len(&extensions) + psk_extension_len;
size_t padding_len = 0;
@@ -3353,11 +3361,14 @@
}
// The PSK extension must be last, including after the padding.
+ const size_t len_before = CBB_len(&extensions);
if (!ext_pre_shared_key_add_clienthello(hs, &extensions,
out_needs_psk_binder)) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
return false;
}
+ assert(psk_extension_len == CBB_len(&extensions) - len_before);
+ (void)len_before; // |assert| is omitted in release builds.
// Discard empty extensions blocks.
if (CBB_len(&extensions) == 0) {