3361 lines
114 KiB
C
3361 lines
114 KiB
C
/**
|
|
* @file hsDecode.c
|
|
* @version $Format:%h%d$
|
|
*
|
|
* SSL/TLS handshake message parsing
|
|
*/
|
|
/*
|
|
* Copyright (c) 2013-2018 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 "matrixsslImpl.h"
|
|
|
|
#ifdef USE_ECC
|
|
# define USE_ECC_EPHEMERAL_KEY_CACHE
|
|
#endif
|
|
|
|
#define COMPRESSION_METHOD_NULL 0x0
|
|
#define COMPRESSION_METHOD_DEFLATE 0x1
|
|
|
|
/* 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 compLen;
|
|
uint32 suiteLen;
|
|
uint32 resumptionOnTrack, cipher = 0;
|
|
int32 rc, i;
|
|
unsigned char *c;
|
|
int32 versionCheckResult;
|
|
|
|
# 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;
|
|
|
|
psTracePrintHsMessageParse(ssl, SSL_HS_CLIENT_HELLO);
|
|
|
|
/* First two bytes are the highest supported major and minor SSL versions */
|
|
|
|
# ifdef USE_MATRIXSSL_STATS
|
|
matrixsslUpdateStat(ssl, CH_RECV_STAT, 1);
|
|
# endif
|
|
if (end - c < 2)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceErrr("Invalid ssl header version length\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
|
|
ssl->peerHelloVersion = psVerFromEncodingMajMin(*c, *(c+1));
|
|
c += 2;
|
|
psTracePrintProtocolVersionNew(INDENT_HS_MSG,
|
|
"client_version",
|
|
ssl->peerHelloVersion,
|
|
PS_TRUE);
|
|
|
|
/*
|
|
Check whether we can support ClientHello.client_version.
|
|
Even if we can't, do not issue protocol_version alert yet.
|
|
This is because the TLS 1.3 draft spec stipulates that
|
|
if the client sends the supported_versions extension,
|
|
ClientHello.client_version must be ignored.
|
|
|
|
So we store the check result and use it only if
|
|
no supported_versions extension is found.
|
|
*/
|
|
versionCheckResult = checkClientHelloVersion(ssl);
|
|
|
|
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;
|
|
psTracePrintHex(INDENT_HS_MSG,
|
|
"client_random",
|
|
ssl->sec.clientRandom,
|
|
SSL_HS_RANDOM_SIZE,
|
|
PS_TRUE);
|
|
|
|
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;
|
|
}
|
|
psTracePrintHex(INDENT_HS_MSG,
|
|
"session_id",
|
|
ssl->sessionId,
|
|
ssl->sessionIdLen,
|
|
PS_TRUE);
|
|
# ifdef USE_DTLS
|
|
/* If DTLS is enabled, make sure we received a valid cookie in the
|
|
CLIENT_HELLO message. */
|
|
if (ACTV_VER(ssl, v_dtls_any))
|
|
{
|
|
psSize_t cookie_len;
|
|
/* Next field is the cookie length */
|
|
if (end - c < 1)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceErrr("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;
|
|
psTraceErrr("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;
|
|
psTraceErrr("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;
|
|
psTraceErrr("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;
|
|
psTraceErrr("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. */
|
|
if (end - c < suiteLen)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceErrr("Malformed clientHello message\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
/* We do not choose a ciphersuite yet, as the cipher we choose
|
|
may depend on an extension sent by the client. For example,
|
|
ALPN for HTTP/2 limits which suites we can negotiate, and
|
|
ELLIPTIC_CURVE/ELLIPTIC_POINT extensions may not match with
|
|
what we have available and we would have to fall back to a
|
|
non-ECC cipher.
|
|
Still, make one entire pass of the cipher suites now
|
|
to search for SCSV if secure rehandshakes are on. This is
|
|
the exception because SCSV is not a true ciphersuite, but
|
|
more like an extension that can be "hidden" for pre-TLS1.0
|
|
implementations. */
|
|
suiteEnd = c + suiteLen;
|
|
psTracePrintEncodedCipherList(INDENT_HS_MSG,
|
|
"cipher_suites",
|
|
c, suiteLen,
|
|
PS_FALSE);
|
|
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
|
|
# ifdef USE_TLS_1_3
|
|
if (isTls13Ciphersuite(cipher))
|
|
{
|
|
ssl->gotTls13CiphersuiteInCH = PS_TRUE;
|
|
}
|
|
# endif /* USE_TLS_1_3 */
|
|
/** 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->peerHelloVersion < psVerGetHighestTls(GET_SUPP_VER(ssl)))
|
|
{
|
|
ssl->err = SSL_ALERT_INAPPROPRIATE_FALLBACK;
|
|
psTraceErrr("Inappropriate version fallback\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Compression parameters */
|
|
if (end - c < 1)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceErrr("Invalid compression header length\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
compLen = *c++;
|
|
if ((uint32) (end - c) < compLen)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceErrr("Invalid compression header length\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
/* Per TLS RFCs proposing null compression is MUST. Check the other end
|
|
has proposed null compression (amongst possible other choices). */
|
|
for (i = 0; i < compLen; i++)
|
|
{
|
|
if (c[i] == COMPRESSION_METHOD_NULL)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (i == compLen)
|
|
{
|
|
/* Note, also catches compLen == 0 */
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceErrr("No compression.null proposed\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)
|
|
{
|
|
psTraceErrr("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)
|
|
{
|
|
psTraceErrr("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
|
|
{
|
|
#if defined(USE_INTERCEPTOR) || defined(ALLOW_SSLV2_CLIENT_HELLO_PARSE)
|
|
/* Parse a SSLv2 ClientHello message. The same information is
|
|
conveyed but the order and format is different.
|
|
First get the cipher suite length, session id length and challenge
|
|
(client random) length - all two byte values, network byte order */
|
|
uint32_t challengeLen;
|
|
psTraceInfo("Parsing SSLv2 ClientHello\n");
|
|
if (end - c < 6)
|
|
{
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
psTraceErrr("Can't parse hello message\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
suiteLen = *c << 8; c++;
|
|
suiteLen += *c; c++;
|
|
if (suiteLen == 0 || suiteLen % 3 != 0)
|
|
{
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
psTraceErrr("Can't parse hello message\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
ssl->sessionIdLen = *c << 8; c++;
|
|
ssl->sessionIdLen += *c; c++;
|
|
/* A resumed session would use a SSLv3 ClientHello, not SSLv2. */
|
|
if (ssl->sessionIdLen != 0)
|
|
{
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
psTraceErrr("Bad resumption request\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
challengeLen = *c << 8; c++;
|
|
challengeLen += *c; c++;
|
|
#ifdef ALLOW_SSLV2_CLIENT_HELLO_PARSE
|
|
if (challengeLen != 32) /* Allow only 32-bit, as per RFC 2246, E.2. */
|
|
#else
|
|
if (challengeLen < 16 || challengeLen > 32)
|
|
#endif
|
|
{
|
|
psTraceErrr("Bad challenge length\n");
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
/* Validate the three lengths that were just sent to us, don't
|
|
want any buffer overflows while parsing the remaining data */
|
|
if ((uint32) (end - c) != suiteLen + ssl->sessionIdLen +
|
|
challengeLen)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceErrr("Malformed SSLv2 clientHello\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
/* Parse the cipher suite list similar to the SSLv3 method, except
|
|
each suite is 3 bytes, instead of two bytes. We define the suite
|
|
as an integer value, so either method works for lookup.
|
|
We don't support session resumption from V2 handshakes, so don't
|
|
need to worry about matching resumed cipher suite. */
|
|
suiteStart = c;
|
|
|
|
/* We don't allow session IDs for v2 ClientHellos */
|
|
if (ssl->sessionIdLen > 0)
|
|
{
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
psTraceErrr("SSLv2 sessions not allowed\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
/* The client random (between 16 and 32 bytes) fills the least
|
|
significant bytes in the (always) 32 byte SSLv3 client random */
|
|
Memset(ssl->sec.clientRandom, 0x0, SSL_HS_RANDOM_SIZE);
|
|
Memcpy(ssl->sec.clientRandom + (SSL_HS_RANDOM_SIZE - challengeLen),
|
|
c, challengeLen);
|
|
c += challengeLen;
|
|
# else
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
psTraceErrr("SSLV2 CLIENT_HELLO not supported.\n");
|
|
return MATRIXSSL_ERROR;
|
|
# endif
|
|
}
|
|
|
|
#ifdef USE_TLS_1_3
|
|
/*
|
|
Check supported_versions even if we have been configured at run-time
|
|
not to support TLS 1.3. This allows us to negotiate an earlier version
|
|
with clients that do DO support 1.3.
|
|
|
|
TLS 1.3 spec, 4.2.1:
|
|
"If this extension is present in the ClientHello, servers MUST
|
|
NOT use the ClientHello.legacy_version value for version
|
|
negotiation and MUST use only the “supported_versions” extension
|
|
to determine client preferences. Servers MUST only select a
|
|
version of TLS present in that extension and MUST ignore any
|
|
unknown versions that are present in that extension. Note that
|
|
this mechanism makes it possible to negotiate a version prior to
|
|
TLS 1.2 if one side supports a sparse range."
|
|
*/
|
|
if (ssl->extFlags.got_supported_versions == 1)
|
|
{
|
|
int32_t rc;
|
|
|
|
if (versionCheckResult < 0)
|
|
{
|
|
if (ssl->err == SSL_ALERT_PROTOCOL_VERSION)
|
|
{
|
|
ssl->err = SSL_ALERT_NONE;
|
|
}
|
|
psTraceInfo("ClientHello.client_version check failed, but " \
|
|
"supported_versions overrides\n");
|
|
}
|
|
rc = checkSupportedVersions(ssl);
|
|
if (rc < 0)
|
|
{
|
|
psTraceErrr("No shared protocol version: " \
|
|
"supported_versions check failed\n");
|
|
/* Encode the alert using the highest version we support.*/
|
|
SET_ACTV_VER(ssl, psVerGetHighestTls(ssl->supportedVersions));
|
|
return rc;
|
|
}
|
|
}
|
|
else
|
|
#endif /* USE_TLS_1_3 */
|
|
{
|
|
/* Client did not send supported_versions.
|
|
Use the result of the legacy_version-based negotiation. */
|
|
if (versionCheckResult < 0)
|
|
{
|
|
/* If no supported_versions extensions was present, and the
|
|
ClientHello.client_version check failed, send the alert now. */
|
|
psTraceErrr("No shared protocol version: " \
|
|
"ClientHello.client_version check failed\n");
|
|
/* Encode the alert using the highest version we support.*/
|
|
SET_ACTV_VER(ssl, psVerGetHighestTls(ssl->supportedVersions));
|
|
return versionCheckResult;
|
|
}
|
|
}
|
|
|
|
/* Protocol version has now been successfully negotiated. */
|
|
psTracePrintNegotiatedProtocolVersion(INDENT_HS_MSG,
|
|
"Chosen protocol version", ssl, PS_TRUE);
|
|
|
|
/* ClientHello should be the only one in the record. */
|
|
if (c != end)
|
|
{
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
psTraceErrr("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_MATRIXSSL_STATS
|
|
matrixsslUpdateStat(ssl, FAILED_RESUMPTIONS_STAT, 1);
|
|
# endif
|
|
|
|
/*
|
|
Failed to resume (both via Session ID and ticket).
|
|
|
|
Clear the Session ID, unless we are using TLS 1.3,
|
|
in which case we MUST echo the client's Session ID.
|
|
*/
|
|
if (!NGTD_VER(ssl, v_tls_1_3_any))
|
|
{
|
|
Memset(ssl->sessionId, 0, SSL_MAX_SESSION_ID_SIZE);
|
|
ssl->sessionIdLen = 0;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
# 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;
|
|
psTraceErrr("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)
|
|
{
|
|
psTraceErrr("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)
|
|
{
|
|
psTraceErrr("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 &&
|
|
!NGTD_VER(ssl, v_tls_1_3_any))
|
|
{
|
|
/* 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))
|
|
{
|
|
psTraceErrr("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);
|
|
psTraceErrr("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 = psDhGenKeyParams(ssl->hsPool, &ssl->keys->dhParams,
|
|
ssl->sec.dhKeyPriv, pkiData)) < 0)
|
|
{
|
|
psFree(ssl->sec.dhKeyPriv, ssl->hsPool);
|
|
ssl->sec.dhKeyPriv = NULL;
|
|
psTraceErrr("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 */
|
|
|
|
if (USING_TLS_1_3(ssl))
|
|
{
|
|
ssl->hsState = SSL_HS_TLS_1_3_RECVD_CH;
|
|
# ifdef USE_TLS_1_3
|
|
if (ssl->tls13IncorrectDheKeyShare &&
|
|
!ssl->sec.tls13ClientCookieOk)
|
|
{
|
|
psTraceErrr("Client failed to respond to HRR with a cookie\n");
|
|
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
# endif
|
|
}
|
|
else
|
|
{
|
|
ssl->hsState = SSL_HS_CLIENT_KEY_EXCHANGE;
|
|
}
|
|
# ifdef USE_CLIENT_AUTH
|
|
/* Next state in client authentication case is to receive the cert */
|
|
/* This is for 1.2 and earlier. For 1.3 the client authentication is
|
|
handled elsewhere */
|
|
if (!USING_TLS_1_3(ssl) &&
|
|
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 = 0;
|
|
unsigned char *pskKey = NULL;
|
|
# endif
|
|
void *pkiData = ssl->userPtr;
|
|
|
|
c = *cp;
|
|
|
|
psTracePrintHsMessageParse(ssl, SSL_HS_CLIENT_KEY_EXCHANGE);
|
|
|
|
/* 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. */
|
|
if ((int32) (end - c) < hsLen)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceErrr("Invalid ClientKeyExchange length 1\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
|
|
pubKeyLen = hsLen;
|
|
# ifdef USE_TLS
|
|
/* TLS - Two byte length is explicit. */
|
|
if (NGTD_VER(ssl, v_tls_any | v_dtls_any))
|
|
{
|
|
if (end - c < 2)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceErrr("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;
|
|
psTraceErrr("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 (NGTD_VER(ssl, v_ssl_3_0))
|
|
{
|
|
# 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;
|
|
psTraceErrr("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;
|
|
psTraceErrr("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;
|
|
psTraceErrr("Invalid ClientKeyExchange PSK length\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
|
|
rc = matrixSslPskGetKey(ssl, c, pubKeyLen, &pskKey, &pskLen);
|
|
if (pskKey == NULL || rc < 0)
|
|
{
|
|
psTraceErrr("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;
|
|
psTraceErrr("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;
|
|
}
|
|
# ifdef USE_ROT_ECC
|
|
ssl->sec.eccKeyPub->rotKeyType = ps_ecc_key_type_ecdhe;
|
|
# endif
|
|
if (psEccX963ImportKey(ssl->hsPool, c, pubKeyLen,
|
|
ssl->sec.eccKeyPub, ssl->sec.eccKeyPriv->curve) < 0)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
# ifdef USE_SSL_INFORMATIONAL_TRACE
|
|
ssl->peerKeyExKeyType = PS_ECC;
|
|
ssl->peerKeyExKeyNBits = ssl->sec.eccKeyPriv->curve->size * 8;
|
|
# endif
|
|
/* 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;
|
|
}
|
|
# ifdef USE_SSL_INFORMATIONAL_TRACE
|
|
ssl->peerKeyExKeyType = PS_DH;
|
|
ssl->peerKeyExKeyNBits = pubKeyLen * 8;
|
|
# endif
|
|
/*
|
|
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; /* psSize_t 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 psSize_t length to the premaster key.
|
|
*/
|
|
if (pskKey == NULL)
|
|
{
|
|
ssl->err = SSL_ALERT_INTERNAL_ERROR;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
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, uint8_t length of PSK and key itself
|
|
*/
|
|
ssl->sec.premaster[ssl->sec.premasterSize + 2] = 0;
|
|
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 (NGTD_VER(ssl, v_ssl_3_0))
|
|
{
|
|
/* SSLv3 for basic PSK suites will not have read off
|
|
pubKeyLen at this point */
|
|
pubKeyLen = *c << 8; c++;
|
|
pubKeyLen += *c; c++;
|
|
}
|
|
rc = matrixSslPskGetKey(ssl, c, pubKeyLen, &pskKey, &pskLen);
|
|
if (rc < 0 || pskKey == NULL)
|
|
{
|
|
psTraceErrr("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] = 0;
|
|
ssl->sec.premaster[1] = (pskLen & 0xFF);
|
|
/* memset to 0 handled middle portion */
|
|
ssl->sec.premaster[2 + pskLen] = 0;
|
|
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)
|
|
{
|
|
psEccKey_t *ecc = &ssl->chosenIdentity->privKey.key.ecc;
|
|
if (NGTD_VER(ssl, v_ssl_3_0))
|
|
{
|
|
/* 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, ecc->curve) < 0)
|
|
{
|
|
return SSL_MEM_ERROR;
|
|
}
|
|
# ifdef USE_ROT_ECC
|
|
ssl->sec.eccKeyPub->rotKeyType = ps_ecc_key_type_ecdhe;
|
|
# endif
|
|
if (psEccX963ImportKey(ssl->hsPool,
|
|
c, pubKeyLen, ssl->sec.eccKeyPub, 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 = 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,
|
|
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.)"
|
|
*/
|
|
# if defined(USE_IDENTITY_CERTIFICATES) && defined(USE_RSA)
|
|
rc = psRsaDecryptPriv(ckepkiPool, &ssl->chosenIdentity->privKey.key.rsa, c,
|
|
pubKeyLen, ssl->sec.premaster, ssl->sec.premasterSize,
|
|
pkiData);
|
|
# else
|
|
rc = PS_FAILURE;
|
|
# endif
|
|
/* Step 1 of Bleichenbacher attack mitigation. We do it here
|
|
after the RSA op, but regardless of the result of the op. */
|
|
if (psGetPrngLocked(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] = psEncodeVersionMaj(ssl->peerHelloVersion);
|
|
ssl->sec.premaster[1] = psEncodeVersionMin(ssl->peerHelloVersion);
|
|
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 */
|
|
psTraceErrr("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 */
|
|
|
|
# ifdef DEBUG_TLS_PREMASTER
|
|
psTraceBytes("server premaster_secret",
|
|
ssl->sec.premaster,
|
|
SSL_HS_RSA_PREMASTER_SIZE);
|
|
# endif
|
|
|
|
/* 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)
|
|
{
|
|
uint16_t sigAlg;
|
|
unsigned char hashAlg;
|
|
uint32_t hashSigAlg;
|
|
psSize_t sigLen;
|
|
unsigned char *refMsg = hsMsgHash;
|
|
psSize_t refMsgLen;
|
|
int32 rc;
|
|
unsigned char *c;
|
|
psBool_t verifyResult;
|
|
psVerifyOptions_t opts;
|
|
psBool_t useEcdsa = PS_FALSE;
|
|
|
|
psTracePrintHsMessageParse(ssl, SSL_HS_CERTIFICATE_VERIFY);
|
|
|
|
c = *cp;
|
|
rc = 0;
|
|
PS_VARIABLE_SET_BUT_UNUSED(rc); /* Note: Only used ifdef USE_ECC. */
|
|
Memset(&opts, 0, sizeof(opts));
|
|
if (ssl->sec.cert->pubKeyAlgorithm == OID_ECDSA_KEY_ALG)
|
|
{
|
|
useEcdsa = PS_TRUE;
|
|
}
|
|
|
|
/*
|
|
TLS 1.2:
|
|
|
|
struct {
|
|
digitally-signed struct {
|
|
opaque handshake_messages[handshake_messages_length];
|
|
}
|
|
} CertificateVerify;
|
|
|
|
struct {
|
|
SignatureAndHashAlgorithm algorithm;
|
|
opaque signature<0..2^16-1>;
|
|
} DigitallySigned;
|
|
|
|
TLS 1.1 and below:
|
|
|
|
struct {
|
|
Signature signature;
|
|
} CertificateVerify;
|
|
Where signature is an opaque vector <0..2^16-1>
|
|
*/
|
|
|
|
/*
|
|
In TLS 1.2, we can just parse the signature algorithm ID.
|
|
For TLS 1.1 and below, we need to use the defaults:
|
|
RSA-MD5-SHA1 or ECDSA-SHA1.
|
|
*/
|
|
sigAlg = OID_RSA_TLS_SIG_ALG;
|
|
refMsgLen = MD5_HASH_SIZE + SHA1_HASH_SIZE;
|
|
if (ssl->sec.cert->pubKeyAlgorithm == OID_ECDSA_KEY_ALG)
|
|
{
|
|
/*
|
|
SHA-1 is default for ECDSA. When using TLS 1.1 or below,
|
|
hsMsgHash contains the MD5-SHA1 handshake hash. So use the
|
|
last 20 bytes from that.
|
|
*/
|
|
refMsg = hsMsgHash + MD5_HASH_SIZE;
|
|
refMsgLen = SHA1_HASH_SIZE;
|
|
sigAlg = OID_ECDSA_TLS_SIG_ALG;
|
|
}
|
|
|
|
# ifdef USE_TLS_1_2
|
|
if (NGTD_VER(ssl, v_tls_with_signature_algorithms))
|
|
{
|
|
if ((uint32) (end - c) < 2)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceErrr("Invalid Certificate Verify message 1\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
|
|
hashAlg = c[0];
|
|
sigAlg = (uint16_t)((c[0] << 8) | c[1]);
|
|
/* Convert from official SignatureAndHashAlgorithm ID to MatrixSSL
|
|
internal "OID". The "OID" format is expected by psVerifySig. */
|
|
sigAlg = tlsSigAlgToMatrix(sigAlg);
|
|
hashSigAlg = HASH_SIG_MASK(c[0], c[1]);
|
|
refMsg = hsMsgHash;
|
|
|
|
psTracePrintSigAlgs(INDENT_HS_MSG,
|
|
"Peer CertificateVerify sig alg",
|
|
hashSigAlg,
|
|
PS_TRUE);
|
|
|
|
if (!useEcdsa)
|
|
{
|
|
/* TLS 1.2 uses DigestInfos with RSA. */
|
|
opts.msgIsDigestInfo = PS_TRUE;
|
|
}
|
|
|
|
if (!(ssl->hashSigAlg & hashSigAlg))
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceErrr("Invalid sig alg in parseCertificateVerify\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
|
|
/* The SHA-256 handshake hash is passed into this function in the
|
|
hsMsgHash buffer. If we need to use a different algorithm,
|
|
we retrieve the hash separately. */
|
|
switch (hashAlg)
|
|
{
|
|
# ifdef USE_SHA1
|
|
case HASH_SIG_SHA1:
|
|
sslSha1RetrieveHSHash(ssl, hsMsgHash);
|
|
refMsgLen = SHA1_HASH_SIZE;
|
|
break;
|
|
# endif
|
|
case HASH_SIG_SHA256:
|
|
refMsgLen = SHA256_HASH_SIZE;
|
|
break;
|
|
# ifdef USE_SHA384
|
|
case HASH_SIG_SHA384:
|
|
sslSha384RetrieveHSHash(ssl, hsMsgHash);
|
|
refMsgLen = SHA384_HASH_SIZE;
|
|
break;
|
|
# endif
|
|
# ifdef USE_SHA512
|
|
case HASH_SIG_SHA512:
|
|
sslSha512RetrieveHSHash(ssl, hsMsgHash);
|
|
refMsgLen = SHA512_HASH_SIZE;
|
|
break;
|
|
# endif
|
|
default:
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceErrr("Invalid Certificate Verify message\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
|
|
c += 2; /* SignatureAndHashAlgorithm parse complete. */
|
|
}
|
|
# endif /* USE_TLS_1_2 */
|
|
|
|
# ifdef USE_ROT_CRYPTO
|
|
/* The crypto-rot implementation of psVerifySig needs the full
|
|
TBS reference data, not just the hash. The HS hash is over
|
|
ClientHello..ClientKeyExchange. */
|
|
refMsg = ssl->hsMsgBuf.start;
|
|
refMsgLen = ssl->hsMsgCHtoCKELen;
|
|
# endif /* USE_TLS_1_2 */
|
|
|
|
if ((uint32) (end - c) < 2)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceErrr("Invalid Certificate Verify message 2\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
sigLen = *c << 8; c++;
|
|
sigLen |= *c; c++;
|
|
if ((uint32) (end - c) < sigLen)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceErrr("Invalid Certificate Verify message 3\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
|
|
rc = psVerifySig(ssl->hsPool,
|
|
refMsg,
|
|
refMsgLen,
|
|
c,
|
|
sigLen,
|
|
&ssl->sec.cert->publicKey,
|
|
sigAlg,
|
|
&verifyResult,
|
|
&opts);
|
|
if (rc != PS_SUCCESS || verifyResult != PS_TRUE)
|
|
{
|
|
psTraceErrr("CertificateVerify signature validation failed\n");
|
|
psTraceIntInfo("psVerifySig: %d\n", rc);
|
|
ssl->err = SSL_ALERT_DECRYPT_ERROR;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
|
|
c += sigLen;
|
|
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;
|
|
|
|
psTracePrintHsMessageParse(ssl, SSL_HS_SERVER_HELLO);
|
|
|
|
# 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;
|
|
psTraceErrr("Invalid ssl header version length\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
ssl->peerHelloVersion = psVerFromEncodingMajMin(*c, *(c+1));
|
|
c += 2;
|
|
psTracePrintProtocolVersionNew(INDENT_HS_MSG,
|
|
"server_version",
|
|
ssl->peerHelloVersion,
|
|
PS_TRUE);
|
|
|
|
rc = checkServerHelloVersion(ssl);
|
|
if (rc < 0)
|
|
{
|
|
return rc;
|
|
}
|
|
|
|
/* 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;
|
|
psTraceErrr("Invalid length of random data\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
Memcpy(ssl->sec.serverRandom, c, SSL_HS_RANDOM_SIZE);
|
|
psTracePrintHex(INDENT_HS_MSG,
|
|
"random",
|
|
ssl->sec.serverRandom,
|
|
SSL_HS_RANDOM_SIZE,
|
|
PS_TRUE);
|
|
|
|
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;
|
|
}
|
|
psTracePrintHex(INDENT_HS_MSG,
|
|
"session_id",
|
|
c,
|
|
(psSizeL_t)sessionIdLen,
|
|
PS_TRUE);
|
|
|
|
/* 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
|
|
}
|
|
# ifdef USE_EAP_FAST /* TODO Could also do this for any TICKET */
|
|
if (ssl->sid->sessionTicketState == SESS_TICKET_STATE_SENT_TICKET)
|
|
{
|
|
if (ssl->flags & SSL_FLAGS_RESUMED)
|
|
{
|
|
/* The server has accepted our session ticket, and indicated that
|
|
by echoing the random session id we sent. */
|
|
ssl->extFlags.eap_fast_master_secret = 1;
|
|
/* TODO could derive eap keys here */
|
|
}
|
|
else
|
|
{
|
|
/* The server isn't going to use our ticket. But may still
|
|
send a ticket extension and (possibly blank) ticket message */
|
|
ssl->extFlags.eap_fast_master_secret = 0;
|
|
}
|
|
}
|
|
# 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;
|
|
psTraceErrr("Invalid cipher suite length\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
cipher = *c << 8; c++;
|
|
cipher += *c; c++;
|
|
|
|
psTracePrintCiphersuiteName(INDENT_HS_MSG,
|
|
"cipher_suite",
|
|
cipher,
|
|
PS_TRUE);
|
|
|
|
/* 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;
|
|
psTraceErrr("Can't support resumed cipher\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ssl->cipher = sslGetCipherSpec(ssl, cipher);
|
|
/*
|
|
Check whether we support the ciphersuite chosen by the server.
|
|
|
|
Do not allow the server to choose the NULL suite - we always
|
|
have it in our supported suites array, since it is used
|
|
as a terminator, and it is not possible to disable it at run-time.
|
|
*/
|
|
if (ssl->cipher == NULL
|
|
|| ssl->cipher->ident == SSL_NULL_WITH_NULL_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;
|
|
psTraceErrr("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;
|
|
psTraceErrr("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)))
|
|
{
|
|
/* If hsLen indicates that there is some extension data to parse,
|
|
check that there are at least two octets; at minimum, extensions
|
|
(if present) must consist of two length octets.
|
|
Note: extData points to the start of the ServerHello. */
|
|
if ((int32) hsLen - (c - extData) < 2)
|
|
{
|
|
psTraceErrr("Invalid ServerHello length encoding\n");
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
rc = parseServerHelloExtensions(ssl, hsLen, extData, &c, end - c);
|
|
if (rc < 0)
|
|
{
|
|
/* Alerts will already have been set inside */
|
|
return rc;
|
|
}
|
|
# ifdef USE_TLS_1_3
|
|
if (!NGTD_VER(ssl, v_tls_1_3_any))
|
|
{
|
|
rc = performTls13DowngradeCheck(ssl);
|
|
if (rc < 0)
|
|
{
|
|
return rc;
|
|
}
|
|
}
|
|
# endif /* USE_TLS_1_3 */
|
|
}
|
|
|
|
# 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)
|
|
{
|
|
psTraceErrr("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 (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[SHA512_HASH_SIZE],
|
|
unsigned char **cp, unsigned char *end)
|
|
{
|
|
unsigned char *c;
|
|
|
|
# ifdef USE_DHE_CIPHER_SUITE
|
|
int32_t rc, i;
|
|
# ifdef REQUIRE_DH_PARAMS
|
|
uint32 pubDhLen;
|
|
# endif
|
|
# ifndef USE_ONLY_PSK_CIPHER_SUITE
|
|
unsigned char *sigStart = NULL, *sigStop = NULL;
|
|
# endif /* USE_ONLY_PSK_CIPHER_SUITE */
|
|
# ifdef USE_ECC_CIPHER_SUITE
|
|
const psEccCurve_t *curve;
|
|
# endif
|
|
|
|
# endif /* USE_DHE_CIPHER_SUITE */
|
|
|
|
c = *cp;
|
|
|
|
psTracePrintHsMessageParse(ssl, SSL_HS_SERVER_KEY_EXCHANGE);
|
|
|
|
# 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;
|
|
psTraceErrr("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;
|
|
psTraceErrr("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;
|
|
psTraceErrr("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++;
|
|
if (!psIsEcdheGroup(i))
|
|
{
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
psTraceErrr("Unsupported ECDHE group in SKE\n");
|
|
psTraceIntInfo("Group ID: %d\n", i);
|
|
}
|
|
ssl->sec.peerCurveId = i;
|
|
|
|
# ifdef USE_X25519
|
|
if (i == namedgroup_x25519)
|
|
{
|
|
/* Next is length octet of opaque point <1..2^8-1>; */
|
|
i = *c;
|
|
c++;
|
|
if ((end - c < i) || (i != PS_DH_X25519_PUBLIC_KEY_BYTES))
|
|
{
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
if (ssl->sec.x25519KeyPub != NULL)
|
|
{
|
|
psFree(ssl->sec.x25519KeyPub, ssl->sec.eccDhKeyPool);
|
|
}
|
|
ssl->sec.x25519KeyPub = psMalloc(ssl->sec.eccDhKeyPool,
|
|
PS_DH_X25519_PUBLIC_KEY_BYTES);
|
|
Memcpy(ssl->sec.x25519KeyPub,
|
|
c,
|
|
PS_DH_X25519_PUBLIC_KEY_BYTES);
|
|
c += PS_DH_X25519_PUBLIC_KEY_BYTES;
|
|
sigStop = c;
|
|
# ifdef USE_SSL_INFORMATIONAL_TRACE
|
|
ssl->peerKeyExKeyType = PS_X25519;
|
|
ssl->peerKeyExKeyNBits = 256;
|
|
# endif
|
|
goto verify_sig;
|
|
}
|
|
# endif /* USE_X25519 */
|
|
|
|
/* 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;
|
|
}
|
|
# ifdef USE_SEC_CONFIG
|
|
rc = matrixSslCallSecurityCallback(ssl,
|
|
secop_ecdh_import_pub,
|
|
curve->size * 8,
|
|
NULL);
|
|
if (rc != PS_SUCCESS)
|
|
{
|
|
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
|
|
return rc;
|
|
}
|
|
# endif /* USE_SEC_CONFIG */
|
|
/*
|
|
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;
|
|
psTraceErrr("Invalid ServerKeyExchange message\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
if (psEccNewKey(ssl->hsPool, &ssl->sec.eccKeyPub, curve) < 0)
|
|
{
|
|
return SSL_MEM_ERROR;
|
|
}
|
|
# ifdef USE_ROT_ECC
|
|
ssl->sec.eccKeyPub->rotKeyType = ps_ecc_key_type_ecdhe;
|
|
# endif
|
|
if (psEccX963ImportKey(ssl->hsPool, c, i,
|
|
ssl->sec.eccKeyPub, curve) < 0)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
# ifdef USE_SSL_INFORMATIONAL_TRACE
|
|
ssl->peerKeyExKeyType = PS_ECC;
|
|
ssl->peerKeyExKeyNBits = curve->size * 8;
|
|
# endif
|
|
|
|
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;
|
|
psTraceErrr("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;
|
|
psTraceErrr("Invalid ServerKeyExchange message\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
if (ssl->sec.dhPLen < ssl->minDhBits/8)
|
|
{
|
|
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
|
|
psTraceErrr("Server's DH group too small\n");
|
|
psTraceIntInfo("Server bits: %hu\n", ssl->sec.dhPLen * 8);
|
|
psTraceIntInfo("Our minimum: %hu\n", ssl->minDhBits);
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
# ifdef USE_SEC_CONFIG
|
|
rc = matrixSslCallSecurityCallback(ssl,
|
|
secop_dh_import_pub,
|
|
ssl->sec.dhPLen * 8,
|
|
NULL);
|
|
if (rc != PS_SUCCESS)
|
|
{
|
|
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
|
|
return rc;
|
|
}
|
|
# endif /* USE_SEC_CONFIG */
|
|
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;
|
|
psTraceErrr("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;
|
|
psTraceErrr("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;
|
|
}
|
|
# ifdef USE_SSL_INFORMATIONAL_TRACE
|
|
ssl->peerKeyExKeyType = PS_DH;
|
|
ssl->peerKeyExKeyNBits = ssl->sec.dhPLen * 8;
|
|
# endif
|
|
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 */
|
|
|
|
/* We are still within if (ssl->flags & SSL_FLAGS_DHE_KEY_EX). */
|
|
# ifdef USE_X25519
|
|
verify_sig:
|
|
# endif
|
|
# ifndef USE_ONLY_PSK_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.
|
|
*/
|
|
{
|
|
psVerifyOptions_t opts = {0};
|
|
|
|
# ifdef USE_ROT_ECC
|
|
opts.noPreHash = PS_TRUE;
|
|
# endif
|
|
rc = tlsVerify(ssl,
|
|
sigStart,
|
|
sigStop - sigStart,
|
|
c,
|
|
end,
|
|
&ssl->sec.cert->publicKey,
|
|
&opts);
|
|
if (rc < 0)
|
|
{
|
|
psTraceErrr("ServerKeyExchange sig verification failed\n");
|
|
return rc;
|
|
}
|
|
}
|
|
/* Signature OK. */
|
|
c += rc; /* tlsVerify returns number of consumed octets. */
|
|
ssl->hsState = SSL_HS_SERVER_HELLO_DONE;
|
|
# endif /* USE_ONLY_PSK_CIPHER_SUITE */
|
|
} /* endif (ssl->flags & SSL_FLAGS_DHE_KEY_EX) */
|
|
# 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;
|
|
psTraceErrr("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;
|
|
psTraceErrr("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_RESPONSE
|
|
int32 parseCertificateStatus(ssl_t *ssl, int32 hsLen, unsigned char **cp,
|
|
unsigned char *end)
|
|
{
|
|
unsigned char *c;
|
|
int32_t responseLen, rc;
|
|
psOcspResponse_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.
|
|
*/
|
|
psTracePrintHsMessageParse(ssl, SSL_HS_CERTIFICATE_STATUS);
|
|
|
|
c = *cp;
|
|
if ((end - c) < 4)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceErrr("Invalid CertificateStatus length\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
|
|
if (*c != 0x1)
|
|
{
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
psTraceErrr("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;
|
|
psTraceErrr("Malformed CertificateStatus message\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
Memset(&response, 0x0, sizeof(psOcspResponse_t));
|
|
rc = psOcspParseResponse(ssl->hsPool, responseLen, &c, end, &response);
|
|
if (rc < 0)
|
|
{
|
|
/* Couldn't parse or no good responses in stream */
|
|
psX509FreeCert(response.OCSPResponseCert);
|
|
ssl->err = SSL_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE;
|
|
psTraceErrr("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 */
|
|
rc = psOcspResponseValidateOld(ssl->hsPool, ssl->keys->CAcerts,
|
|
ssl->sec.cert, &response);
|
|
if (rc < 0)
|
|
{
|
|
/* Couldn't validate */
|
|
psX509FreeCert(response.OCSPResponseCert);
|
|
ssl->err = SSL_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE;
|
|
psTraceErrr("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_RESPONSE */
|
|
|
|
/******************************************************************************/
|
|
|
|
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;
|
|
|
|
psTracePrintHsMessageParse(ssl, SSL_HS_SERVER_HELLO_DONE);
|
|
|
|
if (hsLen != 0)
|
|
{
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
psTraceErrr("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 */
|
|
# ifdef USE_X25519
|
|
if (ssl->sec.peerCurveId == namedgroup_x25519)
|
|
{
|
|
psRes_t res;
|
|
|
|
res = psDhX25519GenKey(ssl->sec.x25519KeyPriv.priv,
|
|
ssl->sec.x25519KeyPriv.pub);
|
|
if (res < 0)
|
|
{
|
|
return PS_FAILURE;
|
|
}
|
|
goto keygen_done;
|
|
}
|
|
# endif
|
|
if (psEccNewKey(ssl->sec.eccDhKeyPool, &ssl->sec.eccKeyPriv,
|
|
ssl->sec.eccKeyPub->curve) < 0)
|
|
{
|
|
return PS_MEM_FAIL;
|
|
}
|
|
rc = matrixSslGenEphemeralEcKey(ssl->keys,
|
|
ssl->sec.eccKeyPriv,
|
|
ssl->sec.eccKeyPub->curve,
|
|
pkiData);
|
|
if (rc < 0)
|
|
{
|
|
psEccDeleteKey(&ssl->sec.eccKeyPriv);
|
|
psTraceErrr("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;
|
|
}
|
|
rc = psDhGenKey(ssl->sec.dhKeyPool, ssl->sec.dhPLen,
|
|
ssl->sec.dhP, ssl->sec.dhPLen, ssl->sec.dhG,
|
|
ssl->sec.dhGLen, ssl->sec.dhKeyPriv, pkiData);
|
|
if (rc < 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 */
|
|
|
|
# ifdef USE_X25519
|
|
keygen_done:
|
|
# endif
|
|
|
|
ssl->hsState = SSL_HS_FINISHED;
|
|
|
|
*cp = c;
|
|
ssl->decState = SSL_HS_SERVER_HELLO_DONE;
|
|
return SSL_PROCESS_DATA;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
# if defined(USE_CLIENT_SIDE_SSL) && defined(USE_CLIENT_AUTH)
|
|
int32 parseCertificateRequest(ssl_t *ssl,
|
|
int32 hsLen, unsigned char **cp,
|
|
unsigned char *end)
|
|
{
|
|
unsigned char *c;
|
|
# ifndef USE_ONLY_PSK_CIPHER_SUITE
|
|
int32 certTypeLen;
|
|
unsigned char *c0;
|
|
sslKeySelectInfo_t *keySelect = &ssl->sec.keySelect;
|
|
# endif
|
|
|
|
if (ssl->flags & SSL_FLAGS_PSK_CIPHER)
|
|
{
|
|
psTraceInfo("Ignoring CertificateRequest - not needed when " \
|
|
"using a PSK ciphersuite.\n");
|
|
c = end;
|
|
goto skip_parse;
|
|
}
|
|
|
|
# ifndef USE_ONLY_PSK_CIPHER_SUITE
|
|
psTracePrintHsMessageParse(ssl, SSL_HS_CERTIFICATE_REQUEST);
|
|
|
|
if (hsLen < 4)
|
|
{
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
psTraceErrr("Invalid Certificate Request message\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
|
|
c = *cp;
|
|
|
|
/* 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;
|
|
psTraceErrr("Invalid Certificate Request message\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
c += certTypeLen; /* Skipping (RSA_SIGN etc.) */
|
|
|
|
/* read short and advance pointer */
|
|
#define GETSHORT(buf) (unsigned short)((buf)[0] << 8) | ((buf)[1])
|
|
|
|
/* TLS 1.2 specifies signature algorithms */
|
|
if (NGTD_VER(ssl, v_tls_with_signature_algorithms))
|
|
{
|
|
size_t len, nSigAlg = 0;
|
|
/* 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;
|
|
psTraceErrr("Invalid SigHash in Certificate Request message "\
|
|
"(short header)\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
len = GETSHORT(c); c += 2;
|
|
if (end - c < len)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceErrr("Invalid SigHash in Certificate Request message " \
|
|
"(short message)\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
/* Parse supported_signature_algorithms list. */
|
|
ssl->serverSigAlgs = 0;
|
|
while (len >= 2)
|
|
{
|
|
uint32_t val = HASH_SIG_MASK(c[0], c[1]);
|
|
keySelect->peerSigAlgs[nSigAlg++] = val;
|
|
ssl->serverSigAlgs |= val;
|
|
c += 2;
|
|
len -= 2;
|
|
}
|
|
keySelect->peerSigAlgsLen = nSigAlg;
|
|
keySelect->peerSigAlgMask = ssl->serverSigAlgs;
|
|
c += len;
|
|
psTracePrintSigAlgs(INDENT_HS_MSG,
|
|
"supported_signature_algorithms",
|
|
ssl->serverSigAlgs,
|
|
PS_TRUE);
|
|
}
|
|
else
|
|
{
|
|
/* <TLS1.2: conveniently all bits are set:
|
|
TBD: set only those supported by library */
|
|
keySelect->peerSigAlgMask = 0xffffffff;
|
|
}
|
|
|
|
/* Read certificate authority names */
|
|
if (end - c >= 2)
|
|
{
|
|
size_t len, certLen, nCas = 0;
|
|
|
|
len = GETSHORT(c); c += 2;
|
|
if (end - c < len)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceErrr("Invalid Certificate Request message " \
|
|
"(short CA's header)\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
/* Count the number of CA's */
|
|
c0 = c; /* remember where we started. */
|
|
while (len > 2)
|
|
{
|
|
certLen = GETSHORT(c); c += 2;
|
|
if (certLen == 0 || (end - c) < certLen || certLen > len)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceErrr("Invalid CertificateRequest message " \
|
|
"(short CA data)\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
c += certLen;
|
|
len -= (2 + certLen);
|
|
nCas++;
|
|
}
|
|
|
|
/* Fill in keySelect - we have now checked the data,
|
|
so taking the easy path. */
|
|
keySelect->nCas = nCas;
|
|
keySelect->caNames = psCalloc(
|
|
ssl->hsPool,
|
|
nCas,
|
|
sizeof(keySelect->caNames[0]));
|
|
keySelect->caNameLens = psCalloc(
|
|
ssl->hsPool,
|
|
nCas,
|
|
sizeof(keySelect->caNameLens[0]));
|
|
if (nCas > 0)
|
|
{
|
|
if (keySelect->caNames == NULL || keySelect->caNameLens == NULL)
|
|
{
|
|
psFree(keySelect->caNames, ssl->hsPool);
|
|
psFree(keySelect->caNameLens, ssl->hsPool);
|
|
ssl->err = SSL_ALERT_INTERNAL_ERROR;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
}
|
|
for (nCas = 0; nCas < keySelect->nCas; nCas++)
|
|
{
|
|
/* NOTE: caNames points to the original TLS record data,
|
|
and pointers are valid only as long as the packet is
|
|
valid. */
|
|
keySelect->caNameLens[nCas] = GETSHORT(c0);
|
|
c0 += 2;
|
|
keySelect->caNames[nCas] = c0;
|
|
c0 += keySelect->caNameLens[nCas];
|
|
}
|
|
}
|
|
|
|
if (ssl->chosenIdentity == NULL)
|
|
{
|
|
int32_t rc;
|
|
|
|
rc = matrixSslChooseClientKeys(ssl, keySelect);
|
|
if (rc != PS_SUCCESS)
|
|
{
|
|
psTraceInfo("Unable to load suitable client certificate\n");
|
|
}
|
|
}
|
|
# endif /* USE_ONLY_PSK_CIPHER_SUITE */
|
|
|
|
/* Consume record and advance state machine */
|
|
skip_parse:
|
|
*cp = c;
|
|
ssl->hsState = SSL_HS_SERVER_HELLO_DONE;
|
|
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;
|
|
|
|
psAssert(hsLen <= SHA384_HASH_SIZE);
|
|
|
|
psTracePrintHsMessageParse(ssl, SSL_HS_FINISHED);
|
|
|
|
/* 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. */
|
|
if (!(ssl->flags & SSL_FLAGS_READ_SECURE))
|
|
{
|
|
ssl->err = SSL_ALERT_UNEXPECTED_MESSAGE;
|
|
psTraceErrr("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 (!NGTD_VER(ssl, v_ssl_3_0))
|
|
{
|
|
if (hsLen != TLS_HS_FINISHED_SIZE)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceErrr("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;
|
|
psTraceErrr("Invalid Finished length\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
#ifdef USE_TLS
|
|
}
|
|
#endif /* USE_TLS */
|
|
if ((int32) (end - c) < hsLen)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceErrr("Invalid Finished length\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
if (memcmpct(c, hsMsgHash, hsLen) != 0)
|
|
{
|
|
ssl->err = SSL_ALERT_DECRYPT_ERROR;
|
|
psTraceErrr("Invalid handshake msg hash\n");
|
|
psTraceBytes("recv", c, hsLen);
|
|
psTraceBytes("have", hsMsgHash, hsLen);
|
|
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 ENABLE_SECURE_REHANDSHAKES
|
|
/* We're the server and we are doing a resumed (i.e. abbreviated)
|
|
handshake. The Finished message we just parsed was the final
|
|
handshake message. */
|
|
ssl->secureRenegotiationInProgress = PS_FALSE;
|
|
#endif
|
|
#ifdef USE_SSL_INFORMATIONAL_TRACE
|
|
/* Server side resumed completion */
|
|
matrixSslPrintHSDetails(ssl);
|
|
#endif
|
|
sslFreeHSHash(ssl);
|
|
}
|
|
}
|
|
else /* We are the client. */
|
|
{
|
|
#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 ENABLE_SECURE_REHANDSHAKES
|
|
/* We are the client and were doing a full handshake.
|
|
The Finished message we just parsed was the final
|
|
handshake message. */
|
|
ssl->secureRenegotiationInProgress = PS_FALSE;
|
|
#endif
|
|
#ifdef USE_SSL_INFORMATIONAL_TRACE
|
|
/* Client side standard completion */
|
|
matrixSslPrintHSDetails(ssl);
|
|
#endif
|
|
sslFreeHSHash(ssl);
|
|
}
|
|
}
|
|
#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->bFlags & BFLAG_KEEP_PEER_CERTS))
|
|
{
|
|
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 (ACTV_VER(ssl, v_dtls_any))
|
|
{
|
|
/* 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;
|
|
int32 pathLen;
|
|
|
|
psTracePrintHsMessageParse(ssl, SSL_HS_CERTIFICATE);
|
|
|
|
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;
|
|
psTraceErrr("Invalid Certificate message\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
certChainLen = *c << 16; c++;
|
|
certChainLen |= *c << 8; c++;
|
|
certChainLen |= *c; c++;
|
|
if (certChainLen < 3)
|
|
{
|
|
# 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 (NGTD_VER(ssl, v_ssl_3_0))
|
|
{
|
|
ssl->err = SSL_ALERT_NO_CERTIFICATE;
|
|
}
|
|
else
|
|
{
|
|
ssl->err = SSL_ALERT_BAD_CERTIFICATE;
|
|
}
|
|
psTraceErrr("No certificate sent to verify\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
if (end - c < 3)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceErrr("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;
|
|
|
|
# if defined(USE_HARDWARE_CRYPTO_PKA) || defined(USE_EXT_CERTIFICATE_VERIFY_SIGNING)
|
|
/* Skip re-parsing the certs if pending. The above few bytes are fine */
|
|
if (ssl->hwflags & SSL_HWFLAGS_PENDING_PKA_R)
|
|
{
|
|
c += certChainLen;
|
|
ssl->hwflags &= ~SSL_HWFLAGS_PENDING_PKA_R;
|
|
goto RESUME_VALIDATE_CERTS;
|
|
}
|
|
# endif /* USE_HARDWARE_CRYPTO_PKA || USE_EXT_CERTIFICATE_VERIFY_SIGNING */
|
|
/* Chain must be at least 3 b certLen */
|
|
while (certChainLen >= 3)
|
|
{
|
|
int32 certFlags = 0;
|
|
|
|
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;
|
|
psTraceErrr("Invalid certificate length\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
if (ssl->bFlags & BFLAG_KEEP_PEER_CERT_DER)
|
|
{
|
|
certFlags |= CERT_STORE_UNPARSED_BUFFER;
|
|
}
|
|
/*
|
|
Extract the binary cert message into the cert structure
|
|
*/
|
|
if ((parseLen = psX509ParseCert(ssl->hsPool, c, certLen, &cert, certFlags))
|
|
< 0)
|
|
{
|
|
psTraceErrr("Parsing of the peer certificate failed\n");
|
|
psX509FreeCert(cert);
|
|
if (parseLen == PS_MEM_FAIL)
|
|
{
|
|
ssl->err = SSL_ALERT_INTERNAL_ERROR;
|
|
}
|
|
else
|
|
{
|
|
ssl->err = SSL_ALERT_BAD_CERTIFICATE;
|
|
}
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
# ifdef ALLOW_VERSION_1_ROOT_CERT_PARSE
|
|
/* When ALLOW_VERSION_1_ROOT_CERT_PARSE is defined,
|
|
psX509ParseCert lets version 1 certificates through, in
|
|
order to support loading of locally trusted v1 root
|
|
certs. This means that we need to explicitly reject v1
|
|
certificates sent to us by the peer. They cannot be
|
|
trusted due to missing Basic Constraints, etc. */
|
|
if (cert->version != 2)
|
|
{
|
|
psTraceErrr("Version 1 peer certificates not allowed\n");
|
|
ssl->err = SSL_ALERT_BAD_CERTIFICATE;
|
|
}
|
|
# endif /* ALLOW_VERSION_1_ROOT_CERT_PARSE */
|
|
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 */
|
|
# if defined(USE_HARDWARE_CRYPTO_PKA) || defined(USE_EXT_CERTIFICATE_VERIFY_SIGNING)
|
|
RESUME_VALIDATE_CERTS:
|
|
# endif /* USE_HARDWARE_CRYPTO_PKA || USE_EXT_CERTIFICATE_VERIFY_SIGNING */
|
|
|
|
rc = matrixValidateCertsExt(ssl->hsPool, ssl->sec.cert,
|
|
ssl->keys == NULL ? NULL : ssl->keys->CAcerts, ssl->expectedName,
|
|
&foundIssuer, pkiData, ssl->memAllocPtr, &ssl->validateCertsOpts);
|
|
|
|
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;
|
|
pathLen = 0;
|
|
while (cert)
|
|
{
|
|
++pathLen;
|
|
if (ssl->validateCertsOpts.max_verify_depth > 0)
|
|
{
|
|
int exceeded = 0;
|
|
psTraceIntInfo("max_verify_depth: %d\n", ssl->validateCertsOpts.max_verify_depth);
|
|
/*
|
|
A maximum verification depth has been specified in session opts.
|
|
*/
|
|
if (pathLen > (ssl->validateCertsOpts.max_verify_depth))
|
|
{
|
|
exceeded = 1;
|
|
}
|
|
else if (pathLen == (ssl->validateCertsOpts.max_verify_depth))
|
|
{
|
|
/*
|
|
We don't have the root in cert->next. So do the
|
|
following: If the cert is _not_ self-signed, it must
|
|
have a valid root cert as the issuer, since this
|
|
is checked in matrixValidateCerts. Now take that root
|
|
into account when checking the path length.
|
|
*/
|
|
if (memcmpct(&cert->subject, &cert->issuer,
|
|
sizeof(cert->subject)))
|
|
{
|
|
/* Root cert causes depth to be exceeded. */
|
|
exceeded = 1;
|
|
}
|
|
}
|
|
if (exceeded)
|
|
{
|
|
/* Max depth exceeded. */
|
|
psTraceErrr("Error: max_verify_depth exceeded\n");
|
|
ssl->err = SSL_ALERT_UNKNOWN_CA;
|
|
cert->authStatus |= PS_CERT_AUTH_FAIL_PATH_LEN;
|
|
cert->authFailFlags |= PS_CERT_AUTH_FAIL_VERIFY_DEPTH_FLAG;
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
|
|
# ifdef USE_SSL_INFORMATIONAL_TRACE
|
|
/* The peer cert will be freed as soon as it is no longer needed,
|
|
so store information about the public key, to be logged
|
|
later in matrixSslPrintHSDetails. */
|
|
ssl->peerAuthKeyType = ssl->sec.cert->publicKey.type;
|
|
#ifdef USE_RSA
|
|
if (ssl->peerAuthKeyType == PS_RSA)
|
|
{
|
|
ssl->peerAuthKeyNBits = ssl->sec.cert->publicKey.keysize * 8;
|
|
}
|
|
#endif
|
|
#ifdef USE_ECC
|
|
if (ssl->peerAuthKeyType == PS_ECC || ssl->peerAuthKeyType == PS_ED25519)
|
|
{
|
|
ssl->peerAuthKeyNBits = ssl->sec.cert->publicKey.key.ecc.curve->size * 8;
|
|
}
|
|
#endif
|
|
# endif /* USE_SSL_INFORMATIONAL_TRACE */
|
|
|
|
/* 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 (rc=%d)\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_RESPONSE
|
|
/* 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 /* USE_OCSP_RESPONSE */
|
|
}
|
|
*cp = c;
|
|
ssl->decState = SSL_HS_CERTIFICATE;
|
|
return MATRIXSSL_SUCCESS;
|
|
}
|
|
# endif /* USE_CLIENT_SIDE_SSL || USE_CLIENT_AUTH */
|
|
#endif /* !USE_ONLY_PSK_CIPHER_SUITE */
|
|
|
|
/******************************************************************************/
|