summaryrefslogtreecommitdiffstats
path: root/security/nss/lib/ssl/tls13exthandle.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/nss/lib/ssl/tls13exthandle.c')
-rw-r--r--security/nss/lib/ssl/tls13exthandle.c1169
1 files changed, 1169 insertions, 0 deletions
diff --git a/security/nss/lib/ssl/tls13exthandle.c b/security/nss/lib/ssl/tls13exthandle.c
new file mode 100644
index 000000000..be93b97db
--- /dev/null
+++ b/security/nss/lib/ssl/tls13exthandle.c
@@ -0,0 +1,1169 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * 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 "nssrenam.h"
+#include "nss.h"
+#include "ssl.h"
+#include "sslproto.h"
+#include "sslimpl.h"
+#include "pk11pub.h"
+#include "ssl3ext.h"
+#include "ssl3exthandle.h"
+#include "tls13exthandle.h"
+
+PRInt32
+tls13_ServerSendStatusRequestXtn(
+ const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ PRBool append,
+ PRUint32 maxBytes)
+{
+ PRInt32 extension_length;
+ const sslServerCert *serverCert = ss->sec.serverCert;
+ const SECItem *item;
+ SECStatus rv;
+
+ if (!serverCert->certStatusArray ||
+ !serverCert->certStatusArray->len) {
+ return 0;
+ }
+
+ item = &serverCert->certStatusArray->items[0];
+
+ /* Only send the first entry. */
+ extension_length = 2 + 2 + 1 /* status_type */ + 3 + item->len;
+ if (maxBytes < (PRUint32)extension_length) {
+ return 0;
+ }
+ if (append) {
+ /* extension_type */
+ rv = ssl3_ExtAppendHandshakeNumber(ss, ssl_cert_status_xtn, 2);
+ if (rv != SECSuccess)
+ return -1;
+ /* length of extension_data */
+ rv = ssl3_ExtAppendHandshakeNumber(ss, extension_length - 4, 2);
+ if (rv != SECSuccess)
+ return -1;
+ /* status_type == ocsp */
+ rv = ssl3_ExtAppendHandshakeNumber(ss, 1 /*ocsp*/, 1);
+ if (rv != SECSuccess)
+ return rv; /* err set by AppendHandshake. */
+ /* opaque OCSPResponse<1..2^24-1> */
+ rv = ssl3_ExtAppendHandshakeVariable(ss, item->data, item->len, 3);
+ if (rv != SECSuccess)
+ return rv; /* err set by AppendHandshake. */
+ }
+
+ return extension_length;
+}
+
+/*
+ * [draft-ietf-tls-tls13-11] Section 6.3.2.3.
+ *
+ * struct {
+ * NamedGroup group;
+ * opaque key_exchange<1..2^16-1>;
+ * } KeyShareEntry;
+ *
+ * struct {
+ * select (role) {
+ * case client:
+ * KeyShareEntry client_shares<4..2^16-1>;
+ *
+ * case server:
+ * KeyShareEntry server_share;
+ * }
+ * } KeyShare;
+ *
+ * DH is Section 6.3.2.3.1.
+ *
+ * opaque dh_Y<1..2^16-1>;
+ *
+ * ECDH is Section 6.3.2.3.2.
+ *
+ * opaque point <1..2^8-1>;
+ */
+static PRUint32
+tls13_SizeOfKeyShareEntry(const SECKEYPublicKey *pubKey)
+{
+ /* Size = NamedGroup(2) + length(2) + opaque<?> share */
+ switch (pubKey->keyType) {
+ case ecKey:
+ return 2 + 2 + pubKey->u.ec.publicValue.len;
+ case dhKey:
+ return 2 + 2 + pubKey->u.dh.prime.len;
+ default:
+ PORT_Assert(0);
+ }
+ return 0;
+}
+
+static PRUint32
+tls13_SizeOfClientKeyShareExtension(const sslSocket *ss)
+{
+ PRCList *cursor;
+ /* Size is: extension(2) + extension_len(2) + client_shares(2) */
+ PRUint32 size = 2 + 2 + 2;
+ for (cursor = PR_NEXT_LINK(&ss->ephemeralKeyPairs);
+ cursor != &ss->ephemeralKeyPairs;
+ cursor = PR_NEXT_LINK(cursor)) {
+ sslEphemeralKeyPair *keyPair = (sslEphemeralKeyPair *)cursor;
+ size += tls13_SizeOfKeyShareEntry(keyPair->keys->pubKey);
+ }
+ return size;
+}
+
+static SECStatus
+tls13_EncodeKeyShareEntry(const sslSocket *ss, const sslEphemeralKeyPair *keyPair)
+{
+ SECStatus rv;
+ SECKEYPublicKey *pubKey = keyPair->keys->pubKey;
+ unsigned int size = tls13_SizeOfKeyShareEntry(pubKey);
+
+ rv = ssl3_ExtAppendHandshakeNumber(ss, keyPair->group->name, 2);
+ if (rv != SECSuccess)
+ return rv;
+ rv = ssl3_ExtAppendHandshakeNumber(ss, size - 4, 2);
+ if (rv != SECSuccess)
+ return rv;
+
+ switch (pubKey->keyType) {
+ case ecKey:
+ rv = tls13_EncodeECDHEKeyShareKEX(ss, pubKey);
+ break;
+ case dhKey:
+ rv = ssl_AppendPaddedDHKeyShare(ss, pubKey, PR_FALSE);
+ break;
+ default:
+ PORT_Assert(0);
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ break;
+ }
+
+ return rv;
+}
+
+PRInt32
+tls13_ClientSendKeyShareXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRBool append,
+ PRUint32 maxBytes)
+{
+ PRUint32 extension_length;
+
+ if (ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_3) {
+ return 0;
+ }
+
+ /* Optimistically try to send an ECDHE key using the
+ * preexisting key (in future will be keys) */
+ SSL_TRC(3, ("%d: TLS13[%d]: send client key share xtn",
+ SSL_GETPID(), ss->fd));
+
+ extension_length = tls13_SizeOfClientKeyShareExtension(ss);
+ if (maxBytes < extension_length) {
+ PORT_Assert(0);
+ return 0;
+ }
+
+ if (append) {
+ SECStatus rv;
+ PRCList *cursor;
+
+ rv = ssl3_ExtAppendHandshakeNumber(ss, ssl_tls13_key_share_xtn, 2);
+ if (rv != SECSuccess)
+ goto loser;
+
+ /* The extension length */
+ rv = ssl3_ExtAppendHandshakeNumber(ss, extension_length - 4, 2);
+ if (rv != SECSuccess)
+ goto loser;
+
+ /* The length of KeyShares */
+ rv = ssl3_ExtAppendHandshakeNumber(ss, extension_length - 6, 2);
+ if (rv != SECSuccess)
+ goto loser;
+
+ for (cursor = PR_NEXT_LINK(&ss->ephemeralKeyPairs);
+ cursor != &ss->ephemeralKeyPairs;
+ cursor = PR_NEXT_LINK(cursor)) {
+ sslEphemeralKeyPair *keyPair = (sslEphemeralKeyPair *)cursor;
+ rv = tls13_EncodeKeyShareEntry(ss, keyPair);
+ if (rv != SECSuccess)
+ goto loser;
+ }
+
+ xtnData->advertised[xtnData->numAdvertised++] =
+ ssl_tls13_key_share_xtn;
+ }
+
+ return extension_length;
+
+loser:
+ return -1;
+}
+
+static SECStatus
+tls13_HandleKeyShareEntry(const sslSocket *ss, TLSExtensionData *xtnData, SECItem *data)
+{
+ SECStatus rv;
+ PRInt32 group;
+ const sslNamedGroupDef *groupDef;
+ TLS13KeyShareEntry *ks = NULL;
+ SECItem share = { siBuffer, NULL, 0 };
+
+ group = ssl3_ExtConsumeHandshakeNumber(ss, 2, &data->data, &data->len);
+ if (group < 0) {
+ PORT_SetError(SSL_ERROR_RX_MALFORMED_KEY_SHARE);
+ goto loser;
+ }
+ groupDef = ssl_LookupNamedGroup(group);
+ rv = ssl3_ExtConsumeHandshakeVariable(ss, &share, 2, &data->data,
+ &data->len);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ /* If the group is disabled, continue. */
+ if (!groupDef) {
+ return SECSuccess;
+ }
+
+ ks = PORT_ZNew(TLS13KeyShareEntry);
+ if (!ks)
+ goto loser;
+ ks->group = groupDef;
+
+ rv = SECITEM_CopyItem(NULL, &ks->key_exchange, &share);
+ if (rv != SECSuccess)
+ goto loser;
+
+ PR_APPEND_LINK(&ks->link, &xtnData->remoteKeyShares);
+ return SECSuccess;
+
+loser:
+ if (ks)
+ tls13_DestroyKeyShareEntry(ks);
+ return SECFailure;
+}
+/* Handle an incoming KeyShare extension at the client and copy to
+ * |xtnData->remoteKeyShares| for future use. The key
+ * share is processed in tls13_HandleServerKeyShare(). */
+SECStatus
+tls13_ClientHandleKeyShareXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type, SECItem *data)
+{
+ SECStatus rv;
+ PORT_Assert(PR_CLIST_IS_EMPTY(&xtnData->remoteKeyShares));
+
+ PORT_Assert(!ss->sec.isServer);
+ if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
+ /* This can't happen because the extension processing
+ * code filters out TLS 1.3 extensions when not in
+ * TLS 1.3 mode. */
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+
+ SSL_TRC(3, ("%d: SSL3[%d]: handle key_share extension",
+ SSL_GETPID(), ss->fd));
+
+ rv = tls13_HandleKeyShareEntry(ss, xtnData, data);
+ if (rv != SECSuccess) {
+ PORT_SetError(SSL_ERROR_RX_MALFORMED_KEY_SHARE);
+ return SECFailure;
+ }
+
+ if (data->len) {
+ PORT_SetError(SSL_ERROR_RX_MALFORMED_KEY_SHARE);
+ return SECFailure;
+ }
+
+ return SECSuccess;
+}
+
+SECStatus
+tls13_ClientHandleKeyShareXtnHrr(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type, SECItem *data)
+{
+ SECStatus rv;
+ PRInt32 tmp;
+ const sslNamedGroupDef *group;
+
+ PORT_Assert(!ss->sec.isServer);
+ PORT_Assert(ss->vrange.max >= SSL_LIBRARY_VERSION_TLS_1_3);
+
+ SSL_TRC(3, ("%d: SSL3[%d]: handle key_share extension in HRR",
+ SSL_GETPID(), ss->fd));
+
+ tmp = ssl3_ExtConsumeHandshakeNumber(ss, 2, &data->data, &data->len);
+ if (tmp < 0) {
+ return SECFailure; /* error code already set */
+ }
+ if (data->len) {
+ ssl3_ExtSendAlert(ss, alert_fatal, decode_error);
+ PORT_SetError(SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST);
+ return SECFailure;
+ }
+
+ group = ssl_LookupNamedGroup((SSLNamedGroup)tmp);
+ /* If the group is not enabled, or we already have a share for the
+ * requested group, abort. */
+ if (!ssl_NamedGroupEnabled(ss, group) ||
+ ssl_HaveEphemeralKeyPair(ss, group)) {
+ ssl3_ExtSendAlert(ss, alert_fatal, illegal_parameter);
+ PORT_SetError(SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST);
+ return SECFailure;
+ }
+
+ /* Now delete all the key shares per [draft-ietf-tls-tls13 S 4.1.2] */
+ ssl_FreeEphemeralKeyPairs(CONST_CAST(sslSocket, ss));
+
+ /* And replace with our new share. */
+ rv = tls13_CreateKeyShare(CONST_CAST(sslSocket, ss), group);
+ if (rv != SECSuccess) {
+ ssl3_ExtSendAlert(ss, alert_fatal, internal_error);
+ PORT_SetError(SEC_ERROR_KEYGEN_FAIL);
+ return SECFailure;
+ }
+
+ return SECSuccess;
+}
+
+/* Handle an incoming KeyShare extension at the server and copy to
+ * |xtnData->remoteKeyShares| for future use. The key
+ * share is processed in tls13_HandleClientKeyShare(). */
+SECStatus
+tls13_ServerHandleKeyShareXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type, SECItem *data)
+{
+ SECStatus rv;
+ PRInt32 length;
+
+ PORT_Assert(ss->sec.isServer);
+ PORT_Assert(PR_CLIST_IS_EMPTY(&xtnData->remoteKeyShares));
+
+ if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
+ return SECSuccess;
+ }
+
+ SSL_TRC(3, ("%d: SSL3[%d]: handle key_share extension",
+ SSL_GETPID(), ss->fd));
+
+ /* Redundant length because of TLS encoding (this vector consumes
+ * the entire extension.) */
+ length = ssl3_ExtConsumeHandshakeNumber(ss, 2, &data->data,
+ &data->len);
+ if (length < 0)
+ goto loser;
+ if (length != data->len) {
+ /* Check for consistency */
+ PORT_SetError(SSL_ERROR_RX_MALFORMED_KEY_SHARE);
+ goto loser;
+ }
+
+ while (data->len) {
+ rv = tls13_HandleKeyShareEntry(ss, xtnData, data);
+ if (rv != SECSuccess)
+ goto loser;
+ }
+
+ /* Check that the client only offered one share if this is
+ * after HRR. */
+ if (ss->ssl3.hs.helloRetry) {
+ if (PR_PREV_LINK(&xtnData->remoteKeyShares) !=
+ PR_NEXT_LINK(&xtnData->remoteKeyShares)) {
+ PORT_SetError(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
+ goto loser;
+ }
+ }
+
+ return SECSuccess;
+
+loser:
+ tls13_DestroyKeyShares(&xtnData->remoteKeyShares);
+ return SECFailure;
+}
+
+PRInt32
+tls13_ServerSendKeyShareXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRBool append,
+ PRUint32 maxBytes)
+{
+ PRUint32 extension_length;
+ PRUint32 entry_length;
+ SECStatus rv;
+ sslEphemeralKeyPair *keyPair;
+
+ /* There should be exactly one key share. */
+ PORT_Assert(!PR_CLIST_IS_EMPTY(&ss->ephemeralKeyPairs));
+ PORT_Assert(PR_PREV_LINK(&ss->ephemeralKeyPairs) ==
+ PR_NEXT_LINK(&ss->ephemeralKeyPairs));
+
+ keyPair = (sslEphemeralKeyPair *)PR_NEXT_LINK(&ss->ephemeralKeyPairs);
+
+ entry_length = tls13_SizeOfKeyShareEntry(keyPair->keys->pubKey);
+ extension_length = 2 + 2 + entry_length; /* Type + length + entry_length */
+ if (maxBytes < extension_length) {
+ PORT_Assert(0);
+ return 0;
+ }
+
+ if (append) {
+ rv = ssl3_ExtAppendHandshakeNumber(ss, ssl_tls13_key_share_xtn, 2);
+ if (rv != SECSuccess)
+ goto loser;
+
+ rv = ssl3_ExtAppendHandshakeNumber(ss, entry_length, 2);
+ if (rv != SECSuccess)
+ goto loser;
+
+ rv = tls13_EncodeKeyShareEntry(ss, keyPair);
+ if (rv != SECSuccess)
+ goto loser;
+ }
+
+ return extension_length;
+
+loser:
+ return -1;
+}
+
+/* Called by clients.
+ *
+ * struct {
+ * opaque identity<0..2^16-1>;
+ * uint32 obfuscated_ticket_age;
+ * } PskIdentity;
+ *
+ * opaque PskBinderEntry<32..255>;
+ *
+ * struct {
+ * select (Handshake.msg_type) {
+ * case client_hello:
+ * PskIdentity identities<6..2^16-1>;
+ * PskBinderEntry binders<33..2^16-1>;
+ *
+ * case server_hello:
+ * uint16 selected_identity;
+ * };
+ *
+ * } PreSharedKeyExtension;
+
+ * Presently the only way to get a PSK is by resumption, so this is
+ * really a ticket label and there will be at most one.
+ */
+PRInt32
+tls13_ClientSendPreSharedKeyXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ PRBool append,
+ PRUint32 maxBytes)
+{
+ PRInt32 extension_length;
+ PRInt32 identities_length;
+ PRInt32 binders_length;
+ NewSessionTicket *session_ticket;
+
+ /* We only set statelessResume on the client in TLS 1.3 code. */
+ if (!ss->statelessResume)
+ return 0;
+
+ PORT_Assert(ss->vrange.max >= SSL_LIBRARY_VERSION_TLS_1_3);
+
+ /* The length computations are simplified by the fact that there
+ * is just one ticket at most. */
+ session_ticket = &ss->sec.ci.sid->u.ssl3.locked.sessionTicket;
+ identities_length =
+ 2 + /* vector length */
+ 2 + session_ticket->ticket.len + /* identity length + ticket len */
+ 4; /* obfuscated_ticket_age */
+ binders_length =
+ 2 + /* vector length */
+ 1 + tls13_GetHashSizeForHash(
+ tls13_GetHashForCipherSuite(ss->sec.ci.sid->u.ssl3.cipherSuite));
+ extension_length =
+ 2 + 2 + /* Type + length */
+ identities_length + binders_length;
+
+ if (maxBytes < (PRUint32)extension_length) {
+ PORT_Assert(0);
+ return 0;
+ }
+
+ if (append) {
+ SECStatus rv;
+ PRUint32 age;
+ unsigned int prefixLength;
+ PRUint8 binder[TLS13_MAX_FINISHED_SIZE];
+ unsigned int binderLen;
+
+ /* extension_type */
+ rv = ssl3_ExtAppendHandshakeNumber(ss, ssl_tls13_pre_shared_key_xtn, 2);
+ if (rv != SECSuccess)
+ goto loser;
+ rv = ssl3_ExtAppendHandshakeNumber(ss, extension_length - 4, 2);
+ if (rv != SECSuccess)
+ goto loser;
+ rv = ssl3_ExtAppendHandshakeNumber(ss, identities_length - 2, 2);
+ if (rv != SECSuccess)
+ goto loser;
+ rv = ssl3_ExtAppendHandshakeVariable(ss, session_ticket->ticket.data,
+ session_ticket->ticket.len, 2);
+ if (rv != SECSuccess)
+ goto loser;
+
+ /* Obfuscated age. */
+ age = ssl_Time() - session_ticket->received_timestamp;
+ age += session_ticket->ticket_age_add;
+ rv = ssl3_ExtAppendHandshakeNumber(ss, age, 4);
+ if (rv != SECSuccess)
+ goto loser;
+
+ /* Now the binders. */
+ prefixLength = ss->ssl3.hs.messages.len;
+ rv = tls13_ComputePskBinder(CONST_CAST(sslSocket, ss), PR_TRUE,
+ prefixLength, binder, &binderLen,
+ sizeof(binder));
+ if (rv != SECSuccess)
+ goto loser;
+ PORT_Assert(binderLen == tls13_GetHashSize(ss));
+ rv = ssl3_ExtAppendHandshakeNumber(ss, binders_length - 2, 2);
+ if (rv != SECSuccess)
+ goto loser;
+ rv = ssl3_ExtAppendHandshakeVariable(ss,
+ binder, binderLen, 1);
+ if (rv != SECSuccess)
+ goto loser;
+
+ PRINT_BUF(50, (ss, "Sending PreSharedKey value",
+ session_ticket->ticket.data,
+ session_ticket->ticket.len));
+
+ xtnData->sentSessionTicketInClientHello = PR_TRUE;
+ xtnData->advertised[xtnData->numAdvertised++] =
+ ssl_tls13_pre_shared_key_xtn;
+ }
+ return extension_length;
+
+loser:
+ xtnData->ticketTimestampVerified = PR_FALSE;
+ return -1;
+}
+
+/* Handle a TLS 1.3 PreSharedKey Extension. We only accept PSKs
+ * that contain session tickets. */
+SECStatus
+tls13_ServerHandlePreSharedKeyXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type,
+ SECItem *data)
+{
+ SECItem inner;
+ SECStatus rv;
+ unsigned int numIdentities = 0;
+ unsigned int numBinders = 0;
+
+ SSL_TRC(3, ("%d: SSL3[%d]: handle pre_shared_key extension",
+ SSL_GETPID(), ss->fd));
+
+ /* If we are doing < TLS 1.3, then ignore this. */
+ if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
+ return SECSuccess;
+ }
+
+ /* Parse the identities list. */
+ rv = ssl3_ExtConsumeHandshakeVariable(ss,
+ &inner, 2, &data->data, &data->len);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ while (inner.len) {
+ SECItem label;
+ PRUint32 utmp;
+
+ rv = ssl3_ExtConsumeHandshakeVariable(ss, &label, 2,
+ &inner.data, &inner.len);
+ if (rv != SECSuccess)
+ return rv;
+ if (!label.len) {
+ goto alert_loser;
+ }
+
+ /* Read and discard session ticket age. Bug 1295163 */
+ rv = ssl3_ExtConsumeHandshake(ss, &utmp, 4,
+ &inner.data, &inner.len);
+ if (rv != SECSuccess)
+ return rv;
+
+ if (!numIdentities) {
+ PRINT_BUF(50, (ss, "Handling PreSharedKey value",
+ label.data, label.len));
+ rv = ssl3_ProcessSessionTicketCommon(
+ CONST_CAST(sslSocket, ss), &label);
+ /* This only happens if we have an internal error, not
+ * a malformed ticket. Bogus tickets just don't resume
+ * and return SECSuccess. */
+ if (rv != SECSuccess)
+ return SECFailure;
+ }
+ ++numIdentities;
+ }
+
+ xtnData->pskBinderPrefixLen = ss->ssl3.hs.messages.len - data->len;
+
+ /* Parse the binders list. */
+ rv = ssl3_ExtConsumeHandshakeVariable(ss,
+ &inner, 2, &data->data, &data->len);
+ if (rv != SECSuccess)
+ return SECFailure;
+ if (data->len) {
+ goto alert_loser;
+ }
+
+ while (inner.len) {
+ SECItem binder;
+ rv = ssl3_ExtConsumeHandshakeVariable(ss, &binder, 1,
+ &inner.data, &inner.len);
+ if (rv != SECSuccess)
+ return rv;
+ if (binder.len < 32) {
+ goto alert_loser;
+ }
+
+ if (!numBinders) {
+ xtnData->pskBinder = binder;
+ }
+ ++numBinders;
+ }
+
+ if (numBinders != numIdentities)
+ goto alert_loser;
+
+ /* Keep track of negotiated extensions. Note that this does not
+ * mean we are resuming. */
+ xtnData->negotiated[xtnData->numNegotiated++] = ex_type;
+
+ return SECSuccess;
+
+alert_loser:
+ ssl3_ExtSendAlert(ss, alert_fatal, illegal_parameter);
+ PORT_SetError(SSL_ERROR_MALFORMED_PRE_SHARED_KEY);
+ return SECFailure;
+}
+
+PRInt32
+tls13_ServerSendPreSharedKeyXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ PRBool append,
+ PRUint32 maxBytes)
+{
+ PRInt32 extension_length =
+ 2 + 2 + 2; /* type + len + index */
+ SECStatus rv;
+
+ if (maxBytes < (PRUint32)extension_length) {
+ PORT_Assert(0);
+ return 0;
+ }
+
+ if (append) {
+ rv = ssl3_ExtAppendHandshakeNumber(ss, ssl_tls13_pre_shared_key_xtn, 2);
+ if (rv != SECSuccess)
+ return -1;
+
+ rv = ssl3_ExtAppendHandshakeNumber(ss, 2, 2);
+ if (rv != SECSuccess)
+ return -1;
+
+ /* We only process the first session ticket the client sends,
+ * so the index is always 0. */
+ rv = ssl3_ExtAppendHandshakeNumber(ss, 0, 2);
+ if (rv != SECSuccess)
+ return -1;
+ }
+
+ return extension_length;
+}
+
+/* Handle a TLS 1.3 PreSharedKey Extension. We only accept PSKs
+ * that contain session tickets. */
+SECStatus
+tls13_ClientHandlePreSharedKeyXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type,
+ SECItem *data)
+{
+ PRInt32 index;
+
+ SSL_TRC(3, ("%d: SSL3[%d]: handle pre_shared_key extension",
+ SSL_GETPID(), ss->fd));
+
+ /* If we are doing < TLS 1.3, then ignore this. */
+ if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
+ return SECSuccess;
+ }
+
+ index = ssl3_ExtConsumeHandshakeNumber(ss, 2, &data->data, &data->len);
+ if (index < 0)
+ return SECFailure;
+
+ /* This should be the end of the extension. */
+ if (data->len) {
+ PORT_SetError(SSL_ERROR_MALFORMED_PRE_SHARED_KEY);
+ return SECFailure;
+ }
+
+ /* We only sent one PSK label so index must be equal to 0 */
+ if (index) {
+ PORT_SetError(SSL_ERROR_MALFORMED_PRE_SHARED_KEY);
+ return SECFailure;
+ }
+
+ /* Keep track of negotiated extensions. */
+ xtnData->negotiated[xtnData->numNegotiated++] = ex_type;
+
+ return SECSuccess;
+}
+
+/*
+ * struct { } EarlyDataIndication;
+ */
+PRInt32
+tls13_ClientSendEarlyDataXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ PRBool append,
+ PRUint32 maxBytes)
+{
+ SECStatus rv;
+ PRInt32 extension_length;
+
+ if (!tls13_ClientAllow0Rtt(ss, ss->sec.ci.sid))
+ return 0;
+
+ /* type + length */
+ extension_length = 2 + 2;
+
+ if (maxBytes < (PRUint32)extension_length) {
+ PORT_Assert(0);
+ return 0;
+ }
+
+ if (append) {
+ rv = ssl3_ExtAppendHandshakeNumber(ss, ssl_tls13_early_data_xtn, 2);
+ if (rv != SECSuccess)
+ return -1;
+
+ rv = ssl3_ExtAppendHandshakeNumber(ss, 0, 2);
+ if (rv != SECSuccess)
+ return -1;
+ }
+
+ xtnData->advertised[xtnData->numAdvertised++] =
+ ssl_tls13_early_data_xtn;
+
+ return extension_length;
+}
+
+SECStatus
+tls13_ServerHandleEarlyDataXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type,
+ SECItem *data)
+{
+ SSL_TRC(3, ("%d: TLS13[%d]: handle early_data extension",
+ SSL_GETPID(), ss->fd));
+
+ /* If we are doing < TLS 1.3, then ignore this. */
+ if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
+ return SECSuccess;
+ }
+
+ if (data->len) {
+ PORT_SetError(SSL_ERROR_MALFORMED_EARLY_DATA);
+ return SECFailure;
+ }
+
+ xtnData->negotiated[xtnData->numNegotiated++] = ex_type;
+
+ return SECSuccess;
+}
+
+/* This is only registered if we are sending it. */
+PRInt32
+tls13_ServerSendEarlyDataXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ PRBool append,
+ PRUint32 maxBytes)
+{
+ SSL_TRC(3, ("%d: TLS13[%d]: send early_data extension",
+ SSL_GETPID(), ss->fd));
+
+ PORT_Assert(ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted);
+ if (maxBytes < 4) {
+ PORT_Assert(0);
+ return 0;
+ }
+
+ if (append) {
+ SECStatus rv;
+
+ rv = ssl3_ExtAppendHandshakeNumber(ss, ssl_tls13_early_data_xtn, 2);
+ if (rv != SECSuccess)
+ return -1;
+
+ rv = ssl3_ExtAppendHandshakeNumber(ss, 0, 2);
+ if (rv != SECSuccess)
+ return -1;
+ }
+
+ return 4;
+}
+
+/* This will only be called if we also offered the extension. */
+SECStatus
+tls13_ClientHandleEarlyDataXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type,
+ SECItem *data)
+{
+ SSL_TRC(3, ("%d: TLS13[%d]: handle early_data extension",
+ SSL_GETPID(), ss->fd));
+
+ /* If we are doing < TLS 1.3, then ignore this. */
+ if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
+ PORT_SetError(SSL_ERROR_EXTENSION_DISALLOWED_FOR_VERSION);
+ return SECFailure;
+ }
+
+ if (data->len) {
+ PORT_SetError(SSL_ERROR_MALFORMED_EARLY_DATA);
+ return SECFailure;
+ }
+
+ /* Keep track of negotiated extensions. */
+ xtnData->negotiated[xtnData->numNegotiated++] = ex_type;
+
+ return SECSuccess;
+}
+
+SECStatus
+tls13_ClientHandleTicketEarlyDataInfoXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type,
+ SECItem *data)
+{
+ PRUint32 utmp;
+ SECStatus rv;
+
+ SSL_TRC(3, ("%d: TLS13[%d]: handle early_data_info extension",
+ SSL_GETPID(), ss->fd));
+
+ /* If we are doing < TLS 1.3, then ignore this. */
+ if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
+ PORT_SetError(SSL_ERROR_EXTENSION_DISALLOWED_FOR_VERSION);
+ return SECFailure;
+ }
+
+ rv = ssl3_ExtConsumeHandshake(ss, &utmp, sizeof(utmp),
+ &data->data, &data->len);
+ if (rv != SECSuccess) {
+ PORT_SetError(SSL_ERROR_RX_MALFORMED_NEW_SESSION_TICKET);
+ return SECFailure;
+ }
+ if (data->len) {
+ PORT_SetError(SSL_ERROR_RX_MALFORMED_NEW_SESSION_TICKET);
+ return SECFailure;
+ }
+
+ xtnData->max_early_data_size = PR_ntohl(utmp);
+
+ return SECSuccess;
+}
+
+/*
+ * struct {
+ * ProtocolVersion versions<2..254>;
+ * } SupportedVersions;
+ */
+PRInt32
+tls13_ClientSendSupportedVersionsXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRBool append,
+ PRUint32 maxBytes)
+{
+ PRInt32 extensions_len;
+ PRUint16 version;
+ SECStatus rv;
+
+ if (ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_3) {
+ return 0;
+ }
+
+ SSL_TRC(3, ("%d: TLS13[%d]: send supported_versions extension",
+ SSL_GETPID(), ss->fd));
+
+ /* Extension type, extension len fiels, vector len field,
+ * length of the values. */
+ extensions_len = 2 + 2 + 1 +
+ 2 * (ss->vrange.max - ss->vrange.min + 1);
+
+ if (maxBytes < (PRUint32)extensions_len) {
+ PORT_Assert(0);
+ return 0;
+ }
+
+ if (append) {
+ rv = ssl3_ExtAppendHandshakeNumber(ss, ssl_tls13_supported_versions_xtn, 2);
+ if (rv != SECSuccess)
+ return -1;
+
+ rv = ssl3_ExtAppendHandshakeNumber(ss, extensions_len - 4, 2);
+ if (rv != SECSuccess)
+ return -1;
+
+ rv = ssl3_ExtAppendHandshakeNumber(ss, extensions_len - 5, 1);
+ if (rv != SECSuccess)
+ return -1;
+
+ for (version = ss->vrange.max; version >= ss->vrange.min; --version) {
+ rv = ssl3_ExtAppendHandshakeNumber(
+ ss, tls13_EncodeDraftVersion(version), 2);
+ if (rv != SECSuccess)
+ return -1;
+ }
+ }
+
+ return extensions_len;
+}
+
+/*
+ * struct {
+ * opaque cookie<1..2^16-1>;
+ * } Cookie;
+ */
+SECStatus
+tls13_ClientHandleHrrCookie(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type, SECItem *data)
+{
+ SECStatus rv;
+
+ SSL_TRC(3, ("%d: TLS13[%d]: handle cookie extension",
+ SSL_GETPID(), ss->fd));
+
+ PORT_Assert(ss->vrange.max >= SSL_LIBRARY_VERSION_TLS_1_3);
+
+ /* IMPORTANT: this is only valid while the HelloRetryRequest is still valid. */
+ rv = ssl3_ExtConsumeHandshakeVariable(
+ ss, &CONST_CAST(sslSocket, ss)->ssl3.hs.cookie, 2,
+ &data->data, &data->len);
+ if (rv != SECSuccess) {
+ PORT_SetError(SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST);
+ return SECFailure;
+ }
+ if (!ss->ssl3.hs.cookie.len || data->len) {
+ ssl3_ExtSendAlert(ss, alert_fatal, decode_error);
+ PORT_SetError(SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST);
+ return SECFailure;
+ }
+
+ return SECSuccess;
+}
+
+PRInt32
+tls13_ClientSendHrrCookieXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRBool append, PRUint32 maxBytes)
+{
+ PRInt32 extension_len;
+
+ if (ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_3 ||
+ !ss->ssl3.hs.cookie.len) {
+ return 0;
+ }
+
+ SSL_TRC(3, ("%d: TLS13[%d]: send cookie extension", SSL_GETPID(), ss->fd));
+
+ /* Extension type, length, cookie length, cookie value. */
+ extension_len = 2 + 2 + 2 + ss->ssl3.hs.cookie.len;
+
+ if (maxBytes < (PRUint32)extension_len) {
+ PORT_Assert(0);
+ return 0;
+ }
+
+ if (append) {
+ SECStatus rv = ssl3_ExtAppendHandshakeNumber(ss, ssl_tls13_cookie_xtn, 2);
+ if (rv != SECSuccess)
+ return -1;
+
+ rv = ssl3_ExtAppendHandshakeNumber(ss, extension_len - 4, 2);
+ if (rv != SECSuccess)
+ return -1;
+
+ rv = ssl3_ExtAppendHandshakeVariable(ss, ss->ssl3.hs.cookie.data,
+ ss->ssl3.hs.cookie.len, 2);
+ if (rv != SECSuccess)
+ return -1;
+ }
+ return extension_len;
+}
+
+/*
+ * enum { psk_ke(0), psk_dhe_ke(1), (255) } PskKeyExchangeMode;
+ *
+ * struct {
+ * PskKeyExchangeMode ke_modes<1..255>;
+ * } PskKeyExchangeModes;
+ */
+PRInt32
+tls13_ClientSendPskKeyExchangeModesXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ PRBool append, PRUint32 maxBytes)
+{
+ static const PRUint8 ke_modes[] = { tls13_psk_dh_ke };
+ static const unsigned long ke_modes_len = sizeof(ke_modes);
+ PRInt32 extension_len;
+
+ if (ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_3 ||
+ ss->opt.noCache) {
+ return 0;
+ }
+
+ extension_len =
+ 2 + 2 + /* Type + length */
+ 1 + ke_modes_len; /* key exchange modes vector */
+
+ SSL_TRC(3, ("%d: TLS13[%d]: send psk key exchange modes extension",
+ SSL_GETPID(), ss->fd));
+
+ if (maxBytes < (PRUint32)extension_len) {
+ PORT_Assert(0);
+ return 0;
+ }
+
+ if (append) {
+ SECStatus rv = ssl3_ExtAppendHandshakeNumber(
+ ss, ssl_tls13_psk_key_exchange_modes_xtn, 2);
+ if (rv != SECSuccess)
+ return -1;
+
+ rv = ssl3_ExtAppendHandshakeNumber(ss, extension_len - 4, 2);
+ if (rv != SECSuccess)
+ return -1;
+
+ rv = ssl3_ExtAppendHandshakeVariable(
+ ss, ke_modes, ke_modes_len, 1);
+ if (rv != SECSuccess)
+ return -1;
+ }
+ return extension_len;
+}
+
+SECStatus
+tls13_ServerHandlePskKeyExchangeModesXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ PRUint16 ex_type, SECItem *data)
+{
+ SECStatus rv;
+
+ /* If we are doing < TLS 1.3, then ignore this. */
+ if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
+ return SECSuccess;
+ }
+
+ SSL_TRC(3, ("%d: TLS13[%d]: handle PSK key exchange modes extension",
+ SSL_GETPID(), ss->fd));
+
+ /* IMPORTANT: We aren't copying these values, just setting pointers.
+ * They will only be valid as long as the ClientHello is in memory. */
+ rv = ssl3_ExtConsumeHandshakeVariable(ss,
+ &xtnData->psk_ke_modes, 1,
+ &data->data, &data->len);
+ if (rv != SECSuccess)
+ return rv;
+ if (!xtnData->psk_ke_modes.len || data->len) {
+ PORT_SetError(SSL_ERROR_MALFORMED_PSK_KEY_EXCHANGE_MODES);
+ return SECFailure;
+ }
+
+ /* Keep track of negotiated extensions. */
+ xtnData->negotiated[xtnData->numNegotiated++] = ex_type;
+
+ return SECSuccess;
+}
+
+PRInt32
+tls13_SendShortHeaderXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ PRBool append, PRUint32 maxBytes)
+{
+ PRUint32 extension_len = 2 + 2; /* Type + length (0). */
+
+ if (!ss->opt.enableShortHeaders) {
+ return 0;
+ }
+
+ /* Presently this is incompatible with 0-RTT. We will fix if
+ * it becomes more than an experiment. */
+ if (ss->opt.enable0RttData) {
+ return 0;
+ }
+
+ if (IS_DTLS(ss)) {
+ return 0;
+ }
+
+ SSL_TRC(3, ("%d: TLS13[%d]: send short_header extension",
+ SSL_GETPID(), ss->fd));
+
+ if (maxBytes < extension_len) {
+ PORT_Assert(0);
+ return 0;
+ }
+
+ if (append) {
+ SECStatus rv;
+
+ rv = ssl3_ExtAppendHandshakeNumber(ss, ssl_tls13_short_header_xtn, 2);
+ if (rv != SECSuccess)
+ return -1;
+
+ rv = ssl3_ExtAppendHandshakeNumber(ss, 0, 2);
+ if (rv != SECSuccess)
+ return -1;
+
+ xtnData->advertised[xtnData->numAdvertised++] =
+ ssl_tls13_short_header_xtn;
+ }
+
+ return extension_len;
+}
+
+SECStatus
+tls13_HandleShortHeaderXtn(
+ const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type,
+ SECItem *data)
+{
+ SSL_TRC(3, ("%d: TLS13[%d]: handle early_data extension",
+ SSL_GETPID(), ss->fd));
+
+ /* If we are doing < TLS 1.3, then ignore this. */
+ if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
+ return SECSuccess;
+ }
+
+ /* Presently this is incompatible with 0-RTT. We will fix if
+ * it becomes more than an experiment. */
+ if (ss->opt.enable0RttData) {
+ return SECSuccess;
+ }
+
+ if (IS_DTLS(ss)) {
+ PORT_SetError(SSL_ERROR_EXTENSION_DISALLOWED_FOR_VERSION);
+ return SECFailure;
+ }
+
+ if (data->len) {
+ PORT_SetError(SSL_ERROR_RX_MALFORMED_HANDSHAKE);
+ return SECFailure;
+ }
+
+ if (!ss->opt.enableShortHeaders) {
+ /* Ignore. */
+ return SECSuccess;
+ }
+
+ /* Keep track of negotiated extensions. */
+ xtnData->negotiated[xtnData->numNegotiated++] = ex_type;
+
+ if (ss->sec.isServer) {
+ SECStatus rv;
+
+ rv = ssl3_RegisterExtensionSender(ss, xtnData,
+ ssl_tls13_short_header_xtn,
+ tls13_SendShortHeaderXtn);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ }
+
+ return SECSuccess;
+}