summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin Jacobs <kjacobs@mozilla.com>2021-01-25 17:42:03 +0000
committerKevin Jacobs <kjacobs@mozilla.com>2021-01-25 17:42:03 +0000
commitd841f5ce8ed0e775c21c8d56feb55e83dd733c41 (patch)
treeea915cd091aee604a4ce4560a589e483560910e8
parente7e4a4a881bc28422834df5b5370a35cf21ba149 (diff)
downloadnss-hg-d841f5ce8ed0e775c21c8d56feb55e83dd733c41.tar.gz
Bug 1681585 - Update ECH to Draft-09. r=mt
This patch updates ECH implementation to draft-09. Changes of note are: - Acceptance signal derivation is now based on the handshake secret. - `config_id` hint changes from 32B to 8B, trial decryption added on the server. - Duplicate code in HRR cookie handling has been consolidated into `tls13_HandleHrrCookie`. - `ech_is_inner` extension is added, which causes a server to indicate ECH acceptance. - Per the above, support signaling ECH acceptance when acting as a backend server in split-mode (i.e. when there is no other local Encrypted Client Hello state). Differential Revision: https://phabricator.services.mozilla.com/D101049
-rw-r--r--automation/abi-check/expected-report-libssl3.so.txt11
-rw-r--r--gtests/ssl_gtest/libssl_internals.c23
-rw-r--r--gtests/ssl_gtest/libssl_internals.h4
-rw-r--r--gtests/ssl_gtest/ssl_extension_unittest.cc16
-rw-r--r--gtests/ssl_gtest/tls_ech_unittest.cc471
-rw-r--r--lib/ssl/ssl3con.c163
-rw-r--r--lib/ssl/ssl3ext.c10
-rw-r--r--lib/ssl/ssl3ext.h10
-rw-r--r--lib/ssl/sslexp.h8
-rw-r--r--lib/ssl/sslimpl.h11
-rw-r--r--lib/ssl/sslsecur.c1
-rw-r--r--lib/ssl/sslsock.c15
-rw-r--r--lib/ssl/sslt.h3
-rw-r--r--lib/ssl/tls13con.c84
-rw-r--r--lib/ssl/tls13con.h2
-rw-r--r--lib/ssl/tls13ech.c785
-rw-r--r--lib/ssl/tls13ech.h25
-rw-r--r--lib/ssl/tls13exthandle.c102
-rw-r--r--lib/ssl/tls13exthandle.h3
-rw-r--r--lib/ssl/tls13hashstate.c241
-rw-r--r--lib/ssl/tls13hashstate.h15
21 files changed, 1241 insertions, 762 deletions
diff --git a/automation/abi-check/expected-report-libssl3.so.txt b/automation/abi-check/expected-report-libssl3.so.txt
index e69de29bb..15e1f47da 100644
--- a/automation/abi-check/expected-report-libssl3.so.txt
+++ b/automation/abi-check/expected-report-libssl3.so.txt
@@ -0,0 +1,11 @@
+
+1 function with some indirect sub-type change:
+
+ [C] 'function SECStatus SSL_HandshakeNegotiatedExtension(PRFileDesc*, SSLExtensionType, PRBool*)' at sslreveal.c:72:1 has some indirect sub-type changes:
+ parameter 2 of type 'typedef SSLExtensionType' has sub-type changes:
+ underlying type 'enum __anonymous_enum__' at sslt.h:519:1 changed:
+ type size hasn't changed
+ 1 enumerator insertion:
+ '__anonymous_enum__::ssl_tls13_ech_is_inner_xtn' value '55817'
+ 1 enumerator change:
+ '__anonymous_enum__::ssl_tls13_encrypted_client_hello_xtn' from value '65032' to '65033' at sslt.h:519:1
diff --git a/gtests/ssl_gtest/libssl_internals.c b/gtests/ssl_gtest/libssl_internals.c
index 01d698e71..db0c9e86b 100644
--- a/gtests/ssl_gtest/libssl_internals.c
+++ b/gtests/ssl_gtest/libssl_internals.c
@@ -494,6 +494,27 @@ SECStatus SSLInt_SetRawEchConfigForRetry(PRFileDesc *fd, const uint8_t *buf,
sslEchConfig *cfg = (sslEchConfig *)PR_LIST_HEAD(&ss->echConfigs);
SECITEM_FreeItem(&cfg->raw, PR_FALSE);
SECITEM_AllocItem(NULL, &cfg->raw, len);
- memcpy(cfg->raw.data, buf, len);
+ PORT_Memcpy(cfg->raw.data, buf, len);
+ return SECSuccess;
+}
+
+// Zero the echConfig.config_id for all configured echConfigs.
+// This mimics a collision on the 8B config ID so that we can
+// test trial decryption.
+SECStatus SSLInt_ZeroEchConfigIds(PRFileDesc *fd) {
+ if (!fd) {
+ return SECFailure;
+ }
+ sslSocket *ss = ssl_FindSocket(fd);
+ if (!ss) {
+ return SECFailure;
+ }
+
+ for (PRCList *cur_p = PR_LIST_HEAD(&ss->echConfigs); cur_p != &ss->echConfigs;
+ cur_p = PR_NEXT_LINK(cur_p)) {
+ PORT_Memset(((sslEchConfig *)cur_p)->configId, 0,
+ sizeof(((sslEchConfig *)cur_p)->configId));
+ }
+
return SECSuccess;
}
diff --git a/gtests/ssl_gtest/libssl_internals.h b/gtests/ssl_gtest/libssl_internals.h
index a6a3239d5..372f254d6 100644
--- a/gtests/ssl_gtest/libssl_internals.h
+++ b/gtests/ssl_gtest/libssl_internals.h
@@ -51,5 +51,5 @@ SECStatus SSLInt_SetDCAdvertisedSigSchemes(PRFileDesc *fd,
SECStatus SSLInt_RemoveServerCertificates(PRFileDesc *fd);
SECStatus SSLInt_SetRawEchConfigForRetry(PRFileDesc *fd, const uint8_t *buf,
size_t len);
-
-#endif // ndef libssl_internals_h_
+SECStatus SSLInt_ZeroEchConfigIds(PRFileDesc *fd);
+#endif // ifndef libssl_internals_h_
diff --git a/gtests/ssl_gtest/ssl_extension_unittest.cc b/gtests/ssl_gtest/ssl_extension_unittest.cc
index b08eba59a..f60697900 100644
--- a/gtests/ssl_gtest/ssl_extension_unittest.cc
+++ b/gtests/ssl_gtest/ssl_extension_unittest.cc
@@ -1098,22 +1098,6 @@ TEST_P(TlsExtensionTest13, HrrThenRemoveSupportedGroups) {
SSL_ERROR_MISSING_SUPPORTED_GROUPS_EXTENSION);
}
-#ifdef NSS_ENABLE_DRAFT_HPKE
-TEST_P(TlsExtensionTest13, HrrThenRemoveEch) {
- if (variant_ == ssl_variant_datagram) {
- // ECH not supported in DTLS.
- GTEST_SKIP();
- }
-
- EnsureTlsSetup();
- SetupEch(client_, server_);
- ExpectAlert(server_, kTlsAlertIllegalParameter);
- HrrThenRemoveExtensionsTest(ssl_tls13_encrypted_client_hello_xtn,
- SSL_ERROR_ILLEGAL_PARAMETER_ALERT,
- SSL_ERROR_BAD_2ND_CLIENT_HELLO);
-}
-#endif
-
TEST_P(TlsExtensionTest13, EmptyVersionList) {
static const uint8_t ext[] = {0x00, 0x00};
ConnectWithBogusVersionList(ext, sizeof(ext));
diff --git a/gtests/ssl_gtest/tls_ech_unittest.cc b/gtests/ssl_gtest/tls_ech_unittest.cc
index b05224bda..64a270624 100644
--- a/gtests/ssl_gtest/tls_ech_unittest.cc
+++ b/gtests/ssl_gtest/tls_ech_unittest.cc
@@ -19,9 +19,9 @@ namespace nss_test {
class TlsAgentEchTest : public TlsAgentTestClient13 {
protected:
- void InstallEchConfig(const DataBuffer& record, PRErrorCode err = 0) {
- SECStatus rv =
- SSL_SetClientEchConfigs(agent_->ssl_fd(), record.data(), record.len());
+ void InstallEchConfig(const DataBuffer& echconfig, PRErrorCode err = 0) {
+ SECStatus rv = SSL_SetClientEchConfigs(agent_->ssl_fd(), echconfig.data(),
+ echconfig.len());
if (err == 0) {
ASSERT_EQ(SECSuccess, rv);
} else {
@@ -143,18 +143,6 @@ class TlsConnectStreamTls13Ech : public TlsConnectTestBase {
Connect();
}
- private:
- // Testing certan invalid CHInner configurations is tricky, particularly
- // since the CHOuter forms AAD and isn't available in filters. Instead of
- // generating these inputs on the fly, use a fixed server keypair so that
- // the input can be generated once (e.g. via a debugger) and replayed in
- // each invocation of the test.
- std::string kFixedServerPubkey =
- "3067020100301406072a8648ce3d020106092b06010401da470f01044c304a"
- "02010104205a8aa0d2476b28521588e0c704b14db82cdd4970d340d293a957"
- "6deaee9ec1c7a1230321008756e2580c07c1d2ffcb662f5fadc6d6ff13da85"
- "abd7adfecf984aaa102c1269";
-
void ImportFixedEchKeypair(ScopedSECKEYPublicKey& pub,
ScopedSECKEYPrivateKey& priv) {
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
@@ -176,16 +164,30 @@ class TlsConnectStreamTls13Ech : public TlsConnectTestBase {
ASSERT_NE(nullptr, tmp_pub);
}
+ private:
+ // Testing certan invalid CHInner configurations is tricky, particularly
+ // since the CHOuter forms AAD and isn't available in filters. Instead of
+ // generating these inputs on the fly, use a fixed server keypair so that
+ // the input can be generated once (e.g. via a debugger) and replayed in
+ // each invocation of the test.
+ std::string kFixedServerPubkey =
+ "3067020100301406072a8648ce3d020106092b06010401da470f01044c304a"
+ "02010104205a8aa0d2476b28521588e0c704b14db82cdd4970d340d293a957"
+ "6deaee9ec1c7a1230321008756e2580c07c1d2ffcb662f5fadc6d6ff13da85"
+ "abd7adfecf984aaa102c1269";
+
void SetMutualEchConfigs(ScopedSECKEYPublicKey& pub,
ScopedSECKEYPrivateKey& priv) {
- DataBuffer record;
+ DataBuffer echconfig;
TlsConnectTestBase::GenerateEchConfig(HpkeDhKemX25519Sha256, kDefaultSuites,
- kPublicName, 100, record, pub, priv);
+ kPublicName, 100, echconfig, pub,
+ priv);
ASSERT_EQ(SECSuccess,
SSL_SetServerEchConfigs(server_->ssl_fd(), pub.get(), priv.get(),
- record.data(), record.len()));
- ASSERT_EQ(SECSuccess, SSL_SetClientEchConfigs(client_->ssl_fd(),
- record.data(), record.len()));
+ echconfig.data(), echconfig.len()));
+ ASSERT_EQ(SECSuccess,
+ SSL_SetClientEchConfigs(client_->ssl_fd(), echconfig.data(),
+ echconfig.len()));
}
};
@@ -224,17 +226,17 @@ TEST_P(TlsAgentEchTest, EchConfigsSupportedYesNo) {
// ECHConfig 2 cipher_suites are unsupported.
const std::string mixed =
- "0086FE08003F000B7075626C69632E6E616D6500203BB6D46C201B820F1AE4AFD4DEC304"
- "444156E4E04D1BF0FFDA7783B6B457F75600200008000100030001000100640000FE0800"
+ "0086FE09003F000B7075626C69632E6E616D6500203BB6D46C201B820F1AE4AFD4DEC304"
+ "444156E4E04D1BF0FFDA7783B6B457F75600200008000100030001000100640000FE0900"
"3F000B7075626C69632E6E616D6500203BB6D46C201B820F1AE4AFD4DEC304444156E4E0"
"4D1BF0FFDA7783B6B457F756002000080001FFFFFFFF000100640000";
std::vector<uint8_t> config = hex_string_to_bytes(mixed);
- DataBuffer record(config.data(), config.size());
+ DataBuffer echconfig(config.data(), config.size());
EnsureInit();
EXPECT_EQ(SECSuccess, SSL_EnableTls13GreaseEch(agent_->ssl_fd(),
PR_FALSE)); // Don't GREASE
- InstallEchConfig(record, 0);
+ InstallEchConfig(echconfig, 0);
auto filter = MakeTlsFilter<TlsExtensionCapture>(
agent_, ssl_tls13_encrypted_client_hello_xtn);
agent_->Handshake();
@@ -249,17 +251,17 @@ TEST_P(TlsAgentEchTest, EchConfigsSupportedNoYes) {
// ECHConfig 1 cipher_suites are unsupported.
const std::string mixed =
- "0086FE08003F000B7075626C69632E6E616D6500203BB6D46C201B820F1AE4AFD4DEC304"
- "444156E4E04D1BF0FFDA7783B6B457F756002000080001FFFFFFFF000100640000FE0800"
+ "0086FE09003F000B7075626C69632E6E616D6500203BB6D46C201B820F1AE4AFD4DEC304"
+ "444156E4E04D1BF0FFDA7783B6B457F756002000080001FFFFFFFF000100640000FE0900"
"3F000B7075626C69632E6E616D6500203BB6D46C201B820F1AE4AFD4DEC304444156E4E0"
"4D1BF0FFDA7783B6B457F75600200008000100030001000100640000";
std::vector<uint8_t> config = hex_string_to_bytes(mixed);
- DataBuffer record(config.data(), config.size());
+ DataBuffer echconfig(config.data(), config.size());
EnsureInit();
EXPECT_EQ(SECSuccess, SSL_EnableTls13GreaseEch(agent_->ssl_fd(),
PR_FALSE)); // Don't GREASE
- InstallEchConfig(record, 0);
+ InstallEchConfig(echconfig, 0);
auto filter = MakeTlsFilter<TlsExtensionCapture>(
agent_, ssl_tls13_encrypted_client_hello_xtn);
agent_->Handshake();
@@ -274,17 +276,17 @@ TEST_P(TlsAgentEchTest, EchConfigsSupportedNoNo) {
// ECHConfig 1 and 2 cipher_suites are unsupported.
const std::string unsupported =
- "0086FE08003F000B7075626C69632E6E616D6500203BB6D46C201B820F1AE4AFD4DEC304"
- "444156E4E04D1BF0FFDA7783B6B457F756002000080001FFFF0001FFFF00640000FE0800"
+ "0086FE09003F000B7075626C69632E6E616D6500203BB6D46C201B820F1AE4AFD4DEC304"
+ "444156E4E04D1BF0FFDA7783B6B457F756002000080001FFFF0001FFFF00640000FE0900"
"3F000B7075626C69632E6E616D6500203BB6D46C201B820F1AE4AFD4DEC304444156E4E0"
"4D1BF0FFDA7783B6B457F75600200008FFFF0003FFFF000100640000";
std::vector<uint8_t> config = hex_string_to_bytes(unsupported);
- DataBuffer record(config.data(), config.size());
+ DataBuffer echconfig(config.data(), config.size());
EnsureInit();
EXPECT_EQ(SECSuccess, SSL_EnableTls13GreaseEch(agent_->ssl_fd(),
PR_FALSE)); // Don't GREASE
- InstallEchConfig(record, SEC_ERROR_INVALID_ARGS);
+ InstallEchConfig(echconfig, SEC_ERROR_INVALID_ARGS);
auto filter = MakeTlsFilter<TlsExtensionCapture>(
agent_, ssl_tls13_encrypted_client_hello_xtn);
agent_->Handshake();
@@ -296,11 +298,11 @@ TEST_P(TlsAgentEchTest, ShortEchConfig) {
EnsureInit();
ScopedSECKEYPublicKey pub;
ScopedSECKEYPrivateKey priv;
- DataBuffer record;
+ DataBuffer echconfig;
TlsConnectTestBase::GenerateEchConfig(HpkeDhKemX25519Sha256, kDefaultSuites,
- kPublicName, 100, record, pub, priv);
- record.Truncate(record.len() - 1);
- InstallEchConfig(record, SEC_ERROR_BAD_DATA);
+ kPublicName, 100, echconfig, pub, priv);
+ echconfig.Truncate(echconfig.len() - 1);
+ InstallEchConfig(echconfig, SEC_ERROR_BAD_DATA);
EXPECT_EQ(SECSuccess, SSL_EnableTls13GreaseEch(agent_->ssl_fd(),
PR_FALSE)); // Don't GREASE
auto filter = MakeTlsFilter<TlsExtensionCapture>(
@@ -314,11 +316,11 @@ TEST_P(TlsAgentEchTest, LongEchConfig) {
EnsureInit();
ScopedSECKEYPublicKey pub;
ScopedSECKEYPrivateKey priv;
- DataBuffer record;
+ DataBuffer echconfig;
TlsConnectTestBase::GenerateEchConfig(HpkeDhKemX25519Sha256, kDefaultSuites,
- kPublicName, 100, record, pub, priv);
- record.Write(record.len(), 1, 1); // Append one byte
- InstallEchConfig(record, SEC_ERROR_BAD_DATA);
+ kPublicName, 100, echconfig, pub, priv);
+ echconfig.Write(echconfig.len(), 1, 1); // Append one byte
+ InstallEchConfig(echconfig, SEC_ERROR_BAD_DATA);
EXPECT_EQ(SECSuccess, SSL_EnableTls13GreaseEch(agent_->ssl_fd(),
PR_FALSE)); // Don't GREASE
auto filter = MakeTlsFilter<TlsExtensionCapture>(
@@ -332,13 +334,13 @@ TEST_P(TlsAgentEchTest, UnsupportedEchConfigVersion) {
EnsureInit();
ScopedSECKEYPublicKey pub;
ScopedSECKEYPrivateKey priv;
- DataBuffer record;
+ DataBuffer echconfig;
static const uint8_t bad_version[] = {0xff, 0xff};
DataBuffer bad_ver_buf(bad_version, sizeof(bad_version));
TlsConnectTestBase::GenerateEchConfig(HpkeDhKemX25519Sha256, kDefaultSuites,
- kPublicName, 100, record, pub, priv);
- record.Splice(bad_ver_buf, 2, 2);
- InstallEchConfig(record, SEC_ERROR_INVALID_ARGS);
+ kPublicName, 100, echconfig, pub, priv);
+ echconfig.Splice(bad_ver_buf, 2, 2);
+ InstallEchConfig(echconfig, SEC_ERROR_INVALID_ARGS);
EXPECT_EQ(SECSuccess, SSL_EnableTls13GreaseEch(agent_->ssl_fd(),
PR_FALSE)); // Don't GREASE
auto filter = MakeTlsFilter<TlsExtensionCapture>(
@@ -352,12 +354,12 @@ TEST_P(TlsAgentEchTest, UnsupportedHpkeKem) {
EnsureInit();
ScopedSECKEYPublicKey pub;
ScopedSECKEYPrivateKey priv;
- DataBuffer record;
+ DataBuffer echconfig;
// SSL_EncodeEchConfig encodes without validation.
TlsConnectTestBase::GenerateEchConfig(static_cast<HpkeKemId>(0xff),
kDefaultSuites, kPublicName, 100,
- record, pub, priv);
- InstallEchConfig(record, SEC_ERROR_INVALID_ARGS);
+ echconfig, pub, priv);
+ InstallEchConfig(echconfig, SEC_ERROR_INVALID_ARGS);
EXPECT_EQ(SECSuccess, SSL_EnableTls13GreaseEch(agent_->ssl_fd(),
PR_FALSE)); // Don't GREASE
auto filter = MakeTlsFilter<TlsExtensionCapture>(
@@ -371,10 +373,26 @@ TEST_P(TlsAgentEchTest, EchRejectIgnoreAllUnknownSuites) {
EnsureInit();
ScopedSECKEYPublicKey pub;
ScopedSECKEYPrivateKey priv;
- DataBuffer record;
+ DataBuffer echconfig;
TlsConnectTestBase::GenerateEchConfig(HpkeDhKemX25519Sha256, kBogusSuite,
- kPublicName, 100, record, pub, priv);
- InstallEchConfig(record, SEC_ERROR_INVALID_ARGS);
+ kPublicName, 100, echconfig, pub, priv);
+ InstallEchConfig(echconfig, SEC_ERROR_INVALID_ARGS);
+ EXPECT_EQ(SECSuccess, SSL_EnableTls13GreaseEch(agent_->ssl_fd(),
+ PR_FALSE)); // Don't GREASE
+ auto filter = MakeTlsFilter<TlsExtensionCapture>(
+ agent_, ssl_tls13_encrypted_client_hello_xtn);
+ agent_->Handshake();
+ ASSERT_FALSE(filter->captured());
+}
+
+TEST_P(TlsAgentEchTest, EchConfigRejectEmptyPublicName) {
+ EnsureInit();
+ ScopedSECKEYPublicKey pub;
+ ScopedSECKEYPrivateKey priv;
+ DataBuffer echconfig;
+ TlsConnectTestBase::GenerateEchConfig(HpkeDhKemX25519Sha256, kBogusSuite, "",
+ 100, echconfig, pub, priv);
+ InstallEchConfig(echconfig, SSL_ERROR_RX_MALFORMED_ECH_CONFIG);
EXPECT_EQ(SECSuccess, SSL_EnableTls13GreaseEch(agent_->ssl_fd(),
PR_FALSE)); // Don't GREASE
auto filter = MakeTlsFilter<TlsExtensionCapture>(
@@ -385,17 +403,18 @@ TEST_P(TlsAgentEchTest, EchRejectIgnoreAllUnknownSuites) {
TEST_F(TlsConnectStreamTls13, EchAcceptIgnoreSingleUnknownSuite) {
EnsureTlsSetup();
- DataBuffer record;
+ DataBuffer echconfig;
ScopedSECKEYPublicKey pub;
ScopedSECKEYPrivateKey priv;
TlsConnectTestBase::GenerateEchConfig(HpkeDhKemX25519Sha256,
kUnknownFirstSuite, kPublicName, 100,
- record, pub, priv);
- ASSERT_EQ(SECSuccess, SSL_SetClientEchConfigs(client_->ssl_fd(),
- record.data(), record.len()));
+ echconfig, pub, priv);
+ ASSERT_EQ(SECSuccess,
+ SSL_SetClientEchConfigs(client_->ssl_fd(), echconfig.data(),
+ echconfig.len()));
ASSERT_EQ(SECSuccess,
SSL_SetServerEchConfigs(server_->ssl_fd(), pub.get(), priv.get(),
- record.data(), record.len()));
+ echconfig.data(), echconfig.len()));
client_->ExpectEch();
server_->ExpectEch();
@@ -488,10 +507,10 @@ TEST_P(TlsAgentEchTest, NoEarlyRetryConfigs) {
ScopedSECKEYPublicKey pub;
ScopedSECKEYPrivateKey priv;
- DataBuffer record;
+ DataBuffer echconfig;
TlsConnectTestBase::GenerateEchConfig(HpkeDhKemX25519Sha256, kDefaultSuites,
- kPublicName, 100, record, pub, priv);
- InstallEchConfig(record, 0);
+ kPublicName, 100, echconfig, pub, priv);
+ InstallEchConfig(echconfig, 0);
EXPECT_EQ(SECFailure,
SSL_GetEchRetryConfigs(agent_->ssl_fd(), &retry_configs));
@@ -502,11 +521,11 @@ TEST_P(TlsAgentEchTest, NoSniSoNoEch) {
EnsureInit();
ScopedSECKEYPublicKey pub;
ScopedSECKEYPrivateKey priv;
- DataBuffer record;
+ DataBuffer echconfig;
TlsConnectTestBase::GenerateEchConfig(HpkeDhKemX25519Sha256, kDefaultSuites,
- kPublicName, 100, record, pub, priv);
+ kPublicName, 100, echconfig, pub, priv);
SSL_SetURL(agent_->ssl_fd(), "");
- InstallEchConfig(record, 0);
+ InstallEchConfig(echconfig, 0);
SSL_SetURL(agent_->ssl_fd(), "");
EXPECT_EQ(SECSuccess, SSL_EnableTls13GreaseEch(agent_->ssl_fd(),
PR_FALSE)); // Don't GREASE
@@ -520,7 +539,7 @@ TEST_P(TlsAgentEchTest, NoEchConfigSoNoEch) {
EnsureInit();
ScopedSECKEYPublicKey pub;
ScopedSECKEYPrivateKey priv;
- DataBuffer record;
+ DataBuffer echconfig;
EXPECT_EQ(SECSuccess, SSL_EnableTls13GreaseEch(agent_->ssl_fd(),
PR_FALSE)); // Don't GREASE
auto filter = MakeTlsFilter<TlsExtensionCapture>(
@@ -533,29 +552,29 @@ TEST_P(TlsAgentEchTest, EchConfigDuplicateExtensions) {
EnsureInit();
ScopedSECKEYPublicKey pub;
ScopedSECKEYPrivateKey priv;
- DataBuffer record;
+ DataBuffer echconfig;
TlsConnectTestBase::GenerateEchConfig(HpkeDhKemX25519Sha256, kDefaultSuites,
- kPublicName, 100, record, pub, priv);
+ kPublicName, 100, echconfig, pub, priv);
static const uint8_t duped_xtn[] = {0x00, 0x08, 0x00, 0x01, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00};
DataBuffer buf(duped_xtn, sizeof(duped_xtn));
- record.Truncate(record.len() - 2);
- record.Append(buf);
+ echconfig.Truncate(echconfig.len() - 2);
+ echconfig.Append(buf);
uint32_t len;
- ASSERT_TRUE(record.Read(0, 2, &len));
+ ASSERT_TRUE(echconfig.Read(0, 2, &len));
len += buf.len() - 2;
DataBuffer new_len;
ASSERT_TRUE(new_len.Write(0, len, 2));
- record.Splice(new_len, 0, 2);
+ echconfig.Splice(new_len, 0, 2);
new_len.Truncate(0);
- ASSERT_TRUE(record.Read(4, 2, &len));
+ ASSERT_TRUE(echconfig.Read(4, 2, &len));
len += buf.len() - 2;
ASSERT_TRUE(new_len.Write(0, len, 2));
- record.Splice(new_len, 4, 2);
+ echconfig.Splice(new_len, 4, 2);
- InstallEchConfig(record, SEC_ERROR_EXTENSION_VALUE_INVALID);
+ InstallEchConfig(echconfig, SEC_ERROR_EXTENSION_VALUE_INVALID);
EXPECT_EQ(SECSuccess, SSL_EnableTls13GreaseEch(agent_->ssl_fd(),
PR_FALSE)); // Don't GREASE
auto filter = MakeTlsFilter<TlsExtensionCapture>(
@@ -570,17 +589,16 @@ TEST_P(TlsAgentEchTest, EchConfigDuplicateExtensions) {
// extension in ClientHelloOuter.
TEST_F(TlsConnectStreamTls13Ech, EchOuterExtensionsReferencesMissing) {
std::string ch =
- "01000170030374d616d97efe591bf9bee4496bcc1118145b4dd02f7d1ff979fd0cf61749"
- "a91e0000061301130313020100014100000010000e00000b7075626c69632e6e616d65ff"
+ "010001580303dfff91b5e1ba00f29d2338419b3abf125ee1051a942ae25163bbf609a1ea"
+ "11920000061301130313020100012900000010000e00000b7075626c69632e6e616d65ff"
"01000100000a00140012001d00170018001901000101010201030104003300260024001d"
- "00204f346f86351b077492c83564c909d1aaab4f6f3ee2566af0e90a4684c793805d002b"
+ "0020d94c1590c261e9ea8ae55bc9581f397cc598115f8b70aec1b0236f4c8c555537002b"
"0003020304000d0018001604030503060302030804080508060401050106010201002d00"
- "020101001c00024001fe0800b30001000320a10698ccbd4bd86df91f617e58dd2ca96b8b"
- "a5f058dd5c5ab1ca9750ef9d28c70020924764b36fe5d4a985f9857ceb75edb10b5f4b5b"
- "f9d59290db70743e3c582163006acea5d7785cc506ecf5c859a9cad18f2b1df1a32231fe"
- "0330471ee0e88ece9047e6491a381bfabed58f7fc542f0ba78eb55030bcfe1d400f67275"
- "eac8619d1e4237e9d6176dd4eb54f3f25865686756f313a4ba47901c83e5ad5413609d39"
- "816346b940115fd68e534609";
+ "020101001c00024001fe09009b0001000308fde4163c5c6e8bb6002067a895efa2721c88"
+ "63ecfa1bea1e520ae6f6cf938e3e37802688f7a83a871a04006aa693f053f87db87cf82a"
+ "7caa20670d79b92ccda97893fdf99352fc766fb3dd5570948311dddb6d41214234fae585"
+ "e354a048c072b3fb00a0a64e8e089e4a90152ee91a2c5b947c99d3dcebfb6334453b023d"
+ "4d725010996a290a0552e4b238ec91c21440adc0d51a4435";
ReplayChWithMalformedInner(ch, kTlsAlertIllegalParameter,
SSL_ERROR_RX_MALFORMED_ECH_EXTENSION,
SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
@@ -589,17 +607,16 @@ TEST_F(TlsConnectStreamTls13Ech, EchOuterExtensionsReferencesMissing) {
// Drop supported_versions from CHInner, make sure we don't negotiate 1.2+ECH.
TEST_F(TlsConnectStreamTls13Ech, EchVersion12Inner) {
std::string ch =
- "0100017003034dd5bf4c12835e9be21f983953720e3595b3a8eeb4a44467678caceb7727"
- "3be90000061301130313020100014100000010000e00000b7075626c69632e6e616d65ff"
+ "0100015103038fbe6f75b0123116fa5c4eccf0cf26c17ab1ded5529307e419c036ac7e9c"
+ "e8e30000061301130313020100012200000010000e00000b7075626c69632e6e616d65ff"
"01000100000a00140012001d00170018001901000101010201030104003300260024001d"
- "0020af7b976cdf69ffcd494ca5a93ae3ecde692b09be518ee033aad908c45b82c368002b"
+ "002078d644583b4f056bec4d8ae9bddd383aed6eb7cdb3294f88b0e37a4f26a02549002b"
"0003020304000d0018001604030503060302030804080508060401050106010201002d00"
- "020101001c0002400100150003000000fe0800ac0001000320a10698ccbd4bd86df91f61"
- "7e58dd2ca96b8ba5f058dd5c5ab1ca9750ef9d28c70020f5ece4c187b76f7e3d467c7506"
- "215e73c27c918cd863c0e80d76a7987ec274320063e037492868eff5296a22dc50885e9d"
- "f6964a5e26546f1bada043f8834988dfea5394b4c45a4d0b3afc52142d33f94161135a63"
- "ed3c1b63f60d8133fb1cff17e1f9ced6c871984e412ed8ddb0f487c4d09d7aea80488004"
- "c45a17cd3b5cdca316155fdb";
+ "020101001c00024001fe0900940001000308fde4163c5c6e8bb600208958e66d1d4bbd46"
+ "4792f392e119dbce91ee3e65067899b45c83855dae61e67a00637df038e7b35483786707"
+ "dd1b25be5cd3dd07f1ca4b33a3595ddb959e5c0da3d2f0b3314417614968691700c05232"
+ "07c729b34f3b5de62728b3cb6b45b00e6f94b204a9504d0e7e24c66f42aacc73591c86ef"
+ "571e61cebd6ba671081150a2dae89e7493";
ReplayChWithMalformedInner(ch, kTlsAlertProtocolVersion,
SSL_ERROR_UNSUPPORTED_VERSION,
SSL_ERROR_PROTOCOL_VERSION_ALERT);
@@ -608,40 +625,103 @@ TEST_F(TlsConnectStreamTls13Ech, EchVersion12Inner) {
// Use CHInner supported_versions to negotiate 1.2.
TEST_F(TlsConnectStreamTls13Ech, EchVersion12InnerSupportedVersions) {
std::string ch =
- "010001700303845c298db4017d2ed2584284b90e4ecba57a63663560c57aa0b1ac51203d"
- "c8560000061301130313020100014100000010000e00000b7075626c69632e6e616d65ff"
+ "01000158030378a601a3f12229e53e0b8d92c3599bf1782e8261d2ecaec9bbe595d4c901"
+ "98770000061301130313020100012900000010000e00000b7075626c69632e6e616d65ff"
"01000100000a00140012001d00170018001901000101010201030104003300260024001d"
- "00203356719e88b539645438f645916aeeffe93c38803a59d6997938aa98eefbcf64002b"
+ "00201c8017d6970f3a92ac1c9919c3a26788052f84599fb0c3cb7bd381304148724e002b"
"0003020304000d0018001604030503060302030804080508060401050106010201002d00"
- "020101001c00024001fe0800b30001000320a10698ccbd4bd86df91f617e58dd2ca96b8b"
- "a5f058dd5c5ab1ca9750ef9d28c700208412c945c53624bcace5eda0dc1ad300a1620e86"
- "5a0f4a27755a3477b115b65b006abf1dfd77ddc1b80c5976732174a5fe7ebcf9ff1a548b"
- "097daa12a37f3e32a613a0798544ba1d96239431bc807ddd9055ac3fb3e32b2eb42cec30"
- "e915357418a953027d73020fd739287414205349eeff376dd464750ca70a965141a88800"
- "6a043fe1d6d882d9a2c2f6f3";
+ "020101001c00024001fe09009b0001000308fde4163c5c6e8bb60020f7347d34f125e866"
+ "76b1cdc43455c6c00918a3c8a961335e1b9aa864da2b5313006a21e6ad81533e90cea24e"
+ "c2c3656f6b53114b4c63bf89462696f1c8ad4e1193d87062a5537edbe83c9b35c41e9763"
+ "1d2333270854758ee02548afb7f2264f904474465415a5085024487f22b017208e250ca4"
+ "7902d61d98fbd1cb8afc0a14dcd70a68343cf67c258758d9";
ReplayChWithMalformedInner(ch, kTlsAlertProtocolVersion,
SSL_ERROR_UNSUPPORTED_VERSION,
SSL_ERROR_PROTOCOL_VERSION_ALERT);
}
-// Replay a CH for which the ECH Inner lacks the required
-// empty ECH extension.
+// Replay a CH for which CHInner lacks the required ech_is_inner extension.
TEST_F(TlsConnectStreamTls13Ech, EchInnerMissingEmptyEch) {
std::string ch =
- "0100017103032bf866cbd6d4abdec8ce23107eaef9af51b644043953e3b70f2f28f1898e"
- "87880000061301130313020100014200000010000e00000b7075626c69632e6e616d65ff"
+ "010001540303033b3284790ada882445bfb38b8af3509659033c931e6ae97febbaa62b19"
+ "b4ac0000061301130313020100012500000010000e00000b7075626c69632e6e616d65ff"
"01000100000a00140012001d00170018001901000101010201030104003300260024001d"
- "00208f614d3017575332ca009a42d33bcaf876b4ba6d44b052e8019c31f6f1559e41002b"
+ "00209d1ed410ccb05ce9e424f52b1be3599bcc1efb0913ae14a24d9a69cbfbc39744002b"
"0003020304000d0018001604030503060302030804080508060401050106010201002d00"
- "020101001c000240010015000100fe0800af0001000320a10698ccbd4bd86df91f617e58"
- "dd2ca96b8ba5f058dd5c5ab1ca9750ef9d28c70020da1d5d9f183a5d5e49892e38eaae5e"
- "9e3e6c5d404a5fdb672ca37f9cebabd57400660ea1d61917cc1049aab22506078ccecfc4"
- "16a364a1beaa8915b250bb86ac2c725698c3c641830c4aa4e8b7f50152b5732b29b1ac43"
- "45c97fc018855fd68e5600d0ef188e905b69997c3711b0ec0114a857177df728c7b84f52"
- "2923f932838f7f15bb22644fd4";
- ReplayChWithMalformedInner(ch, kTlsAlertDecodeError,
+ "020101001c00024001fe0900970001000308fde4163c5c6e8bb600206321bdc543a23d47"
+ "7a7104ba69177cb722927c6c485117df4a077b8e82167f0b0066103d9aac7e5fc4ef990b"
+ "2ce38593589f7f6ba043847d7db6c9136adb811f63b956d56e6ca8cbe6864e3fc43a3bc5"
+ "94a332d4d63833e411c89ef14af63b5cd18c7adee99ffd1ad3112449ea18d6650bbaca66"
+ "528f7e4146fafbf338c27cf89b145a55022b26a3";
+ ReplayChWithMalformedInner(ch, kTlsAlertIllegalParameter,
SSL_ERROR_MISSING_ECH_EXTENSION,
- SSL_ERROR_DECODE_ERROR_ALERT);
+ SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
+}
+
+// Replay a CH for which CHInner contains both an ECH and ech_is_inner
+// extension.
+TEST_F(TlsConnectStreamTls13Ech, InnerWithEchAndEchIsInner) {
+ std::string ch =
+ "0100015c030383fb49c98b62bcdf04cbbae418dd684f8f9512f40fca6861ba40555269a9"
+ "789f0000061301130313020100012d00000010000e00000b7075626c69632e6e616d65ff"
+ "01000100000a00140012001d00170018001901000101010201030104003300260024001d"
+ "00201e3d35a6755b7dddf7e481359429e9677baaa8dd99569c2bf0b0f7ea56e68b12002b"
+ "0003020304000d0018001604030503060302030804080508060401050106010201002d00"
+ "020101001c00024001fe09009f0001000308fde4163c5c6e8bb6002090110b89c1ba6618"
+ "942ea7aae8c472c22e97f10bef7dd490bee50cc108082b48006eed016fa2b3e3419cf5ef"
+ "9b41ab9ecffa84a4b60e2f4cc710cf31c739d1f6f88b48207aaf7ccabdd744a25a8f2a38"
+ "029d1b133e9d990681cf08c07a255d9242b3a002bc0865935cbb609b2b1996fab0626cb0"
+ "2ece6544bbde0d3218333ffd95c383a41854b76b1a254bb346a2702b";
+ ReplayChWithMalformedInner(ch, kTlsAlertIllegalParameter,
+ SSL_ERROR_RX_MALFORMED_CLIENT_HELLO,
+ SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
+}
+
+TEST_F(TlsConnectStreamTls13, OuterWithEchAndEchIsInner) {
+ static uint8_t empty_buf[1] = {0};
+ DataBuffer empty(empty_buf, 0);
+
+ EnsureTlsSetup();
+ EXPECT_EQ(SECSuccess, SSL_EnableTls13GreaseEch(client_->ssl_fd(), PR_TRUE));
+ MakeTlsFilter<TlsExtensionAppender>(client_, kTlsHandshakeClientHello,
+ ssl_tls13_ech_is_inner_xtn, empty);
+ ConnectExpectAlert(server_, kTlsAlertIllegalParameter);
+ client_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
+ server_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_EXTENSION);
+}
+
+// Apply two ECHConfigs on the server. They are identical with the exception
+// of the public key: the first ECHConfig contains a public key for which we
+// lack the private value. Use an SSLInt function to zero all the config_ids
+// (client and server), then confirm that trial decryption works.
+TEST_F(TlsConnectStreamTls13Ech, EchConfigsTrialDecrypt) {
+ ScopedSECKEYPublicKey pub;
+ ScopedSECKEYPrivateKey priv;
+ EnsureTlsSetup();
+ ImportFixedEchKeypair(pub, priv);
+
+ const std::string two_configs_str =
+ "007EFE09003B000B7075626C69632E6E616D650020111111111111111111111111111111"
+ "1111111111111111111111111111111111002000040001000100640000fe09003B000B70"
+ "75626C69632E6E616D6500208756E2580C07C1D2FFCB662F5FADC6D6FF13DA85ABD7ADFE"
+ "CF984AAA102C1269002000040001000100640000";
+ const std::string second_config_str =
+ "003FFE09003B000B7075626C69632E6E616D6500208756E2580C07C1D2FFCB662F5FADC6"
+ "D6FF13DA85ABD7ADFECF984AAA102C1269002000040001000100640000";
+ std::vector<uint8_t> two_configs = hex_string_to_bytes(two_configs_str);
+ std::vector<uint8_t> second_config = hex_string_to_bytes(second_config_str);
+ ASSERT_EQ(SECSuccess,
+ SSL_SetServerEchConfigs(server_->ssl_fd(), pub.get(), priv.get(),
+ two_configs.data(), two_configs.size()));
+ ASSERT_EQ(SECSuccess,
+ SSL_SetClientEchConfigs(client_->ssl_fd(), second_config.data(),
+ second_config.size()));
+
+ ASSERT_EQ(SECSuccess, SSLInt_ZeroEchConfigIds(client_->ssl_fd()));
+ ASSERT_EQ(SECSuccess, SSLInt_ZeroEchConfigIds(server_->ssl_fd()));
+ client_->ExpectEch();
+ server_->ExpectEch();
+ Connect();
}
// An empty config_id should prompt an alert. We don't support
@@ -769,16 +849,17 @@ SSLHelloRetryRequestAction RetryEchHello(PRBool firstHello,
TEST_F(TlsConnectStreamTls13, EchAcceptWithHrr) {
ScopedSECKEYPublicKey pub;
ScopedSECKEYPrivateKey priv;
- DataBuffer record;
+ DataBuffer echconfig;
ConfigureSelfEncrypt();
EnsureTlsSetup();
TlsConnectTestBase::GenerateEchConfig(HpkeDhKemX25519Sha256, kDefaultSuites,
- kPublicName, 100, record, pub, priv);
+ kPublicName, 100, echconfig, pub, priv);
ASSERT_EQ(SECSuccess,
SSL_SetServerEchConfigs(server_->ssl_fd(), pub.get(), priv.get(),
- record.data(), record.len()));
- ASSERT_EQ(SECSuccess, SSL_SetClientEchConfigs(client_->ssl_fd(),
- record.data(), record.len()));
+ echconfig.data(), echconfig.len()));
+ ASSERT_EQ(SECSuccess,
+ SSL_SetClientEchConfigs(client_->ssl_fd(), echconfig.data(),
+ echconfig.len()));
client_->ExpectEch();
server_->ExpectEch();
client_->SetAuthCertificateCallback(AuthCompleteSuccess);
@@ -795,7 +876,7 @@ TEST_F(TlsConnectStreamTls13, EchAcceptWithHrr) {
MakeNewServer();
ASSERT_EQ(SECSuccess,
SSL_SetServerEchConfigs(server_->ssl_fd(), pub.get(), priv.get(),
- record.data(), record.len()));
+ echconfig.data(), echconfig.len()));
client_->ExpectEch();
server_->ExpectEch();
client_->SetAuthCertificateCallback(AuthCompleteSuccess);
@@ -805,6 +886,42 @@ TEST_F(TlsConnectStreamTls13, EchAcceptWithHrr) {
SendReceive();
}
+// Send GREASE ECH in CH1. CH2 must send exactly the same GREASE ECH contents.
+TEST_F(TlsConnectStreamTls13, GreaseEchHrrMatches) {
+ ConfigureSelfEncrypt();
+ EnsureTlsSetup();
+ size_t cb_called = 0;
+ EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(
+ server_->ssl_fd(), RetryEchHello, &cb_called));
+
+ EXPECT_EQ(SECSuccess, SSL_EnableTls13GreaseEch(client_->ssl_fd(),
+ PR_TRUE)); // GREASE
+ auto capture = MakeTlsFilter<TlsExtensionCapture>(
+ client_, ssl_tls13_encrypted_client_hello_xtn);
+
+ // Start the handshake.
+ client_->StartConnect();
+ server_->StartConnect();
+ client_->Handshake(); // Send CH1
+ EXPECT_TRUE(capture->captured());
+ DataBuffer ch1_grease = capture->extension();
+
+ server_->Handshake();
+ MakeNewServer();
+ capture = MakeTlsFilter<TlsExtensionCapture>(
+ client_, ssl_tls13_encrypted_client_hello_xtn);
+
+ EXPECT_FALSE(capture->captured());
+ client_->Handshake(); // Send CH2
+ EXPECT_TRUE(capture->captured());
+ EXPECT_EQ(ch1_grease, capture->extension());
+
+ EXPECT_EQ(1U, cb_called);
+ server_->StartConnect();
+ Handshake();
+ CheckConnected();
+}
+
// Fail to decrypt CH2. Unlike CH1, this generates an alert.
TEST_F(TlsConnectStreamTls13, EchFailDecryptCH2) {
EnsureTlsSetup();
@@ -848,9 +965,9 @@ TEST_F(TlsConnectStreamTls13, EchHrrChangeCh2OfferingYN) {
MakeNewServer();
EXPECT_EQ(SECSuccess, SSL_EnableTls13GreaseEch(client_->ssl_fd(),
PR_FALSE)); // Don't GREASE
- ExpectAlert(server_, kTlsAlertIllegalParameter);
+ ExpectAlert(server_, kTlsAlertMissingExtension);
Handshake();
- client_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
+ client_->CheckErrorCode(SSL_ERROR_MISSING_EXTENSION_ALERT);
server_->CheckErrorCode(SSL_ERROR_BAD_2ND_CLIENT_HELLO);
EXPECT_EQ(1U, cb_called);
}
@@ -858,20 +975,47 @@ TEST_F(TlsConnectStreamTls13, EchHrrChangeCh2OfferingYN) {
TEST_F(TlsConnectStreamTls13, EchHrrChangeCh2OfferingNY) {
ConfigureSelfEncrypt();
EnsureTlsSetup();
+ SetupEch(client_, server_);
size_t cb_called = 0;
EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(
server_->ssl_fd(), RetryEchHello, &cb_called));
- EXPECT_EQ(SECSuccess, SSL_EnableTls13GreaseEch(client_->ssl_fd(),
- PR_FALSE)); // Don't GREASE
+ MakeTlsFilter<TlsExtensionDropper>(client_,
+ ssl_tls13_encrypted_client_hello_xtn);
// Start the handshake.
client_->StartConnect();
server_->StartConnect();
client_->Handshake();
server_->Handshake();
MakeNewServer();
- EXPECT_EQ(SECSuccess, SSL_EnableTls13GreaseEch(client_->ssl_fd(),
- PR_TRUE)); // Send GREASE
+ client_->ClearFilter(); // Let the second ECH offering through.
+ ExpectAlert(server_, kTlsAlertIllegalParameter);
+ Handshake();
+ client_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
+ server_->CheckErrorCode(SSL_ERROR_BAD_2ND_CLIENT_HELLO);
+ EXPECT_EQ(1U, cb_called);
+}
+
+// Change the ECHCipherSuite between CH1 and CH2. Expect alert.
+TEST_F(TlsConnectStreamTls13, EchHrrChangeCipherSuite) {
+ ConfigureSelfEncrypt();
+ EnsureTlsSetup();
+ SetupEch(client_, server_);
+
+ size_t cb_called = 0;
+ EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(
+ server_->ssl_fd(), RetryEchHello, &cb_called));
+ // Start the handshake and trigger HRR.
+ client_->StartConnect();
+ server_->StartConnect();
+ client_->Handshake();
+ server_->Handshake();
+ MakeNewServer();
+
+ // Damage the first byte of the ciphersuite (offset 0)
+ MakeTlsFilter<TlsExtensionDamager>(client_,
+ ssl_tls13_encrypted_client_hello_xtn, 0);
+
ExpectAlert(server_, kTlsAlertIllegalParameter);
Handshake();
client_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
@@ -884,17 +1028,18 @@ TEST_F(TlsConnectStreamTls13, EchHrrChangeCh2OfferingNY) {
TEST_F(TlsConnectStreamTls13, EchAcceptWithHrrAndPsk) {
ScopedSECKEYPublicKey pub;
ScopedSECKEYPrivateKey priv;
- DataBuffer record;
+ DataBuffer echconfig;
ConfigureSelfEncrypt();
EnsureTlsSetup();
TlsConnectTestBase::GenerateEchConfig(HpkeDhKemX25519Sha256, kDefaultSuites,
- kPublicName, 100, record, pub, priv);
+ kPublicName, 100, echconfig, pub, priv);
ASSERT_EQ(SECSuccess,
SSL_SetServerEchConfigs(server_->ssl_fd(), pub.get(), priv.get(),
- record.data(), record.len()));
- ASSERT_EQ(SECSuccess, SSL_SetClientEchConfigs(client_->ssl_fd(),
- record.data(), record.len()));
+ echconfig.data(), echconfig.len()));
+ ASSERT_EQ(SECSuccess,
+ SSL_SetClientEchConfigs(client_->ssl_fd(), echconfig.data(),
+ echconfig.len()));
client_->ExpectEch();
server_->ExpectEch();
@@ -922,7 +1067,7 @@ TEST_F(TlsConnectStreamTls13, EchAcceptWithHrrAndPsk) {
MakeNewServer();
ASSERT_EQ(SECSuccess,
SSL_SetServerEchConfigs(server_->ssl_fd(), pub.get(), priv.get(),
- record.data(), record.len()));
+ echconfig.data(), echconfig.len()));
client_->ExpectEch();
server_->ExpectEch();
EXPECT_EQ(SECSuccess,
@@ -940,7 +1085,7 @@ TEST_F(TlsConnectStreamTls13, EchAcceptWithHrrAndPsk) {
TEST_F(TlsConnectStreamTls13Ech, EchRejectWithHrr) {
ScopedSECKEYPublicKey pub;
ScopedSECKEYPrivateKey priv;
- DataBuffer record;
+ DataBuffer echconfig;
ConfigureSelfEncrypt();
EnsureTlsSetup();
SetupForEchRetry();
@@ -966,19 +1111,20 @@ TEST_F(TlsConnectStreamTls13Ech, EchRejectWithHrr) {
EXPECT_EQ(1U, cb_called);
}
-// Reject ECH on CH1 and (HRR) CH2. PSKs are no longer allowed
-// in CHOuter, but can still make sure the handshake succeeds.
-// (prompting ech_required at the completion).
+// Reject ECH on CH1 and CH2. PSKs are no longer allowed
+// in CHOuter, but we can still make sure the handshake succeeds.
+// This prompts an ech_required alert when the handshake completes.
TEST_F(TlsConnectStreamTls13, EchRejectWithHrrAndPsk) {
ScopedSECKEYPublicKey pub;
ScopedSECKEYPrivateKey priv;
- DataBuffer record;
+ DataBuffer echconfig;
ConfigureSelfEncrypt();
EnsureTlsSetup();
TlsConnectTestBase::GenerateEchConfig(HpkeDhKemX25519Sha256, kDefaultSuites,
- kPublicName, 100, record, pub, priv);
- ASSERT_EQ(SECSuccess, SSL_SetClientEchConfigs(client_->ssl_fd(),
- record.data(), record.len()));
+ kPublicName, 100, echconfig, pub, priv);
+ ASSERT_EQ(SECSuccess,
+ SSL_SetClientEchConfigs(client_->ssl_fd(), echconfig.data(),
+ echconfig.len()));
size_t cb_called = 0;
EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(
@@ -1113,7 +1259,7 @@ TEST_F(TlsConnectStreamTls13, EchRejectUnknownCriticalExtension) {
EnsureTlsSetup();
ScopedSECKEYPublicKey pub;
ScopedSECKEYPrivateKey priv;
- DataBuffer record;
+ DataBuffer echconfig;
DataBuffer crit_rec;
DataBuffer len_buf;
uint64_t tmp;
@@ -1124,9 +1270,9 @@ TEST_F(TlsConnectStreamTls13, EchRejectUnknownCriticalExtension) {
DataBuffer non_crit_exts(extensions, sizeof(extensions));
TlsConnectTestBase::GenerateEchConfig(HpkeDhKemX25519Sha256, kSuiteChaCha,
- kPublicName, 100, record, pub, priv);
- record.Truncate(record.len() - 2); // Eat the empty extensions.
- crit_rec.Assign(record);
+ kPublicName, 100, echconfig, pub, priv);
+ echconfig.Truncate(echconfig.len() - 2); // Eat the empty extensions.
+ crit_rec.Assign(echconfig);
ASSERT_TRUE(crit_rec.Read(0, 2, &tmp));
len_buf.Write(0, tmp + crit_exts.len() - 2, 2); // two bytes of length
crit_rec.Splice(len_buf, 0, 2);
@@ -1138,13 +1284,13 @@ TEST_F(TlsConnectStreamTls13, EchRejectUnknownCriticalExtension) {
crit_rec.Splice(len_buf, 4, 2);
len_buf.Truncate(0);
- ASSERT_TRUE(record.Read(0, 2, &tmp));
+ ASSERT_TRUE(echconfig.Read(0, 2, &tmp));
len_buf.Write(0, tmp + non_crit_exts.len() - 2, 2);
- record.Append(non_crit_exts);
- record.Splice(len_buf, 0, 2);
- ASSERT_TRUE(record.Read(4, 2, &tmp));
+ echconfig.Append(non_crit_exts);
+ echconfig.Splice(len_buf, 0, 2);
+ ASSERT_TRUE(echconfig.Read(4, 2, &tmp));
len_buf.Write(0, tmp + non_crit_exts.len() - 2, 2);
- record.Splice(len_buf, 4, 2);
+ echconfig.Splice(len_buf, 4, 2);
EXPECT_EQ(SECFailure,
SSL_SetClientEchConfigs(client_->ssl_fd(), crit_rec.data(),
@@ -1162,8 +1308,9 @@ TEST_F(TlsConnectStreamTls13, EchRejectUnknownCriticalExtension) {
// Now try a variant with non-critical extensions, it should work.
Reset();
EnsureTlsSetup();
- EXPECT_EQ(SECSuccess, SSL_SetClientEchConfigs(client_->ssl_fd(),
- record.data(), record.len()));
+ EXPECT_EQ(SECSuccess,
+ SSL_SetClientEchConfigs(client_->ssl_fd(), echconfig.data(),
+ echconfig.len()));
filter = MakeTlsFilter<TlsExtensionCapture>(
client_, ssl_tls13_encrypted_client_hello_xtn);
StartConnect();
@@ -1577,6 +1724,34 @@ TEST_F(TlsConnectStreamTls13, EchOuterExtensionsInCHOuter) {
server_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
}
+// At draft-09: If a CH containing the ech_is_inner extension is received, the
+// server acts as backend server in split-mode by responding with the ECH
+// acceptance signal. The signal value itself depends on the handshake secret,
+// which we've broken by appending ech_is_inner. For now, just check that the
+// server negotiates ech_is_inner (which is what triggers sending the signal).
+TEST_F(TlsConnectStreamTls13, EchBackendAcceptance) {
+ DataBuffer ch_buf;
+ static uint8_t empty_buf[1] = {0};
+ DataBuffer empty(empty_buf, 0);
+
+ EnsureTlsSetup();
+ StartConnect();
+ EXPECT_EQ(SECSuccess, SSL_EnableTls13GreaseEch(client_->ssl_fd(), PR_FALSE));
+ MakeTlsFilter<TlsExtensionAppender>(client_, kTlsHandshakeClientHello,
+ ssl_tls13_ech_is_inner_xtn, empty);
+
+ EXPECT_EQ(SECSuccess, SSL_EnableTls13BackendEch(server_->ssl_fd(), PR_TRUE));
+ client_->Handshake();
+ server_->Handshake();
+
+ ExpectAlert(client_, kTlsAlertBadRecordMac);
+ client_->Handshake();
+ EXPECT_EQ(TlsAgent::STATE_ERROR, client_->state());
+ EXPECT_EQ(PR_TRUE, SSLInt_ExtensionNegotiated(server_->ssl_fd(),
+ ssl_tls13_ech_is_inner_xtn));
+ server_->ExpectReceiveAlert(kTlsAlertCloseNotify, kTlsAlertWarning);
+}
+
INSTANTIATE_TEST_SUITE_P(EchAgentTest, TlsAgentEchTest,
::testing::Combine(TlsConnectTestBase::kTlsVariantsAll,
TlsConnectTestBase::kTlsV13));
diff --git a/lib/ssl/ssl3con.c b/lib/ssl/ssl3con.c
index cd36b80a5..fd491f896 100644
--- a/lib/ssl/ssl3con.c
+++ b/lib/ssl/ssl3con.c
@@ -70,6 +70,9 @@ PRBool ssl_IsRsaPssSignatureScheme(SSLSignatureScheme scheme);
PRBool ssl_IsRsaeSignatureScheme(SSLSignatureScheme scheme);
PRBool ssl_IsRsaPkcs1SignatureScheme(SSLSignatureScheme scheme);
PRBool ssl_IsDsaSignatureScheme(SSLSignatureScheme scheme);
+static SECStatus ssl3_UpdateDefaultHandshakeHashes(sslSocket *ss,
+ const unsigned char *b,
+ unsigned int l);
const PRUint8 ssl_hello_retry_random[] = {
0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11,
@@ -3848,11 +3851,18 @@ ssl3_InitHandshakeHashes(sslSocket *ss)
if (ss->ssl3.hs.hashType != handshake_hash_record &&
ss->ssl3.hs.messages.len > 0) {
- if (ssl3_UpdateHandshakeHashes(ss, ss->ssl3.hs.messages.buf,
- ss->ssl3.hs.messages.len) != SECSuccess) {
+ /* When doing ECH, ssl3_UpdateHandshakeHashes will store outer messages into
+ * the both the outer and inner transcripts. ssl3_UpdateDefaultHandshakeHashes
+ * uses only the default context (which is the outer when doing ECH). */
+ if (ssl3_UpdateDefaultHandshakeHashes(ss, ss->ssl3.hs.messages.buf,
+ ss->ssl3.hs.messages.len) != SECSuccess) {
return SECFailure;
}
- sslBuffer_Clear(&ss->ssl3.hs.messages);
+ /* When doing ECH, deriving accept_confirmation requires all messages
+ * up to SH, then a synthetic SH. Don't free the buffers just yet. */
+ if (!ss->ssl3.hs.echHpkeCtx) {
+ sslBuffer_Clear(&ss->ssl3.hs.messages);
+ }
}
if (ss->ssl3.hs.shaEchInner &&
ss->ssl3.hs.echInnerMessages.len > 0) {
@@ -3861,7 +3871,9 @@ ssl3_InitHandshakeHashes(sslSocket *ss)
ssl_MapLowLevelError(SSL_ERROR_DIGEST_FAILURE);
return SECFailure;
}
- sslBuffer_Clear(&ss->ssl3.hs.echInnerMessages);
+ if (!ss->ssl3.hs.echHpkeCtx) {
+ sslBuffer_Clear(&ss->ssl3.hs.echInnerMessages);
+ }
}
return SECSuccess;
@@ -3893,52 +3905,28 @@ ssl3_RestartHandshakeHashes(sslSocket *ss)
}
}
-/* For TLS 1.3 EncryptedClientHello, add the provided buffer to the
- * given hash context. This is only needed for the initial CH,
- * after which ssl3_UpdateHandshakeHashes will update both contexts
- * until ssl3_CoalesceEchHandshakeHashes. */
+/* Add the provided bytes to the handshake hash context. When doing
+ * TLS 1.3 ECH, |target| may be provided to specify only the inner/outer
+ * transcript, else the input is added to both contexts. This happens
+ * only on the client. On the server, only the default context is used. */
SECStatus
-ssl3_UpdateExplicitHandshakeTranscript(sslSocket *ss, const unsigned char *b,
- unsigned int l, sslBuffer *target)
-{
- PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
- PORT_Assert(ss->vrange.max >= SSL_LIBRARY_VERSION_TLS_1_3);
- if (ss->sec.isServer) {
- /* Only the client maintains two states at the outset. */
- PORT_Assert(target != &ss->ssl3.hs.echInnerMessages);
- }
- return sslBuffer_Append(target, b, l);
-}
-static SECStatus
-ssl3_UpdateOuterHandshakeHashes(sslSocket *ss, const unsigned char *b,
- unsigned int l)
+ssl3_UpdateHandshakeHashesInt(sslSocket *ss, const unsigned char *b,
+ unsigned int l, sslBuffer *target)
{
- return ssl3_UpdateExplicitHandshakeTranscript(ss, b, l,
- &ss->ssl3.hs.messages);
-}
-static SECStatus
-ssl3_UpdateInnerHandshakeHashes(sslSocket *ss, const unsigned char *b,
- unsigned int l)
-{
- return ssl3_UpdateExplicitHandshakeTranscript(ss, b, l,
- &ss->ssl3.hs.echInnerMessages);
-}
-/*
- * Handshake messages
- */
-/* Called from ssl3_InitHandshakeHashes()
-** ssl3_AppendHandshake()
-** ssl3_HandleV2ClientHello()
-** ssl3_HandleHandshakeMessage()
-** Caller must hold the ssl3Handshake lock.
-*/
-SECStatus
-ssl3_UpdateHandshakeHashes(sslSocket *ss, const unsigned char *b, unsigned int l)
-{
- SECStatus rv = SECSuccess;
+ SECStatus rv = SECSuccess;
+ PRBool explicit = (target != NULL);
+ PRBool appendToEchInner = !ss->sec.isServer &&
+ ss->ssl3.hs.echHpkeCtx &&
+ !explicit;
PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
+ PORT_Assert(target != &ss->ssl3.hs.echInnerMessages ||
+ !ss->sec.isServer);
+ if (target == NULL) {
+ /* Default context. */
+ target = &ss->ssl3.hs.messages;
+ }
/* With TLS 1.3, and versions TLS.1.1 and older, we keep the hash(es)
* always up to date. However, we must initially buffer the handshake
* messages, until we know what to do.
@@ -3950,15 +3938,14 @@ ssl3_UpdateHandshakeHashes(sslSocket *ss, const unsigned char *b, unsigned int l
* and never update the hash, because the hash function we must use for
* certificate_verify might be different from the hash function we use
* when signing other handshake hashes. */
-
if (ss->ssl3.hs.hashType == handshake_hash_unknown ||
ss->ssl3.hs.hashType == handshake_hash_record) {
- rv = sslBuffer_Append(&ss->ssl3.hs.messages, b, l);
+ rv = sslBuffer_Append(target, b, l);
if (rv != SECSuccess) {
return SECFailure;
}
- if (!ss->sec.isServer && ss->ssl3.hs.echHpkeCtx) {
- return ssl3_UpdateInnerHandshakeHashes(ss, b, l);
+ if (appendToEchInner) {
+ return sslBuffer_Append(&ss->ssl3.hs.echInnerMessages, b, l);
}
return SECSuccess;
}
@@ -3967,10 +3954,20 @@ ssl3_UpdateHandshakeHashes(sslSocket *ss, const unsigned char *b, unsigned int l
if (ss->ssl3.hs.hashType == handshake_hash_single) {
PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3);
- rv = PK11_DigestOp(ss->ssl3.hs.sha, b, l);
- if (rv != SECSuccess) {
- ssl_MapLowLevelError(SSL_ERROR_DIGEST_FAILURE);
- return rv;
+ if (target == &ss->ssl3.hs.messages) {
+ rv = PK11_DigestOp(ss->ssl3.hs.sha, b, l);
+ if (rv != SECSuccess) {
+ ssl_MapLowLevelError(SSL_ERROR_DIGEST_FAILURE);
+ return rv;
+ }
+ }
+ if (ss->ssl3.hs.shaEchInner &&
+ (target == &ss->ssl3.hs.echInnerMessages || !explicit)) {
+ rv = PK11_DigestOp(ss->ssl3.hs.shaEchInner, b, l);
+ if (rv != SECSuccess) {
+ ssl_MapLowLevelError(SSL_ERROR_DIGEST_FAILURE);
+ return rv;
+ }
}
} else if (ss->ssl3.hs.hashType == handshake_hash_combo) {
rv = PK11_DigestOp(ss->ssl3.hs.md5, b, l);
@@ -3987,6 +3984,37 @@ ssl3_UpdateHandshakeHashes(sslSocket *ss, const unsigned char *b, unsigned int l
return rv;
}
+static SECStatus
+ssl3_UpdateDefaultHandshakeHashes(sslSocket *ss, const unsigned char *b,
+ unsigned int l)
+{
+ return ssl3_UpdateHandshakeHashesInt(ss, b, l,
+ &ss->ssl3.hs.messages);
+}
+
+static SECStatus
+ssl3_UpdateInnerHandshakeHashes(sslSocket *ss, const unsigned char *b,
+ unsigned int l)
+{
+ return ssl3_UpdateHandshakeHashesInt(ss, b, l,
+ &ss->ssl3.hs.echInnerMessages);
+}
+
+/*
+ * Handshake messages
+ */
+/* Called from ssl3_InitHandshakeHashes()
+** ssl3_AppendHandshake()
+** ssl3_HandleV2ClientHello()
+** ssl3_HandleHandshakeMessage()
+** Caller must hold the ssl3Handshake lock.
+*/
+SECStatus
+ssl3_UpdateHandshakeHashes(sslSocket *ss, const unsigned char *b, unsigned int l)
+{
+ return ssl3_UpdateHandshakeHashesInt(ss, b, l, NULL);
+}
+
SECStatus
ssl3_UpdatePostHandshakeHashes(sslSocket *ss, const unsigned char *b, unsigned int l)
{
@@ -5513,7 +5541,7 @@ ssl3_SendClientHello(sslSocket *ss, sslClientHelloType type)
if (rv != SECSuccess) {
goto loser; /* code set */
}
- rv = ssl3_UpdateOuterHandshakeHashes(ss, chBuf.buf, chBuf.len);
+ rv = ssl3_UpdateDefaultHandshakeHashes(ss, chBuf.buf, chBuf.len);
if (rv != SECSuccess) {
goto loser; /* code set */
}
@@ -7064,11 +7092,6 @@ ssl3_HandleServerHello(sslSocket *ss, PRUint8 *b, PRUint32 length)
return SECSuccess;
}
- rv = tls13_MaybeHandleEchSignal(ss);
- if (rv != SECSuccess) {
- goto alert_loser;
- }
-
rv = ssl3_HandleParsedExtensions(ss, ssl_hs_server_hello);
ssl3_DestroyRemoteExtensions(&ss->ssl3.hs.remoteExtensions);
if (rv != SECSuccess) {
@@ -7082,7 +7105,7 @@ ssl3_HandleServerHello(sslSocket *ss, PRUint8 *b, PRUint32 length)
}
if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) {
- rv = tls13_HandleServerHelloPart2(ss);
+ rv = tls13_HandleServerHelloPart2(ss, savedMsg, savedLength);
if (rv != SECSuccess) {
errCode = PORT_GetError();
goto loser;
@@ -7093,6 +7116,7 @@ ssl3_HandleServerHello(sslSocket *ss, PRUint8 *b, PRUint32 length)
goto loser;
}
+ ss->ssl3.hs.preliminaryInfo |= ssl_preinfo_ech;
return SECSuccess;
alert_loser:
@@ -8628,13 +8652,6 @@ ssl_GenerateServerRandom(sslSocket *ss)
return SECFailure;
}
- if (ss->ssl3.hs.echAccepted) {
- rv = tls13_WriteServerEchSignal(ss);
- if (rv != SECSuccess) {
- return SECFailure;
- }
- }
-
if (ss->version == ss->vrange.max) {
return SECSuccess;
}
@@ -9775,6 +9792,15 @@ ssl_ConstructServerHello(sslSocket *ss, PRBool helloRetry,
}
}
+ if (!helloRetry && ssl3_ExtensionNegotiated(ss, ssl_tls13_ech_is_inner_xtn)) {
+ /* Signal ECH acceptance if we handled handled both CHOuter/CHInner (i.e.
+ * in shared mode), or if we received a CHInner in split/backend mode. */
+ if (ss->ssl3.hs.echAccepted || ss->opt.enableTls13BackendEch) {
+ return tls13_WriteServerEchSignal(ss, SSL_BUFFER_BASE(messageBuf),
+ SSL_BUFFER_LEN(messageBuf));
+ }
+ }
+
return SECSuccess;
}
@@ -12284,7 +12310,7 @@ ssl_HashHandshakeMessageDefault(sslSocket *ss, SSLHandshakeType ct,
const PRUint8 *b, PRUint32 length)
{
return ssl_HashHandshakeMessageInt(ss, ct, ss->ssl3.hs.recvMessageSeq,
- b, length, ssl3_UpdateOuterHandshakeHashes);
+ b, length, ssl3_UpdateDefaultHandshakeHashes);
}
SECStatus
ssl_HashHandshakeMessageEchInner(sslSocket *ss, SSLHandshakeType ct,
@@ -13847,6 +13873,7 @@ ssl3_DestroySSL3Info(sslSocket *ss)
/* TLS 1.3 ECH state. */
PK11_HPKE_DestroyContext(ss->ssl3.hs.echHpkeCtx, PR_TRUE);
PORT_Free((void *)ss->ssl3.hs.echPublicName); /* CONST */
+ sslBuffer_Clear(&ss->ssl3.hs.greaseEchBuf);
}
/*
diff --git a/lib/ssl/ssl3ext.c b/lib/ssl/ssl3ext.c
index 78c2b901a..199cf459a 100644
--- a/lib/ssl/ssl3ext.c
+++ b/lib/ssl/ssl3ext.c
@@ -15,6 +15,7 @@
#include "sslimpl.h"
#include "sslproto.h"
#include "ssl3exthandle.h"
+#include "tls13ech.h"
#include "tls13err.h"
#include "tls13exthandle.h"
#include "tls13subcerts.h"
@@ -54,6 +55,7 @@ static const ssl3ExtensionHandler clientHelloHandlers[] = {
{ ssl_tls13_psk_key_exchange_modes_xtn, &tls13_ServerHandlePskModesXtn },
{ ssl_tls13_cookie_xtn, &tls13_ServerHandleCookieXtn },
{ ssl_tls13_post_handshake_auth_xtn, &tls13_ServerHandlePostHandshakeAuthXtn },
+ { ssl_tls13_ech_is_inner_xtn, &tls13_ServerHandleEchIsInnerXtn },
{ ssl_record_size_limit_xtn, &ssl_HandleRecordSizeLimitXtn },
{ 0, NULL }
};
@@ -1020,12 +1022,8 @@ ssl3_DestroyExtensionData(TLSExtensionData *xtnData)
PORT_Free(xtnData->advertised);
tls13_DestroyDelegatedCredential(xtnData->peerDelegCred);
- /* ECH State */
- SECITEM_FreeItem(&xtnData->innerCh, PR_FALSE);
- SECITEM_FreeItem(&xtnData->echSenderPubKey, PR_FALSE);
- SECITEM_FreeItem(&xtnData->echConfigId, PR_FALSE);
- SECITEM_FreeItem(&xtnData->echRetryConfigs, PR_FALSE);
- xtnData->echRetryConfigsValid = PR_FALSE;
+ tls13_DestroyEchXtnState(xtnData->ech);
+ xtnData->ech = NULL;
}
/* Free everything that has been allocated and then reset back to
diff --git a/lib/ssl/ssl3ext.h b/lib/ssl/ssl3ext.h
index 45510041e..685a7a99c 100644
--- a/lib/ssl/ssl3ext.h
+++ b/lib/ssl/ssl3ext.h
@@ -131,13 +131,9 @@ struct TLSExtensionDataStr {
* rather through tls13_DestoryPskList(). */
sslPsk *selectedPsk;
- /* ECH working state. */
- SECItem innerCh; /* Server: "payload value of ClientECH. */
- SECItem echSenderPubKey; /* Server: "enc value of ClientECH, required for CHInner decryption. */
- SECItem echConfigId; /* Server: "config_id" value of ClientECH. */
- PRUint32 echCipherSuite; /* Server: "cipher_suite" value of ClientECH. */
- SECItem echRetryConfigs; /* Client: Retry_configs from ServerEncryptedCH. */
- PRBool echRetryConfigsValid; /* Client: Permits retry_configs to be extracted. */
+ /* ECH working state. Non-null when a valid Encrypted Client Hello extension
+ * was received. */
+ sslEchXtnState *ech;
};
typedef struct TLSExtensionStr {
diff --git a/lib/ssl/sslexp.h b/lib/ssl/sslexp.h
index a02f0f351..8bacc6b42 100644
--- a/lib/ssl/sslexp.h
+++ b/lib/ssl/sslexp.h
@@ -509,6 +509,14 @@ typedef SECStatus(PR_CALLBACK *SSLResumptionTokenCallback)(
SSL_EXPERIMENTAL_API("SSL_EnableTls13GreaseEch", \
(PRFileDesc * _fd, PRBool _enabled), (fd, enabled))
+/* If |enabled|, a server receiving a Client Hello containing the ech_is_inner
+ * (and not encrypted_client_hello) extension will respond with the ECH
+ * acceptance signal. This signals the client to continue with the inner
+ * transcript rather than outer. */
+#define SSL_EnableTls13BackendEch(fd, enabled) \
+ SSL_EXPERIMENTAL_API("SSL_EnableTls13BackendEch", \
+ (PRFileDesc * _fd, PRBool _enabled), (fd, enabled))
+
/* Called by the client after an initial ECH connection fails with
* SSL_ERROR_ECH_RETRY_WITH_ECH. Returns compatible ECHConfigs, which
* are configured via SetClientEchConfigs for an ECH retry attempt.
diff --git a/lib/ssl/sslimpl.h b/lib/ssl/sslimpl.h
index a126cb8c3..1b7dfb107 100644
--- a/lib/ssl/sslimpl.h
+++ b/lib/ssl/sslimpl.h
@@ -36,9 +36,9 @@
typedef struct sslSocketStr sslSocket;
typedef struct sslNamedGroupDefStr sslNamedGroupDef;
-typedef struct sslEsniKeysStr sslEsniKeys;
typedef struct sslEchConfigStr sslEchConfig;
typedef struct sslEchConfigContentsStr sslEchConfigContents;
+typedef struct sslEchXtnStateStr sslEchXtnState;
typedef struct sslPskStr sslPsk;
typedef struct sslDelegatedCredentialStr sslDelegatedCredential;
typedef struct sslEphemeralKeyPairStr sslEphemeralKeyPair;
@@ -287,6 +287,7 @@ typedef struct sslOptionsStr {
unsigned int enableDtls13VersionCompat : 1;
unsigned int suppressEndOfEarlyData : 1;
unsigned int enableTls13GreaseEch : 1;
+ unsigned int enableTls13BackendEch : 1;
} sslOptions;
typedef enum { sslHandshakingUndetermined = 0,
@@ -748,6 +749,7 @@ typedef struct SSL3HandshakeStateStr {
HpkeContext *echHpkeCtx; /* Client/Server: HPKE context for ECH. */
const char *echPublicName; /* Client: If rejected, the ECHConfig.publicName to
* use for certificate verification. */
+ sslBuffer greaseEchBuf; /* Client: Remember GREASE ECH, as advertised, for CH2 (HRR case). */
} SSL3HandshakeState;
@@ -1122,8 +1124,9 @@ struct sslSocketStr {
SSLProtocolVariant protocolVariant;
/* TLS 1.3 Encrypted Client Hello. */
- PRCList echConfigs; /* Client/server: Must not change while hs is in-progress. */
- SECKEYPublicKey *echPubKey; /* Server: The ECH keypair used in HPKE setup */
+ PRCList echConfigs; /* Client/server: Must not change while hs
+ * is in-progress. */
+ SECKEYPublicKey *echPubKey; /* Server: The ECH keypair used in HPKE. */
SECKEYPrivateKey *echPrivKey; /* As above. */
/* Anti-replay for TLS 1.3 0-RTT. */
@@ -1948,6 +1951,8 @@ SECStatus SSLExp_DestroyMaskingContext(SSLMaskingContext *ctx);
SECStatus SSLExp_EnableTls13GreaseEch(PRFileDesc *fd, PRBool enabled);
+SECStatus SSLExp_EnableTls13BackendEch(PRFileDesc *fd, PRBool enabled);
+
SEC_END_PROTOS
#if defined(XP_UNIX) || defined(XP_OS2) || defined(XP_BEOS)
diff --git a/lib/ssl/sslsecur.c b/lib/ssl/sslsecur.c
index 162fc66d0..2c9a4dbf9 100644
--- a/lib/ssl/sslsecur.c
+++ b/lib/ssl/sslsecur.c
@@ -183,6 +183,7 @@ SSL_ResetHandshake(PRFileDesc *s, PRBool asServer)
PORT_Assert(ss->ssl3.hs.echPublicName);
PORT_Free((void *)ss->ssl3.hs.echPublicName); /* CONST */
ss->ssl3.hs.echPublicName = NULL;
+ sslBuffer_Clear(&ss->ssl3.hs.greaseEchBuf);
}
if (!ss->TCPconnected)
diff --git a/lib/ssl/sslsock.c b/lib/ssl/sslsock.c
index e075e23c8..b698d8f43 100644
--- a/lib/ssl/sslsock.c
+++ b/lib/ssl/sslsock.c
@@ -93,7 +93,8 @@ static sslOptions ssl_defaults = {
.enableV2CompatibleHello = PR_FALSE,
.enablePostHandshakeAuth = PR_FALSE,
.suppressEndOfEarlyData = PR_FALSE,
- .enableTls13GreaseEch = PR_FALSE
+ .enableTls13GreaseEch = PR_FALSE,
+ .enableTls13BackendEch = PR_FALSE
};
/*
@@ -4293,6 +4294,7 @@ struct {
EXP(DestroyAead),
EXP(DestroyMaskingContext),
EXP(DestroyResumptionTokenInfo),
+ EXP(EnableTls13BackendEch),
EXP(EnableTls13GreaseEch),
EXP(EncodeEchConfig),
EXP(GetCurrentEpoch),
@@ -4372,6 +4374,17 @@ SSLExp_EnableTls13GreaseEch(PRFileDesc *fd, PRBool enabled)
}
SECStatus
+SSLExp_EnableTls13BackendEch(PRFileDesc *fd, PRBool enabled)
+{
+ sslSocket *ss = ssl_FindSocket(fd);
+ if (!ss) {
+ return SECFailure;
+ }
+ ss->opt.enableTls13BackendEch = enabled;
+ return SECSuccess;
+}
+
+SECStatus
SSLExp_SetDtls13VersionWorkaround(PRFileDesc *fd, PRBool enabled)
{
sslSocket *ss = ssl_FindSocket(fd);
diff --git a/lib/ssl/sslt.h b/lib/ssl/sslt.h
index f1713069b..1d5c4d179 100644
--- a/lib/ssl/sslt.h
+++ b/lib/ssl/sslt.h
@@ -545,8 +545,9 @@ typedef enum {
ssl_next_proto_nego_xtn = 13172, /* Deprecated. */
ssl_renegotiation_info_xtn = 0xff01,
ssl_tls13_short_header_xtn = 0xff03, /* Deprecated. */
+ ssl_tls13_ech_is_inner_xtn = 0xda09,
ssl_tls13_outer_extensions_xtn = 0xfd00,
- ssl_tls13_encrypted_client_hello_xtn = 0xfe08,
+ ssl_tls13_encrypted_client_hello_xtn = 0xfe09,
ssl_tls13_encrypted_sni_xtn = 0xffce, /* Deprecated. */
} SSLExtensionType;
diff --git a/lib/ssl/tls13con.c b/lib/ssl/tls13con.c
index a10962981..1347f3fe2 100644
--- a/lib/ssl/tls13con.c
+++ b/lib/ssl/tls13con.c
@@ -61,7 +61,7 @@ tls13_DeriveSecretWrap(sslSocket *ss, PK11SymKey *key,
const char *suffix,
const char *keylogLabel,
PK11SymKey **dest);
-static SECStatus
+SECStatus
tls13_DeriveSecret(sslSocket *ss, PK11SymKey *key,
const char *label,
unsigned int labelLen,
@@ -1184,13 +1184,12 @@ tls13_DeriveEarlySecrets(sslSocket *ss)
}
static SECStatus
-tls13_ComputeHandshakeSecrets(sslSocket *ss)
+tls13_ComputeHandshakeSecret(sslSocket *ss)
{
SECStatus rv;
PK11SymKey *derivedSecret = NULL;
PK11SymKey *newSecret = NULL;
-
- SSL_TRC(5, ("%d: TLS13[%d]: compute handshake secrets (%s)",
+ SSL_TRC(5, ("%d: TLS13[%d]: compute handshake secret (%s)",
SSL_GETPID(), ss->fd, SSL_ROLE(ss)));
/* If no PSK, generate the default early secret. */
@@ -1205,7 +1204,7 @@ tls13_ComputeHandshakeSecrets(sslSocket *ss)
PORT_Assert(ss->ssl3.hs.currentSecret);
PORT_Assert(ss->ssl3.hs.dheSecret);
- /* Expand before we extract. */
+ /* Derive-Secret(., "derived", "") */
rv = tls13_DeriveSecretNullHash(ss, ss->ssl3.hs.currentSecret,
kHkdfLabelDerivedSecret,
strlen(kHkdfLabelDerivedSecret),
@@ -1215,18 +1214,32 @@ tls13_ComputeHandshakeSecrets(sslSocket *ss)
return rv;
}
+ /* HKDF-Extract(ECDHE, .) = Handshake Secret */
rv = tls13_HkdfExtract(derivedSecret, ss->ssl3.hs.dheSecret,
tls13_GetHash(ss), &newSecret);
PK11_FreeSymKey(derivedSecret);
-
if (rv != SECSuccess) {
LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE);
return rv;
}
- PK11_FreeSymKey(ss->ssl3.hs.dheSecret);
- ss->ssl3.hs.dheSecret = NULL;
+
PK11_FreeSymKey(ss->ssl3.hs.currentSecret);
ss->ssl3.hs.currentSecret = newSecret;
+ return SECSuccess;
+}
+
+static SECStatus
+tls13_ComputeHandshakeSecrets(sslSocket *ss)
+{
+ SECStatus rv;
+ PK11SymKey *derivedSecret = NULL;
+ PK11SymKey *newSecret = NULL;
+
+ PK11_FreeSymKey(ss->ssl3.hs.dheSecret);
+ ss->ssl3.hs.dheSecret = NULL;
+
+ SSL_TRC(5, ("%d: TLS13[%d]: compute handshake secrets (%s)",
+ SSL_GETPID(), ss->fd, SSL_ROLE(ss)));
/* Now compute |*HsTrafficSecret| */
rv = tls13_DeriveSecretWrap(ss, ss->ssl3.hs.currentSecret,
@@ -1865,22 +1878,16 @@ tls13_HandleClientHelloPart2(sslSocket *ss,
PRINT_BUF(50, (ss, "Client sent cookie",
ss->xtnData.cookie.data, ss->xtnData.cookie.len));
- rv = tls13_RecoverHashState(ss, ss->xtnData.cookie.data,
- ss->xtnData.cookie.len,
- &previousCipherSuite,
- &previousGroup,
- &previousEchOffered);
+ rv = tls13_HandleHrrCookie(ss, ss->xtnData.cookie.data,
+ ss->xtnData.cookie.len,
+ &previousCipherSuite,
+ &previousGroup,
+ &previousEchOffered,
+ NULL, NULL, NULL, NULL, PR_TRUE);
if (rv != SECSuccess) {
FATAL_ERROR(ss, SSL_ERROR_BAD_2ND_CLIENT_HELLO, illegal_parameter);
goto loser;
}
-
- /* CH1/CH2 must either both include ECH, or both exclude it. */
- if ((ss->xtnData.echConfigId.len > 0) != previousEchOffered) {
- FATAL_ERROR(ss, SSL_ERROR_BAD_2ND_CLIENT_HELLO,
- illegal_parameter);
- goto loser;
- }
}
/* Now merge the ClientHello into the hash state. */
@@ -1936,6 +1943,13 @@ tls13_HandleClientHelloPart2(sslSocket *ss,
goto loser;
}
+ /* CH1/CH2 must either both include ECH, or both exclude it. */
+ if (previousEchOffered != (ss->xtnData.ech != NULL)) {
+ FATAL_ERROR(ss, SSL_ERROR_BAD_2ND_CLIENT_HELLO,
+ previousEchOffered ? missing_extension : illegal_parameter);
+ goto loser;
+ }
+
/* If we requested a new key share, check that the client provided just
* one of the right type. */
if (previousGroup) {
@@ -2825,6 +2839,11 @@ tls13_SendServerHelloSequence(sslSocket *ss)
return SECFailure;
}
+ rv = tls13_ComputeHandshakeSecret(ss);
+ if (rv != SECSuccess) {
+ return SECFailure; /* error code is set. */
+ }
+
rv = ssl3_SendServerHello(ss);
if (rv != SECSuccess) {
return rv; /* err code is set. */
@@ -2909,7 +2928,7 @@ tls13_SendServerHelloSequence(sslSocket *ss)
}
SECStatus
-tls13_HandleServerHelloPart2(sslSocket *ss)
+tls13_HandleServerHelloPart2(sslSocket *ss, const PRUint8 *savedMsg, PRUint32 savedLength)
{
SECStatus rv;
sslSessionID *sid = ss->sec.ci.sid;
@@ -2991,6 +3010,17 @@ tls13_HandleServerHelloPart2(sslSocket *ss)
if (rv != SECSuccess) {
return SECFailure;
}
+
+ rv = tls13_ComputeHandshakeSecret(ss);
+ if (rv != SECSuccess) {
+ return SECFailure; /* error code is set. */
+ }
+
+ rv = tls13_MaybeHandleEchSignal(ss, savedMsg, savedLength);
+ if (rv != SECSuccess) {
+ return SECFailure; /* error code is set. */
+ }
+
rv = tls13_ComputeHandshakeSecrets(ss);
if (rv != SECSuccess) {
return SECFailure; /* error code is set. */
@@ -4960,15 +4990,16 @@ tls13_FinishHandshake(sslSocket *ss)
TLS13_SET_HS_STATE(ss, idle_handshake);
- if (offeredEch &&
- !ssl3_ExtensionNegotiated(ss, ssl_tls13_encrypted_client_hello_xtn)) {
+ PORT_Assert(ss->ssl3.hs.echAccepted ==
+ ssl3_ExtensionNegotiated(ss, ssl_tls13_encrypted_client_hello_xtn));
+ if (offeredEch && !ss->ssl3.hs.echAccepted) {
SSL3_SendAlert(ss, alert_fatal, ech_required);
- /* "If [one, none] of the values contains a supported version, the client can
+ /* "If [one, none] of the retry_configs contains a supported version, the client can
* regard ECH as securely [replaced, disabled] by the server." */
- if (ss->xtnData.echRetryConfigs.len) {
+ if (ss->xtnData.ech && ss->xtnData.ech->retryConfigs.len) {
PORT_SetError(SSL_ERROR_ECH_RETRY_WITH_ECH);
- ss->xtnData.echRetryConfigsValid = PR_TRUE;
+ ss->xtnData.ech->retryConfigsValid = PR_TRUE;
} else {
PORT_SetError(SSL_ERROR_ECH_RETRY_WITHOUT_ECH);
}
@@ -5520,6 +5551,7 @@ static const struct {
hello_retry_request) },
{ ssl_record_size_limit_xtn, _M2(client_hello, encrypted_extensions) },
{ ssl_tls13_encrypted_client_hello_xtn, _M2(client_hello, encrypted_extensions) },
+ { ssl_tls13_ech_is_inner_xtn, _M1(client_hello) },
{ ssl_tls13_outer_extensions_xtn, _M_NONE /* Encoding/decoding only */ },
{ ssl_tls13_post_handshake_auth_xtn, _M1(client_hello) }
};
diff --git a/lib/ssl/tls13con.h b/lib/ssl/tls13con.h
index ae0b4ae33..8c648ac30 100644
--- a/lib/ssl/tls13con.h
+++ b/lib/ssl/tls13con.h
@@ -76,7 +76,7 @@ SECStatus tls13_HandleClientHelloPart2(sslSocket *ss,
sslSessionID *sid,
const PRUint8 *msg,
unsigned int len);
-SECStatus tls13_HandleServerHelloPart2(sslSocket *ss);
+SECStatus tls13_HandleServerHelloPart2(sslSocket *ss, const PRUint8 *savedMsg, PRUint32 savedLength);
SECStatus tls13_HandlePostHelloHandshakeMessage(sslSocket *ss, PRUint8 *b,
PRUint32 length);
SECStatus tls13_ConstructHelloRetryRequest(sslSocket *ss,
diff --git a/lib/ssl/tls13ech.c b/lib/ssl/tls13ech.c
index a42bda01a..7b6c2f0a4 100644
--- a/lib/ssl/tls13ech.c
+++ b/lib/ssl/tls13ech.c
@@ -14,14 +14,22 @@
#include "ssl3exthandle.h"
#include "tls13ech.h"
#include "tls13exthandle.h"
+#include "tls13hashstate.h"
#include "tls13hkdf.h"
extern SECStatus
-ssl3_UpdateExplicitHandshakeTranscript(sslSocket *ss, const unsigned char *b,
- unsigned int l, sslBuffer *transcriptBuf);
+ssl3_UpdateHandshakeHashesInt(sslSocket *ss, const unsigned char *b,
+ unsigned int l, sslBuffer *transcriptBuf);
extern SECStatus
ssl3_HandleClientHelloPreamble(sslSocket *ss, PRUint8 **b, PRUint32 *length, SECItem *sidBytes,
SECItem *cookieBytes, SECItem *suites, SECItem *comps);
+extern SECStatus
+tls13_DeriveSecret(sslSocket *ss, PK11SymKey *key,
+ const char *label,
+ unsigned int labelLen,
+ const SSL3Hashes *hashes,
+ PK11SymKey **dest,
+ SSLHashType hash);
void
tls13_DestroyEchConfig(sslEchConfig *config)
@@ -48,6 +56,19 @@ tls13_DestroyEchConfigs(PRCList *list)
}
}
+void
+tls13_DestroyEchXtnState(sslEchXtnState *state)
+{
+ if (!state) {
+ return;
+ }
+ SECITEM_FreeItem(&state->innerCh, PR_FALSE);
+ SECITEM_FreeItem(&state->senderPubKey, PR_FALSE);
+ SECITEM_FreeItem(&state->configId, PR_FALSE);
+ SECITEM_FreeItem(&state->retryConfigs, PR_FALSE);
+ PORT_ZFree(state, sizeof(*state));
+}
+
SECStatus
tls13_CopyEchConfigs(PRCList *oConfigs, PRCList *configs)
{
@@ -86,6 +107,7 @@ tls13_CopyEchConfigs(PRCList *oConfigs, PRCList *configs)
newConfig->contents.kdfId = config->contents.kdfId;
newConfig->contents.aeadId = config->contents.aeadId;
newConfig->contents.maxNameLen = config->contents.maxNameLen;
+ newConfig->version = config->version;
PORT_Memcpy(newConfig->configId, config->configId, sizeof(newConfig->configId));
PR_APPEND_LINK(&newConfig->link, configs);
}
@@ -127,7 +149,7 @@ tls13_DigestEchConfig(const sslEchConfig *cfg, PRUint8 *digest, size_t maxDigest
params.pInfo = CONST_CAST(CK_BYTE, hHkdfInfoEchConfigID);
params.ulInfoLen = strlen(hHkdfInfoEchConfigID);
derived = PK11_DeriveWithFlags(configKey, CKM_HKDF_DATA,
- &paramsi, CKM_HKDF_DERIVE, CKA_DERIVE, 32,
+ &paramsi, CKM_HKDF_DERIVE, CKA_DERIVE, 8,
CKF_SIGN | CKF_VERIFY);
rv = PK11_ExtractKeyValue(derived);
@@ -182,8 +204,11 @@ tls13_DecodeEchConfigContents(const sslReadBuffer *rawConfig,
if (rv != SECSuccess) {
goto loser;
}
- /* Make sure the public name doesn't contain any NULLs.
- * TODO: Just store the SECItem instead. */
+
+ if (tmpBuf.len == 0) {
+ PORT_SetError(SSL_ERROR_RX_MALFORMED_ECH_CONFIG);
+ goto loser;
+ }
for (tmpn = 0; tmpn < tmpBuf.len; tmpn++) {
if (tmpBuf.buf[tmpn] == '\0') {
PORT_SetError(SSL_ERROR_RX_MALFORMED_ECH_CONFIG);
@@ -193,7 +218,6 @@ tls13_DecodeEchConfigContents(const sslReadBuffer *rawConfig,
contents.publicName = PORT_ZAlloc(tmpBuf.len + 1);
if (!contents.publicName) {
- PORT_SetError(SSL_ERROR_RX_MALFORMED_ECH_CONFIG);
goto loser;
}
PORT_Memcpy(contents.publicName, (PRUint8 *)tmpBuf.buf, tmpBuf.len);
@@ -430,8 +454,8 @@ SSLExp_EncodeEchConfig(const char *publicName, const PRUint32 *hpkeSuites,
PRUint8 tmpBuf[66]; // Large enough for an EC public key, currently only X25519.
unsigned int tmpLen;
- if (!publicName || PORT_Strlen(publicName) == 0 || !hpkeSuites ||
- hpkeSuiteCount == 0 || !pubKey || maxNameLen == 0 || !out || !outlen) {
+ if (!publicName || !hpkeSuites || hpkeSuiteCount == 0 ||
+ !pubKey || maxNameLen == 0 || !out || !outlen) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
@@ -535,12 +559,18 @@ SSLExp_GetEchRetryConfigs(PRFileDesc *fd, SECItem *retryConfigs)
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
- if (!ss->xtnData.echRetryConfigsValid) {
+
+ /* We don't distinguish between "handshake completed
+ * without retry configs", and "handshake not completed".
+ * An application should only call this after receiving a
+ * RETRY_WITH_ECH error code, which implies retry_configs. */
+ if (!ss->xtnData.ech || !ss->xtnData.ech->retryConfigsValid) {
PORT_SetError(SSL_ERROR_HANDSHAKE_NOT_COMPLETED);
return SECFailure;
}
+
/* May be empty. */
- rv = SECITEM_CopyItem(NULL, &out, &ss->xtnData.echRetryConfigs);
+ rv = SECITEM_CopyItem(NULL, &out, &ss->xtnData.ech->retryConfigs);
if (rv == SECFailure) {
return SECFailure;
}
@@ -571,8 +601,8 @@ SSLExp_RemoveEchConfigs(PRFileDesc *fd)
}
/* Also remove any retry_configs and handshake context. */
- if (ss->xtnData.echRetryConfigs.len) {
- SECITEM_FreeItem(&ss->xtnData.echRetryConfigs, PR_FALSE);
+ if (ss->xtnData.ech && ss->xtnData.ech->retryConfigs.len) {
+ SECITEM_FreeItem(&ss->xtnData.ech->retryConfigs, PR_FALSE);
}
if (ss->ssl3.hs.echHpkeCtx) {
@@ -704,14 +734,7 @@ tls13_ClientSetupEch(sslSocket *ss, sslClientHelloType type)
HpkeContext *cx = NULL;
SECKEYPublicKey *pkR = NULL;
SECItem hpkeInfo = { siBuffer, NULL, 0 };
- PK11SymKey *hrrPsk = NULL;
sslEchConfig *cfg = NULL;
- const SECItem kEchHrrInfoItem = { siBuffer,
- (unsigned char *)kHpkeInfoEchHrr,
- strlen(kHpkeInfoEchHrr) };
- const SECItem kEchHrrPskLabelItem = { siBuffer,
- (unsigned char *)kHpkeLabelHrrPsk,
- strlen(kHpkeLabelHrrPsk) };
if (PR_CLIST_IS_EMPTY(&ss->echConfigs) ||
!ssl_ShouldSendSNIExtension(ss, ss->url) ||
@@ -739,20 +762,12 @@ tls13_ClientSetupEch(sslSocket *ss, sslClientHelloType type)
cfg->contents.aeadId, NULL, NULL);
break;
case client_hello_retry:
- PORT_Assert(ss->ssl3.hs.echHpkeCtx && ss->ssl3.hs.echPublicName);
- rv = PK11_HPKE_ExportSecret(ss->ssl3.hs.echHpkeCtx,
- &kEchHrrInfoItem, 32, &hrrPsk);
- if (rv != SECSuccess) {
- goto loser;
+ if (!ss->ssl3.hs.echHpkeCtx || !ss->ssl3.hs.echPublicName) {
+ FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
+ return SECFailure;
}
-
- PK11_HPKE_DestroyContext(ss->ssl3.hs.echHpkeCtx, PR_TRUE);
- PORT_Free((void *)ss->ssl3.hs.echPublicName); /* CONST */
- ss->ssl3.hs.echHpkeCtx = NULL;
- ss->ssl3.hs.echPublicName = NULL;
- cx = PK11_HPKE_NewContext(cfg->contents.kemId, cfg->contents.kdfId,
- cfg->contents.aeadId, hrrPsk, &kEchHrrPskLabelItem);
- break;
+ /* Nothing else to do. */
+ return SECSuccess;
default:
PORT_Assert(0);
goto loser;
@@ -779,11 +794,9 @@ tls13_ClientSetupEch(sslSocket *ss, sslClientHelloType type)
goto loser;
}
- if (!ss->ssl3.hs.helloRetry) {
- rv = ssl3_GetNewRandom(ss->ssl3.hs.client_inner_random);
- if (rv != SECSuccess) {
- goto loser; /* code set */
- }
+ rv = ssl3_GetNewRandom(ss->ssl3.hs.client_inner_random);
+ if (rv != SECSuccess) {
+ goto loser; /* code set */
}
/* If ECH is rejected, the application will use SSLChannelInfo
@@ -794,22 +807,21 @@ tls13_ClientSetupEch(sslSocket *ss, sslClientHelloType type)
}
ss->ssl3.hs.echHpkeCtx = cx;
- PK11_FreeSymKey(hrrPsk);
SECKEY_DestroyPublicKey(pkR);
SECITEM_FreeItem(&hpkeInfo, PR_FALSE);
return SECSuccess;
loser:
PK11_HPKE_DestroyContext(cx, PR_TRUE);
- PK11_FreeSymKey(hrrPsk);
SECKEY_DestroyPublicKey(pkR);
SECITEM_FreeItem(&hpkeInfo, PR_FALSE);
+ PORT_Assert(PORT_GetError() != 0);
return SECFailure;
}
/*
* enum {
- * encrypted_client_hello(0xfe08), (65535)
+ * encrypted_client_hello(0xfe09), (65535)
* } ExtensionType;
*
* struct {
@@ -871,13 +883,22 @@ tls13_EncryptClientHello(sslSocket *ss, sslBuffer *outerAAD, sslBuffer *chInner)
if (rv != SECSuccess) {
goto loser;
}
- rv = sslBuffer_AppendVariable(chInner, cfg->configId, sizeof(cfg->configId), 1);
- if (rv != SECSuccess) {
- goto loser;
- }
- rv = sslBuffer_AppendVariable(chInner, hpkeEnc->data, hpkeEnc->len, 2);
- if (rv != SECSuccess) {
- goto loser;
+
+ if (!ss->ssl3.hs.helloRetry) {
+ rv = sslBuffer_AppendVariable(chInner, cfg->configId, sizeof(cfg->configId), 1);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ rv = sslBuffer_AppendVariable(chInner, hpkeEnc->data, hpkeEnc->len, 2);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ } else {
+ /* one byte for empty configId, two for empty Enc. */
+ rv = sslBuffer_AppendNumber(chInner, 0, 3);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
}
rv = sslBuffer_AppendVariable(chInner, chCt->data, chCt->len, 2);
if (rv != SECSuccess) {
@@ -892,14 +913,14 @@ loser:
}
SECStatus
-tls13_GetMatchingEchConfig(const sslSocket *ss, HpkeKdfId kdf, HpkeAeadId aead,
- const SECItem *configId, sslEchConfig **cfg)
+tls13_GetMatchingEchConfigs(const sslSocket *ss, HpkeKdfId kdf, HpkeAeadId aead,
+ const SECItem *configId, const sslEchConfig *cur, sslEchConfig **next)
{
- sslEchConfig *candidate;
PRINT_BUF(50, (ss, "Server GetMatchingEchConfig with digest:",
configId->data, configId->len));
- for (PRCList *cur_p = PR_LIST_HEAD(&ss->echConfigs);
+ /* If |cur|, resume the search at that node, else the list head. */
+ for (PRCList *cur_p = cur ? ((PRCList *)cur)->next : PR_LIST_HEAD(&ss->echConfigs);
cur_p != &ss->echConfigs;
cur_p = PR_NEXT_LINK(cur_p)) {
sslEchConfig *echConfig = (sslEchConfig *)cur_p;
@@ -907,139 +928,17 @@ tls13_GetMatchingEchConfig(const sslSocket *ss, HpkeKdfId kdf, HpkeAeadId aead,
PORT_Memcmp(echConfig->configId, configId->data, sizeof(echConfig->configId))) {
continue;
}
- candidate = (sslEchConfig *)PR_LIST_HEAD(&ss->echConfigs);
- if (candidate->contents.aeadId != aead ||
- candidate->contents.kdfId != kdf) {
- continue;
+ if (echConfig->contents.aeadId == aead &&
+ echConfig->contents.kdfId == kdf) {
+ *next = echConfig;
+ return SECSuccess;
}
- *cfg = candidate;
- return SECSuccess;
}
- SSL_TRC(50, ("%d: TLS13[%d]: Server found no matching ECHConfig",
- SSL_GETPID(), ss->fd));
-
- *cfg = NULL;
+ *next = NULL;
return SECSuccess;
}
-/* This is unfortunate in that it requires a second decryption of the cookie.
- * This is largely copied from tls13hashstate.c as HRR handling is still in flux.
- * TODO: Consolidate this code no later than -09. */
-/* struct {
- * uint8 indicator = 0xff; // To disambiguate from tickets.
- * uint16 cipherSuite; // Selected cipher suite.
- * uint16 keyShare; // Requested key share group (0=none)
- * opaque applicationToken<0..65535>; // Application token
- * opaque echHrrPsk<0..255>; // Encrypted ClientHello HRR PSK
- * opaque echConfigId<0..255>; // ECH config ID selected in CH1, to decrypt the CH2 ECH payload.
- * opaque ch_hash[rest_of_buffer]; // H(ClientHello)
- * } CookieInner;
- */
-SECStatus
-tls13_GetEchInfoFromCookie(sslSocket *ss, const TLSExtension *hrrCookie, PK11SymKey **echHrrPsk, SECItem *echConfigId)
-{
- SECStatus rv;
- PK11SymKey *hrrKey = NULL;
- PRUint64 tmpn;
- sslReadBuffer tmpReader = { 0 };
- PK11SlotInfo *slot = NULL;
- unsigned char plaintext[1024];
- unsigned int plaintextLen = 0;
- SECItem hrrPskItem = { siBuffer, NULL, 0 };
- SECItem hrrCookieData = { siBuffer, NULL, 0 };
- SECItem saveHrrCookieData = hrrCookieData;
- SECItem previousEchConfigId = { siBuffer, NULL, 0 };
-
- /* Copy the extension data so as to not consume it in the handler.
- * The extension handler walks the pointer, so save a copy to free. */
- rv = SECITEM_CopyItem(NULL, &hrrCookieData, &hrrCookie->data);
- if (rv != SECSuccess) {
- goto loser;
- }
- saveHrrCookieData = hrrCookieData;
-
- rv = tls13_ServerHandleCookieXtn(ss, &ss->xtnData, &hrrCookieData);
- if (rv != SECSuccess) {
- goto loser;
- }
-
- rv = ssl_SelfEncryptUnprotect(ss, ss->xtnData.cookie.data, ss->xtnData.cookie.len,
- plaintext, &plaintextLen, sizeof(plaintext));
- if (rv != SECSuccess) {
- goto loser;
- }
-
- sslReader reader = SSL_READER(plaintext, plaintextLen);
-
- /* Should start with 0xff. */
- rv = sslRead_ReadNumber(&reader, 1, &tmpn);
- if ((rv != SECSuccess) || (tmpn != 0xff)) {
- rv = SECFailure;
- goto loser;
- }
- rv = sslRead_ReadNumber(&reader, 2, &tmpn);
- if (rv != SECSuccess) {
- goto loser;
- }
- /* The named group, if any. */
- rv = sslRead_ReadNumber(&reader, 2, &tmpn);
- if (rv != SECSuccess) {
- goto loser;
- }
- /* Application token. */
- rv = sslRead_ReadNumber(&reader, 2, &tmpn);
- if (rv != SECSuccess) {
- goto loser;
- }
- rv = sslRead_Read(&reader, tmpn, &tmpReader);
- if (rv != SECSuccess) {
- goto loser;
- }
-
- /* ECH Config ID */
- rv = sslRead_ReadVariable(&reader, 1, &tmpReader);
- if (rv != SECSuccess) {
- goto loser;
- }
- rv = SECITEM_MakeItem(NULL, &previousEchConfigId,
- tmpReader.buf, tmpReader.len);
- if (rv != SECSuccess) {
- goto loser;
- }
-
- /* ECH HRR key. */
- rv = sslRead_ReadVariable(&reader, 1, &tmpReader);
- if (rv != SECSuccess) {
- goto loser;
- }
- if (tmpReader.len) {
- slot = PK11_GetInternalSlot();
- if (!slot) {
- rv = SECFailure;
- goto loser;
- }
- hrrPskItem.len = tmpReader.len;
- hrrPskItem.data = CONST_CAST(PRUint8, tmpReader.buf);
- hrrKey = PK11_ImportSymKey(slot, CKM_HKDF_KEY_GEN, PK11_OriginUnwrap,
- CKA_DERIVE, &hrrPskItem, NULL);
- PK11_FreeSlot(slot);
- if (!hrrKey) {
- rv = SECFailure;
- goto loser;
- }
- }
- *echConfigId = previousEchConfigId;
- *echHrrPsk = hrrKey;
- SECITEM_FreeItem(&saveHrrCookieData, PR_FALSE);
- return SECSuccess;
-
-loser:
- SECITEM_FreeItem(&previousEchConfigId, PR_FALSE);
- SECITEM_FreeItem(&saveHrrCookieData, PR_FALSE);
- return SECFailure;
-}
-
/* Given a CH with extensions, copy from the start up to the extensions
* into |writer| and return the extensions themselves in |extensions|.
* If |explicitSid|, place this value into |writer| as the SID. Else,
@@ -1110,19 +1009,55 @@ tls13_CopyChPreamble(sslReader *reader, const SECItem *explicitSid, sslBuffer *w
return SECSuccess;
}
+/*
+ * struct {
+ * HpkeKdfId kdfId; // ClientECH.cipher_suite.kdf
+ * HpkeAeadId aeadId; // ClientECH.cipher_suite.aead
+ * opaque config_id<0..255>; // ClientECH.config_id
+ * opaque enc<1..2^16-1>; // ClientECH.enc
+ * opaque outer_hello<1..2^24-1>;
+ * } ClientHelloOuterAAD;
+ */
static SECStatus
-tls13_MakeChOuterAAD(const SECItem *outer, sslBuffer *outerAAD)
+tls13_MakeChOuterAAD(sslSocket *ss, const SECItem *outer, SECItem *outerAAD)
{
SECStatus rv;
sslBuffer aad = SSL_BUFFER_EMPTY;
- sslReadBuffer aadXtns;
+ sslReadBuffer aadXtns = { 0 };
sslReader chReader = SSL_READER(outer->data, outer->len);
PRUint64 tmpn;
- sslReadBuffer tmpvar;
+ sslReadBuffer tmpvar = { 0 };
unsigned int offset;
- unsigned int preambleLen;
+ unsigned int savedOffset;
+ PORT_Assert(ss->xtnData.ech);
+
+ rv = sslBuffer_AppendNumber(&aad, ss->xtnData.ech->kdfId, 2);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ rv = sslBuffer_AppendNumber(&aad, ss->xtnData.ech->aeadId, 2);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
- rv = sslBuffer_Skip(&aad, 4, NULL);
+ if (!ss->ssl3.hs.helloRetry) {
+ rv = sslBuffer_AppendVariable(&aad, ss->xtnData.ech->configId.data,
+ ss->xtnData.ech->configId.len, 1);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ rv = sslBuffer_AppendVariable(&aad, ss->xtnData.ech->senderPubKey.data,
+ ss->xtnData.ech->senderPubKey.len, 2);
+ } else {
+ /* 1B config_id length, 2B enc length. */
+ rv = sslBuffer_AppendNumber(&aad, 0, 3);
+ }
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ /* Skip 3 bytes for the CHOuter length. */
+ rv = sslBuffer_Skip(&aad, 3, &savedOffset);
if (rv != SECSuccess) {
goto loser;
}
@@ -1132,9 +1067,7 @@ tls13_MakeChOuterAAD(const SECItem *outer, sslBuffer *outerAAD)
if (rv != SECSuccess) {
goto loser;
}
-
sslReader xtnsReader = SSL_READER(aadXtns.buf, aadXtns.len);
- preambleLen = SSL_BUFFER_LEN(&aad);
/* Save room for extensions length. */
rv = sslBuffer_Skip(&aad, 2, &offset);
@@ -1165,22 +1098,18 @@ tls13_MakeChOuterAAD(const SECItem *outer, sslBuffer *outerAAD)
}
}
- rv = sslBuffer_InsertNumber(&aad, offset, SSL_BUFFER_LEN(&aad) - preambleLen - 2, 2);
+ rv = sslBuffer_InsertLength(&aad, offset, 2);
if (rv != SECSuccess) {
goto loser;
}
- /* Give it a message header. */
- rv = sslBuffer_InsertNumber(&aad, 0, ssl_hs_client_hello, 1);
+ rv = sslBuffer_InsertLength(&aad, savedOffset, 3);
if (rv != SECSuccess) {
goto loser;
}
- rv = sslBuffer_InsertLength(&aad, 1, 3);
- if (rv != SECSuccess) {
- goto loser;
- }
- *outerAAD = aad;
+ outerAAD->data = aad.buf;
+ outerAAD->len = aad.len;
return SECSuccess;
loser:
@@ -1189,55 +1118,47 @@ loser:
}
SECStatus
-tls13_OpenClientHelloInner(sslSocket *ss, const SECItem *outer, sslEchConfig *cfg, PK11SymKey *echHrrPsk, SECItem **chInner)
+tls13_OpenClientHelloInner(sslSocket *ss, const SECItem *outer, const SECItem *outerAAD, sslEchConfig *cfg, SECItem **chInner)
{
SECStatus rv;
- sslBuffer outerAAD = SSL_BUFFER_EMPTY;
HpkeContext *cx = NULL;
SECItem *decryptedChInner = NULL;
SECItem hpkeInfo = { siBuffer, NULL, 0 };
- SECItem outerAADItem = { siBuffer, NULL, 0 };
- const SECItem kEchHrrPskLabelItem = { siBuffer,
- (unsigned char *)kHpkeLabelHrrPsk,
- strlen(kHpkeLabelHrrPsk) };
SSL_TRC(50, ("%d: TLS13[%d]: Server opening ECH Inner%s", SSL_GETPID(),
ss->fd, ss->ssl3.hs.helloRetry ? " after HRR" : ""));
- cx = PK11_HPKE_NewContext(cfg->contents.kemId, cfg->contents.kdfId,
- cfg->contents.aeadId, echHrrPsk,
- echHrrPsk ? &kEchHrrPskLabelItem : NULL);
- if (!cx) {
- goto loser;
- }
-
- if (!SECITEM_AllocItem(NULL, &hpkeInfo, strlen(kHpkeInfoEch) + 1 + cfg->raw.len)) {
- goto loser;
- }
- PORT_Memcpy(&hpkeInfo.data[0], kHpkeInfoEch, strlen(kHpkeInfoEch));
- PORT_Memset(&hpkeInfo.data[strlen(kHpkeInfoEch)], 0, 1);
- PORT_Memcpy(&hpkeInfo.data[strlen(kHpkeInfoEch) + 1], cfg->raw.data, cfg->raw.len);
+ if (!ss->ssl3.hs.helloRetry) {
+ PORT_Assert(!ss->ssl3.hs.echHpkeCtx);
+ cx = PK11_HPKE_NewContext(cfg->contents.kemId, cfg->contents.kdfId,
+ cfg->contents.aeadId, NULL, NULL);
+ if (!cx) {
+ goto loser;
+ }
- rv = PK11_HPKE_SetupR(cx, ss->echPubKey, ss->echPrivKey,
- &ss->xtnData.echSenderPubKey, &hpkeInfo);
- if (rv != SECSuccess) {
- goto loser; /* code set */
- }
+ if (!SECITEM_AllocItem(NULL, &hpkeInfo, strlen(kHpkeInfoEch) + 1 + cfg->raw.len)) {
+ goto loser;
+ }
+ PORT_Memcpy(&hpkeInfo.data[0], kHpkeInfoEch, strlen(kHpkeInfoEch));
+ PORT_Memset(&hpkeInfo.data[strlen(kHpkeInfoEch)], 0, 1);
+ PORT_Memcpy(&hpkeInfo.data[strlen(kHpkeInfoEch) + 1], cfg->raw.data, cfg->raw.len);
- rv = tls13_MakeChOuterAAD(outer, &outerAAD);
- if (rv != SECSuccess) {
- goto loser; /* code set */
+ rv = PK11_HPKE_SetupR(cx, ss->echPubKey, ss->echPrivKey,
+ &ss->xtnData.ech->senderPubKey, &hpkeInfo);
+ if (rv != SECSuccess) {
+ goto loser; /* code set */
+ }
+ } else {
+ PORT_Assert(ss->ssl3.hs.echHpkeCtx);
+ cx = ss->ssl3.hs.echHpkeCtx;
}
- outerAADItem.data = outerAAD.buf;
- outerAADItem.len = outerAAD.len;
-
#ifndef UNSAFE_FUZZER_MODE
- rv = PK11_HPKE_Open(cx, &outerAADItem, &ss->xtnData.innerCh, &decryptedChInner);
+ rv = PK11_HPKE_Open(cx, outerAAD, &ss->xtnData.ech->innerCh, &decryptedChInner);
if (rv != SECSuccess) {
goto loser; /* code set */
}
#else
- rv = SECITEM_CopyItem(NULL, decryptedChInner, &ss->xtnData.innerCh);
+ rv = SECITEM_CopyItem(NULL, decryptedChInner, &ss->xtnData.ech->innerCh);
if (rv != SECSuccess) {
goto loser;
}
@@ -1248,14 +1169,15 @@ tls13_OpenClientHelloInner(sslSocket *ss, const SECItem *outer, sslEchConfig *cf
ss->ssl3.hs.echHpkeCtx = cx;
*chInner = decryptedChInner;
SECITEM_FreeItem(&hpkeInfo, PR_FALSE);
- sslBuffer_Clear(&outerAAD);
return SECSuccess;
loser:
SECITEM_FreeItem(decryptedChInner, PR_TRUE);
- PK11_HPKE_DestroyContext(cx, PR_TRUE);
SECITEM_FreeItem(&hpkeInfo, PR_FALSE);
- sslBuffer_Clear(&outerAAD);
+ if (cx != ss->ssl3.hs.echHpkeCtx) {
+ /* Don't double-free if it's already global. */
+ PK11_HPKE_DestroyContext(cx, PR_TRUE);
+ }
return SECFailure;
}
@@ -1289,7 +1211,7 @@ tls13_ConstructInnerExtensionsFromOuter(sslSocket *ss, sslBuffer *chOuterXtnsBuf
/* When offering the "encrypted_client_hello" extension in its
* ClientHelloOuter, the client MUST also offer an empty
* "encrypted_client_hello" extension in its ClientHelloInner. */
- rv = sslBuffer_AppendNumber(chInnerXtns, ssl_tls13_encrypted_client_hello_xtn, 2);
+ rv = sslBuffer_AppendNumber(chInnerXtns, ssl_tls13_ech_is_inner_xtn, 2);
if (rv != SECSuccess) {
goto loser;
}
@@ -1508,9 +1430,11 @@ tls13_ConstructClientHelloWithEch(sslSocket *ss, const sslSessionID *sid, PRBool
sslBuffer encodedChInner = SSL_BUFFER_EMPTY;
sslBuffer chInnerXtns = SSL_BUFFER_EMPTY;
sslBuffer pskXtn = SSL_BUFFER_EMPTY;
- sslBuffer outerAAD = SSL_BUFFER_EMPTY;
+ sslBuffer aad = SSL_BUFFER_EMPTY;
unsigned int encodedChLen;
unsigned int preambleLen;
+ const SECItem *hpkeEnc = NULL;
+ unsigned int savedOffset;
SSL_TRC(50, ("%d: TLS13[%d]: Constructing ECH inner", SSL_GETPID()));
/* Create the full (uncompressed) inner extensions and steal any PSK extension.
@@ -1551,8 +1475,8 @@ tls13_ConstructClientHelloWithEch(sslSocket *ss, const sslSessionID *sid, PRBool
goto loser;
}
- rv = ssl3_UpdateExplicitHandshakeTranscript(ss, chInner.buf, chInner.len,
- &ss->ssl3.hs.echInnerMessages);
+ rv = ssl3_UpdateHandshakeHashesInt(ss, chInner.buf, chInner.len,
+ &ss->ssl3.hs.echInnerMessages);
if (rv != SECSuccess) {
goto loser; /* code set */
}
@@ -1566,7 +1490,6 @@ tls13_ConstructClientHelloWithEch(sslSocket *ss, const sslSessionID *sid, PRBool
goto loser;
}
- /* TODO: Pad CHInner */
rv = tls13_EncodeClientHelloInner(ss, &chInner, &chInnerXtns, &encodedChInner);
if (rv != SECSuccess) {
goto loser;
@@ -1575,29 +1498,67 @@ tls13_ConstructClientHelloWithEch(sslSocket *ss, const sslSessionID *sid, PRBool
/* Pad the outer prior to appending ECH (for the AAD).
* Encoded extension size is (echCipherSuite + enc + configId + payload + tag).
* Post-encryption, we'll assert that this was correct. */
- encodedChLen = 4 + 33 + 34 + 2 + encodedChInner.len + 16;
+ encodedChLen = 4 + 1 + 2 + 2 + encodedChInner.len + 16;
+ if (!ss->ssl3.hs.helloRetry) {
+ encodedChLen += 8 + 32; /* configId || enc */
+ }
rv = ssl_InsertPaddingExtension(ss, chOuter->len + encodedChLen, chOuterXtnsBuf);
if (rv != SECSuccess) {
goto loser;
}
- /* Make the ClientHelloOuterAAD value, which is complete
- * chOuter minus encrypted_client_hello xtn. */
- rv = sslBuffer_Append(&outerAAD, chOuter->buf, chOuter->len);
+ PORT_Assert(!PR_CLIST_IS_EMPTY(&ss->echConfigs));
+ sslEchConfig *cfg = (sslEchConfig *)PR_LIST_HEAD(&ss->echConfigs);
+ rv = sslBuffer_AppendNumber(&aad, cfg->contents.kdfId, 2);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ rv = sslBuffer_AppendNumber(&aad, cfg->contents.aeadId, 2);
if (rv != SECSuccess) {
goto loser;
}
- rv = sslBuffer_AppendBufferVariable(&outerAAD, chOuterXtnsBuf, 2);
+
+ if (!ss->ssl3.hs.helloRetry) {
+ rv = sslBuffer_AppendVariable(&aad, cfg->configId, sizeof(cfg->configId), 1);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ hpkeEnc = PK11_HPKE_GetEncapPubKey(ss->ssl3.hs.echHpkeCtx);
+ if (!hpkeEnc) {
+ FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
+ goto loser;
+ }
+ rv = sslBuffer_AppendVariable(&aad, hpkeEnc->data, hpkeEnc->len, 2);
+ } else {
+ /* 1B config_id length, 2B enc length. */
+ rv = sslBuffer_AppendNumber(&aad, 0, 3);
+ }
if (rv != SECSuccess) {
goto loser;
}
- rv = sslBuffer_InsertLength(&outerAAD, 1, 3);
+
+ rv = sslBuffer_Skip(&aad, 3, &savedOffset);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ /* Skip the handshake header. */
+ PORT_Assert(chOuter->len > 4);
+ rv = sslBuffer_Append(&aad, &chOuter->buf[4], chOuter->len - 4);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ rv = sslBuffer_AppendBufferVariable(&aad, chOuterXtnsBuf, 2);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ rv = sslBuffer_InsertLength(&aad, savedOffset, 3);
if (rv != SECSuccess) {
goto loser;
}
/* Insert the encrypted_client_hello xtn and coalesce. */
- rv = tls13_EncryptClientHello(ss, &outerAAD, &encodedChInner);
+ rv = tls13_EncryptClientHello(ss, &aad, &encodedChInner);
if (rv != SECSuccess) {
goto loser;
}
@@ -1618,64 +1579,123 @@ tls13_ConstructClientHelloWithEch(sslSocket *ss, const sslSessionID *sid, PRBool
if (rv != SECSuccess) {
goto loser;
}
+ sslBuffer_Clear(&chInner);
+ sslBuffer_Clear(&encodedChInner);
+ sslBuffer_Clear(&chInnerXtns);
+ sslBuffer_Clear(&pskXtn);
+ sslBuffer_Clear(&aad);
+ return SECSuccess;
loser:
sslBuffer_Clear(&chInner);
sslBuffer_Clear(&encodedChInner);
sslBuffer_Clear(&chInnerXtns);
sslBuffer_Clear(&pskXtn);
- sslBuffer_Clear(&outerAAD);
- return rv;
+ sslBuffer_Clear(&aad);
+ PORT_Assert(PORT_GetError() != 0);
+ return SECFailure;
}
+/* Compute the ECH signal using the transcript (up to, excluding) Server Hello.
+ * We'll append an artificial SH (ServerHelloECHConf). The server sources
+ * this transcript prefix from ss->ssl3.hs.messages, as it never uses
+ * ss->ssl3.hs.echInnerMessages. The client uses the inner transcript, echInnerMessages. */
static SECStatus
-tls13_ComputeEchSignal(sslSocket *ss, PRUint8 *out)
+tls13_ComputeEchSignal(sslSocket *ss, const PRUint8 *sh, unsigned int shLen, PRUint8 *out)
{
SECStatus rv;
- PRUint8 derived[64];
- SECItem randItem = { siBuffer,
- ss->sec.isServer ? ss->ssl3.hs.client_random : ss->ssl3.hs.client_inner_random,
- SSL3_RANDOM_LENGTH };
- SSLHashType hashAlg = tls13_GetHash(ss);
- PK11SymKey *extracted = NULL;
- PK11SymKey *randKey = NULL;
- PK11SlotInfo *slot = PK11_GetInternalSlot();
- if (!slot) {
+ PK11SymKey *confirmationKey = NULL;
+ sslBuffer confMsgs = SSL_BUFFER_EMPTY;
+ sslBuffer *chSource = ss->sec.isServer ? &ss->ssl3.hs.messages : &ss->ssl3.hs.echInnerMessages;
+ SSL3Hashes hashes;
+ SECItem *confirmationBytes;
+ unsigned int offset = sizeof(SSL3ProtocolVersion) +
+ SSL3_RANDOM_LENGTH - TLS13_ECH_SIGNAL_LEN;
+ PORT_Assert(sh && shLen > offset);
+ PORT_Assert(TLS13_ECH_SIGNAL_LEN <= SSL3_RANDOM_LENGTH);
+
+ rv = sslBuffer_AppendBuffer(&confMsgs, chSource);
+ if (rv != SECSuccess) {
goto loser;
}
- randKey = PK11_ImportDataKey(slot, CKM_HKDF_DATA, PK11_OriginUnwrap,
- CKA_DERIVE, &randItem, NULL);
- if (!randKey) {
+ /* Re-create the message header. */
+ rv = sslBuffer_AppendNumber(&confMsgs, ssl_hs_server_hello, 1);
+ if (rv != SECSuccess) {
goto loser;
}
- rv = tls13_HkdfExtract(NULL, randKey, hashAlg, &extracted);
+ rv = sslBuffer_AppendNumber(&confMsgs, shLen, 3);
if (rv != SECSuccess) {
goto loser;
}
- rv = tls13_HkdfExpandLabelRaw(extracted, hashAlg, ss->ssl3.hs.server_random, 24,
- kHkdfInfoEchConfirm, strlen(kHkdfInfoEchConfirm),
- ss->protocolVariant, derived, TLS13_ECH_SIGNAL_LEN);
+ /* Copy the version and 24B of server_random. */
+ rv = sslBuffer_Append(&confMsgs, sh, offset);
if (rv != SECSuccess) {
goto loser;
}
- PORT_Memcpy(out, derived, TLS13_ECH_SIGNAL_LEN);
+ /* Zero the signal placeholder. */
+ rv = sslBuffer_AppendNumber(&confMsgs, 0, TLS13_ECH_SIGNAL_LEN);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ offset += TLS13_ECH_SIGNAL_LEN;
+
+ /* Use the remainder of SH. */
+ rv = sslBuffer_Append(&confMsgs, &sh[offset], shLen - offset);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ rv = tls13_ComputeHash(ss, &hashes, confMsgs.buf, confMsgs.len,
+ tls13_GetHash(ss));
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ /* accept_confirmation =
+ * Derive-Secret(Handshake Secret,
+ * "ech accept confirmation",
+ * ClientHelloInner...ServerHelloECHConf)
+ */
+ rv = tls13_DeriveSecret(ss, ss->ssl3.hs.currentSecret,
+ kHkdfInfoEchConfirm, strlen(kHkdfInfoEchConfirm),
+ &hashes, &confirmationKey, tls13_GetHash(ss));
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ rv = PK11_ExtractKeyValue(confirmationKey);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ confirmationBytes = PK11_GetKeyData(confirmationKey);
+ if (!confirmationBytes) {
+ rv = SECFailure;
+ PORT_SetError(SSL_ERROR_ECH_FAILED);
+ goto loser;
+ }
+ if (confirmationBytes->len < TLS13_ECH_SIGNAL_LEN) {
+ FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
+ goto loser;
+ }
SSL_TRC(50, ("%d: TLS13[%d]: %s computed ECH signal", SSL_GETPID(), ss->fd, SSL_ROLE(ss)));
PRINT_BUF(50, (ss, "", out, TLS13_ECH_SIGNAL_LEN));
- PK11_FreeSymKey(extracted);
- PK11_FreeSymKey(randKey);
- PK11_FreeSlot(slot);
+
+ PORT_Memcpy(out, confirmationBytes->data, TLS13_ECH_SIGNAL_LEN);
+ PK11_FreeSymKey(confirmationKey);
+ sslBuffer_Clear(&confMsgs);
+ sslBuffer_Clear(&ss->ssl3.hs.messages);
+ sslBuffer_Clear(&ss->ssl3.hs.echInnerMessages);
return SECSuccess;
loser:
- PK11_FreeSymKey(extracted);
- PK11_FreeSymKey(randKey);
- if (slot) {
- PK11_FreeSlot(slot);
- }
+ PK11_FreeSymKey(confirmationKey);
+ sslBuffer_Clear(&confMsgs);
+ sslBuffer_Clear(&ss->ssl3.hs.messages);
+ sslBuffer_Clear(&ss->ssl3.hs.echInnerMessages);
return SECFailure;
}
@@ -1695,6 +1715,8 @@ tls13_MaybeGreaseEch(sslSocket *ss, unsigned int preambleLen, sslBuffer *buf)
SECItem *rawData;
CK_HKDF_PARAMS params;
SECItem paramsi;
+ /* 1B aead determinant (don't send), 8B config_id, 32B enc, payload */
+ const int kNonPayloadLen = 41;
if (!ss->opt.enableTls13GreaseEch || ss->ssl3.hs.echHpkeCtx) {
return SECSuccess;
@@ -1705,6 +1727,13 @@ tls13_MaybeGreaseEch(sslSocket *ss, unsigned int preambleLen, sslBuffer *buf)
return SECSuccess;
}
+ /* In draft-09, CH2 sends exactly the same GREASE ECH extension. */
+ if (ss->ssl3.hs.helloRetry) {
+ return ssl3_EmplaceExtension(ss, buf, ssl_tls13_encrypted_client_hello_xtn,
+ ss->ssl3.hs.greaseEchBuf.buf,
+ ss->ssl3.hs.greaseEchBuf.len, PR_TRUE);
+ }
+
/* Compress the extensions for payload length. */
rv = tls13_ConstructInnerExtensionsFromOuter(ss, buf, &chInnerXtns,
NULL, PR_TRUE);
@@ -1734,7 +1763,7 @@ tls13_MaybeGreaseEch(sslSocket *ss, unsigned int preambleLen, sslBuffer *buf)
paramsi.len = sizeof(params);
derivedData = PK11_DeriveWithFlags(hmacPrk, CKM_HKDF_DATA,
&paramsi, CKM_HKDF_DATA,
- CKA_DERIVE, 65 + payloadLen,
+ CKA_DERIVE, kNonPayloadLen + payloadLen,
CKF_VERIFY);
if (!derivedData) {
goto loser;
@@ -1745,12 +1774,11 @@ tls13_MaybeGreaseEch(sslSocket *ss, unsigned int preambleLen, sslBuffer *buf)
goto loser;
}
- /* 1B aead determinant (don't send), 32B config_id, 32B enc, payload */
rawData = PK11_GetKeyData(derivedData);
if (!rawData) {
goto loser;
}
- PORT_Assert(rawData->len == 65 + payloadLen);
+ PORT_Assert(rawData->len == kNonPayloadLen + payloadLen);
/* struct {
HpkeKdfId kdf_id;
@@ -1773,31 +1801,34 @@ tls13_MaybeGreaseEch(sslSocket *ss, unsigned int preambleLen, sslBuffer *buf)
goto loser;
}
- rv = sslBuffer_AppendVariable(&greaseBuf, &rawData->data[1], 32, 1);
+ /* config_id, 8B */
+ rv = sslBuffer_AppendVariable(&greaseBuf, &rawData->data[1], 8, 1);
if (rv != SECSuccess) {
goto loser;
}
/* enc len is fixed 32B for X25519. */
- rv = sslBuffer_AppendVariable(&greaseBuf, &rawData->data[33], 32, 2);
+ rv = sslBuffer_AppendVariable(&greaseBuf, &rawData->data[9], 32, 2);
if (rv != SECSuccess) {
goto loser;
}
- rv = sslBuffer_AppendVariable(&greaseBuf, &rawData->data[65], payloadLen, 2);
+ rv = sslBuffer_AppendVariable(&greaseBuf, &rawData->data[kNonPayloadLen], payloadLen, 2);
if (rv != SECSuccess) {
goto loser;
}
/* Mark ECH as advertised so that we can validate any response.
- * We'll use echHpkeCtx to determine if we sent real or GREASE ECH.
- * TODO: Maybe a broader need to similarly track GREASED extensions? */
+ * We'll use echHpkeCtx to determine if we sent real or GREASE ECH. */
rv = ssl3_EmplaceExtension(ss, buf, ssl_tls13_encrypted_client_hello_xtn,
greaseBuf.buf, greaseBuf.len, PR_TRUE);
if (rv != SECSuccess) {
goto loser;
}
- sslBuffer_Clear(&greaseBuf);
+
+ /* Stash the GREASE ECH extension - in the case of HRR, CH2 must echo it. */
+ ss->ssl3.hs.greaseEchBuf = greaseBuf;
+
sslBuffer_Clear(&chInnerXtns);
PK11_FreeSymKey(hmacPrk);
PK11_FreeSymKey(derivedData);
@@ -1805,7 +1836,6 @@ tls13_MaybeGreaseEch(sslSocket *ss, unsigned int preambleLen, sslBuffer *buf)
return SECSuccess;
loser:
- sslBuffer_Clear(&greaseBuf);
sslBuffer_Clear(&chInnerXtns);
PK11_FreeSymKey(hmacPrk);
PK11_FreeSymKey(derivedData);
@@ -1861,15 +1891,11 @@ tls13_MaybeHandleEch(sslSocket *ss, const PRUint8 *msg, PRUint32 msgLen, SECItem
/* Since in Outer we explicitly call the ECH handler, do the same on Inner.
* Extensions are already parsed in tls13_MaybeAcceptEch. */
- echExtension = ssl3_FindExtension(ss, ssl_tls13_encrypted_client_hello_xtn);
+ echExtension = ssl3_FindExtension(ss, ssl_tls13_ech_is_inner_xtn);
if (!echExtension) {
- FATAL_ERROR(ss, SSL_ERROR_MISSING_ECH_EXTENSION, decode_error);
+ FATAL_ERROR(ss, SSL_ERROR_MISSING_ECH_EXTENSION, illegal_parameter);
goto loser;
}
- rv = tls13_ServerHandleEchXtn(ss, &ss->xtnData, &echExtension->data);
- if (rv != SECSuccess) {
- goto loser; /* code set, alert sent. */
- }
versionExtension = ssl3_FindExtension(ss, ssl_tls13_supported_versions_xtn);
if (!versionExtension) {
@@ -1895,11 +1921,12 @@ tls13_MaybeHandleEch(sslSocket *ss, const PRUint8 *msg, PRUint32 msgLen, SECItem
loser:
SECITEM_FreeItem(tmpEchInner, PR_TRUE);
+ PORT_Assert(PORT_GetError() != 0);
return SECFailure;
}
SECStatus
-tls13_MaybeHandleEchSignal(sslSocket *ss)
+tls13_MaybeHandleEchSignal(sslSocket *ss, const PRUint8 *sh, PRUint32 shLen)
{
SECStatus rv;
PRUint8 computed[TLS13_ECH_SIGNAL_LEN];
@@ -1907,29 +1934,41 @@ tls13_MaybeHandleEchSignal(sslSocket *ss)
PORT_Assert(!ss->sec.isServer);
/* If !echHpkeCtx, we either didn't advertise or sent GREASE ECH. */
- if (ss->ssl3.hs.echHpkeCtx) {
- PORT_Assert(ssl3_ExtensionAdvertised(ss, ssl_tls13_encrypted_client_hello_xtn));
- rv = tls13_ComputeEchSignal(ss, computed);
- if (rv != SECSuccess) {
+ if (!ss->ssl3.hs.echHpkeCtx) {
+ ss->ssl3.hs.preliminaryInfo |= ssl_preinfo_ech;
+ return SECSuccess;
+ }
+
+ PORT_Assert(ssl3_ExtensionAdvertised(ss, ssl_tls13_encrypted_client_hello_xtn));
+ rv = tls13_ComputeEchSignal(ss, sh, shLen, computed);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ ss->ssl3.hs.echAccepted = !PORT_Memcmp(computed, signal, TLS13_ECH_SIGNAL_LEN);
+ ss->ssl3.hs.preliminaryInfo |= ssl_preinfo_ech;
+ if (ss->ssl3.hs.echAccepted) {
+ if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
+ FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_SERVER_HELLO, illegal_parameter);
return SECFailure;
}
-
- ss->ssl3.hs.echAccepted = !PORT_Memcmp(computed, signal, TLS13_ECH_SIGNAL_LEN);
- if (ss->ssl3.hs.echAccepted) {
- if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
- FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_SERVER_HELLO, illegal_parameter);
+ if (ss->ssl3.hs.helloRetry && ss->sec.isServer) {
+ /* Enc and ConfigId are stored in the cookie and must not
+ * be included in CH2.ClientECH. */
+ if (ss->xtnData.ech->senderPubKey.len || ss->xtnData.ech->configId.len) {
+ ssl3_ExtSendAlert(ss, alert_fatal, illegal_parameter);
+ PORT_SetError(SSL_ERROR_BAD_2ND_CLIENT_HELLO);
return SECFailure;
}
- ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ssl_tls13_encrypted_client_hello_xtn;
- PORT_Memcpy(ss->ssl3.hs.client_random, ss->ssl3.hs.client_inner_random, SSL3_RANDOM_LENGTH);
}
- /* If rejected, leave echHpkeCtx and echPublicName for rejection paths. */
- ssl3_CoalesceEchHandshakeHashes(ss);
- SSL_TRC(50, ("%d: TLS13[%d]: ECH %s accepted by server",
- SSL_GETPID(), ss->fd, ss->ssl3.hs.echAccepted ? "is" : "is not"));
- }
- ss->ssl3.hs.preliminaryInfo |= ssl_preinfo_ech;
+ ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ssl_tls13_encrypted_client_hello_xtn;
+ PORT_Memcpy(ss->ssl3.hs.client_random, ss->ssl3.hs.client_inner_random, SSL3_RANDOM_LENGTH);
+ }
+ /* If rejected, leave echHpkeCtx and echPublicName for rejection paths. */
+ ssl3_CoalesceEchHandshakeHashes(ss);
+ SSL_TRC(50, ("%d: TLS13[%d]: ECH %s accepted by server",
+ SSL_GETPID(), ss->fd, ss->ssl3.hs.echAccepted ? "is" : "is not"));
return SECSuccess;
}
@@ -2092,75 +2131,111 @@ tls13_MaybeAcceptEch(sslSocket *ss, const SECItem *sidBytes, const PRUint8 *chOu
SECStatus rv;
SECItem outer = { siBuffer, CONST_CAST(PRUint8, chOuter), chOuterLen };
SECItem *decryptedChInner = NULL;
- PK11SymKey *echHrrPsk = NULL;
SECItem hrrCh1ConfigId = { siBuffer, NULL, 0 };
- HpkeKdfId kdf;
- HpkeAeadId aead;
+ SECItem outerAAD = { siBuffer, NULL, 0 };
+ SECItem cookieData = { siBuffer, NULL, 0 };
+ HpkeContext *ch1EchHpkeCtx = NULL;
+ HpkeKdfId echKdfId;
+ HpkeAeadId echAeadId;
sslEchConfig *candidate = NULL; /* non-owning */
TLSExtension *hrrXtn;
- SECItem *configId = ss->ssl3.hs.helloRetry ? &hrrCh1ConfigId : &ss->xtnData.echConfigId;
- if (!ss->xtnData.innerCh.len) {
+
+ if (!ss->xtnData.ech) {
return SECSuccess;
}
- PORT_Assert(ss->xtnData.echSenderPubKey.data);
- PORT_Assert(ss->xtnData.echConfigId.data);
- PORT_Assert(ss->xtnData.echCipherSuite);
+ PORT_Assert(ss->xtnData.ech->innerCh.data);
if (ss->ssl3.hs.helloRetry) {
+ PORT_Assert(!ss->ssl3.hs.echHpkeCtx);
hrrXtn = ssl3_FindExtension(ss, ssl_tls13_cookie_xtn);
if (!hrrXtn) {
/* If the client doesn't echo cookie, we can't decrypt. */
return SECSuccess;
}
- rv = tls13_GetEchInfoFromCookie(ss, hrrXtn, &echHrrPsk, &hrrCh1ConfigId);
+ PORT_Assert(!ss->xtnData.ech->configId.data);
+ PORT_Assert(!ss->ssl3.hs.echHpkeCtx);
+
+ PRUint8 *tmp = hrrXtn->data.data;
+ PRUint32 len = hrrXtn->data.len;
+ rv = ssl3_ExtConsumeHandshakeVariable(ss, &cookieData, 2,
+ &tmp, &len);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ /* Extract ECH info without restoring hash state. If there's
+ * something wrong with the cookie, continue without ECH
+ * and let HRR code handle the problem. */
+ rv = tls13_HandleHrrCookie(ss, cookieData.data, cookieData.len,
+ NULL, NULL, NULL, &echKdfId, &echAeadId,
+ &hrrCh1ConfigId, &ch1EchHpkeCtx, PR_FALSE);
if (rv != SECSuccess) {
- /* If we failed due to an issue with the cookie, continue without
- * ECH and let the HRR code handle the problem. */
- goto exit_success;
+ return SECSuccess;
}
- /* No CH1 config_id means ECH wasn't advertised in CH1.
- * No CH1 HRR PSK means that ECH was not accepted in CH1, and the
- * HRR was generated off CH1Outer. */
- if (hrrCh1ConfigId.len == 0) {
+ ss->xtnData.ech->configId = hrrCh1ConfigId;
+ ss->ssl3.hs.echHpkeCtx = ch1EchHpkeCtx;
+
+ if (echKdfId != ss->xtnData.ech->kdfId ||
+ echAeadId != ss->xtnData.ech->aeadId) {
FATAL_ERROR(ss, SSL_ERROR_BAD_2ND_CLIENT_HELLO,
illegal_parameter);
- goto loser;
+ return SECFailure;
}
- if (!echHrrPsk) {
- goto exit_success;
+
+ if (!ss->ssl3.hs.echHpkeCtx) {
+ return SECSuccess;
}
}
- kdf = (HpkeKdfId)(ss->xtnData.echCipherSuite & 0xFFFF);
- aead = (HpkeAeadId)(((ss->xtnData.echCipherSuite) >> 16) & 0xFFFF);
- rv = tls13_GetMatchingEchConfig(ss, kdf, aead, configId, &candidate);
+
+ /* Cookie data was good, proceed with ECH. */
+ PORT_Assert(ss->xtnData.ech->configId.data);
+ rv = tls13_GetMatchingEchConfigs(ss, ss->xtnData.ech->kdfId, ss->xtnData.ech->aeadId,
+ &ss->xtnData.ech->configId, candidate, &candidate);
if (rv != SECSuccess) {
- goto loser;
+ FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
+ return SECFailure;
}
- if (!candidate || candidate->contents.kdfId != kdf ||
- candidate->contents.aeadId != aead) {
- /* Send retry_configs if we have any.
- * This does *not* count as negotiating ECH. */
- rv = ssl3_RegisterExtensionSender(ss, &ss->xtnData,
- ssl_tls13_encrypted_client_hello_xtn,
- tls13_ServerSendEchXtn);
- goto exit_success;
+
+ if (candidate) {
+ rv = tls13_MakeChOuterAAD(ss, &outer, &outerAAD);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
}
- rv = tls13_OpenClientHelloInner(ss, &outer, candidate, echHrrPsk, &decryptedChInner);
- if (rv != SECSuccess) {
+ while (candidate) {
+ rv = tls13_OpenClientHelloInner(ss, &outer, &outerAAD, candidate, &decryptedChInner);
+ if (rv != SECSuccess) {
+ /* Get the next matching config */
+ rv = tls13_GetMatchingEchConfigs(ss, ss->xtnData.ech->kdfId, ss->xtnData.ech->aeadId,
+ &ss->xtnData.ech->configId, candidate, &candidate);
+ if (rv != SECSuccess) {
+ FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
+ SECITEM_FreeItem(&outerAAD, PR_FALSE);
+ return SECFailure;
+ }
+ continue;
+ }
+ break;
+ }
+ SECITEM_FreeItem(&outerAAD, PR_FALSE);
+
+ if (rv != SECSuccess || !decryptedChInner) {
if (ss->ssl3.hs.helloRetry) {
- FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_ESNI_EXTENSION, decrypt_error);
- goto loser;
+ FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_ECH_EXTENSION, decrypt_error);
+ return SECFailure;
} else {
- rv = ssl3_RegisterExtensionSender(ss, &ss->xtnData,
- ssl_tls13_encrypted_client_hello_xtn,
- tls13_ServerSendEchXtn);
- goto exit_success;
+ /* Send retry_configs (if we have any) when we fail to decrypt or
+ * found no candidates. This does *not* count as negotiating ECH. */
+ return ssl3_RegisterExtensionSender(ss, &ss->xtnData,
+ ssl_tls13_encrypted_client_hello_xtn,
+ tls13_ServerSendEchXtn);
}
}
+
SSL_TRC(20, ("%d: TLS13[%d]: Successfully opened ECH inner CH",
SSL_GETPID(), ss->fd));
ss->ssl3.hs.echAccepted = PR_TRUE;
@@ -2172,31 +2247,33 @@ tls13_MaybeAcceptEch(sslSocket *ss, const SECItem *sidBytes, const PRUint8 *chOu
rv = tls13_UnencodeChInner(ss, sidBytes, &decryptedChInner);
if (rv != SECSuccess) {
SECITEM_FreeItem(decryptedChInner, PR_TRUE);
- goto loser; /* code set */
+ return SECFailure; /* code set */
}
*chInner = decryptedChInner;
-
-exit_success:
- PK11_FreeSymKey(echHrrPsk);
- SECITEM_FreeItem(&hrrCh1ConfigId, PR_FALSE);
return SECSuccess;
-
-loser:
- PK11_FreeSymKey(echHrrPsk);
- SECITEM_FreeItem(&hrrCh1ConfigId, PR_FALSE);
- return SECFailure;
}
SECStatus
-tls13_WriteServerEchSignal(sslSocket *ss)
+tls13_WriteServerEchSignal(sslSocket *ss, PRUint8 *sh, unsigned int shLen)
{
SECStatus rv;
PRUint8 signal[TLS13_ECH_SIGNAL_LEN];
- rv = tls13_ComputeEchSignal(ss, signal);
+ PRUint8 *msg_random = &sh[sizeof(SSL3ProtocolVersion)];
+
+ PORT_Assert(shLen > sizeof(SSL3ProtocolVersion) + SSL3_RANDOM_LENGTH);
+ PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3);
+
+ rv = tls13_ComputeEchSignal(ss, sh, shLen, signal);
if (rv != SECSuccess) {
return SECFailure;
}
- PRUint8 *dest = &ss->ssl3.hs.server_random[SSL3_RANDOM_LENGTH - TLS13_ECH_SIGNAL_LEN];
+ PRUint8 *dest = &msg_random[SSL3_RANDOM_LENGTH - TLS13_ECH_SIGNAL_LEN];
+ PORT_Memcpy(dest, signal, TLS13_ECH_SIGNAL_LEN);
+
+ /* Keep the socket copy consistent. */
+ PORT_Assert(0 == memcmp(msg_random, &ss->ssl3.hs.server_random, SSL3_RANDOM_LENGTH - TLS13_ECH_SIGNAL_LEN));
+ dest = &ss->ssl3.hs.server_random[SSL3_RANDOM_LENGTH - TLS13_ECH_SIGNAL_LEN];
PORT_Memcpy(dest, signal, TLS13_ECH_SIGNAL_LEN);
+
return SECSuccess;
}
diff --git a/lib/ssl/tls13ech.h b/lib/ssl/tls13ech.h
index 5c53b3f8a..a39a0295c 100644
--- a/lib/ssl/tls13ech.h
+++ b/lib/ssl/tls13ech.h
@@ -11,7 +11,7 @@
#include "pk11hpke.h"
-/* draft-08, shared-mode only.
+/* draft-09, supporting shared-mode and split-mode as a backend server only.
* Notes on the implementation status:
* - Padding (https://tools.ietf.org/html/draft-ietf-tls-esni-08#section-6.2),
* is not implemented (see bug 1677181).
@@ -21,12 +21,10 @@
* - Some of the buffering (construction/compression/decompression) could likely
* be optimized, but the spec is still evolving so that work is deferred.
*/
-#define TLS13_ECH_VERSION 0xfe08
+#define TLS13_ECH_VERSION 0xfe09
#define TLS13_ECH_SIGNAL_LEN 8
static const char kHpkeInfoEch[] = "tls ech";
-static const char kHpkeInfoEchHrr[] = "tls ech hrr key";
-static const char kHpkeLabelHrrPsk[] = "hrr key";
static const char hHkdfInfoEchConfigID[] = "tls ech config id";
static const char kHkdfInfoEchConfirm[] = "ech accept confirmation";
@@ -45,11 +43,23 @@ struct sslEchConfigContentsStr {
struct sslEchConfigStr {
PRCList link;
SECItem raw;
- PRUint8 configId[32];
+ PRUint8 configId[8];
PRUint16 version;
sslEchConfigContents contents;
};
+struct sslEchXtnStateStr {
+ SECItem innerCh; /* Server: ClientECH.payload */
+ SECItem senderPubKey; /* Server: ClientECH.enc */
+ SECItem configId; /* Server: ClientECH.config_id */
+ HpkeKdfId kdfId; /* Server: ClientECH.cipher_suite.kdf */
+ HpkeAeadId aeadId; /* Server: ClientECH.cipher_suite.aead */
+ SECItem retryConfigs; /* Client: ServerECH.retry_configs*/
+ PRBool retryConfigsValid; /* Client: Extraction of retry_configss is allowed.
+ * This is set once the handshake completes (having
+ * verified to the ECHConfig public name). */
+};
+
SECStatus SSLExp_EncodeEchConfig(const char *publicName, const PRUint32 *hpkeSuites,
unsigned int hpkeSuiteCount, HpkeKemId kemId,
const SECKEYPublicKey *pubKey, PRUint16 maxNameLen,
@@ -69,14 +79,15 @@ SECStatus tls13_ConstructClientHelloWithEch(sslSocket *ss, const sslSessionID *s
SECStatus tls13_CopyEchConfigs(PRCList *oconfigs, PRCList *configs);
SECStatus tls13_DecodeEchConfigs(const SECItem *data, PRCList *configs);
void tls13_DestroyEchConfigs(PRCList *list);
+void tls13_DestroyEchXtnState(sslEchXtnState *state);
SECStatus tls13_GetMatchingEchConfig(const sslSocket *ss, HpkeKdfId kdf, HpkeAeadId aead,
const SECItem *configId, sslEchConfig **cfg);
SECStatus tls13_MaybeHandleEch(sslSocket *ss, const PRUint8 *msg, PRUint32 msgLen, SECItem *sidBytes,
SECItem *comps, SECItem *cookieBytes, SECItem *suites, SECItem **echInner);
-SECStatus tls13_MaybeHandleEchSignal(sslSocket *ss);
+SECStatus tls13_MaybeHandleEchSignal(sslSocket *ss, const PRUint8 *savedMsg, PRUint32 savedLength);
SECStatus tls13_MaybeAcceptEch(sslSocket *ss, const SECItem *sidBytes, const PRUint8 *chOuter,
unsigned int chOuterLen, SECItem **chInner);
SECStatus tls13_MaybeGreaseEch(sslSocket *ss, unsigned int prefixLen, sslBuffer *buf);
-SECStatus tls13_WriteServerEchSignal(sslSocket *ss);
+SECStatus tls13_WriteServerEchSignal(sslSocket *ss, PRUint8 *sh, unsigned int shLen);
#endif
diff --git a/lib/ssl/tls13exthandle.c b/lib/ssl/tls13exthandle.c
index 54a4abb09..7991a12c2 100644
--- a/lib/ssl/tls13exthandle.c
+++ b/lib/ssl/tls13exthandle.c
@@ -1211,6 +1211,12 @@ tls13_ClientHandleEchXtn(const sslSocket *ss, TLSExtensionData *xtnData,
PRCList parsedConfigs;
PR_INIT_CLIST(&parsedConfigs);
+ PORT_Assert(!xtnData->ech);
+ xtnData->ech = PORT_ZNew(sslEchXtnState);
+ if (!xtnData->ech) {
+ return SECFailure;
+ }
+
/* Parse the list to determine 1) That the configs are valid
* and properly encoded, and 2) If any are compatible. */
rv = tls13_DecodeEchConfigs(data, &parsedConfigs);
@@ -1219,11 +1225,11 @@ tls13_ClientHandleEchXtn(const sslSocket *ss, TLSExtensionData *xtnData,
PORT_SetError(SSL_ERROR_RX_MALFORMED_ECH_CONFIG);
return SECFailure;
}
- /* Don't mark ECH negotiated on retry. Save the the raw
- * configs so the application can retry. If we sent GREASE
- * ECH (no echHpkeCtx), don't apply returned retry_configs. */
+ /* Don't mark ECH negotiated on rejection with retry_config.
+ * Save the the raw configs so the application can retry. If
+ * we sent GREASE ECH (no echHpkeCtx), don't apply retry_configs. */
if (ss->ssl3.hs.echHpkeCtx && !PR_CLIST_IS_EMPTY(&parsedConfigs)) {
- rv = SECITEM_CopyItem(NULL, &xtnData->echRetryConfigs, data);
+ rv = SECITEM_CopyItem(NULL, &xtnData->ech->retryConfigs, data);
}
tls13_DestroyEchConfigs(&parsedConfigs);
@@ -1465,14 +1471,22 @@ tls13_ServerHandleEchXtn(const sslSocket *ss, TLSExtensionData *xtnData,
return SECSuccess;
}
- /* On CHInner, the extension must be empty. */
- if (ss->ssl3.hs.echAccepted && data->len > 0) {
+ if (ss->ssl3.hs.echAccepted) {
ssl3_ExtSendAlert(ss, alert_fatal, illegal_parameter);
- PORT_SetError(SSL_ERROR_RX_MALFORMED_ECH_EXTENSION);
+ PORT_SetError(SSL_ERROR_RX_UNEXPECTED_EXTENSION);
+ return SECFailure;
+ }
+
+ if (ssl3_FindExtension(CONST_CAST(sslSocket, ss), ssl_tls13_ech_is_inner_xtn)) {
+ ssl3_ExtSendAlert(ss, alert_fatal, illegal_parameter);
+ PORT_SetError(SSL_ERROR_RX_UNEXPECTED_EXTENSION);
+ return SECFailure;
+ }
+
+ PORT_Assert(!xtnData->ech);
+ xtnData->ech = PORT_ZNew(sslEchXtnState);
+ if (!xtnData->ech) {
return SECFailure;
- } else if (ss->ssl3.hs.echAccepted) {
- xtnData->negotiated[xtnData->numNegotiated++] = ssl_tls13_encrypted_client_hello_xtn;
- return SECSuccess;
}
/* Parse the KDF and AEAD. */
@@ -1503,37 +1517,40 @@ tls13_ServerHandleEchXtn(const sslSocket *ss, TLSExtensionData *xtnData,
goto alert_loser;
}
- /* payload */
+ /* payload, which must be final and non-empty. */
rv = ssl3_ExtConsumeHandshakeVariable(ss, &encryptedCh, 2,
&data->data, &data->len);
if (rv != SECSuccess) {
goto alert_loser;
}
-
- if (data->len) {
+ if (data->len || !encryptedCh.len) {
goto alert_loser;
}
- /* All fields required. */
- if (!configId.len || !senderPubKey.len || !encryptedCh.len) {
- goto alert_loser;
- }
+ if (!ss->ssl3.hs.helloRetry) {
+ /* In the real ECH HRR case, config_id and enc should be empty. This
+ * is checked after acceptance, because it might be GREASE ECH. */
+ if (!configId.len || !senderPubKey.len) {
+ goto alert_loser;
+ }
- rv = SECITEM_CopyItem(NULL, &xtnData->echSenderPubKey, &senderPubKey);
- if (rv == SECFailure) {
- return SECFailure;
- }
+ rv = SECITEM_CopyItem(NULL, &xtnData->ech->senderPubKey, &senderPubKey);
+ if (rv == SECFailure) {
+ return SECFailure;
+ }
- rv = SECITEM_CopyItem(NULL, &xtnData->innerCh, &encryptedCh);
- if (rv == SECFailure) {
- return SECFailure;
+ rv = SECITEM_CopyItem(NULL, &xtnData->ech->configId, &configId);
+ if (rv == SECFailure) {
+ return SECFailure;
+ }
}
- rv = SECITEM_CopyItem(NULL, &xtnData->echConfigId, &configId);
+ rv = SECITEM_CopyItem(NULL, &xtnData->ech->innerCh, &encryptedCh);
if (rv == SECFailure) {
return SECFailure;
}
- xtnData->echCipherSuite = (aead & 0xFFFF) << 16 | (kdf & 0xFFFF);
+ xtnData->ech->kdfId = kdf;
+ xtnData->ech->aeadId = aead;
/* Not negotiated until tls13_MaybeAcceptEch. */
return SECSuccess;
@@ -1543,3 +1560,36 @@ alert_loser:
PORT_SetError(SSL_ERROR_RX_MALFORMED_ECH_EXTENSION);
return SECFailure;
}
+
+SECStatus
+tls13_ServerHandleEchIsInnerXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ SECItem *data)
+{
+ SSL_TRC(3, ("%d: TLS13[%d]: handle ech_is_inner extension",
+ SSL_GETPID(), ss->fd));
+
+ if (data->len) {
+ PORT_SetError(SSL_ERROR_RX_MALFORMED_ECH_EXTENSION);
+ return SECFailure;
+ }
+
+ if (ssl3_FindExtension(CONST_CAST(sslSocket, ss), ssl_tls13_encrypted_client_hello_xtn)) {
+ ssl3_ExtSendAlert(ss, alert_fatal, illegal_parameter);
+ PORT_SetError(SSL_ERROR_RX_UNEXPECTED_EXTENSION);
+ return SECFailure;
+ }
+
+ /* Consider encrypted_client_hello_xtn negotiated if we performed the
+ * CHOuter decryption. This is only supported in shared mode, so we'll also
+ * handle ech_is_inner in that case. We might, however, receive a CHInner
+ * that was forwarded by a different client-facing server. In this case,
+ * mark ech_is_inner as negotiated, which triggers sending of the ECH
+ * acceptance signal. ech_is_inner_xtn being negotiated does not imply
+ * that any other ECH state actually exists. */
+ if (ss->ssl3.hs.echAccepted) {
+ xtnData->negotiated[xtnData->numNegotiated++] = ssl_tls13_encrypted_client_hello_xtn;
+ }
+ xtnData->negotiated[xtnData->numNegotiated++] = ssl_tls13_ech_is_inner_xtn;
+ return SECSuccess;
+}
diff --git a/lib/ssl/tls13exthandle.h b/lib/ssl/tls13exthandle.h
index 556737210..ae79ecba8 100644
--- a/lib/ssl/tls13exthandle.h
+++ b/lib/ssl/tls13exthandle.h
@@ -94,6 +94,9 @@ SECStatus tls13_ServerSendEchXtn(const sslSocket *ss, TLSExtensionData *xtnData,
sslBuffer *buf, PRBool *added);
SECStatus tls13_ClientHandleEchXtn(const sslSocket *ss, TLSExtensionData *xtnData,
SECItem *data);
+SECStatus tls13_ServerHandleEchIsInnerXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ SECItem *data);
SECStatus tls13_ClientSendPostHandshakeAuthXtn(const sslSocket *ss,
TLSExtensionData *xtnData,
sslBuffer *buf, PRBool *added);
diff --git a/lib/ssl/tls13hashstate.c b/lib/ssl/tls13hashstate.c
index 011b4838e..f2f55ba0f 100644
--- a/lib/ssl/tls13hashstate.c
+++ b/lib/ssl/tls13hashstate.c
@@ -24,9 +24,11 @@
* uint8 indicator = 0xff; // To disambiguate from tickets.
* uint16 cipherSuite; // Selected cipher suite.
* uint16 keyShare; // Requested key share group (0=none)
+ * HpkeKdfId kdfId; // ECH KDF (uint16)
+ * HpkeAeadId aeadId; // ECH AEAD (uint16)
+ * opaque echConfigId<0..255>; // ECH config_id
+ * opaque echHpkeCtx<0..65535>; // ECH serialized HPKE context
* opaque applicationToken<0..65535>; // Application token
- * echConfigId<0..255>; // Encrypted Client Hello config_id
- * echHrrPsk<0..255>; // Encrypted Client Hello HRR PSK
* opaque ch_hash[rest_of_buffer]; // H(ClientHello)
* } CookieInner;
*
@@ -43,10 +45,7 @@ tls13_MakeHrrCookie(sslSocket *ss, const sslNamedGroupDef *selectedGroup,
PRUint8 cookie[1024];
sslBuffer cookieBuf = SSL_BUFFER(cookie);
static const PRUint8 indicator = 0xff;
- SECItem hrrNonceInfoItem = { siBuffer, (unsigned char *)kHpkeInfoEchHrr,
- strlen(kHpkeInfoEchHrr) };
- PK11SymKey *echHrrPsk = NULL;
- SECItem *rawEchPsk = NULL;
+ SECItem *echHpkeCtx = NULL;
/* Encode header. */
rv = sslBuffer_Append(&cookieBuf, &indicator, 1);
@@ -63,50 +62,49 @@ tls13_MakeHrrCookie(sslSocket *ss, const sslNamedGroupDef *selectedGroup,
return SECFailure;
}
- /* Application token. */
- rv = sslBuffer_AppendVariable(&cookieBuf, appToken, appTokenLen, 2);
- if (rv != SECSuccess) {
- return SECFailure;
- }
-
- /* Received ECH config_id, regardless of acceptance or possession
- * of a matching ECHConfig. If rejecting ECH, this is essentially a boolean
- * indicating that ECH was offered in CH1. If accepting ECH, this config_id
- * will be used for the ECH decryption in CH2. */
- if (ss->xtnData.echConfigId.len) {
- rv = sslBuffer_AppendVariable(&cookieBuf, ss->xtnData.echConfigId.data,
- ss->xtnData.echConfigId.len, 1);
- } else {
- PORT_Assert(!ssl3_FindExtension(ss, ssl_tls13_encrypted_client_hello_xtn));
- rv = sslBuffer_AppendNumber(&cookieBuf, 0, 1);
- }
- if (rv != SECSuccess) {
- return SECFailure;
- }
-
- /* Extract and encode the ech-hrr-key, if ECH was accepted
- * (i.e. an Open() succeeded. */
- if (ss->ssl3.hs.echAccepted) {
- rv = PK11_HPKE_ExportSecret(ss->ssl3.hs.echHpkeCtx, &hrrNonceInfoItem, 32, &echHrrPsk);
+ if (ss->xtnData.ech) {
+ rv = sslBuffer_AppendNumber(&cookieBuf, ss->xtnData.ech->kdfId, 2);
if (rv != SECSuccess) {
return SECFailure;
}
- rv = PK11_ExtractKeyValue(echHrrPsk);
+ rv = sslBuffer_AppendNumber(&cookieBuf, ss->xtnData.ech->aeadId, 2);
if (rv != SECSuccess) {
- PK11_FreeSymKey(echHrrPsk);
return SECFailure;
}
- rawEchPsk = PK11_GetKeyData(echHrrPsk);
- if (!rawEchPsk) {
- PK11_FreeSymKey(echHrrPsk);
+
+ /* Received ECH config_id, regardless of acceptance or possession
+ * of a matching ECHConfig. */
+ PORT_Assert(ss->xtnData.ech->configId.len == 8);
+ rv = sslBuffer_AppendVariable(&cookieBuf, ss->xtnData.ech->configId.data,
+ ss->xtnData.ech->configId.len, 1);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ /* There might be no HPKE Context, e.g. when we lack a matching ECHConfig. */
+ if (ss->ssl3.hs.echHpkeCtx) {
+ rv = PK11_HPKE_ExportContext(ss->ssl3.hs.echHpkeCtx, NULL, &echHpkeCtx);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ rv = sslBuffer_AppendVariable(&cookieBuf, echHpkeCtx->data, echHpkeCtx->len, 2);
+ SECITEM_ZfreeItem(echHpkeCtx, PR_TRUE);
+ } else {
+ /* Zero length HPKE context. */
+ rv = sslBuffer_AppendNumber(&cookieBuf, 0, 2);
+ }
+ if (rv != SECSuccess) {
return SECFailure;
}
- rv = sslBuffer_AppendVariable(&cookieBuf, rawEchPsk->data, rawEchPsk->len, 1);
- PK11_FreeSymKey(echHrrPsk);
} else {
- /* Zero length ech_hrr_key. */
- rv = sslBuffer_AppendNumber(&cookieBuf, 0, 1);
+ rv = sslBuffer_AppendNumber(&cookieBuf, 0, 7);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
}
+
+ /* Application token. */
+ rv = sslBuffer_AppendVariable(&cookieBuf, appToken, appTokenLen, 2);
if (rv != SECSuccess) {
return SECFailure;
}
@@ -131,23 +129,34 @@ tls13_MakeHrrCookie(sslSocket *ss, const sslNamedGroupDef *selectedGroup,
return SECSuccess;
}
-/* Recover the hash state from the cookie. */
+/* Given a cookie and cookieLen, decrypt and parse, returning
+ * any values that were requested via the "previous_" params. If
+ * recoverHashState is true, the transcript state is recovered */
SECStatus
-tls13_RecoverHashState(sslSocket *ss,
- unsigned char *cookie, unsigned int cookieLen,
- ssl3CipherSuite *previousCipherSuite,
- const sslNamedGroupDef **previousGroup,
- PRBool *previousEchOffered)
+tls13_HandleHrrCookie(sslSocket *ss,
+ unsigned char *cookie, unsigned int cookieLen,
+ ssl3CipherSuite *previousCipherSuite,
+ const sslNamedGroupDef **previousGroup,
+ PRBool *previousEchOffered,
+ HpkeKdfId *previousEchKdfId,
+ HpkeAeadId *previousEchAeadId,
+ SECItem *previousEchConfigId,
+ HpkeContext **previousEchHpkeCtx,
+ PRBool recoverHashState)
{
SECStatus rv;
unsigned char plaintext[1024];
unsigned int plaintextLen = 0;
sslBuffer messageBuf = SSL_BUFFER_EMPTY;
- sslReadBuffer echPskBuf;
- sslReadBuffer echConfigIdBuf;
+ sslReadBuffer echHpkeBuf = { 0 };
+ sslReadBuffer echConfigIdBuf = { 0 };
PRUint64 sentinel;
PRUint64 cipherSuite;
+ HpkeContext *hpkeContext = NULL;
+ HpkeKdfId echKdfId;
+ HpkeAeadId echAeadId;
PRUint64 group;
+ PRUint64 tmp64;
const sslNamedGroupDef *selectedGroup;
PRUint64 appTokenLen;
@@ -180,77 +189,129 @@ tls13_RecoverHashState(sslSocket *ss,
}
selectedGroup = ssl_LookupNamedGroup(group);
- /* Application token. */
- PORT_Assert(ss->xtnData.applicationToken.len == 0);
- rv = sslRead_ReadNumber(&reader, 2, &appTokenLen);
+ /* ECH Ciphersuite */
+ rv = sslRead_ReadNumber(&reader, 2, &tmp64);
if (rv != SECSuccess) {
FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, illegal_parameter);
return SECFailure;
}
- if (SECITEM_AllocItem(NULL, &ss->xtnData.applicationToken,
- appTokenLen) == NULL) {
- FATAL_ERROR(ss, PORT_GetError(), internal_error);
- return SECFailure;
- }
- ss->xtnData.applicationToken.len = appTokenLen;
- sslReadBuffer appTokenReader = { 0 };
- rv = sslRead_Read(&reader, appTokenLen, &appTokenReader);
+ echKdfId = (HpkeKdfId)tmp64;
+
+ rv = sslRead_ReadNumber(&reader, 2, &tmp64);
if (rv != SECSuccess) {
FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, illegal_parameter);
return SECFailure;
}
- PORT_Assert(appTokenReader.len == appTokenLen);
- PORT_Memcpy(ss->xtnData.applicationToken.data, appTokenReader.buf, appTokenLen);
+ echAeadId = (HpkeAeadId)tmp64;
- /* ECH Config ID, which may be empty. */
+ /* ECH Config ID and HPKE context may be empty. */
rv = sslRead_ReadVariable(&reader, 1, &echConfigIdBuf);
if (rv != SECSuccess) {
FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, illegal_parameter);
return SECFailure;
}
- /* ECH HRR PSK, if present, is already used by tls13_GetEchInfoFromCookie */
- rv = sslRead_ReadVariable(&reader, 1, &echPskBuf);
+ rv = sslRead_ReadVariable(&reader, 2, &echHpkeBuf);
if (rv != SECSuccess) {
FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, illegal_parameter);
return SECFailure;
}
- /* The remainder is the hash. */
- unsigned int hashLen = SSL_READER_REMAINING(&reader);
- if (hashLen != tls13_GetHashSize(ss)) {
+ /* Application token. */
+ PORT_Assert(ss->xtnData.applicationToken.len == 0);
+ rv = sslRead_ReadNumber(&reader, 2, &appTokenLen);
+ if (rv != SECSuccess) {
FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, illegal_parameter);
return SECFailure;
}
-
- /* Now reinject the message. */
- SSL_ASSERT_HASHES_EMPTY(ss);
- rv = ssl_HashHandshakeMessageInt(ss, ssl_hs_message_hash, 0,
- SSL_READER_CURRENT(&reader), hashLen,
- ssl3_UpdateHandshakeHashes);
- if (rv != SECSuccess) {
+ if (SECITEM_AllocItem(NULL, &ss->xtnData.applicationToken,
+ appTokenLen) == NULL) {
+ FATAL_ERROR(ss, PORT_GetError(), internal_error);
return SECFailure;
}
-
- /* And finally reinject the HRR. */
- rv = tls13_ConstructHelloRetryRequest(ss, cipherSuite,
- selectedGroup,
- cookie, cookieLen,
- &messageBuf);
+ ss->xtnData.applicationToken.len = appTokenLen;
+ sslReadBuffer appTokenReader = { 0 };
+ rv = sslRead_Read(&reader, appTokenLen, &appTokenReader);
if (rv != SECSuccess) {
+ FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, illegal_parameter);
return SECFailure;
}
+ PORT_Assert(appTokenReader.len == appTokenLen);
+ PORT_Memcpy(ss->xtnData.applicationToken.data, appTokenReader.buf, appTokenLen);
- rv = ssl_HashHandshakeMessageInt(ss, ssl_hs_server_hello, 0,
- SSL_BUFFER_BASE(&messageBuf),
- SSL_BUFFER_LEN(&messageBuf),
- ssl3_UpdateHandshakeHashes);
- sslBuffer_Clear(&messageBuf);
- if (rv != SECSuccess) {
- return SECFailure;
+ /* The remainder is the hash. */
+ if (recoverHashState) {
+ unsigned int hashLen = SSL_READER_REMAINING(&reader);
+ if (hashLen != tls13_GetHashSize(ss)) {
+ FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, illegal_parameter);
+ return SECFailure;
+ }
+
+ /* Now reinject the message. */
+ SSL_ASSERT_HASHES_EMPTY(ss);
+ rv = ssl_HashHandshakeMessageInt(ss, ssl_hs_message_hash, 0,
+ SSL_READER_CURRENT(&reader), hashLen,
+ ssl3_UpdateHandshakeHashes);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ /* And finally reinject the HRR. */
+ rv = tls13_ConstructHelloRetryRequest(ss, cipherSuite,
+ selectedGroup,
+ cookie, cookieLen,
+ &messageBuf);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ rv = ssl_HashHandshakeMessageInt(ss, ssl_hs_server_hello, 0,
+ SSL_BUFFER_BASE(&messageBuf),
+ SSL_BUFFER_LEN(&messageBuf),
+ ssl3_UpdateHandshakeHashes);
+ sslBuffer_Clear(&messageBuf);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ }
+
+ if (previousEchHpkeCtx && echHpkeBuf.len) {
+ const SECItem hpkeItem = { siBuffer, CONST_CAST(unsigned char, echHpkeBuf.buf),
+ echHpkeBuf.len };
+ hpkeContext = PK11_HPKE_ImportContext(&hpkeItem, NULL);
+ if (!hpkeContext) {
+ FATAL_ERROR(ss, PORT_GetError(), internal_error);
+ return SECFailure;
+ }
}
- *previousCipherSuite = cipherSuite;
- *previousGroup = selectedGroup;
- *previousEchOffered = echConfigIdBuf.len > 0;
+ if (previousEchConfigId && echConfigIdBuf.len) {
+ SECItem tmp = { siBuffer, NULL, 0 };
+ rv = SECITEM_MakeItem(NULL, &tmp, echConfigIdBuf.buf, echConfigIdBuf.len);
+ if (rv != SECSuccess) {
+ PK11_HPKE_DestroyContext(hpkeContext, PR_TRUE);
+ FATAL_ERROR(ss, PORT_GetError(), internal_error);
+ return SECFailure;
+ }
+ *previousEchConfigId = tmp;
+ }
+
+ if (previousEchKdfId) {
+ *previousEchKdfId = echKdfId;
+ }
+ if (previousEchAeadId) {
+ *previousEchAeadId = echAeadId;
+ }
+ if (previousEchHpkeCtx) {
+ *previousEchHpkeCtx = hpkeContext;
+ }
+ if (previousCipherSuite) {
+ *previousCipherSuite = cipherSuite;
+ }
+ if (previousGroup) {
+ *previousGroup = selectedGroup;
+ }
+ if (previousEchOffered) {
+ *previousEchOffered = echConfigIdBuf.len > 0;
+ }
return SECSuccess;
}
diff --git a/lib/ssl/tls13hashstate.h b/lib/ssl/tls13hashstate.h
index 8126bd0db..48832f04a 100644
--- a/lib/ssl/tls13hashstate.h
+++ b/lib/ssl/tls13hashstate.h
@@ -17,9 +17,14 @@ SECStatus tls13_MakeHrrCookie(sslSocket *ss, const sslNamedGroupDef *selectedGro
const PRUint8 *appToken, unsigned int appTokenLen,
PRUint8 *buf, unsigned int *len, unsigned int maxlen);
SECStatus tls13_GetHrrCookieLength(sslSocket *ss, unsigned int *length);
-SECStatus tls13_RecoverHashState(sslSocket *ss,
- unsigned char *cookie, unsigned int cookieLen,
- ssl3CipherSuite *previousCipherSuite,
- const sslNamedGroupDef **previousGroup,
- PRBool *previousEchOffered);
+SECStatus tls13_HandleHrrCookie(sslSocket *ss,
+ unsigned char *cookie, unsigned int cookieLen,
+ ssl3CipherSuite *previousCipherSuite,
+ const sslNamedGroupDef **previousGroup,
+ PRBool *previousEchOffered,
+ HpkeKdfId *previousEchKdfId,
+ HpkeAeadId *previousEchAeadId,
+ SECItem *previousEchConfigId,
+ HpkeContext **previousEchHpkeCtx,
+ PRBool recoverHashState);
#endif