summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorLeander Schwarz <lschwarz@mozilla.com>2023-02-15 10:45:12 +0000
committerLeander Schwarz <lschwarz@mozilla.com>2023-02-15 10:45:12 +0000
commit4c1e6ff0d8911b11b607eabcafb9f2b52888481c (patch)
tree34743f1f6a358c0c7a83ae6755a7e014efe95af9 /lib
parente5675eccc38b57b1028d51f5238729a0bc4b98c1 (diff)
downloadnss-hg-4c1e6ff0d8911b11b607eabcafb9f2b52888481c.tar.gz
Bug 1570615: TLS GREASE (RFC8701) r=djackson
Differential Revision: https://phabricator.services.mozilla.com/D161806
Diffstat (limited to 'lib')
-rw-r--r--lib/ssl/ssl.h27
-rw-r--r--lib/ssl/ssl3con.c53
-rw-r--r--lib/ssl/ssl3ecc.c14
-rw-r--r--lib/ssl/ssl3ext.c19
-rw-r--r--lib/ssl/ssl3exthandle.c28
-rw-r--r--lib/ssl/sslimpl.h26
-rw-r--r--lib/ssl/sslsecur.c2
-rw-r--r--lib/ssl/sslsock.c5
-rw-r--r--lib/ssl/sslt.h2
-rw-r--r--lib/ssl/tls13con.c166
-rw-r--r--lib/ssl/tls13con.h6
-rw-r--r--lib/ssl/tls13ech.c17
-rw-r--r--lib/ssl/tls13exthandle.c82
-rw-r--r--lib/ssl/tls13exthandle.h6
14 files changed, 428 insertions, 25 deletions
diff --git a/lib/ssl/ssl.h b/lib/ssl/ssl.h
index 8b6aab20b..d2e92b170 100644
--- a/lib/ssl/ssl.h
+++ b/lib/ssl/ssl.h
@@ -346,6 +346,33 @@ SSL_IMPORT PRFileDesc *DTLS_ImportFD(PRFileDesc *model, PRFileDesc *fd);
*/
#define SSL_SUPPRESS_END_OF_EARLY_DATA 41
+/* Enables TLS GREASE (specified in RFC8701, following Chrome 55 implementation
+ * decisions).
+ *
+ * If enabled and the client's ss->vrange.max >= SSL_LIBRARY_VERSION_TLS_1_3 or
+ * the server's ss->version >= SSL_LIBRARY_VERSION_TLS_1_3, this adds random
+ * GREASE values to:
+ * - ClientHello (Client):
+ * - A cipher_suite value to the cipher_suites field.
+ * - An empty and a 1B zeroed payload extension.
+ * - A named group value to the supported_groups extension and a
+ * KeyShareEntry value for the added named group.
+ * - A signature algorithm value to the signature_algorithms extension.
+ * - A version value to the supported_versions extension.
+ * - A PskKeyExchangeMode value to the psk_key_exchange_modes extension.
+ * - A alpn value to the application_layer_protocol_negotiation extension.
+ *
+ * - CertificateRequest (Server):
+ * - An empty extension.
+ * - A signature algorithm value to the signature_algorithms extension.
+ *
+ * - NewSessionTicket (Server):
+ * - An empty extension.
+ *
+ * GREASE values MUST nerver be negotiated but ignored.
+ */
+#define SSL_ENABLE_GREASE 42
+
#ifdef SSL_DEPRECATED_FUNCTION
/* Old deprecated function names */
SSL_IMPORT SECStatus SSL_Enable(PRFileDesc *fd, int option, PRIntn on);
diff --git a/lib/ssl/ssl3con.c b/lib/ssl/ssl3con.c
index 8e418a458..982d8587b 100644
--- a/lib/ssl/ssl3con.c
+++ b/lib/ssl/ssl3con.c
@@ -5099,6 +5099,18 @@ ssl3_AppendCipherSuites(sslSocket *ss, PRBool fallbackSCSV, sslBuffer *buf)
}
}
}
+
+ /* GREASE CipherSuites:
+ * A client MAY select one or more GREASE cipher suite values and advertise
+ * them in the "cipher_suites" field [RFC8701, Section 3.1]. */
+ if (ss->opt.enableGrease && ss->vrange.max >= SSL_LIBRARY_VERSION_TLS_1_3) {
+ rv = sslBuffer_AppendNumber(buf, ss->ssl3.hs.grease->idx[grease_cipher],
+ sizeof(ssl3CipherSuite));
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ }
+
if (SSL_ALL_VERSIONS_DISABLED(&ss->vrange) ||
(SSL_BUFFER_LEN(buf) - saveLen) == 0) {
PORT_SetError(SSL_ERROR_SSL_DISABLED);
@@ -10200,7 +10212,7 @@ ssl3_SendServerKeyExchange(sslSocket *ss)
SECStatus
ssl3_EncodeSigAlgs(const sslSocket *ss, PRUint16 minVersion, PRBool forCert,
- sslBuffer *buf)
+ PRBool grease, sslBuffer *buf)
{
SSLSignatureScheme filtered[MAX_SIGNATURE_SCHEMES] = { 0 };
unsigned int filteredCount = 0;
@@ -10211,12 +10223,12 @@ ssl3_EncodeSigAlgs(const sslSocket *ss, PRUint16 minVersion, PRBool forCert,
if (rv != SECSuccess) {
return SECFailure;
}
- return ssl3_EncodeFilteredSigAlgs(ss, filtered, filteredCount, buf);
+ return ssl3_EncodeFilteredSigAlgs(ss, filtered, filteredCount, grease, buf);
}
SECStatus
ssl3_EncodeFilteredSigAlgs(const sslSocket *ss, const SSLSignatureScheme *schemes,
- PRUint32 numSchemes, sslBuffer *buf)
+ PRUint32 numSchemes, PRBool grease, sslBuffer *buf)
{
if (!numSchemes) {
PORT_SetError(SSL_ERROR_NO_SUPPORTED_SIGNATURE_ALGORITHM);
@@ -10237,6 +10249,35 @@ ssl3_EncodeFilteredSigAlgs(const sslSocket *ss, const SSLSignatureScheme *scheme
return SECFailure;
}
}
+
+ /* GREASE SignatureAlgorithms:
+ * A client MAY select one or more GREASE signature algorithm values and
+ * advertise them in the "signature_algorithms" or
+ * "signature_algorithms_cert" extensions, if sent [RFC8701, Section 3.1].
+ *
+ * When sending a CertificateRequest in TLS 1.3, a server MAY behave as
+ * follows: [...] A server MAY select one or more GREASE signature
+ * algorithm values and advertise them in the "signature_algorithms" or
+ * "signature_algorithms_cert" extensions, if present
+ * [RFC8701, Section 4.1]. */
+ if (grease &&
+ ((!ss->sec.isServer && ss->vrange.max >= SSL_LIBRARY_VERSION_TLS_1_3) ||
+ (ss->sec.isServer && ss->version >= SSL_LIBRARY_VERSION_TLS_1_3))) {
+ PRUint16 value;
+ if (ss->sec.isServer) {
+ rv = tls13_RandomGreaseValue(&value);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ } else {
+ value = ss->ssl3.hs.grease->idx[grease_sigalg];
+ }
+ rv = sslBuffer_AppendNumber(buf, value, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ }
+
return sslBuffer_InsertLength(buf, lengthOffset, 2);
}
@@ -10328,7 +10369,8 @@ ssl3_SendCertificateRequest(sslSocket *ss)
length = 1 + certTypesLength + 2 + calen;
if (isTLS12) {
- rv = ssl3_EncodeSigAlgs(ss, ss->version, PR_TRUE /* forCert */, &sigAlgsBuf);
+ rv = ssl3_EncodeSigAlgs(ss, ss->version, PR_TRUE /* forCert */,
+ PR_FALSE /* GREASE */, &sigAlgsBuf);
if (rv != SECSuccess) {
return rv;
}
@@ -14089,6 +14131,9 @@ ssl3_DestroySSL3Info(sslSocket *ss)
PK11_HPKE_DestroyContext(ss->ssl3.hs.echHpkeCtx, PR_TRUE);
PORT_Free((void *)ss->ssl3.hs.echPublicName); /* CONST */
sslBuffer_Clear(&ss->ssl3.hs.greaseEchBuf);
+
+ /* TLS 1.3 GREASE (client) state. */
+ tls13_ClientGreaseDestroy(ss);
}
/* check if the current cipher spec is FIPS. We only need to
diff --git a/lib/ssl/ssl3ecc.c b/lib/ssl/ssl3ecc.c
index d5ad372e5..168ec59bf 100644
--- a/lib/ssl/ssl3ecc.c
+++ b/lib/ssl/ssl3ecc.c
@@ -906,6 +906,20 @@ ssl_SendSupportedGroupsXtn(const sslSocket *ss, TLSExtensionData *xtnData,
}
}
+ /* GREASE SupportedGroups:
+ * A client MAY select one or more GREASE named group values and advertise
+ * them in the "supported_groups" extension, if sent [RFC8701, Section 3.1].
+ */
+ if (!ss->sec.isServer &&
+ ss->opt.enableGrease &&
+ ss->vrange.max >= SSL_LIBRARY_VERSION_TLS_1_3) {
+ rv = sslBuffer_AppendNumber(buf, ss->ssl3.hs.grease->idx[grease_group], 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ found = PR_TRUE;
+ }
+
if (!found) {
/* We added nothing, don't send the extension. */
return SECSuccess;
diff --git a/lib/ssl/ssl3ext.c b/lib/ssl/ssl3ext.c
index afb9d3dfb..04731115f 100644
--- a/lib/ssl/ssl3ext.c
+++ b/lib/ssl/ssl3ext.c
@@ -123,6 +123,8 @@ static const ssl3ExtensionHandler certificateRequestHandlers[] = {
* extension, if it were listed last). See bug 1243641.
*/
static const sslExtensionBuilder clientHelloSendersTLS[] = {
+ /* TLS 1.3 GREASE extensions - empty. */
+ { ssl_tls13_grease_xtn, &tls13_SendEmptyGreaseXtn },
{ ssl_server_name_xtn, &ssl3_ClientSendServerNameXtn },
{ ssl_extended_master_secret_xtn, &ssl3_SendExtendedMasterSecretXtn },
{ ssl_renegotiation_info_xtn, &ssl3_SendRenegotiationInfoXtn },
@@ -146,6 +148,8 @@ static const sslExtensionBuilder clientHelloSendersTLS[] = {
{ ssl_tls13_psk_key_exchange_modes_xtn, &tls13_ClientSendPskModesXtn },
{ ssl_tls13_post_handshake_auth_xtn, &tls13_ClientSendPostHandshakeAuthXtn },
{ ssl_record_size_limit_xtn, &ssl_SendRecordSizeLimitXtn },
+ /* TLS 1.3 GREASE extensions - 1 zero byte. */
+ { ssl_tls13_grease_xtn, &tls13_SendGreaseXtn },
/* The pre_shared_key extension MUST be last. */
{ ssl_tls13_pre_shared_key_xtn, &tls13_ClientSendPreSharedKeyXtn },
{ 0, NULL }
@@ -159,6 +163,8 @@ static const sslExtensionBuilder clientHelloSendersSSL3[] = {
static const sslExtensionBuilder tls13_cert_req_senders[] = {
{ ssl_signature_algorithms_xtn, &ssl3_SendSigAlgsXtn },
{ ssl_tls13_certificate_authorities_xtn, &tls13_SendCertAuthoritiesXtn },
+ /* TLS 1.3 GREASE extension. */
+ { ssl_tls13_grease_xtn, &tls13_SendEmptyGreaseXtn },
{ 0, NULL }
};
@@ -791,6 +797,7 @@ ssl_ConstructExtensions(sslSocket *ss, sslBuffer *buf, SSLHandshakeType message)
}
for (; sender->ex_sender != NULL; ++sender) {
+ PRUint16 ex_type = sender->ex_type;
PRBool append = PR_FALSE;
unsigned int start = buf->len;
unsigned int length;
@@ -814,8 +821,14 @@ ssl_ConstructExtensions(sslSocket *ss, sslBuffer *buf, SSLHandshakeType message)
continue;
}
- buf->len = start;
- rv = sslBuffer_AppendNumber(buf, sender->ex_type, 2);
+ /* If TLS 1.3 GREASE is enabled, replace ssl_tls13_grease_xtn dummy
+ * GREASE extension types with randomly generated GREASE value. */
+ rv = tls13_MaybeGreaseExtensionType(ss, message, &ex_type);
+ if (rv != SECSuccess) {
+ goto loser; /* Code already set. */
+ }
+
+ rv = sslBuffer_AppendNumber(buf, ex_type, 2);
if (rv != SECSuccess) {
goto loser; /* Code already set. */
}
@@ -829,7 +842,7 @@ ssl_ConstructExtensions(sslSocket *ss, sslBuffer *buf, SSLHandshakeType message)
if (message == ssl_hs_client_hello ||
message == ssl_hs_certificate_request) {
ss->xtnData.advertised[ss->xtnData.numAdvertised++] =
- sender->ex_type;
+ ex_type;
}
}
diff --git a/lib/ssl/ssl3exthandle.c b/lib/ssl/ssl3exthandle.c
index 7134447bf..9dd4fba51 100644
--- a/lib/ssl/ssl3exthandle.c
+++ b/lib/ssl/ssl3exthandle.c
@@ -464,7 +464,10 @@ ssl3_ClientSendAppProtoXtn(const sslSocket *ss, TLSExtensionData *xtnData,
}
if (len > 0) {
- /* Each protocol string is prefixed with a single byte length. */
+ /* Each protocol string is prefixed with a single byte length.
+ *
+ * The comment correctly states that this should be a 1 byte length,
+ * see bug 1804688! */
rv = sslBuffer_AppendNumber(buf, len, 2);
if (rv != SECSuccess) {
return SECFailure;
@@ -475,6 +478,26 @@ ssl3_ClientSendAppProtoXtn(const sslSocket *ss, TLSExtensionData *xtnData,
}
}
+ /* GREASE ALPN:
+ * A client MAY select one or more GREASE ALPN identifiers and advertise
+ * them in the "application_layer_protocol_negotiation" extension, if sent
+ * [RFC8701, Section 3.1]. */
+ if (ss->opt.enableGrease && ss->vrange.max >= SSL_LIBRARY_VERSION_TLS_1_3) {
+ /* Each protocol string is prefixed with a single byte length.
+ *
+ * The comment correctly states that this should be a 1 byte length,
+ * see bug 1804688! We send a GREASE extension with incorrect length
+ * field for consistency with (incorrect) non-GREASE extensions. */
+ rv = sslBuffer_AppendNumber(buf, 2, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ rv = sslBuffer_AppendNumber(buf, ss->ssl3.hs.grease->idx[grease_alpn], 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ }
+
*added = PR_TRUE;
return SECSuccess;
}
@@ -1648,7 +1671,8 @@ ssl3_SendSigAlgsXtn(const sslSocket *ss, TLSExtensionData *xtnData,
minVersion = ss->vrange.min; /* ClientHello */
}
- SECStatus rv = ssl3_EncodeSigAlgs(ss, minVersion, PR_TRUE /* forCert */, buf);
+ SECStatus rv = ssl3_EncodeSigAlgs(ss, minVersion, PR_TRUE /* forCert */,
+ ss->opt.enableGrease, buf);
if (rv != SECSuccess) {
return SECFailure;
}
diff --git a/lib/ssl/sslimpl.h b/lib/ssl/sslimpl.h
index af61823c1..64c657788 100644
--- a/lib/ssl/sslimpl.h
+++ b/lib/ssl/sslimpl.h
@@ -290,6 +290,7 @@ typedef struct sslOptionsStr {
unsigned int enableTls13GreaseEch : 1;
unsigned int enableTls13BackendEch : 1;
unsigned int callExtensionWriterOnEchInner : 1;
+ unsigned int enableGrease : 1;
} sslOptions;
typedef enum { sslHandshakingUndetermined = 0,
@@ -610,6 +611,24 @@ typedef struct {
PRUint32 timeout;
} dtlsTimer;
+/* TLS 1.3 client GREASE entry indices. */
+typedef enum {
+ grease_cipher,
+ grease_extension1,
+ grease_extension2,
+ grease_group,
+ grease_sigalg,
+ grease_version,
+ grease_alpn,
+ grease_entries
+} tls13ClientGreaseEntry;
+
+/* TLS 1.3 client GREASE values struct. */
+typedef struct tls13ClientGreaseStr {
+ PRUint16 idx[grease_entries];
+ PRUint8 pskKem;
+} tls13ClientGrease;
+
/*
** This is the "hs" member of the "ssl3" struct.
** This entire struct is protected by ssl3HandshakeLock
@@ -762,6 +781,9 @@ typedef struct SSL3HandshakeStateStr {
sslBuffer greaseEchBuf; /* Client: Remember GREASE ECH, as advertised, for CH2 (HRR case).
Server: Remember HRR Grease Value, for transcript calculations */
PRBool echInvalidExtension; /* Client: True if the server offered an invalid extension for the ClientHelloInner */
+
+ /* TLS 1.3 GREASE state. */
+ tls13ClientGrease *grease;
} SSL3HandshakeState;
#define SSL_ASSERT_HASHES_EMPTY(ss) \
@@ -1740,10 +1762,10 @@ SECStatus ssl3_AuthCertificate(sslSocket *ss);
SECStatus ssl_ReadCertificateStatus(sslSocket *ss, PRUint8 *b,
PRUint32 length);
SECStatus ssl3_EncodeSigAlgs(const sslSocket *ss, PRUint16 minVersion, PRBool forCert,
- sslBuffer *buf);
+ PRBool grease, sslBuffer *buf);
SECStatus ssl3_EncodeFilteredSigAlgs(const sslSocket *ss,
const SSLSignatureScheme *schemes,
- PRUint32 numSchemes, sslBuffer *buf);
+ PRUint32 numSchemes, PRBool grease, sslBuffer *buf);
SECStatus ssl3_FilterSigAlgs(const sslSocket *ss, PRUint16 minVersion, PRBool disableRsae, PRBool forCert,
unsigned int maxSchemes, SSLSignatureScheme *filteredSchemes,
unsigned int *numFilteredSchemes);
diff --git a/lib/ssl/sslsecur.c b/lib/ssl/sslsecur.c
index e1f53108c..37affd36a 100644
--- a/lib/ssl/sslsecur.c
+++ b/lib/ssl/sslsecur.c
@@ -217,6 +217,8 @@ SSL_ResetHandshake(PRFileDesc *s, PRBool asServer)
sslBuffer_Clear(&ss->ssl3.hs.greaseEchBuf);
}
+ tls13_ClientGreaseDestroy(ss);
+
if (!ss->TCPconnected)
ss->TCPconnected = (PR_SUCCESS == ssl_DefGetpeername(ss, &addr));
diff --git a/lib/ssl/sslsock.c b/lib/ssl/sslsock.c
index 4843b990e..3d1603d92 100644
--- a/lib/ssl/sslsock.c
+++ b/lib/ssl/sslsock.c
@@ -96,6 +96,7 @@ static sslOptions ssl_defaults = {
.enableTls13GreaseEch = PR_FALSE,
.enableTls13BackendEch = PR_FALSE,
.callExtensionWriterOnEchInner = PR_FALSE,
+ .enableGrease = PR_FALSE,
};
/*
@@ -891,6 +892,10 @@ SSL_OptionSet(PRFileDesc *fd, PRInt32 which, PRIntn val)
ss->opt.suppressEndOfEarlyData = val;
break;
+ case SSL_ENABLE_GREASE:
+ ss->opt.enableGrease = val;
+ break;
+
default:
PORT_SetError(SEC_ERROR_INVALID_ARGS);
rv = SECFailure;
diff --git a/lib/ssl/sslt.h b/lib/ssl/sslt.h
index 95f4d744a..d12b3c91f 100644
--- a/lib/ssl/sslt.h
+++ b/lib/ssl/sslt.h
@@ -551,6 +551,8 @@ typedef enum {
ssl_tls13_post_handshake_auth_xtn = 49,
ssl_signature_algorithms_cert_xtn = 50,
ssl_tls13_key_share_xtn = 51,
+ /* TLS 1.3 GREASE extension dummy type for builders. */
+ ssl_tls13_grease_xtn = 0x0a0a,
ssl_next_proto_nego_xtn = 13172, /* Deprecated. */
ssl_renegotiation_info_xtn = 0xff01,
ssl_tls13_short_header_xtn = 0xff03, /* Deprecated. */
diff --git a/lib/ssl/tls13con.c b/lib/ssl/tls13con.c
index 4cfb6c360..144731982 100644
--- a/lib/ssl/tls13con.c
+++ b/lib/ssl/tls13con.c
@@ -454,6 +454,11 @@ tls13_SetupClientHello(sslSocket *ss, sslClientHelloType chType)
return SECSuccess;
}
+ rv = tls13_ClientGreaseSetup(ss);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
/* Select the first enabled group.
* TODO(ekr@rtfm.com): be smarter about offering the group
* that the other side negotiated if we are resuming. */
@@ -1563,6 +1568,9 @@ tls13_NegotiateKeyExchange(sslSocket *ss,
missing_extension);
return SECFailure;
}
+ /* Since the server insists on DHE to provide forward secracy, for
+ * every other PskKem value but DHE stateless resumption is disabled,
+ * this includes other specified and GREASE values. */
if (!memchr(ss->xtnData.psk_ke_modes.data, tls13_psk_dh_ke,
ss->xtnData.psk_ke_modes.len)) {
SSL_TRC(3, ("%d: TLS13[%d]: client offered PSK without DH",
@@ -5294,6 +5302,7 @@ tls13_SendNewSessionTicket(sslSocket *ss, const PRUint8 *appToken,
SECStatus rv;
NewSessionTicket ticket = { 0 };
PRUint32 max_early_data_size_len = 0;
+ PRUint32 greaseLen = 0;
PRUint8 ticketNonce[sizeof(ss->ssl3.hs.ticketNonce)];
sslBuffer ticketNonceBuf = SSL_BUFFER(ticketNonce);
@@ -5307,6 +5316,10 @@ tls13_SendNewSessionTicket(sslSocket *ss, const PRUint8 *appToken,
}
ticket.ticket_lifetime_hint = ssl_ticket_lifetime;
+ if (ss->opt.enableGrease) {
+ greaseLen = 4; /* type + len + 0 (empty) */
+ }
+
/* The ticket age obfuscator. */
rv = PK11_GenerateRandom((PRUint8 *)&ticket.ticket_age_add,
sizeof(ticket.ticket_age_add));
@@ -5338,11 +5351,13 @@ tls13_SendNewSessionTicket(sslSocket *ss, const PRUint8 *appToken,
goto loser;
message_length =
- 4 + /* lifetime */
- 4 + /* ticket_age_add */
- 1 + sizeof(ticketNonce) + /* ticket_nonce */
- 2 + max_early_data_size_len + /* max_early_data_size_len */
- 2 + /* ticket length */
+ 4 + /* lifetime */
+ 4 + /* ticket_age_add */
+ 1 + sizeof(ticketNonce) + /* ticket_nonce */
+ 2 + /* extensions lentgh */
+ max_early_data_size_len + /* max_early_data_size extension length */
+ greaseLen + /* GREASE extension length */
+ 2 + /* ticket length */
ticket_data.len;
rv = ssl3_AppendHandshakeHeader(ss, ssl_hs_new_session_ticket,
@@ -5370,11 +5385,33 @@ tls13_SendNewSessionTicket(sslSocket *ss, const PRUint8 *appToken,
if (rv != SECSuccess)
goto loser;
- /* Extensions. */
- rv = ssl3_AppendHandshakeNumber(ss, max_early_data_size_len, 2);
+ /* Extensions */
+ rv = ssl3_AppendHandshakeNumber(ss, max_early_data_size_len + greaseLen, 2);
if (rv != SECSuccess)
goto loser;
+ /* GREASE NewSessionTicket:
+ * When sending a NewSessionTicket message in TLS 1.3, a server MAY select
+ * one or more GREASE extension values and advertise them as extensions
+ * with varying length and contents [RFC8701, SEction 4.1]. */
+ if (ss->opt.enableGrease) {
+ PR_ASSERT(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3);
+
+ PRUint16 grease;
+ rv = tls13_RandomGreaseValue(&grease);
+ if (rv != SECSuccess)
+ goto loser;
+ /* Extension type */
+ rv = ssl3_AppendHandshakeNumber(ss, grease, 2);
+ if (rv != SECSuccess)
+ goto loser;
+ /* Extension length */
+ rv = ssl3_AppendHandshakeNumber(ss, 0, 2);
+ if (rv != SECSuccess)
+ goto loser;
+ }
+
+ /* Max early data size extension. */
if (max_early_data_size_len) {
rv = ssl3_AppendHandshakeNumber(
ss, ssl_tls13_early_data_xtn, 2);
@@ -6415,4 +6452,117 @@ tls13_MaybeTls13(sslSocket *ss)
}
return PR_FALSE;
-} \ No newline at end of file
+}
+
+/* Setup random client GREASE values according to RFC8701. State must be kept
+ * so an equal ClientHello might be send on HelloRetryRequest. */
+SECStatus
+tls13_ClientGreaseSetup(sslSocket *ss)
+{
+ if (!ss->opt.enableGrease) {
+ return SECSuccess;
+ }
+
+ PORT_Assert(ss->vrange.max >= SSL_LIBRARY_VERSION_TLS_1_3);
+
+ if (ss->ssl3.hs.grease) {
+ return SECFailure;
+ }
+ ss->ssl3.hs.grease = PORT_Alloc(sizeof(tls13ClientGrease));
+ if (!ss->ssl3.hs.grease) {
+ return SECFailure;
+ }
+
+ tls13ClientGrease *grease = ss->ssl3.hs.grease;
+ /* We require eight GREASE values and randoms. */
+ PRUint8 random[8];
+
+ /* Generate random GREASE values. */
+ if (PK11_GenerateRandom(random, sizeof(random)) != SECSuccess) {
+ return SECFailure;
+ }
+ for (size_t i = 0; i < PR_ARRAY_SIZE(grease->idx); i++) {
+ random[i] = ((random[i] & 0xf0) | 0x0a);
+ grease->idx[i] = ((random[i] << 8) | random[i]);
+ }
+ /* Specific PskKeyExchangeMode GREASE value. */
+ grease->pskKem = 0x0b + ((random[8 - 1] >> 5) * 0x1f);
+
+ /* Duplicate extensions are not allowed. */
+ if (grease->idx[grease_extension1] == grease->idx[grease_extension2]) {
+ grease->idx[grease_extension2] ^= 0x1010;
+ }
+
+ return SECSuccess;
+}
+
+/* Destroy client GREASE state. */
+void
+tls13_ClientGreaseDestroy(sslSocket *ss)
+{
+ if (ss->ssl3.hs.grease) {
+ PORT_Free(ss->ssl3.hs.grease);
+ ss->ssl3.hs.grease = NULL;
+ }
+}
+
+/* Generate a random GREASE value according to RFC8701.
+ * This function does not provide valid PskKeyExchangeMode GREASE values! */
+SECStatus
+tls13_RandomGreaseValue(PRUint16 *out)
+{
+ PRUint8 random;
+
+ if (PK11_GenerateRandom(&random, sizeof(random)) != SECSuccess) {
+ return SECFailure;
+ }
+
+ random = ((random & 0xf0) | 0x0a);
+ *out = ((random << 8) | random);
+
+ return SECSuccess;
+}
+
+/* Set TLS 1.3 GREASE Extension random GREASE type. */
+SECStatus
+tls13_MaybeGreaseExtensionType(const sslSocket *ss,
+ const SSLHandshakeType message,
+ PRUint16 *exType)
+{
+ if (*exType != ssl_tls13_grease_xtn) {
+ return SECSuccess;
+ }
+
+ PR_ASSERT(ss->opt.enableGrease);
+ PR_ASSERT(message == ssl_hs_client_hello ||
+ message == ssl_hs_certificate_request);
+
+ /* GREASE ClientHello:
+ * A client MAY select one or more GREASE extension values and
+ * advertise them as extensions with varying length and contents
+ * [RFC8701, Section 3.1]. */
+ if (message == ssl_hs_client_hello) {
+ PR_ASSERT(ss->vrange.max >= SSL_LIBRARY_VERSION_TLS_1_3);
+ /* Check if the first GREASE extension was already added. */
+ if (!ssl3_ExtensionAdvertised(ss, ss->ssl3.hs.grease->idx[grease_extension1])) {
+ *exType = ss->ssl3.hs.grease->idx[grease_extension1];
+ } else {
+ *exType = ss->ssl3.hs.grease->idx[grease_extension2];
+ }
+ }
+ /* GREASE CertificateRequest:
+ * When sending a CertificateRequest in TLS 1.3, a server MAY behave as
+ * follows: A server MAY select one or more GREASE extension values and
+ * advertise them as extensions with varying length and contents
+ * [RFC8701, Section 4.1]. */
+ else if (message == ssl_hs_certificate_request) {
+ PR_ASSERT(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3);
+ /* Get random grease extension type. */
+ SECStatus rv = tls13_RandomGreaseValue(exType);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ }
+
+ return SECSuccess;
+}
diff --git a/lib/ssl/tls13con.h b/lib/ssl/tls13con.h
index a635dbd86..1e082d639 100644
--- a/lib/ssl/tls13con.h
+++ b/lib/ssl/tls13con.h
@@ -152,6 +152,12 @@ SECStatus tls13_AEAD(PK11Context *context, PRBool decrypt,
const unsigned char *in, unsigned int inLen);
void tls13_SetSpecRecordVersion(sslSocket *ss, ssl3CipherSpec *spec);
SECStatus SSLExp_SendCertificateRequest(PRFileDesc *fd);
+SECStatus tls13_ClientGreaseSetup(sslSocket *ss);
+void tls13_ClientGreaseDestroy(sslSocket *ss);
+SECStatus tls13_RandomGreaseValue(PRUint16 *out);
+SECStatus tls13_MaybeGreaseExtensionType(const sslSocket *ss,
+ const SSLHandshakeType message,
+ PRUint16 *exType);
/* Use this instead of FATAL_ERROR when no alert shall be sent. */
#define LOG_ERROR(ss, prError) \
diff --git a/lib/ssl/tls13ech.c b/lib/ssl/tls13ech.c
index 7b485720c..9808a9fe2 100644
--- a/lib/ssl/tls13ech.c
+++ b/lib/ssl/tls13ech.c
@@ -1420,23 +1420,34 @@ tls13_ConstructInnerExtensionsFromOuter(sslSocket *ss, sslBuffer *chOuterXtnsBuf
}
break;
case ssl_tls13_supported_versions_xtn:
- /* Only TLS 1.3 on CHInner. */
+ /* Only TLS 1.3 and GREASE on CHInner. */
rv = sslBuffer_AppendNumber(chInnerXtns, extensionType, 2);
if (rv != SECSuccess) {
goto loser;
}
- rv = sslBuffer_AppendNumber(chInnerXtns, 3, 2);
+ /* Extension length. */
+ tmpLen = (ss->opt.enableGrease) ? 5 : 3;
+ rv = sslBuffer_AppendNumber(chInnerXtns, tmpLen, 2);
if (rv != SECSuccess) {
goto loser;
}
- rv = sslBuffer_AppendNumber(chInnerXtns, 2, 1);
+ /* ProtocolVersion length */
+ rv = sslBuffer_AppendNumber(chInnerXtns, tmpLen - 1, 1);
if (rv != SECSuccess) {
goto loser;
}
+ /* ProtocolVersion TLS 1.3 */
rv = sslBuffer_AppendNumber(chInnerXtns, SSL_LIBRARY_VERSION_TLS_1_3, 2);
if (rv != SECSuccess) {
goto loser;
}
+ /* ProtocolVersion GREASE */
+ if (ss->opt.enableGrease) {
+ rv = sslBuffer_AppendNumber(chInnerXtns, ss->ssl3.hs.grease->idx[grease_version], 2);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ }
/* Only update state on second invocation of this function */
if (shouldCompress) {
ss->xtnData.echAdvertised[ss->xtnData.echNumAdvertised++] = extensionType;
diff --git a/lib/ssl/tls13exthandle.c b/lib/ssl/tls13exthandle.c
index 91f96b933..4d24b37d6 100644
--- a/lib/ssl/tls13exthandle.c
+++ b/lib/ssl/tls13exthandle.c
@@ -154,6 +154,29 @@ tls13_ClientSendKeyShareXtn(const sslSocket *ss, TLSExtensionData *xtnData,
return SECFailure;
}
}
+
+ /* GREASE KeyShareEntry:
+ * [The client] MAY also send KeyShareEntry values for a subset of those
+ * selected in the "key_share" extension. For each of these, the
+ * "key_exchange" field MAY be any value [RFC8701, Section 3.1].
+ *
+ * By default we do not send KeyShares for every NamedGroup so the
+ * ServerKeyShare handshake message / additional round-trip is not
+ * triggered by sending GREASE KeyShareEntries. */
+ if (ss->opt.enableGrease) {
+ rv = sslBuffer_AppendNumber(buf, ss->ssl3.hs.grease->idx[grease_group], 2);
+ if (rv != SECSuccess)
+ return rv;
+ /* Entry length */
+ rv = sslBuffer_AppendNumber(buf, 2, 2);
+ if (rv != SECSuccess)
+ return rv;
+ /* Entry value */
+ rv = sslBuffer_AppendNumber(buf, 0xCD, 2);
+ if (rv != SECSuccess)
+ return rv;
+ }
+
rv = sslBuffer_InsertLength(buf, lengthOffset, 2);
if (rv != SECSuccess) {
return SECFailure;
@@ -877,6 +900,16 @@ tls13_ClientSendSupportedVersionsXtn(const sslSocket *ss, TLSExtensionData *xtnD
}
}
+ /* GREASE SupportedVersions:
+ * A client MAY select one or more GREASE version values and advertise them
+ * in the "supported_versions" extension, if sent [RFC8701, Section 3.1]. */
+ if (ss->opt.enableGrease) {
+ rv = sslBuffer_AppendNumber(buf, ss->ssl3.hs.grease->idx[grease_version], 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ }
+
rv = sslBuffer_InsertLength(buf, lengthOffset, 1);
if (rv != SECSuccess) {
return SECFailure;
@@ -1045,7 +1078,6 @@ SECStatus
tls13_ClientSendPskModesXtn(const sslSocket *ss, TLSExtensionData *xtnData,
sslBuffer *buf, PRBool *added)
{
- static const PRUint8 ke_modes[] = { tls13_psk_dh_ke };
SECStatus rv;
if (ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_3 ||
@@ -1056,7 +1088,15 @@ tls13_ClientSendPskModesXtn(const sslSocket *ss, TLSExtensionData *xtnData,
SSL_TRC(3, ("%d: TLS13[%d]: send psk key exchange modes extension",
SSL_GETPID(), ss->fd));
- rv = sslBuffer_AppendVariable(buf, ke_modes, sizeof(ke_modes), 1);
+ /* GREASE PskKeyExchangeMode:
+ * A client MAY select one or more GREASE PskKeyExchangeMode values and
+ * advertise them in the "psk_key_exchange_modes" extension, if sent
+ * [RFC8701, Section 3.1]. */
+ if (ss->opt.enableGrease) {
+ rv = sslBuffer_AppendVariable(buf, (PRUint8[]){ tls13_psk_dh_ke, ss->ssl3.hs.grease->pskKem }, 2, 1);
+ } else {
+ rv = sslBuffer_AppendVariable(buf, (PRUint8[]){ tls13_psk_dh_ke }, 1, 1);
+ }
if (rv != SECSuccess) {
return SECFailure;
}
@@ -1341,7 +1381,8 @@ tls13_ClientSendDelegatedCredentialsXtn(const sslSocket *ss,
return SECSuccess;
}
- rv = ssl3_EncodeFilteredSigAlgs(ss, filtered, filteredCount, buf);
+ rv = ssl3_EncodeFilteredSigAlgs(ss, filtered, filteredCount,
+ PR_FALSE /* GREASE */, buf);
if (rv != SECSuccess) {
return SECFailure;
}
@@ -1715,3 +1756,38 @@ alert_loser:
PORT_SetError(SSL_ERROR_RX_MALFORMED_ECH_EXTENSION);
return SECFailure;
}
+
+SECStatus
+tls13_SendEmptyGreaseXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added)
+{
+ if (!ss->opt.enableGrease ||
+ (!ss->sec.isServer && ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_3) ||
+ (ss->sec.isServer && ss->version < SSL_LIBRARY_VERSION_TLS_1_3)) {
+ return SECSuccess;
+ }
+
+ *added = PR_TRUE;
+ return SECSuccess;
+}
+
+SECStatus
+tls13_SendGreaseXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added)
+{
+ if (!ss->opt.enableGrease ||
+ (!ss->sec.isServer && ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_3) ||
+ (ss->sec.isServer && ss->version < SSL_LIBRARY_VERSION_TLS_1_3)) {
+ return SECSuccess;
+ }
+
+ SECStatus rv = sslBuffer_AppendVariable(buf, (PRUint8[]){ 0x00 }, 1, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ *added = PR_TRUE;
+ return SECSuccess;
+}
diff --git a/lib/ssl/tls13exthandle.h b/lib/ssl/tls13exthandle.h
index d9bc3530d..fb4a18965 100644
--- a/lib/ssl/tls13exthandle.h
+++ b/lib/ssl/tls13exthandle.h
@@ -115,5 +115,11 @@ SECStatus tls13_ClientSendDelegatedCredentialsXtn(const sslSocket *ss,
SECStatus tls13_ServerHandleDelegatedCredentialsXtn(const sslSocket *ss,
TLSExtensionData *xtnData,
SECItem *data);
+SECStatus tls13_SendEmptyGreaseXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added);
+SECStatus tls13_SendGreaseXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added);
#endif