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, | |||
¶msi, CKM_HKDF_DERIVE, CKA_DERIVE, 32, | ¶msi, 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 *)¶ms; | paramsi.data = (unsigned char *)¶ms; | |||
paramsi.len = sizeof(params); | paramsi.len = sizeof(params); | |||
derivedData = PK11_DeriveWithFlags(hmacPrk, CKM_HKDF_DATA, | derivedData = PK11_DeriveWithFlags(hmacPrk, CKM_HKDF_DATA, | |||
¶msi, CKM_HKDF_DATA, | ¶msi, 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 |