"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "nss/lib/ssl/tls13ech.c" between
nss-3.61.tar.gz and nss-3.62.tar.gz

About: NSS is a set of libraries, APIs, utilities, and documentation designed to support cross-platform development of security-enabled client and server applications. It provides a complete implementation of the crypto libraries used by Mozilla and other companies.

tls13ech.c  (nss-3.61):tls13ech.c  (nss-3.62)
skipping to change at line 17 skipping to change at line 17
#include "nss.h" #include "nss.h"
#include "pk11func.h" #include "pk11func.h"
#include "pk11hpke.h" #include "pk11hpke.h"
#include "ssl.h" #include "ssl.h"
#include "sslproto.h" #include "sslproto.h"
#include "sslimpl.h" #include "sslimpl.h"
#include "selfencrypt.h" #include "selfencrypt.h"
#include "ssl3exthandle.h" #include "ssl3exthandle.h"
#include "tls13ech.h" #include "tls13ech.h"
#include "tls13exthandle.h" #include "tls13exthandle.h"
#include "tls13hashstate.h"
#include "tls13hkdf.h" #include "tls13hkdf.h"
extern SECStatus extern SECStatus
ssl3_UpdateExplicitHandshakeTranscript(sslSocket *ss, const unsigned char *b, ssl3_UpdateHandshakeHashesInt(sslSocket *ss, const unsigned char *b,
unsigned int l, sslBuffer *transcriptBuf) unsigned int l, sslBuffer *transcriptBuf);
;
extern SECStatus extern SECStatus
ssl3_HandleClientHelloPreamble(sslSocket *ss, PRUint8 **b, PRUint32 *length, SEC Item *sidBytes, ssl3_HandleClientHelloPreamble(sslSocket *ss, PRUint8 **b, PRUint32 *length, SEC Item *sidBytes,
SECItem *cookieBytes, SECItem *suites, SECItem *c omps); SECItem *cookieBytes, SECItem *suites, SECItem *c omps);
extern SECStatus
tls13_DeriveSecret(sslSocket *ss, PK11SymKey *key,
const char *label,
unsigned int labelLen,
const SSL3Hashes *hashes,
PK11SymKey **dest,
SSLHashType hash);
void void
tls13_DestroyEchConfig(sslEchConfig *config) tls13_DestroyEchConfig(sslEchConfig *config)
{ {
if (!config) { if (!config) {
return; return;
} }
SECITEM_FreeItem(&config->contents.publicKey, PR_FALSE); SECITEM_FreeItem(&config->contents.publicKey, PR_FALSE);
SECITEM_FreeItem(&config->contents.suites, PR_FALSE); SECITEM_FreeItem(&config->contents.suites, PR_FALSE);
SECITEM_FreeItem(&config->raw, PR_FALSE); SECITEM_FreeItem(&config->raw, PR_FALSE);
skipping to change at line 51 skipping to change at line 59
tls13_DestroyEchConfigs(PRCList *list) tls13_DestroyEchConfigs(PRCList *list)
{ {
PRCList *cur_p; PRCList *cur_p;
while (!PR_CLIST_IS_EMPTY(list)) { while (!PR_CLIST_IS_EMPTY(list)) {
cur_p = PR_LIST_TAIL(list); cur_p = PR_LIST_TAIL(list);
PR_REMOVE_LINK(cur_p); PR_REMOVE_LINK(cur_p);
tls13_DestroyEchConfig((sslEchConfig *)cur_p); tls13_DestroyEchConfig((sslEchConfig *)cur_p);
} }
} }
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 SECStatus
tls13_CopyEchConfigs(PRCList *oConfigs, PRCList *configs) tls13_CopyEchConfigs(PRCList *oConfigs, PRCList *configs)
{ {
SECStatus rv; SECStatus rv;
sslEchConfig *config; sslEchConfig *config;
sslEchConfig *newConfig = NULL; sslEchConfig *newConfig = NULL;
for (PRCList *cur_p = PR_LIST_HEAD(oConfigs); for (PRCList *cur_p = PR_LIST_HEAD(oConfigs);
cur_p != oConfigs; cur_p != oConfigs;
cur_p = PR_NEXT_LINK(cur_p)) { cur_p = PR_NEXT_LINK(cur_p)) {
skipping to change at line 89 skipping to change at line 110
} }
rv = SECITEM_CopyItem(NULL, &newConfig->contents.suites, rv = SECITEM_CopyItem(NULL, &newConfig->contents.suites,
&config->contents.suites); &config->contents.suites);
if (rv != SECSuccess) { if (rv != SECSuccess) {
goto loser; goto loser;
} }
newConfig->contents.kemId = config->contents.kemId; newConfig->contents.kemId = config->contents.kemId;
newConfig->contents.kdfId = config->contents.kdfId; newConfig->contents.kdfId = config->contents.kdfId;
newConfig->contents.aeadId = config->contents.aeadId; newConfig->contents.aeadId = config->contents.aeadId;
newConfig->contents.maxNameLen = config->contents.maxNameLen; newConfig->contents.maxNameLen = config->contents.maxNameLen;
newConfig->version = config->version;
PORT_Memcpy(newConfig->configId, config->configId, sizeof(newConfig->con figId)); PORT_Memcpy(newConfig->configId, config->configId, sizeof(newConfig->con figId));
PR_APPEND_LINK(&newConfig->link, configs); PR_APPEND_LINK(&newConfig->link, configs);
} }
return SECSuccess; return SECSuccess;
loser: loser:
tls13_DestroyEchConfig(newConfig); tls13_DestroyEchConfig(newConfig);
tls13_DestroyEchConfigs(configs); tls13_DestroyEchConfigs(configs);
return SECFailure; return SECFailure;
} }
skipping to change at line 130 skipping to change at line 152
/* We only support SHA256 KDF. */ /* We only support SHA256 KDF. */
PORT_Assert(cfg->contents.kdfId == HpkeKdfHkdfSha256); PORT_Assert(cfg->contents.kdfId == HpkeKdfHkdfSha256);
params.bExtract = CK_TRUE; params.bExtract = CK_TRUE;
params.bExpand = CK_TRUE; params.bExpand = CK_TRUE;
params.prfHashMechanism = CKM_SHA256; params.prfHashMechanism = CKM_SHA256;
params.ulSaltType = CKF_HKDF_SALT_NULL; params.ulSaltType = CKF_HKDF_SALT_NULL;
params.pInfo = CONST_CAST(CK_BYTE, hHkdfInfoEchConfigID); params.pInfo = CONST_CAST(CK_BYTE, hHkdfInfoEchConfigID);
params.ulInfoLen = strlen(hHkdfInfoEchConfigID); params.ulInfoLen = strlen(hHkdfInfoEchConfigID);
derived = PK11_DeriveWithFlags(configKey, CKM_HKDF_DATA, 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); CKF_SIGN | CKF_VERIFY);
rv = PK11_ExtractKeyValue(derived); rv = PK11_ExtractKeyValue(derived);
if (rv != SECSuccess) { if (rv != SECSuccess) {
goto loser; goto loser;
} }
derivedItem = PK11_GetKeyData(derived); derivedItem = PK11_GetKeyData(derived);
if (!derivedItem) { if (!derivedItem) {
goto loser; goto loser;
skipping to change at line 185 skipping to change at line 207
sslReader configReader = SSL_READER(rawConfig->buf, rawConfig->len); sslReader configReader = SSL_READER(rawConfig->buf, rawConfig->len);
sslReader suiteReader; sslReader suiteReader;
sslReader extensionReader; sslReader extensionReader;
PRBool hasValidSuite = PR_FALSE; PRBool hasValidSuite = PR_FALSE;
/* Parse the public_name. */ /* Parse the public_name. */
rv = sslRead_ReadVariable(&configReader, 2, &tmpBuf); rv = sslRead_ReadVariable(&configReader, 2, &tmpBuf);
if (rv != SECSuccess) { if (rv != SECSuccess) {
goto loser; 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++) { for (tmpn = 0; tmpn < tmpBuf.len; tmpn++) {
if (tmpBuf.buf[tmpn] == '\0') { if (tmpBuf.buf[tmpn] == '\0') {
PORT_SetError(SSL_ERROR_RX_MALFORMED_ECH_CONFIG); PORT_SetError(SSL_ERROR_RX_MALFORMED_ECH_CONFIG);
goto loser; goto loser;
} }
} }
contents.publicName = PORT_ZAlloc(tmpBuf.len + 1); contents.publicName = PORT_ZAlloc(tmpBuf.len + 1);
if (!contents.publicName) { if (!contents.publicName) {
PORT_SetError(SSL_ERROR_RX_MALFORMED_ECH_CONFIG);
goto loser; goto loser;
} }
PORT_Memcpy(contents.publicName, (PRUint8 *)tmpBuf.buf, tmpBuf.len); PORT_Memcpy(contents.publicName, (PRUint8 *)tmpBuf.buf, tmpBuf.len);
/* Public key. */ /* Public key. */
rv = sslRead_ReadVariable(&configReader, 2, &tmpBuf); rv = sslRead_ReadVariable(&configReader, 2, &tmpBuf);
if (rv != SECSuccess) { if (rv != SECSuccess) {
goto loser; goto loser;
} }
rv = SECITEM_MakeItem(NULL, &contents.publicKey, (PRUint8 *)tmpBuf.buf, tmpB uf.len); rv = SECITEM_MakeItem(NULL, &contents.publicKey, (PRUint8 *)tmpBuf.buf, tmpB uf.len);
skipping to change at line 433 skipping to change at line 457
const SECKEYPublicKey *pubKey, PRUint16 maxNameLen, const SECKEYPublicKey *pubKey, PRUint16 maxNameLen,
PRUint8 *out, unsigned int *outlen, unsigned int maxlen) PRUint8 *out, unsigned int *outlen, unsigned int maxlen)
{ {
SECStatus rv; SECStatus rv;
unsigned int savedOffset; unsigned int savedOffset;
unsigned int len; unsigned int len;
sslBuffer b = SSL_BUFFER_EMPTY; sslBuffer b = SSL_BUFFER_EMPTY;
PRUint8 tmpBuf[66]; // Large enough for an EC public key, currently only X25 519. PRUint8 tmpBuf[66]; // Large enough for an EC public key, currently only X25 519.
unsigned int tmpLen; unsigned int tmpLen;
if (!publicName || PORT_Strlen(publicName) == 0 || !hpkeSuites || if (!publicName || !hpkeSuites || hpkeSuiteCount == 0 ||
hpkeSuiteCount == 0 || !pubKey || maxNameLen == 0 || !out || !outlen) { !pubKey || maxNameLen == 0 || !out || !outlen) {
PORT_SetError(SEC_ERROR_INVALID_ARGS); PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure; return SECFailure;
} }
rv = sslBuffer_Skip(&b, 2, NULL); rv = sslBuffer_Skip(&b, 2, NULL);
if (rv != SECSuccess) { if (rv != SECSuccess) {
goto loser; goto loser;
} }
rv = sslBuffer_AppendNumber(&b, TLS13_ECH_VERSION, 2); rv = sslBuffer_AppendNumber(&b, TLS13_ECH_VERSION, 2);
skipping to change at line 538 skipping to change at line 562
PORT_SetError(SEC_ERROR_INVALID_ARGS); PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure; return SECFailure;
} }
ss = ssl_FindSocket(fd); ss = ssl_FindSocket(fd);
if (!ss) { if (!ss) {
SSL_DBG(("%d: SSL[%d]: bad socket in %s", SSL_DBG(("%d: SSL[%d]: bad socket in %s",
SSL_GETPID(), fd, __FUNCTION__)); SSL_GETPID(), fd, __FUNCTION__));
PORT_SetError(SEC_ERROR_INVALID_ARGS); PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure; 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); PORT_SetError(SSL_ERROR_HANDSHAKE_NOT_COMPLETED);
return SECFailure; return SECFailure;
} }
/* May be empty. */ /* May be empty. */
rv = SECITEM_CopyItem(NULL, &out, &ss->xtnData.echRetryConfigs); rv = SECITEM_CopyItem(NULL, &out, &ss->xtnData.ech->retryConfigs);
if (rv == SECFailure) { if (rv == SECFailure) {
return SECFailure; return SECFailure;
} }
*retryConfigs = out; *retryConfigs = out;
return SECSuccess; return SECSuccess;
} }
SECStatus SECStatus
SSLExp_RemoveEchConfigs(PRFileDesc *fd) SSLExp_RemoveEchConfigs(PRFileDesc *fd)
{ {
skipping to change at line 574 skipping to change at line 604
SSL_GETPID(), fd, __FUNCTION__)); SSL_GETPID(), fd, __FUNCTION__));
PORT_SetError(SEC_ERROR_INVALID_ARGS); PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure; return SECFailure;
} }
if (!PR_CLIST_IS_EMPTY(&ss->echConfigs)) { if (!PR_CLIST_IS_EMPTY(&ss->echConfigs)) {
tls13_DestroyEchConfigs(&ss->echConfigs); tls13_DestroyEchConfigs(&ss->echConfigs);
} }
/* Also remove any retry_configs and handshake context. */ /* Also remove any retry_configs and handshake context. */
if (ss->xtnData.echRetryConfigs.len) { if (ss->xtnData.ech && ss->xtnData.ech->retryConfigs.len) {
SECITEM_FreeItem(&ss->xtnData.echRetryConfigs, PR_FALSE); SECITEM_FreeItem(&ss->xtnData.ech->retryConfigs, PR_FALSE);
} }
if (ss->ssl3.hs.echHpkeCtx) { if (ss->ssl3.hs.echHpkeCtx) {
PK11_HPKE_DestroyContext(ss->ssl3.hs.echHpkeCtx, PR_TRUE); PK11_HPKE_DestroyContext(ss->ssl3.hs.echHpkeCtx, PR_TRUE);
ss->ssl3.hs.echHpkeCtx = NULL; ss->ssl3.hs.echHpkeCtx = NULL;
} }
PORT_Free(CONST_CAST(char, ss->ssl3.hs.echPublicName)); PORT_Free(CONST_CAST(char, ss->ssl3.hs.echPublicName));
ss->ssl3.hs.echPublicName = NULL; ss->ssl3.hs.echPublicName = NULL;
return SECSuccess; return SECSuccess;
skipping to change at line 707 skipping to change at line 737
/* Set up ECH. This generates an ephemeral sender /* Set up ECH. This generates an ephemeral sender
* keypair and the HPKE context */ * keypair and the HPKE context */
SECStatus SECStatus
tls13_ClientSetupEch(sslSocket *ss, sslClientHelloType type) tls13_ClientSetupEch(sslSocket *ss, sslClientHelloType type)
{ {
SECStatus rv; SECStatus rv;
HpkeContext *cx = NULL; HpkeContext *cx = NULL;
SECKEYPublicKey *pkR = NULL; SECKEYPublicKey *pkR = NULL;
SECItem hpkeInfo = { siBuffer, NULL, 0 }; SECItem hpkeInfo = { siBuffer, NULL, 0 };
PK11SymKey *hrrPsk = NULL;
sslEchConfig *cfg = 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) || if (PR_CLIST_IS_EMPTY(&ss->echConfigs) ||
!ssl_ShouldSendSNIExtension(ss, ss->url) || !ssl_ShouldSendSNIExtension(ss, ss->url) ||
IS_DTLS(ss)) { IS_DTLS(ss)) {
return SECSuccess; return SECSuccess;
} }
/* Maybe apply our own priority if >1. For now, we only support /* Maybe apply our own priority if >1. For now, we only support
* one version and one KEM. Each ECHConfig can specify multiple * one version and one KEM. Each ECHConfig can specify multiple
* KDF/AEADs, so just use the first. */ * KDF/AEADs, so just use the first. */
skipping to change at line 742 skipping to change at line 765
SSL_TRC(50, ("%d: TLS13[%d]: Setup client ECH", SSL_TRC(50, ("%d: TLS13[%d]: Setup client ECH",
SSL_GETPID(), ss->fd)); SSL_GETPID(), ss->fd));
switch (type) { switch (type) {
case client_hello_initial: case client_hello_initial:
PORT_Assert(!ss->ssl3.hs.echHpkeCtx && !ss->ssl3.hs.echPublicName); PORT_Assert(!ss->ssl3.hs.echHpkeCtx && !ss->ssl3.hs.echPublicName);
cx = PK11_HPKE_NewContext(cfg->contents.kemId, cfg->contents.kdfId, cx = PK11_HPKE_NewContext(cfg->contents.kemId, cfg->contents.kdfId,
cfg->contents.aeadId, NULL, NULL); cfg->contents.aeadId, NULL, NULL);
break; break;
case client_hello_retry: case client_hello_retry:
PORT_Assert(ss->ssl3.hs.echHpkeCtx && ss->ssl3.hs.echPublicName); if (!ss->ssl3.hs.echHpkeCtx || !ss->ssl3.hs.echPublicName) {
rv = PK11_HPKE_ExportSecret(ss->ssl3.hs.echHpkeCtx, FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
&kEchHrrInfoItem, 32, &hrrPsk); return SECFailure;
if (rv != SECSuccess) {
goto loser;
} }
/* Nothing else to do. */
PK11_HPKE_DestroyContext(ss->ssl3.hs.echHpkeCtx, PR_TRUE); return SECSuccess;
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, &kEchHrrPskL
abelItem);
break;
default: default:
PORT_Assert(0); PORT_Assert(0);
goto loser; goto loser;
} }
if (!cx) { if (!cx) {
goto loser; goto loser;
} }
rv = PK11_HPKE_Deserialize(cx, cfg->contents.publicKey.data, cfg->contents.p ublicKey.len, &pkR); rv = PK11_HPKE_Deserialize(cx, cfg->contents.publicKey.data, cfg->contents.p ublicKey.len, &pkR);
if (rv != SECSuccess) { if (rv != SECSuccess) {
skipping to change at line 782 skipping to change at line 797
PORT_Memcpy(&hpkeInfo.data[0], kHpkeInfoEch, strlen(kHpkeInfoEch)); PORT_Memcpy(&hpkeInfo.data[0], kHpkeInfoEch, strlen(kHpkeInfoEch));
PORT_Memset(&hpkeInfo.data[strlen(kHpkeInfoEch)], 0, 1); PORT_Memset(&hpkeInfo.data[strlen(kHpkeInfoEch)], 0, 1);
PORT_Memcpy(&hpkeInfo.data[strlen(kHpkeInfoEch) + 1], cfg->raw.data, cfg->ra w.len); PORT_Memcpy(&hpkeInfo.data[strlen(kHpkeInfoEch) + 1], cfg->raw.data, cfg->ra w.len);
/* Setup with an ephemeral sender keypair. */ /* Setup with an ephemeral sender keypair. */
rv = PK11_HPKE_SetupS(cx, NULL, NULL, pkR, &hpkeInfo); rv = PK11_HPKE_SetupS(cx, NULL, NULL, pkR, &hpkeInfo);
if (rv != SECSuccess) { if (rv != SECSuccess) {
goto loser; goto loser;
} }
if (!ss->ssl3.hs.helloRetry) { rv = ssl3_GetNewRandom(ss->ssl3.hs.client_inner_random);
rv = ssl3_GetNewRandom(ss->ssl3.hs.client_inner_random); if (rv != SECSuccess) {
if (rv != SECSuccess) { goto loser; /* code set */
goto loser; /* code set */
}
} }
/* If ECH is rejected, the application will use SSLChannelInfo /* If ECH is rejected, the application will use SSLChannelInfo
* to fetch this field and perform cert chain verification. */ * to fetch this field and perform cert chain verification. */
ss->ssl3.hs.echPublicName = PORT_Strdup(cfg->contents.publicName); ss->ssl3.hs.echPublicName = PORT_Strdup(cfg->contents.publicName);
if (!ss->ssl3.hs.echPublicName) { if (!ss->ssl3.hs.echPublicName) {
goto loser; goto loser;
} }
ss->ssl3.hs.echHpkeCtx = cx; ss->ssl3.hs.echHpkeCtx = cx;
PK11_FreeSymKey(hrrPsk);
SECKEY_DestroyPublicKey(pkR); SECKEY_DestroyPublicKey(pkR);
SECITEM_FreeItem(&hpkeInfo, PR_FALSE); SECITEM_FreeItem(&hpkeInfo, PR_FALSE);
return SECSuccess; return SECSuccess;
loser: loser:
PK11_HPKE_DestroyContext(cx, PR_TRUE); PK11_HPKE_DestroyContext(cx, PR_TRUE);
PK11_FreeSymKey(hrrPsk);
SECKEY_DestroyPublicKey(pkR); SECKEY_DestroyPublicKey(pkR);
SECITEM_FreeItem(&hpkeInfo, PR_FALSE); SECITEM_FreeItem(&hpkeInfo, PR_FALSE);
PORT_Assert(PORT_GetError() != 0);
return SECFailure; return SECFailure;
} }
/* /*
* enum { * enum {
* encrypted_client_hello(0xfe08), (65535) * encrypted_client_hello(0xfe09), (65535)
* } ExtensionType; * } ExtensionType;
* *
* struct { * struct {
* HpkeKdfId kdf_id; * HpkeKdfId kdf_id;
* HpkeAeadId aead_id; * HpkeAeadId aead_id;
* } ECHCipherSuite; * } ECHCipherSuite;
* struct { * struct {
* ECHCipherSuite cipher_suite; * ECHCipherSuite cipher_suite;
* opaque config_id<0..255>; * opaque config_id<0..255>;
* opaque enc<1..2^16-1>; * opaque enc<1..2^16-1>;
skipping to change at line 874 skipping to change at line 886
/* Format the encrypted_client_hello extension. */ /* Format the encrypted_client_hello extension. */
sslBuffer_Clear(chInner); sslBuffer_Clear(chInner);
rv = sslBuffer_AppendNumber(chInner, cfg->contents.kdfId, 2); rv = sslBuffer_AppendNumber(chInner, cfg->contents.kdfId, 2);
if (rv != SECSuccess) { if (rv != SECSuccess) {
goto loser; goto loser;
} }
rv = sslBuffer_AppendNumber(chInner, cfg->contents.aeadId, 2); rv = sslBuffer_AppendNumber(chInner, cfg->contents.aeadId, 2);
if (rv != SECSuccess) { if (rv != SECSuccess) {
goto loser; goto loser;
} }
rv = sslBuffer_AppendVariable(chInner, cfg->configId, sizeof(cfg->configId),
1); if (!ss->ssl3.hs.helloRetry) {
if (rv != SECSuccess) { rv = sslBuffer_AppendVariable(chInner, cfg->configId, sizeof(cfg->config
goto loser; Id), 1);
} if (rv != SECSuccess) {
rv = sslBuffer_AppendVariable(chInner, hpkeEnc->data, hpkeEnc->len, 2); goto loser;
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); rv = sslBuffer_AppendVariable(chInner, chCt->data, chCt->len, 2);
if (rv != SECSuccess) { if (rv != SECSuccess) {
goto loser; goto loser;
} }
SECITEM_FreeItem(chCt, PR_TRUE); SECITEM_FreeItem(chCt, PR_TRUE);
return SECSuccess; return SECSuccess;
loser: loser:
SECITEM_FreeItem(chCt, PR_TRUE); SECITEM_FreeItem(chCt, PR_TRUE);
return SECFailure; return SECFailure;
} }
SECStatus SECStatus
tls13_GetMatchingEchConfig(const sslSocket *ss, HpkeKdfId kdf, HpkeAeadId aead, tls13_GetMatchingEchConfigs(const sslSocket *ss, HpkeKdfId kdf, HpkeAeadId aead,
const SECItem *configId, sslEchConfig **cfg) const SECItem *configId, const sslEchConfig *cur, ss
lEchConfig **next)
{ {
sslEchConfig *candidate;
PRINT_BUF(50, (ss, "Server GetMatchingEchConfig with digest:", PRINT_BUF(50, (ss, "Server GetMatchingEchConfig with digest:",
configId->data, configId->len)); 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->echCo
nfigs);
cur_p != &ss->echConfigs; cur_p != &ss->echConfigs;
cur_p = PR_NEXT_LINK(cur_p)) { cur_p = PR_NEXT_LINK(cur_p)) {
sslEchConfig *echConfig = (sslEchConfig *)cur_p; sslEchConfig *echConfig = (sslEchConfig *)cur_p;
if (configId->len != sizeof(echConfig->configId) || if (configId->len != sizeof(echConfig->configId) ||
PORT_Memcmp(echConfig->configId, configId->data, sizeof(echConfig->c onfigId))) { PORT_Memcmp(echConfig->configId, configId->data, sizeof(echConfig->c onfigId))) {
continue; continue;
} }
candidate = (sslEchConfig *)PR_LIST_HEAD(&ss->echConfigs); if (echConfig->contents.aeadId == aead &&
if (candidate->contents.aeadId != aead || echConfig->contents.kdfId == kdf) {
candidate->contents.kdfId != kdf) { *next = echConfig;
continue; return SECSuccess;
} }
*cfg = candidate;
return SECSuccess;
} }
SSL_TRC(50, ("%d: TLS13[%d]: Server found no matching ECHConfig", *next = NULL;
SSL_GETPID(), ss->fd));
*cfg = NULL;
return SECSuccess; 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 d
ecrypt the CH2 ECH payload.
* opaque ch_hash[rest_of_buffer]; // H(ClientHello)
* } CookieInner;
*/
SECStatus
tls13_GetEchInfoFromCookie(sslSocket *ss, const TLSExtension *hrrCookie, PK11Sym
Key **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.cooki
e.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 /* Given a CH with extensions, copy from the start up to the extensions
* into |writer| and return the extensions themselves in |extensions|. * into |writer| and return the extensions themselves in |extensions|.
* If |explicitSid|, place this value into |writer| as the SID. Else, * If |explicitSid|, place this value into |writer| as the SID. Else,
* the sid is copied from |reader| to |writer|. */ * the sid is copied from |reader| to |writer|. */
static SECStatus static SECStatus
tls13_CopyChPreamble(sslReader *reader, const SECItem *explicitSid, sslBuffer *w riter, sslReadBuffer *extensions) tls13_CopyChPreamble(sslReader *reader, const SECItem *explicitSid, sslBuffer *w riter, sslReadBuffer *extensions)
{ {
SECStatus rv; SECStatus rv;
sslReadBuffer tmpReadBuf; sslReadBuffer tmpReadBuf;
skipping to change at line 1113 skipping to change at line 1012
} }
if (SSL_READER_REMAINING(reader) != 0) { if (SSL_READER_REMAINING(reader) != 0) {
PORT_SetError(SSL_ERROR_RX_MALFORMED_ECH_EXTENSION); PORT_SetError(SSL_ERROR_RX_MALFORMED_ECH_EXTENSION);
return SECFailure; return SECFailure;
} }
return SECSuccess; 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 static SECStatus
tls13_MakeChOuterAAD(const SECItem *outer, sslBuffer *outerAAD) tls13_MakeChOuterAAD(sslSocket *ss, const SECItem *outer, SECItem *outerAAD)
{ {
SECStatus rv; SECStatus rv;
sslBuffer aad = SSL_BUFFER_EMPTY; sslBuffer aad = SSL_BUFFER_EMPTY;
sslReadBuffer aadXtns; sslReadBuffer aadXtns = { 0 };
sslReader chReader = SSL_READER(outer->data, outer->len); sslReader chReader = SSL_READER(outer->data, outer->len);
PRUint64 tmpn; PRUint64 tmpn;
sslReadBuffer tmpvar; sslReadBuffer tmpvar = { 0 };
unsigned int offset; 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) { if (rv != SECSuccess) {
goto loser; goto loser;
} }
/* aad := preamble, aadXtn := extensions */ /* aad := preamble, aadXtn := extensions */
rv = tls13_CopyChPreamble(&chReader, NULL, &aad, &aadXtns); rv = tls13_CopyChPreamble(&chReader, NULL, &aad, &aadXtns);
if (rv != SECSuccess) { if (rv != SECSuccess) {
goto loser; goto loser;
} }
sslReader xtnsReader = SSL_READER(aadXtns.buf, aadXtns.len); sslReader xtnsReader = SSL_READER(aadXtns.buf, aadXtns.len);
preambleLen = SSL_BUFFER_LEN(&aad);
/* Save room for extensions length. */ /* Save room for extensions length. */
rv = sslBuffer_Skip(&aad, 2, &offset); rv = sslBuffer_Skip(&aad, 2, &offset);
if (rv != SECSuccess) { if (rv != SECSuccess) {
goto loser; goto loser;
} }
/* Append each extension, minus encrypted_client_hello_xtn. */ /* Append each extension, minus encrypted_client_hello_xtn. */
while (SSL_READER_REMAINING(&xtnsReader)) { while (SSL_READER_REMAINING(&xtnsReader)) {
rv = sslRead_ReadNumber(&xtnsReader, 2, &tmpn); rv = sslRead_ReadNumber(&xtnsReader, 2, &tmpn);
skipping to change at line 1168 skipping to change at line 1101
if (rv != SECSuccess) { if (rv != SECSuccess) {
goto loser; goto loser;
} }
rv = sslBuffer_AppendVariable(&aad, tmpvar.buf, tmpvar.len, 2); rv = sslBuffer_AppendVariable(&aad, tmpvar.buf, tmpvar.len, 2);
if (rv != SECSuccess) { if (rv != SECSuccess) {
goto loser; goto loser;
} }
} }
} }
rv = sslBuffer_InsertNumber(&aad, offset, SSL_BUFFER_LEN(&aad) - preambleLen - 2, 2); rv = sslBuffer_InsertLength(&aad, offset, 2);
if (rv != SECSuccess) { if (rv != SECSuccess) {
goto loser; goto loser;
} }
/* Give it a message header. */ rv = sslBuffer_InsertLength(&aad, savedOffset, 3);
rv = sslBuffer_InsertNumber(&aad, 0, ssl_hs_client_hello, 1);
if (rv != SECSuccess) { if (rv != SECSuccess) {
goto loser; goto loser;
} }
rv = sslBuffer_InsertLength(&aad, 1, 3); outerAAD->data = aad.buf;
if (rv != SECSuccess) { outerAAD->len = aad.len;
goto loser;
}
*outerAAD = aad;
return SECSuccess; return SECSuccess;
loser: loser:
sslBuffer_Clear(&aad); sslBuffer_Clear(&aad);
return SECFailure; return SECFailure;
} }
SECStatus SECStatus
tls13_OpenClientHelloInner(sslSocket *ss, const SECItem *outer, sslEchConfig *cf g, PK11SymKey *echHrrPsk, SECItem **chInner) tls13_OpenClientHelloInner(sslSocket *ss, const SECItem *outer, const SECItem *o uterAAD, sslEchConfig *cfg, SECItem **chInner)
{ {
SECStatus rv; SECStatus rv;
sslBuffer outerAAD = SSL_BUFFER_EMPTY;
HpkeContext *cx = NULL; HpkeContext *cx = NULL;
SECItem *decryptedChInner = NULL; SECItem *decryptedChInner = NULL;
SECItem hpkeInfo = { siBuffer, NULL, 0 }; 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(), SSL_TRC(50, ("%d: TLS13[%d]: Server opening ECH Inner%s", SSL_GETPID(),
ss->fd, ss->ssl3.hs.helloRetry ? " after HRR" : "")); ss->fd, ss->ssl3.hs.helloRetry ? " after HRR" : ""));
cx = PK11_HPKE_NewContext(cfg->contents.kemId, cfg->contents.kdfId, if (!ss->ssl3.hs.helloRetry) {
cfg->contents.aeadId, echHrrPsk, PORT_Assert(!ss->ssl3.hs.echHpkeCtx);
echHrrPsk ? &kEchHrrPskLabelItem : NULL); cx = PK11_HPKE_NewContext(cfg->contents.kemId, cfg->contents.kdfId,
if (!cx) { cfg->contents.aeadId, NULL, NULL);
goto loser; 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->ra
w.len);
rv = PK11_HPKE_SetupR(cx, ss->echPubKey, ss->echPrivKey, if (!SECITEM_AllocItem(NULL, &hpkeInfo, strlen(kHpkeInfoEch) + 1 + cfg->
&ss->xtnData.echSenderPubKey, &hpkeInfo); raw.len)) {
if (rv != SECSuccess) { goto loser;
goto loser; /* code set */ }
} 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); rv = PK11_HPKE_SetupR(cx, ss->echPubKey, ss->echPrivKey,
if (rv != SECSuccess) { &ss->xtnData.ech->senderPubKey, &hpkeInfo);
goto loser; /* code set */ 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 #ifndef UNSAFE_FUZZER_MODE
rv = PK11_HPKE_Open(cx, &outerAADItem, &ss->xtnData.innerCh, &decryptedChInn er); rv = PK11_HPKE_Open(cx, outerAAD, &ss->xtnData.ech->innerCh, &decryptedChInn er);
if (rv != SECSuccess) { if (rv != SECSuccess) {
goto loser; /* code set */ goto loser; /* code set */
} }
#else #else
rv = SECITEM_CopyItem(NULL, decryptedChInner, &ss->xtnData.innerCh); rv = SECITEM_CopyItem(NULL, decryptedChInner, &ss->xtnData.ech->innerCh);
if (rv != SECSuccess) { if (rv != SECSuccess) {
goto loser; goto loser;
} }
decryptedChInner->len -= 16; /* Fake tag */ decryptedChInner->len -= 16; /* Fake tag */
#endif #endif
/* Stash the context, we may need it for HRR. */ /* Stash the context, we may need it for HRR. */
ss->ssl3.hs.echHpkeCtx = cx; ss->ssl3.hs.echHpkeCtx = cx;
*chInner = decryptedChInner; *chInner = decryptedChInner;
SECITEM_FreeItem(&hpkeInfo, PR_FALSE); SECITEM_FreeItem(&hpkeInfo, PR_FALSE);
sslBuffer_Clear(&outerAAD);
return SECSuccess; return SECSuccess;
loser: loser:
SECITEM_FreeItem(decryptedChInner, PR_TRUE); SECITEM_FreeItem(decryptedChInner, PR_TRUE);
PK11_HPKE_DestroyContext(cx, PR_TRUE);
SECITEM_FreeItem(&hpkeInfo, PR_FALSE); 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; return SECFailure;
} }
/* Given a buffer of extensions prepared for CHOuter, translate those extensions to a /* Given a buffer of extensions prepared for CHOuter, translate those extensions to a
* buffer suitable for CHInner. This is intended to be called twice: once withou t * buffer suitable for CHInner. This is intended to be called twice: once withou t
* compression for the transcript hash and binders, and once with compression fo r * compression for the transcript hash and binders, and once with compression fo r
* encoding the actual CHInner value. On the first run, if |inOutPskXtn| and * encoding the actual CHInner value. On the first run, if |inOutPskXtn| and
* chOuterXtnsBuf contains a PSK extension, remove it and return in the outparam . * chOuterXtnsBuf contains a PSK extension, remove it and return in the outparam .
* The caller will compute the binder value based on the uncompressed output. Ne xt, * The caller will compute the binder value based on the uncompressed output. Ne xt,
* if |compress|, consolidate duplicated extensions (that would otherwise be cop ied) * if |compress|, consolidate duplicated extensions (that would otherwise be cop ied)
skipping to change at line 1292 skipping to change at line 1214
sslBuffer dupXtns = SSL_BUFFER_EMPTY; /* Dupcliated extensions, types-only i f |compress|. */ sslBuffer dupXtns = SSL_BUFFER_EMPTY; /* Dupcliated extensions, types-only i f |compress|. */
unsigned int tmpOffset; unsigned int tmpOffset;
unsigned int tmpLen; unsigned int tmpLen;
unsigned int srcXtnBase; /* To truncate CHOuter and remove the PSK extension . */ unsigned int srcXtnBase; /* To truncate CHOuter and remove the PSK extension . */
SSL_TRC(50, ("%d: TLS13[%d]: Constructing ECH inner extensions %s compressio n", SSL_TRC(50, ("%d: TLS13[%d]: Constructing ECH inner extensions %s compressio n",
SSL_GETPID(), compress ? "with" : "without")); SSL_GETPID(), compress ? "with" : "without"));
/* When offering the "encrypted_client_hello" extension in its /* When offering the "encrypted_client_hello" extension in its
* ClientHelloOuter, the client MUST also offer an empty * ClientHelloOuter, the client MUST also offer an empty
* "encrypted_client_hello" extension in its ClientHelloInner. */ * "encrypted_client_hello" extension in its ClientHelloInner. */
rv = sslBuffer_AppendNumber(chInnerXtns, ssl_tls13_encrypted_client_hello_xt n, 2); rv = sslBuffer_AppendNumber(chInnerXtns, ssl_tls13_ech_is_inner_xtn, 2);
if (rv != SECSuccess) { if (rv != SECSuccess) {
goto loser; goto loser;
} }
rv = sslBuffer_AppendNumber(chInnerXtns, 0, 2); rv = sslBuffer_AppendNumber(chInnerXtns, 0, 2);
if (rv != SECSuccess) { if (rv != SECSuccess) {
goto loser; goto loser;
} }
sslReader rdr = SSL_READER(chOuterXtnsBuf->buf, chOuterXtnsBuf->len); sslReader rdr = SSL_READER(chOuterXtnsBuf->buf, chOuterXtnsBuf->len);
while (SSL_READER_REMAINING(&rdr)) { while (SSL_READER_REMAINING(&rdr)) {
skipping to change at line 1511 skipping to change at line 1433
SECStatus SECStatus
tls13_ConstructClientHelloWithEch(sslSocket *ss, const sslSessionID *sid, PRBool freshSid, tls13_ConstructClientHelloWithEch(sslSocket *ss, const sslSessionID *sid, PRBool freshSid,
sslBuffer *chOuter, sslBuffer *chOuterXtnsBuf) sslBuffer *chOuter, sslBuffer *chOuterXtnsBuf)
{ {
SECStatus rv; SECStatus rv;
sslBuffer chInner = SSL_BUFFER_EMPTY; sslBuffer chInner = SSL_BUFFER_EMPTY;
sslBuffer encodedChInner = SSL_BUFFER_EMPTY; sslBuffer encodedChInner = SSL_BUFFER_EMPTY;
sslBuffer chInnerXtns = SSL_BUFFER_EMPTY; sslBuffer chInnerXtns = SSL_BUFFER_EMPTY;
sslBuffer pskXtn = SSL_BUFFER_EMPTY; sslBuffer pskXtn = SSL_BUFFER_EMPTY;
sslBuffer outerAAD = SSL_BUFFER_EMPTY; sslBuffer aad = SSL_BUFFER_EMPTY;
unsigned int encodedChLen; unsigned int encodedChLen;
unsigned int preambleLen; unsigned int preambleLen;
const SECItem *hpkeEnc = NULL;
unsigned int savedOffset;
SSL_TRC(50, ("%d: TLS13[%d]: Constructing ECH inner", SSL_GETPID())); SSL_TRC(50, ("%d: TLS13[%d]: Constructing ECH inner", SSL_GETPID()));
/* Create the full (uncompressed) inner extensions and steal any PSK extensi on. /* Create the full (uncompressed) inner extensions and steal any PSK extensi on.
* NB: Neither chOuterXtnsBuf nor chInnerXtns are length-prefixed. */ * NB: Neither chOuterXtnsBuf nor chInnerXtns are length-prefixed. */
rv = tls13_ConstructInnerExtensionsFromOuter(ss, chOuterXtnsBuf, &chInnerXtn s, rv = tls13_ConstructInnerExtensionsFromOuter(ss, chOuterXtnsBuf, &chInnerXtn s,
&pskXtn, PR_FALSE); &pskXtn, PR_FALSE);
if (rv != SECSuccess) { if (rv != SECSuccess) {
goto loser; /* code set */ goto loser; /* code set */
} }
skipping to change at line 1554 skipping to change at line 1478
rv = tls13_WriteExtensionsWithBinder(ss, &chInnerXtns, &chInner); rv = tls13_WriteExtensionsWithBinder(ss, &chInnerXtns, &chInner);
/* Update the stolen PSK extension with the binder value. */ /* Update the stolen PSK extension with the binder value. */
PORT_Memcpy(pskXtn.buf, &chInnerXtns.buf[chInnerXtns.len - pskXtn.len], pskXtn.len); PORT_Memcpy(pskXtn.buf, &chInnerXtns.buf[chInnerXtns.len - pskXtn.len], pskXtn.len);
} else { } else {
rv = sslBuffer_AppendBufferVariable(&chInner, &chInnerXtns, 2); rv = sslBuffer_AppendBufferVariable(&chInner, &chInnerXtns, 2);
} }
if (rv != SECSuccess) { if (rv != SECSuccess) {
goto loser; goto loser;
} }
rv = ssl3_UpdateExplicitHandshakeTranscript(ss, chInner.buf, chInner.len, rv = ssl3_UpdateHandshakeHashesInt(ss, chInner.buf, chInner.len,
&ss->ssl3.hs.echInnerMessages); &ss->ssl3.hs.echInnerMessages);
if (rv != SECSuccess) { if (rv != SECSuccess) {
goto loser; /* code set */ goto loser; /* code set */
} }
/* Un-append the extensions, then append compressed via Encoded. */ /* Un-append the extensions, then append compressed via Encoded. */
SSL_BUFFER_LEN(&chInner) = preambleLen; SSL_BUFFER_LEN(&chInner) = preambleLen;
sslBuffer_Clear(&chInnerXtns); sslBuffer_Clear(&chInnerXtns);
rv = tls13_ConstructInnerExtensionsFromOuter(ss, chOuterXtnsBuf, rv = tls13_ConstructInnerExtensionsFromOuter(ss, chOuterXtnsBuf,
&chInnerXtns, &pskXtn, PR_TRUE) ; &chInnerXtns, &pskXtn, PR_TRUE) ;
if (rv != SECSuccess) { if (rv != SECSuccess) {
goto loser; goto loser;
} }
/* TODO: Pad CHInner */
rv = tls13_EncodeClientHelloInner(ss, &chInner, &chInnerXtns, &encodedChInne r); rv = tls13_EncodeClientHelloInner(ss, &chInner, &chInnerXtns, &encodedChInne r);
if (rv != SECSuccess) { if (rv != SECSuccess) {
goto loser; goto loser;
} }
/* Pad the outer prior to appending ECH (for the AAD). /* Pad the outer prior to appending ECH (for the AAD).
* Encoded extension size is (echCipherSuite + enc + configId + payload + ta g). * Encoded extension size is (echCipherSuite + enc + configId + payload + ta g).
* Post-encryption, we'll assert that this was correct. */ * 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, chOuterXtns Buf); rv = ssl_InsertPaddingExtension(ss, chOuter->len + encodedChLen, chOuterXtns Buf);
if (rv != SECSuccess) { if (rv != SECSuccess) {
goto loser; goto loser;
} }
/* Make the ClientHelloOuterAAD value, which is complete PORT_Assert(!PR_CLIST_IS_EMPTY(&ss->echConfigs));
* chOuter minus encrypted_client_hello xtn. */ sslEchConfig *cfg = (sslEchConfig *)PR_LIST_HEAD(&ss->echConfigs);
rv = sslBuffer_Append(&outerAAD, chOuter->buf, chOuter->len); rv = sslBuffer_AppendNumber(&aad, cfg->contents.kdfId, 2);
if (rv != SECSuccess) {
goto loser;
}
rv = sslBuffer_AppendNumber(&aad, cfg->contents.aeadId, 2);
if (rv != SECSuccess) { if (rv != SECSuccess) {
goto loser; 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) { if (rv != SECSuccess) {
goto loser; 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) { if (rv != SECSuccess) {
goto loser; goto loser;
} }
/* Insert the encrypted_client_hello xtn and coalesce. */ /* Insert the encrypted_client_hello xtn and coalesce. */
rv = tls13_EncryptClientHello(ss, &outerAAD, &encodedChInner); rv = tls13_EncryptClientHello(ss, &aad, &encodedChInner);
if (rv != SECSuccess) { if (rv != SECSuccess) {
goto loser; goto loser;
} }
PORT_Assert(encodedChLen == encodedChInner.len); PORT_Assert(encodedChLen == encodedChInner.len);
rv = ssl3_EmplaceExtension(ss, chOuterXtnsBuf, ssl_tls13_encrypted_client_he llo_xtn, rv = ssl3_EmplaceExtension(ss, chOuterXtnsBuf, ssl_tls13_encrypted_client_he llo_xtn,
encodedChInner.buf, encodedChInner.len, PR_TRUE); encodedChInner.buf, encodedChInner.len, PR_TRUE);
if (rv != SECSuccess) { if (rv != SECSuccess) {
goto loser; goto loser;
} }
rv = ssl3_InsertChHeaderSize(ss, chOuter, chOuterXtnsBuf); rv = ssl3_InsertChHeaderSize(ss, chOuter, chOuterXtnsBuf);
if (rv != SECSuccess) { if (rv != SECSuccess) {
goto loser; goto loser;
} }
rv = sslBuffer_AppendBufferVariable(chOuter, chOuterXtnsBuf, 2); rv = sslBuffer_AppendBufferVariable(chOuter, chOuterXtnsBuf, 2);
if (rv != SECSuccess) { if (rv != SECSuccess) {
goto loser; goto loser;
} }
sslBuffer_Clear(&chInner);
sslBuffer_Clear(&encodedChInner);
sslBuffer_Clear(&chInnerXtns);
sslBuffer_Clear(&pskXtn);
sslBuffer_Clear(&aad);
return SECSuccess;
loser: loser:
sslBuffer_Clear(&chInner); sslBuffer_Clear(&chInner);
sslBuffer_Clear(&encodedChInner); sslBuffer_Clear(&encodedChInner);
sslBuffer_Clear(&chInnerXtns); sslBuffer_Clear(&chInnerXtns);
sslBuffer_Clear(&pskXtn); sslBuffer_Clear(&pskXtn);
sslBuffer_Clear(&outerAAD); sslBuffer_Clear(&aad);
return rv; 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, echInnerM
essages. */
static SECStatus static SECStatus
tls13_ComputeEchSignal(sslSocket *ss, PRUint8 *out) tls13_ComputeEchSignal(sslSocket *ss, const PRUint8 *sh, unsigned int shLen, PRU int8 *out)
{ {
SECStatus rv; SECStatus rv;
PRUint8 derived[64]; PK11SymKey *confirmationKey = NULL;
SECItem randItem = { siBuffer, sslBuffer confMsgs = SSL_BUFFER_EMPTY;
ss->sec.isServer ? ss->ssl3.hs.client_random : ss->ssl3 sslBuffer *chSource = ss->sec.isServer ? &ss->ssl3.hs.messages : &ss->ssl3.h
.hs.client_inner_random, s.echInnerMessages;
SSL3_RANDOM_LENGTH }; SSL3Hashes hashes;
SSLHashType hashAlg = tls13_GetHash(ss); SECItem *confirmationBytes;
PK11SymKey *extracted = NULL; unsigned int offset = sizeof(SSL3ProtocolVersion) +
PK11SymKey *randKey = NULL; SSL3_RANDOM_LENGTH - TLS13_ECH_SIGNAL_LEN;
PK11SlotInfo *slot = PK11_GetInternalSlot(); PORT_Assert(sh && shLen > offset);
if (!slot) { PORT_Assert(TLS13_ECH_SIGNAL_LEN <= SSL3_RANDOM_LENGTH);
rv = sslBuffer_AppendBuffer(&confMsgs, chSource);
if (rv != SECSuccess) {
goto loser; goto loser;
} }
randKey = PK11_ImportDataKey(slot, CKM_HKDF_DATA, PK11_OriginUnwrap, /* Re-create the message header. */
CKA_DERIVE, &randItem, NULL); rv = sslBuffer_AppendNumber(&confMsgs, ssl_hs_server_hello, 1);
if (!randKey) { if (rv != SECSuccess) {
goto loser; goto loser;
} }
rv = tls13_HkdfExtract(NULL, randKey, hashAlg, &extracted); rv = sslBuffer_AppendNumber(&confMsgs, shLen, 3);
if (rv != SECSuccess) { if (rv != SECSuccess) {
goto loser; goto loser;
} }
rv = tls13_HkdfExpandLabelRaw(extracted, hashAlg, ss->ssl3.hs.server_random, /* Copy the version and 24B of server_random. */
24, rv = sslBuffer_Append(&confMsgs, sh, offset);
kHkdfInfoEchConfirm, strlen(kHkdfInfoEchConfir
m),
ss->protocolVariant, derived, TLS13_ECH_SIGNAL
_LEN);
if (rv != SECSuccess) { if (rv != SECSuccess) {
goto loser; 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))); 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)); PRINT_BUF(50, (ss, "", out, TLS13_ECH_SIGNAL_LEN));
PK11_FreeSymKey(extracted);
PK11_FreeSymKey(randKey); PORT_Memcpy(out, confirmationBytes->data, TLS13_ECH_SIGNAL_LEN);
PK11_FreeSlot(slot); PK11_FreeSymKey(confirmationKey);
sslBuffer_Clear(&confMsgs);
sslBuffer_Clear(&ss->ssl3.hs.messages);
sslBuffer_Clear(&ss->ssl3.hs.echInnerMessages);
return SECSuccess; return SECSuccess;
loser: loser:
PK11_FreeSymKey(extracted); PK11_FreeSymKey(confirmationKey);
PK11_FreeSymKey(randKey); sslBuffer_Clear(&confMsgs);
if (slot) { sslBuffer_Clear(&ss->ssl3.hs.messages);
PK11_FreeSlot(slot); sslBuffer_Clear(&ss->ssl3.hs.echInnerMessages);
}
return SECFailure; return SECFailure;
} }
/* Called just prior to padding the CH. Use the size of the CH to estimate /* Called just prior to padding the CH. Use the size of the CH to estimate
* the size of a corresponding ECH extension, then add it to the buffer. */ * the size of a corresponding ECH extension, then add it to the buffer. */
SECStatus SECStatus
tls13_MaybeGreaseEch(sslSocket *ss, unsigned int preambleLen, sslBuffer *buf) tls13_MaybeGreaseEch(sslSocket *ss, unsigned int preambleLen, sslBuffer *buf)
{ {
SECStatus rv; SECStatus rv;
sslBuffer chInnerXtns = SSL_BUFFER_EMPTY; sslBuffer chInnerXtns = SSL_BUFFER_EMPTY;
sslBuffer greaseBuf = SSL_BUFFER_EMPTY; sslBuffer greaseBuf = SSL_BUFFER_EMPTY;
unsigned int payloadLen; unsigned int payloadLen;
HpkeAeadId aead; HpkeAeadId aead;
PK11SlotInfo *slot = NULL; PK11SlotInfo *slot = NULL;
PK11SymKey *hmacPrk = NULL; PK11SymKey *hmacPrk = NULL;
PK11SymKey *derivedData = NULL; PK11SymKey *derivedData = NULL;
SECItem *rawData; SECItem *rawData;
CK_HKDF_PARAMS params; CK_HKDF_PARAMS params;
SECItem paramsi; 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) { if (!ss->opt.enableTls13GreaseEch || ss->ssl3.hs.echHpkeCtx) {
return SECSuccess; return SECSuccess;
} }
if (ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_3 || if (ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_3 ||
IS_DTLS(ss)) { IS_DTLS(ss)) {
return SECSuccess; 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_x
tn,
ss->ssl3.hs.greaseEchBuf.buf,
ss->ssl3.hs.greaseEchBuf.len, PR_TRUE);
}
/* Compress the extensions for payload length. */ /* Compress the extensions for payload length. */
rv = tls13_ConstructInnerExtensionsFromOuter(ss, buf, &chInnerXtns, rv = tls13_ConstructInnerExtensionsFromOuter(ss, buf, &chInnerXtns,
NULL, PR_TRUE); NULL, PR_TRUE);
if (rv != SECSuccess) { if (rv != SECSuccess) {
goto loser; /* Code set */ goto loser; /* Code set */
} }
payloadLen = preambleLen + 2 /* Xtns len */ + chInnerXtns.len - 4 /* msg hea der */; payloadLen = preambleLen + 2 /* Xtns len */ + chInnerXtns.len - 4 /* msg hea der */;
payloadLen += 16; /* Aead tag */ payloadLen += 16; /* Aead tag */
/* HMAC-Expand to get something that will pass for ciphertext. */ /* HMAC-Expand to get something that will pass for ciphertext. */
skipping to change at line 1737 skipping to change at line 1766
params.bExtract = CK_FALSE; params.bExtract = CK_FALSE;
params.bExpand = CK_TRUE; params.bExpand = CK_TRUE;
params.prfHashMechanism = CKM_SHA256; params.prfHashMechanism = CKM_SHA256;
params.pInfo = NULL; params.pInfo = NULL;
params.ulInfoLen = 0; params.ulInfoLen = 0;
paramsi.data = (unsigned char *)&params; paramsi.data = (unsigned char *)&params;
paramsi.len = sizeof(params); paramsi.len = sizeof(params);
derivedData = PK11_DeriveWithFlags(hmacPrk, CKM_HKDF_DATA, derivedData = PK11_DeriveWithFlags(hmacPrk, CKM_HKDF_DATA,
&paramsi, CKM_HKDF_DATA, &paramsi, CKM_HKDF_DATA,
CKA_DERIVE, 65 + payloadLen, CKA_DERIVE, kNonPayloadLen + payloadLen,
CKF_VERIFY); CKF_VERIFY);
if (!derivedData) { if (!derivedData) {
goto loser; goto loser;
} }
rv = PK11_ExtractKeyValue(derivedData); rv = PK11_ExtractKeyValue(derivedData);
if (rv != SECSuccess) { if (rv != SECSuccess) {
goto loser; goto loser;
} }
/* 1B aead determinant (don't send), 32B config_id, 32B enc, payload */
rawData = PK11_GetKeyData(derivedData); rawData = PK11_GetKeyData(derivedData);
if (!rawData) { if (!rawData) {
goto loser; goto loser;
} }
PORT_Assert(rawData->len == 65 + payloadLen); PORT_Assert(rawData->len == kNonPayloadLen + payloadLen);
/* struct { /* struct {
HpkeKdfId kdf_id; HpkeKdfId kdf_id;
HpkeAeadId aead_id; HpkeAeadId aead_id;
opaque config_id<0..255>; opaque config_id<0..255>;
opaque enc<1..2^16-1>; opaque enc<1..2^16-1>;
opaque payload<1..2^16-1>; opaque payload<1..2^16-1>;
} ClientECH; */ } ClientECH; */
/* Only support SHA256. */ /* Only support SHA256. */
skipping to change at line 1776 skipping to change at line 1804
goto loser; goto loser;
} }
/* HpkeAeadAes128Gcm = 1, HpkeAeadChaCha20Poly1305 = 3, */ /* HpkeAeadAes128Gcm = 1, HpkeAeadChaCha20Poly1305 = 3, */
aead = (rawData->data[0] & 1) ? HpkeAeadAes128Gcm : HpkeAeadChaCha20Poly1305 ; aead = (rawData->data[0] & 1) ? HpkeAeadAes128Gcm : HpkeAeadChaCha20Poly1305 ;
rv = sslBuffer_AppendNumber(&greaseBuf, aead, 2); rv = sslBuffer_AppendNumber(&greaseBuf, aead, 2);
if (rv != SECSuccess) { if (rv != SECSuccess) {
goto loser; 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) { if (rv != SECSuccess) {
goto loser; goto loser;
} }
/* enc len is fixed 32B for X25519. */ /* 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) { if (rv != SECSuccess) {
goto loser; goto loser;
} }
rv = sslBuffer_AppendVariable(&greaseBuf, &rawData->data[65], payloadLen, 2) ; rv = sslBuffer_AppendVariable(&greaseBuf, &rawData->data[kNonPayloadLen], pa yloadLen, 2);
if (rv != SECSuccess) { if (rv != SECSuccess) {
goto loser; goto loser;
} }
/* Mark ECH as advertised so that we can validate any response. /* Mark ECH as advertised so that we can validate any response.
* We'll use echHpkeCtx to determine if we sent real or GREASE ECH. * We'll use echHpkeCtx to determine if we sent real or GREASE ECH. */
* TODO: Maybe a broader need to similarly track GREASED extensions? */
rv = ssl3_EmplaceExtension(ss, buf, ssl_tls13_encrypted_client_hello_xtn, rv = ssl3_EmplaceExtension(ss, buf, ssl_tls13_encrypted_client_hello_xtn,
greaseBuf.buf, greaseBuf.len, PR_TRUE); greaseBuf.buf, greaseBuf.len, PR_TRUE);
if (rv != SECSuccess) { if (rv != SECSuccess) {
goto loser; 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); sslBuffer_Clear(&chInnerXtns);
PK11_FreeSymKey(hmacPrk); PK11_FreeSymKey(hmacPrk);
PK11_FreeSymKey(derivedData); PK11_FreeSymKey(derivedData);
PK11_FreeSlot(slot); PK11_FreeSlot(slot);
return SECSuccess; return SECSuccess;
loser: loser:
sslBuffer_Clear(&greaseBuf);
sslBuffer_Clear(&chInnerXtns); sslBuffer_Clear(&chInnerXtns);
PK11_FreeSymKey(hmacPrk); PK11_FreeSymKey(hmacPrk);
PK11_FreeSymKey(derivedData); PK11_FreeSymKey(derivedData);
if (slot) { if (slot) {
PK11_FreeSlot(slot); PK11_FreeSlot(slot);
} }
return SECFailure; return SECFailure;
} }
SECStatus SECStatus
skipping to change at line 1864 skipping to change at line 1894
b = tmpEchInner->data; b = tmpEchInner->data;
length = tmpEchInner->len; length = tmpEchInner->len;
rv = ssl3_HandleClientHelloPreamble(ss, &b, &length, &tmpSid, rv = ssl3_HandleClientHelloPreamble(ss, &b, &length, &tmpSid,
&tmpCookie, &tmpSuites, &tmpComps); &tmpCookie, &tmpSuites, &tmpComps);
if (rv != SECSuccess) { if (rv != SECSuccess) {
goto loser; /* code set, alert sent. */ goto loser; /* code set, alert sent. */
} }
/* Since in Outer we explicitly call the ECH handler, do the same on Inn er. /* Since in Outer we explicitly call the ECH handler, do the same on Inn er.
* Extensions are already parsed in tls13_MaybeAcceptEch. */ * Extensions are already parsed in tls13_MaybeAcceptEch. */
echExtension = ssl3_FindExtension(ss, ssl_tls13_encrypted_client_hello_x tn); echExtension = ssl3_FindExtension(ss, ssl_tls13_ech_is_inner_xtn);
if (!echExtension) { if (!echExtension) {
FATAL_ERROR(ss, SSL_ERROR_MISSING_ECH_EXTENSION, decode_error); FATAL_ERROR(ss, SSL_ERROR_MISSING_ECH_EXTENSION, illegal_parameter);
goto loser; 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_x tn); versionExtension = ssl3_FindExtension(ss, ssl_tls13_supported_versions_x tn);
if (!versionExtension) { if (!versionExtension) {
FATAL_ERROR(ss, SSL_ERROR_UNSUPPORTED_VERSION, protocol_version); FATAL_ERROR(ss, SSL_ERROR_UNSUPPORTED_VERSION, protocol_version);
goto loser; goto loser;
} }
rv = tls13_NegotiateVersion(ss, versionExtension); rv = tls13_NegotiateVersion(ss, versionExtension);
if (rv != SECSuccess) { if (rv != SECSuccess) {
/* Could be malformed or not allowed in ECH. */ /* Could be malformed or not allowed in ECH. */
error = PORT_GetError(); error = PORT_GetError();
skipping to change at line 1898 skipping to change at line 1924
*comps = tmpComps; *comps = tmpComps;
*cookieBytes = tmpCookie; *cookieBytes = tmpCookie;
*sidBytes = tmpSid; *sidBytes = tmpSid;
*suites = tmpSuites; *suites = tmpSuites;
*echInner = tmpEchInner; *echInner = tmpEchInner;
} }
return SECSuccess; return SECSuccess;
loser: loser:
SECITEM_FreeItem(tmpEchInner, PR_TRUE); SECITEM_FreeItem(tmpEchInner, PR_TRUE);
PORT_Assert(PORT_GetError() != 0);
return SECFailure; return SECFailure;
} }
SECStatus SECStatus
tls13_MaybeHandleEchSignal(sslSocket *ss) tls13_MaybeHandleEchSignal(sslSocket *ss, const PRUint8 *sh, PRUint32 shLen)
{ {
SECStatus rv; SECStatus rv;
PRUint8 computed[TLS13_ECH_SIGNAL_LEN]; PRUint8 computed[TLS13_ECH_SIGNAL_LEN];
const PRUint8 *signal = &ss->ssl3.hs.server_random[SSL3_RANDOM_LENGTH - TLS1 3_ECH_SIGNAL_LEN]; const PRUint8 *signal = &ss->ssl3.hs.server_random[SSL3_RANDOM_LENGTH - TLS1 3_ECH_SIGNAL_LEN];
PORT_Assert(!ss->sec.isServer); PORT_Assert(!ss->sec.isServer);
/* If !echHpkeCtx, we either didn't advertise or sent GREASE ECH. */ /* If !echHpkeCtx, we either didn't advertise or sent GREASE ECH. */
if (ss->ssl3.hs.echHpkeCtx) { if (!ss->ssl3.hs.echHpkeCtx) {
PORT_Assert(ssl3_ExtensionAdvertised(ss, ssl_tls13_encrypted_client_hell ss->ssl3.hs.preliminaryInfo |= ssl_preinfo_ech;
o_xtn)); return SECSuccess;
rv = tls13_ComputeEchSignal(ss, computed); }
if (rv != SECSuccess) {
PORT_Assert(ssl3_ExtensionAdvertised(ss, ssl_tls13_encrypted_client_hello_xt
n));
rv = tls13_ComputeEchSignal(ss, sh, shLen, computed);
if (rv != SECSuccess) {
return SECFailure;
}
ss->ssl3.hs.echAccepted = !PORT_Memcmp(computed, signal, TLS13_ECH_SIGNAL_LE
N);
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_paramet
er);
return SECFailure; return SECFailure;
} }
if (ss->ssl3.hs.helloRetry && ss->sec.isServer) {
ss->ssl3.hs.echAccepted = !PORT_Memcmp(computed, signal, TLS13_ECH_SIGNA /* Enc and ConfigId are stored in the cookie and must not
L_LEN); * be included in CH2.ClientECH. */
if (ss->ssl3.hs.echAccepted) { if (ss->xtnData.ech->senderPubKey.len || ss->xtnData.ech->configId.l
if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) { en) {
FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_SERVER_HELLO, illegal_par ssl3_ExtSendAlert(ss, alert_fatal, illegal_parameter);
ameter); PORT_SetError(SSL_ERROR_BAD_2ND_CLIENT_HELLO);
return SECFailure; return SECFailure;
} }
ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ssl_tls13_encr
ypted_client_hello_xtn;
PORT_Memcpy(ss->ssl3.hs.client_random, ss->ssl3.hs.client_inner_rand
om, 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_encrypte
d_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; return SECSuccess;
} }
static SECStatus static SECStatus
tls13_UnencodeChInner(sslSocket *ss, const SECItem *sidBytes, SECItem **echInner ) tls13_UnencodeChInner(sslSocket *ss, const SECItem *sidBytes, SECItem **echInner )
{ {
SECStatus rv; SECStatus rv;
sslReadBuffer outerExtensionsList; sslReadBuffer outerExtensionsList;
sslReadBuffer tmpReadBuf; sslReadBuffer tmpReadBuf;
sslBuffer unencodedChInner = SSL_BUFFER_EMPTY; sslBuffer unencodedChInner = SSL_BUFFER_EMPTY;
skipping to change at line 2095 skipping to change at line 2134
return SECFailure; return SECFailure;
} }
SECStatus SECStatus
tls13_MaybeAcceptEch(sslSocket *ss, const SECItem *sidBytes, const PRUint8 *chOu ter, tls13_MaybeAcceptEch(sslSocket *ss, const SECItem *sidBytes, const PRUint8 *chOu ter,
unsigned int chOuterLen, SECItem **chInner) unsigned int chOuterLen, SECItem **chInner)
{ {
SECStatus rv; SECStatus rv;
SECItem outer = { siBuffer, CONST_CAST(PRUint8, chOuter), chOuterLen }; SECItem outer = { siBuffer, CONST_CAST(PRUint8, chOuter), chOuterLen };
SECItem *decryptedChInner = NULL; SECItem *decryptedChInner = NULL;
PK11SymKey *echHrrPsk = NULL;
SECItem hrrCh1ConfigId = { siBuffer, NULL, 0 }; SECItem hrrCh1ConfigId = { siBuffer, NULL, 0 };
HpkeKdfId kdf; SECItem outerAAD = { siBuffer, NULL, 0 };
HpkeAeadId aead; SECItem cookieData = { siBuffer, NULL, 0 };
HpkeContext *ch1EchHpkeCtx = NULL;
HpkeKdfId echKdfId;
HpkeAeadId echAeadId;
sslEchConfig *candidate = NULL; /* non-owning */ sslEchConfig *candidate = NULL; /* non-owning */
TLSExtension *hrrXtn; TLSExtension *hrrXtn;
SECItem *configId = ss->ssl3.hs.helloRetry ? &hrrCh1ConfigId : &ss->xtnData.
echConfigId; if (!ss->xtnData.ech) {
if (!ss->xtnData.innerCh.len) {
return SECSuccess; return SECSuccess;
} }
PORT_Assert(ss->xtnData.echSenderPubKey.data); PORT_Assert(ss->xtnData.ech->innerCh.data);
PORT_Assert(ss->xtnData.echConfigId.data);
PORT_Assert(ss->xtnData.echCipherSuite);
if (ss->ssl3.hs.helloRetry) { if (ss->ssl3.hs.helloRetry) {
PORT_Assert(!ss->ssl3.hs.echHpkeCtx);
hrrXtn = ssl3_FindExtension(ss, ssl_tls13_cookie_xtn); hrrXtn = ssl3_FindExtension(ss, ssl_tls13_cookie_xtn);
if (!hrrXtn) { if (!hrrXtn) {
/* If the client doesn't echo cookie, we can't decrypt. */ /* If the client doesn't echo cookie, we can't decrypt. */
return SECSuccess; 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 (rv != SECSuccess) {
/* If we failed due to an issue with the cookie, continue without return SECSuccess;
* ECH and let the HRR code handle the problem. */
goto exit_success;
} }
/* No CH1 config_id means ECH wasn't advertised in CH1. ss->xtnData.ech->configId = hrrCh1ConfigId;
* No CH1 HRR PSK means that ECH was not accepted in CH1, and the ss->ssl3.hs.echHpkeCtx = ch1EchHpkeCtx;
* HRR was generated off CH1Outer. */
if (hrrCh1ConfigId.len == 0) { if (echKdfId != ss->xtnData.ech->kdfId ||
echAeadId != ss->xtnData.ech->aeadId) {
FATAL_ERROR(ss, SSL_ERROR_BAD_2ND_CLIENT_HELLO, FATAL_ERROR(ss, SSL_ERROR_BAD_2ND_CLIENT_HELLO,
illegal_parameter); 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); /* Cookie data was good, proceed with ECH. */
rv = tls13_GetMatchingEchConfig(ss, kdf, aead, configId, &candidate); PORT_Assert(ss->xtnData.ech->configId.data);
rv = tls13_GetMatchingEchConfigs(ss, ss->xtnData.ech->kdfId, ss->xtnData.ech
->aeadId,
&ss->xtnData.ech->configId, candidate, &can
didate);
if (rv != SECSuccess) { 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) { if (candidate) {
/* Send retry_configs if we have any. rv = tls13_MakeChOuterAAD(ss, &outer, &outerAAD);
* This does *not* count as negotiating ECH. */ if (rv != SECSuccess) {
rv = ssl3_RegisterExtensionSender(ss, &ss->xtnData, return SECFailure;
ssl_tls13_encrypted_client_hello_xtn, }
tls13_ServerSendEchXtn);
goto exit_success;
} }
rv = tls13_OpenClientHelloInner(ss, &outer, candidate, echHrrPsk, &decrypted while (candidate) {
ChInner); rv = tls13_OpenClientHelloInner(ss, &outer, &outerAAD, candidate, &decry
if (rv != SECSuccess) { ptedChInner);
if (rv != SECSuccess) {
/* Get the next matching config */
rv = tls13_GetMatchingEchConfigs(ss, ss->xtnData.ech->kdfId, ss->xtn
Data.ech->aeadId,
&ss->xtnData.ech->configId, candida
te, &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) { if (ss->ssl3.hs.helloRetry) {
FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_ESNI_EXTENSION, decrypt_error FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_ECH_EXTENSION, decrypt_error)
); ;
goto loser; return SECFailure;
} else { } else {
rv = ssl3_RegisterExtensionSender(ss, &ss->xtnData, /* Send retry_configs (if we have any) when we fail to decrypt or
ssl_tls13_encrypted_client_hello_x * found no candidates. This does *not* count as negotiating ECH. */
tn, return ssl3_RegisterExtensionSender(ss, &ss->xtnData,
tls13_ServerSendEchXtn); ssl_tls13_encrypted_client_hello
goto exit_success; _xtn,
tls13_ServerSendEchXtn);
} }
} }
SSL_TRC(20, ("%d: TLS13[%d]: Successfully opened ECH inner CH", SSL_TRC(20, ("%d: TLS13[%d]: Successfully opened ECH inner CH",
SSL_GETPID(), ss->fd)); SSL_GETPID(), ss->fd));
ss->ssl3.hs.echAccepted = PR_TRUE; ss->ssl3.hs.echAccepted = PR_TRUE;
/* Stash the CHOuter extensions. They're not yet handled (only parsed). If /* Stash the CHOuter extensions. They're not yet handled (only parsed). If
* the CHInner contains outer_extensions_xtn, we'll need to reference them. */ * the CHInner contains outer_extensions_xtn, we'll need to reference them. */
ssl3_MoveRemoteExtensions(&ss->ssl3.hs.echOuterExtensions, &ss->ssl3.hs.remo teExtensions); ssl3_MoveRemoteExtensions(&ss->ssl3.hs.echOuterExtensions, &ss->ssl3.hs.remo teExtensions);
rv = tls13_UnencodeChInner(ss, sidBytes, &decryptedChInner); rv = tls13_UnencodeChInner(ss, sidBytes, &decryptedChInner);
if (rv != SECSuccess) { if (rv != SECSuccess) {
SECITEM_FreeItem(decryptedChInner, PR_TRUE); SECITEM_FreeItem(decryptedChInner, PR_TRUE);
goto loser; /* code set */ return SECFailure; /* code set */
} }
*chInner = decryptedChInner; *chInner = decryptedChInner;
exit_success:
PK11_FreeSymKey(echHrrPsk);
SECITEM_FreeItem(&hrrCh1ConfigId, PR_FALSE);
return SECSuccess; return SECSuccess;
loser:
PK11_FreeSymKey(echHrrPsk);
SECITEM_FreeItem(&hrrCh1ConfigId, PR_FALSE);
return SECFailure;
} }
SECStatus SECStatus
tls13_WriteServerEchSignal(sslSocket *ss) tls13_WriteServerEchSignal(sslSocket *ss, PRUint8 *sh, unsigned int shLen)
{ {
SECStatus rv; SECStatus rv;
PRUint8 signal[TLS13_ECH_SIGNAL_LEN]; 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) { if (rv != SECSuccess) {
return SECFailure; return SECFailure;
} }
PRUint8 *dest = &ss->ssl3.hs.server_random[SSL3_RANDOM_LENGTH - TLS13_ECH_SI PRUint8 *dest = &msg_random[SSL3_RANDOM_LENGTH - TLS13_ECH_SIGNAL_LEN];
GNAL_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); PORT_Memcpy(dest, signal, TLS13_ECH_SIGNAL_LEN);
return SECSuccess; return SECSuccess;
} }
 End of changes. 119 change blocks. 
380 lines changed or deleted 456 lines changed or added

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)