Fix some bugs in TLS 1.3 server key_share code.
Found by libFuzzer and then one more mistake caught by valgrind. Add a
test for this case.
Change-Id: I92773bc1231bafe5fc069e8568d93ac0df4c8acb
Reviewed-on: https://boringssl-review.googlesource.com/11129
Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c
index a89f5cf..32d9594 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -2196,41 +2196,66 @@
if (!tls1_get_shared_group(ssl, &group_id) ||
!CBS_get_u16_length_prefixed(contents, &key_shares) ||
CBS_len(contents) != 0) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
return 0;
}
- *out_found = 0;
+ /* Find the corresponding key share. */
+ int found = 0;
+ CBS peer_key;
while (CBS_len(&key_shares) > 0) {
uint16_t id;
- CBS peer_key;
+ CBS peer_key_tmp;
if (!CBS_get_u16(&key_shares, &id) ||
- !CBS_get_u16_length_prefixed(&key_shares, &peer_key)) {
+ !CBS_get_u16_length_prefixed(&key_shares, &peer_key_tmp)) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
return 0;
}
- if (id != group_id || *out_found) {
- continue;
- }
+ if (id == group_id) {
+ if (found) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_DUPLICATE_KEY_SHARE);
+ *out_alert = SSL_AD_ILLEGAL_PARAMETER;
+ return 0;
+ }
- SSL_ECDH_CTX group;
- memset(&group, 0, sizeof(SSL_ECDH_CTX));
- CBB public_key;
- if (!CBB_init(&public_key, 0) ||
- !SSL_ECDH_CTX_init(&group, group_id) ||
- !SSL_ECDH_CTX_accept(&group, &public_key, out_secret, out_secret_len,
- out_alert, CBS_data(&peer_key),
- CBS_len(&peer_key)) ||
- !CBB_finish(&public_key, &ssl->s3->hs->public_key,
- &ssl->s3->hs->public_key_len)) {
- SSL_ECDH_CTX_cleanup(&group);
- CBB_cleanup(&public_key);
- return 0;
+ found = 1;
+ peer_key = peer_key_tmp;
+ /* Continue parsing the structure to keep peers honest. */
}
- SSL_ECDH_CTX_cleanup(&group);
-
- *out_found = 1;
}
+ if (!found) {
+ *out_found = 0;
+ *out_secret = NULL;
+ *out_secret_len = 0;
+ return 1;
+ }
+
+ /* Compute the DH secret. */
+ uint8_t *secret = NULL;
+ size_t secret_len;
+ SSL_ECDH_CTX group;
+ memset(&group, 0, sizeof(SSL_ECDH_CTX));
+ CBB public_key;
+ if (!CBB_init(&public_key, 32) ||
+ !SSL_ECDH_CTX_init(&group, group_id) ||
+ !SSL_ECDH_CTX_accept(&group, &public_key, &secret, &secret_len,
+ out_alert, CBS_data(&peer_key),
+ CBS_len(&peer_key)) ||
+ !CBB_finish(&public_key, &ssl->s3->hs->public_key,
+ &ssl->s3->hs->public_key_len)) {
+ OPENSSL_free(secret);
+ SSL_ECDH_CTX_cleanup(&group);
+ CBB_cleanup(&public_key);
+ return 0;
+ }
+
+ SSL_ECDH_CTX_cleanup(&group);
+
+ *out_secret = secret;
+ *out_secret_len = secret_len;
+ *out_found = 1;
return 1;
}
diff --git a/ssl/test/runner/common.go b/ssl/test/runner/common.go
index d2f29fa..cd6c8f9 100644
--- a/ssl/test/runner/common.go
+++ b/ssl/test/runner/common.go
@@ -1076,6 +1076,10 @@
// always send a ServerKeyExchange for PSK ciphers, even if the identity
// hint is empty.
AlwaysSendPreSharedKeyIdentityHint bool
+
+ // TrailingKeyShareData, if true, causes the client key share list to
+ // include a trailing byte.
+ TrailingKeyShareData bool
}
func (c *Config) serverInit() {
diff --git a/ssl/test/runner/handshake_client.go b/ssl/test/runner/handshake_client.go
index d9d4451..c5c4495 100644
--- a/ssl/test/runner/handshake_client.go
+++ b/ssl/test/runner/handshake_client.go
@@ -113,6 +113,7 @@
if hello.vers >= VersionTLS13 {
keyShares = make(map[CurveID]ecdhCurve)
hello.hasKeyShares = true
+ hello.trailingKeyShareData = c.config.Bugs.TrailingKeyShareData
curvesToSend := c.config.defaultCurves()
for _, curveID := range hello.supportedCurves {
if !curvesToSend[curveID] {
diff --git a/ssl/test/runner/handshake_messages.go b/ssl/test/runner/handshake_messages.go
index 8f87881..63290fb 100644
--- a/ssl/test/runner/handshake_messages.go
+++ b/ssl/test/runner/handshake_messages.go
@@ -142,6 +142,7 @@
supportedPoints []uint8
hasKeyShares bool
keyShares []keyShareEntry
+ trailingKeyShareData bool
pskIdentities [][]uint8
hasEarlyData bool
earlyDataContext []byte
@@ -181,6 +182,7 @@
bytes.Equal(m.supportedPoints, m1.supportedPoints) &&
m.hasKeyShares == m1.hasKeyShares &&
eqKeyShareEntryLists(m.keyShares, m1.keyShares) &&
+ m.trailingKeyShareData == m1.trailingKeyShareData &&
eqByteSlices(m.pskIdentities, m1.pskIdentities) &&
m.hasEarlyData == m1.hasEarlyData &&
bytes.Equal(m.earlyDataContext, m1.earlyDataContext) &&
@@ -300,6 +302,10 @@
keyExchange := keyShares.addU16LengthPrefixed()
keyExchange.addBytes(keyShare.keyExchange)
}
+
+ if m.trailingKeyShareData {
+ keyShares.addU8(0)
+ }
}
if len(m.pskIdentities) > 0 {
extensions.addU16(extensionPreSharedKey)
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index 99d7fac..c350ac5 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -7978,6 +7978,8 @@
DuplicateKeyShares: true,
},
},
+ shouldFail: true,
+ expectedError: ":DUPLICATE_KEY_SHARE:",
})
testCases = append(testCases, testCase{
@@ -8175,6 +8177,19 @@
shouldFail: true,
expectedError: ":DECODE_ERROR:",
})
+
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "TLS13-TrailingKeyShareData",
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ TrailingKeyShareData: true,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":DECODE_ERROR:",
+ })
}
func worker(statusChan chan statusMsg, c chan *testCase, shimPath string, wg *sync.WaitGroup) {
diff --git a/ssl/tls13_server.c b/ssl/tls13_server.c
index 4cfd265..53e5363 100644
--- a/ssl/tls13_server.c
+++ b/ssl/tls13_server.c
@@ -88,7 +88,7 @@
int found_key_share;
uint8_t *dhe_secret;
size_t dhe_secret_len;
- uint8_t alert;
+ uint8_t alert = SSL_AD_DECODE_ERROR;
if (!ssl_ext_key_share_parse_clienthello(ssl, &found_key_share, &dhe_secret,
&dhe_secret_len, &alert,
&key_share)) {