--- /dev/null
+/*
+ * Copyright (c) 1999-2001,2005-2008,2010-2012 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+/*
+ * sslHandshakeHello.c - Support for client hello and server hello messages.
+ */
+
+#include "sslContext.h"
+#include "sslHandshake.h"
+#include "sslMemory.h"
+#include "sslSession.h"
+#include "sslUtils.h"
+#include "sslDebug.h"
+#include "sslCrypto.h"
+
+#include "sslDigests.h"
+#include "cipherSpecs.h"
+
+#include <string.h>
+
+/* IE treats null session id as valid; two consecutive sessions with NULL ID
+ * are considered a match. Workaround: when resumable sessions are disabled,
+ * send a random session ID. */
+#define SSL_IE_NULL_RESUME_BUG 1
+#if SSL_IE_NULL_RESUME_BUG
+#define SSL_NULL_ID_LEN 32 /* length of bogus session ID */
+#endif
+
+OSStatus
+SSLEncodeServerHello(SSLRecord *serverHello, SSLContext *ctx)
+{ OSStatus err;
+ UInt8 *charPtr;
+ int sessionIDLen;
+ size_t msglen;
+ int head;
+
+ sessionIDLen = 0;
+ if (ctx->sessionID.data != 0)
+ sessionIDLen = (UInt8)ctx->sessionID.length;
+ #if SSL_IE_NULL_RESUME_BUG
+ if(sessionIDLen == 0) {
+ sessionIDLen = SSL_NULL_ID_LEN;
+ }
+ #endif /* SSL_IE_NULL_RESUME_BUG */
+
+ msglen = 38 + sessionIDLen;
+
+ /* this was set to a known quantity in SSLProcessClientHello */
+ assert(ctx->negProtocolVersion != SSL_Version_Undetermined);
+ /* should not be here in this case */
+ assert(ctx->negProtocolVersion != SSL_Version_2_0);
+ sslLogNegotiateDebug("===SSL3 server: sending version %d_%d",
+ ctx->negProtocolVersion >> 8, ctx->negProtocolVersion & 0xff);
+ sslLogNegotiateDebug("...sessionIDLen = %d", sessionIDLen);
+ serverHello->protocolVersion = ctx->negProtocolVersion;
+ serverHello->contentType = SSL_RecordTypeHandshake;
+ head = SSLHandshakeHeaderSize(serverHello);
+ if ((err = SSLAllocBuffer(&serverHello->contents, msglen + head, ctx)) != 0)
+ return err;
+
+ charPtr = SSLEncodeHandshakeHeader(ctx, serverHello, SSL_HdskServerHello, msglen);
+
+ charPtr = SSLEncodeInt(charPtr, serverHello->protocolVersion, 2);
+
+ #if SSL_PAC_SERVER_ENABLE
+ /* serverRandom might have already been set, in SSLAdvanceHandshake() */
+ if(!ctx->serverRandomValid) {
+ if ((err = SSLEncodeRandom(ctx->serverRandom, ctx)) != 0) {
+ return err;
+ }
+ }
+ #else
+ /* This is the normal production code path */
+ if ((err = SSLEncodeRandom(ctx->serverRandom, ctx)) != 0)
+ return err;
+ #endif /* SSL_PAC_SERVER_ENABLE */
+
+ memcpy(charPtr, ctx->serverRandom, SSL_CLIENT_SRVR_RAND_SIZE);
+
+ charPtr += SSL_CLIENT_SRVR_RAND_SIZE;
+ *(charPtr++) = (UInt8)sessionIDLen;
+ #if SSL_IE_NULL_RESUME_BUG
+ if(ctx->sessionID.data != NULL) {
+ /* normal path for enabled resumable session */
+ memcpy(charPtr, ctx->sessionID.data, sessionIDLen);
+ }
+ else {
+ /* IE workaround */
+ SSLBuffer rb;
+ rb.data = charPtr;
+ rb.length = SSL_NULL_ID_LEN;
+ sslRand(ctx, &rb);
+ }
+ #else
+ if (sessionIDLen > 0)
+ memcpy(charPtr, ctx->sessionID.data, sessionIDLen);
+ #endif /* SSL_IE_NULL_RESUME_BUG */
+ charPtr += sessionIDLen;
+ charPtr = SSLEncodeInt(charPtr, ctx->selectedCipher, 2);
+ *(charPtr++) = 0; /* Null compression */
+
+ sslLogNegotiateDebug("ssl3: server specifying cipherSuite 0x%lx",
+ (UInt32)ctx->selectedCipher);
+
+ assert(charPtr == serverHello->contents.data + serverHello->contents.length);
+
+ return noErr;
+}
+
+OSStatus
+SSLEncodeServerHelloVerifyRequest(SSLRecord *helloVerifyRequest, SSLContext *ctx)
+{ OSStatus err;
+ UInt8 *charPtr;
+ size_t msglen;
+ int head;
+
+ assert(ctx->protocolSide == kSSLServerSide);
+ assert(ctx->negProtocolVersion == DTLS_Version_1_0);
+ assert(ctx->dtlsCookie.length);
+
+ msglen = 3 + ctx->dtlsCookie.length;
+
+ helloVerifyRequest->protocolVersion = DTLS_Version_1_0;
+ helloVerifyRequest->contentType = SSL_RecordTypeHandshake;
+ head = SSLHandshakeHeaderSize(helloVerifyRequest);
+ if ((err = SSLAllocBuffer(&helloVerifyRequest->contents, msglen + head, ctx)) != 0)
+ return err;
+
+ charPtr = SSLEncodeHandshakeHeader(ctx, helloVerifyRequest, SSL_HdskHelloVerifyRequest, msglen);
+
+ charPtr = SSLEncodeInt(charPtr, helloVerifyRequest->protocolVersion, 2);
+
+ *charPtr++ = ctx->dtlsCookie.length;
+ memcpy(charPtr, ctx->dtlsCookie.data, ctx->dtlsCookie.length);
+ charPtr += ctx->dtlsCookie.length;
+
+ assert(charPtr == (helloVerifyRequest->contents.data + helloVerifyRequest->contents.length));
+
+ return noErr;
+}
+
+
+OSStatus
+SSLProcessServerHelloVerifyRequest(SSLBuffer message, SSLContext *ctx)
+{ OSStatus err;
+ SSLProtocolVersion protocolVersion;
+ unsigned int cookieLen;
+ UInt8 *p;
+
+ assert(ctx->protocolSide == kSSLClientSide);
+
+ /* TODO: those length values should not be hardcoded */
+ /* 3 bytes at least with empty cookie */
+ if (message.length < 3 ) {
+ sslErrorLog("SSLProcessServerHelloVerifyRequest: msg len error\n");
+ return errSSLProtocol;
+ }
+ p = message.data;
+
+ protocolVersion = (SSLProtocolVersion)SSLDecodeInt(p, 2);
+ p += 2;
+
+ /* TODO: Not clear what else to do with protocol version here */
+ if(protocolVersion != DTLS_Version_1_0) {
+ sslErrorLog("SSLProcessServerHelloVerifyRequest: protocol version error\n");
+ return errSSLProtocol;
+ }
+
+ cookieLen = *p++;
+ sslLogNegotiateDebug("cookieLen = %d, msglen=%d\n", cookieLen, message.length);
+ /* TODO: hardcoded '15' again */
+ if (message.length < (3 + cookieLen)) {
+ sslErrorLog("SSLProcessServerHelloVerifyRequest: msg len error 2\n");
+ return errSSLProtocol;
+ }
+
+ err = SSLAllocBuffer(&ctx->dtlsCookie, cookieLen, ctx);
+ if (err == 0)
+ memcpy(ctx->dtlsCookie.data, p, cookieLen);
+
+ return err;
+}
+
+static void
+SSLProcessServerHelloExtension_SecureRenegotiation(SSLContext *ctx, UInt16 extLen, UInt8 *p)
+{
+ if(extLen!= (1 + ctx->ownVerifyData.length + ctx->peerVerifyData.length))
+ return;
+
+ if(*p!=ctx->ownVerifyData.length + ctx->ownVerifyData.length)
+ return;
+ p++;
+
+ if(memcmp(p, ctx->ownVerifyData.data, ctx->ownVerifyData.length))
+ return;
+ p+=ctx->ownVerifyData.length;
+
+ if(memcmp(p, ctx->peerVerifyData.data, ctx->peerVerifyData.length))
+ return;
+
+ ctx->secure_renegotiation_received = true;
+}
+
+
+static OSStatus
+SSLProcessServerHelloExtensions(SSLContext *ctx, UInt16 extensionsLen, UInt8 *p)
+{
+ Boolean got_secure_renegotiation = false;
+ UInt16 remaining;
+
+ if(extensionsLen<2) {
+ sslErrorLog("SSLProcessHelloExtensions: need a least 2 bytes\n");
+ return errSSLProtocol;
+ }
+
+ remaining = SSLDecodeInt(p, 2); p+=2;
+ extensionsLen -=2;
+
+ /* remaining = number of bytes remaining to process according to buffer data */
+ /* extensionsLen = number of bytes in the buffer */
+
+ if(remaining>extensionsLen) {
+ sslErrorLog("SSLProcessHelloExtensions: ext len error 1\n");
+ return errSSLProtocol;
+ }
+
+ if(remaining<extensionsLen) {
+ sslErrorLog("Warning: SSLProcessServerHelloExtensions: Too many bytes\n");
+ }
+
+ while(remaining) {
+ UInt16 extType;
+ UInt16 extLen;
+
+ if (remaining<4) {
+ sslErrorLog("SSLProcessHelloExtensions: ext len error\n");
+ return errSSLProtocol;
+ }
+
+ extType = SSLDecodeInt(p, 2); p+=2;
+ extLen = SSLDecodeInt(p, 2); p+=2;
+
+ if (remaining<(4+extLen)) {
+ sslErrorLog("SSLProcessHelloExtension: ext len error 2\n");
+ return errSSLProtocol;
+ }
+ remaining -= (4+extLen);
+
+ switch (extType) {
+ case SSL_HE_SecureRenegotation:
+ if(got_secure_renegotiation)
+ return errSSLProtocol; /* Fail if we already processed one */
+ got_secure_renegotiation = true;
+ SSLProcessServerHelloExtension_SecureRenegotiation(ctx, extLen, p);
+ break;
+ default:
+ /*
+ Do nothing for other extensions. Per RFC 5246, we should (MUST) error
+ if we received extensions we didnt specify in the Client Hello.
+ Client should also abort handshake if multiple extensions of the same
+ type are found
+ */
+ break;
+ }
+ p+=extLen;
+ }
+
+ return noErr;
+}
+
+OSStatus
+SSLProcessServerHello(SSLBuffer message, SSLContext *ctx)
+{ OSStatus err;
+ SSLProtocolVersion protocolVersion, negVersion;
+ size_t sessionIDLen;
+ size_t extensionsLen;
+ UInt8 *p;
+
+ assert(ctx->protocolSide == kSSLClientSide);
+
+ if (message.length < 38) {
+ sslErrorLog("SSLProcessServerHello: msg len error\n");
+ return errSSLProtocol;
+ }
+ p = message.data;
+
+ protocolVersion = (SSLProtocolVersion)SSLDecodeInt(p, 2);
+ p += 2;
+ /* FIXME this should probably send appropriate alerts */
+ err = sslVerifyProtVersion(ctx, protocolVersion, &negVersion);
+ if(err) {
+ return err;
+ }
+ ctx->negProtocolVersion = negVersion;
+ switch(negVersion) {
+ case SSL_Version_3_0:
+ ctx->sslTslCalls = &Ssl3Callouts;
+ break;
+ case TLS_Version_1_0:
+ case TLS_Version_1_1:
+ case DTLS_Version_1_0:
+ ctx->sslTslCalls = &Tls1Callouts;
+ break;
+ case TLS_Version_1_2:
+ ctx->sslTslCalls = &Tls12Callouts;
+ break;
+ default:
+ return errSSLNegotiation;
+ }
+ sslLogNegotiateDebug("===SSL3 client: negVersion is %d_%d",
+ (negVersion >> 8) & 0xff, negVersion & 0xff);
+
+ memcpy(ctx->serverRandom, p, 32);
+ p += 32;
+
+ sessionIDLen = *p++;
+ if (message.length < (38 + sessionIDLen)) {
+ sslErrorLog("SSLProcessServerHello: msg len error 2\n");
+ return errSSLProtocol;
+ }
+ if (sessionIDLen > 0 && ctx->peerID.data != 0)
+ { /* Don't die on error; just treat it as an uncached session */
+ if (ctx->sessionID.data)
+ SSLFreeBuffer(&ctx->sessionID, ctx);
+ err = SSLAllocBuffer(&ctx->sessionID, sessionIDLen, ctx);
+ if (err == 0)
+ memcpy(ctx->sessionID.data, p, sessionIDLen);
+ }
+ p += sessionIDLen;
+
+ ctx->selectedCipher = (UInt16)SSLDecodeInt(p,2);
+ sslLogNegotiateDebug("===ssl3: server requests cipherKind %x",
+ (unsigned)ctx->selectedCipher);
+ p += 2;
+ if ((err = FindCipherSpec(ctx)) != 0) {
+ return err;
+ }
+
+ if (*p++ != 0) /* Compression */
+ return unimpErr;
+
+ /* Process ServerHello extensions */
+ extensionsLen = message.length - (38 + sessionIDLen);
+
+ if(extensionsLen) {
+ err = SSLProcessServerHelloExtensions(ctx, extensionsLen, p);
+ if(err)
+ return err;
+ }
+
+ /* RFC 5746: Make sure the renegotiation is secure */
+ if(ctx->secure_renegotiation && !ctx->secure_renegotiation_received)
+ return errSSLNegotiation;
+
+ if(ctx->secure_renegotiation_received)
+ ctx->secure_renegotiation = true;
+
+ /*
+ * Note: the server MAY send a SSL_HE_EC_PointFormats extension if
+ * we've negotiated an ECDSA ciphersuite...but
+ * a) the provided format list MUST contain SSL_PointFormatUncompressed per
+ * RFC 4492 5.2; and
+ * b) The uncompressed format is the only one we support.
+ *
+ * Thus we drop a possible incoming SSL_HE_EC_PointFormats extension here.
+ * IF we ever support other point formats, we have to parse the extension
+ * to see what the server supports.
+ */
+ return noErr;
+}
+
+OSStatus
+SSLEncodeClientHello(SSLRecord *clientHello, SSLContext *ctx)
+{
+ size_t length;
+ unsigned i;
+ OSStatus err;
+ unsigned char *p;
+ SSLBuffer sessionIdentifier = { 0, NULL };
+ size_t sessionIDLen;
+ size_t sessionTicketLen = 0;
+ size_t serverNameLen = 0;
+ size_t pointFormatLen = 0;
+ size_t suppCurveLen = 0;
+ size_t signatureAlgorithmsLen = 0;
+ size_t totalExtenLen = 0;
+ UInt16 numCipherSuites;
+ int head;
+
+ assert(ctx->protocolSide == kSSLClientSide);
+
+ clientHello->contents.length = 0;
+ clientHello->contents.data = NULL;
+
+ sessionIDLen = 0;
+ if (ctx->resumableSession.data != 0)
+ { if ((err = SSLRetrieveSessionID(ctx->resumableSession,
+ &sessionIdentifier, ctx)) != 0)
+ { return err;
+ }
+ sessionIDLen = sessionIdentifier.length;
+ }
+
+ /*
+ * Since we're not in SSLv2 compatibility mode, only count non-SSLv2 ciphers.
+ */
+#if ENABLE_SSLV2
+ numCipherSuites = ctx->numValidNonSSLv2Specs;
+#else
+ numCipherSuites = ctx->numValidCipherSuites;
+#endif
+
+ /* RFC 5746 : add the fake ciphersuite unless we are including the extension */
+ if(!ctx->secure_renegotiation)
+ numCipherSuites+=1;
+
+ length = 39 + 2*numCipherSuites + sessionIDLen;
+
+ err = sslGetMaxProtVersion(ctx, &clientHello->protocolVersion);
+ if(err) {
+ /* we don't have a protocol enabled */
+ goto err_exit;
+ }
+
+ /* RFC 5746: If are starting a new handshake, so we didnt received this yet */
+ ctx->secure_renegotiation_received = false;
+
+ /* If we already negotiated the protocol version previously,
+ we should just use that */
+ if(ctx->negProtocolVersion != SSL_Version_Undetermined) {
+ clientHello->protocolVersion = ctx->negProtocolVersion;
+ }
+
+#if ENABLE_DTLS
+ if(clientHello->protocolVersion == DTLS_Version_1_0) {
+ /* extra space for cookie */
+ /* TODO: cookie len - 0 for now */
+ length += 1 + ctx->dtlsCookie.length;
+ sslLogNegotiateDebug("==DTLS Hello: len=%lu\n", length);
+ }
+ /* Because of the way the version number for DTLS is encoded,
+ the following code mean that you can use extensions with DTLS... */
+#endif /* ENABLE_DTLS */
+
+ /* RFC 5746: We add the extension only for renegotiation ClientHello */
+ if(ctx->secure_renegotiation) {
+ totalExtenLen += 2 + /* extension type */
+ 2 + /* extension length */
+ 1 + /* lenght of renegotiated_conection (client verify data) */
+ ctx->ownVerifyData.length;
+ }
+
+ /* prepare for optional ClientHello extensions */
+ if((clientHello->protocolVersion >= TLS_Version_1_0) &&
+ (ctx->peerDomainName != NULL) &&
+ (ctx->peerDomainNameLen != 0)) {
+ serverNameLen = 2 + /* extension type */
+ 2 + /* 2-byte vector length, extension_data */
+ 2 + /* length of server_name_list */
+ 1 + /* length of name_type */
+ 2 + /* length of HostName */
+ ctx->peerDomainNameLen;
+ totalExtenLen += serverNameLen;
+ }
+ if(ctx->sessionTicket.length) {
+ sessionTicketLen = 2 + /* extension type */
+ 2 + /* 2-byte vector length, extension_data */
+ ctx->sessionTicket.length;
+ totalExtenLen += sessionTicketLen;
+ }
+ if((clientHello->protocolVersion >= TLS_Version_1_0) &&
+ (ctx->ecdsaEnable)) {
+ /* Two more extensions: point format, supported curves */
+ pointFormatLen = 2 + /* extension type */
+ 2 + /* 2-byte vector length, extension_data */
+ 1 + /* length of the ec_point_format_list */
+ 1; /* the single format we support */
+ suppCurveLen = 2 + /* extension type */
+ 2 + /* 2-byte vector length, extension_data */
+ 2 + /* length of the elliptic_curve_list */
+ (2 * ctx->ecdhNumCurves); /* each curve is 2 bytes */
+ totalExtenLen += (pointFormatLen + suppCurveLen);
+ }
+ if(ctx->isDTLS
+ ? clientHello->protocolVersion < DTLS_Version_1_0
+ : clientHello->protocolVersion >= TLS_Version_1_2) {
+ signatureAlgorithmsLen = 2 + /* extension type */
+ 2 + /* 2-byte vector length, extension_data */
+ 2 + /* length of signatureAlgorithms list */
+ 2 * (ctx->ecdsaEnable ? 5 : 3); //FIXME: 5:3 should not be hardcoded here.
+ totalExtenLen += signatureAlgorithmsLen;
+ }
+ if(totalExtenLen != 0) {
+ /*
+ * Total length extensions have to fit in a 16 bit field...
+ */
+ if(totalExtenLen > 0xffff) {
+ sslErrorLog("Total extensions length EXCEEDED\n");
+ totalExtenLen = 0;
+ sessionTicketLen = 0;
+ serverNameLen = 0;
+ pointFormatLen = 0;
+ suppCurveLen = 0;
+ signatureAlgorithmsLen = 0;
+ }
+ else {
+ /* add length of total length plus lengths of extensions */
+ length += (totalExtenLen + 2);
+ }
+ }
+
+ clientHello->contentType = SSL_RecordTypeHandshake;
+ head = SSLHandshakeHeaderSize(clientHello);
+ if ((err = SSLAllocBuffer(&clientHello->contents, length + head, ctx)) != 0)
+ goto err_exit;
+
+ p = SSLEncodeHandshakeHeader(ctx, clientHello, SSL_HdskClientHello, length);
+
+ p = SSLEncodeInt(p, clientHello->protocolVersion, 2);
+
+ sslLogNegotiateDebug("===SSL3 client: proclaiming max protocol "
+ "%d_%d capable ONLY",
+ clientHello->protocolVersion >> 8, clientHello->protocolVersion & 0xff);
+ if ((err = SSLEncodeRandom(p, ctx)) != 0)
+ { goto err_exit;
+ }
+ memcpy(ctx->clientRandom, p, SSL_CLIENT_SRVR_RAND_SIZE);
+ p += 32;
+ *p++ = sessionIDLen; /* 1 byte vector length */
+ if (sessionIDLen > 0)
+ { memcpy(p, sessionIdentifier.data, sessionIDLen);
+ }
+ p += sessionIDLen;
+#if ENABLE_DTLS
+ if (clientHello->protocolVersion == DTLS_Version_1_0) {
+ /* TODO: Add the cookie ! Currently: size=0 -> no cookie */
+ *p++ = ctx->dtlsCookie.length;
+ if(ctx->dtlsCookie.length) {
+ memcpy(p, ctx->dtlsCookie.data, ctx->dtlsCookie.length);
+ p+=ctx->dtlsCookie.length;
+ }
+ sslLogNegotiateDebug("==DTLS Hello: cookie len = %d\n",ctx->dtlsCookie.length);
+ }
+#endif
+
+
+ p = SSLEncodeInt(p, 2*numCipherSuites, 2);
+ /* 2 byte long vector length */
+
+ /* RFC 5746 : add the fake ciphersuite unless we are including the extension */
+ if(!ctx->secure_renegotiation)
+ p = SSLEncodeInt(p, TLS_EMPTY_RENEGOTIATION_INFO_SCSV, 2);
+
+ for (i = 0; i<ctx->numValidCipherSuites; ++i) {
+#if ENABLE_SSLV2
+ if(CIPHER_SUITE_IS_SSLv2(ctx->validCipherSuites[i])) {
+ continue;
+ }
+#endif
+ sslLogNegotiateDebug("ssl3EncodeClientHello sending suite %x",
+ (unsigned)ctx->validCipherSuites[i]);
+ p = SSLEncodeInt(p, ctx->validCipherSuites[i], 2);
+ }
+ *p++ = 1; /* 1 byte long vector */
+ *p++ = 0; /* null compression */
+
+ /*
+ * Append ClientHello extensions.
+ */
+ if(totalExtenLen != 0) {
+ /* first, total length of all extensions */
+ p = SSLEncodeSize(p, totalExtenLen, 2);
+ }
+ if(ctx->secure_renegotiation){
+ assert(ctx->ownVerifyData.length<=255);
+ p = SSLEncodeInt(p, SSL_HE_SecureRenegotation, 2);
+ p = SSLEncodeSize(p, ctx->ownVerifyData.length+1, 2);
+ p = SSLEncodeSize(p, ctx->ownVerifyData.length, 1);
+ memcpy(p, ctx->ownVerifyData.data, ctx->ownVerifyData.length);
+ p += ctx->ownVerifyData.length;
+ }
+ if(sessionTicketLen) {
+ sslEapDebug("Adding %lu bytes of sessionTicket to ClientHello",
+ ctx->sessionTicket.length);
+ p = SSLEncodeInt(p, SSL_HE_SessionTicket, 2);
+ p = SSLEncodeSize(p, ctx->sessionTicket.length, 2);
+ memcpy(p, ctx->sessionTicket.data, ctx->sessionTicket.length);
+ p += ctx->sessionTicket.length;
+ }
+ if(serverNameLen) {
+ sslEapDebug("Specifying ServerNameIndication");
+ p = SSLEncodeInt(p, SSL_HE_ServerName, 2);
+ p = SSLEncodeSize(p, ctx->peerDomainNameLen + 5, 2);
+ p = SSLEncodeSize(p, ctx->peerDomainNameLen + 3, 2);
+ p = SSLEncodeInt(p, SSL_NT_HostName, 1);
+ p = SSLEncodeSize(p, ctx->peerDomainNameLen, 2);
+ memcpy(p, ctx->peerDomainName, ctx->peerDomainNameLen);
+ p += ctx->peerDomainNameLen;
+ }
+ if(suppCurveLen) {
+ UInt32 len = 2 * ctx->ecdhNumCurves;
+ unsigned dex;
+ p = SSLEncodeInt(p, SSL_HE_EllipticCurves, 2);
+ p = SSLEncodeSize(p, len+2, 2); /* length of extension data */
+ p = SSLEncodeSize(p, len, 2); /* length of elliptic_curve_list */
+ for(dex=0; dex<ctx->ecdhNumCurves; dex++) {
+ sslEcdsaDebug("+++ adding supported curves %u to ClientHello",
+ (unsigned)ctx->ecdhCurves[dex]);
+ p = SSLEncodeInt(p, ctx->ecdhCurves[dex], 2);
+ }
+ }
+ if(pointFormatLen) {
+ sslEcdsaDebug("+++ adding point format to ClientHello");
+ p = SSLEncodeInt(p, SSL_HE_EC_PointFormats, 2);
+ p = SSLEncodeSize(p, 2, 2); /* length of extension data */
+ p = SSLEncodeSize(p, 1, 1); /* length of ec_point_format_list */
+ p = SSLEncodeInt(p, SSL_PointFormatUncompressed, 1);
+ }
+ if (signatureAlgorithmsLen) {
+ sslEcdsaDebug("+++ adding signature algorithms to ClientHello");
+ /* TODO: Don't hardcode this */
+ /* We dont support SHA512 or SHA224 because we didnot implement the digest abstraction for those
+ and we dont keep a running hash for those.
+ We dont support SHA384/ECDSA because corecrypto ec does not support it with 256 bits curves */
+ UInt32 len = 2 * (ctx->ecdsaEnable ? 5 : 3); //FIXME: 5:3 should not be hardcoded here.
+ p = SSLEncodeInt(p, SSL_HE_SignatureAlgorithms, 2);
+ p = SSLEncodeSize(p, len+2, 2); /* length of extension data */
+ p = SSLEncodeSize(p, len, 2); /* length of extension data */
+ // p = SSLEncodeInt(p, SSL_HashAlgorithmSHA512, 1);
+ // p = SSLEncodeInt(p, SSL_SignatureAlgorithmRSA, 1);
+ p = SSLEncodeInt(p, SSL_HashAlgorithmSHA384, 1);
+ p = SSLEncodeInt(p, SSL_SignatureAlgorithmRSA, 1);
+ p = SSLEncodeInt(p, SSL_HashAlgorithmSHA256, 1);
+ p = SSLEncodeInt(p, SSL_SignatureAlgorithmRSA, 1);
+ // p = SSLEncodeInt(p, SSL_HashAlgorithmSHA224, 1);
+ // p = SSLEncodeInt(p, SSL_SignatureAlgorithmRSA, 1);
+ p = SSLEncodeInt(p, SSL_HashAlgorithmSHA1, 1);
+ p = SSLEncodeInt(p, SSL_SignatureAlgorithmRSA, 1);
+ if (ctx->ecdsaEnable) {
+ // p = SSLEncodeInt(p, SSL_HashAlgorithmSHA512, 1);
+ // p = SSLEncodeInt(p, SSL_SignatureAlgorithmECDSA, 1);
+ // p = SSLEncodeInt(p, SSL_HashAlgorithmSHA384, 1);
+ // p = SSLEncodeInt(p, SSL_SignatureAlgorithmECDSA, 1);
+ p = SSLEncodeInt(p, SSL_HashAlgorithmSHA256, 1);
+ p = SSLEncodeInt(p, SSL_SignatureAlgorithmECDSA, 1);
+ // p = SSLEncodeInt(p, SSL_HashAlgorithmSHA224, 1);
+ // p = SSLEncodeInt(p, SSL_SignatureAlgorithmECDSA, 1);
+ p = SSLEncodeInt(p, SSL_HashAlgorithmSHA1, 1);
+ p = SSLEncodeInt(p, SSL_SignatureAlgorithmECDSA, 1);
+ }
+ }
+
+ sslLogNegotiateDebug("Client Hello : data=%p p=%p len=%08x\n", clientHello->contents.data, p, clientHello->contents.length);
+
+ assert(p == clientHello->contents.data + clientHello->contents.length);
+
+ if ((err = SSLInitMessageHashes(ctx)) != 0)
+ goto err_exit;
+
+err_exit:
+ if (err != 0) {
+ SSLFreeBuffer(&clientHello->contents, ctx);
+ }
+ SSLFreeBuffer(&sessionIdentifier, ctx);
+
+ return err;
+}
+
+OSStatus
+SSLProcessClientHello(SSLBuffer message, SSLContext *ctx)
+{ OSStatus err;
+ SSLProtocolVersion negVersion;
+ UInt16 cipherListLen, cipherCount, desiredSuite, cipherSuite;
+ UInt8 sessionIDLen, compressionCount;
+ UInt8 *charPtr;
+ unsigned i;
+ UInt8 *eom; /* end of message */
+
+ if (message.length < 41) {
+ sslErrorLog("SSLProcessClientHello: msg len error 1\n");
+ return errSSLProtocol;
+ }
+ charPtr = message.data;
+ eom = charPtr + message.length;
+ ctx->clientReqProtocol = (SSLProtocolVersion)SSLDecodeInt(charPtr, 2);
+ charPtr += 2;
+ err = sslVerifyProtVersion(ctx, ctx->clientReqProtocol, &negVersion);
+ if(err) {
+ sslErrorLog("SSLProcessClientHello: protocol version error %04x - %04x\n", ctx->clientReqProtocol, negVersion);
+ return err;
+ }
+ switch(negVersion) {
+ case SSL_Version_3_0:
+ ctx->sslTslCalls = &Ssl3Callouts;
+ break;
+ case TLS_Version_1_0:
+ case TLS_Version_1_1:
+ case DTLS_Version_1_0:
+ ctx->sslTslCalls = &Tls1Callouts;
+ break;
+ case TLS_Version_1_2:
+ ctx->sslTslCalls = &Tls12Callouts;
+ break;
+ default:
+ return errSSLNegotiation;
+ }
+ ctx->negProtocolVersion = negVersion;
+ sslLogNegotiateDebug("===SSL3 server: negVersion is %d_%d",
+ negVersion >> 8, negVersion & 0xff);
+
+ memcpy(ctx->clientRandom, charPtr, SSL_CLIENT_SRVR_RAND_SIZE);
+ charPtr += 32;
+ sessionIDLen = *(charPtr++);
+ if (message.length < (unsigned)(41 + sessionIDLen)) {
+ sslErrorLog("SSLProcessClientHello: msg len error 2\n");
+ return errSSLProtocol;
+ }
+ /* FIXME peerID is never set on server side.... */
+ if (sessionIDLen > 0 && ctx->peerID.data != 0)
+ { /* Don't die on error; just treat it as an uncacheable session */
+ err = SSLAllocBuffer(&ctx->sessionID, sessionIDLen, ctx);
+ if (err == 0)
+ memcpy(ctx->sessionID.data, charPtr, sessionIDLen);
+ }
+ charPtr += sessionIDLen;
+
+#if ENABLE_DTLS
+ /* TODO: actually do something with this cookie */
+ if(negVersion==DTLS_Version_1_0) {
+ UInt8 cookieLen = *charPtr++;
+
+ sslLogNegotiateDebug("cookieLen=%d\n", cookieLen);
+
+ if((ctx->dtlsCookie.length==0) || ((cookieLen==ctx->dtlsCookie.length) && (memcmp(ctx->dtlsCookie.data, charPtr, cookieLen)==0)))
+ {
+ ctx->cookieVerified=true;
+ } else {
+ ctx->cookieVerified=false;
+ }
+
+ charPtr+=cookieLen;
+ }
+
+ /* TODO: if we are about to send a HelloVerifyRequest, we probably dont need to process the cipherspecs */
+#endif
+
+ cipherListLen = (UInt16)SSLDecodeInt(charPtr, 2);
+ /* Count of cipherSuites, must be even & >= 2 */
+ charPtr += 2;
+ if((charPtr + cipherListLen) > eom) {
+ sslErrorLog("SSLProcessClientHello: msg len error 5\n");
+ return errSSLProtocol;
+ }
+ if ((cipherListLen & 1) ||
+ (cipherListLen < 2) ||
+ (message.length < (unsigned)(39 + sessionIDLen + cipherListLen))) {
+ sslErrorLog("SSLProcessClientHello: msg len error 3\n");
+ return errSSLProtocol;
+ }
+ cipherCount = cipherListLen/2;
+ cipherSuite = 0xFFFF; /* No match marker */
+ while (cipherSuite == 0xFFFF && cipherCount--)
+ { desiredSuite = (UInt16)SSLDecodeInt(charPtr, 2);
+ charPtr += 2;
+ for (i = 0; i <ctx->numValidCipherSuites; i++)
+ { if (ctx->validCipherSuites[i] == desiredSuite)
+ { cipherSuite = desiredSuite;
+ break;
+ }
+ }
+ }
+
+ if (cipherSuite == 0xFFFF)
+ return errSSLNegotiation;
+ charPtr += 2 * cipherCount; /* Advance past unchecked cipherCounts */
+ ctx->selectedCipher = cipherSuite;
+ /* validate cipher later, after we get possible sessionTicket */
+
+ compressionCount = *(charPtr++);
+ if ((compressionCount < 1) ||
+ (message.length <
+ (unsigned)(38 + sessionIDLen + cipherListLen + compressionCount))) {
+ sslErrorLog("SSLProcessClientHello: msg len error 4\n");
+ return errSSLProtocol;
+ }
+ /* Ignore list; we're doing null */
+
+ /*
+ * Handle ClientHello extensions.
+ */
+ /* skip compression list */
+ charPtr += compressionCount;
+ if(charPtr < eom) {
+ ptrdiff_t remLen = eom - charPtr;
+ UInt32 totalExtensLen;
+ UInt32 extenType;
+ UInt32 extenLen;
+ if(remLen < 6) {
+ /*
+ * Not enough for extension type and length, but not an error...
+ * skip it and proceed.
+ */
+ sslEapDebug("SSLProcessClientHello: too small for any extension");
+ goto proceed;
+ }
+ totalExtensLen = SSLDecodeInt(charPtr, 2);
+ charPtr += 2;
+ if((charPtr + totalExtensLen) > eom) {
+ sslEapDebug("SSLProcessClientHello: too small for specified total_extension_length");
+ goto proceed;
+ }
+ while(charPtr < eom) {
+ extenType = SSLDecodeInt(charPtr, 2);
+ charPtr += 2;
+ extenLen = SSLDecodeInt(charPtr, 2);
+ charPtr += 2;
+ if((charPtr + extenLen) > eom) {
+ sslEapDebug("SSLProcessClientHello: too small for specified extension_length");
+ break;
+ }
+ switch(extenType) {
+#if SSL_PAC_SERVER_ENABLE
+
+ case SSL_HE_SessionTicket:
+ SSLFreeBuffer(&ctx->sessionTicket, NULL);
+ SSLCopyBufferFromData(charPtr, extenLen, &ctx->sessionTicket);
+ sslEapDebug("Saved %lu bytes of sessionTicket from ClientHello",
+ (unsigned long)extenLen);
+ break;
+#endif
+ case SSL_HE_ServerName:
+ {
+ /*
+ * This is for debug only (it's disabled for Deployment builds).
+ * Someday, I imagine we'll have a getter in the API to get this info.
+ */
+ UInt8 *cp = charPtr;
+ UInt32 v = SSLDecodeInt(cp, 2);
+ cp += 2;
+ sslEapDebug("SSL_HE_ServerName: length of server_name_list %lu",
+ (unsigned long)v);
+ v = SSLDecodeInt(cp, 1);
+ cp++;
+ sslEapDebug("SSL_HE_ServerName: name_type %lu", (unsigned long)v);
+ v = SSLDecodeInt(cp, 2);
+ cp += 2;
+ sslEapDebug("SSL_HE_ServerName: length of HostName %lu",
+ (unsigned long)v);
+ char hostString[v + 1];
+ memmove(hostString, cp, v);
+ hostString[v] = '\0';
+ sslEapDebug("SSL_HE_ServerName: ServerName '%s'", hostString);
+ break;
+ }
+ case SSL_HE_SignatureAlgorithms:
+ {
+ UInt8 *cp = charPtr, *end = charPtr + extenLen;
+ UInt32 sigAlgsSize = SSLDecodeInt(cp, 2);
+ cp += 2;
+
+ if (extenLen != sigAlgsSize + 2 || extenLen & 1 || sigAlgsSize & 1) {
+ sslEapDebug("SSL_HE_SignatureAlgorithms: odd length of signature algorithms list %lu %lu",
+ (unsigned long)extenLen, (unsigned long)sigAlgsSize);
+ break;
+ }
+
+ ctx->numClientSigAlgs = sigAlgsSize / 2;
+ if(ctx->clientSigAlgs != NULL) {
+ sslFree(ctx->clientSigAlgs);
+ }
+ ctx->clientSigAlgs = (SSLSignatureAndHashAlgorithm *)
+ sslMalloc((ctx->numClientSigAlgs) * sizeof(SSLSignatureAndHashAlgorithm));
+ for(i=0; i<ctx->numClientSigAlgs; i++) {
+ /* TODO: Validate hash and signature fields. */
+ ctx->clientSigAlgs[i].hash = *cp++;
+ ctx->clientSigAlgs[i].signature = *cp++;
+ sslLogNegotiateDebug("===Client specifies sigAlg %d %d",
+ ctx->clientSigAlgs[i].hash,
+ ctx->clientSigAlgs[i].signature);
+ }
+ assert(cp==end);
+ break;
+ }
+ default:
+ sslEapDebug("SSLProcessClientHello: unknown extenType (%lu)",
+ (unsigned long)extenType);
+ break;
+ }
+ charPtr += extenLen;
+ }
+ }
+proceed:
+ if ((err = FindCipherSpec(ctx)) != 0) {
+ return err;
+ }
+ sslLogNegotiateDebug("ssl3 server: selecting cipherKind 0x%x", (unsigned)ctx->selectedCipher);
+ if ((err = SSLInitMessageHashes(ctx)) != 0)
+ return err;
+
+ return noErr;
+}
+
+OSStatus
+SSLEncodeRandom(unsigned char *p, SSLContext *ctx)
+{ SSLBuffer randomData;
+ OSStatus err;
+ uint32_t now;
+
+ if ((err = sslTime(&now)) != 0)
+ return err;
+ SSLEncodeInt(p, now, 4);
+ randomData.data = p+4;
+ randomData.length = 28;
+ if((err = sslRand(ctx, &randomData)) != 0)
+ return err;
+ return noErr;
+}
+
+OSStatus
+SSLInitMessageHashes(SSLContext *ctx)
+{ OSStatus err;
+
+ if ((err = CloseHash(&SSLHashSHA1, &ctx->shaState, ctx)) != 0)
+ return err;
+ if ((err = CloseHash(&SSLHashMD5, &ctx->md5State, ctx)) != 0)
+ return err;
+ if ((err = CloseHash(&SSLHashSHA256, &ctx->sha256State, ctx)) != 0)
+ return err;
+ if ((err = CloseHash(&SSLHashSHA384, &ctx->sha512State, ctx)) != 0)
+ return err;
+ if ((err = ReadyHash(&SSLHashSHA1, &ctx->shaState, ctx)) != 0)
+ return err;
+ if ((err = ReadyHash(&SSLHashMD5, &ctx->md5State, ctx)) != 0)
+ return err;
+ if ((err = ReadyHash(&SSLHashSHA256, &ctx->sha256State, ctx)) != 0)
+ return err;
+ if ((err = ReadyHash(&SSLHashSHA384, &ctx->sha512State, ctx)) != 0)
+ return err;
+ return noErr;
+}