| Index: net/third_party/nss/ssl/tls13con.c
|
| diff --git a/net/third_party/nss/ssl/tls13con.c b/net/third_party/nss/ssl/tls13con.c
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..4bb136a5eefbe4173acf93fb2cbf679baf4a8526
|
| --- /dev/null
|
| +++ b/net/third_party/nss/ssl/tls13con.c
|
| @@ -0,0 +1,2059 @@
|
| +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
| +/*
|
| + * TLS 1.3 Protocol
|
| + *
|
| + * 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 "stdarg.h"
|
| +#include "cert.h"
|
| +#include "ssl.h"
|
| +#include "keyhi.h"
|
| +#include "pk11func.h"
|
| +#include "secitem.h"
|
| +#include "sslimpl.h"
|
| +#include "sslproto.h"
|
| +#include "sslerr.h"
|
| +#include "tls13hkdf.h"
|
| +#include "tls13con.h"
|
| +
|
| +typedef enum {
|
| + TrafficKeyEarlyData,
|
| + TrafficKeyHandshake,
|
| + TrafficKeyApplicationData
|
| +} TrafficKeyType;
|
| +
|
| +typedef enum {
|
| + InstallCipherSpecRead,
|
| + InstallCipherSpecWrite,
|
| + InstallCipherSpecBoth
|
| +} InstallCipherSpecDirection;
|
| +
|
| +#define MAX_FINISHED_SIZE 64
|
| +
|
| +static SECStatus tls13_InitializeHandshakeEncryption(sslSocket *ss);
|
| +static SECStatus tls13_InstallCipherSpec(
|
| + sslSocket *ss, InstallCipherSpecDirection direction);
|
| +static SECStatus tls13_InitCipherSpec(
|
| + sslSocket *ss, TrafficKeyType type, InstallCipherSpecDirection install);
|
| +static SECStatus tls13_AESGCM(
|
| + ssl3KeyMaterial *keys,
|
| + PRBool doDecrypt,
|
| + unsigned char *out, int *outlen, int maxout,
|
| + const unsigned char *in, int inlen,
|
| + const unsigned char *additionalData, int additionalDataLen);
|
| +static SECStatus tls13_SendEncryptedExtensions(sslSocket *ss);
|
| +static SECStatus tls13_HandleEncryptedExtensions(sslSocket *ss, SSL3Opaque *b,
|
| + PRUint32 length);
|
| +static SECStatus tls13_HandleCertificate(
|
| + sslSocket *ss, SSL3Opaque *b, PRUint32 length);
|
| +static SECStatus tls13_HandleCertificateRequest(sslSocket *ss, SSL3Opaque *b,
|
| + PRUint32 length);
|
| +static SECStatus tls13_HandleCertificateStatus(sslSocket *ss, SSL3Opaque *b,
|
| + PRUint32 length);
|
| +static SECStatus tls13_HandleCertificateVerify(
|
| + sslSocket *ss, SSL3Opaque *b, PRUint32 length,
|
| + SSL3Hashes *hashes);
|
| +static SECStatus tls13_HkdfExtractSharedKey(sslSocket *ss, PK11SymKey *key,
|
| + SharedSecretType keyType);
|
| +static SECStatus tls13_SendFinished(sslSocket *ss);
|
| +static SECStatus tls13_HandleFinished(sslSocket *ss, SSL3Opaque *b, PRUint32 length,
|
| + const SSL3Hashes *hashes);
|
| +static SECStatus tls13_HandleNewSessionTicket(sslSocket *ss, SSL3Opaque *b,
|
| + PRUint32 length);
|
| +static SECStatus tls13_ComputeSecrets1(sslSocket *ss);
|
| +static SECStatus tls13_ComputeFinished(
|
| + sslSocket *ss, const SSL3Hashes *hashes,
|
| + PRBool sending,
|
| + PRUint8 *output, unsigned int *outputLen,
|
| + unsigned int maxOutputLen);
|
| +static SECStatus tls13_SendClientSecondRound(sslSocket *ss);
|
| +static SECStatus tls13_FinishHandshake(sslSocket *ss);
|
| +
|
| +const char kHkdfLabelExpandedSs[] = "expanded static secret";
|
| +const char kHkdfLabelExpandedEs[] = "expanded ephemeral secret";
|
| +const char kHkdfLabelMasterSecret[] = "master secret";
|
| +const char kHkdfLabelTrafficSecret[] = "traffic secret";
|
| +const char kHkdfLabelClientFinishedSecret[] = "client finished";
|
| +const char kHkdfLabelServerFinishedSecret[] = "server finished";
|
| +const char kHkdfLabelResumptionMasterSecret[] = "resumption master secret";
|
| +const char kHkdfLabelExporterMasterSecret[] = "exporter master secret";
|
| +const char kHkdfPhaseEarlyHandshakeDataKeys[] = "early handshake key expansion";
|
| +const char kHkdfPhaseEarlyApplicationDataKeys[] = "early application data key expansion";
|
| +const char kHkdfPhaseHandshakeKeys[] = "handshake key expansion";
|
| +const char kHkdfPhaseApplicationDataKeys[] = "application data key expansion";
|
| +const char kHkdfPurposeClientWriteKey[] = "client write key";
|
| +const char kHkdfPurposeServerWriteKey[] = "server write key";
|
| +const char kHkdfPurposeClientWriteIv[] = "client write iv";
|
| +const char kHkdfPurposeServerWriteIv[] = "server write iv";
|
| +const char kClientFinishedLabel[] = "client finished";
|
| +const char kServerFinishedLabel[] = "server finished";
|
| +
|
| +const SSL3ProtocolVersion kRecordVersion = 0x0301U;
|
| +
|
| +#define FATAL_ERROR(ss, prError, desc) \
|
| + do { \
|
| + SSL_TRC(3, ("%d: TLS13[%d]: fatal error %d in %s (%s:%d)", \
|
| + SSL_GETPID(), ss->fd, prError, __func__, __FILE__, __LINE__)); \
|
| + tls13_FatalError(ss, prError, desc); \
|
| + } while (0)
|
| +
|
| +#define UNIMPLEMENTED() \
|
| + do { \
|
| + SSL_TRC(3, ("%d: TLS13[%d]: unimplemented feature in %s (%s:%d)", \
|
| + SSL_GETPID(), ss->fd, __func__, __FILE__, __LINE__)); \
|
| + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); \
|
| + PORT_Assert(0); \
|
| + return SECFailure; \
|
| + } while (0)
|
| +
|
| +void
|
| +tls13_FatalError(sslSocket *ss, PRErrorCode prError, SSL3AlertDescription desc)
|
| +{
|
| + PORT_Assert(desc != internal_error); /* These should never happen */
|
| + (void)SSL3_SendAlert(ss, alert_fatal, desc);
|
| + PORT_SetError(prError);
|
| +}
|
| +
|
| +#ifdef TRACE
|
| +#define STATE_CASE(a) \
|
| + case a: \
|
| + return #a
|
| +static char *
|
| +tls13_HandshakeState(SSL3WaitState st)
|
| +{
|
| + switch (st) {
|
| + STATE_CASE(wait_client_hello);
|
| + STATE_CASE(wait_client_cert);
|
| + STATE_CASE(wait_cert_verify);
|
| + STATE_CASE(wait_finished);
|
| + STATE_CASE(wait_server_hello);
|
| + STATE_CASE(wait_certificate_status);
|
| + STATE_CASE(wait_server_cert);
|
| + STATE_CASE(wait_cert_request);
|
| + STATE_CASE(wait_encrypted_extensions);
|
| + STATE_CASE(idle_handshake);
|
| + default:
|
| + break;
|
| + }
|
| + PORT_Assert(0);
|
| + return "unknown";
|
| +}
|
| +#endif
|
| +
|
| +#define TLS13_WAIT_STATE_MASK 0x80
|
| +
|
| +#define TLS13_BASE_WAIT_STATE(ws) (ws & ~TLS13_WAIT_STATE_MASK)
|
| +/* We don't mask idle_handshake because other parts of the code use it*/
|
| +#define TLS13_WAIT_STATE(ws) (ws == idle_handshake ? ws : ws | TLS13_WAIT_STATE_MASK)
|
| +#define TLS13_CHECK_HS_STATE(ss, err, ...) \
|
| + tls13_CheckHsState(ss, err, #err, __func__, __FILE__, __LINE__, \
|
| + __VA_ARGS__, \
|
| + wait_invalid)
|
| +void
|
| +tls13_SetHsState(sslSocket *ss, SSL3WaitState ws,
|
| + const char *func, const char *file, int line)
|
| +{
|
| +#ifdef TRACE
|
| + const char *new_state_name =
|
| + tls13_HandshakeState(ws);
|
| +
|
| + SSL_TRC(3, ("%d: TLS13[%d]: state change from %s->%s in %s (%s:%d)",
|
| + SSL_GETPID(), ss->fd,
|
| + tls13_HandshakeState(TLS13_BASE_WAIT_STATE(ss->ssl3.hs.ws)),
|
| + new_state_name,
|
| + func, file, line));
|
| +#endif
|
| +
|
| + ss->ssl3.hs.ws = TLS13_WAIT_STATE(ws);
|
| +}
|
| +
|
| +static PRBool
|
| +tls13_InHsStateV(sslSocket *ss, va_list ap)
|
| +{
|
| + SSL3WaitState ws;
|
| +
|
| + while ((ws = va_arg(ap, SSL3WaitState)) != wait_invalid) {
|
| + if (ws == TLS13_BASE_WAIT_STATE(ss->ssl3.hs.ws)) {
|
| + return PR_TRUE;
|
| + }
|
| + }
|
| + return PR_FALSE;
|
| +}
|
| +
|
| +PRBool
|
| +tls13_InHsState(sslSocket *ss, ...)
|
| +{
|
| + PRBool found;
|
| + va_list ap;
|
| +
|
| + va_start(ap, ss);
|
| + found = tls13_InHsStateV(ss, ap);
|
| + va_end(ap);
|
| +
|
| + return found;
|
| +}
|
| +
|
| +static SECStatus
|
| +tls13_CheckHsState(sslSocket *ss, int err, const char *error_name,
|
| + const char *func, const char *file, int line,
|
| + ...)
|
| +{
|
| + va_list ap;
|
| + va_start(ap, line);
|
| + if (tls13_InHsStateV(ss, ap)) {
|
| + va_end(ap);
|
| + return SECSuccess;
|
| + }
|
| + va_end(ap);
|
| +
|
| + SSL_TRC(3, ("%d: TLS13[%d]: error %s state is (%s) at %s (%s:%d)",
|
| + SSL_GETPID(), ss->fd,
|
| + error_name,
|
| + tls13_HandshakeState(TLS13_BASE_WAIT_STATE(ss->ssl3.hs.ws)),
|
| + func, file, line));
|
| + tls13_FatalError(ss, err, unexpected_message);
|
| + return SECFailure;
|
| +}
|
| +
|
| +SSLHashType
|
| +tls13_GetHash(sslSocket *ss)
|
| +{
|
| + /* TODO(ekr@rtfm.com): This needs to actually be looked up. */
|
| + return ssl_hash_sha256;
|
| +}
|
| +
|
| +CK_MECHANISM_TYPE
|
| +tls13_GetHkdfMechanism(sslSocket *ss)
|
| +{
|
| + /* TODO(ekr@rtfm.com): This needs to actually be looked up. */
|
| + return CKM_NSS_HKDF_SHA256;
|
| +}
|
| +
|
| +static CK_MECHANISM_TYPE
|
| +tls13_GetHmacMechanism(sslSocket *ss)
|
| +{
|
| + /* TODO(ekr@rtfm.com): This needs to actually be looked up. */
|
| + return CKM_SHA256_HMAC;
|
| +}
|
| +
|
| +/*
|
| + * Called from ssl3_SendClientHello
|
| + */
|
| +SECStatus
|
| +tls13_SetupClientHello(sslSocket *ss)
|
| +{
|
| + SECStatus rv;
|
| + /* TODO(ekr@rtfm.com): Handle multiple curves here. */
|
| + ECName curves_to_try[] = { ec_secp256r1 };
|
| +
|
| + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
|
| + PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss));
|
| +
|
| + PORT_Assert(!ss->ephemeralECDHKeyPair);
|
| +
|
| + rv = ssl3_CreateECDHEphemeralKeyPair(curves_to_try[0],
|
| + &ss->ephemeralECDHKeyPair);
|
| + if (rv != SECSuccess)
|
| + return rv;
|
| +
|
| + return SECSuccess;
|
| +}
|
| +
|
| +static SECStatus
|
| +tls13_HandleECDHEKeyShare(sslSocket *ss,
|
| + TLS13KeyShareEntry *entry,
|
| + SECKEYPrivateKey *privKey,
|
| + SharedSecretType type)
|
| +{
|
| + SECStatus rv;
|
| + SECKEYPublicKey *peerKey;
|
| + PK11SymKey *shared;
|
| +
|
| + peerKey = tls13_ImportECDHKeyShare(ss, entry->key_exchange.data,
|
| + entry->key_exchange.len,
|
| + entry->group);
|
| + if (!peerKey)
|
| + return SECFailure; /* Error code set already. */
|
| +
|
| + /* Compute shared key. */
|
| + shared = tls13_ComputeECDHSharedKey(ss, privKey, peerKey);
|
| + SECKEY_DestroyPublicKey(peerKey);
|
| + if (!shared) {
|
| + return SECFailure; /* Error code set already. */
|
| + }
|
| +
|
| + /* Extract key. */
|
| + rv = tls13_HkdfExtractSharedKey(ss, shared, type);
|
| + PK11_FreeSymKey(shared);
|
| +
|
| + return rv;
|
| +}
|
| +
|
| +SECStatus
|
| +tls13_HandlePostHelloHandshakeMessage(sslSocket *ss, SSL3Opaque *b,
|
| + PRUint32 length, SSL3Hashes *hashesPtr)
|
| +{
|
| + /* TODO(ekr@rtfm.com): Would it be better to check all the states here? */
|
| + switch (ss->ssl3.hs.msg_type) {
|
| + case certificate:
|
| + return tls13_HandleCertificate(ss, b, length);
|
| +
|
| + case certificate_status:
|
| + return tls13_HandleCertificateStatus(ss, b, length);
|
| +
|
| + case certificate_request:
|
| + return tls13_HandleCertificateRequest(ss, b, length);
|
| +
|
| + case certificate_verify:
|
| + return tls13_HandleCertificateVerify(ss, b, length, hashesPtr);
|
| +
|
| + case encrypted_extensions:
|
| + return tls13_HandleEncryptedExtensions(ss, b, length);
|
| +
|
| + case new_session_ticket:
|
| + return tls13_HandleNewSessionTicket(ss, b, length);
|
| +
|
| + case finished:
|
| + return tls13_HandleFinished(ss, b, length, hashesPtr);
|
| +
|
| + default:
|
| + FATAL_ERROR(ss, SSL_ERROR_RX_UNKNOWN_HANDSHAKE, unexpected_message);
|
| + return SECFailure;
|
| + }
|
| +
|
| + PORT_Assert(0); /* Unreached */
|
| + return SECFailure;
|
| +}
|
| +
|
| +/* Called from ssl3_HandleClientHello.
|
| + *
|
| + * Caller must hold Handshake and RecvBuf locks.
|
| + */
|
| +SECStatus
|
| +tls13_HandleClientKeyShare(sslSocket *ss)
|
| +{
|
| + ECName expectedGroup;
|
| + SECStatus rv;
|
| + TLS13KeyShareEntry *found = NULL;
|
| + PRCList *cur_p;
|
| +
|
| + SSL_TRC(3, ("%d: TLS13[%d]: handle client_key_share handshake",
|
| + SSL_GETPID(), ss->fd));
|
| +
|
| + PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
|
| + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
|
| +
|
| + rv = ssl3_SetupPendingCipherSpec(ss);
|
| + if (rv != SECSuccess)
|
| + return SECFailure; /* Error code set below */
|
| +
|
| + /* Figure out what group we expect */
|
| + switch (ss->ssl3.hs.kea_def->exchKeyType) {
|
| +#ifndef NSS_DISABLE_ECC
|
| + case ssl_kea_ecdh:
|
| + expectedGroup = ssl3_GetCurveNameForServerSocket(ss);
|
| + if (!expectedGroup) {
|
| + FATAL_ERROR(ss, SSL_ERROR_NO_CYPHER_OVERLAP,
|
| + handshake_failure);
|
| + return SECFailure;
|
| + }
|
| + break;
|
| +#endif
|
| + default:
|
| + /* Got an unknown or unsupported Key Exchange Algorithm.
|
| + * Can't happen. */
|
| + FATAL_ERROR(ss, SEC_ERROR_UNSUPPORTED_KEYALG,
|
| + internal_error);
|
| + return SECFailure;
|
| + }
|
| +
|
| + /* Now walk through the keys until we find one for our group */
|
| + cur_p = PR_NEXT_LINK(&ss->ssl3.hs.remoteKeyShares);
|
| + while (cur_p != &ss->ssl3.hs.remoteKeyShares) {
|
| + TLS13KeyShareEntry *offer = (TLS13KeyShareEntry *)cur_p;
|
| +
|
| + if (offer->group == expectedGroup) {
|
| + found = offer;
|
| + break;
|
| + }
|
| + cur_p = PR_NEXT_LINK(cur_p);
|
| + }
|
| +
|
| + if (!found) {
|
| + /* No acceptable group. In future, we will need to correct the client.
|
| + * Currently just generate an error.
|
| + * TODO(ekr@rtfm.com): Write code to correct client.
|
| + */
|
| + FATAL_ERROR(ss, SSL_ERROR_NO_CYPHER_OVERLAP, handshake_failure);
|
| + return SECFailure;
|
| + }
|
| +
|
| + /* Generate our key */
|
| + rv = ssl3_CreateECDHEphemeralKeyPair(expectedGroup, &ss->ephemeralECDHKeyPair);
|
| + if (rv != SECSuccess)
|
| + return rv;
|
| +
|
| + ss->sec.keaType = ss->ssl3.hs.kea_def->exchKeyType;
|
| + ss->sec.keaKeyBits = SECKEY_PublicKeyStrengthInBits(
|
| + ss->ephemeralECDHKeyPair->pubKey);
|
| +
|
| + /* Register the sender */
|
| + rv = ssl3_RegisterServerHelloExtensionSender(ss, ssl_tls13_key_share_xtn,
|
| + tls13_ServerSendKeyShareXtn);
|
| + if (rv != SECSuccess)
|
| + return SECFailure; /* Error code set below */
|
| +
|
| + rv = tls13_HandleECDHEKeyShare(ss, found,
|
| + ss->ephemeralECDHKeyPair->privKey,
|
| + EphemeralSharedSecret);
|
| + if (rv != SECSuccess)
|
| + return SECFailure; /* Error code set below */
|
| +
|
| + return SECSuccess;
|
| +}
|
| +
|
| +/*
|
| + * [draft-ietf-tls-tls13-11] Section 6.3.3.2
|
| + *
|
| + * opaque DistinguishedName<1..2^16-1>;
|
| + *
|
| + * struct {
|
| + * opaque certificate_extension_oid<1..2^8-1>;
|
| + * opaque certificate_extension_values<0..2^16-1>;
|
| + * } CertificateExtension;
|
| + *
|
| + * struct {
|
| + * opaque certificate_request_context<0..2^8-1>;
|
| + * SignatureAndHashAlgorithm
|
| + * supported_signature_algorithms<2..2^16-2>;
|
| + * DistinguishedName certificate_authorities<0..2^16-1>;
|
| + * CertificateExtension certificate_extensions<0..2^16-1>;
|
| + * } CertificateRequest;
|
| + */
|
| +static SECStatus
|
| +tls13_SendCertificateRequest(sslSocket *ss)
|
| +{
|
| + SECStatus rv;
|
| + int calen;
|
| + SECItem *names;
|
| + int nnames;
|
| + SECItem *name;
|
| + int i;
|
| + PRUint8 sigAlgs[MAX_SIGNATURE_ALGORITHMS * 2];
|
| + unsigned int sigAlgsLength = 0;
|
| + int length;
|
| +
|
| + SSL_TRC(3, ("%d: TLS13[%d]: begin send certificate_request",
|
| + SSL_GETPID(), ss->fd));
|
| +
|
| + /* Fixed context value. */
|
| + ss->ssl3.hs.certReqContext[0] = 0;
|
| + ss->ssl3.hs.certReqContextLen = 1;
|
| +
|
| + rv = ssl3_EncodeCertificateRequestSigAlgs(ss, sigAlgs, sizeof(sigAlgs),
|
| + &sigAlgsLength);
|
| + if (rv != SECSuccess) {
|
| + return rv;
|
| + }
|
| +
|
| + ssl3_GetCertificateRequestCAs(ss, &calen, &names, &nnames);
|
| + length = 1 + ss->ssl3.hs.certReqContextLen +
|
| + 2 + sigAlgsLength + 2 + calen + 2;
|
| +
|
| + rv = ssl3_AppendHandshakeHeader(ss, certificate_request, length);
|
| + if (rv != SECSuccess) {
|
| + return rv; /* err set by AppendHandshake. */
|
| + }
|
| + rv = ssl3_AppendHandshakeVariable(ss, ss->ssl3.hs.certReqContext,
|
| + ss->ssl3.hs.certReqContextLen, 1);
|
| + if (rv != SECSuccess) {
|
| + return rv; /* err set by AppendHandshake. */
|
| + }
|
| + rv = ssl3_AppendHandshakeVariable(ss, sigAlgs, sigAlgsLength, 2);
|
| + if (rv != SECSuccess) {
|
| + return rv; /* err set by AppendHandshake. */
|
| + }
|
| + rv = ssl3_AppendHandshakeNumber(ss, calen, 2);
|
| + if (rv != SECSuccess) {
|
| + return rv; /* err set by AppendHandshake. */
|
| + }
|
| + for (i = 0, name = names; i < nnames; i++, name++) {
|
| + rv = ssl3_AppendHandshakeVariable(ss, name->data, name->len, 2);
|
| + if (rv != SECSuccess) {
|
| + return rv; /* err set by AppendHandshake. */
|
| + }
|
| + }
|
| + rv = ssl3_AppendHandshakeNumber(ss, 0, 2);
|
| + if (rv != SECSuccess) {
|
| + return rv; /* err set by AppendHandshake. */
|
| + }
|
| +
|
| + return SECSuccess;
|
| +}
|
| +
|
| +static SECStatus
|
| +tls13_HandleCertificateRequest(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
|
| +{
|
| + SECStatus rv;
|
| + SECItem context = { siBuffer, NULL, 0 };
|
| + SECItem algorithms = { siBuffer, NULL, 0 };
|
| + PLArenaPool *arena;
|
| + CERTDistNames ca_list;
|
| + PRInt32 extensionsLength;
|
| +
|
| + SSL_TRC(3, ("%d: TLS13[%d]: handle certificate_request sequence",
|
| + SSL_GETPID(), ss->fd));
|
| +
|
| + PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
|
| + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
|
| +
|
| + /* Client */
|
| + rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_CERT_REQUEST, wait_cert_request);
|
| + if (rv != SECSuccess) {
|
| + return SECFailure;
|
| + }
|
| +
|
| + PORT_Assert(ss->ssl3.clientCertChain == NULL);
|
| + PORT_Assert(ss->ssl3.clientCertificate == NULL);
|
| + PORT_Assert(ss->ssl3.clientPrivateKey == NULL);
|
| +
|
| + rv = ssl3_ConsumeHandshakeVariable(ss, &context, 1, &b, &length);
|
| + if (rv != SECSuccess)
|
| + return SECFailure;
|
| + PORT_Assert(sizeof(ss->ssl3.hs.certReqContext) == 255);
|
| + PORT_Memcpy(ss->ssl3.hs.certReqContext, context.data, context.len);
|
| + ss->ssl3.hs.certReqContextLen = context.len;
|
| +
|
| + rv = ssl3_ConsumeHandshakeVariable(ss, &algorithms, 2, &b, &length);
|
| + if (rv != SECSuccess)
|
| + return SECFailure;
|
| +
|
| + if (algorithms.len == 0 || (algorithms.len & 1) != 0) {
|
| + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CERT_REQUEST,
|
| + illegal_parameter);
|
| + return SECFailure;
|
| + }
|
| +
|
| + arena = ca_list.arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
| + if (!arena) {
|
| + FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
|
| + return SECFailure;
|
| + }
|
| +
|
| + rv = ssl3_ParseCertificateRequestCAs(ss, &b, &length, arena, &ca_list);
|
| + if (rv != SECSuccess)
|
| + goto loser; /* alert sent below */
|
| +
|
| + /* Verify that the extensions length is correct. */
|
| + extensionsLength = ssl3_ConsumeHandshakeNumber(ss, 2, &b, &length);
|
| + if (extensionsLength < 0) {
|
| + goto loser; /* alert sent below */
|
| + }
|
| + if (extensionsLength != length) {
|
| + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CERT_REQUEST,
|
| + illegal_parameter);
|
| + goto loser;
|
| + }
|
| +
|
| + TLS13_SET_HS_STATE(ss, wait_server_cert);
|
| +
|
| + rv = ssl3_CompleteHandleCertificateRequest(ss, &algorithms, &ca_list);
|
| + if (rv != SECSuccess) {
|
| + FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
|
| + goto loser;
|
| + }
|
| +
|
| + return SECSuccess;
|
| +
|
| +loser:
|
| + PORT_FreeArena(arena, PR_FALSE);
|
| + return SECFailure;
|
| +}
|
| +
|
| +static SECStatus
|
| +tls13_InitializeHandshakeEncryption(sslSocket *ss)
|
| +{
|
| + SECStatus rv;
|
| +
|
| + /* For all present cipher suites, SS = ES.
|
| + * TODO(ekr@rtfm.com): Revisit for 0-RTT. */
|
| + ss->ssl3.hs.xSS = PK11_ReferenceSymKey(ss->ssl3.hs.xES);
|
| + if (!ss->ssl3.hs.xSS) {
|
| + FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
|
| + return SECFailure;
|
| + }
|
| +
|
| + rv = tls13_InitCipherSpec(ss, TrafficKeyHandshake,
|
| + InstallCipherSpecBoth);
|
| + if (rv != SECSuccess) {
|
| + FATAL_ERROR(ss, SSL_ERROR_INIT_CIPHER_SUITE_FAILURE, internal_error);
|
| + return SECFailure;
|
| + }
|
| +
|
| + return rv;
|
| +}
|
| +
|
| +/* Called from: ssl3_HandleClientHello */
|
| +SECStatus
|
| +tls13_SendServerHelloSequence(sslSocket *ss)
|
| +{
|
| + SECStatus rv;
|
| + SSL3KEAType certIndex;
|
| +
|
| + SSL_TRC(3, ("%d: TLS13[%d]: begin send server_hello sequence",
|
| + SSL_GETPID(), ss->fd));
|
| +
|
| + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
|
| + PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss));
|
| +
|
| + rv = ssl3_SendServerHello(ss);
|
| + if (rv != SECSuccess) {
|
| + return rv; /* err code is set. */
|
| + }
|
| +
|
| + rv = tls13_InitializeHandshakeEncryption(ss);
|
| + if (rv != SECSuccess) {
|
| + return SECFailure; /* error code is set. */
|
| + }
|
| +
|
| + rv = tls13_SendEncryptedExtensions(ss);
|
| + if (rv != SECSuccess) {
|
| + return SECFailure; /* error code is set. */
|
| + }
|
| +
|
| + if (ss->opt.requestCertificate) {
|
| + rv = tls13_SendCertificateRequest(ss);
|
| + if (rv != SECSuccess) {
|
| + return SECFailure; /* error code is set. */
|
| + }
|
| + }
|
| + rv = ssl3_SendCertificate(ss);
|
| + if (rv != SECSuccess) {
|
| + return SECFailure; /* error code is set. */
|
| + }
|
| + rv = ssl3_SendCertificateStatus(ss);
|
| + if (rv != SECSuccess) {
|
| + return SECFailure; /* error code is set. */
|
| + }
|
| +
|
| + /* This was copied from: ssl3_SendCertificate.
|
| + * TODO(ekr@rtfm.com): Verify that this selection logic is correct.
|
| + * Bug 1237514.
|
| + */
|
| + if ((ss->ssl3.hs.kea_def->kea == kea_ecdhe_rsa) ||
|
| + (ss->ssl3.hs.kea_def->kea == kea_dhe_rsa)) {
|
| + certIndex = kt_rsa;
|
| + } else {
|
| + certIndex = ss->ssl3.hs.kea_def->exchKeyType;
|
| + }
|
| + rv = ssl3_SendCertificateVerify(ss, ss->serverCerts[certIndex].SERVERKEY);
|
| + if (rv != SECSuccess) {
|
| + return rv; /* err code is set. */
|
| + }
|
| +
|
| + /* Compute the rest of the secrets except for the resumption
|
| + * and exporter secret. */
|
| + rv = tls13_ComputeSecrets1(ss);
|
| + if (rv != SECSuccess) {
|
| + FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
|
| + return SECFailure;
|
| + }
|
| +
|
| + rv = tls13_SendFinished(ss);
|
| + if (rv != SECSuccess) {
|
| + return rv; /* error code is set. */
|
| + }
|
| +
|
| + TLS13_SET_HS_STATE(ss, ss->opt.requestCertificate ? wait_client_cert
|
| + : wait_finished);
|
| +
|
| + return SECSuccess;
|
| +}
|
| +
|
| +/*
|
| + * Called from ssl3_HandleServerHello.
|
| + *
|
| + * Caller must hold Handshake and RecvBuf locks.
|
| + */
|
| +SECStatus
|
| +tls13_HandleServerKeyShare(sslSocket *ss)
|
| +{
|
| + SECStatus rv;
|
| + ECName expectedGroup;
|
| + PRCList *cur_p;
|
| + TLS13KeyShareEntry *entry;
|
| +
|
| + SSL_TRC(3, ("%d: TLS13[%d]: handle server_key_share handshake",
|
| + SSL_GETPID(), ss->fd));
|
| + PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
|
| + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
|
| +
|
| + switch (ss->ssl3.hs.kea_def->exchKeyType) {
|
| +#ifndef NSS_DISABLE_ECC
|
| + case ssl_kea_ecdh:
|
| + expectedGroup = ssl3_PubKey2ECName(ss->ephemeralECDHKeyPair->pubKey);
|
| + break;
|
| +#endif /* NSS_DISABLE_ECC */
|
| + default:
|
| + FATAL_ERROR(ss, SEC_ERROR_UNSUPPORTED_KEYALG, handshake_failure);
|
| + return SECFailure;
|
| + }
|
| +
|
| + /* This list should have one entry. */
|
| + cur_p = PR_NEXT_LINK(&ss->ssl3.hs.remoteKeyShares);
|
| + if (!cur_p) {
|
| + FATAL_ERROR(ss, SSL_ERROR_MISSING_KEY_SHARE, missing_extension);
|
| + return SECFailure;
|
| + }
|
| + PORT_Assert(PR_NEXT_LINK(cur_p) == &ss->ssl3.hs.remoteKeyShares);
|
| +
|
| + entry = (TLS13KeyShareEntry *)cur_p;
|
| + if (entry->group != expectedGroup) {
|
| + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_KEY_SHARE, illegal_parameter);
|
| + return SECFailure;
|
| + }
|
| +
|
| + rv = tls13_HandleECDHEKeyShare(ss, entry,
|
| + ss->ephemeralECDHKeyPair->privKey,
|
| + EphemeralSharedSecret);
|
| +
|
| + ss->sec.keaType = ss->ssl3.hs.kea_def->exchKeyType;
|
| + ss->sec.keaKeyBits = SECKEY_PublicKeyStrengthInBits(
|
| + ss->ephemeralECDHKeyPair->pubKey);
|
| +
|
| + if (rv != SECSuccess)
|
| + return SECFailure; /* Error code set below */
|
| +
|
| + return tls13_InitializeHandshakeEncryption(ss);
|
| +}
|
| +
|
| +/* Called from tls13_CompleteHandleHandshakeMessage() when it has deciphered a complete
|
| + * tls13 Certificate message.
|
| + * Caller must hold Handshake and RecvBuf locks.
|
| + */
|
| +static SECStatus
|
| +tls13_HandleCertificate(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
|
| +{
|
| + SECStatus rv;
|
| + SECItem context = { siBuffer, NULL, 0 };
|
| +
|
| + SSL_TRC(3, ("%d: TLS13[%d]: handle certificate handshake",
|
| + SSL_GETPID(), ss->fd));
|
| + PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
|
| + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
|
| +
|
| + if (ss->sec.isServer) {
|
| + rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_CERTIFICATE,
|
| + wait_client_cert);
|
| + } else {
|
| + rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_CERTIFICATE,
|
| + wait_cert_request, wait_server_cert);
|
| + }
|
| + if (rv != SECSuccess)
|
| + return SECFailure;
|
| +
|
| + /* Process the context string */
|
| + rv = ssl3_ConsumeHandshakeVariable(ss, &context, 1, &b, &length);
|
| + if (rv != SECSuccess)
|
| + return SECFailure;
|
| + if (!ss->sec.isServer) {
|
| + if (context.len) {
|
| + /* The server's context string MUST be empty */
|
| + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CERTIFICATE,
|
| + illegal_parameter);
|
| + return SECFailure;
|
| + }
|
| + } else {
|
| + if (!context.len || context.len != ss->ssl3.hs.certReqContextLen ||
|
| + (NSS_SecureMemcmp(ss->ssl3.hs.certReqContext,
|
| + context.data, context.len) != 0)) {
|
| + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CERTIFICATE,
|
| + illegal_parameter);
|
| + return SECFailure;
|
| + }
|
| + context.len = 0; /* Belt and suspenders. Zero out the context. */
|
| + }
|
| +
|
| + return ssl3_CompleteHandleCertificate(ss, b, length);
|
| +}
|
| +
|
| +/* Called from tls13_CompleteHandleHandshakeMessage() when it has deciphered a complete
|
| + * ssl3 CertificateStatus message.
|
| + * Caller must hold Handshake and RecvBuf locks.
|
| + */
|
| +static SECStatus
|
| +tls13_HandleCertificateStatus(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
|
| +{
|
| + SECStatus rv;
|
| +
|
| + rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_CERT_STATUS,
|
| + wait_certificate_status);
|
| + if (rv != SECSuccess)
|
| + return rv;
|
| +
|
| + return ssl3_CompleteHandleCertificateStatus(ss, b, length);
|
| +}
|
| +
|
| +/*
|
| + * TODO(ekr@rtfm.com): This install logic needs renaming since it's
|
| + * what happens at various stages of cipher spec setup. Legacy from ssl3con.c.
|
| + */
|
| +int
|
| +tls13_InstallCipherSpec(sslSocket *ss, InstallCipherSpecDirection direction)
|
| +{
|
| + SSL_TRC(3, ("%d: TLS13[%d]: Installing new cipher specs direction = %s",
|
| + SSL_GETPID(), ss->fd,
|
| + direction == InstallCipherSpecRead ? "read" : "write"));
|
| +
|
| + PORT_Assert(!IS_DTLS(ss)); /* TODO(ekr@rtfm.com): Update for DTLS */
|
| + /* TODO(ekr@rtfm.com): Holddown timer for DTLS. */
|
| + ssl_GetSpecWriteLock(ss); /**************************************/
|
| +
|
| + /* Flush out any old stuff in the handshake buffers */
|
| + switch (direction) {
|
| + case InstallCipherSpecWrite: {
|
| + ssl3CipherSpec *pwSpec;
|
| + pwSpec = ss->ssl3.pwSpec;
|
| +
|
| + ss->ssl3.pwSpec = ss->ssl3.cwSpec;
|
| + ss->ssl3.cwSpec = pwSpec;
|
| + break;
|
| + } break;
|
| + case InstallCipherSpecRead: {
|
| + ssl3CipherSpec *prSpec;
|
| +
|
| + prSpec = ss->ssl3.prSpec;
|
| + ss->ssl3.prSpec = ss->ssl3.crSpec;
|
| + ss->ssl3.crSpec = prSpec;
|
| + } break;
|
| + default:
|
| + PORT_Assert(0);
|
| + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
|
| + ssl_ReleaseSpecWriteLock(ss); /**************************************/
|
| + return SECFailure;
|
| + }
|
| +
|
| + /* If we are really through with the old cipher prSpec
|
| + * (Both the read and write sides have changed) destroy it.
|
| + */
|
| + if (ss->ssl3.prSpec == ss->ssl3.pwSpec) {
|
| + ssl3_DestroyCipherSpec(ss->ssl3.prSpec, PR_FALSE /*freeSrvName*/);
|
| + }
|
| + ssl_ReleaseSpecWriteLock(ss); /**************************************/
|
| +
|
| + return SECSuccess;
|
| +}
|
| +
|
| +/* Add context to the hash functions as described in
|
| + [draft-ietf-tls-tls13; Section 4.9.1] */
|
| +SECStatus
|
| +tls13_AddContextToHashes(sslSocket *ss, SSL3Hashes *hashes /* IN/OUT */,
|
| + SSLHashType algorithm, PRBool sending)
|
| +{
|
| + SECStatus rv = SECSuccess;
|
| + PK11Context *ctx;
|
| + const unsigned char context_padding[] = {
|
| + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
| + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
| + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
| + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
| + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
| + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
| + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
| + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
| + };
|
| + const char *client_cert_verify_string = "TLS 1.3, client CertificateVerify";
|
| + const char *server_cert_verify_string = "TLS 1.3, server CertificateVerify";
|
| + const char *context_string = (sending ^ ss->sec.isServer) ? client_cert_verify_string
|
| + : server_cert_verify_string;
|
| + unsigned int hashlength;
|
| +
|
| + /* Double check that we are doing SHA-256 for the handshake hash.*/
|
| + PORT_Assert(hashes->hashAlg == ssl_hash_sha256);
|
| + if (hashes->hashAlg != ssl_hash_sha256) {
|
| + PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
| + goto loser;
|
| + }
|
| + PORT_Assert(hashes->len == 32);
|
| +
|
| + ctx = PK11_CreateDigestContext(ssl3_TLSHashAlgorithmToOID(algorithm));
|
| + if (!ctx) {
|
| + PORT_SetError(SEC_ERROR_NO_MEMORY);
|
| + goto loser;
|
| + }
|
| +
|
| + PORT_Assert(SECFailure);
|
| + PORT_Assert(!SECSuccess);
|
| +
|
| + rv |= PK11_DigestBegin(ctx);
|
| + rv |= PK11_DigestOp(ctx, context_padding, sizeof(context_padding));
|
| + rv |= PK11_DigestOp(ctx, (unsigned char *)context_string,
|
| + strlen(context_string) + 1); /* +1 includes the terminating 0 */
|
| + rv |= PK11_DigestOp(ctx, hashes->u.raw, hashes->len);
|
| + /* Update the hash in-place */
|
| + rv |= PK11_DigestFinal(ctx, hashes->u.raw, &hashlength, sizeof(hashes->u.raw));
|
| + PK11_DestroyContext(ctx, PR_TRUE);
|
| + PRINT_BUF(90, (NULL, "TLS 1.3 hash with context", hashes->u.raw, hashlength));
|
| +
|
| + hashes->len = hashlength;
|
| + hashes->hashAlg = algorithm;
|
| +
|
| + if (rv) {
|
| + ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE);
|
| + goto loser;
|
| + }
|
| + return SECSuccess;
|
| +
|
| +loser:
|
| + return SECFailure;
|
| +}
|
| +
|
| +static SECStatus
|
| +tls13_HkdfExtractSharedKey(sslSocket *ss, PK11SymKey *key,
|
| + SharedSecretType keyType)
|
| +{
|
| + PK11SymKey **destp;
|
| +
|
| + switch (keyType) {
|
| + case EphemeralSharedSecret:
|
| + destp = &ss->ssl3.hs.xES;
|
| + break;
|
| + case StaticSharedSecret:
|
| + destp = &ss->ssl3.hs.xSS;
|
| + break;
|
| + default:
|
| + PORT_Assert(0);
|
| + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
|
| + return SECFailure;
|
| + }
|
| +
|
| + PORT_Assert(!*destp);
|
| + return tls13_HkdfExtract(NULL, key, tls13_GetHash(ss), destp);
|
| +}
|
| +
|
| +static SECStatus
|
| +tls13_DeriveTrafficKeys(sslSocket *ss, ssl3CipherSpec *pwSpec,
|
| + TrafficKeyType type)
|
| +{
|
| + size_t keySize = pwSpec->cipher_def->key_size;
|
| + size_t ivSize = pwSpec->cipher_def->iv_size +
|
| + pwSpec->cipher_def->explicit_nonce_size; /* This isn't always going to
|
| + * work, but it does for
|
| + * AES-GCM */
|
| + CK_MECHANISM_TYPE bulkAlgorithm = ssl3_Alg2Mech(pwSpec->cipher_def->calg);
|
| + SSL3Hashes hashes;
|
| + PK11SymKey *prk = NULL;
|
| + const char *phase;
|
| + char label[256]; /* Arbitrary buffer large enough to hold the label */
|
| + SECStatus rv;
|
| +
|
| +#define FORMAT_LABEL(phase_, purpose_) \
|
| + do { \
|
| + PRUint32 n = PR_snprintf(label, sizeof(label), "%s, %s", phase_, purpose_); \
|
| + /* Check for getting close. */ \
|
| + if ((n + 1) >= sizeof(label)) { \
|
| + PORT_Assert(0); \
|
| + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); \
|
| + goto loser; \
|
| + } \
|
| + } while (0)
|
| +#define EXPAND_TRAFFIC_KEY(purpose_, target_) \
|
| + do { \
|
| + FORMAT_LABEL(phase, purpose_); \
|
| + rv = tls13_HkdfExpandLabel(prk, tls13_GetHash(ss), \
|
| + hashes.u.raw, hashes.len, \
|
| + label, strlen(label), \
|
| + bulkAlgorithm, keySize, &pwSpec->target_); \
|
| + if (rv != SECSuccess) { \
|
| + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); \
|
| + PORT_Assert(0); \
|
| + goto loser; \
|
| + } \
|
| + } while (0)
|
| +
|
| +#define EXPAND_TRAFFIC_IV(purpose_, target_) \
|
| + do { \
|
| + FORMAT_LABEL(phase, purpose_); \
|
| + rv = tls13_HkdfExpandLabelRaw(prk, tls13_GetHash(ss), \
|
| + hashes.u.raw, hashes.len, \
|
| + label, strlen(label), \
|
| + pwSpec->target_, ivSize); \
|
| + if (rv != SECSuccess) { \
|
| + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); \
|
| + PORT_Assert(0); \
|
| + goto loser; \
|
| + } \
|
| + } while (0)
|
| +
|
| + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
|
| + PORT_Assert(ss->opt.noLocks || ssl_HaveSpecWriteLock(ss));
|
| + PORT_Assert(ss->ssl3.prSpec == ss->ssl3.pwSpec);
|
| +
|
| + rv = ssl3_ComputeHandshakeHashes(ss, pwSpec, &hashes, 0);
|
| + if (rv != SECSuccess) {
|
| + PORT_Assert(0); /* Should never fail */
|
| + ssl_MapLowLevelError(SSL_ERROR_SESSION_KEY_GEN_FAILURE);
|
| + return SECFailure;
|
| + }
|
| + PRINT_BUF(60, (ss, "Deriving traffic keys. Session hash=", hashes.u.raw,
|
| + hashes.len));
|
| +
|
| + switch (type) {
|
| + case TrafficKeyHandshake:
|
| + phase = kHkdfPhaseHandshakeKeys;
|
| + prk = ss->ssl3.hs.xES;
|
| + break;
|
| + case TrafficKeyApplicationData:
|
| + phase = kHkdfPhaseApplicationDataKeys;
|
| + prk = ss->ssl3.hs.trafficSecret;
|
| + break;
|
| + default:
|
| + PORT_Assert(0);
|
| + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
|
| + return SECFailure;
|
| + }
|
| + PORT_Assert(prk != NULL);
|
| +
|
| + SSL_TRC(3, ("%d: TLS13[%d]: deriving traffic keys phase='%s'",
|
| + SSL_GETPID(), ss->fd, phase));
|
| +
|
| + EXPAND_TRAFFIC_KEY(kHkdfPurposeClientWriteKey, client.write_key);
|
| + EXPAND_TRAFFIC_KEY(kHkdfPurposeServerWriteKey, server.write_key);
|
| + EXPAND_TRAFFIC_IV(kHkdfPurposeClientWriteIv, client.write_iv);
|
| + EXPAND_TRAFFIC_IV(kHkdfPurposeServerWriteIv, server.write_iv);
|
| +
|
| + return SECSuccess;
|
| +
|
| +loser:
|
| + return SECFailure;
|
| +}
|
| +
|
| +/* Set up a cipher spec with keys. If install is nonzero, then also install
|
| + * it as the current cipher spec for each value in the mask. */
|
| +SECStatus
|
| +tls13_InitCipherSpec(sslSocket *ss, TrafficKeyType type, InstallCipherSpecDirection install)
|
| +{
|
| + ssl3CipherSpec *pwSpec;
|
| + ssl3CipherSpec *cwSpec;
|
| + SECStatus rv;
|
| + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
|
| +
|
| + if (install == InstallCipherSpecWrite ||
|
| + install == InstallCipherSpecBoth) {
|
| + ssl_GetXmitBufLock(ss);
|
| +
|
| + rv = ssl3_FlushHandshake(ss, ssl_SEND_FLAG_FORCE_INTO_BUFFER);
|
| + ssl_ReleaseXmitBufLock(ss);
|
| + if (rv != SECSuccess) {
|
| + goto loser;
|
| + }
|
| + }
|
| +
|
| + ssl_GetSpecWriteLock(ss); /**************************************/
|
| +
|
| + PORT_Assert(ss->ssl3.prSpec == ss->ssl3.pwSpec);
|
| +
|
| + pwSpec = ss->ssl3.pwSpec;
|
| + cwSpec = ss->ssl3.cwSpec;
|
| +
|
| + switch (pwSpec->cipher_def->calg) {
|
| + case calg_aes_gcm:
|
| + pwSpec->aead = tls13_AESGCM;
|
| + break;
|
| + default:
|
| + PORT_Assert(0);
|
| + goto loser;
|
| + break;
|
| + }
|
| +
|
| + /* Generic behaviors -- common to all crypto methods */
|
| + if (!IS_DTLS(ss)) {
|
| + pwSpec->read_seq_num.high = pwSpec->write_seq_num.high = 0;
|
| + } else {
|
| + if (cwSpec->epoch == PR_UINT16_MAX) {
|
| + /* The problem here is that we have rehandshaked too many
|
| + * times (you are not allowed to wrap the epoch). The
|
| + * spec says you should be discarding the connection
|
| + * and start over, so not much we can do here. */
|
| + rv = SECFailure;
|
| + goto loser;
|
| + }
|
| + /* The sequence number has the high 16 bits as the epoch. */
|
| + pwSpec->epoch = cwSpec->epoch + 1;
|
| + pwSpec->read_seq_num.high = pwSpec->write_seq_num.high =
|
| + pwSpec->epoch << 16;
|
| +
|
| + dtls_InitRecvdRecords(&pwSpec->recvdRecords);
|
| + }
|
| + pwSpec->read_seq_num.low = pwSpec->write_seq_num.low = 0;
|
| +
|
| + rv = tls13_DeriveTrafficKeys(ss, pwSpec, type);
|
| + if (rv != SECSuccess) {
|
| + goto loser;
|
| + }
|
| + if (install == InstallCipherSpecWrite ||
|
| + install == InstallCipherSpecBoth) {
|
| + rv = tls13_InstallCipherSpec(ss, InstallCipherSpecWrite);
|
| + if (rv != SECSuccess) {
|
| + goto loser;
|
| + }
|
| + }
|
| + if (install == InstallCipherSpecRead ||
|
| + install == InstallCipherSpecBoth) {
|
| + rv = tls13_InstallCipherSpec(ss, InstallCipherSpecRead);
|
| + if (rv != SECSuccess) {
|
| + goto loser;
|
| + }
|
| + }
|
| + ssl_ReleaseSpecWriteLock(ss); /**************************************/
|
| +
|
| + return SECSuccess;
|
| +
|
| +loser:
|
| + ssl_ReleaseSpecWriteLock(ss); /**************************************/
|
| + PORT_SetError(SSL_ERROR_INIT_CIPHER_SUITE_FAILURE);
|
| + return SECFailure;
|
| +}
|
| +
|
| +static SECStatus
|
| +tls13_ComputeSecrets1(sslSocket *ss)
|
| +{
|
| + SECStatus rv;
|
| + PK11SymKey *mSS = NULL;
|
| + PK11SymKey *mES = NULL;
|
| + PK11SymKey *masterSecret = NULL;
|
| + SSL3Hashes hashes;
|
| +
|
| + rv = ssl3_SetupPendingCipherSpec(ss);
|
| + if (rv != SECSuccess) {
|
| + return rv; /* error code set below. */
|
| + }
|
| +
|
| + rv = ssl3_ComputeHandshakeHashes(ss, ss->ssl3.pwSpec, &hashes, 0);
|
| + if (rv != SECSuccess) {
|
| + PORT_Assert(0); /* Should never fail */
|
| + ssl_MapLowLevelError(SSL_ERROR_SESSION_KEY_GEN_FAILURE);
|
| + return SECFailure;
|
| + }
|
| +
|
| + PORT_Assert(ss->ssl3.hs.xSS);
|
| + PORT_Assert(ss->ssl3.hs.xES);
|
| +
|
| + rv = tls13_HkdfExpandLabel(ss->ssl3.hs.xSS,
|
| + tls13_GetHash(ss),
|
| + hashes.u.raw, hashes.len,
|
| + kHkdfLabelExpandedSs,
|
| + strlen(kHkdfLabelExpandedSs),
|
| + tls13_GetHkdfMechanism(ss),
|
| + hashes.len, &mSS);
|
| + if (rv != SECSuccess) {
|
| + goto loser;
|
| + }
|
| +
|
| + rv = tls13_HkdfExpandLabel(ss->ssl3.hs.xES,
|
| + tls13_GetHash(ss),
|
| + hashes.u.raw, hashes.len,
|
| + kHkdfLabelExpandedEs,
|
| + strlen(kHkdfLabelExpandedEs),
|
| + tls13_GetHkdfMechanism(ss),
|
| + hashes.len, &mES);
|
| + if (rv != SECSuccess) {
|
| + goto loser;
|
| + }
|
| +
|
| + rv = tls13_HkdfExtract(mSS, mES,
|
| + tls13_GetHash(ss),
|
| + &masterSecret);
|
| +
|
| + if (rv != SECSuccess) {
|
| + goto loser;
|
| + }
|
| +
|
| + rv = tls13_HkdfExpandLabel(masterSecret,
|
| + tls13_GetHash(ss),
|
| + hashes.u.raw, hashes.len,
|
| + kHkdfLabelTrafficSecret,
|
| + strlen(kHkdfLabelTrafficSecret),
|
| + tls13_GetHkdfMechanism(ss),
|
| + hashes.len, &ss->ssl3.hs.trafficSecret);
|
| + if (rv != SECSuccess) {
|
| + goto loser;
|
| + }
|
| +
|
| + rv = tls13_HkdfExpandLabel(masterSecret,
|
| + tls13_GetHash(ss),
|
| + NULL, 0,
|
| + kHkdfLabelClientFinishedSecret,
|
| + strlen(kHkdfLabelClientFinishedSecret),
|
| + tls13_GetHmacMechanism(ss),
|
| + hashes.len, &ss->ssl3.hs.clientFinishedSecret);
|
| + if (rv != SECSuccess) {
|
| + goto loser;
|
| + }
|
| +
|
| + rv = tls13_HkdfExpandLabel(masterSecret,
|
| + tls13_GetHash(ss),
|
| + NULL, 0,
|
| + kHkdfLabelServerFinishedSecret,
|
| + strlen(kHkdfLabelServerFinishedSecret),
|
| + tls13_GetHmacMechanism(ss),
|
| + hashes.len, &ss->ssl3.hs.serverFinishedSecret);
|
| + if (rv != SECSuccess) {
|
| + goto loser;
|
| + }
|
| +
|
| +loser:
|
| + PK11_FreeSymKey(ss->ssl3.hs.xSS);
|
| + ss->ssl3.hs.xSS = NULL;
|
| + PK11_FreeSymKey(ss->ssl3.hs.xES);
|
| + ss->ssl3.hs.xES = NULL;
|
| +
|
| + if (mSS) {
|
| + PK11_FreeSymKey(mSS);
|
| + }
|
| + if (mES) {
|
| + PK11_FreeSymKey(mES);
|
| + }
|
| + if (masterSecret) {
|
| + PK11_FreeSymKey(masterSecret);
|
| + }
|
| +
|
| + return rv;
|
| +}
|
| +
|
| +void
|
| +tls13_DestroyKeyShareEntry(TLS13KeyShareEntry *offer)
|
| +{
|
| + SECITEM_ZfreeItem(&offer->key_exchange, PR_FALSE);
|
| + PORT_ZFree(offer, sizeof(*offer));
|
| +}
|
| +
|
| +void
|
| +tls13_DestroyKeyShares(PRCList *list)
|
| +{
|
| + PRCList *cur_p;
|
| +
|
| + while (!PR_CLIST_IS_EMPTY(list)) {
|
| + cur_p = PR_LIST_TAIL(list);
|
| + PR_REMOVE_LINK(cur_p);
|
| + tls13_DestroyKeyShareEntry((TLS13KeyShareEntry *)cur_p);
|
| + }
|
| +}
|
| +
|
| +/* Implement the SSLAEADCipher interface defined in sslimpl.h.
|
| + *
|
| + * That interface mixes the AD and the sequence number, but in
|
| + * TLS 1.3 there is no additional data so this value is just the
|
| + * encoded sequence number and we call it |seqNumBuf|.
|
| + */
|
| +static SECStatus
|
| +tls13_AESGCM(ssl3KeyMaterial *keys,
|
| + PRBool doDecrypt,
|
| + unsigned char *out,
|
| + int *outlen,
|
| + int maxout,
|
| + const unsigned char *in,
|
| + int inlen,
|
| + const unsigned char *seqNumBuf,
|
| + int seqNumLen)
|
| +{
|
| + SECItem param;
|
| + SECStatus rv = SECFailure;
|
| + unsigned char nonce[12];
|
| + size_t i;
|
| + unsigned int uOutLen;
|
| + CK_GCM_PARAMS gcmParams;
|
| + static const int tagSize = 16;
|
| +
|
| + PORT_Assert(seqNumLen == 8);
|
| +
|
| + /* draft-ietf-tls-tls13 Section 5.2.2 specifies the following
|
| + * nonce algorithm:
|
| + *
|
| + * The length of the per-record nonce (iv_length) is set to max(8 bytes,
|
| + * N_MIN) for the AEAD algorithm (see [RFC5116] Section 4). An AEAD
|
| + * algorithm where N_MAX is less than 8 bytes MUST NOT be used with TLS.
|
| + * The per-record nonce for the AEAD construction is formed as follows:
|
| + *
|
| + * 1. The 64-bit record sequence number is padded to the left with
|
| + * zeroes to iv_length.
|
| + *
|
| + * 2. The padded sequence number is XORed with the static
|
| + * client_write_iv or server_write_iv, depending on the role.
|
| + *
|
| + * The resulting quantity (of length iv_length) is used as the per-
|
| + * record nonce.
|
| + *
|
| + * Per RFC 5288: N_MIN = N_MAX = 12 bytes.
|
| + *
|
| + */
|
| + memcpy(nonce, keys->write_iv, sizeof(nonce));
|
| + for (i = 0; i < 8; ++i) {
|
| + nonce[4 + i] ^= seqNumBuf[i];
|
| + }
|
| +
|
| + param.type = siBuffer;
|
| + param.data = (unsigned char *)&gcmParams;
|
| + param.len = sizeof(gcmParams);
|
| + gcmParams.pIv = nonce;
|
| + gcmParams.ulIvLen = sizeof(nonce);
|
| + gcmParams.pAAD = NULL;
|
| + gcmParams.ulAADLen = 0;
|
| + gcmParams.ulTagBits = tagSize * 8;
|
| +
|
| + if (doDecrypt) {
|
| + rv = PK11_Decrypt(keys->write_key, CKM_AES_GCM, ¶m, out, &uOutLen,
|
| + maxout, in, inlen);
|
| + } else {
|
| + rv = PK11_Encrypt(keys->write_key, CKM_AES_GCM, ¶m, out, &uOutLen,
|
| + maxout, in, inlen);
|
| + }
|
| + *outlen = (int)uOutLen;
|
| +
|
| + return rv;
|
| +}
|
| +
|
| +static SECStatus
|
| +tls13_HandleEncryptedExtensions(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
|
| +{
|
| + SECStatus rv;
|
| + PRInt32 innerLength;
|
| +
|
| + PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
|
| + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
|
| +
|
| + SSL_TRC(3, ("%d: TLS13[%d]: handle encrypted extensions",
|
| + SSL_GETPID(), ss->fd));
|
| +
|
| + rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_ENCRYPTED_EXTENSIONS,
|
| + wait_encrypted_extensions);
|
| + if (rv != SECSuccess) {
|
| + return SECFailure;
|
| + }
|
| +
|
| + innerLength = ssl3_ConsumeHandshakeNumber(ss, 2, &b, &length);
|
| + if (innerLength < 0) {
|
| + return SECFailure; /* Alert already sent. */
|
| + }
|
| + if (innerLength != length) {
|
| + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_ENCRYPTED_EXTENSIONS,
|
| + illegal_parameter);
|
| + return SECFailure;
|
| + }
|
| +
|
| + rv = ssl3_HandleHelloExtensions(ss, &b, &length, encrypted_extensions);
|
| + if (rv != SECSuccess) {
|
| + return SECFailure; /* Error code set below */
|
| + }
|
| +
|
| + TLS13_SET_HS_STATE(ss, wait_cert_request);
|
| + return SECSuccess;
|
| +}
|
| +
|
| +static SECStatus
|
| +tls13_SendEncryptedExtensions(sslSocket *ss)
|
| +{
|
| + SECStatus rv;
|
| + PRInt32 extensions_len = 0;
|
| + PRInt32 sent_len = 0;
|
| + PRUint32 maxBytes = 65535;
|
| +
|
| + SSL_TRC(3, ("%d: TLS13[%d]: send encrypted extensions handshake",
|
| + SSL_GETPID(), ss->fd));
|
| +
|
| + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
|
| + PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss));
|
| +
|
| + extensions_len = ssl3_CallHelloExtensionSenders(
|
| + ss, PR_FALSE, maxBytes, &ss->xtnData.encryptedExtensionsSenders[0]);
|
| +
|
| + rv = ssl3_AppendHandshakeHeader(ss, encrypted_extensions,
|
| + extensions_len + 2);
|
| + if (rv != SECSuccess) {
|
| + FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
|
| + return SECFailure;
|
| + }
|
| + rv = ssl3_AppendHandshakeNumber(ss, extensions_len, 2);
|
| + if (rv != SECSuccess) {
|
| + FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
|
| + return SECFailure;
|
| + }
|
| + sent_len = ssl3_CallHelloExtensionSenders(
|
| + ss, PR_TRUE, extensions_len,
|
| + &ss->xtnData.encryptedExtensionsSenders[0]);
|
| + PORT_Assert(sent_len == extensions_len);
|
| + if (sent_len != extensions_len) {
|
| + PORT_Assert(sent_len == 0);
|
| + FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
|
| + return SECFailure;
|
| + }
|
| +
|
| + return SECSuccess;
|
| +}
|
| +
|
| +/* Called from tls13_CompleteHandleHandshakeMessage() when it has deciphered a complete
|
| + * tls13 CertificateVerify message
|
| + * Caller must hold Handshake and RecvBuf locks.
|
| + */
|
| +SECStatus
|
| +tls13_HandleCertificateVerify(sslSocket *ss, SSL3Opaque *b, PRUint32 length,
|
| + SSL3Hashes *hashes)
|
| +{
|
| + SECItem signed_hash = { siBuffer, NULL, 0 };
|
| + SECStatus rv;
|
| + SSLSignatureAndHashAlg sigAndHash;
|
| +
|
| + SSL_TRC(3, ("%d: TLS13[%d]: handle certificate_verify handshake",
|
| + SSL_GETPID(), ss->fd));
|
| + PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
|
| + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
|
| +
|
| + rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_CERT_VERIFY,
|
| + wait_cert_verify);
|
| + if (rv != SECSuccess) {
|
| + return SECFailure;
|
| + }
|
| +
|
| + if (!hashes) {
|
| + FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
|
| + return SECFailure;
|
| + }
|
| +
|
| + /* We only support CertificateVerify messages that use the handshake
|
| + * hash.
|
| + * TODO(ekr@rtfm.com): This should be easy to relax in TLS 1.3 by
|
| + * reading the client's hash algorithm first, but there may
|
| + * be subtleties so retain the restriction for now.
|
| + */
|
| + rv = tls13_AddContextToHashes(ss, hashes, hashes->hashAlg, PR_FALSE);
|
| + if (rv != SECSuccess) {
|
| + FATAL_ERROR(ss, SSL_ERROR_DIGEST_FAILURE, internal_error);
|
| + return SECFailure;
|
| + }
|
| +
|
| + rv = ssl3_ConsumeSignatureAndHashAlgorithm(ss, &b, &length,
|
| + &sigAndHash);
|
| + if (rv != SECSuccess) {
|
| + PORT_SetError(SSL_ERROR_RX_MALFORMED_CERT_VERIFY);
|
| + return SECFailure;
|
| + }
|
| +
|
| + rv = ssl3_CheckSignatureAndHashAlgorithmConsistency(
|
| + ss, &sigAndHash, ss->sec.peerCert);
|
| + if (rv != SECSuccess) {
|
| + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CERT_VERIFY, decrypt_error);
|
| + return SECFailure;
|
| + }
|
| +
|
| + /* We only support CertificateVerify messages that use the handshake
|
| + * hash. */
|
| + if (sigAndHash.hashAlg != hashes->hashAlg) {
|
| + FATAL_ERROR(ss, SSL_ERROR_UNSUPPORTED_HASH_ALGORITHM, decrypt_error);
|
| + return SECFailure;
|
| + }
|
| +
|
| + rv = ssl3_ConsumeHandshakeVariable(ss, &signed_hash, 2, &b, &length);
|
| + if (rv != SECSuccess) {
|
| + PORT_SetError(SSL_ERROR_RX_MALFORMED_CERT_VERIFY);
|
| + return SECFailure;
|
| + }
|
| +
|
| + if (length != 0) {
|
| + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CERT_VERIFY, decode_error);
|
| + return SECFailure;
|
| + }
|
| +
|
| + rv = ssl3_VerifySignedHashes(hashes, ss->sec.peerCert, &signed_hash,
|
| + PR_TRUE, ss->pkcs11PinArg);
|
| + if (rv != SECSuccess) {
|
| + FATAL_ERROR(ss, PORT_GetError(), decrypt_error);
|
| + return SECFailure;
|
| + }
|
| +
|
| + if (!ss->sec.isServer) {
|
| + /* Compute the rest of the secrets except for the resumption
|
| + * and exporter secret. */
|
| + rv = tls13_ComputeSecrets1(ss);
|
| + if (rv != SECSuccess) {
|
| + FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
|
| + return SECFailure;
|
| + }
|
| + }
|
| + TLS13_SET_HS_STATE(ss, wait_finished);
|
| +
|
| + return SECSuccess;
|
| +}
|
| +
|
| +static SECStatus
|
| +tls13_ComputeFinished(sslSocket *ss, const SSL3Hashes *hashes, PRBool sending,
|
| + PRUint8 *output, unsigned int *outputLen, unsigned int maxOutputLen)
|
| +{
|
| + SECStatus rv;
|
| + PK11Context *hmacCtx = NULL;
|
| + CK_MECHANISM_TYPE macAlg = tls13_GetHmacMechanism(ss);
|
| + SECItem param = { siBuffer, NULL, 0 };
|
| + unsigned int outputLenUint;
|
| + PK11SymKey *secret = (ss->sec.isServer ^ sending) ? ss->ssl3.hs.clientFinishedSecret
|
| + : ss->ssl3.hs.serverFinishedSecret;
|
| +
|
| + PORT_Assert(secret);
|
| + PRINT_BUF(90, (NULL, "Handshake hash", hashes->u.raw, hashes->len));
|
| +
|
| + hmacCtx = PK11_CreateContextBySymKey(macAlg, CKA_SIGN,
|
| + secret, ¶m);
|
| + if (!hmacCtx) {
|
| + goto abort;
|
| + }
|
| +
|
| + rv = PK11_DigestBegin(hmacCtx);
|
| + if (rv != SECSuccess)
|
| + goto abort;
|
| +
|
| + rv = PK11_DigestOp(hmacCtx, hashes->u.raw, hashes->len);
|
| + if (rv != SECSuccess)
|
| + goto abort;
|
| +
|
| + PORT_Assert(maxOutputLen >= hashes->len);
|
| + rv = PK11_DigestFinal(hmacCtx, output, &outputLenUint, maxOutputLen);
|
| + if (rv != SECSuccess)
|
| + goto abort;
|
| + *outputLen = outputLenUint;
|
| +
|
| + PK11_DestroyContext(hmacCtx, PR_TRUE);
|
| + return SECSuccess;
|
| +
|
| +abort:
|
| + if (hmacCtx) {
|
| + PK11_DestroyContext(hmacCtx, PR_TRUE);
|
| + }
|
| +
|
| + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
|
| + return SECFailure;
|
| +}
|
| +
|
| +static SECStatus
|
| +tls13_SendFinished(sslSocket *ss)
|
| +{
|
| + SECStatus rv;
|
| + PRUint8 finishedBuf[MAX_FINISHED_SIZE];
|
| + unsigned int finishedLen;
|
| + SSL3Hashes hashes;
|
| + int errCode;
|
| +
|
| + SSL_TRC(3, ("%d: TLS13[%d]: send finished handshake", SSL_GETPID(), ss->fd));
|
| +
|
| + PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss));
|
| + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
|
| +
|
| + rv = ssl3_ComputeHandshakeHashes(ss, ss->ssl3.cwSpec, &hashes, 0);
|
| + if (rv != SECSuccess) {
|
| + FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
|
| + return SECFailure;
|
| + }
|
| +
|
| + ssl_GetSpecReadLock(ss);
|
| + rv = tls13_ComputeFinished(ss, &hashes, PR_TRUE,
|
| + finishedBuf, &finishedLen, sizeof(finishedBuf));
|
| + ssl_ReleaseSpecReadLock(ss);
|
| + if (rv != SECSuccess) {
|
| + FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
|
| + return SECFailure;
|
| + }
|
| +
|
| + rv = ssl3_AppendHandshakeHeader(ss, finished, finishedLen);
|
| + if (rv != SECSuccess) {
|
| + errCode = PR_GetError();
|
| + goto alert_loser;
|
| + }
|
| +
|
| + rv = ssl3_AppendHandshake(ss, finishedBuf, finishedLen);
|
| + if (rv != SECSuccess) {
|
| + errCode = PR_GetError();
|
| + goto alert_loser;
|
| + }
|
| +
|
| + rv = ssl3_FlushHandshake(ss, 0);
|
| + if (rv != SECSuccess) {
|
| + errCode = PR_GetError();
|
| + goto alert_loser;
|
| + }
|
| +
|
| + if (ss->sec.isServer) {
|
| + rv = tls13_InitCipherSpec(ss, TrafficKeyApplicationData,
|
| + InstallCipherSpecWrite);
|
| + } else {
|
| + rv = tls13_InstallCipherSpec(ss, InstallCipherSpecWrite);
|
| + }
|
| + if (rv != SECSuccess) {
|
| + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
|
| + return SECFailure;
|
| + }
|
| +
|
| + /* TODO(ekr@rtfm.com): Record key log */
|
| + return SECSuccess;
|
| +
|
| +alert_loser:
|
| + (void)SSL3_SendAlert(ss, alert_fatal, internal_error);
|
| + PORT_SetError(errCode); /* Restore error code */
|
| + return rv;
|
| +}
|
| +
|
| +static SECStatus
|
| +tls13_HandleFinished(sslSocket *ss, SSL3Opaque *b, PRUint32 length,
|
| + const SSL3Hashes *hashes)
|
| +{
|
| + SECStatus rv;
|
| + PRUint8 finishedBuf[MAX_FINISHED_SIZE];
|
| + unsigned int finishedLen;
|
| +
|
| + PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
|
| + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
|
| +
|
| + SSL_TRC(3, ("%d: TLS13[%d]: handle finished handshake",
|
| + SSL_GETPID(), ss->fd));
|
| +
|
| + rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_FINISHED, wait_finished);
|
| + if (rv != SECSuccess) {
|
| + return SECFailure;
|
| + }
|
| + if (!hashes) {
|
| + FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
|
| + return SECFailure;
|
| + }
|
| +
|
| + ssl_GetSpecReadLock(ss);
|
| + rv = tls13_ComputeFinished(ss, hashes, PR_FALSE,
|
| + finishedBuf, &finishedLen, sizeof(finishedBuf));
|
| + ssl_ReleaseSpecReadLock(ss);
|
| + if (rv != SECSuccess) {
|
| + FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
|
| + return SECFailure;
|
| + }
|
| +
|
| + if (length != finishedLen) {
|
| + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_FINISHED, decode_error);
|
| + return SECFailure;
|
| + }
|
| +
|
| + if (NSS_SecureMemcmp(b, finishedBuf, finishedLen) != 0) {
|
| + FATAL_ERROR(ss, SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE,
|
| + decrypt_error);
|
| + return SECFailure;
|
| + }
|
| +
|
| + /* Server is now finished.
|
| + * Client sends second flight
|
| + */
|
| + /* TODO(ekr@rtfm.com): Send NewSession Ticket if server. */
|
| + if (ss->sec.isServer) {
|
| + rv = tls13_InstallCipherSpec(ss, InstallCipherSpecRead);
|
| + if (rv != SECSuccess) {
|
| + FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
|
| + return SECFailure;
|
| + }
|
| +
|
| + rv = tls13_FinishHandshake(ss);
|
| + } else {
|
| + if (ss->ssl3.hs.authCertificatePending) {
|
| + /* TODO(ekr@rtfm.com): Handle pending auth */
|
| + FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
|
| + PORT_Assert(0);
|
| + return SECFailure;
|
| + }
|
| + rv = tls13_InitCipherSpec(ss, TrafficKeyApplicationData,
|
| + InstallCipherSpecRead);
|
| + if (rv != SECSuccess) {
|
| + FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
|
| + return SECFailure;
|
| + }
|
| +
|
| + rv = tls13_SendClientSecondRound(ss);
|
| + if (rv != SECSuccess)
|
| + return SECFailure; /* Error code and alerts handled below */
|
| + }
|
| +
|
| + return rv;
|
| +}
|
| +
|
| +static SECStatus
|
| +tls13_FinishHandshake(sslSocket *ss)
|
| +{
|
| + PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
|
| + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
|
| + PORT_Assert(ss->ssl3.hs.restartTarget == NULL);
|
| +
|
| + /* The first handshake is now completed. */
|
| + ss->handshake = NULL;
|
| +
|
| + TLS13_SET_HS_STATE(ss, idle_handshake);
|
| +
|
| + ssl_FinishHandshake(ss);
|
| +
|
| + return SECSuccess;
|
| +}
|
| +
|
| +static SECStatus
|
| +tls13_SendClientSecondRound(sslSocket *ss)
|
| +{
|
| + SECStatus rv;
|
| + PRBool sendClientCert;
|
| +
|
| + PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
|
| + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
|
| +
|
| + sendClientCert = !ss->ssl3.sendEmptyCert &&
|
| + ss->ssl3.clientCertChain != NULL &&
|
| + ss->ssl3.clientPrivateKey != NULL;
|
| +
|
| + /* Defer client authentication sending if we are still
|
| + * waiting for server authentication. See the long block
|
| + * comment in ssl3_SendClientSecondRound for more detail.
|
| + */
|
| + if (ss->ssl3.hs.restartTarget) {
|
| + PR_NOT_REACHED("unexpected ss->ssl3.hs.restartTarget");
|
| + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
|
| + return SECFailure;
|
| + }
|
| + if (ss->ssl3.hs.authCertificatePending && (sendClientCert ||
|
| + ss->ssl3.sendEmptyCert)) {
|
| + SSL_TRC(3, ("%d: TLS13[%p]: deferring ssl3_SendClientSecondRound because"
|
| + " certificate authentication is still pending.",
|
| + SSL_GETPID(), ss->fd));
|
| + ss->ssl3.hs.restartTarget = tls13_SendClientSecondRound;
|
| + return SECWouldBlock;
|
| + }
|
| +
|
| + ssl_GetXmitBufLock(ss); /*******************************/
|
| + if (ss->ssl3.sendEmptyCert) {
|
| + ss->ssl3.sendEmptyCert = PR_FALSE;
|
| + rv = ssl3_SendEmptyCertificate(ss);
|
| + /* Don't send verify */
|
| + if (rv != SECSuccess) {
|
| + goto loser; /* error code is set. */
|
| + }
|
| + } else if (sendClientCert) {
|
| + rv = ssl3_SendCertificate(ss);
|
| + if (rv != SECSuccess) {
|
| + goto loser; /* error code is set. */
|
| + }
|
| + }
|
| +
|
| + if (sendClientCert) {
|
| + rv = ssl3_SendCertificateVerify(ss, ss->ssl3.clientPrivateKey);
|
| + SECKEY_DestroyPrivateKey(ss->ssl3.clientPrivateKey);
|
| + ss->ssl3.clientPrivateKey = NULL;
|
| + if (rv != SECSuccess) {
|
| + goto loser; /* err is set. */
|
| + }
|
| + }
|
| +
|
| + rv = tls13_SendFinished(ss);
|
| + if (rv != SECSuccess) {
|
| + goto loser; /* err code was set. */
|
| + }
|
| + ssl_ReleaseXmitBufLock(ss); /*******************************/
|
| +
|
| + /* The handshake is now finished */
|
| + return tls13_FinishHandshake(ss);
|
| +
|
| +loser:
|
| + ssl_ReleaseXmitBufLock(ss); /*******************************/
|
| + return SECFailure;
|
| +}
|
| +
|
| +static SECStatus
|
| +tls13_HandleNewSessionTicket(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
|
| +{
|
| + SECStatus rv;
|
| +
|
| + rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_NEW_SESSION_TICKET,
|
| + idle_handshake);
|
| + if (rv != SECSuccess) {
|
| + return SECFailure;
|
| + }
|
| +
|
| + UNIMPLEMENTED();
|
| +
|
| + /* Ignore */
|
| + return SECSuccess;
|
| +}
|
| +
|
| +typedef enum {
|
| + ExtensionNotUsed,
|
| + ExtensionClientOnly,
|
| + ExtensionSendClear,
|
| + ExtensionSendEncrypted,
|
| +} Tls13ExtensionStatus;
|
| +
|
| +static const struct {
|
| + SSLExtensionType ex_value;
|
| + Tls13ExtensionStatus status;
|
| +} KnownExtensions[] = {
|
| + { ssl_server_name_xtn,
|
| + ExtensionSendEncrypted },
|
| + {
|
| + ssl_cert_status_xtn,
|
| + ExtensionNotUsed /* TODO(ekr@rtfm.com): Disabled because broken
|
| + in TLS 1.3. */
|
| + /* ExtensionSendEncrypted */
|
| + },
|
| + { ssl_elliptic_curves_xtn,
|
| + ExtensionSendClear },
|
| + { ssl_ec_point_formats_xtn,
|
| + ExtensionNotUsed },
|
| + { ssl_signature_algorithms_xtn,
|
| + ExtensionClientOnly },
|
| + { ssl_use_srtp_xtn,
|
| + ExtensionSendEncrypted },
|
| + { ssl_app_layer_protocol_xtn,
|
| + ExtensionSendEncrypted },
|
| + { ssl_padding_xtn,
|
| + ExtensionNotUsed },
|
| + { ssl_extended_master_secret_xtn,
|
| + ExtensionNotUsed },
|
| + { ssl_session_ticket_xtn,
|
| + ExtensionClientOnly },
|
| + { ssl_tls13_key_share_xtn,
|
| + ExtensionSendClear },
|
| + { ssl_next_proto_nego_xtn,
|
| + ExtensionNotUsed },
|
| + { ssl_renegotiation_info_xtn,
|
| + ExtensionNotUsed },
|
| + { ssl_tls13_draft_version_xtn,
|
| + ExtensionClientOnly }
|
| +};
|
| +
|
| +PRBool
|
| +tls13_ExtensionAllowed(PRUint16 extension, SSL3HandshakeType message)
|
| +{
|
| + unsigned int i;
|
| +
|
| + PORT_Assert((message == client_hello) ||
|
| + (message == server_hello) ||
|
| + (message == encrypted_extensions));
|
| +
|
| + for (i = 0; i < PR_ARRAY_SIZE(KnownExtensions); i++) {
|
| + if (KnownExtensions[i].ex_value == extension)
|
| + break;
|
| + }
|
| + if (i == PR_ARRAY_SIZE(KnownExtensions)) {
|
| + /* We have never heard of this extension which is OK on
|
| + * the server but not the client. */
|
| + return message == client_hello;
|
| + }
|
| +
|
| + switch (KnownExtensions[i].status) {
|
| + case ExtensionNotUsed:
|
| + return PR_FALSE;
|
| + case ExtensionClientOnly:
|
| + return message == client_hello;
|
| + case ExtensionSendClear:
|
| + return message == client_hello ||
|
| + message == server_hello;
|
| + case ExtensionSendEncrypted:
|
| + return message == client_hello ||
|
| + message == encrypted_extensions;
|
| + }
|
| +
|
| + PORT_Assert(0);
|
| +
|
| + /* Not reached */
|
| + return PR_TRUE;
|
| +}
|
| +
|
| +/* Helper function to encode a uint32 into a buffer */
|
| +unsigned char *
|
| +tls13_EncodeUintX(PRUint32 value, unsigned int bytes, unsigned char *to)
|
| +{
|
| + PRUint32 encoded;
|
| +
|
| + PORT_Assert(bytes > 0 && bytes <= 4);
|
| +
|
| + encoded = PR_htonl(value);
|
| + memcpy(to, ((unsigned char *)(&encoded)) + (4 - bytes), bytes);
|
| + return to + bytes;
|
| +}
|
| +
|
| +/* TLS 1.3 doesn't actually have additional data but the aead function
|
| + * signature overloads additional data to carry the record sequence
|
| + * number and that's what we put here. The TLS 1.3 AEAD functions
|
| + * just use this input as the sequence number and not as additional
|
| + * data. */
|
| +static void
|
| +tls13_FormatAdditionalData(unsigned char *aad, unsigned int length,
|
| + SSL3SequenceNumber seqNum)
|
| +{
|
| + unsigned char *ptr = aad;
|
| +
|
| + PORT_Assert(length == 8);
|
| + ptr = tls13_EncodeUintX(seqNum.high, 4, ptr);
|
| + ptr = tls13_EncodeUintX(seqNum.low, 4, ptr);
|
| + PORT_Assert((ptr - aad) == length);
|
| +}
|
| +
|
| +SECStatus
|
| +tls13_ProtectRecord(sslSocket *ss,
|
| + SSL3ContentType type,
|
| + const SSL3Opaque *pIn,
|
| + PRUint32 contentLen,
|
| + sslBuffer *wrBuf)
|
| +{
|
| + ssl3CipherSpec *cwSpec = ss->ssl3.cwSpec;
|
| + const ssl3BulkCipherDef *cipher_def = cwSpec->cipher_def;
|
| + SECStatus rv;
|
| + PRUint16 headerLen;
|
| + int cipherBytes = 0;
|
| + const int tagLen = cipher_def->tag_size;
|
| +
|
| + SSL_TRC(3, ("%d: TLS13[%d]: protect record of length %u, seq=0x%0x%0x",
|
| + SSL_GETPID(), ss->fd, contentLen,
|
| + cwSpec->write_seq_num.high,
|
| + cwSpec->write_seq_num.low));
|
| +
|
| + headerLen = IS_DTLS(ss) ? DTLS_RECORD_HEADER_LENGTH : SSL3_RECORD_HEADER_LENGTH;
|
| +
|
| + if (headerLen + contentLen + 1 + tagLen > wrBuf->space) {
|
| + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
|
| + return SECFailure;
|
| + }
|
| +
|
| + /* Copy the data into the wrBuf. We're going to encrypt in-place
|
| + * in the AEAD branch anyway */
|
| + PORT_Memcpy(wrBuf->buf + headerLen, pIn, contentLen);
|
| +
|
| + if (cipher_def->calg == ssl_calg_null) {
|
| + /* Shortcut for plaintext */
|
| + cipherBytes = contentLen;
|
| + } else {
|
| + unsigned char aad[8];
|
| + PORT_Assert(cipher_def->type == type_aead);
|
| +
|
| + /* Add the content type at the end. */
|
| + wrBuf->buf[headerLen + contentLen] = type;
|
| +
|
| + /* Stomp the content type to be application_data */
|
| + type = content_application_data;
|
| +
|
| + tls13_FormatAdditionalData(aad, sizeof(aad),
|
| + cwSpec->write_seq_num);
|
| + cipherBytes = contentLen + 1; /* Room for the content type on the end. */
|
| + rv = cwSpec->aead(
|
| + ss->sec.isServer ? &cwSpec->server : &cwSpec->client,
|
| + PR_FALSE, /* do encrypt */
|
| + wrBuf->buf + headerLen, /* output */
|
| + &cipherBytes, /* out len */
|
| + wrBuf->space - headerLen, /* max out */
|
| + wrBuf->buf + headerLen, contentLen + 1, /* input */
|
| + aad, sizeof(aad));
|
| + if (rv != SECSuccess) {
|
| + PORT_SetError(SSL_ERROR_ENCRYPTION_FAILURE);
|
| + return SECFailure;
|
| + }
|
| + }
|
| +
|
| + PORT_Assert(cipherBytes <= MAX_FRAGMENT_LENGTH + 256);
|
| +
|
| + wrBuf->len = cipherBytes + headerLen;
|
| + wrBuf->buf[0] = type;
|
| +
|
| + if (IS_DTLS(ss)) {
|
| + (void)tls13_EncodeUintX(2, dtls_TLSVersionToDTLSVersion(kRecordVersion),
|
| + &wrBuf->buf[1]);
|
| + (void)tls13_EncodeUintX(cwSpec->write_seq_num.high, 4, &wrBuf->buf[3]);
|
| + (void)tls13_EncodeUintX(cwSpec->write_seq_num.low, 4, &wrBuf->buf[7]);
|
| + (void)tls13_EncodeUintX(cipherBytes, 2, &wrBuf->buf[11]);
|
| + } else {
|
| + (void)tls13_EncodeUintX(kRecordVersion, 2, &wrBuf->buf[1]);
|
| + (void)tls13_EncodeUintX(cipherBytes, 2, &wrBuf->buf[3]);
|
| + }
|
| + ssl3_BumpSequenceNumber(&cwSpec->write_seq_num);
|
| +
|
| + return SECSuccess;
|
| +}
|
| +
|
| +/* Unprotect a TLS 1.3 record and leave the result in plaintext.
|
| + *
|
| + * Called by ssl3_HandleRecord. Caller must hold the spec read lock.
|
| + * Therefore, we MUST not call SSL3_SendAlert().
|
| + *
|
| + * If SECFailure is returned, we:
|
| + * 1. Set |*alert| to the alert to be sent.
|
| + * 2. Call PORT_SetError() witn an appropriate code.
|
| + */
|
| +SECStatus
|
| +tls13_UnprotectRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *plaintext,
|
| + SSL3AlertDescription *alert)
|
| +{
|
| + ssl3CipherSpec *crSpec = ss->ssl3.crSpec;
|
| + const ssl3BulkCipherDef *cipher_def = crSpec->cipher_def;
|
| + unsigned char aad[8];
|
| + SECStatus rv;
|
| +
|
| + *alert = bad_record_mac; /* Default alert for most issues. */
|
| +
|
| + SSL_TRC(3, ("%d: TLS13[%d]: unprotect record of length %u",
|
| + SSL_GETPID(), ss->fd, cText->buf->len));
|
| +
|
| + /* We can perform this test in variable time because the record's total
|
| + * length and the ciphersuite are both public knowledge. */
|
| + if (cText->buf->len < cipher_def->tag_size) {
|
| + PORT_SetError(SSL_ERROR_RX_RECORD_TOO_LONG);
|
| + return SECFailure;
|
| + }
|
| +
|
| + /* Verify that the content type is right, even though we overwrite it. */
|
| + if (cText->type != content_application_data) {
|
| + /* Do we need a better error here? */
|
| + PORT_SetError(SSL_ERROR_BAD_MAC_READ);
|
| + return SECFailure;
|
| + }
|
| +
|
| + /* Check the version number in the record */
|
| + if (cText->version != kRecordVersion) {
|
| + /* Do we need a better error here? */
|
| + PORT_SetError(SSL_ERROR_BAD_MAC_READ);
|
| + return SECFailure;
|
| + }
|
| +
|
| + /* Decrypt */
|
| + PORT_Assert(cipher_def->type == type_aead);
|
| + tls13_FormatAdditionalData(aad, sizeof(aad),
|
| + IS_DTLS(ss) ? cText->seq_num
|
| + : crSpec->read_seq_num);
|
| + rv = crSpec->aead(
|
| + ss->sec.isServer ? &crSpec->client : &crSpec->server,
|
| + PR_TRUE, /* do decrypt */
|
| + plaintext->buf, /* out */
|
| + (int *)&plaintext->len, /* outlen */
|
| + plaintext->space, /* maxout */
|
| + cText->buf->buf, /* in */
|
| + cText->buf->len, /* inlen */
|
| + aad, sizeof(aad));
|
| + if (rv != SECSuccess) {
|
| + PORT_SetError(SSL_ERROR_BAD_MAC_READ);
|
| + return SECFailure;
|
| + }
|
| +
|
| + /* The record is right-padded with 0s, followed by the true
|
| + * content type, so read from the right until we receive a
|
| + * nonzero byte. */
|
| + while (plaintext->len > 0 && !(plaintext->buf[plaintext->len - 1])) {
|
| + --plaintext->len;
|
| + }
|
| +
|
| + /* Bogus padding. */
|
| + if (plaintext->len < 1) {
|
| + /* It's safe to report this specifically because it happened
|
| + * after the MAC has been verified. */
|
| + PORT_SetError(SSL_ERROR_BAD_BLOCK_PADDING);
|
| + return SECFailure;
|
| + }
|
| +
|
| + /* Record the type. */
|
| + cText->type = plaintext->buf[plaintext->len - 1];
|
| + --plaintext->len;
|
| +
|
| + return SECSuccess;
|
| +}
|
|
|