Files
mars-matrixssl/matrixssl/hsDecode.c
2016-05-03 17:36:14 -07:00

3094 lines
98 KiB
C

/**
* @file hsDecode.c
* @version $Format:%h%d$
*
* SSL/TLS handshake message parsing
*/
/*
* Copyright (c) 2013-2016 INSIDE Secure Corporation
* Copyright (c) PeerSec Networks, 2002-2011
* All Rights Reserved
*
* The latest version of this code is available at http://www.matrixssl.org
*
* This software is open source; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This General Public License does NOT permit incorporating this software
* into proprietary programs. If you are unable to comply with the GPL, a
* commercial license for this software may be purchased from INSIDE at
* http://www.insidesecure.com/
*
* This program is distributed in WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* http://www.gnu.org/copyleft/gpl.html
*/
/******************************************************************************/
#include "matrixsslApi.h"
#define USE_ECC_EPHEMERAL_KEY_CACHE
/* Errors from these routines must either be MATRIXSSL_ERROR or PS_MEM_FAIL */
/******************************************************************************/
#ifdef USE_SERVER_SIDE_SSL
int32 parseClientHello(ssl_t *ssl, unsigned char **cp, unsigned char *end)
{
unsigned char *suiteStart, *suiteEnd;
unsigned char compareMin, compareMaj, suiteLen, compLen, serverHighestMinor;
uint32 resumptionOnTrack, cipher = 0;
int32 rc;
unsigned char *c;
#ifdef USE_ECC_CIPHER_SUITE
const psEccCurve_t *curve;
#endif
#if defined(USE_ECC) || defined(REQUIRE_DH_PARAMS)
void *pkiData = ssl->userPtr;
#endif
c = *cp;
/* First two bytes are the highest supported major and minor SSL versions */
psTraceHs(">>> Server parsing CLIENT_HELLO\n");
#ifdef USE_MATRIXSSL_STATS
matrixsslUpdateStat(ssl, CH_RECV_STAT, 1);
#endif
if (end - c < 2) {
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("Invalid ssl header version length\n");
return MATRIXSSL_ERROR;
}
ssl->reqMajVer = *c; c++;
ssl->reqMinVer = *c; c++;
/* Client should always be sending highest supported protocol. Server
will reply with a match or a lower version if enabled (or forced). */
if (ssl->majVer != 0) {
/* If our forced server version is a later protocol than their
request, we have to exit */
if (ssl->reqMinVer < ssl->minVer) {
ssl->err = SSL_ALERT_PROTOCOL_VERSION;
psTraceInfo("Won't support client's SSL version\n");
return MATRIXSSL_ERROR;
}
#ifdef USE_DTLS
if (ssl->flags & SSL_FLAGS_DTLS) {
/* DTLS specfication somehow assigned minimum version of DTLS 1.0
as 255 so there was nowhere to go but down in DTLS 1.1 so
that is 253 and requires the opposite test from above */
if (ssl->reqMinVer > ssl->minVer) {
ssl->err = SSL_ALERT_PROTOCOL_VERSION;
psTraceInfo("Won't support client's DTLS version\n");
return MATRIXSSL_ERROR;
}
}
#endif
/* Otherwise we just set our forced version to act like it was
what the client wanted in order to move through the standard
negotiation. */
compareMin = ssl->minVer;
compareMaj = ssl->majVer;
/* Set the highest version to the version explicitly set */
serverHighestMinor = ssl->minVer;
} else {
compareMin = ssl->reqMinVer;
compareMaj = ssl->reqMajVer;
/* If no explicit version was set for the server, use the highest supported */
serverHighestMinor = TLS_HIGHEST_MINOR;
}
if (compareMaj >= SSL3_MAJ_VER) {
ssl->majVer = compareMaj;
#ifdef USE_TLS
if (compareMin >= TLS_MIN_VER) {
#ifndef DISABLE_TLS_1_0
ssl->minVer = TLS_MIN_VER;
ssl->flags |= SSL_FLAGS_TLS;
#endif
#ifdef USE_TLS_1_1 /* TLS_1_1 */
if (compareMin >= TLS_1_1_MIN_VER) {
#ifndef DISABLE_TLS_1_1
ssl->minVer = TLS_1_1_MIN_VER;
ssl->flags |= SSL_FLAGS_TLS_1_1 | SSL_FLAGS_TLS;
#endif
}
#ifdef USE_TLS_1_2
if (compareMin == TLS_1_2_MIN_VER) {
ssl->minVer = TLS_1_2_MIN_VER;
ssl->flags |= SSL_FLAGS_TLS_1_2 | SSL_FLAGS_TLS_1_1 | SSL_FLAGS_TLS;
}
#ifdef USE_DTLS
if (ssl->flags & SSL_FLAGS_DTLS) {
if (compareMin == DTLS_1_2_MIN_VER) {
ssl->minVer = DTLS_1_2_MIN_VER;
}
}
#endif
#endif /* USE_TLS_1_2 */
#endif /* USE_TLS_1_1 */
if (ssl->minVer == 0) {
/* TLS versions are disabled. Go SSLv3 if available. */
#ifdef DISABLE_SSLV3
ssl->err = SSL_ALERT_PROTOCOL_VERSION;
psTraceInfo("Can't support client's SSL version\n");
return MATRIXSSL_ERROR;
#else
ssl->minVer = SSL3_MIN_VER;
#endif
}
} else if (compareMin == 0) {
#ifdef DISABLE_SSLV3
ssl->err = SSL_ALERT_PROTOCOL_VERSION;
psTraceInfo("Client wanted to talk SSLv3 but it's disabled\n");
return MATRIXSSL_ERROR;
#else
ssl->minVer = SSL3_MIN_VER;
#endif /* DISABLE_SSLV3 */
}
#ifdef USE_DTLS
if (ssl->flags & SSL_FLAGS_DTLS) {
if (compareMin < DTLS_1_2_MIN_VER) {
ssl->err = SSL_ALERT_PROTOCOL_VERSION;
psTraceInfo("Error: incorrect DTLS required version\n");
return MATRIXSSL_ERROR;
}
ssl->minVer = DTLS_MIN_VER;
#ifdef USE_TLS_1_2
if (compareMin == DTLS_1_2_MIN_VER) {
ssl->flags |= SSL_FLAGS_TLS_1_2 | SSL_FLAGS_TLS_1_1 | SSL_FLAGS_TLS;
ssl->minVer = DTLS_1_2_MIN_VER;
}
#ifdef USE_DTLS
if (ssl->flags & SSL_FLAGS_DTLS) {
if (compareMin == DTLS_1_2_MIN_VER) {
ssl->minVer = DTLS_1_2_MIN_VER;
}
}
#endif /* USE_DTLS */
#endif /* USE_TLS_1_2 */
}
#endif /* USE_DTLS */
#else
ssl->minVer = SSL3_MIN_VER;
#endif /* USE_TLS */
} else {
ssl->err = SSL_ALERT_PROTOCOL_VERSION;
psTraceIntInfo("Unsupported ssl version: %d\n", compareMaj);
return MATRIXSSL_ERROR;
}
if (ssl->rec.majVer > SSL2_MAJ_VER) {
/* Next is a 32 bytes of random data for key generation
and a single byte with the session ID length */
if (end - c < SSL_HS_RANDOM_SIZE + 1) {
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
psTraceIntInfo("Invalid length of random data %d\n",
(int32)(end - c));
return MATRIXSSL_ERROR;
}
memcpy(ssl->sec.clientRandom, c, SSL_HS_RANDOM_SIZE);
c += SSL_HS_RANDOM_SIZE;
ssl->sessionIdLen = *c; c++; /* length verified with + 1 above */
/* If a session length was specified, the client is asking to
resume a previously established session to speed up the handshake */
if (ssl->sessionIdLen > 0) {
if (ssl->sessionIdLen > SSL_MAX_SESSION_ID_SIZE ||
end - c < ssl->sessionIdLen) {
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
#ifdef USE_MATRIXSSL_STATS
matrixsslUpdateStat(ssl, FAILED_RESUMPTIONS_STAT, 1);
#endif
return MATRIXSSL_ERROR;
}
memcpy(ssl->sessionId, c, ssl->sessionIdLen);
c += ssl->sessionIdLen;
} else {
/* Always clear the RESUMED flag if no client session id
It may be re-enabled if a client session ticket extension is recvd */
ssl->flags &= ~SSL_FLAGS_RESUMED;
}
#ifdef USE_DTLS
/* If DTLS is enabled, make sure we received a valid cookie in the
CLIENT_HELLO message. */
if (ssl->flags & SSL_FLAGS_DTLS) {
uint16_t cookie_len;
/* Next field is the cookie length */
if (end - c < 1) {
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("Cookie length not provided\n");
return MATRIXSSL_ERROR;
}
/** Calculate what we expect the cookie should be by hashing the
client_hello data up to this point:
2 byte version + 1 byte session_id_len +
session_id + client_random
@future The creation of the cookie should ideally take some
IP Tuple information about the client into account.
@impl MatrixSSL sends a zero length cookie on re-handshake, but
other implementations may not, so this allows either
to be supported.
*/
cookie_len = 3 + ssl->sessionIdLen + SSL_HS_RANDOM_SIZE;
if (dtlsComputeCookie(ssl, c - cookie_len, cookie_len) < 0) {
ssl->err = SSL_ALERT_INTERNAL_ERROR;
psTraceInfo("Invalid cookie length\n");
return MATRIXSSL_ERROR;
}
cookie_len = *c++;
if (cookie_len > 0) {
if (end - c < cookie_len || cookie_len != DTLS_COOKIE_SIZE) {
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
psTraceInfo("Invalid cookie length\n");
return MATRIXSSL_ERROR;
}
if (memcmpct(c, ssl->srvCookie, DTLS_COOKIE_SIZE) != 0) {
/* Cookie mismatch. Error to avoid possible DOS */
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
psTraceInfo("Cookie mismatch\n");
return MATRIXSSL_ERROR;
}
c += DTLS_COOKIE_SIZE;
} else {
/* If client sent an empty cookie, and we're not secure
yet, set the hsState to encode a HELLO_VERIFY message to
the client, which will provide a new cookie. */
if (!(ssl->flags & SSL_FLAGS_READ_SECURE)) {
ssl->hsState = SSL_HS_CLIENT_HELLO;
c = end;
*cp = c;
/* Clear session so it will be found again when the cookie
clientHello message comes in next */
if (ssl->flags & SSL_FLAGS_RESUMED) {
matrixClearSession(ssl, 0);
}
/* Will cause HELLO_VERIFY to be encoded */
return SSL_PROCESS_DATA;
}
/** No cookie provided on already secure connection.
@impl This is a re-handshake case. MatrixSSL lets it slide
since we're already authenticated. */
}
}
#endif /* USE_DTLS */
/* Next is the two byte cipher suite list length, network byte order.
It must not be zero, and must be a multiple of two. */
if (end - c < 2) {
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("Invalid cipher suite list length\n");
return MATRIXSSL_ERROR;
}
suiteLen = *c << 8; c++;
suiteLen += *c; c++;
/* Save aside. We're going to come back after extensions are
parsed and choose a cipher suite */
suiteStart = c;
if (suiteLen <= 0 || suiteLen & 1) {
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceIntInfo("Unable to parse cipher suite list: %d\n",
suiteLen);
return MATRIXSSL_ERROR;
}
/* Now is 'suiteLen' bytes of the supported cipher suite list,
listed in order of preference. Loop through and find the
first cipher suite we support. */
if (end - c < suiteLen) {
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("Malformed clientHello message\n");
return MATRIXSSL_ERROR;
}
/* Do want to make one entire pass of the cipher suites now
to search for SCSV if secure rehandshakes are on */
suiteEnd = c + suiteLen;
while (c < suiteEnd) {
cipher = *c << 8; c++;
cipher += *c; c++;
#ifdef ENABLE_SECURE_REHANDSHAKES
if (ssl->myVerifyDataLen == 0) {
if (cipher == TLS_EMPTY_RENEGOTIATION_INFO_SCSV) {
ssl->secureRenegotiationFlag = PS_TRUE;
}
}
#endif
/** If TLS_FALLBACK_SCSV appears in ClientHello.cipher_suites and the
highest protocol version supported by the server is higher than
the version indicated in ClientHello.client_version, the server
MUST respond with a fatal inappropriate_fallback alert.
@see https://tools.ietf.org/html/rfc7507#section-3 */
if (cipher == TLS_FALLBACK_SCSV) {
if (ssl->reqMinVer < serverHighestMinor) {
ssl->err = SSL_ALERT_INAPPROPRIATE_FALLBACK;
psTraceInfo("Inappropriate version fallback\n");
return MATRIXSSL_ERROR;
}
}
}
/* Compression parameters */
if (end - c < 1) {
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("Invalid compression header length\n");
return MATRIXSSL_ERROR;
}
compLen = *c++;
if ((uint32)(end - c) < compLen) {
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("Invalid compression header length\n");
return MATRIXSSL_ERROR;
}
#ifdef USE_ZLIB_COMPRESSION
while (compLen > 0) {
/* Client wants it and we have it. Enable if we're not already
in a compression state. FUTURE: Could be re-handshake */
if (ssl->compression == 0) {
if (*c++ == 0x01) {
ssl->inflate.zalloc = NULL;
ssl->inflate.zfree = NULL;
ssl->inflate.opaque = NULL;
ssl->inflate.avail_in = 0;
ssl->inflate.next_in = NULL;
if (inflateInit(&ssl->inflate) != Z_OK) {
psTraceInfo("inflateInit fail. No compression\n");
} else {
ssl->deflate.zalloc = Z_NULL;
ssl->deflate.zfree = Z_NULL;
ssl->deflate.opaque = Z_NULL;
if (deflateInit(&ssl->deflate,
Z_DEFAULT_COMPRESSION) != Z_OK) {
psTraceInfo("deflateInit fail. No compression\n");
inflateEnd(&ssl->inflate);
} else {
/* Init good. Let's enable it */
ssl->compression = 1;
}
}
}
compLen--;
} else {
c++;
compLen--;
}
}
#else
c += compLen;
#endif
rc = parseClientHelloExtensions(ssl, &c, end - c);
if (rc < 0) {
/* Alerts are set by the extension parse */
return rc;
}
} else {
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
psTraceInfo("SSLV2 CLIENT_HELLO not supported.\n");
return MATRIXSSL_ERROR;
}
/* ClientHello should be the only one in the record. */
if (c != end) {
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
psTraceInfo("Invalid final client hello length\n");
return MATRIXSSL_ERROR;
}
/* Look up the session id for ssl session resumption. If found, we
load the pre-negotiated masterSecret and cipher.
A resumed request must meet the following restrictions:
The id must be present in the lookup table
The requested version must match the original version
The cipher suite list must contain the original cipher suite
*/
if (ssl->sessionIdLen > 0) {
/* Check if we are resuming on a session ticket first. It is
legal for a client to send both a session ID and a ticket. If
the ticket is used, the session ID should not be used at all */
#ifdef USE_STATELESS_SESSION_TICKETS
if ((ssl->flags & SSL_FLAGS_RESUMED) && (ssl->sid) &&
(ssl->sid->sessionTicketState == SESS_TICKET_STATE_USING_TICKET)){
goto SKIP_STANDARD_RESUMPTION;
}
#endif
if (matrixResumeSession(ssl) >= 0) {
ssl->flags &= ~SSL_FLAGS_CLIENT_AUTH;
ssl->flags |= SSL_FLAGS_RESUMED;
#ifdef USE_MATRIXSSL_STATS
matrixsslUpdateStat(ssl, RESUMPTIONS_STAT, 1);
#endif
} else {
ssl->flags &= ~SSL_FLAGS_RESUMED;
#ifdef USE_STATELESS_SESSION_TICKETS
/* Client MAY generate and include a Session ID in the
TLS ClientHello. If the server accepts the ticket
and the Session ID is not empty, then it MUST respond
with the same Session ID present in the ClientHello. Check
if client is even using the mechanism though */
if (ssl->sid) {
if (ssl->sid->sessionTicketState == SESS_TICKET_STATE_INIT) {
memset(ssl->sessionId, 0, SSL_MAX_SESSION_ID_SIZE);
ssl->sessionIdLen = 0;
} else {
/* This flag means we received a session we can't resume
but we have to send it back if we also get a ticket
later that we like */
ssl->extFlags.session_id = 1;
}
} else {
memset(ssl->sessionId, 0, SSL_MAX_SESSION_ID_SIZE);
ssl->sessionIdLen = 0;
}
#else
memset(ssl->sessionId, 0, SSL_MAX_SESSION_ID_SIZE);
ssl->sessionIdLen = 0;
#ifdef USE_MATRIXSSL_STATS
matrixsslUpdateStat(ssl, FAILED_RESUMPTIONS_STAT, 1);
#endif
#endif
}
}
#ifdef USE_STATELESS_SESSION_TICKETS
SKIP_STANDARD_RESUMPTION:
#endif
/* If resumed, confirm the cipher suite was sent. Otherwise, choose
the cipher suite based on what the user has loaded or what the user
sends in the pubkey callback */
if (ssl->flags & SSL_FLAGS_RESUMED) {
/* Have to rewalk ciphers and see if they sent the cipher. Can
move suiteStart safely since we'll be the last to use it */
suiteEnd = suiteStart + suiteLen;
resumptionOnTrack = 0;
while (suiteStart < suiteEnd) {
if (ssl->rec.majVer > SSL2_MAJ_VER) {
cipher = *suiteStart << 8; suiteStart++;
cipher += *suiteStart; suiteStart++;
} else {
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
psTraceInfo("SSLV2 not supported.\n");
return MATRIXSSL_ERROR;
}
if (cipher == ssl->cipher->ident) {
resumptionOnTrack = 1;
}
}
if (resumptionOnTrack == 0) {
/* Previous cipher suite wasn't sent for resumption. This is an
error according to the specs */
psTraceIntInfo("Client didn't send cipher %d for resumption\n",
ssl->cipher->ident);
ssl->cipher = sslGetCipherSpec(ssl, SSL_NULL_WITH_NULL_NULL);
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
return MATRIXSSL_ERROR;
}
} else {
/* User helps pick the cipher based on the key material. Successful
end result will be assignment of ssl->cipher */
if (chooseCipherSuite(ssl, suiteStart, suiteLen) < 0) {
psTraceInfo("Server could not support any client cipher suites\n");
ssl->cipher = sslGetCipherSpec(ssl, SSL_NULL_WITH_NULL_NULL);
if (ssl->err != SSL_ALERT_UNRECOGNIZED_NAME) {
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
}
return MATRIXSSL_ERROR;
}
if (ssl->cipher->ident == 0) {
psTraceInfo("Client attempting SSL_NULL_WITH_NULL_NULL conn\n");
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
return MATRIXSSL_ERROR;
}
}
matrixSslSetKexFlags(ssl);
/* If we're resuming a handshake, then the next handshake message we
expect is the finished message. Otherwise we do the full handshake */
if (ssl->flags & SSL_FLAGS_RESUMED) {
ssl->hsState = SSL_HS_FINISHED;
} else {
#ifdef USE_DHE_CIPHER_SUITE
/* If we are DH key exchange we need to generate some keys. The
FLAGS_DHE_KEY_EXCH will eventually drive the state matchine to
the ServerKeyExchange path, but ECDH_ suites need the key gen now */
if (ssl->flags & SSL_FLAGS_DHE_KEY_EXCH) {
#ifdef USE_ECC_CIPHER_SUITE
if (ssl->flags & SSL_FLAGS_ECC_CIPHER) {
/* If ecCurveId is zero and we received the extension, then
we really couldn't match and can't continue. */
if (ssl->ecInfo.ecCurveId == 0 &&
(ssl->ecInfo.ecFlags & IS_RECVD_EXT)) {
psTraceInfo("Did not share any EC curves with client\n");
/* Don't see any particular alert for this case */
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
return MATRIXSSL_ERROR;
}
/* A ecCurveId of zero (with no extension) will return a
default which is fine according to spec */
if (getEccParamById(ssl->ecInfo.ecCurveId, &curve) < 0) {
return MATRIXSSL_ERROR;
}
if (psEccNewKey(ssl->hsPool, &ssl->sec.eccKeyPriv, curve) < 0) {
return PS_MEM_FAIL;
}
#ifdef USE_ECC_EPHEMERAL_KEY_CACHE
if ((rc = matrixSslGenEphemeralEcKey(ssl->keys,
ssl->sec.eccKeyPriv, curve, pkiData)) < 0) {
#else
if ((rc = psEccGenKey(ssl->hsPool, ssl->sec.eccKeyPriv,
curve, pkiData)) < 0) {
#endif
psEccDeleteKey(&ssl->sec.eccKeyPriv);
psTraceInfo("GenEphemeralEcc failed\n");
ssl->err = SSL_ALERT_INTERNAL_ERROR;
return rc;
}
} else {
#endif /* USE_ECC_CIPHER_SUITE */
#ifdef REQUIRE_DH_PARAMS
/* Servers using DH suites know DH key sizes when handshake
pool is created so that has been accounted for here */
if ((ssl->sec.dhKeyPriv = psMalloc(ssl->hsPool,
sizeof(psDhKey_t))) == NULL) {
return MATRIXSSL_ERROR;
}
if ((rc = psDhGenKeyInts(ssl->hsPool, ssl->keys->dhParams.size,
&ssl->keys->dhParams.p, &ssl->keys->dhParams.g,
ssl->sec.dhKeyPriv, pkiData)) < 0) {
psFree(ssl->sec.dhKeyPriv, ssl->hsPool);
ssl->sec.dhKeyPriv = NULL;
psTraceInfo("Error generating DH keys\n");
ssl->err = SSL_ALERT_INTERNAL_ERROR;
return MATRIXSSL_ERROR;
}
#endif
#ifdef USE_ECC_CIPHER_SUITE
}
#endif /* USE_ECC_CIPHER_SUITE */
}
#endif /* USE_DHE_CIPHER_SUITE */
ssl->hsState = SSL_HS_CLIENT_KEY_EXCHANGE;
#ifdef USE_CLIENT_AUTH
/* Next state in client authentication case is to receive the cert */
if (ssl->flags & SSL_FLAGS_CLIENT_AUTH) {
#ifdef USE_ANON_DH_CIPHER_SUITE
/* However, what if the server has called for client auth and
the client is requesting an 'anon' cipher suite?
SECURITY: Options are to default to what the
client wants, what the server wants, or error out. The
current implementation does what the client wants. */
if (ssl->flags & SSL_FLAGS_ANON_CIPHER) {
psTraceIntInfo(
"Anon cipher %d negotiated. Disabling client auth\n",
ssl->cipher->ident);
ssl->flags &= ~SSL_FLAGS_CLIENT_AUTH;
} else {
#endif /* USE_ANON_DH_CIPHER_SUITE */
ssl->hsState = SSL_HS_CERTIFICATE;
#ifdef USE_ANON_DH_CIPHER_SUITE
}
#endif /* USE_ANON_DH_CIPHER_SUITE */
}
#endif /* USE_CLIENT_AUTH */
}
/* Now that we've parsed the ClientHello, we need to tell the caller that
we have a handshake response to write out.
The caller should call sslWrite upon receiving this return code. */
*cp = c;
ssl->decState = SSL_HS_CLIENT_HELLO;
return SSL_PROCESS_DATA;
}
/******************************************************************************/
int32 parseClientKeyExchange(ssl_t *ssl, int32 hsLen, unsigned char **cp,
unsigned char *end)
{
int32 rc, pubKeyLen;
unsigned char *c;
#ifdef USE_RSA_CIPHER_SUITE
unsigned char R[SSL_HS_RSA_PREMASTER_SIZE - 2];
psPool_t *ckepkiPool = NULL;
#endif
#ifdef USE_PSK_CIPHER_SUITE
uint8_t pskLen;
unsigned char *pskKey = NULL;
#endif
void *pkiData = ssl->userPtr;
c = *cp;
/* RSA: This message contains the premaster secret encrypted with the
server's public key (from the Certificate). The premaster
secret is 48 bytes of random data, but the message will be longer
than that because the 48 bytes are padded before encryption
according to PKCS#1v1.5. After encryption, we should have the
correct length. */
psTraceHs(">>> Server parsing CLIENT_KEY_EXCHANGE\n");
if ((int32)(end - c) < hsLen) {
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("Invalid ClientKeyExchange length 1\n");
return MATRIXSSL_ERROR;
}
pubKeyLen = hsLen;
#ifdef USE_TLS
/* TLS - Two byte length is explicit. */
if (ssl->majVer >= TLS_MAJ_VER && ssl->minVer >= TLS_MIN_VER) {
if (end - c < 2) {
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("Invalid ClientKeyExchange length 2\n");
return MATRIXSSL_ERROR;
}
#ifdef USE_ECC_CIPHER_SUITE
if (ssl->flags & SSL_FLAGS_ECC_CIPHER) {
pubKeyLen = *c; c++;
} else {
#endif /* USE_ECC_CIPHER_SUITE */
pubKeyLen = *c << 8; c++;
pubKeyLen += *c; c++;
#ifdef USE_ECC_CIPHER_SUITE
}
#endif /* USE_ECC_CIPHER_SUITE */
if ((int32)(end - c) < pubKeyLen) {
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("Invalid ClientKeyExchange length 3\n");
return MATRIXSSL_ERROR;
}
}
#endif /* USE_TLS */
#ifdef USE_DHE_CIPHER_SUITE
if (ssl->flags & SSL_FLAGS_DHE_KEY_EXCH) {
if (ssl->majVer == SSL3_MAJ_VER && ssl->minVer == SSL3_MIN_VER) {
#ifdef USE_ECC_CIPHER_SUITE
/* Support ECC ciphers in SSLv3. This isn't really a desirable
combination and it's a fuzzy area in the specs but it works */
if (!(ssl->flags & SSL_FLAGS_ECC_CIPHER)) {
#endif
/* DH cipher suites use the ClientDiffieHellmanPublic format
which always includes the explicit key length regardless
of protocol. If TLS, we already stripped it out above. */
if (end - c < 2) {
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("Invalid ClientKeyExchange length 4\n");
return MATRIXSSL_ERROR;
}
pubKeyLen = *c << 8; c++;
pubKeyLen += *c; c++;
if ((int32)(end - c) < pubKeyLen) {
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("Invalid ClientKeyExchange length 5\n");
return MATRIXSSL_ERROR;
}
#ifdef USE_ECC_CIPHER_SUITE
} else {
pubKeyLen = *c; c++;
}
#endif
}
#ifdef USE_PSK_CIPHER_SUITE
if (ssl->flags & SSL_FLAGS_PSK_CIPHER) {
/* That initial pubKeyLen we read off the top was actually the
length of the PSK id that we need to find a key for */
if ((uint32)(end - c) < pubKeyLen) {
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("Invalid ClientKeyExchange PSK length\n");
return MATRIXSSL_ERROR;
}
/* If there are PSKs loaded, look at those. Otherwise see if
there is a callback. */
if (ssl->keys && ssl->keys->pskKeys) {
matrixSslPskGetKey(ssl, c, pubKeyLen, &pskKey, &pskLen);
} else if (ssl->sec.pskCb) {
(ssl->sec.pskCb)(ssl, c, pubKeyLen, &pskKey, &pskLen);
}
if (pskKey == NULL) {
psTraceInfo("Server doesn't not have matching pre-shared key\n");
ssl->err = SSL_ALERT_UNKNOWN_PSK_IDENTITY;
return MATRIXSSL_ERROR;
}
c += pubKeyLen;
/* This is the DH pub key now */
pubKeyLen = *c << 8; c++;
pubKeyLen += *c; c++;
if ((uint32)(end - c) < pubKeyLen) {
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("Invalid ClientKeyExchange length\n");
return MATRIXSSL_ERROR;
}
}
#endif /* USE_PSK_CIPHER_SUITE */
#ifdef USE_ECC_CIPHER_SUITE
if (ssl->flags & SSL_FLAGS_ECC_CIPHER) {
if (psEccNewKey(ssl->hsPool, &ssl->sec.eccKeyPub,
ssl->sec.eccKeyPriv->curve) < 0) {
return SSL_MEM_ERROR;
}
if (psEccX963ImportKey(ssl->hsPool, c, pubKeyLen,
ssl->sec.eccKeyPub, ssl->sec.eccKeyPriv->curve) < 0) {
ssl->err = SSL_ALERT_DECODE_ERROR;
return MATRIXSSL_ERROR;
}
/* BUG FIX after 3.8.1a release. This increment is done later
in the function. So in cases where multiple handshake messages
were put in a single record, we are moving pubKeyLen farther
than we want which could still be in the valid buffer.
The error would be an "unexpected handshake message" when
the next message parse was attempted */
//c += pubKeyLen;
ssl->sec.premasterSize = ssl->sec.eccKeyPriv->curve->size;
ssl->sec.premaster = psMalloc(ssl->hsPool,
ssl->sec.premasterSize);
if (ssl->sec.premaster == NULL) {
return SSL_MEM_ERROR;
}
if ((rc = psEccGenSharedSecret(ssl->hsPool, ssl->sec.eccKeyPriv,
ssl->sec.eccKeyPub, ssl->sec.premaster,
&ssl->sec.premasterSize, pkiData)) < 0) {
ssl->err = SSL_ALERT_INTERNAL_ERROR;
psFree(ssl->sec.premaster, ssl->hsPool);
ssl->sec.premaster = NULL;
return MATRIXSSL_ERROR;
}
psEccDeleteKey(&ssl->sec.eccKeyPub);
psEccDeleteKey(&ssl->sec.eccKeyPriv);
} else {
#endif /* USE_ECC_CIPHER_SUITE */
#ifdef REQUIRE_DH_PARAMS
if ((ssl->sec.dhKeyPub = psMalloc(ssl->hsPool, sizeof(psDhKey_t))) == NULL) {
return MATRIXSSL_ERROR;
}
if (psDhImportPubKey(ssl->hsPool, c, pubKeyLen,
ssl->sec.dhKeyPub) < 0) {
psFree(ssl->sec.dhKeyPub, ssl->hsPool);
ssl->sec.dhKeyPub = NULL;
return MATRIXSSL_ERROR;
}
/*
Now know the premaster details. Create it.
A Diffie-Hellman shared secret has, at maximum, the same number of
bytes as the prime. Use this number as our max buffer size that
will be into psDhGenSecret.
*/
ssl->sec.premasterSize = ssl->sec.dhPLen;
#ifdef USE_PSK_CIPHER_SUITE
if (ssl->flags & SSL_FLAGS_PSK_CIPHER) {
/*
Premaster is appended with the PSK. Account for that length
here to avoid a realloc after the standard DH premaster is
created below.
*/
ssl->sec.premasterSize += pskLen + 4; /* uint16 len heads */
}
#endif /* USE_PSK_CIPHER_SUITE */
ssl->sec.premaster = psMalloc(ssl->hsPool, ssl->sec.premasterSize);
if (ssl->sec.premaster == NULL) {
return SSL_MEM_ERROR;
}
if ((rc = psDhGenSharedSecret(ssl->hsPool, ssl->sec.dhKeyPriv,
ssl->sec.dhKeyPub, ssl->sec.dhP, ssl->sec.dhPLen,
ssl->sec.premaster,
&ssl->sec.premasterSize, pkiData)) < 0) {
return MATRIXSSL_ERROR;
}
psFree(ssl->sec.dhP, ssl->hsPool);
ssl->sec.dhP = NULL; ssl->sec.dhPLen = 0;
psFree(ssl->sec.dhG, ssl->hsPool);
ssl->sec.dhG = NULL; ssl->sec.dhGLen = 0;
psDhClearKey(ssl->sec.dhKeyPub);
psFree(ssl->sec.dhKeyPub, ssl->hsPool);
ssl->sec.dhKeyPub = NULL;
psDhClearKey(ssl->sec.dhKeyPriv);
psFree(ssl->sec.dhKeyPriv, ssl->hsPool);
ssl->sec.dhKeyPriv = NULL;
#ifdef USE_PSK_CIPHER_SUITE
if (ssl->flags & SSL_FLAGS_PSK_CIPHER) {
/*
Need to prepend a uint16 length to the premaster key.
*/
memmove(&ssl->sec.premaster[2], ssl->sec.premaster,
ssl->sec.premasterSize);
ssl->sec.premaster[0] = (ssl->sec.premasterSize & 0xFF00) >> 8;
ssl->sec.premaster[1] = (ssl->sec.premasterSize & 0xFF);
/*
Next, uint16 length of PSK and key itself
*/
ssl->sec.premaster[ssl->sec.premasterSize + 2] =
(pskLen & 0xFF00) >> 8;
ssl->sec.premaster[ssl->sec.premasterSize + 3] =(pskLen & 0xFF);
memcpy(&ssl->sec.premaster[ssl->sec.premasterSize + 4], pskKey,
pskLen);
/*
Lastly, adjust the premasterSize
*/
ssl->sec.premasterSize += pskLen + 4;
}
#endif /* USE_PSK_CIPHER_SUITE */
#endif /* REQUIRE_DH_PARAMS */
#ifdef USE_ECC_CIPHER_SUITE
}
#endif /* USE_ECC_CIPHER_SUITE */
} else {
#endif /* USE_DHE_CIPHER_SUITE */
#ifdef USE_PSK_CIPHER_SUITE
if (ssl->flags & SSL_FLAGS_PSK_CIPHER) {
if (ssl->majVer == SSL3_MAJ_VER && ssl->minVer == SSL3_MIN_VER){
/* SSLv3 for basic PSK suites will not have read off
pubKeyLen at this point */
pubKeyLen = *c << 8; c++;
pubKeyLen += *c; c++;
}
/* If there are PSKs loaded, look at those. Otherwise see if
there is a callback. */
if (ssl->keys && ssl->keys->pskKeys) {
matrixSslPskGetKey(ssl, c, pubKeyLen, &pskKey,
&pskLen);
} else if (ssl->sec.pskCb) {
if ((ssl->sec.pskCb)(ssl, c, pubKeyLen, &pskKey, &pskLen)
< 0) {
psTraceInfo("User couldn't find pre-shared key\n");
ssl->err = SSL_ALERT_UNKNOWN_PSK_IDENTITY;
return MATRIXSSL_ERROR;
}
}
if (pskKey == NULL) {
psTraceInfo("Server doesn't have matching pre-shared key\n");
ssl->err = SSL_ALERT_UNKNOWN_PSK_IDENTITY;
return MATRIXSSL_ERROR;
}
ssl->sec.premasterSize = (pskLen * 2) + 4;
ssl->sec.premaster = psMalloc(ssl->hsPool,
ssl->sec.premasterSize);
if (ssl->sec.premaster == NULL) {
return SSL_MEM_ERROR;
}
memset(ssl->sec.premaster, 0, ssl->sec.premasterSize);
ssl->sec.premaster[0] = (pskLen & 0xFF00) >> 8;
ssl->sec.premaster[1] = (pskLen & 0xFF);
/* memset to 0 handled middle portion */
ssl->sec.premaster[2 + pskLen] = (pskLen & 0xFF00) >> 8;
ssl->sec.premaster[3 + pskLen] = (pskLen & 0xFF);
memcpy(&ssl->sec.premaster[4 + pskLen], pskKey, pskLen);
} else {
#endif
#ifdef USE_ECC_CIPHER_SUITE
if (ssl->cipher->type == CS_ECDH_ECDSA ||
ssl->cipher->type == CS_ECDH_RSA) {
if (ssl->majVer == SSL3_MAJ_VER &&
ssl->minVer == SSL3_MIN_VER) {
/* Support ECC ciphers in SSLv3. This isn't really a
desirable combination and it's a fuzzy area in the
specs but it works */
pubKeyLen = *c; c++;
}
if (ssl->keys == NULL) {
ssl->err = SSL_ALERT_INTERNAL_ERROR;
return MATRIXSSL_ERROR;
}
if (psEccNewKey(ssl->hsPool, &ssl->sec.eccKeyPub,
ssl->keys->privKey.key.ecc.curve) < 0) {
return SSL_MEM_ERROR;
}
if (psEccX963ImportKey(ssl->hsPool, c, pubKeyLen,
ssl->sec.eccKeyPub, ssl->keys->privKey.key.ecc.curve)
< 0) {
ssl->err = SSL_ALERT_DECODE_ERROR;
return MATRIXSSL_ERROR;
}
/* BUG FIX after 3.8.1a release. This increment is done
later in the function. So in cases where multiple
handshake messages were put in a single record, we are
moving pubKeyLen farther than we want which could still
be in the valid buffer. The error would be an
"unexpected handshake message" when the next message
parse was attempted */
//c += pubKeyLen;
ssl->sec.premasterSize =
ssl->keys->privKey.key.ecc.curve->size;
ssl->sec.premaster = psMalloc(ssl->hsPool,
ssl->sec.premasterSize);
if (ssl->sec.premaster == NULL) {
return SSL_MEM_ERROR;
}
if ((rc = psEccGenSharedSecret(ssl->hsPool,
&ssl->keys->privKey.key.ecc, ssl->sec.eccKeyPub,
ssl->sec.premaster, &ssl->sec.premasterSize,
pkiData)) < 0) {
ssl->err = SSL_ALERT_INTERNAL_ERROR;
psFree(ssl->sec.premaster, ssl->hsPool);
ssl->sec.premaster = NULL;
return MATRIXSSL_ERROR;
}
psEccDeleteKey(&ssl->sec.eccKeyPub);
} else {
#endif /* USE_ECC_CIPHER_SUITE */
#ifdef USE_RSA_CIPHER_SUITE
if (ssl->keys == NULL) {
ssl->err = SSL_ALERT_INTERNAL_ERROR;
return MATRIXSSL_ERROR;
}
/* Standard RSA suite. Now have a handshake pool to allocate
the premaster storage */
ssl->sec.premasterSize = SSL_HS_RSA_PREMASTER_SIZE;
ssl->sec.premaster = psMalloc(ssl->hsPool,
SSL_HS_RSA_PREMASTER_SIZE);
if (ssl->sec.premaster == NULL) {
return SSL_MEM_ERROR;
}
/**
@security Caution - the results of an RSA private key
decryption should never have any bearing on timing or response,
otherwise we can be vulnerable to a side channel attack.
@see http://web-in-security.blogspot.co.at/2014/08/old-attacks-on-new-tls-implementations.html
@see https://tools.ietf.org/html/rfc5246#section-7.4.7.1
"In any case, a TLS server MUST NOT generate an alert if processing an
RSA-encrypted premaster secret message fails, or the version number
is not as expected. Instead, it MUST continue the handshake with a
randomly generated premaster secret. It may be useful to log the
real cause of failure for troubleshooting purposes; however, care
must be taken to avoid leaking the information to an attacker
(through, e.g., timing, log files, or other channels.)"
*/
rc = psRsaDecryptPriv(ckepkiPool, &ssl->keys->privKey.key.rsa, c,
pubKeyLen, ssl->sec.premaster, ssl->sec.premasterSize,
pkiData);
/* Step 1 of Bleichenbacher attack mitigation. We do it here
after the RSA op, but regardless of the result of the op. */
if (matrixCryptoGetPrngData(R, sizeof(R), ssl->userPtr) < 0) {
ssl->err = SSL_ALERT_INTERNAL_ERROR;
return MATRIXSSL_ERROR;
}
/* Step 3
If the PKCS#1 padding is not correct, or the length of message
M is not exactly 48 bytes:
pre_master_secret = ClientHello.client_version || R
else
pre_master_secret = ClientHello.client_version || M[2..47]
Note that explicitly constructing the pre_master_secret with the
ClientHello.client_version produces an invalid master_secret if the
client has sent the wrong version in the original pre_master_secret.
Note: The version number in the PreMasterSecret is the version
offered by the client in the ClientHello.client_version, not the
version negotiated for the connection. This feature is designed to
prevent rollback attacks. Unfortunately, some old implementations
use the negotiated version instead, and therefore checking the
version number may lead to failure to interoperate with such
incorrect client implementations. This is known in OpenSSL as the
SSL_OP_TLS_ROLLBACK_BUG. MatrixSSL doesn't support these
incorrect implementations.
*/
ssl->sec.premaster[0] = ssl->reqMajVer;
ssl->sec.premaster[1] = ssl->reqMinVer;
if (rc < 0) {
memcpy(ssl->sec.premaster + 2, R, sizeof(R));
} else {
/* Not necessary, but keep timing similar */
memcpy(R, ssl->sec.premaster + 2, sizeof(R));
}
/* R may contain sensitive data, eg. premaster */
memzero_s(R, sizeof(R));
#else /* RSA is the 'default' so if that didn't get hit there is a problem */
psTraceInfo("There is no handler for ClientKeyExchange parse. ERROR\n");
return MATRIXSSL_ERROR;
#endif /* USE_RSA_CIPHER_SUITE */
#ifdef USE_ECC_CIPHER_SUITE
}
#endif /* USE_ECC_CIPHER_SUITE */
#ifdef USE_PSK_CIPHER_SUITE
}
#endif /* USE_PSK_CIPHER_SUITE */
#ifdef USE_DHE_CIPHER_SUITE
}
#endif /* USE_DHE_CIPHER_SUITE */
/* Now that we've got the premaster secret, derive the various
symmetric keys using it and the client and server random values.
Update the cached session (if found) with the masterSecret and
negotiated cipher. */
if (ssl->extFlags.extended_master_secret == 1) {
if (tlsExtendedDeriveKeys(ssl) < 0) {
return MATRIXSSL_ERROR;
}
} else {
if (sslCreateKeys(ssl) < 0) {
ssl->err = SSL_ALERT_INTERNAL_ERROR;
return MATRIXSSL_ERROR;
}
}
matrixUpdateSession(ssl);
c += pubKeyLen;
ssl->hsState = SSL_HS_FINISHED;
#ifdef USE_DTLS
/* The freeing of premaster and cert were not done at the normal time
because of the retransmit scenarios. This is server side */
if (ssl->sec.premaster) {
psFree(ssl->sec.premaster, ssl->hsPool); ssl->sec.premaster = NULL;
ssl->sec.premasterSize = 0;
}
#endif /* USE_DTLS */
#ifdef USE_CLIENT_AUTH
/* In the non client auth case, we are done with the handshake pool */
if (!(ssl->flags & SSL_FLAGS_CLIENT_AUTH)) {
#ifdef USE_DTLS
#ifndef USE_ONLY_PSK_CIPHER_SUITE
if (ssl->sec.cert) {
psFree(ssl->sec.cert, NULL); ssl->sec.cert = NULL;
}
#endif
if (ssl->ckeMsg != NULL) {
psFree(ssl->ckeMsg, ssl->hsPool); ssl->ckeMsg = NULL;
}
#endif /* USE_DTLS */
ssl->hsPool = NULL;
}
#else /* CLIENT_AUTH */
#ifdef USE_DTLS
if (ssl->ckeMsg != NULL) {
psFree(ssl->ckeMsg, ssl->hsPool); ssl->ckeMsg = NULL;
}
#endif /* USE_DTLS */
ssl->hsPool = NULL;
#endif
#ifdef USE_CLIENT_AUTH
/* Tweak the state here for client authentication case */
if (ssl->flags & SSL_FLAGS_CLIENT_AUTH) {
ssl->hsState = SSL_HS_CERTIFICATE_VERIFY;
}
#endif /* USE_CLIENT_AUTH */
*cp = c;
ssl->decState = SSL_HS_CLIENT_KEY_EXCHANGE;
return PS_SUCCESS;
}
/******************************************************************************/
#ifndef USE_ONLY_PSK_CIPHER_SUITE
#ifdef USE_CLIENT_AUTH
int32 parseCertificateVerify(ssl_t *ssl,
unsigned char hsMsgHash[SHA512_HASH_SIZE],
unsigned char **cp, unsigned char *end)
{
uint32 certVerifyLen, pubKeyLen;
int32 rc, i;
#ifdef USE_RSA
unsigned char certVerify[SHA512_HASH_SIZE];
#endif /* USE_RSA */
unsigned char *c;
psPool_t *cvpkiPool = NULL;
void *pkiData = ssl->userPtr;
c = *cp;
rc = 0;
psTraceHs(">>> Server parsing CERTIFICATE_VERIFY message\n");
#ifdef USE_TLS_1_2
if (ssl->flags & SSL_FLAGS_TLS_1_2) {
uint32_t hashSigAlg;
if ((uint32)(end - c) < 2) {
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("Invalid Certificate Verify message\n");
return MATRIXSSL_ERROR;
}
hashSigAlg = HASH_SIG_MASK(c[0], c[1]);
/* The server-sent algorithms has to be one of the ones we sent in
our ClientHello extension */
if (!(ssl->hashSigAlg & hashSigAlg)) {
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("Invalid SigHash\n");
return MATRIXSSL_ERROR;
}
switch (c[0]) {
case HASH_SIG_SHA256:
certVerifyLen = SHA256_HASH_SIZE;
break;
#ifdef USE_SHA384
case HASH_SIG_SHA384:
/* The one-off grab of SHA-384 handshake hash */
sslSha384RetrieveHSHash(ssl, hsMsgHash);
certVerifyLen = SHA384_HASH_SIZE;
break;
#endif
#ifdef USE_SHA512
case HASH_SIG_SHA512:
/* The one-off grab of SHA-512 handshake hash */
sslSha512RetrieveHSHash(ssl, hsMsgHash);
certVerifyLen = SHA512_HASH_SIZE;
break;
#endif
#ifdef USE_SHA1
case HASH_SIG_SHA1:
/* The one-off grab of SHA-1 handshake hash */
sslSha1RetrieveHSHash(ssl, hsMsgHash);
certVerifyLen = SHA1_HASH_SIZE;
break;
#endif
default:
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("Invalid Certificate Verify message\n");
return MATRIXSSL_ERROR;
}
c += 2;
} else {
certVerifyLen = MD5_HASH_SIZE + SHA1_HASH_SIZE;
}
#else
certVerifyLen = MD5_HASH_SIZE + SHA1_HASH_SIZE;
#endif /* USE_TLS_1_2 */
if ((uint32)(end - c) < 2) {
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("Invalid Certificate Verify message\n");
return MATRIXSSL_ERROR;
}
pubKeyLen = *c << 8; c++;
pubKeyLen |= *c; c++;
if ((uint32)(end - c) < pubKeyLen) {
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("Invalid Certificate Verify message\n");
return MATRIXSSL_ERROR;
}
/* The server side verification of client identity. If we can match
the signature we know the client has possesion of the private key. */
#ifdef USE_ECC
/* Need to read sig algorithm type out of cert itself */
if (ssl->sec.cert->pubKeyAlgorithm == OID_ECDSA_KEY_ALG) {
rc = 0;
#ifdef USE_TLS_1_2
if (ssl->flags & SSL_FLAGS_TLS_1_2) {
if ((i = psEccDsaVerify(cvpkiPool,
&ssl->sec.cert->publicKey.key.ecc,
hsMsgHash, certVerifyLen,
c, pubKeyLen, &rc, pkiData)) != 0) {
psTraceInfo("ECDSA signature validation failed\n");
ssl->err = SSL_ALERT_BAD_CERTIFICATE;
return MATRIXSSL_ERROR;
}
} else {
certVerifyLen = SHA1_HASH_SIZE; /* per spec */
if ((i = psEccDsaVerify(cvpkiPool,
&ssl->sec.cert->publicKey.key.ecc,
hsMsgHash + MD5_HASH_SIZE, certVerifyLen,
c, pubKeyLen,
&rc, pkiData)) != 0) {
psTraceInfo("ECDSA signature validation failed\n");
ssl->err = SSL_ALERT_BAD_CERTIFICATE;
return MATRIXSSL_ERROR;
}
}
#else
certVerifyLen = SHA1_HASH_SIZE; /* per spec */
if ((i = psEccDsaVerify(cvpkiPool,
&ssl->sec.cert->publicKey.key.ecc,
hsMsgHash + MD5_HASH_SIZE, certVerifyLen,
c, pubKeyLen,
&rc, pkiData)) != 0) {
psTraceInfo("ECDSA signature validation failed\n");
ssl->err = SSL_ALERT_BAD_CERTIFICATE;
return MATRIXSSL_ERROR;
}
#endif
if (rc != 1) {
psTraceInfo("Can't verify certVerify sig\n");
ssl->err = SSL_ALERT_BAD_CERTIFICATE;
return MATRIXSSL_ERROR;
}
rc = MATRIXSSL_SUCCESS; /* done using rc as a temp */
} else {
#endif /* USE_ECC */
#ifdef USE_RSA
#ifdef USE_TLS_1_2
if (ssl->flags & SSL_FLAGS_TLS_1_2) {
if ((i = pubRsaDecryptSignedElement(cvpkiPool,
&ssl->sec.cert->publicKey.key.rsa, c, pubKeyLen, certVerify,
certVerifyLen, pkiData)) < 0) {
psTraceInfo("Unable to decrypt CertVerify digital element\n");
return MATRIXSSL_ERROR;
}
} else {
if ((i = psRsaDecryptPub(cvpkiPool, &ssl->sec.cert->publicKey.key.rsa, c,
pubKeyLen, certVerify, certVerifyLen, pkiData)) < 0) {
psTraceInfo("Unable to publicly decrypt Certificate Verify message\n");
return MATRIXSSL_ERROR;
}
}
#else /* !USE_TLS_1_2 */
if ((i = psRsaDecryptPub(cvpkiPool, &ssl->sec.cert->publicKey.key.rsa, c,
pubKeyLen, certVerify, certVerifyLen, pkiData)) < 0) {
psTraceInfo("Unable to publicly decrypt Certificate Verify message\n");
return MATRIXSSL_ERROR;
}
#endif /* USE_TLS_1_2 */
if (memcmpct(certVerify, hsMsgHash, certVerifyLen) != 0) {
psTraceInfo("Unable to verify client certificate signature\n");
return MATRIXSSL_ERROR;
}
#else /* RSA is 'default' so if that didn't get hit there is a problem */
psTraceInfo("There is no handler for CertificateVerify parse. ERROR\n");
return MATRIXSSL_ERROR;
#endif /* USE_RSA */
#ifdef USE_ECC
}
#endif /* USE_ECC*/
c += pubKeyLen;
ssl->hsState = SSL_HS_FINISHED;
*cp = c;
ssl->decState = SSL_HS_CERTIFICATE_VERIFY;
return PS_SUCCESS;
}
#endif /* !USE_ONLY_PSK_CIPHER_SUITE */
#endif /* USE_ONLY_PSK_CIPHER_SUITE */
#endif /* USE_SERVER_SIDE_SSL */
/******************************************************************************/
#ifdef USE_CLIENT_SIDE_SSL
int32 parseServerHello(ssl_t *ssl, int32 hsLen, unsigned char **cp,
unsigned char *end)
{
uint32 sessionIdLen, cipher = 0;
int32 rc;
unsigned char *extData;
unsigned char *c;
c = *cp;
psTraceHs(">>> Client parsing SERVER_HELLO message\n");
#ifdef USE_MATRIXSSL_STATS
matrixsslUpdateStat(ssl, SH_RECV_STAT, 1);
#endif
/* Need to track hsLen because there is no explict way to tell if
hello extensions are appended so it isn't clear if the record data
after the compression parameters are a new message or extension data */
extData = c;
#ifdef USE_DTLS
/* Know now that the allocated members that were helping with the
HELLO_VERIFY_REQUEST exchange have finished serving their purpose */
if (ssl->cookie) {
psFree(ssl->cookie, ssl->hsPool); ssl->cookie = NULL;
ssl->cookieLen = 0; ssl->haveCookie = 0;
}
if (ssl->helloExt) {
psFree(ssl->helloExt, ssl->hsPool); ssl->helloExt = NULL;
ssl->helloExtLen = 0;
}
#endif /* USE_DTLS */
/* First two bytes are the negotiated SSL version */
if (end - c < 2) {
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("Invalid ssl header version length\n");
return MATRIXSSL_ERROR;
}
ssl->reqMajVer = *c; c++;
ssl->reqMinVer = *c; c++;
if (ssl->reqMajVer != ssl->majVer) {
ssl->err = SSL_ALERT_PROTOCOL_VERSION;
psTraceIntInfo("Unsupported ssl version: %d\n", ssl->reqMajVer);
return MATRIXSSL_ERROR;
}
#ifdef USE_TLS
/* See if the protocol is being downgraded */
if (ssl->reqMinVer != ssl->minVer) {
if (ssl->reqMinVer == SSL3_MIN_VER && ssl->minVer >= TLS_MIN_VER) {
#ifdef DISABLE_SSLV3
ssl->err = SSL_ALERT_PROTOCOL_VERSION;
psTraceInfo("Server wants to talk SSLv3 but it's disabled\n");
return MATRIXSSL_ERROR;
#else
/* Server minVer now becomes OUR initial requested version.
This is used during the creation of the premaster where
this initial requested version is part of the calculation.
The RFC actually says to use the original requested version
but no implemenations seem to follow that and just use the
agreed upon one. */
ssl->reqMinVer = ssl->minVer;
ssl->minVer = SSL3_MIN_VER;
ssl->flags &= ~SSL_FLAGS_TLS;
#ifdef USE_TLS_1_1
ssl->flags &= ~SSL_FLAGS_TLS_1_1;
#endif /* USE_TLS_1_1 */
#ifdef USE_TLS_1_2
ssl->flags &= ~SSL_FLAGS_TLS_1_2;
#endif /* USE_TLS_1_2 */
#endif /* DISABLE_SSLV3 */
} else {
#ifdef USE_TLS_1_1
#ifdef USE_TLS_1_2
/* Step down one at a time */
if (ssl->reqMinVer < TLS_1_2_MIN_VER &&
(ssl->flags & SSL_FLAGS_TLS_1_2)) {
ssl->flags &= ~SSL_FLAGS_TLS_1_2;
if (ssl->reqMinVer == TLS_1_1_MIN_VER) {
#ifdef DISABLE_TLS_1_1
ssl->err = SSL_ALERT_PROTOCOL_VERSION;
psTraceInfo("Server wants to talk TLS1.1 but it's disabled\n");
return MATRIXSSL_ERROR;
#else
ssl->reqMinVer = ssl->minVer;
ssl->minVer = TLS_1_1_MIN_VER;
goto PROTOCOL_DETERMINED;
#endif
}
}
#endif /* USE_TLS_1_2 */
if (ssl->reqMinVer == TLS_MIN_VER &&
ssl->minVer <= TLS_1_2_MIN_VER) {
#ifdef DISABLE_TLS_1_0
ssl->err = SSL_ALERT_PROTOCOL_VERSION;
psTraceInfo("Server wants to talk TLS1.0 but it's disabled\n");
return MATRIXSSL_ERROR;
#endif
ssl->reqMinVer = ssl->minVer;
ssl->minVer = TLS_MIN_VER;
ssl->flags &= ~SSL_FLAGS_TLS_1_1;
} else {
#endif/* USE_TLS_1_1 */
#ifdef USE_DTLS
/* Tests for DTLS downgrades */
if (ssl->flags & SSL_FLAGS_DTLS) {
if (ssl->reqMinVer == DTLS_MIN_VER &&
ssl->minVer == DTLS_1_2_MIN_VER) {
ssl->reqMinVer = ssl->minVer;
ssl->minVer = DTLS_MIN_VER;
ssl->flags &= ~SSL_FLAGS_TLS_1_2;
goto PROTOCOL_DETERMINED;
}
}
#endif
/* Wasn't able to settle on a common protocol */
ssl->err = SSL_ALERT_PROTOCOL_VERSION;
psTraceIntInfo("Unsupported ssl version: %d\n",
ssl->reqMajVer);
return MATRIXSSL_ERROR;
#ifdef USE_TLS_1_1
}
#endif /* USE_TLS_1_1 */
}
}
#endif /* USE_TLS */
#if defined (USE_TLS_1_2) || defined (USE_DTLS)
PROTOCOL_DETERMINED:
#endif /* USE_TLS_1_2 || USE_DTLS */
/* Next is a 32 bytes of random data for key generation
and a single byte with the session ID length */
if (end - c < SSL_HS_RANDOM_SIZE + 1) {
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("Invalid length of random data\n");
return MATRIXSSL_ERROR;
}
memcpy(ssl->sec.serverRandom, c, SSL_HS_RANDOM_SIZE);
c += SSL_HS_RANDOM_SIZE;
sessionIdLen = *c; c++;
if (sessionIdLen > SSL_MAX_SESSION_ID_SIZE ||
(uint32)(end - c) < sessionIdLen) {
ssl->err = SSL_ALERT_DECODE_ERROR;
return MATRIXSSL_ERROR;
}
/* If a session length was specified, the server has sent us a
session Id. We may have requested a specific session, and the
server may or may not agree to use that session. */
if (sessionIdLen > 0) {
if (ssl->sessionIdLen > 0) {
if (memcmp(ssl->sessionId, c, sessionIdLen) == 0) {
ssl->flags |= SSL_FLAGS_RESUMED;
} else {
ssl->cipher = sslGetCipherSpec(ssl,SSL_NULL_WITH_NULL_NULL);
memset(ssl->sec.masterSecret, 0x0, SSL_HS_MASTER_SIZE);
ssl->sessionIdLen = (unsigned char)sessionIdLen;
memcpy(ssl->sessionId, c, sessionIdLen);
ssl->flags &= ~SSL_FLAGS_RESUMED;
#ifdef USE_MATRIXSSL_STATS
matrixsslUpdateStat(ssl, FAILED_RESUMPTIONS_STAT, 1);
#endif
}
} else {
ssl->sessionIdLen = (unsigned char)sessionIdLen;
memcpy(ssl->sessionId, c, sessionIdLen);
}
c += sessionIdLen;
} else {
if (ssl->sessionIdLen > 0) {
ssl->cipher = sslGetCipherSpec(ssl, SSL_NULL_WITH_NULL_NULL);
memset(ssl->sec.masterSecret, 0x0, SSL_HS_MASTER_SIZE);
ssl->sessionIdLen = 0;
memset(ssl->sessionId, 0x0, SSL_MAX_SESSION_ID_SIZE);
ssl->flags &= ~SSL_FLAGS_RESUMED;
#ifdef USE_MATRIXSSL_STATS
matrixsslUpdateStat(ssl, FAILED_RESUMPTIONS_STAT, 1);
#endif
}
}
/* Next is the two byte cipher suite */
if (end - c < 2) {
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("Invalid cipher suite length\n");
return MATRIXSSL_ERROR;
}
cipher = *c << 8; c++;
cipher += *c; c++;
/* A resumed session can only match the cipher originally
negotiated. Otherwise, match the first cipher that we support */
if (ssl->flags & SSL_FLAGS_RESUMED) {
psAssert(ssl->cipher != NULL);
if (ssl->cipher->ident != cipher) {
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
psTraceInfo("Can't support resumed cipher\n");
return MATRIXSSL_ERROR;
}
} else {
if ((ssl->cipher = sslGetCipherSpec(ssl, cipher)) == NULL) {
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
psTraceIntInfo("Can't support requested cipher: %d\n", cipher);
return MATRIXSSL_ERROR;
}
}
matrixSslSetKexFlags(ssl);
/* Decode the compression parameter byte. */
#define COMPRESSION_METHOD_NULL 0x0
#define COMPRESSION_METHOD_DEFLATE 0x1
if (end - c < 1) {
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
psTraceInfo("Expected compression value\n");
return MATRIXSSL_ERROR;
}
switch (*c) {
case COMPRESSION_METHOD_NULL:
/* No compression */
break;
#ifdef USE_ZLIB_COMPRESSION
case COMPRESSION_METHOD_DEFLATE:
ssl->inflate.zalloc = NULL;
ssl->inflate.zfree = NULL;
ssl->inflate.opaque = NULL;
ssl->inflate.avail_in = 0;
ssl->inflate.next_in = NULL;
if (inflateInit(&ssl->inflate) != Z_OK) {
psTraceInfo("inflateInit fail. No compression\n");
} else {
ssl->deflate.zalloc = Z_NULL;
ssl->deflate.zfree = Z_NULL;
ssl->deflate.opaque = Z_NULL;
if (deflateInit(&ssl->deflate, Z_DEFAULT_COMPRESSION) != Z_OK) {
psTraceInfo("deflateInit fail. No compression\n");
inflateEnd(&ssl->inflate);
} else {
ssl->compression = 1; /* Both contexts initialized */
}
}
break;
#endif /* USE_ZLIB_COMPRESSION */
default:
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("zlib compression not enabled.\n");
return MATRIXSSL_ERROR;
}
/* At this point, if we're resumed, we have all the required info
to derive keys. The next handshake message we expect is
the Finished message.
After incrementing c below, we will either be pointing at 'end'
with no more data in the message, or at the first byte of an optional
extension. */
c++;
/* If our sent ClientHello had an extension there could be extension data
to parse here: http://www.faqs.org/rfcs/rfc3546.html
The explict test on hsLen is necessary for TLS 1.0 and 1.1 because
there is no good way to tell if the remaining record data is the
next handshake message or if it is extension data */
if (c != end && ((int32)hsLen > (c - extData))) {
rc = parseServerHelloExtensions(ssl, hsLen, extData, &c, c - end);
if (rc < 0) {
/* Alerts will already have been set inside */
return rc;
}
}
#ifdef USE_OCSP_MUST_STAPLE
/* Will catch cases where a server does not send any extensions at all */
if (ssl->extFlags.req_status_request == 1) {
if (ssl->extFlags.status_request == 0) {
psTraceInfo("Server doesn't support OCSP stapling\n");
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
return MATRIXSSL_ERROR;
}
}
#endif
if (ssl->maxPtFrag & 0x10000 || ssl->extFlags.req_max_fragment_len) {
/* Server didn't respond to our MAX_FRAG request. Reset default */
psTraceInfo("Server ignored max fragment length ext request\n");
ssl->maxPtFrag = SSL_MAX_PLAINTEXT_LEN;
}
if (ssl->extFlags.req_sni) {
psTraceInfo("Server ignored SNI ext request\n");
}
#ifdef USE_STATELESS_SESSION_TICKETS
if (ssl->sid &&
ssl->sid->sessionTicketState == SESS_TICKET_STATE_SENT_TICKET) {
/*
Server did not send an extension reply to our populated ticket.
From the updated RFC 5077:
"It is also permissible to have an exchange using the
abbreviated handshake defined in Figure 2 of RFC 4346, where
the client uses the SessionTicket extension to resume the
session, but the server does not wish to issue a new ticket,
and therefore does not send a SessionTicket extension."
Lame. We don't get an indication that the server accepted or
rejected our ticket until we see the next handshake message.
If they accepted it we'll see a ChangeCipherSpec message and
if they rejected it we'll see a Certificate message. Let's
flag this case of a non-response and handle it in the CCS parse.
TODO - could also send a sessionId and see if it is returned here.
Spec requires the same sessionId to be returned if ticket is accepted.
*/
ssl->sid->sessionTicketState = SESS_TICKET_STATE_IN_LIMBO;
}
#endif /* USE_STATELESS_SESSION_TICKETS */
#if 0 //TODO moved to extDecode:parseServerHelloExtensions
#ifdef ENABLE_SECURE_REHANDSHAKES
if (renegotiationExt == 0) {
#ifdef REQUIRE_SECURE_REHANDSHAKES
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
psTraceInfo("Srv doesn't support renegotiationInfo\n");
return MATRIXSSL_ERROR;
#else
if (ssl->secureRenegotiationFlag == PS_TRUE) {
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
psTraceInfo("Srv didn't send renegotiationInfo on re-hndshk\n");
return MATRIXSSL_ERROR;
}
#ifndef ENABLE_INSECURE_REHANDSHAKES
/* This case can only be hit if ENABLE_SECURE is on because otherwise
we wouldn't even have got this far because both would be off. */
if (ssl->secureRenegotiationFlag == PS_FALSE &&
ssl->myVerifyDataLen > 0) {
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
psTraceInfo("Srv attempting insecure renegotiation\n");
return MATRIXSSL_ERROR;
}
#endif /* !ENABLE_SECURE_REHANDSHAKES */
#endif /* REQUIRE_SECURE_REHANDSHAKES */
}
#endif /* ENABLE_SECURE_REHANDSHAKES */
#endif
if (ssl->flags & SSL_FLAGS_RESUMED) {
if (sslCreateKeys(ssl) < 0) {
ssl->err = SSL_ALERT_INTERNAL_ERROR;
return MATRIXSSL_ERROR;
}
ssl->hsState = SSL_HS_FINISHED;
} else {
ssl->hsState = SSL_HS_CERTIFICATE;
#ifdef USE_ANON_DH_CIPHER_SUITE
/* Anonymous DH uses SERVER_KEY_EXCHANGE message to send key params */
if (ssl->flags & SSL_FLAGS_ANON_CIPHER) {
ssl->hsState = SSL_HS_SERVER_KEY_EXCHANGE;
}
#endif /* USE_ANON_DH_CIPHER_SUITE */
#ifdef USE_PSK_CIPHER_SUITE
/* PSK ciphers never send a CERTIFICATE message. */
if (ssl->flags & SSL_FLAGS_PSK_CIPHER) {
ssl->hsState = SSL_HS_SERVER_KEY_EXCHANGE;
}
#endif /* USE_PSK_CIPHER_SUITE */
}
*cp = c;
ssl->decState = SSL_HS_SERVER_HELLO;
return PS_SUCCESS;
}
/******************************************************************************/
int32 parseServerKeyExchange(ssl_t *ssl,
unsigned char hsMsgHash[SHA384_HASH_SIZE],
unsigned char **cp, unsigned char *end)
{
unsigned char *c;
#ifdef USE_DHE_CIPHER_SUITE
int32 i;
uint32 pubDhLen, hashSize;
psPool_t *skepkiPool = NULL;
#ifndef USE_ONLY_PSK_CIPHER_SUITE
psDigestContext_t digestCtx;
unsigned char *sigStart = NULL, *sigStop = NULL;
#endif /* USE_ONLY_PSK_CIPHER_SUITE */
#ifdef USE_TLS_1_2
uint32 skeHashSigAlg;
#endif
#ifdef USE_RSA_CIPHER_SUITE
unsigned char sigOut[SHA384_HASH_SIZE];
#endif
#ifdef USE_ECC_CIPHER_SUITE
uint32 res;
const psEccCurve_t *curve;
#endif
void *pkiData = ssl->userPtr;
#endif /* USE_DHE_CIPHER_SUITE */
c = *cp;
psTraceHs(">>> Client parsing SERVER_KEY_EXCHANGE message\n");
#ifdef USE_DHE_CIPHER_SUITE
/* Check the DH status. Could also be a PSK_DHE suite */
if (ssl->flags & SSL_FLAGS_DHE_KEY_EXCH) {
#ifdef USE_PSK_CIPHER_SUITE
if (ssl->flags & SSL_FLAGS_PSK_CIPHER) {
/* Using the value of MAX_HINT_SIZE to know if the user is
expecting a hint. The PSK specification ONLY allows these
hints if the "application profile specification" says to
include them.
Contact Support if you require assistance here */
if (SSL_PSK_MAX_HINT_SIZE > 0) {
if ((end - c) < 2) {
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("Invalid PSK Hint Len\n");
return MATRIXSSL_ERROR;
}
ssl->sec.hintLen = *c << 8; c++;
ssl->sec.hintLen |= *c; c++;
if (ssl->sec.hintLen > 0) {
if ((unsigned short)(end - c) < ssl->sec.hintLen) {
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("Invalid PSK Hint\n");
return MATRIXSSL_ERROR;
}
ssl->sec.hint = psMalloc(ssl->hsPool, ssl->sec.hintLen);
if (ssl->sec.hint == NULL) {
return SSL_MEM_ERROR;
}
memcpy(ssl->sec.hint, c, ssl->sec.hintLen);
c += ssl->sec.hintLen;
}
}
}
#endif /* USE_PSK_CIPHER_SUITE */
#ifdef USE_ECC_CIPHER_SUITE
if (ssl->flags & SSL_FLAGS_ECC_CIPHER) {
/* Entry point for ECDHE SKE parsing */
sigStart = c;
if ((end - c) < 4) { /* ECCurveType, NamedCurve, ECPoint len */
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("Invalid ServerKeyExchange message\n");
return MATRIXSSL_ERROR;
}
/*
Only named curves are currently supported
enum { explicit_prime (1), explicit_char2 (2),
named_curve (3), reserved(248..255) } ECCurveType;
*/
if ((int32)*c != 3) {
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
psTraceIntInfo("Unsupported ECCurveType message %d\n",
(int32)*c);
return MATRIXSSL_ERROR;
}
c++;
/* Next is curveId */
i = *c << 8; c++;
i |= *c; c++;
/* Return -1 if this isn't a curve we specified in client hello */
if (getEccParamById(i, &curve) < 0) {
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
psTraceIntInfo("Error: Could not match EC curve: %d\n", i);
return MATRIXSSL_ERROR;
}
/*
struct {
opaque point <1..2^8-1>;
} ECPoint;
RFC4492
This is the byte string representation of an elliptic curve
point following the conversion routine in Section 4.3.6 of ANSI
X9.62. This byte string may represent an elliptic curve point
in uncompressed or compressed format; it MUST conform to what
client has requested through a Supported Point Formats Extension
if this extension was used.
*/
i = *c; c++;
if ((end - c) < i) {
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("Invalid ServerKeyExchange message\n");
return MATRIXSSL_ERROR;
}
if (psEccNewKey(ssl->hsPool, &ssl->sec.eccKeyPub, curve) < 0) {
return SSL_MEM_ERROR;
}
if (psEccX963ImportKey(ssl->hsPool, c, i,
ssl->sec.eccKeyPub, curve) < 0) {
ssl->err = SSL_ALERT_DECODE_ERROR;
return MATRIXSSL_ERROR;
}
c += i;
sigStop = c;
} else {
#endif /* USE_ECC_CIPHER_SUITE */
#ifdef REQUIRE_DH_PARAMS
/* Entry point for standard DH SKE parsing */
if ((end - c) < 2) {
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("Invalid ServerKeyExchange message\n");
return MATRIXSSL_ERROR;
}
#ifndef USE_ONLY_PSK_CIPHER_SUITE
sigStart = c;
#endif
ssl->sec.dhPLen = *c << 8; c++;
ssl->sec.dhPLen |= *c; c++;
if ((uint32)(end - c) < ssl->sec.dhPLen) {
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("Invalid ServerKeyExchange message\n");
return MATRIXSSL_ERROR;
}
ssl->sec.dhP = psMalloc(ssl->hsPool, ssl->sec.dhPLen);
if (ssl->sec.dhP == NULL) {
return SSL_MEM_ERROR;
}
memcpy(ssl->sec.dhP, c, ssl->sec.dhPLen);
c += ssl->sec.dhPLen;
ssl->sec.dhGLen = *c << 8; c++;
ssl->sec.dhGLen |= *c; c++;
if ((uint32)(end - c) < ssl->sec.dhGLen) {
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("Invalid ServerKeyExchange message\n");
return MATRIXSSL_ERROR;
}
ssl->sec.dhG = psMalloc(ssl->hsPool, ssl->sec.dhGLen);
if (ssl->sec.dhG == NULL) {
return SSL_MEM_ERROR;
}
memcpy(ssl->sec.dhG, c, ssl->sec.dhGLen);
c += ssl->sec.dhGLen;
pubDhLen = *c << 8; c++;
pubDhLen |= *c; c++;
if ((uint32)(end - c) < pubDhLen) {
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("Invalid ServerKeyExchange message\n");
return MATRIXSSL_ERROR;
}
/*
The next bit on the wire is the public key. Assign to
the session in structure format
*/
if ((ssl->sec.dhKeyPub = psMalloc(ssl->hsPool, sizeof(psDhKey_t))) == NULL) {
return MATRIXSSL_ERROR;
}
if (psDhImportPubKey(ssl->hsPool, c, pubDhLen,
ssl->sec.dhKeyPub) < 0) {
psFree(ssl->sec.dhKeyPub, ssl->hsPool);
ssl->sec.dhKeyPub = NULL;
return MATRIXSSL_ERROR;
}
c += pubDhLen;
#ifndef USE_ONLY_PSK_CIPHER_SUITE
sigStop = c;
#endif
/*
Key size is now known for premaster storage. The extra byte
is to account for the cases where the pubkey length ends
up being a byte less than the premaster. The premaster size
is adjusted accordingly when the actual secret is generated.
*/
ssl->sec.premasterSize = ssl->sec.dhPLen;
#ifdef USE_PSK_CIPHER_SUITE
if (ssl->flags & SSL_FLAGS_PSK_CIPHER) {
/*
In the PSK case, the true premaster size is still unknown
but didn't want to change the allocation logic so just
make sure the size is large enough for the additional
PSK and length bytes
*/
ssl->sec.premasterSize += SSL_PSK_MAX_KEY_SIZE + 4;
}
#endif /* USE_PSK_CIPHER_SUITE */
ssl->sec.premaster = psMalloc(ssl->hsPool, ssl->sec.premasterSize);
if (ssl->sec.premaster == NULL) {
return SSL_MEM_ERROR;
}
#ifdef USE_ANON_DH_CIPHER_SUITE
if (ssl->flags & SSL_FLAGS_ANON_CIPHER) {
/*
In the anonymous case, there is no signature to follow
*/
ssl->hsState = SSL_HS_SERVER_HELLO_DONE;
*cp = c;
ssl->decState = SSL_HS_SERVER_KEY_EXCHANGE;
return PS_SUCCESS;
}
#endif /* USE_ANON_DH_CIPHER_SUITE */
#endif /* REQUIRE_DH_PARAMS */
#ifdef USE_ECC_CIPHER_SUITE
}
#endif /* USE_ECC_CIPHER_SUITE */
/*
This layer of authentation is at the key exchange level.
The server has sent a signature of the key material that
the client can validate here.
*/
if ((end - c) < 2) {
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("Invalid ServerKeyExchange message\n");
return MATRIXSSL_ERROR;
}
#ifdef USE_TLS_1_2
hashSize = 0;
if (ssl->flags & SSL_FLAGS_TLS_1_2) {
skeHashSigAlg = *c << 8; c++;
skeHashSigAlg += *c; c++;
if ((skeHashSigAlg >> 8) == 0x4) {
hashSize = SHA256_HASH_SIZE;
} else if ((skeHashSigAlg >> 8) == 0x5) {
hashSize = SHA384_HASH_SIZE;
} else if ((skeHashSigAlg >> 8) == 0x6) {
hashSize = SHA512_HASH_SIZE;
} else if ((skeHashSigAlg >> 8) == 0x2) {
hashSize = SHA1_HASH_SIZE;
} else {
psTraceIntInfo("Unsupported hashAlg SKE parse: %d\n",
skeHashSigAlg);
return MATRIXSSL_ERROR;
}
}
#endif /* USE_TLS_1_2 */
pubDhLen = *c << 8; c++; /* Reusing variable */
pubDhLen |= *c; c++;
if ((uint32)(end - c) < pubDhLen) {
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("Invalid ServerKeyExchange message\n");
return MATRIXSSL_ERROR;
}
#ifdef USE_RSA_CIPHER_SUITE
if (ssl->flags & SSL_FLAGS_DHE_WITH_RSA) {
/*
We are using the public key provided by the server during the
CERTIFICATE message. That cert has already been authenticated
by this point so this signature is to ensure that entity is also
the one negotiating keys with us.
*/
#ifdef USE_TLS_1_2
/* TLS 1.2 uses single hashes everywhere */
if (ssl->flags & SSL_FLAGS_TLS_1_2) {
if (hashSize == SHA256_HASH_SIZE) {
psSha256Init(&digestCtx.sha256);
psSha256Update(&digestCtx.sha256, ssl->sec.clientRandom,
SSL_HS_RANDOM_SIZE);
psSha256Update(&digestCtx.sha256, ssl->sec.serverRandom,
SSL_HS_RANDOM_SIZE);
psSha256Update(&digestCtx.sha256, sigStart,
(uint32)(sigStop - sigStart));
psSha256Final(&digestCtx.sha256, hsMsgHash);
#ifdef USE_SHA384
} else if (hashSize == SHA384_HASH_SIZE) {
psSha384Init(&digestCtx.sha384);
psSha384Update(&digestCtx.sha384, ssl->sec.clientRandom,
SSL_HS_RANDOM_SIZE);
psSha384Update(&digestCtx.sha384, ssl->sec.serverRandom,
SSL_HS_RANDOM_SIZE);
psSha384Update(&digestCtx.sha384, sigStart,
(uint32)(sigStop - sigStart));
psSha384Final(&digestCtx.sha384, hsMsgHash);
#endif /* USE_SHA384 */
#ifdef USE_SHA512
} else if (hashSize == SHA512_HASH_SIZE) {
psSha512Init(&digestCtx.sha512);
psSha512Update(&digestCtx.sha512, ssl->sec.clientRandom,
SSL_HS_RANDOM_SIZE);
psSha512Update(&digestCtx.sha512, ssl->sec.serverRandom,
SSL_HS_RANDOM_SIZE);
psSha512Update(&digestCtx.sha512, sigStart,
(uint32)(sigStop - sigStart));
psSha512Final(&digestCtx.sha512, hsMsgHash);
#endif /* USE_SHA512 */
#ifdef USE_SHA1
} else if (hashSize == SHA1_HASH_SIZE) {
psSha1Init(&digestCtx.sha1);
psSha1Update(&digestCtx.sha1, ssl->sec.clientRandom,
SSL_HS_RANDOM_SIZE);
psSha1Update(&digestCtx.sha1, ssl->sec.serverRandom,
SSL_HS_RANDOM_SIZE);
psSha1Update(&digestCtx.sha1, sigStart,
(uint32)(sigStop - sigStart));
psSha1Final(&digestCtx.sha1, hsMsgHash);
#endif
} else {
return MATRIXSSL_ERROR;
}
} else {
#ifdef USE_MD5
psMd5Init(&digestCtx.md5);
psMd5Update(&digestCtx.md5, ssl->sec.clientRandom,
SSL_HS_RANDOM_SIZE);
psMd5Update(&digestCtx.md5, ssl->sec.serverRandom,
SSL_HS_RANDOM_SIZE);
psMd5Update(&digestCtx.md5, sigStart,
(uint32)(sigStop - sigStart));
psMd5Final(&digestCtx.md5, hsMsgHash);
psSha1Init(&digestCtx.sha1);
psSha1Update(&digestCtx.sha1, ssl->sec.clientRandom,
SSL_HS_RANDOM_SIZE);
psSha1Update(&digestCtx.sha1, ssl->sec.serverRandom,
SSL_HS_RANDOM_SIZE);
psSha1Update(&digestCtx.sha1, sigStart,
(uint32)(sigStop - sigStart));
psSha1Final(&digestCtx.sha1, hsMsgHash + MD5_HASH_SIZE);
#else
return MATRIXSSL_ERROR;
#endif
}
#else /* USE_TLS_1_2 */
/*
The signature portion is an MD5 and SHA1 concat of the randoms
and the contents of this server key exchange message.
*/
psMd5Init(&digestCtx.md5);
psMd5Update(&digestCtx.md5, ssl->sec.clientRandom,
SSL_HS_RANDOM_SIZE);
psMd5Update(&digestCtx.md5, ssl->sec.serverRandom,
SSL_HS_RANDOM_SIZE);
psMd5Update(&digestCtx.md5, sigStart,
(uint32)(sigStop - sigStart));
psMd5Final(&digestCtx.md5, hsMsgHash);
psSha1Init(&digestCtx.sha1);
psSha1Update(&digestCtx.sha1, ssl->sec.clientRandom,
SSL_HS_RANDOM_SIZE);
psSha1Update(&digestCtx.sha1, ssl->sec.serverRandom,
SSL_HS_RANDOM_SIZE);
psSha1Update(&digestCtx.sha1, sigStart,
(uint32)(sigStop - sigStart));
psSha1Final(&digestCtx.sha1, hsMsgHash + MD5_HASH_SIZE);
#endif /* USE_TLS_1_2 */
#ifdef USE_TLS_1_2
if (ssl->flags & SSL_FLAGS_TLS_1_2) {
/* TLS 1.2 doesn't just sign the straight hash so we can't
pass it through the normal public decryption becuase
that expects an output length of a known size. These
signatures are done on elements with some ASN.1
wrapping so a special decryption with parse is needed */
if ((i = pubRsaDecryptSignedElement(skepkiPool,
&ssl->sec.cert->publicKey.key.rsa, c, pubDhLen, sigOut,
hashSize, pkiData)) < 0) {
psTraceInfo("Can't decrypt serverKeyExchange sig\n");
ssl->err = SSL_ALERT_BAD_CERTIFICATE;
return MATRIXSSL_ERROR;
}
} else {
hashSize = MD5_HASH_SIZE + SHA1_HASH_SIZE;
if ((i = psRsaDecryptPub(skepkiPool,
&ssl->sec.cert->publicKey.key.rsa, c, pubDhLen, sigOut,
hashSize, pkiData)) < 0) {
psTraceInfo("Can't decrypt server key exchange sig\n");
ssl->err = SSL_ALERT_BAD_CERTIFICATE;
return MATRIXSSL_ERROR;
}
}
#else /* ! USE_TLS_1_2 */
hashSize = MD5_HASH_SIZE + SHA1_HASH_SIZE;
if ((i = psRsaDecryptPub(skepkiPool, &ssl->sec.cert->publicKey.key.rsa,
c, pubDhLen, sigOut, hashSize, pkiData)) < 0) {
psTraceInfo("Unable to decrypt server key exchange sig\n");
ssl->err = SSL_ALERT_BAD_CERTIFICATE;
return MATRIXSSL_ERROR;
}
#endif /* USE_TLS_1_2 */
/* Now have hash from the server. Create ours and check match */
c += pubDhLen;
if (memcmpct(sigOut, hsMsgHash, hashSize) != 0) {
psTraceInfo("Fail to verify serverKeyExchange sig\n");
ssl->err = SSL_ALERT_BAD_CERTIFICATE;
return MATRIXSSL_ERROR;
}
}
#endif /* USE_RSA_CIPHER_SUITE */
#ifdef USE_ECC_CIPHER_SUITE
if (ssl->flags & SSL_FLAGS_DHE_WITH_DSA) {
/*
RFC4492: The default hash function is SHA-1, and sha_size is 20.
*/
#ifdef USE_TLS_1_2
if (ssl->flags & SSL_FLAGS_TLS_1_2 &&
(hashSize == SHA256_HASH_SIZE)) {
psSha256Init(&digestCtx.sha256);
psSha256Update(&digestCtx.sha256, ssl->sec.clientRandom,
SSL_HS_RANDOM_SIZE);
psSha256Update(&digestCtx.sha256, ssl->sec.serverRandom,
SSL_HS_RANDOM_SIZE);
psSha256Update(&digestCtx.sha256, sigStart,
(int32)(sigStop - sigStart));
psSha256Final(&digestCtx.sha256, hsMsgHash);
#ifdef USE_SHA384
} else if (ssl->flags & SSL_FLAGS_TLS_1_2 &&
(hashSize == SHA384_HASH_SIZE)) {
psSha384Init(&digestCtx.sha384);
psSha384Update(&digestCtx.sha384, ssl->sec.clientRandom,
SSL_HS_RANDOM_SIZE);
psSha384Update(&digestCtx.sha384, ssl->sec.serverRandom,
SSL_HS_RANDOM_SIZE);
psSha384Update(&digestCtx.sha384, sigStart,
(int32)(sigStop - sigStart));
psSha384Final(&digestCtx.sha384, hsMsgHash);
#endif
#ifdef USE_SHA512
} else if (hashSize == SHA512_HASH_SIZE) {
psSha512Init(&digestCtx.sha512);
psSha512Update(&digestCtx.sha512, ssl->sec.clientRandom,
SSL_HS_RANDOM_SIZE);
psSha512Update(&digestCtx.sha512, ssl->sec.serverRandom,
SSL_HS_RANDOM_SIZE);
psSha512Update(&digestCtx.sha512, sigStart,
(uint32)(sigStop - sigStart));
psSha512Final(&digestCtx.sha512, hsMsgHash);
#endif /* USE_SHA512 */
#ifdef USE_SHA1
} else if (ssl->minVer < TLS_1_2_MIN_VER ||
#ifdef USE_DTLS
ssl->minVer == DTLS_MIN_VER ||
#endif
((ssl->flags & SSL_FLAGS_TLS_1_2) &&
(hashSize == SHA1_HASH_SIZE))) {
hashSize = SHA1_HASH_SIZE;
psSha1Init(&digestCtx.sha1);
psSha1Update(&digestCtx.sha1, ssl->sec.clientRandom,
SSL_HS_RANDOM_SIZE);
psSha1Update(&digestCtx.sha1, ssl->sec.serverRandom,
SSL_HS_RANDOM_SIZE);
psSha1Update(&digestCtx.sha1, sigStart,
(int32)(sigStop - sigStart));
psSha1Final(&digestCtx.sha1, hsMsgHash);
#endif
} else {
return MATRIXSSL_ERROR;
}
#else /* USE_TLS_1_2 */
hashSize = SHA1_HASH_SIZE;
psSha1Init(&digestCtx.sha1);
psSha1Update(&digestCtx.sha1, ssl->sec.clientRandom,
SSL_HS_RANDOM_SIZE);
psSha1Update(&digestCtx.sha1, ssl->sec.serverRandom,
SSL_HS_RANDOM_SIZE);
psSha1Update(&digestCtx.sha1, sigStart,
(int32)(sigStop - sigStart));
psSha1Final(&digestCtx.sha1, hsMsgHash);
#endif /* USE_TLS_1_2 */
i = 0;
if ((res = psEccDsaVerify(skepkiPool,
&ssl->sec.cert->publicKey.key.ecc,
hsMsgHash, hashSize,
c, pubDhLen,
&i, pkiData)) != 0) {
psTraceInfo("ECDSA signature validation failed\n");
ssl->err = SSL_ALERT_BAD_CERTIFICATE;
return MATRIXSSL_ERROR;
}
c += pubDhLen;
/*
The validation code comes out of the final parameter
*/
if (i != 1) {
psTraceInfo("Can't verify serverKeyExchange sig\n");
ssl->err = SSL_ALERT_BAD_CERTIFICATE;
return MATRIXSSL_ERROR;
}
}
#endif /* USE_ECC_CIPHER_SUITE */
ssl->hsState = SSL_HS_SERVER_HELLO_DONE;
}
#endif /* USE_DHE_CIPHER_SUITE */
#ifdef USE_PSK_CIPHER_SUITE
/*
Entry point for basic PSK ciphers (not DHE or RSA) parsing SKE message
*/
if (ssl->flags & SSL_FLAGS_PSK_CIPHER) {
if ((end - c) < 2) {
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("Invalid ServerKeyExchange message\n");
return MATRIXSSL_ERROR;
}
ssl->sec.hintLen = *c << 8; c++;
ssl->sec.hintLen |= *c; c++;
if ((uint32)(end - c) < ssl->sec.hintLen) {
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("Invalid ServerKeyExchange message\n");
return MATRIXSSL_ERROR;
}
if (ssl->sec.hintLen > 0) {
ssl->sec.hint = psMalloc(ssl->hsPool, ssl->sec.hintLen);
if (ssl->sec.hint == NULL) {
return SSL_MEM_ERROR;
}
memcpy(ssl->sec.hint, c, ssl->sec.hintLen);
c += ssl->sec.hintLen;
}
ssl->hsState = SSL_HS_SERVER_HELLO_DONE;
}
#endif /* USE_PSK_CIPHER_SUITE */
*cp = c;
ssl->decState = SSL_HS_SERVER_KEY_EXCHANGE;
return PS_SUCCESS;
}
#ifdef USE_OCSP
int32 parseCertificateStatus(ssl_t *ssl, int32 hsLen, unsigned char **cp,
unsigned char *end)
{
unsigned char *c;
int32_t responseLen, rc;
mOCSPResponse_t response;
/*
struct {
CertificateStatusType status_type;
select (status_type) {
case ocsp: OCSPResponse;
} response;
} CertificateStatus;
enum { ocsp(1), (255) } CertificateStatusType;
opaque OCSPResponse<1..2^24-1>;
An "ocsp_response" contains a complete, DER-encoded OCSP response
(using the ASN.1 type OCSPResponse defined in [RFC6960]). Only one
OCSP response may be sent.
*/
c = *cp;
if ((end - c) < 4) {
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("Invalid CertificateStatus length\n");
return MATRIXSSL_ERROR;
}
if (*c != 0x1) {
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
psTraceInfo("Invalid status_type in certificateStatus message\n");
return MATRIXSSL_ERROR;
}
c++;
responseLen = *c << 16; c++;
responseLen |= *c << 8; c++;
responseLen |= *c; c++;
if (responseLen > (end - c)) {
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("Malformed CertificateStatus message\n");
return MATRIXSSL_ERROR;
}
memset(&response, 0x0, sizeof(mOCSPResponse_t));
if ((rc = parseOCSPResponse(ssl->hsPool, responseLen, &c, end, &response))
< 0) {
/* Couldn't parse or no good responses in stream */
psX509FreeCert(response.OCSPResponseCert);
ssl->err = SSL_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE;
psTraceInfo("Unable to parse OCSPResponse\n");
return MATRIXSSL_ERROR;
}
*cp = c;
/* Authenticate the parsed response based on the registered CA files
AND passing through the server chain as well because some real
world examples we have seen use the intermediate cert as the
OCSP responder */
if ((rc = validateOCSPResponse(ssl->hsPool, ssl->keys->CAcerts,
ssl->sec.cert, &response)) < 0) {
/* Couldn't validate */
psX509FreeCert(response.OCSPResponseCert);
ssl->err = SSL_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE;
psTraceInfo("Unable to validate OCSPResponse\n");
return MATRIXSSL_ERROR;
}
psX509FreeCert(response.OCSPResponseCert);
/* Same logic to determine next state as in end of SSL_HS_CERTIFICATE */
ssl->hsState = SSL_HS_SERVER_HELLO_DONE;
#ifdef USE_DHE_CIPHER_SUITE
if (ssl->flags & SSL_FLAGS_DHE_KEY_EXCH) {
ssl->hsState = SSL_HS_SERVER_KEY_EXCHANGE;
}
#endif /* USE_DHE_CIPHER_SUITE */
ssl->decState = SSL_HS_CERTIFICATE_STATUS;
return PS_SUCCESS;
}
#endif /* USE_OCSP */
/******************************************************************************/
int32 parseServerHelloDone(ssl_t *ssl, int32 hsLen, unsigned char **cp,
unsigned char *end)
{
unsigned char *c;
#if defined(USE_DHE_CIPHER_SUITE) || defined(REQUIRE_DH_PARAMS)
int32 rc;
void *pkiData = ssl->userPtr;
#endif /* DH */
c = *cp;
psTraceHs(">>> Client parsing SERVER_HELLO_DONE message\n");
if (hsLen != 0) {
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
psTraceInfo("Invalid ServerHelloDone message\n");
return MATRIXSSL_ERROR;
}
#ifdef USE_DHE_CIPHER_SUITE
if (ssl->flags & SSL_FLAGS_DHE_KEY_EXCH) {
#ifdef USE_ECC_CIPHER_SUITE
if (ssl->flags & SSL_FLAGS_ECC_CIPHER) {
/* Set up our private side of the ECC key based on the agreed
upon curve */
if (psEccNewKey(ssl->sec.eccDhKeyPool, &ssl->sec.eccKeyPriv,
ssl->sec.eccKeyPub->curve) < 0) {
return PS_MEM_FAIL;
}
if ((rc = matrixSslGenEphemeralEcKey(ssl->keys,
ssl->sec.eccKeyPriv, ssl->sec.eccKeyPub->curve,
pkiData)) < 0) {
psEccDeleteKey(&ssl->sec.eccKeyPriv);
psTraceInfo("GenEphemeralEcc failed\n");
ssl->err = SSL_ALERT_INTERNAL_ERROR;
return MATRIXSSL_ERROR;
}
} else {
#endif
#ifdef REQUIRE_DH_PARAMS
/* Can safely set up our ssl->sec.dhKeyPriv with DH keys
based on the parameters passed over from the server.
Storing these in a client specific DH pool because at
handshake pool creation, the size for PKI was not known */
if ((ssl->sec.dhKeyPriv = psMalloc(ssl->sec.dhKeyPool,
sizeof(psDhKey_t))) == NULL) {
return MATRIXSSL_ERROR;
}
if ((rc = psDhGenKey(ssl->sec.dhKeyPool, ssl->sec.dhPLen,
ssl->sec.dhP, ssl->sec.dhPLen, ssl->sec.dhG,
ssl->sec.dhGLen, ssl->sec.dhKeyPriv, pkiData)) < 0) {
psFree(ssl->sec.dhKeyPriv, ssl->sec.dhKeyPool);
ssl->sec.dhKeyPriv = NULL;
return MATRIXSSL_ERROR;
}
/* Freeing as we go. No more need for G */
psFree(ssl->sec.dhG, ssl->hsPool); ssl->sec.dhG = NULL;
#endif /* REQUIRE_DH_PARAMS */
#ifdef USE_ECC_CIPHER_SUITE
}
#endif /* USE_ECC_CIPHER_SUITE */
}
#endif /* USE_DHE_CIPHER_SUITE */
ssl->hsState = SSL_HS_FINISHED;
*cp = c;
ssl->decState = SSL_HS_SERVER_HELLO_DONE;
return SSL_PROCESS_DATA;
}
/******************************************************************************/
#ifndef USE_ONLY_PSK_CIPHER_SUITE
int32 parseCertificateRequest(ssl_t *ssl, int32 hsLen, unsigned char **cp,
unsigned char *end)
{
#ifdef USE_CLIENT_AUTH
psX509Cert_t *cert;
int32 i;
#endif
int32 certTypeLen, certChainLen;
uint32 certLen;
#ifdef USE_TLS_1_2
uint32 sigAlgMatch;
#ifdef USE_CLIENT_AUTH
uint32 hashSigAlg;
#endif
#endif
unsigned char *c;
c = *cp;
psTraceHs(">>> Client parsing CERTIFICATE_REQUEST message\n");
if (hsLen < 4) {
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
psTraceInfo("Invalid Certificate Request message\n");
return MATRIXSSL_ERROR;
}
/* Currently ignoring the authentication type request because it was
underspecified up to TLS 1.1 and TLS 1.2 is now taking care of this
with the supported_signature_algorithms handling */
certTypeLen = *c++;
if (end - c < certTypeLen) {
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("Invalid Certificate Request message\n");
return MATRIXSSL_ERROR;
}
c += certTypeLen; /* Skipping (RSA_SIGN etc.) */
#ifdef USE_TLS_1_2
sigAlgMatch = 0;
if (ssl->flags & SSL_FLAGS_TLS_1_2) {
/* supported_signature_algorithms field
enum {none(0), md5(1), sha1(2), sha224(3), sha256(4), sha384(5),
sha512(6), (255) } HashAlgorithm;
enum { anonymous(0), rsa(1), dsa(2), ecdsa(3), (255) } SigAlg */
if (end - c < 2) {
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("Invalid SigHash in Certificate Request message\n");
return MATRIXSSL_ERROR;
}
certChainLen = *c << 8; c++; /* just borrowing this variable */
certChainLen |= *c; c++;
if (end - c < certChainLen) {
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("Invalid SigHash in Certificate Request message\n");
return MATRIXSSL_ERROR;
}
#ifdef USE_CLIENT_AUTH
/* Going to adhere to this supported_signature_algorithm to
be compliant with the spec. This is now the first line
of testing about what certificates the server will accept.
If any of our certs do not use a signature algorithm
that the server supports we will flag that here which will
ultimately result in an empty CERTIFICATE message and
no CERTIFICATE_VERIFY message. We're going to convert
MD5 to use SHA1 instead though.
Start by building a bitmap of supported algs */
hashSigAlg = 0;
while (certChainLen >= 2) {
i = HASH_SIG_MASK(c[0], c[1]);
/* Our own ssl->hashSigAlg is the list we support. So choose
from those only */
if (ssl->hashSigAlg & i) {
hashSigAlg |= i;
}
c += 2;
certChainLen -= 2;
}
/* RFC: The end-entity certificate provided by the client MUST
contain a key that is compatible with certificate_types.
If the key is a signature key, it MUST be usable with some
hash/signature algorithm pair in supported_signature_algorithms.
So not only do we have to check the signature algorithm, we
have to check the pub key type as well. */
sigAlgMatch = 1; /* de-flag if we hit unsupported one */
if (ssl->keys == NULL || ssl->keys->cert == NULL) {
sigAlgMatch = 0;
} else {
cert = ssl->keys->cert;
while (cert) {
if (cert->pubKeyAlgorithm == OID_RSA_KEY_ALG) {
if (!(hashSigAlg & HASH_SIG_SHA1_RSA_MASK) &&
#ifdef USE_SHA384
!(hashSigAlg & HASH_SIG_SHA384_RSA_MASK) &&
#endif
!(hashSigAlg & HASH_SIG_SHA256_RSA_MASK) &&
!(hashSigAlg & HASH_SIG_MD5_RSA_MASK)) {
sigAlgMatch = 0;
}
}
if (cert->sigAlgorithm == OID_SHA1_RSA_SIG ||
cert->sigAlgorithm == OID_MD5_RSA_SIG) {
if (!(hashSigAlg & HASH_SIG_SHA1_RSA_MASK)) {
sigAlgMatch = 0;
}
}
if (cert->sigAlgorithm == OID_SHA256_RSA_SIG) {
if (!(hashSigAlg & HASH_SIG_SHA256_RSA_MASK)) {
sigAlgMatch = 0;
}
}
#ifdef USE_SHA384
if (cert->sigAlgorithm == OID_SHA384_RSA_SIG) {
if (!(hashSigAlg & HASH_SIG_SHA384_RSA_MASK)) {
sigAlgMatch = 0;
}
}
#endif
#ifdef USE_SHA512
if (cert->sigAlgorithm == OID_SHA512_RSA_SIG) {
if (!(hashSigAlg & HASH_SIG_SHA512_RSA_MASK)) {
sigAlgMatch = 0;
}
}
#endif
#ifdef USE_ECC
if (cert->pubKeyAlgorithm == OID_ECDSA_KEY_ALG) {
if (!(hashSigAlg & HASH_SIG_SHA1_ECDSA_MASK) &&
#ifdef USE_SHA384
!(hashSigAlg & HASH_SIG_SHA384_ECDSA_MASK) &&
#endif
#ifdef USE_SHA512
!(hashSigAlg & HASH_SIG_SHA512_ECDSA_MASK) &&
#endif
!(hashSigAlg & HASH_SIG_SHA256_ECDSA_MASK) &&
!(hashSigAlg & HASH_SIG_SHA1_ECDSA_MASK)) {
sigAlgMatch = 0;
}
}
if (cert->sigAlgorithm == OID_SHA1_ECDSA_SIG) {
if (!(hashSigAlg & HASH_SIG_SHA1_ECDSA_MASK)) {
sigAlgMatch = 0;
}
}
if (cert->sigAlgorithm == OID_SHA256_ECDSA_SIG) {
if (!(hashSigAlg & HASH_SIG_SHA256_ECDSA_MASK)) {
sigAlgMatch = 0;
}
}
#ifdef USE_SHA384
if (cert->sigAlgorithm == OID_SHA384_ECDSA_SIG) {
if (!(hashSigAlg & HASH_SIG_SHA384_ECDSA_MASK)) {
sigAlgMatch = 0;
}
}
#endif
#ifdef USE_SHA512
if (cert->sigAlgorithm == OID_SHA512_ECDSA_SIG) {
if (!(hashSigAlg & HASH_SIG_SHA512_ECDSA_MASK)) {
sigAlgMatch = 0;
}
}
#endif
#endif /* USE_ECC */
cert = cert->next;
}
}
#endif /* USE_CLIENT_AUTH */
c += certChainLen;
}
#endif /* TLS_1_2 */
certChainLen = 0;
if (end - c >= 2) {
certChainLen = *c << 8; c++;
certChainLen |= *c; c++;
if (end - c < certChainLen) {
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("Invalid Certificate Request message\n");
return MATRIXSSL_ERROR;
}
}
/* Check the passed in DNs against our cert issuer to see if they match.
Only supporting a single cert on the client side. */
ssl->sec.certMatch = 0;
#ifdef USE_CLIENT_AUTH
/* If the user has actually gone to the trouble to load a certificate
to reply with, we flag that here so there is some flexibility as
to whether we want to reply with something (even if it doesn't match)
just in case the server is willing to do a custom test of the cert */
if (ssl->keys != NULL && ssl->keys->cert) {
ssl->sec.certMatch = SSL_ALLOW_ANON_CONNECTION;
}
#endif /* USE_CLIENT_AUTH */
while (certChainLen > 2) {
certLen = *c << 8; c++;
certLen |= *c; c++;
if ((uint32)(end - c) < certLen || certLen <= 0 ||
(int32)certLen > certChainLen) {
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("Invalid CertificateRequest message\n");
return MATRIXSSL_ERROR;
}
certChainLen -= 2;
#ifdef USE_CLIENT_AUTH
/* Can parse the message, but will not look for a match. The
setting of certMatch to 1 will trigger the correct response
in sslEncode */
if (ssl->keys != NULL && ssl->keys->cert) {
/* Flag a match if the hash of the DN issuer is identical */
if (ssl->keys->cert->issuer.dnencLen == certLen) {
if (memcmp(ssl->keys->cert->issuer.dnenc, c, certLen) == 0){
ssl->sec.certMatch = 1;
}
}
}
#endif /* USE_CLIENT_AUTH */
c += certLen;
certChainLen -= certLen;
}
#ifdef USE_TLS_1_2
if (ssl->flags & SSL_FLAGS_TLS_1_2) {
/* We let the DN parse complete but if we didn't get a sigAlgMatch
from the previous test we're going to adhere to that for spec
compliance. So here goes */
if (sigAlgMatch == 0) {
ssl->sec.certMatch = 0;
}
}
#endif
ssl->hsState = SSL_HS_SERVER_HELLO_DONE;
*cp = c;
ssl->decState = SSL_HS_CERTIFICATE_REQUEST;
return PS_SUCCESS;
}
#endif /* USE_ONLY_PSK_CIPHER_SUITE */
#endif /* USE_CLIENT_SIDE_SSL */
/******************************************************************************/
int32 parseFinished(ssl_t *ssl, int32 hsLen,
unsigned char hsMsgHash[SHA384_HASH_SIZE],
unsigned char **cp,
unsigned char *end)
{
int32 rc;
unsigned char *c;
rc = PS_SUCCESS;
c = *cp;
/* Before the finished handshake message, we should have seen the
CHANGE_CIPHER_SPEC message come through in the record layer, which
would have activated the read cipher, and set the READ_SECURE flag.
This is the first handshake message that was sent securely. */
psTraceStrHs(">>> %s parsing FINISHED message\n",
(ssl->flags & SSL_FLAGS_SERVER) ? "Server" : "Client");
if (!(ssl->flags & SSL_FLAGS_READ_SECURE)) {
ssl->err = SSL_ALERT_UNEXPECTED_MESSAGE;
psTraceInfo("Finished before ChangeCipherSpec\n");
return MATRIXSSL_ERROR;
}
/* The contents of the finished message is a 16 byte MD5 hash followed
by a 20 byte sha1 hash of all the handshake messages so far, to verify
that nothing has been tampered with while we were still insecure.
Compare the message to the value we calculated at the beginning of
this function. */
#ifdef USE_TLS
if (ssl->flags & SSL_FLAGS_TLS) {
if (hsLen != TLS_HS_FINISHED_SIZE) {
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("Invalid Finished length\n");
return MATRIXSSL_ERROR;
}
} else {
#endif /* USE_TLS */
if (hsLen != MD5_HASH_SIZE + SHA1_HASH_SIZE) {
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("Invalid Finished length\n");
return MATRIXSSL_ERROR;
}
#ifdef USE_TLS
}
#endif /* USE_TLS */
if ((int32)(end - c) < hsLen) {
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("Invalid Finished length\n");
return MATRIXSSL_ERROR;
}
if (memcmpct(c, hsMsgHash, hsLen) != 0) {
ssl->err = SSL_ALERT_DECRYPT_ERROR;
psTraceInfo("Invalid handshake msg hash\n");
return MATRIXSSL_ERROR;
}
#ifdef ENABLE_SECURE_REHANDSHAKES
/* Got the peer verify_data for secure renegotiations */
memcpy(ssl->peerVerifyData, c, hsLen);
ssl->peerVerifyDataLen = hsLen;
#endif /* ENABLE_SECURE_REHANDSHAKES */
c += hsLen;
ssl->hsState = SSL_HS_DONE;
/* Now that we've parsed the Finished message, if we're a resumed
connection, we're done with handshaking, otherwise, we return
SSL_PROCESS_DATA to get our own cipher spec and finished messages
sent out by the caller. */
if (ssl->flags & SSL_FLAGS_SERVER) {
if (!(ssl->flags & SSL_FLAGS_RESUMED)) {
rc = SSL_PROCESS_DATA;
} else {
#ifdef USE_SSL_INFORMATIONAL_TRACE
/* Server side resumed completion */
matrixSslPrintHSDetails(ssl);
#endif
}
} else {
#ifdef USE_STATELESS_SESSION_TICKETS
/* Now that FINISHED is verified, we can mark the ticket as
valid to conform to section 3.3 of the 5077 RFC */
if (ssl->sid && ssl->sid->sessionTicketLen > 0) {
ssl->sid->sessionTicketState = SESS_TICKET_STATE_USING_TICKET;
}
#endif
if (ssl->flags & SSL_FLAGS_RESUMED) {
rc = SSL_PROCESS_DATA;
} else {
#ifdef USE_SSL_INFORMATIONAL_TRACE
/* Client side standard completion */
matrixSslPrintHSDetails(ssl);
#endif
}
}
#ifndef USE_ONLY_PSK_CIPHER_SUITE
#if defined(USE_CLIENT_SIDE_SSL) || defined(USE_CLIENT_AUTH)
/* There is also an attempt to free the cert during
the sending of the finished message to deal with client
and server and differing handshake types. Both cases are
attempted keep the lifespan of this allocation as short as possible. */
if (ssl->sec.cert) {
psX509FreeCert(ssl->sec.cert);
ssl->sec.cert = NULL;
}
#endif /* USE_CLIENT_SIDE_SSL || USE_CLIENT_AUTH */
#endif /* !USE_ONLY_PSK_CIPHER_SUITE */
#ifdef USE_DTLS
if (ssl->flags & SSL_FLAGS_DTLS) {
/* A successful parse of the FINISHED message means the record sequence
numbers have been reset so we need to clear out our replay detector */
zeroSixByte(ssl->lastRsn);
/* This will just be set between CCS parse and FINISHED parse */
ssl->parsedCCS = 1;
/* Look at the comment in the fragment parsing code to see the
justification of placing this free here. Bascially, this
is the best place to do it because we know there can be no
further fragmented messages. More importantly, the
hanshake pool is being freed here! */
if (ssl->fragMessage != NULL) {
psFree(ssl->fragMessage, ssl->hsPool);
ssl->fragMessage = NULL;
}
}
/* Premaster was not freed at the usual spot becasue of retransmit cases */
if (ssl->sec.premaster) {
psFree(ssl->sec.premaster, ssl->hsPool); ssl->sec.premaster = NULL;
}
if (ssl->ckeMsg) {
psFree(ssl->ckeMsg, ssl->hsPool); ssl->ckeMsg = NULL;
}
if (ssl->certVerifyMsg) {
psFree(ssl->certVerifyMsg, ssl->hsPool); ssl->certVerifyMsg = NULL;
}
#if defined(USE_PSK_CIPHER_SUITE) && defined(USE_CLIENT_SIDE_SSL)
if (ssl->sec.hint) {
psFree(ssl->sec.hint, ssl->hsPool); ssl->sec.hint = NULL;
}
#endif
#endif /* USE_DTLS */
ssl->hsPool = NULL;
*cp = c;
ssl->decState = SSL_HS_FINISHED;
return rc;
}
/******************************************************************************/
#ifndef USE_ONLY_PSK_CIPHER_SUITE
#if defined(USE_CLIENT_SIDE_SSL) || defined(USE_CLIENT_AUTH)
int32 parseCertificate(ssl_t *ssl, unsigned char **cp, unsigned char *end)
{
psX509Cert_t *currentCert, *cert, *foundIssuer;
unsigned char *c;
uint32 certLen;
int32 rc, i, certChainLen, parseLen = 0;
void *pkiData = ssl->userPtr;
psTraceStrHs(">>> %s parsing CERTIFICATE message\n",
(ssl->flags & SSL_FLAGS_SERVER) ? "Server" : "Client");
c = *cp;
#ifdef USE_CERT_CHAIN_PARSING
if (ssl->rec.partial) {
/* The test for a first pass is against the record header length */
if (ssl->rec.hsBytesParsed == ssl->recordHeadLen) {
/* Account for the one-time header portion parsed above
and the 3 byte cert chain length about to be parsed below.
The minimum length tests have already been performed. */
ssl->rec.hsBytesParsed += ssl->hshakeHeadLen + 3;
} else {
goto SKIP_CERT_CHAIN_INIT;
}
}
#endif
if (end - c < 3) {
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("Invalid Certificate message\n");
return MATRIXSSL_ERROR;
}
certChainLen = *c << 16; c++;
certChainLen |= *c << 8; c++;
certChainLen |= *c; c++;
if (certChainLen == 0) {
#ifdef SERVER_WILL_ACCEPT_EMPTY_CLIENT_CERT_MSG
if (ssl->flags & SSL_FLAGS_SERVER) {
ssl->err = SSL_ALERT_BAD_CERTIFICATE;
ssl->flags &= ~SSL_FLAGS_CLIENT_AUTH;
goto STRAIGHT_TO_USER_CALLBACK;
}
#endif
if (ssl->majVer == SSL3_MAJ_VER && ssl->minVer == SSL3_MIN_VER) {
ssl->err = SSL_ALERT_NO_CERTIFICATE;
} else {
ssl->err = SSL_ALERT_BAD_CERTIFICATE;
}
psTraceInfo("No certificate sent to verify\n");
return MATRIXSSL_ERROR;
}
if (end - c < 3) {
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("Invalid Certificate message\n");
return MATRIXSSL_ERROR;
}
#ifdef USE_CERT_CHAIN_PARSING
SKIP_CERT_CHAIN_INIT:
if (ssl->rec.partial) {
/* It is possible to activate the CERT_STREAM_PARSE feature and not
receive a cert chain in multiple buffers. If we are not flagged
for 'partial' parsing, we can drop into the standard parse case */
while (end - c > 0) {
certLen = *c << 16; c++;
certLen |= *c << 8; c++;
certLen |= *c; c++;
if ((parseLen = parseSingleCert(ssl, c, end, certLen)) < 0 ) {
return parseLen;
}
ssl->rec.hsBytesParsed += parseLen + 3; /* 3 for certLen */
c += parseLen;
}
if (ssl->rec.hsBytesParsed < ssl->rec.trueLen) {
*cp = c;
return MATRIXSSL_SUCCESS;
}
psAssert(ssl->rec.hsBytesParsed == ssl->rec.trueLen);
/* Got it all. Disable the stream mechanism. */
ssl->rec.partial = 0x0;
ssl->rec.hsBytesParsed = 0;
ssl->rec.hsBytesHashed = 0;
} else {
psAssert(certChainLen > 0);
#endif /* USE_CERT_CHAIN_PARSING */
i = 0;
currentCert = NULL;
/* Chain must be at least 3 b certLen */
while (certChainLen >= 3) {
certLen = *c << 16; c++;
certLen |= *c << 8; c++;
certLen |= *c; c++;
certChainLen -= 3;
if ((uint32)(end - c) < certLen || (int32)certLen > certChainLen) {
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("Invalid certificate length\n");
return MATRIXSSL_ERROR;
}
/*
Extract the binary cert message into the cert structure
*/
if ((parseLen = psX509ParseCert(ssl->hsPool, c, certLen, &cert, 0))
< 0) {
psX509FreeCert(cert);
if (parseLen == PS_MEM_FAIL) {
ssl->err = SSL_ALERT_INTERNAL_ERROR;
} else {
ssl->err = SSL_ALERT_BAD_CERTIFICATE;
}
return MATRIXSSL_ERROR;
}
c += parseLen;
if (i++ == 0) {
ssl->sec.cert = cert;
currentCert = ssl->sec.cert;
} else {
currentCert->next = cert;
currentCert = currentCert->next;
}
certChainLen -= certLen;
}
#ifdef USE_CERT_CHAIN_PARSING
}
#endif /* USE_CERT_CHAIN_PARSING */
#ifdef USE_CLIENT_SIDE_SSL
/* Now want to test to see if supplied child-most cert is the appropriate
pubkey algorithm for the chosen cipher suite. Have seen test
cases with OpenSSL where an RSA cert will be sent for an ECDHE_ECDSA
suite, for example. Just testing on the client side because client
auth is a bit more flexible on the algorithm choices. */
if (!(ssl->flags & SSL_FLAGS_SERVER)) {
if (csCheckCertAgainstCipherSuite(ssl->sec.cert->publicKey.type,
ssl->cipher->type) == 0) {
psTraceIntInfo("Server sent bad pubkey type for cipher suite %d\n",
ssl->cipher->type);
ssl->err = SSL_ALERT_UNSUPPORTED_CERTIFICATE;
return MATRIXSSL_ERROR;
}
}
#endif
/* Time to authenticate the supplied cert against our CAs */
rc = matrixValidateCerts(ssl->hsPool, ssl->sec.cert,
ssl->keys == NULL ? NULL : ssl->keys->CAcerts, ssl->expectedName,
&foundIssuer, pkiData, ssl->memAllocPtr);
if (rc == PS_MEM_FAIL) {
ssl->err = SSL_ALERT_INTERNAL_ERROR;
return MATRIXSSL_ERROR;
}
/* Now walk the subject certs and convert any parse or authentication error
into an SSL alert. The alerts SHOULD be read by the user callback
to determine whether they are fatal or not. If no user callback,
the first alert will be considered fatal. */
cert = ssl->sec.cert;
while (cert) {
if (ssl->err != SSL_ALERT_NONE) {
break; /* The first alert is the logical one to send */
}
switch (cert->authStatus) {
case PS_CERT_AUTH_FAIL_SIG:
ssl->err = SSL_ALERT_BAD_CERTIFICATE;
break;
case PS_CERT_AUTH_FAIL_REVOKED:
ssl->err = SSL_ALERT_CERTIFICATE_REVOKED;
break;
case PS_CERT_AUTH_FAIL_AUTHKEY:
case PS_CERT_AUTH_FAIL_PATH_LEN:
ssl->err = SSL_ALERT_BAD_CERTIFICATE;
break;
case PS_CERT_AUTH_FAIL_EXTENSION:
/* The math and basic constraints matched. This case is
for X.509 extension mayhem */
if (cert->authFailFlags & PS_CERT_AUTH_FAIL_DATE_FLAG) {
ssl->err = SSL_ALERT_CERTIFICATE_EXPIRED;
} else if(cert->authFailFlags & PS_CERT_AUTH_FAIL_SUBJECT_FLAG){
/* expectedName was giving to NewSession but couldn't
match what the peer gave us */
ssl->err = SSL_ALERT_CERTIFICATE_UNKNOWN;
} else if (cert->next != NULL) {
/* This is an extension problem in the chain.
Even if it's minor, we are shutting it down */
ssl->err = SSL_ALERT_BAD_CERTIFICATE;
} else {
/* This is the case where we did successfully find the
correct CA to validate the cert and the math passed
but the extensions had a problem. Give app a
different message in this case */
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
}
break;
case PS_CERT_AUTH_FAIL_BC:
case PS_CERT_AUTH_FAIL_DN:
/* These two are pre-math tests. If this was a problem in the
middle of the chain it means the chain couldn't even
validate itself. If it is at the end it means a matching
CA could not be found */
if (cert->next != NULL) {
ssl->err = SSL_ALERT_BAD_CERTIFICATE;
} else {
ssl->err = SSL_ALERT_UNKNOWN_CA;
}
break;
default:
break;
}
cert = cert->next;
}
/* The last thing we want to check before passing the certificates to
the user callback is the case in which we don't have any
CA files loaded but we were passed a valid chain that was
terminated with a self-signed cert. The fact that a CA on this
peer has not validated the chain should result in an UNKNOWN_CA alert
NOTE: This case should only ever get hit if VALIDATE_KEY_MATERIAL
has been disabled in matrixssllib.h */
if (ssl->err == SSL_ALERT_NONE &&
(ssl->keys == NULL || ssl->keys->CAcerts == NULL)) {
ssl->err = SSL_ALERT_UNKNOWN_CA;
psTraceInfo("WARNING: Valid self-signed cert or cert chain but no local authentication\n");
rc = -1; /* Force the check on existence of user callback */
}
if (rc < 0) {
psTraceInfo("WARNING: cert did not pass internal validation test\n");
/* Cert auth failed. If there is no user callback issue fatal alert
because there will be no intervention to give it a second look. */
if (ssl->sec.validateCert == NULL) {
/* ssl->err should have been set correctly above but catch
any missed cases with the generic BAD_CERTIFICATE alert */
if (ssl->err == SSL_ALERT_NONE) {
ssl->err = SSL_ALERT_BAD_CERTIFICATE;
}
return MATRIXSSL_ERROR;
}
}
#ifdef SERVER_WILL_ACCEPT_EMPTY_CLIENT_CERT_MSG
STRAIGHT_TO_USER_CALLBACK:
#endif
/* Return from user validation space with knowledge that there is a fatal
alert or that this is an ANONYMOUS connection. */
rc = matrixUserCertValidator(ssl, ssl->err, ssl->sec.cert,
ssl->sec.validateCert);
/* Test what the user callback returned. */
ssl->sec.anon = 0;
if (rc == SSL_ALLOW_ANON_CONNECTION) {
ssl->sec.anon = 1;
} else if (rc > 0) {
/* User returned an alert. May or may not be the alert that was
determined above */
psTraceIntInfo("Certificate authentication alert %d\n", rc);
ssl->err = rc;
return MATRIXSSL_ERROR;
} else if (rc < 0) {
psTraceIntInfo("User certificate callback had an internal error\n", rc);
ssl->err = SSL_ALERT_INTERNAL_ERROR;
return MATRIXSSL_ERROR;
}
/* User callback returned 0 (continue on). Did they determine the alert
was not fatal after all? */
if (ssl->err != SSL_ALERT_NONE) {
psTraceIntInfo("User certificate callback determined alert %d was NOT fatal\n",
ssl->err);
ssl->err = SSL_ALERT_NONE;
}
/* Either a client or server could have been processing the cert as part of
the authentication process. If server, we move to the client key
exchange state. */
if (ssl->flags & SSL_FLAGS_SERVER) {
ssl->hsState = SSL_HS_CLIENT_KEY_EXCHANGE;
} else {
ssl->hsState = SSL_HS_SERVER_HELLO_DONE;
#ifdef USE_DHE_CIPHER_SUITE
if (ssl->flags & SSL_FLAGS_DHE_KEY_EXCH) {
ssl->hsState = SSL_HS_SERVER_KEY_EXCHANGE;
}
#endif /* USE_DHE_CIPHER_SUITE */
#ifdef USE_OCSP
/* State management for OCSP use. Testing if we received a
status_request from the server to set next expected state */
if (ssl->extFlags.status_request || ssl->extFlags.status_request_v2) {
/* Why do they allow an ambiguous state here?! From RFC 6066:
Note that a server MAY also choose not to send a
"CertificateStatus" message, even if has received a
"status_request" extension in the client hello message and has
sent a "status_request" extension in the server hello message */
ssl->hsState = SSL_HS_CERTIFICATE_STATUS;
}
#endif
}
*cp = c;
ssl->decState = SSL_HS_CERTIFICATE;
return MATRIXSSL_SUCCESS;
}
#endif /* USE_CLIENT_SIDE_SSL || USE_CLIENT_AUTH */
#endif /* !USE_ONLY_PSK_CIPHER_SUITE */
/******************************************************************************/