summaryrefslogtreecommitdiffstats
path: root/security/nss/lib/ssl/tls13hashstate.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /security/nss/lib/ssl/tls13hashstate.c
parentInitial commit. (diff)
downloadfirefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz
firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'security/nss/lib/ssl/tls13hashstate.c')
-rw-r--r--security/nss/lib/ssl/tls13hashstate.c256
1 files changed, 256 insertions, 0 deletions
diff --git a/security/nss/lib/ssl/tls13hashstate.c b/security/nss/lib/ssl/tls13hashstate.c
new file mode 100644
index 0000000000..011b4838e7
--- /dev/null
+++ b/security/nss/lib/ssl/tls13hashstate.c
@@ -0,0 +1,256 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is PRIVATE to SSL.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "pk11func.h"
+#include "ssl.h"
+#include "sslt.h"
+#include "sslimpl.h"
+#include "selfencrypt.h"
+#include "tls13con.h"
+#include "tls13ech.h"
+#include "tls13err.h"
+#include "tls13hashstate.h"
+
+/*
+ * The cookie is structured as a self-encrypted structure with the
+ * inner value being.
+ *
+ * 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
+ * echConfigId<0..255>; // Encrypted Client Hello config_id
+ * echHrrPsk<0..255>; // Encrypted Client Hello HRR PSK
+ * opaque ch_hash[rest_of_buffer]; // H(ClientHello)
+ * } CookieInner;
+ *
+ * An empty echConfigId means that ECH was not offered in the first ClientHello.
+ * An empty echHrrPsk means that ECH was not accepted in CH1.
+ */
+SECStatus
+tls13_MakeHrrCookie(sslSocket *ss, const sslNamedGroupDef *selectedGroup,
+ const PRUint8 *appToken, unsigned int appTokenLen,
+ PRUint8 *buf, unsigned int *len, unsigned int maxlen)
+{
+ SECStatus rv;
+ SSL3Hashes hashes;
+ PRUint8 cookie[1024];
+ sslBuffer cookieBuf = SSL_BUFFER(cookie);
+ static const PRUint8 indicator = 0xff;
+ SECItem hrrNonceInfoItem = { siBuffer, (unsigned char *)kHpkeInfoEchHrr,
+ strlen(kHpkeInfoEchHrr) };
+ PK11SymKey *echHrrPsk = NULL;
+ SECItem *rawEchPsk = NULL;
+
+ /* Encode header. */
+ rv = sslBuffer_Append(&cookieBuf, &indicator, 1);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ rv = sslBuffer_AppendNumber(&cookieBuf, ss->ssl3.hs.cipher_suite, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ rv = sslBuffer_AppendNumber(&cookieBuf,
+ selectedGroup ? selectedGroup->name : 0, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ /* Application token. */
+ rv = sslBuffer_AppendVariable(&cookieBuf, appToken, appTokenLen, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ /* Received ECH config_id, regardless of acceptance or possession
+ * of a matching ECHConfig. If rejecting ECH, this is essentially a boolean
+ * indicating that ECH was offered in CH1. If accepting ECH, this config_id
+ * will be used for the ECH decryption in CH2. */
+ if (ss->xtnData.echConfigId.len) {
+ rv = sslBuffer_AppendVariable(&cookieBuf, ss->xtnData.echConfigId.data,
+ ss->xtnData.echConfigId.len, 1);
+ } else {
+ PORT_Assert(!ssl3_FindExtension(ss, ssl_tls13_encrypted_client_hello_xtn));
+ rv = sslBuffer_AppendNumber(&cookieBuf, 0, 1);
+ }
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ /* Extract and encode the ech-hrr-key, if ECH was accepted
+ * (i.e. an Open() succeeded. */
+ if (ss->ssl3.hs.echAccepted) {
+ rv = PK11_HPKE_ExportSecret(ss->ssl3.hs.echHpkeCtx, &hrrNonceInfoItem, 32, &echHrrPsk);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ rv = PK11_ExtractKeyValue(echHrrPsk);
+ if (rv != SECSuccess) {
+ PK11_FreeSymKey(echHrrPsk);
+ return SECFailure;
+ }
+ rawEchPsk = PK11_GetKeyData(echHrrPsk);
+ if (!rawEchPsk) {
+ PK11_FreeSymKey(echHrrPsk);
+ return SECFailure;
+ }
+ rv = sslBuffer_AppendVariable(&cookieBuf, rawEchPsk->data, rawEchPsk->len, 1);
+ PK11_FreeSymKey(echHrrPsk);
+ } else {
+ /* Zero length ech_hrr_key. */
+ rv = sslBuffer_AppendNumber(&cookieBuf, 0, 1);
+ }
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ /* Compute and encode hashes. */
+ rv = tls13_ComputeHandshakeHashes(ss, &hashes);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ rv = sslBuffer_Append(&cookieBuf, hashes.u.raw, hashes.len);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ /* Encrypt right into the buffer. */
+ rv = ssl_SelfEncryptProtect(ss, cookieBuf.buf, cookieBuf.len,
+ buf, len, maxlen);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ return SECSuccess;
+}
+
+/* Recover the hash state from the cookie. */
+SECStatus
+tls13_RecoverHashState(sslSocket *ss,
+ unsigned char *cookie, unsigned int cookieLen,
+ ssl3CipherSuite *previousCipherSuite,
+ const sslNamedGroupDef **previousGroup,
+ PRBool *previousEchOffered)
+{
+ SECStatus rv;
+ unsigned char plaintext[1024];
+ unsigned int plaintextLen = 0;
+ sslBuffer messageBuf = SSL_BUFFER_EMPTY;
+ sslReadBuffer echPskBuf;
+ sslReadBuffer echConfigIdBuf;
+ PRUint64 sentinel;
+ PRUint64 cipherSuite;
+ PRUint64 group;
+ const sslNamedGroupDef *selectedGroup;
+ PRUint64 appTokenLen;
+
+ rv = ssl_SelfEncryptUnprotect(ss, cookie, cookieLen,
+ plaintext, &plaintextLen, sizeof(plaintext));
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ sslReader reader = SSL_READER(plaintext, plaintextLen);
+
+ /* Should start with the sentinel value. */
+ rv = sslRead_ReadNumber(&reader, 1, &sentinel);
+ if ((rv != SECSuccess) || (sentinel != TLS13_COOKIE_SENTINEL)) {
+ FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, illegal_parameter);
+ return SECFailure;
+ }
+ /* The cipher suite should be the same or there are some shenanigans. */
+ rv = sslRead_ReadNumber(&reader, 2, &cipherSuite);
+ if (rv != SECSuccess) {
+ FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, illegal_parameter);
+ return SECFailure;
+ }
+
+ /* The named group, if any. */
+ rv = sslRead_ReadNumber(&reader, 2, &group);
+ if (rv != SECSuccess) {
+ FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, illegal_parameter);
+ return SECFailure;
+ }
+ selectedGroup = ssl_LookupNamedGroup(group);
+
+ /* Application token. */
+ PORT_Assert(ss->xtnData.applicationToken.len == 0);
+ rv = sslRead_ReadNumber(&reader, 2, &appTokenLen);
+ if (rv != SECSuccess) {
+ FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, illegal_parameter);
+ return SECFailure;
+ }
+ if (SECITEM_AllocItem(NULL, &ss->xtnData.applicationToken,
+ appTokenLen) == NULL) {
+ FATAL_ERROR(ss, PORT_GetError(), internal_error);
+ return SECFailure;
+ }
+ ss->xtnData.applicationToken.len = appTokenLen;
+ sslReadBuffer appTokenReader = { 0 };
+ rv = sslRead_Read(&reader, appTokenLen, &appTokenReader);
+ if (rv != SECSuccess) {
+ FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, illegal_parameter);
+ return SECFailure;
+ }
+ PORT_Assert(appTokenReader.len == appTokenLen);
+ PORT_Memcpy(ss->xtnData.applicationToken.data, appTokenReader.buf, appTokenLen);
+
+ /* ECH Config ID, which may be empty. */
+ rv = sslRead_ReadVariable(&reader, 1, &echConfigIdBuf);
+ if (rv != SECSuccess) {
+ FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, illegal_parameter);
+ return SECFailure;
+ }
+ /* ECH HRR PSK, if present, is already used by tls13_GetEchInfoFromCookie */
+ rv = sslRead_ReadVariable(&reader, 1, &echPskBuf);
+ if (rv != SECSuccess) {
+ FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, illegal_parameter);
+ return SECFailure;
+ }
+
+ /* The remainder is the hash. */
+ unsigned int hashLen = SSL_READER_REMAINING(&reader);
+ if (hashLen != tls13_GetHashSize(ss)) {
+ FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, illegal_parameter);
+ return SECFailure;
+ }
+
+ /* Now reinject the message. */
+ SSL_ASSERT_HASHES_EMPTY(ss);
+ rv = ssl_HashHandshakeMessageInt(ss, ssl_hs_message_hash, 0,
+ SSL_READER_CURRENT(&reader), hashLen,
+ ssl3_UpdateHandshakeHashes);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ /* And finally reinject the HRR. */
+ rv = tls13_ConstructHelloRetryRequest(ss, cipherSuite,
+ selectedGroup,
+ cookie, cookieLen,
+ &messageBuf);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ rv = ssl_HashHandshakeMessageInt(ss, ssl_hs_server_hello, 0,
+ SSL_BUFFER_BASE(&messageBuf),
+ SSL_BUFFER_LEN(&messageBuf),
+ ssl3_UpdateHandshakeHashes);
+ sslBuffer_Clear(&messageBuf);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ *previousCipherSuite = cipherSuite;
+ *previousGroup = selectedGroup;
+ *previousEchOffered = echConfigIdBuf.len > 0;
+ return SECSuccess;
+}