5411 lines
158 KiB
C
5411 lines
158 KiB
C
/* cyassl_int.c
|
|
*
|
|
* Copyright (C) 2006-2011 Sawtooth Consulting Ltd.
|
|
*
|
|
* This file is part of CyaSSL.
|
|
*
|
|
* CyaSSL is free software; 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.
|
|
*
|
|
* CyaSSL is distributed in the hope that it will be useful,
|
|
* but 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
|
|
*/
|
|
|
|
|
|
|
|
#include "cyassl_int.h"
|
|
#include "cyassl_error.h"
|
|
#include "asn.h"
|
|
|
|
#ifdef HAVE_LIBZ
|
|
#include "zlib.h"
|
|
#endif
|
|
|
|
#ifdef HAVE_NTRU
|
|
#include "crypto_ntru.h"
|
|
#endif
|
|
|
|
#if defined(DEBUG_CYASSL) || defined(SHOW_SECRETS)
|
|
#include <stdio.h>
|
|
#endif
|
|
|
|
#ifdef __sun
|
|
#include <sys/filio.h>
|
|
#endif
|
|
|
|
#define TRUE 1
|
|
#define FALSE 0
|
|
|
|
|
|
#if defined(OPENSSL_EXTRA) && defined(NO_DH)
|
|
#error OPENSSL_EXTRA needs DH, please remove NO_DH
|
|
#endif
|
|
|
|
|
|
int CyaSSL_negotiate(SSL*);
|
|
|
|
|
|
#ifndef NO_CYASSL_CLIENT
|
|
static int DoHelloVerifyRequest(SSL* ssl, const byte* input, word32*);
|
|
static int DoServerHello(SSL* ssl, const byte* input, word32*);
|
|
static int DoCertificateRequest(SSL* ssl, const byte* input, word32*);
|
|
static int DoServerKeyExchange(SSL* ssl, const byte* input, word32*);
|
|
#endif
|
|
|
|
|
|
#ifndef NO_CYASSL_SERVER
|
|
static int DoClientHello(SSL* ssl, const byte* input, word32*, word32,
|
|
word32);
|
|
static int DoCertificateVerify(SSL* ssl, byte*, word32*, word32);
|
|
static int DoClientKeyExchange(SSL* ssl, byte* input, word32*);
|
|
#endif
|
|
|
|
typedef enum {
|
|
doProcessInit = 0,
|
|
#ifndef NO_CYASSL_SERVER
|
|
runProcessOldClientHello,
|
|
#endif
|
|
getRecordLayerHeader,
|
|
getData,
|
|
runProcessingOneMessage
|
|
} processReply;
|
|
|
|
static void Hmac(SSL* ssl, byte* digest, const byte* buffer, word32 sz,
|
|
int content, int verify);
|
|
|
|
static void BuildCertHashes(SSL* ssl, Hashes* hashes);
|
|
|
|
|
|
void BuildTlsFinished(SSL* ssl, Hashes* hashes, const byte* sender);
|
|
|
|
|
|
#ifndef min
|
|
|
|
static INLINE word32 min(word32 a, word32 b)
|
|
{
|
|
return a > b ? b : a;
|
|
}
|
|
|
|
#endif /* min */
|
|
|
|
|
|
int IsTLS(const SSL* ssl)
|
|
{
|
|
if (ssl->version.major == SSLv3_MAJOR && ssl->version.minor >=TLSv1_MINOR)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int IsAtLeastTLSv1_2(const SSL* ssl)
|
|
{
|
|
if (ssl->version.major == SSLv3_MAJOR && ssl->version.minor >=TLSv1_2_MINOR)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
#ifdef HAVE_NTRU
|
|
|
|
static byte GetEntropy(ENTROPY_CMD cmd, byte* out)
|
|
{
|
|
/* TODO: add locking? */
|
|
static RNG rng;
|
|
|
|
if (cmd == INIT) {
|
|
int ret = InitRng(&rng);
|
|
if (ret == 0)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
if (out == NULL)
|
|
return 0;
|
|
|
|
if (cmd == GET_BYTE_OF_ENTROPY) {
|
|
RNG_GenerateBlock(&rng, out, 1);
|
|
return 1;
|
|
}
|
|
|
|
if (cmd == GET_NUM_BYTES_PER_BYTE_OF_ENTROPY) {
|
|
*out = 1;
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif /* HAVE_NTRU */
|
|
|
|
static INLINE void c32to24(word32 in, word24 out)
|
|
{
|
|
out[0] = (in >> 16) & 0xff;
|
|
out[1] = (in >> 8) & 0xff;
|
|
out[2] = in & 0xff;
|
|
}
|
|
|
|
|
|
static INLINE void c32to48(word32 in, byte out[6])
|
|
{
|
|
out[0] = 0;
|
|
out[1] = 0;
|
|
out[2] = (in >> 24) & 0xff;
|
|
out[3] = (in >> 16) & 0xff;
|
|
out[4] = (in >> 8) & 0xff;
|
|
out[5] = in & 0xff;
|
|
}
|
|
|
|
|
|
/* convert 16 bit integer to opaque */
|
|
static INLINE void c16toa(word16 u16, byte* c)
|
|
{
|
|
c[0] = (u16 >> 8) & 0xff;
|
|
c[1] = u16 & 0xff;
|
|
}
|
|
|
|
|
|
/* convert 32 bit integer to opaque */
|
|
static INLINE void c32toa(word32 u32, byte* c)
|
|
{
|
|
c[0] = (u32 >> 24) & 0xff;
|
|
c[1] = (u32 >> 16) & 0xff;
|
|
c[2] = (u32 >> 8) & 0xff;
|
|
c[3] = u32 & 0xff;
|
|
}
|
|
|
|
|
|
/* convert a 24 bit integer into a 32 bit one */
|
|
static INLINE void c24to32(const word24 u24, word32* u32)
|
|
{
|
|
*u32 = 0;
|
|
*u32 = (u24[0] << 16) | (u24[1] << 8) | u24[2];
|
|
}
|
|
|
|
|
|
/* convert opaque to 16 bit integer */
|
|
static INLINE void ato16(const byte* c, word16* u16)
|
|
{
|
|
*u16 = 0;
|
|
*u16 = (c[0] << 8) | (c[1]);
|
|
}
|
|
|
|
|
|
/* convert opaque to 32 bit integer */
|
|
static INLINE void ato32(const byte* c, word32* u32)
|
|
{
|
|
*u32 = 0;
|
|
*u32 = (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3];
|
|
}
|
|
|
|
|
|
#ifdef HAVE_LIBZ
|
|
|
|
/* alloc user allocs to work with zlib */
|
|
void* myAlloc(void* opaque, unsigned int item, unsigned int size)
|
|
{
|
|
return XMALLOC(item * size, opaque, DYNAMIC_TYPE_LIBZ);
|
|
}
|
|
|
|
|
|
void myFree(void* opaque, void* memory)
|
|
{
|
|
XFREE(memory, opaque, DYNAMIC_TYPE_LIBZ);
|
|
}
|
|
|
|
|
|
/* init zlib comp/decomp streams, 0 on success */
|
|
static int InitStreams(SSL* ssl)
|
|
{
|
|
ssl->c_stream.zalloc = (alloc_func)myAlloc;
|
|
ssl->c_stream.zfree = (free_func)myFree;
|
|
ssl->c_stream.opaque = (voidpf)ssl->heap;
|
|
|
|
if (deflateInit(&ssl->c_stream, 8) != Z_OK) return ZLIB_INIT_ERROR;
|
|
|
|
ssl->didStreamInit = 1;
|
|
|
|
ssl->d_stream.zalloc = (alloc_func)myAlloc;
|
|
ssl->d_stream.zfree = (free_func)myFree;
|
|
ssl->d_stream.opaque = (voidpf)ssl->heap;
|
|
|
|
if (inflateInit(&ssl->d_stream) != Z_OK) return ZLIB_INIT_ERROR;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void FreeStreams(SSL* ssl)
|
|
{
|
|
if (ssl->didStreamInit) {
|
|
deflateEnd(&ssl->c_stream);
|
|
inflateEnd(&ssl->d_stream);
|
|
}
|
|
}
|
|
|
|
|
|
/* compress in to out, return out size or error */
|
|
static int Compress(SSL* ssl, byte* in, int inSz, byte* out, int outSz)
|
|
{
|
|
int err;
|
|
int currTotal = ssl->c_stream.total_out;
|
|
|
|
/* put size in front of compression */
|
|
c16toa((word16)inSz, out);
|
|
out += 2;
|
|
outSz -= 2;
|
|
|
|
ssl->c_stream.next_in = in;
|
|
ssl->c_stream.avail_in = inSz;
|
|
ssl->c_stream.next_out = out;
|
|
ssl->c_stream.avail_out = outSz;
|
|
|
|
err = deflate(&ssl->c_stream, Z_SYNC_FLUSH);
|
|
if (err != Z_OK && err != Z_STREAM_END) return ZLIB_COMPRESS_ERROR;
|
|
|
|
return ssl->c_stream.total_out - currTotal + sizeof(word16);
|
|
}
|
|
|
|
|
|
/* decompress in to out, returnn out size or error */
|
|
static int DeCompress(SSL* ssl, byte* in, int inSz, byte* out, int outSz)
|
|
{
|
|
int err;
|
|
int currTotal = ssl->d_stream.total_out;
|
|
word16 len;
|
|
|
|
/* find size in front of compression */
|
|
ato16(in, &len);
|
|
in += 2;
|
|
inSz -= 2;
|
|
|
|
ssl->d_stream.next_in = in;
|
|
ssl->d_stream.avail_in = inSz;
|
|
ssl->d_stream.next_out = out;
|
|
ssl->d_stream.avail_out = outSz;
|
|
|
|
err = inflate(&ssl->d_stream, Z_SYNC_FLUSH);
|
|
if (err != Z_OK && err != Z_STREAM_END) return ZLIB_DECOMPRESS_ERROR;
|
|
|
|
return ssl->d_stream.total_out - currTotal;
|
|
}
|
|
|
|
#endif /* HAVE_LIBZ */
|
|
|
|
|
|
void InitSSL_Method(SSL_METHOD* method, ProtocolVersion pv)
|
|
{
|
|
method->version = pv;
|
|
method->side = CLIENT_END;
|
|
method->verifyPeer = 0;
|
|
method->verifyNone = 0;
|
|
method->failNoCert = 0;
|
|
method->downgrade = 0;
|
|
}
|
|
|
|
|
|
void InitSSL_Ctx(SSL_CTX* ctx, SSL_METHOD* method)
|
|
{
|
|
ctx->method = method;
|
|
ctx->certificate.buffer = 0;
|
|
ctx->privateKey.buffer = 0;
|
|
ctx->haveDH = 0;
|
|
ctx->haveNTRU = 0; /* start off */
|
|
ctx->haveECDSA = 0; /* start off */
|
|
ctx->heap = ctx; /* defaults to self */
|
|
#ifndef NO_PSK
|
|
ctx->havePSK = 0;
|
|
ctx->server_hint[0] = 0;
|
|
ctx->client_psk_cb = 0;
|
|
ctx->server_psk_cb = 0;
|
|
#endif /* NO_PSK */
|
|
|
|
#ifdef OPENSSL_EXTRA
|
|
ctx->passwd_cb = 0;
|
|
ctx->userdata = 0;
|
|
#endif /* OPENSSL_EXTRA */
|
|
|
|
#ifndef CYASSL_USER_IO
|
|
ctx->CBIORecv = EmbedReceive;
|
|
ctx->CBIOSend = EmbedSend;
|
|
#else
|
|
/* user will set */
|
|
ctx->CBIORecv = NULL;
|
|
ctx->CBIOSend = NULL;
|
|
#endif
|
|
ctx->partialWrite = 0;
|
|
ctx->verifyCallback = 0;
|
|
|
|
ctx->caList = 0;
|
|
#ifdef HAVE_NTRU
|
|
if (method->side == CLIENT_END)
|
|
ctx->haveNTRU = 1; /* always on cliet side */
|
|
/* server can turn on by loading key */
|
|
#endif
|
|
#ifdef HAVE_ECC
|
|
if (method->side == CLIENT_END)
|
|
ctx->haveECDSA = 1; /* always on cliet side */
|
|
/* server can turn on by loading key */
|
|
#endif
|
|
/* remove DH later if server didn't set, add psk later */
|
|
InitSuites(&ctx->suites, method->version, TRUE, FALSE, ctx->haveNTRU,
|
|
ctx->haveECDSA, method->side);
|
|
ctx->verifyPeer = 0;
|
|
ctx->verifyNone = 0;
|
|
ctx->failNoCert = 0;
|
|
ctx->sessionCacheOff = 0; /* initially on */
|
|
ctx->sessionCacheFlushOff = 0; /* initially on */
|
|
ctx->sendVerify = 0;
|
|
ctx->quietShutdown = 0;
|
|
|
|
}
|
|
|
|
|
|
/* In case contexts are held in array and don't want to free actual ctx */
|
|
void SSL_CtxResourceFree(SSL_CTX* ctx)
|
|
{
|
|
XFREE(ctx->privateKey.buffer, ctx->heap, DYNAMIC_TYPE_KEY);
|
|
XFREE(ctx->certificate.buffer, ctx->heap, DYNAMIC_TYPE_CERT);
|
|
XFREE(ctx->method, ctx->heap, DYNAMIC_TYPE_METHOD);
|
|
|
|
FreeSigners(ctx->caList, ctx->heap);
|
|
}
|
|
|
|
|
|
void FreeSSL_Ctx(SSL_CTX* ctx)
|
|
{
|
|
SSL_CtxResourceFree(ctx);
|
|
XFREE(ctx, ctx->heap, DYNAMIC_TYPE_CTX);
|
|
}
|
|
|
|
|
|
|
|
void InitSuites(Suites* suites, ProtocolVersion pv, byte haveDH, byte havePSK,
|
|
byte haveNTRU, byte haveECDSA, int side)
|
|
{
|
|
word32 idx = 0;
|
|
int tls = pv.major == SSLv3_MAJOR && pv.minor >= TLSv1_MINOR;
|
|
int tls1_2 = pv.major == SSLv3_MAJOR && pv.minor >= TLSv1_2_MINOR;
|
|
int haveRSA = 1;
|
|
|
|
(void)tls; /* shut up compiler */
|
|
|
|
if (side == SERVER_END && haveECDSA)
|
|
haveRSA = 0; /* can't do RSA with ECDSA cert */
|
|
|
|
#ifdef CYASSL_DTLS
|
|
if (pv.major == DTLS_MAJOR && pv.minor == DTLS_MINOR)
|
|
tls = 1;
|
|
#endif
|
|
|
|
suites->setSuites = 0; /* user hasn't set yet */
|
|
|
|
#ifdef BUILD_TLS_NTRU_RSA_WITH_AES_256_CBC_SHA
|
|
if (tls && haveNTRU && haveRSA) {
|
|
suites->suites[idx++] = 0;
|
|
suites->suites[idx++] = TLS_NTRU_RSA_WITH_AES_256_CBC_SHA;
|
|
}
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_NTRU_RSA_WITH_AES_128_CBC_SHA
|
|
if (tls && haveNTRU && haveRSA) {
|
|
suites->suites[idx++] = 0;
|
|
suites->suites[idx++] = TLS_NTRU_RSA_WITH_AES_128_CBC_SHA;
|
|
}
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_NTRU_RSA_WITH_RC4_128_SHA
|
|
if (tls && haveNTRU && haveRSA) {
|
|
suites->suites[idx++] = 0;
|
|
suites->suites[idx++] = TLS_NTRU_RSA_WITH_RC4_128_SHA;
|
|
}
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_NTRU_RSA_WITH_3DES_EDE_CBC_SHA
|
|
if (tls && haveNTRU && haveRSA) {
|
|
suites->suites[idx++] = 0;
|
|
suites->suites[idx++] = TLS_NTRU_RSA_WITH_3DES_EDE_CBC_SHA;
|
|
}
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
|
|
if (tls && haveECDSA) {
|
|
suites->suites[idx++] = ECC_BYTE;
|
|
suites->suites[idx++] = TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA;
|
|
}
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
|
|
if (tls && haveECDSA) {
|
|
suites->suites[idx++] = ECC_BYTE;
|
|
suites->suites[idx++] = TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA;
|
|
}
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_RC4_128_SHA
|
|
if (tls && haveECDSA) {
|
|
suites->suites[idx++] = ECC_BYTE;
|
|
suites->suites[idx++] = TLS_ECDHE_ECDSA_WITH_RC4_128_SHA;
|
|
}
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA
|
|
if (tls && haveECDSA) {
|
|
suites->suites[idx++] = ECC_BYTE;
|
|
suites->suites[idx++] = TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA;
|
|
}
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
|
|
if (tls && haveRSA) {
|
|
suites->suites[idx++] = ECC_BYTE;
|
|
suites->suites[idx++] = TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA;
|
|
}
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
|
|
if (tls && haveRSA) {
|
|
suites->suites[idx++] = ECC_BYTE;
|
|
suites->suites[idx++] = TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA;
|
|
}
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_ECDHE_RSA_WITH_RC4_128_SHA
|
|
if (tls && haveRSA) {
|
|
suites->suites[idx++] = ECC_BYTE;
|
|
suites->suites[idx++] = TLS_ECDHE_RSA_WITH_RC4_128_SHA;
|
|
}
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
|
|
if (tls && haveRSA) {
|
|
suites->suites[idx++] = ECC_BYTE;
|
|
suites->suites[idx++] = TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA;
|
|
}
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_DHE_RSA_WITH_AES_256_CBC_SHA
|
|
if (tls && haveDH && haveRSA) {
|
|
suites->suites[idx++] = 0;
|
|
suites->suites[idx++] = TLS_DHE_RSA_WITH_AES_256_CBC_SHA;
|
|
}
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_DHE_RSA_WITH_AES_128_CBC_SHA
|
|
if (tls && haveDH && haveRSA) {
|
|
suites->suites[idx++] = 0;
|
|
suites->suites[idx++] = TLS_DHE_RSA_WITH_AES_128_CBC_SHA;
|
|
}
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_RSA_WITH_AES_256_CBC_SHA256
|
|
if (tls1_2 && haveRSA) {
|
|
suites->suites[idx++] = 0;
|
|
suites->suites[idx++] = TLS_RSA_WITH_AES_256_CBC_SHA256;
|
|
}
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_RSA_WITH_AES_128_CBC_SHA256
|
|
if (tls1_2 && haveRSA) {
|
|
suites->suites[idx++] = 0;
|
|
suites->suites[idx++] = TLS_RSA_WITH_AES_128_CBC_SHA256;
|
|
}
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_RSA_WITH_AES_256_CBC_SHA
|
|
if (tls && haveRSA) {
|
|
suites->suites[idx++] = 0;
|
|
suites->suites[idx++] = TLS_RSA_WITH_AES_256_CBC_SHA;
|
|
}
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_RSA_WITH_AES_128_CBC_SHA
|
|
if (tls && haveRSA) {
|
|
suites->suites[idx++] = 0;
|
|
suites->suites[idx++] = TLS_RSA_WITH_AES_128_CBC_SHA;
|
|
}
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_PSK_WITH_AES_256_CBC_SHA
|
|
if (tls && havePSK) {
|
|
suites->suites[idx++] = 0;
|
|
suites->suites[idx++] = TLS_PSK_WITH_AES_256_CBC_SHA;
|
|
}
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_PSK_WITH_AES_128_CBC_SHA
|
|
if (tls && havePSK) {
|
|
suites->suites[idx++] = 0;
|
|
suites->suites[idx++] = TLS_PSK_WITH_AES_128_CBC_SHA;
|
|
}
|
|
#endif
|
|
|
|
#ifdef BUILD_SSL_RSA_WITH_RC4_128_SHA
|
|
if (haveRSA ) {
|
|
suites->suites[idx++] = 0;
|
|
suites->suites[idx++] = SSL_RSA_WITH_RC4_128_SHA;
|
|
}
|
|
#endif
|
|
|
|
#ifdef BUILD_SSL_RSA_WITH_RC4_128_MD5
|
|
if (haveRSA ) {
|
|
suites->suites[idx++] = 0;
|
|
suites->suites[idx++] = SSL_RSA_WITH_RC4_128_MD5;
|
|
}
|
|
#endif
|
|
|
|
#ifdef BUILD_SSL_RSA_WITH_3DES_EDE_CBC_SHA
|
|
if (haveRSA ) {
|
|
suites->suites[idx++] = 0;
|
|
suites->suites[idx++] = SSL_RSA_WITH_3DES_EDE_CBC_SHA;
|
|
}
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_RSA_WITH_HC_128_CBC_MD5
|
|
if (tls && haveRSA) {
|
|
suites->suites[idx++] = 0;
|
|
suites->suites[idx++] = TLS_RSA_WITH_HC_128_CBC_MD5;
|
|
}
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_RSA_WITH_HC_128_CBC_SHA
|
|
if (tls && haveRSA) {
|
|
suites->suites[idx++] = 0;
|
|
suites->suites[idx++] = TLS_RSA_WITH_HC_128_CBC_SHA;
|
|
}
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_RSA_WITH_RABBIT_CBC_SHA
|
|
if (tls && haveRSA) {
|
|
suites->suites[idx++] = 0;
|
|
suites->suites[idx++] = TLS_RSA_WITH_RABBIT_CBC_SHA;
|
|
}
|
|
#endif
|
|
|
|
suites->suiteSz = idx;
|
|
}
|
|
|
|
|
|
int InitSSL(SSL* ssl, SSL_CTX* ctx)
|
|
{
|
|
int ret;
|
|
byte havePSK = 0;
|
|
|
|
ssl->ctx = ctx; /* only for passing to calls, options could change */
|
|
ssl->version = ctx->method->version;
|
|
ssl->suites = ctx->suites;
|
|
|
|
#ifdef HAVE_LIBZ
|
|
ssl->didStreamInit = 0;
|
|
#endif
|
|
|
|
ssl->buffers.certificate.buffer = 0;
|
|
ssl->buffers.key.buffer = 0;
|
|
ssl->buffers.inputBuffer.length = 0;
|
|
ssl->buffers.inputBuffer.idx = 0;
|
|
ssl->buffers.inputBuffer.buffer = ssl->buffers.inputBuffer.staticBuffer;
|
|
ssl->buffers.inputBuffer.bufferSize = STATIC_BUFFER_LEN;
|
|
ssl->buffers.inputBuffer.dynamicFlag = 0;
|
|
ssl->buffers.outputBuffer.length = 0;
|
|
ssl->buffers.outputBuffer.idx = 0;
|
|
ssl->buffers.outputBuffer.buffer = ssl->buffers.outputBuffer.staticBuffer;
|
|
ssl->buffers.outputBuffer.bufferSize = STATIC_BUFFER_LEN;
|
|
ssl->buffers.outputBuffer.dynamicFlag = 0;
|
|
ssl->buffers.domainName.buffer = 0;
|
|
ssl->buffers.serverDH_P.buffer = 0;
|
|
ssl->buffers.serverDH_G.buffer = 0;
|
|
ssl->buffers.serverDH_Pub.buffer = 0;
|
|
ssl->buffers.serverDH_Priv.buffer = 0;
|
|
ssl->buffers.clearOutputBuffer.buffer = 0;
|
|
ssl->buffers.clearOutputBuffer.length = 0;
|
|
ssl->buffers.prevSent = 0;
|
|
ssl->buffers.plainSz = 0;
|
|
|
|
if ( (ret = InitRng(&ssl->rng)) )
|
|
return ret;
|
|
|
|
InitMd5(&ssl->hashMd5);
|
|
InitSha(&ssl->hashSha);
|
|
#ifndef NO_SHA256
|
|
InitSha256(&ssl->hashSha256);
|
|
#endif
|
|
InitRsaKey(&ssl->peerRsaKey, ctx->heap);
|
|
|
|
ssl->peerRsaKeyPresent = 0;
|
|
ssl->options.side = ctx->method->side;
|
|
ssl->options.downgrade = ctx->method->downgrade;
|
|
ssl->error = 0;
|
|
ssl->options.connReset = 0;
|
|
ssl->options.isClosed = 0;
|
|
ssl->options.closeNotify = 0;
|
|
ssl->options.sentNotify = 0;
|
|
ssl->options.usingCompression = 0;
|
|
ssl->options.haveDH = ctx->haveDH;
|
|
ssl->options.haveNTRU = ctx->haveNTRU;
|
|
ssl->options.haveECDSA = ctx->haveECDSA;
|
|
ssl->options.havePeerCert = 0;
|
|
ssl->options.usingPSK_cipher = 0;
|
|
ssl->options.sendAlertState = 0;
|
|
#ifndef NO_PSK
|
|
havePSK = ctx->havePSK;
|
|
ssl->options.havePSK = ctx->havePSK;
|
|
ssl->options.client_psk_cb = ctx->client_psk_cb;
|
|
ssl->options.server_psk_cb = ctx->server_psk_cb;
|
|
#endif /* NO_PSK */
|
|
|
|
ssl->options.serverState = NULL_STATE;
|
|
ssl->options.clientState = NULL_STATE;
|
|
ssl->options.connectState = CONNECT_BEGIN;
|
|
ssl->options.acceptState = ACCEPT_BEGIN;
|
|
ssl->options.handShakeState = NULL_STATE;
|
|
ssl->options.processReply = doProcessInit;
|
|
|
|
#ifdef CYASSL_DTLS
|
|
ssl->keys.dtls_sequence_number = 0;
|
|
ssl->keys.dtls_peer_sequence_number = 0;
|
|
ssl->keys.dtls_handshake_number = 0;
|
|
ssl->keys.dtls_epoch = 0;
|
|
ssl->keys.dtls_peer_epoch = 0;
|
|
#endif
|
|
ssl->keys.encryptionOn = 0; /* initially off */
|
|
ssl->options.sessionCacheOff = ctx->sessionCacheOff;
|
|
ssl->options.sessionCacheFlushOff = ctx->sessionCacheFlushOff;
|
|
|
|
ssl->options.verifyPeer = ctx->verifyPeer;
|
|
ssl->options.verifyNone = ctx->verifyNone;
|
|
ssl->options.failNoCert = ctx->failNoCert;
|
|
ssl->options.sendVerify = ctx->sendVerify;
|
|
|
|
ssl->options.resuming = 0;
|
|
ssl->hmac = Hmac; /* default to SSLv3 */
|
|
ssl->heap = ctx->heap; /* defaults to self */
|
|
ssl->options.tls = 0;
|
|
ssl->options.tls1_1 = 0;
|
|
ssl->options.dtls = 0;
|
|
ssl->options.partialWrite = ctx->partialWrite;
|
|
ssl->options.quietShutdown = ctx->quietShutdown;
|
|
|
|
/* SSL_CTX still owns certificate, key, and caList buffers */
|
|
ssl->buffers.certificate = ctx->certificate;
|
|
ssl->buffers.key = ctx->privateKey;
|
|
ssl->caList = ctx->caList;
|
|
|
|
#ifdef OPENSSL_EXTRA
|
|
ssl->peerCert.issuer.sz = 0;
|
|
ssl->peerCert.subject.sz = 0;
|
|
#endif
|
|
|
|
/* make sure server has cert and key unless using PSK */
|
|
if (ssl->options.side == SERVER_END && !havePSK)
|
|
if (!ssl->buffers.certificate.buffer || !ssl->buffers.key.buffer)
|
|
return NO_PRIVATE_KEY;
|
|
|
|
#ifndef NO_PSK
|
|
ssl->arrays.client_identity[0] = 0;
|
|
if (ctx->server_hint[0]) /* set in CTX */
|
|
XSTRNCPY(ssl->arrays.server_hint, ctx->server_hint, MAX_PSK_ID_LEN);
|
|
else
|
|
ssl->arrays.server_hint[0] = 0;
|
|
#endif /* NO_PSK */
|
|
|
|
#ifdef CYASSL_CALLBACKS
|
|
ssl->hsInfoOn = 0;
|
|
ssl->toInfoOn = 0;
|
|
#endif
|
|
|
|
#ifdef HAVE_ECC
|
|
ssl->peerEccKeyPresent = 0;
|
|
ecc_init(&ssl->peerEccKey);
|
|
ssl->peerEccDsaKeyPresent = 0;
|
|
ecc_init(&ssl->peerEccDsaKey);
|
|
ssl->eccDsaKeyPresent = 0;
|
|
ecc_init(&ssl->eccDsaKey);
|
|
ssl->eccTempKeyPresent = 0;
|
|
ecc_init(&ssl->eccTempKey);
|
|
|
|
/* make ECDHE for server side */
|
|
if (ssl->options.side == SERVER_END) {
|
|
if (ecc_make_key(&ssl->rng, ECDHE_SIZE, &ssl->eccTempKey) != 0)
|
|
return ECC_MAKEKEY_ERROR;
|
|
ssl->eccTempKeyPresent = 1;
|
|
}
|
|
#endif
|
|
|
|
/* make sure server has DH parms, and add PSK if there, add NTRU too */
|
|
if (!ssl->ctx->suites.setSuites) { /* trust user override */
|
|
if (ssl->options.side == SERVER_END)
|
|
InitSuites(&ssl->suites, ssl->version,ssl->options.haveDH, havePSK,
|
|
ssl->options.haveNTRU, ssl->options.haveECDSA,
|
|
ssl->ctx->method->side);
|
|
else
|
|
InitSuites(&ssl->suites, ssl->version, TRUE, havePSK,
|
|
ssl->options.haveNTRU, ssl->options.haveECDSA,
|
|
ssl->ctx->method->side);
|
|
}
|
|
|
|
ssl->rfd = -1; /* set to invalid descriptor */
|
|
ssl->wfd = -1;
|
|
ssl->biord = 0;
|
|
ssl->biowr = 0;
|
|
|
|
ssl->IOCB_ReadCtx = &ssl->rfd; /* prevent invalid pointer acess if not */
|
|
ssl->IOCB_WriteCtx = &ssl->wfd; /* correctly set */
|
|
|
|
#ifdef SESSION_CERTS
|
|
ssl->session.chain.count = 0;
|
|
#endif
|
|
|
|
ssl->cipher.ssl = ssl;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int BIO_free(BIO*); /* cyassl_int doesn't have */
|
|
|
|
|
|
/* In case holding SSL object in array and don't want to free actual ssl */
|
|
void SSL_ResourceFree(SSL* ssl)
|
|
{
|
|
XFREE(ssl->buffers.serverDH_Priv.buffer, ssl->heap, DYNAMIC_TYPE_DH);
|
|
XFREE(ssl->buffers.serverDH_Pub.buffer, ssl->heap, DYNAMIC_TYPE_DH);
|
|
XFREE(ssl->buffers.serverDH_G.buffer, ssl->heap, DYNAMIC_TYPE_DH);
|
|
XFREE(ssl->buffers.serverDH_P.buffer, ssl->heap, DYNAMIC_TYPE_DH);
|
|
XFREE(ssl->buffers.domainName.buffer, ssl->heap, DYNAMIC_TYPE_DOMAIN);
|
|
FreeRsaKey(&ssl->peerRsaKey);
|
|
if (ssl->buffers.inputBuffer.dynamicFlag)
|
|
ShrinkInputBuffer(ssl, FORCED_FREE);
|
|
if (ssl->buffers.outputBuffer.dynamicFlag)
|
|
ShrinkOutputBuffer(ssl);
|
|
#if defined(OPENSSL_EXTRA) || defined(GOAHEAD_WS)
|
|
BIO_free(ssl->biord);
|
|
if (ssl->biord != ssl->biowr) /* in case same as write */
|
|
BIO_free(ssl->biowr);
|
|
#endif
|
|
#ifdef HAVE_LIBZ
|
|
FreeStreams(ssl);
|
|
#endif
|
|
#ifdef HAVE_ECC
|
|
ecc_free(&ssl->peerEccKey);
|
|
ecc_free(&ssl->peerEccDsaKey);
|
|
ecc_free(&ssl->eccTempKey);
|
|
ecc_free(&ssl->eccDsaKey);
|
|
#endif
|
|
}
|
|
|
|
|
|
void FreeSSL(SSL* ssl)
|
|
{
|
|
SSL_ResourceFree(ssl);
|
|
XFREE(ssl, ssl->heap, DYNAMIC_TYPE_SSL);
|
|
}
|
|
|
|
|
|
ProtocolVersion MakeSSLv3(void)
|
|
{
|
|
ProtocolVersion pv;
|
|
pv.major = SSLv3_MAJOR;
|
|
pv.minor = SSLv3_MINOR;
|
|
|
|
return pv;
|
|
}
|
|
|
|
|
|
#ifdef CYASSL_DTLS
|
|
|
|
ProtocolVersion MakeDTLSv1(void)
|
|
{
|
|
ProtocolVersion pv;
|
|
pv.major = DTLS_MAJOR;
|
|
pv.minor = DTLS_MINOR;
|
|
|
|
return pv;
|
|
}
|
|
|
|
#endif /* CYASSL_DTLS */
|
|
|
|
|
|
|
|
|
|
#ifdef USE_WINDOWS_API
|
|
|
|
timer_d Timer(void)
|
|
{
|
|
static int init = 0;
|
|
static LARGE_INTEGER freq;
|
|
LARGE_INTEGER count;
|
|
|
|
if (!init) {
|
|
QueryPerformanceFrequency(&freq);
|
|
init = 1;
|
|
}
|
|
|
|
QueryPerformanceCounter(&count);
|
|
|
|
return (double)count.QuadPart / freq.QuadPart;
|
|
}
|
|
|
|
|
|
word32 LowResTimer(void)
|
|
{
|
|
return (word32)Timer();
|
|
}
|
|
|
|
|
|
#elif defined(THREADX)
|
|
|
|
#include "rtptime.h"
|
|
|
|
word32 LowResTimer(void)
|
|
{
|
|
return (word32)rtp_get_system_sec();
|
|
}
|
|
|
|
|
|
#elif defined(MICRIUM)
|
|
|
|
word32 LowResTimer(void)
|
|
{
|
|
NET_SECURE_OS_TICK clk;
|
|
|
|
#if (NET_SECURE_MGR_CFG_EN == DEF_ENABLED)
|
|
clk = NetSecure_OS_TimeGet();
|
|
#endif
|
|
return (word32)clk;
|
|
}
|
|
|
|
#elif defined(USER_TICKS)
|
|
|
|
word32 LowResTimer(void)
|
|
{
|
|
/*
|
|
write your own clock tick function if don't want time(0)
|
|
needs second accuracy but doesn't have to correlated to EPOCH
|
|
*/
|
|
}
|
|
|
|
#else /* !USE_WINDOWS_API && !THREADX && !MICRIUM && !USER_TICKS */
|
|
|
|
#include <time.h>
|
|
|
|
word32 LowResTimer(void)
|
|
{
|
|
return time(0);
|
|
}
|
|
|
|
|
|
#endif /* USE_WINDOWS_API */
|
|
|
|
|
|
/* add output to md5 and sha handshake hashes, exclude record header */
|
|
static void HashOutput(SSL* ssl, const byte* output, int sz, int ivSz)
|
|
{
|
|
const byte* buffer = output + RECORD_HEADER_SZ + ivSz;
|
|
sz -= RECORD_HEADER_SZ;
|
|
|
|
#ifdef CYASSL_DTLS
|
|
if (ssl->options.dtls) {
|
|
buffer += DTLS_RECORD_EXTRA;
|
|
sz -= DTLS_RECORD_EXTRA;
|
|
}
|
|
#endif
|
|
|
|
Md5Update(&ssl->hashMd5, buffer, sz);
|
|
ShaUpdate(&ssl->hashSha, buffer, sz);
|
|
#ifndef NO_SHA256
|
|
if (IsAtLeastTLSv1_2(ssl))
|
|
Sha256Update(&ssl->hashSha256, buffer, sz);
|
|
#endif
|
|
}
|
|
|
|
|
|
/* add input to md5 and sha handshake hashes, include handshake header */
|
|
static void HashInput(SSL* ssl, const byte* input, int sz)
|
|
{
|
|
const byte* buffer = input - HANDSHAKE_HEADER_SZ;
|
|
sz += HANDSHAKE_HEADER_SZ;
|
|
|
|
#ifdef CYASSL_DTLS
|
|
if (ssl->options.dtls) {
|
|
buffer -= DTLS_HANDSHAKE_EXTRA;
|
|
sz += DTLS_HANDSHAKE_EXTRA;
|
|
}
|
|
#endif
|
|
|
|
Md5Update(&ssl->hashMd5, buffer, sz);
|
|
ShaUpdate(&ssl->hashSha, buffer, sz);
|
|
#ifndef NO_SHA256
|
|
if (IsAtLeastTLSv1_2(ssl))
|
|
Sha256Update(&ssl->hashSha256, buffer, sz);
|
|
#endif
|
|
}
|
|
|
|
|
|
/* add record layer header for message */
|
|
static void AddRecordHeader(byte* output, word32 length, byte type, SSL* ssl)
|
|
{
|
|
RecordLayerHeader* rl;
|
|
|
|
/* record layer header */
|
|
rl = (RecordLayerHeader*)output;
|
|
rl->type = type;
|
|
rl->version = ssl->version; /* type and version same in each */
|
|
|
|
if (!ssl->options.dtls)
|
|
c16toa((word16)length, rl->length);
|
|
else {
|
|
#ifdef CYASSL_DTLS
|
|
DtlsRecordLayerHeader* dtls;
|
|
|
|
/* dtls record layer header extensions */
|
|
dtls = (DtlsRecordLayerHeader*)output;
|
|
c16toa(ssl->keys.dtls_epoch, dtls->epoch);
|
|
c32to48(ssl->keys.dtls_sequence_number++, dtls->sequence_number);
|
|
c16toa((word16)length, dtls->length);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
/* add handshake header for message */
|
|
static void AddHandShakeHeader(byte* output, word32 length, byte type, SSL* ssl)
|
|
{
|
|
HandShakeHeader* hs;
|
|
|
|
/* handshake header */
|
|
hs = (HandShakeHeader*)output;
|
|
hs->type = type;
|
|
c32to24(length, hs->length); /* type and length same for each */
|
|
#ifdef CYASSL_DTLS
|
|
if (ssl->options.dtls) {
|
|
DtlsHandShakeHeader* dtls;
|
|
|
|
/* dtls handshake header extensions */
|
|
dtls = (DtlsHandShakeHeader*)output;
|
|
c16toa(ssl->keys.dtls_handshake_number++, dtls->message_seq);
|
|
c32to24(0, dtls->fragment_offset);
|
|
c32to24(length, dtls->fragment_length);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
/* add both headers for handshake message */
|
|
static void AddHeaders(byte* output, word32 length, byte type, SSL* ssl)
|
|
{
|
|
if (!ssl->options.dtls) {
|
|
AddRecordHeader(output, length + HANDSHAKE_HEADER_SZ, handshake, ssl);
|
|
AddHandShakeHeader(output + RECORD_HEADER_SZ, length, type, ssl);
|
|
}
|
|
else {
|
|
AddRecordHeader(output, length+DTLS_HANDSHAKE_HEADER_SZ, handshake,ssl);
|
|
AddHandShakeHeader(output + DTLS_RECORD_HEADER_SZ, length, type, ssl);
|
|
}
|
|
}
|
|
|
|
|
|
static int Receive(SSL* ssl, byte* buf, word32 sz, int flags)
|
|
{
|
|
int recvd;
|
|
|
|
retry:
|
|
recvd = ssl->ctx->CBIORecv((char *)buf, (int)sz, ssl->IOCB_ReadCtx);
|
|
if (recvd < 0)
|
|
switch (recvd) {
|
|
case IO_ERR_GENERAL: /* general/unknown error */
|
|
return -1;
|
|
|
|
case IO_ERR_WANT_READ: /* want read, would block */
|
|
return WANT_READ;
|
|
|
|
case IO_ERR_CONN_RST: /* connection reset */
|
|
ssl->options.connReset = 1;
|
|
return -1;
|
|
|
|
case IO_ERR_ISR: /* interrupt */
|
|
/* see if we got our timeout */
|
|
#ifdef CYASSL_CALLBACKS
|
|
if (ssl->toInfoOn) {
|
|
struct itimerval timeout;
|
|
getitimer(ITIMER_REAL, &timeout);
|
|
if (timeout.it_value.tv_sec == 0 &&
|
|
timeout.it_value.tv_usec == 0) {
|
|
XSTRNCPY(ssl->timeoutInfo.timeoutName,
|
|
"recv() timeout", MAX_TIMEOUT_NAME_SZ);
|
|
return 0;
|
|
}
|
|
}
|
|
#endif
|
|
goto retry;
|
|
|
|
case IO_ERR_CONN_CLOSE: /* peer closed connection */
|
|
ssl->options.isClosed = 1;
|
|
return -1;
|
|
}
|
|
|
|
return recvd;
|
|
}
|
|
|
|
|
|
/* Switch dynamic output buffer back to static, buffer is assumed clear */
|
|
void ShrinkOutputBuffer(SSL* ssl)
|
|
{
|
|
CYASSL_MSG("Shrinking output buffer\n");
|
|
XFREE(ssl->buffers.outputBuffer.buffer, ssl->heap, DYNAMIC_TYPE_OUT_BUFFER);
|
|
ssl->buffers.outputBuffer.buffer = ssl->buffers.outputBuffer.staticBuffer;
|
|
ssl->buffers.outputBuffer.bufferSize = STATIC_BUFFER_LEN;
|
|
ssl->buffers.outputBuffer.dynamicFlag = 0;
|
|
}
|
|
|
|
|
|
/* Switch dynamic input buffer back to static, keep any remaining input */
|
|
/* forced free means cleaning up */
|
|
void ShrinkInputBuffer(SSL* ssl, int forcedFree)
|
|
{
|
|
int usedLength = ssl->buffers.inputBuffer.length -
|
|
ssl->buffers.inputBuffer.idx;
|
|
if (!forcedFree && usedLength > STATIC_BUFFER_LEN)
|
|
return;
|
|
|
|
CYASSL_MSG("Shrinking input buffer\n");
|
|
|
|
if (!forcedFree && usedLength)
|
|
XMEMCPY(ssl->buffers.inputBuffer.staticBuffer,
|
|
ssl->buffers.inputBuffer.buffer + ssl->buffers.inputBuffer.idx,
|
|
usedLength);
|
|
|
|
XFREE(ssl->buffers.inputBuffer.buffer, ssl->heap, DYNAMIC_TYPE_IN_BUFFER);
|
|
ssl->buffers.inputBuffer.buffer = ssl->buffers.inputBuffer.staticBuffer;
|
|
ssl->buffers.inputBuffer.bufferSize = STATIC_BUFFER_LEN;
|
|
ssl->buffers.inputBuffer.dynamicFlag = 0;
|
|
ssl->buffers.inputBuffer.idx = 0;
|
|
ssl->buffers.inputBuffer.length = usedLength;
|
|
}
|
|
|
|
|
|
int SendBuffered(SSL* ssl)
|
|
{
|
|
while (ssl->buffers.outputBuffer.length > 0) {
|
|
int sent = ssl->ctx->CBIOSend((char*)ssl->buffers.outputBuffer.buffer +
|
|
ssl->buffers.outputBuffer.idx,
|
|
(int)ssl->buffers.outputBuffer.length,
|
|
ssl->IOCB_WriteCtx);
|
|
if (sent < 0) {
|
|
switch (sent) {
|
|
|
|
case IO_ERR_WANT_WRITE: /* would block */
|
|
return WANT_WRITE;
|
|
|
|
case IO_ERR_CONN_RST: /* connection reset */
|
|
ssl->options.connReset = 1;
|
|
break;
|
|
|
|
case IO_ERR_ISR: /* interrupt */
|
|
/* see if we got our timeout */
|
|
#ifdef CYASSL_CALLBACKS
|
|
if (ssl->toInfoOn) {
|
|
struct itimerval timeout;
|
|
getitimer(ITIMER_REAL, &timeout);
|
|
if (timeout.it_value.tv_sec == 0 &&
|
|
timeout.it_value.tv_usec == 0) {
|
|
XSTRNCPY(ssl->timeoutInfo.timeoutName,
|
|
"send() timeout", MAX_TIMEOUT_NAME_SZ);
|
|
return WANT_WRITE;
|
|
}
|
|
}
|
|
#endif
|
|
continue;
|
|
|
|
case IO_ERR_CONN_CLOSE: /* epipe / conn closed, same as reset */
|
|
ssl->options.connReset = 1;
|
|
break;
|
|
}
|
|
|
|
return SOCKET_ERROR_E;
|
|
}
|
|
|
|
ssl->buffers.outputBuffer.idx += sent;
|
|
ssl->buffers.outputBuffer.length -= sent;
|
|
}
|
|
|
|
ssl->buffers.outputBuffer.idx = 0;
|
|
|
|
if (ssl->buffers.outputBuffer.dynamicFlag)
|
|
ShrinkOutputBuffer(ssl);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Grow the output buffer, should only be to send cert, should be blank */
|
|
static INLINE int GrowOutputBuffer(SSL* ssl, int size)
|
|
{
|
|
byte* tmp = (byte*) XMALLOC(size + ssl->buffers.outputBuffer.length,
|
|
ssl->heap, DYNAMIC_TYPE_OUT_BUFFER);
|
|
CYASSL_MSG("growing output buffer\n");
|
|
|
|
if (!tmp) return -1;
|
|
|
|
if (ssl->buffers.outputBuffer.length)
|
|
XMEMCPY(tmp, ssl->buffers.outputBuffer.buffer,
|
|
ssl->buffers.outputBuffer.length);
|
|
|
|
if (ssl->buffers.outputBuffer.dynamicFlag)
|
|
XFREE(ssl->buffers.outputBuffer.buffer, ssl->heap,
|
|
DYNAMIC_TYPE_OUT_BUFFER);
|
|
ssl->buffers.outputBuffer.dynamicFlag = 1;
|
|
ssl->buffers.outputBuffer.buffer = tmp;
|
|
ssl->buffers.outputBuffer.bufferSize = size +
|
|
ssl->buffers.outputBuffer.length;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Grow the input buffer, should only be to read cert or big app data */
|
|
static INLINE int GrowInputBuffer(SSL* ssl, int size, int usedLength)
|
|
{
|
|
byte* tmp = (byte*) XMALLOC(size + usedLength, ssl->heap,
|
|
DYNAMIC_TYPE_IN_BUFFER);
|
|
CYASSL_MSG("growing input buffer\n");
|
|
|
|
if (!tmp) return -1;
|
|
|
|
if (usedLength)
|
|
XMEMCPY(tmp, ssl->buffers.inputBuffer.buffer +
|
|
ssl->buffers.inputBuffer.idx, usedLength);
|
|
|
|
if (ssl->buffers.inputBuffer.dynamicFlag)
|
|
XFREE(ssl->buffers.inputBuffer.buffer,ssl->heap,DYNAMIC_TYPE_IN_BUFFER);
|
|
|
|
ssl->buffers.inputBuffer.dynamicFlag = 1;
|
|
ssl->buffers.inputBuffer.buffer = tmp;
|
|
ssl->buffers.inputBuffer.bufferSize = size + usedLength;
|
|
ssl->buffers.inputBuffer.idx = 0;
|
|
ssl->buffers.inputBuffer.length = usedLength;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* check avalaible size into outbut buffer */
|
|
static INLINE int CheckAvalaibleSize(SSL *ssl, int size)
|
|
{
|
|
if ((word32)size > ssl->buffers.outputBuffer.bufferSize)
|
|
if (GrowOutputBuffer(ssl, size) < 0)
|
|
return MEMORY_E;
|
|
|
|
if (ssl->buffers.outputBuffer.bufferSize - ssl->buffers.outputBuffer.length
|
|
< (word32)size) {
|
|
if (SendBuffered(ssl) == SOCKET_ERROR_E)
|
|
return SOCKET_ERROR_E;
|
|
if (ssl->buffers.outputBuffer.bufferSize -
|
|
ssl->buffers.outputBuffer.length < (word32)size)
|
|
return WANT_WRITE;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* do all verify and sanity checks on record header */
|
|
static int GetRecordHeader(SSL* ssl, const byte* input, word32* inOutIdx,
|
|
RecordLayerHeader* rh, word16 *size)
|
|
{
|
|
if (!ssl->options.dtls) {
|
|
XMEMCPY(rh, input + *inOutIdx, RECORD_HEADER_SZ);
|
|
*inOutIdx += RECORD_HEADER_SZ;
|
|
ato16(rh->length, size);
|
|
}
|
|
else {
|
|
#ifdef CYASSL_DTLS
|
|
/* type and version in same sport */
|
|
XMEMCPY(rh, input + *inOutIdx, ENUM_LEN + VERSION_SZ);
|
|
*inOutIdx += ENUM_LEN + VERSION_SZ;
|
|
*inOutIdx += 4; /* skip epoch and first 2 seq bytes for now */
|
|
ato32(input + *inOutIdx, &ssl->keys.dtls_peer_sequence_number);
|
|
*inOutIdx += 4; /* advance past rest of seq */
|
|
ato16(input + *inOutIdx, size);
|
|
*inOutIdx += LENGTH_SZ;
|
|
#endif
|
|
}
|
|
|
|
/* catch version mismatch */
|
|
if (rh->version.major != ssl->version.major ||
|
|
rh->version.minor != ssl->version.minor) {
|
|
|
|
if (ssl->options.side == SERVER_END && ssl->options.downgrade == 1 &&
|
|
ssl->options.acceptState == ACCEPT_BEGIN)
|
|
; /* haven't negotiated yet */
|
|
else
|
|
return VERSION_ERROR; /* only use requested version */
|
|
}
|
|
|
|
/* record layer length check */
|
|
if (*size > (MAX_RECORD_SIZE + MAX_COMP_EXTRA + MAX_MSG_EXTRA))
|
|
return LENGTH_ERROR;
|
|
|
|
/* verify record type here as well */
|
|
switch ((enum ContentType)rh->type) {
|
|
case handshake:
|
|
case change_cipher_spec:
|
|
case application_data:
|
|
case alert:
|
|
break;
|
|
default:
|
|
return UNKNOWN_RECORD_TYPE;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int GetHandShakeHeader(SSL* ssl, const byte* input, word32* inOutIdx,
|
|
byte *type, word32 *size)
|
|
{
|
|
const byte *ptr = input + *inOutIdx;
|
|
*inOutIdx += HANDSHAKE_HEADER_SZ;
|
|
|
|
#ifdef CYASSL_DTLS
|
|
if (ssl->options.dtls)
|
|
*inOutIdx += DTLS_HANDSHAKE_EXTRA;
|
|
#endif
|
|
|
|
*type = ptr[0];
|
|
c24to32(&ptr[1], size);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* fill with MD5 pad size since biggest required */
|
|
static const byte PAD1[PAD_MD5] =
|
|
{ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
|
|
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
|
|
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
|
|
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
|
|
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
|
|
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36
|
|
};
|
|
static const byte PAD2[PAD_MD5] =
|
|
{ 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
|
|
0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
|
|
0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
|
|
0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
|
|
0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
|
|
0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c
|
|
};
|
|
|
|
/* calculate MD5 hash for finished */
|
|
static void BuildMD5(SSL* ssl, Hashes* hashes, const byte* sender)
|
|
{
|
|
byte md5_result[MD5_DIGEST_SIZE];
|
|
|
|
/* make md5 inner */
|
|
Md5Update(&ssl->hashMd5, sender, SIZEOF_SENDER);
|
|
Md5Update(&ssl->hashMd5, ssl->arrays.masterSecret, SECRET_LEN);
|
|
Md5Update(&ssl->hashMd5, PAD1, PAD_MD5);
|
|
Md5Final(&ssl->hashMd5, md5_result);
|
|
|
|
/* make md5 outer */
|
|
Md5Update(&ssl->hashMd5, ssl->arrays.masterSecret, SECRET_LEN);
|
|
Md5Update(&ssl->hashMd5, PAD2, PAD_MD5);
|
|
Md5Update(&ssl->hashMd5, md5_result, MD5_DIGEST_SIZE);
|
|
|
|
Md5Final(&ssl->hashMd5, hashes->md5);
|
|
}
|
|
|
|
|
|
/* calculate SHA hash for finished */
|
|
static void BuildSHA(SSL* ssl, Hashes* hashes, const byte* sender)
|
|
{
|
|
byte sha_result[SHA_DIGEST_SIZE];
|
|
|
|
/* make sha inner */
|
|
ShaUpdate(&ssl->hashSha, sender, SIZEOF_SENDER);
|
|
ShaUpdate(&ssl->hashSha, ssl->arrays.masterSecret, SECRET_LEN);
|
|
ShaUpdate(&ssl->hashSha, PAD1, PAD_SHA);
|
|
ShaFinal(&ssl->hashSha, sha_result);
|
|
|
|
/* make sha outer */
|
|
ShaUpdate(&ssl->hashSha, ssl->arrays.masterSecret, SECRET_LEN);
|
|
ShaUpdate(&ssl->hashSha, PAD2, PAD_SHA);
|
|
ShaUpdate(&ssl->hashSha, sha_result, SHA_DIGEST_SIZE);
|
|
|
|
ShaFinal(&ssl->hashSha, hashes->sha);
|
|
}
|
|
|
|
|
|
static void BuildFinished(SSL* ssl, Hashes* hashes, const byte* sender)
|
|
{
|
|
/* store current states, building requires get_digest which resets state */
|
|
Md5 md5 = ssl->hashMd5;
|
|
Sha sha = ssl->hashSha;
|
|
#ifndef NO_SHA256
|
|
Sha256 sha256;
|
|
if (IsAtLeastTLSv1_2(ssl))
|
|
sha256 = ssl->hashSha256;
|
|
#endif
|
|
|
|
if (ssl->options.tls)
|
|
BuildTlsFinished(ssl, hashes, sender);
|
|
else {
|
|
BuildMD5(ssl, hashes, sender);
|
|
BuildSHA(ssl, hashes, sender);
|
|
}
|
|
|
|
/* restore */
|
|
ssl->hashMd5 = md5;
|
|
ssl->hashSha = sha;
|
|
#ifndef NO_SHA256
|
|
if (IsAtLeastTLSv1_2(ssl))
|
|
ssl->hashSha256 = sha256;
|
|
#endif
|
|
}
|
|
|
|
|
|
static int DoCertificate(SSL* ssl, byte* input, word32* inOutIdx)
|
|
{
|
|
word32 listSz, i = *inOutIdx;
|
|
int ret = 0;
|
|
int anyError = 0;
|
|
int firstTime = 1; /* peer's is at front */
|
|
char domain[ASN_NAME_MAX];
|
|
|
|
#ifdef CYASSL_CALLBACKS
|
|
if (ssl->hsInfoOn) AddPacketName("Certificate", &ssl->handShakeInfo);
|
|
if (ssl->toInfoOn) AddLateName("Certificate", &ssl->timeoutInfo);
|
|
#endif
|
|
c24to32(&input[i], &listSz);
|
|
i += CERT_HEADER_SZ;
|
|
|
|
while (listSz) {
|
|
/* cert size */
|
|
buffer myCert;
|
|
word32 certSz;
|
|
DecodedCert dCert;
|
|
word32 idx = 0;
|
|
|
|
c24to32(&input[i], &certSz);
|
|
i += CERT_HEADER_SZ;
|
|
|
|
myCert.length = certSz;
|
|
myCert.buffer = input + i;
|
|
i += certSz;
|
|
|
|
listSz -= certSz + CERT_HEADER_SZ;
|
|
|
|
if (ret != 0 && anyError == 0)
|
|
anyError = ret; /* save error from last time */
|
|
|
|
#ifdef SESSION_CERTS
|
|
if (ssl->session.chain.count < MAX_CHAIN_DEPTH &&
|
|
myCert.length < MAX_X509_SIZE) {
|
|
ssl->session.chain.certs[ssl->session.chain.count].length =
|
|
myCert.length;
|
|
XMEMCPY(ssl->session.chain.certs[ssl->session.chain.count].buffer,
|
|
myCert.buffer, myCert.length);
|
|
ssl->session.chain.count++;
|
|
} else {
|
|
CYASSL_MSG("Couldn't store chain cert for session");
|
|
}
|
|
#endif
|
|
|
|
InitDecodedCert(&dCert, myCert.buffer, ssl->heap);
|
|
ret = ParseCertRelative(&dCert, myCert.length, CERT_TYPE,
|
|
!ssl->options.verifyNone, ssl->caList);
|
|
|
|
if (!firstTime) {
|
|
FreeDecodedCert(&dCert);
|
|
continue;
|
|
}
|
|
|
|
/* get rest of peer info in case user wants to continue */
|
|
if (ret != 0) {
|
|
if (!(ret == ASN_BEFORE_DATE_E || ret == ASN_AFTER_DATE_E ||
|
|
ret == ASN_SIG_CONFIRM_E)) {
|
|
FreeDecodedCert(&dCert);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
/* first one has peer's key */
|
|
firstTime = 0;
|
|
|
|
ssl->options.havePeerCert = 1;
|
|
/* set X509 format */
|
|
#ifdef OPENSSL_EXTRA
|
|
ssl->peerCert.issuer.sz = (int)XSTRLEN(dCert.issuer) + 1;
|
|
XSTRNCPY(ssl->peerCert.issuer.name, dCert.issuer, ASN_NAME_MAX);
|
|
ssl->peerCert.subject.sz = (int)XSTRLEN(dCert.subject) + 1;
|
|
XSTRNCPY(ssl->peerCert.subject.name, dCert.subject, ASN_NAME_MAX);
|
|
XMEMCPY(ssl->peerCert.serial, dCert.serial, SERIAL_SIZE);
|
|
#endif
|
|
|
|
XMEMCPY(domain, dCert.subjectCN, dCert.subjectCNLen);
|
|
domain[dCert.subjectCNLen] = '\0';
|
|
|
|
if (!ssl->options.verifyNone && ssl->buffers.domainName.buffer)
|
|
if (XSTRNCMP((char*)ssl->buffers.domainName.buffer,
|
|
dCert.subjectCN,
|
|
ssl->buffers.domainName.length - 1)) {
|
|
ret = DOMAIN_NAME_MISMATCH; /* try to get peer key still */
|
|
}
|
|
|
|
/* decode peer key */
|
|
if (dCert.keyOID == RSAk) {
|
|
if (RsaPublicKeyDecode(dCert.publicKey, &idx,
|
|
&ssl->peerRsaKey, dCert.pubKeySize) != 0) {
|
|
ret = PEER_KEY_ERROR;
|
|
FreeDecodedCert(&dCert);
|
|
continue;
|
|
}
|
|
ssl->peerRsaKeyPresent = 1;
|
|
}
|
|
#ifdef HAVE_NTRU
|
|
else if (dCert.keyOID == NTRUk) {
|
|
if (dCert.pubKeySize > sizeof(ssl->peerNtruKey)) {
|
|
ret = PEER_KEY_ERROR;
|
|
FreeDecodedCert(&dCert);
|
|
continue;
|
|
}
|
|
XMEMCPY(ssl->peerNtruKey, dCert.publicKey, dCert.pubKeySize);
|
|
ssl->peerNtruKeyLen = (word16)dCert.pubKeySize;
|
|
ssl->peerNtruKeyPresent = 1;
|
|
}
|
|
#endif /* HAVE_NTRU */
|
|
#ifdef HAVE_ECC
|
|
else if (dCert.keyOID == ECDSAk) {
|
|
if (ecc_import_x963(dCert.publicKey, dCert.pubKeySize,
|
|
&ssl->peerEccDsaKey) != 0) {
|
|
ret = PEER_KEY_ERROR;
|
|
FreeDecodedCert(&dCert);
|
|
continue;
|
|
}
|
|
ssl->peerEccDsaKeyPresent = 1;
|
|
}
|
|
#endif /* HAVE_ECC */
|
|
|
|
FreeDecodedCert(&dCert);
|
|
}
|
|
|
|
if (anyError != 0)
|
|
ret = anyError;
|
|
|
|
if (ret == 0 && ssl->options.side == CLIENT_END)
|
|
ssl->options.serverState = SERVER_CERT_COMPLETE;
|
|
|
|
if (ret != 0) {
|
|
if (!ssl->options.verifyNone) {
|
|
int why = bad_certificate;
|
|
if (ret == ASN_AFTER_DATE_E || ret == ASN_BEFORE_DATE_E)
|
|
why = certificate_expired;
|
|
if (ssl->ctx->verifyCallback) {
|
|
int ok;
|
|
X509_STORE_CTX store;
|
|
|
|
store.error = ret;
|
|
store.error_depth = 1;
|
|
store.domain = domain;
|
|
#ifdef OPENSSL_EXTRA
|
|
store.current_cert = &ssl->peerCert;
|
|
#else
|
|
store.current_cert = NULL;
|
|
#endif
|
|
ok = ssl->ctx->verifyCallback(0, &store);
|
|
if (ok)
|
|
ret = 0;
|
|
}
|
|
if (ret != 0) {
|
|
SendAlert(ssl, alert_fatal, why); /* try to send */
|
|
ssl->options.isClosed = 1;
|
|
}
|
|
}
|
|
ssl->error = ret;
|
|
}
|
|
|
|
*inOutIdx = i;
|
|
return ret;
|
|
}
|
|
|
|
|
|
int DoFinished(SSL* ssl, const byte* input, word32* inOutIdx, int sniff)
|
|
{
|
|
byte verifyMAC[SHA256_DIGEST_SIZE];
|
|
int finishedSz = ssl->options.tls ? TLS_FINISHED_SZ : FINISHED_SZ;
|
|
int headerSz = HANDSHAKE_HEADER_SZ;
|
|
word32 macSz = finishedSz + HANDSHAKE_HEADER_SZ,
|
|
idx = *inOutIdx,
|
|
padSz = ssl->keys.encryptSz - HANDSHAKE_HEADER_SZ - finishedSz -
|
|
ssl->specs.hash_size;
|
|
const byte* mac;
|
|
|
|
#ifdef CYASSL_DTLS
|
|
if (ssl->options.dtls) {
|
|
headerSz += DTLS_HANDSHAKE_EXTRA;
|
|
macSz += DTLS_HANDSHAKE_EXTRA;
|
|
padSz -= DTLS_HANDSHAKE_EXTRA;
|
|
}
|
|
#endif
|
|
|
|
#ifdef CYASSL_CALLBACKS
|
|
if (ssl->hsInfoOn) AddPacketName("Finished", &ssl->handShakeInfo);
|
|
if (ssl->toInfoOn) AddLateName("Finished", &ssl->timeoutInfo);
|
|
#endif
|
|
if (sniff == NO_SNIFF) {
|
|
if (XMEMCMP(input + idx, &ssl->verifyHashes, finishedSz))
|
|
return VERIFY_FINISHED_ERROR;
|
|
}
|
|
|
|
ssl->hmac(ssl, verifyMAC, input + idx - headerSz, macSz,
|
|
handshake, 1);
|
|
idx += finishedSz;
|
|
|
|
/* read mac and fill */
|
|
mac = input + idx;
|
|
idx += ssl->specs.hash_size;
|
|
|
|
if (ssl->options.tls1_1 && ssl->specs.cipher_type == block)
|
|
padSz -= ssl->specs.block_size;
|
|
|
|
idx += padSz;
|
|
|
|
/* verify mac */
|
|
if (XMEMCMP(mac, verifyMAC, ssl->specs.hash_size))
|
|
return VERIFY_MAC_ERROR;
|
|
|
|
if (ssl->options.side == CLIENT_END) {
|
|
ssl->options.serverState = SERVER_FINISHED_COMPLETE;
|
|
if (!ssl->options.resuming)
|
|
ssl->options.handShakeState = HANDSHAKE_DONE;
|
|
}
|
|
else {
|
|
ssl->options.clientState = CLIENT_FINISHED_COMPLETE;
|
|
if (ssl->options.resuming)
|
|
ssl->options.handShakeState = HANDSHAKE_DONE;
|
|
}
|
|
|
|
*inOutIdx = idx;
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int DoHandShakeMsg(SSL* ssl, byte* input, word32* inOutIdx,
|
|
word32 totalSz)
|
|
{
|
|
byte type;
|
|
word32 size;
|
|
int ret = 0;
|
|
|
|
CYASSL_ENTER("DoHandShakeMsg()");
|
|
|
|
if (GetHandShakeHeader(ssl, input, inOutIdx, &type, &size) != 0)
|
|
return PARSE_ERROR;
|
|
|
|
if (*inOutIdx + size > totalSz)
|
|
return INCOMPLETE_DATA;
|
|
|
|
HashInput(ssl, input + *inOutIdx, size);
|
|
#ifdef CYASSL_CALLBACKS
|
|
/* add name later, add on record and handshake header part back on */
|
|
if (ssl->toInfoOn) {
|
|
int add = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ;
|
|
AddPacketInfo(0, &ssl->timeoutInfo, input + *inOutIdx - add,
|
|
size + add, ssl->heap);
|
|
AddLateRecordHeader(&ssl->curRL, &ssl->timeoutInfo);
|
|
}
|
|
#endif
|
|
|
|
switch (type) {
|
|
|
|
#ifndef NO_CYASSL_CLIENT
|
|
case hello_verify_request:
|
|
CYASSL_MSG("processing hello verify request");
|
|
ret = DoHelloVerifyRequest(ssl, input,inOutIdx);
|
|
break;
|
|
|
|
case server_hello:
|
|
CYASSL_MSG("processing server hello");
|
|
ret = DoServerHello(ssl, input, inOutIdx);
|
|
break;
|
|
|
|
case certificate_request:
|
|
CYASSL_MSG("processing certificate request");
|
|
ret = DoCertificateRequest(ssl, input, inOutIdx);
|
|
break;
|
|
|
|
case server_key_exchange:
|
|
CYASSL_MSG("processing server key exchange");
|
|
ret = DoServerKeyExchange(ssl, input, inOutIdx);
|
|
break;
|
|
#endif
|
|
|
|
case certificate:
|
|
CYASSL_MSG("processing certificate");
|
|
ret = DoCertificate(ssl, input, inOutIdx);
|
|
break;
|
|
|
|
case server_hello_done:
|
|
CYASSL_MSG("processing server hello done");
|
|
#ifdef CYASSL_CALLBACKS
|
|
if (ssl->hsInfoOn)
|
|
AddPacketName("ServerHelloDone", &ssl->handShakeInfo);
|
|
if (ssl->toInfoOn)
|
|
AddLateName("ServerHelloDone", &ssl->timeoutInfo);
|
|
#endif
|
|
ssl->options.serverState = SERVER_HELLODONE_COMPLETE;
|
|
break;
|
|
|
|
case finished:
|
|
CYASSL_MSG("processing finished");
|
|
ret = DoFinished(ssl, input, inOutIdx, NO_SNIFF);
|
|
break;
|
|
|
|
#ifndef NO_CYASSL_SERVER
|
|
case client_hello:
|
|
CYASSL_MSG("processing client hello");
|
|
ret = DoClientHello(ssl, input, inOutIdx, totalSz, size);
|
|
break;
|
|
|
|
case client_key_exchange:
|
|
CYASSL_MSG("processing client key exchange");
|
|
ret = DoClientKeyExchange(ssl, input, inOutIdx);
|
|
break;
|
|
|
|
case certificate_verify:
|
|
CYASSL_MSG("processing certificate verify");
|
|
ret = DoCertificateVerify(ssl, input, inOutIdx, totalSz);
|
|
break;
|
|
|
|
#endif
|
|
|
|
default:
|
|
ret = UNKNOWN_HANDSHAKE_TYPE;
|
|
}
|
|
|
|
CYASSL_LEAVE("DoHandShakeMsg()", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static INLINE void Encrypt(SSL* ssl, byte* out, const byte* input, word32 sz)
|
|
{
|
|
switch (ssl->specs.bulk_cipher_algorithm) {
|
|
#ifdef BUILD_ARC4
|
|
case rc4:
|
|
Arc4Process(&ssl->encrypt.arc4, out, input, sz);
|
|
break;
|
|
#endif
|
|
|
|
#ifdef BUILD_DES3
|
|
case triple_des:
|
|
Des3_CbcEncrypt(&ssl->encrypt.des3, out, input, sz);
|
|
break;
|
|
#endif
|
|
|
|
#ifdef BUILD_AES
|
|
case aes:
|
|
#ifdef CYASSL_AESNI
|
|
if ((word)input % 16) {
|
|
byte buffer[MAX_RECORD_SIZE + MAX_COMP_EXTRA+MAX_MSG_EXTRA];
|
|
XMEMCPY(buffer, input, sz);
|
|
AesCbcEncrypt(&ssl->encrypt.aes, buffer, buffer, sz);
|
|
XMEMCPY(out, buffer, sz);
|
|
break;
|
|
}
|
|
#endif
|
|
AesCbcEncrypt(&ssl->encrypt.aes, out, input, sz);
|
|
break;
|
|
#endif
|
|
|
|
#ifdef BUILD_HC128
|
|
case hc128:
|
|
Hc128_Process(&ssl->encrypt.hc128, out, input, sz);
|
|
break;
|
|
#endif
|
|
|
|
#ifdef BUILD_RABBIT
|
|
case rabbit:
|
|
RabbitProcess(&ssl->encrypt.rabbit, out, input, sz);
|
|
break;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
static INLINE void Decrypt(SSL* ssl, byte* plain, const byte* input, word32 sz)
|
|
{
|
|
switch (ssl->specs.bulk_cipher_algorithm) {
|
|
#ifdef BUILD_ARC4
|
|
case rc4:
|
|
Arc4Process(&ssl->decrypt.arc4, plain, input, sz);
|
|
break;
|
|
#endif
|
|
|
|
#ifdef BUILD_DES3
|
|
case triple_des:
|
|
Des3_CbcDecrypt(&ssl->decrypt.des3, plain, input, sz);
|
|
break;
|
|
#endif
|
|
|
|
#ifdef BUILD_AES
|
|
case aes:
|
|
AesCbcDecrypt(&ssl->decrypt.aes, plain, input, sz);
|
|
break;
|
|
#endif
|
|
|
|
#ifdef BUILD_HC128
|
|
case hc128:
|
|
Hc128_Process(&ssl->decrypt.hc128, plain, input, sz);
|
|
break;
|
|
#endif
|
|
|
|
#ifdef BUILD_RABBIT
|
|
case rabbit:
|
|
RabbitProcess(&ssl->decrypt.rabbit, plain, input, sz);
|
|
break;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
/* decrypt input message in place */
|
|
static int DecryptMessage(SSL* ssl, byte* input, word32 sz, word32* idx)
|
|
{
|
|
Decrypt(ssl, input, input, sz);
|
|
ssl->keys.encryptSz = sz;
|
|
if (ssl->options.tls1_1 && ssl->specs.cipher_type == block)
|
|
*idx += ssl->specs.block_size; /* go past TLSv1.1 IV */
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static INLINE word32 GetSEQIncrement(SSL* ssl, int verify)
|
|
{
|
|
if (verify)
|
|
return ssl->keys.peer_sequence_number++;
|
|
else
|
|
return ssl->keys.sequence_number++;
|
|
}
|
|
|
|
|
|
int DoApplicationData(SSL* ssl, byte* input, word32* inOutIdx)
|
|
{
|
|
word32 msgSz = ssl->keys.encryptSz;
|
|
word32 pad = 0,
|
|
padByte = 0,
|
|
idx = *inOutIdx,
|
|
digestSz = ssl->specs.hash_size;
|
|
int dataSz;
|
|
int ivExtra = 0;
|
|
byte* rawData = input + idx; /* keep current for hmac */
|
|
#ifdef HAVE_LIBZ
|
|
byte decomp[MAX_RECORD_SIZE + MAX_COMP_EXTRA];
|
|
#endif
|
|
|
|
byte verify[SHA256_DIGEST_SIZE];
|
|
const byte* mac;
|
|
|
|
if (ssl->specs.cipher_type == block) {
|
|
if (ssl->options.tls1_1)
|
|
ivExtra = ssl->specs.block_size;
|
|
pad = *(input + idx + msgSz - ivExtra - 1);
|
|
padByte = 1;
|
|
}
|
|
|
|
dataSz = msgSz - ivExtra - digestSz - pad - padByte;
|
|
if (dataSz < 0)
|
|
return BUFFER_ERROR;
|
|
|
|
/* read data */
|
|
if (dataSz) {
|
|
int rawSz = dataSz; /* keep raw size for hmac */
|
|
|
|
ssl->hmac(ssl, verify, rawData, rawSz, application_data, 1);
|
|
|
|
#ifdef HAVE_LIBZ
|
|
if (ssl->options.usingCompression) {
|
|
dataSz = DeCompress(ssl, rawData, dataSz, decomp, sizeof(decomp));
|
|
if (dataSz < 0) return dataSz;
|
|
}
|
|
#endif
|
|
|
|
if (ssl->options.usingCompression)
|
|
idx += rawSz;
|
|
else
|
|
idx += dataSz;
|
|
|
|
ssl->buffers.clearOutputBuffer.buffer = rawData;
|
|
ssl->buffers.clearOutputBuffer.length = dataSz;
|
|
}
|
|
|
|
/* read mac and fill */
|
|
mac = input + idx;
|
|
idx += digestSz;
|
|
|
|
idx += pad;
|
|
if (padByte)
|
|
idx++;
|
|
|
|
#ifdef HAVE_LIBZ
|
|
if (ssl->options.usingCompression)
|
|
XMEMMOVE(rawData, decomp, dataSz);
|
|
#endif
|
|
|
|
/* verify */
|
|
if (dataSz) {
|
|
if (XMEMCMP(mac, verify, digestSz))
|
|
return VERIFY_MAC_ERROR;
|
|
}
|
|
else
|
|
GetSEQIncrement(ssl, 1); /* even though no data, increment verify */
|
|
|
|
*inOutIdx = idx;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* process alert, return level */
|
|
static int DoAlert(SSL* ssl, byte* input, word32* inOutIdx, int* type)
|
|
{
|
|
byte level;
|
|
|
|
#ifdef CYASSL_CALLBACKS
|
|
if (ssl->hsInfoOn)
|
|
AddPacketName("Alert", &ssl->handShakeInfo);
|
|
if (ssl->toInfoOn)
|
|
/* add record header back on to info + 2 byte level, data */
|
|
AddPacketInfo("Alert", &ssl->timeoutInfo, input + *inOutIdx -
|
|
RECORD_HEADER_SZ, 2 + RECORD_HEADER_SZ, ssl->heap);
|
|
#endif
|
|
level = input[(*inOutIdx)++];
|
|
*type = (int)input[(*inOutIdx)++];
|
|
|
|
if (*type == close_notify)
|
|
ssl->options.closeNotify = 1;
|
|
|
|
if (ssl->keys.encryptionOn) {
|
|
int aSz = ALERT_SIZE;
|
|
const byte* mac;
|
|
byte verify[SHA256_DIGEST_SIZE];
|
|
int padSz = ssl->keys.encryptSz - aSz - ssl->specs.hash_size;
|
|
|
|
ssl->hmac(ssl, verify, input + *inOutIdx - aSz, aSz, alert, 1);
|
|
|
|
/* read mac and fill */
|
|
mac = input + *inOutIdx;
|
|
*inOutIdx += (ssl->specs.hash_size + padSz);
|
|
|
|
/* verify */
|
|
if (XMEMCMP(mac, verify, ssl->specs.hash_size))
|
|
return VERIFY_MAC_ERROR;
|
|
}
|
|
|
|
return level;
|
|
}
|
|
|
|
static int GetInputData(SSL *ssl, size_t size)
|
|
{
|
|
int in;
|
|
int inSz;
|
|
int maxLength;
|
|
int usedLength;
|
|
|
|
|
|
/* check max input length */
|
|
usedLength = ssl->buffers.inputBuffer.length - ssl->buffers.inputBuffer.idx;
|
|
maxLength = ssl->buffers.inputBuffer.bufferSize - usedLength;
|
|
inSz = (int)(size - usedLength); /* from last partial read */
|
|
|
|
#ifdef CYASSL_DTLS
|
|
if (ssl->options.dtls)
|
|
inSz = MAX_MTU; /* read ahead up to MTU */
|
|
#endif
|
|
|
|
if (inSz > maxLength) {
|
|
if (GrowInputBuffer(ssl, size, usedLength) < 0)
|
|
return MEMORY_E;
|
|
}
|
|
|
|
if (inSz <= 0)
|
|
return BUFFER_ERROR;
|
|
|
|
/* Put buffer data at start if not there */
|
|
if (usedLength > 0 && ssl->buffers.inputBuffer.idx != 0)
|
|
XMEMMOVE(ssl->buffers.inputBuffer.buffer,
|
|
ssl->buffers.inputBuffer.buffer + ssl->buffers.inputBuffer.idx,
|
|
usedLength);
|
|
|
|
/* remove processed data */
|
|
ssl->buffers.inputBuffer.idx = 0;
|
|
ssl->buffers.inputBuffer.length = usedLength;
|
|
|
|
/* read data from network */
|
|
do {
|
|
in = Receive(ssl,
|
|
ssl->buffers.inputBuffer.buffer +
|
|
ssl->buffers.inputBuffer.length,
|
|
inSz, 0);
|
|
if (in == -1)
|
|
return SOCKET_ERROR_E;
|
|
|
|
if (in == WANT_READ)
|
|
return WANT_READ;
|
|
|
|
ssl->buffers.inputBuffer.length += in;
|
|
inSz -= in;
|
|
|
|
} while (ssl->buffers.inputBuffer.length < size);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* process input requests, return 0 is done, 1 is call again to complete, and
|
|
negative number is error */
|
|
int ProcessReply(SSL* ssl)
|
|
{
|
|
int ret, type, readSz;
|
|
word32 startIdx = 0;
|
|
#ifndef NO_CYASSL_SERVER
|
|
byte b0, b1;
|
|
#endif
|
|
#ifdef CYASSL_DTLS
|
|
int used;
|
|
#endif
|
|
|
|
for (;;) {
|
|
switch ((processReply)ssl->options.processReply) {
|
|
|
|
/* in the CYASSL_SERVER case, get the first byte for detecting
|
|
* old client hello */
|
|
case doProcessInit:
|
|
|
|
readSz = RECORD_HEADER_SZ;
|
|
|
|
#ifdef CYASSL_DTLS
|
|
if (ssl->options.dtls)
|
|
readSz = DTLS_RECORD_HEADER_SZ;
|
|
#endif
|
|
|
|
/* get header or return error */
|
|
if (!ssl->options.dtls) {
|
|
if ((ret = GetInputData(ssl, readSz)) < 0)
|
|
return ret;
|
|
} else {
|
|
#ifdef CYASSL_DTLS
|
|
/* read ahead may already have header */
|
|
used = ssl->buffers.inputBuffer.length -
|
|
ssl->buffers.inputBuffer.idx;
|
|
if (used < readSz)
|
|
if ((ret = GetInputData(ssl, readSz)) < 0)
|
|
return ret;
|
|
#endif
|
|
}
|
|
|
|
#ifndef NO_CYASSL_SERVER
|
|
|
|
/* see if sending SSLv2 client hello */
|
|
if ( ssl->options.side == SERVER_END &&
|
|
ssl->options.clientState == NULL_STATE &&
|
|
ssl->buffers.inputBuffer.buffer[ssl->buffers.inputBuffer.idx]
|
|
!= handshake) {
|
|
ssl->options.processReply = runProcessOldClientHello;
|
|
|
|
/* how many bytes need ProcessOldClientHello */
|
|
b0 =
|
|
ssl->buffers.inputBuffer.buffer[ssl->buffers.inputBuffer.idx++];
|
|
b1 =
|
|
ssl->buffers.inputBuffer.buffer[ssl->buffers.inputBuffer.idx++];
|
|
ssl->curSize = ((b0 & 0x7f) << 8) | b1;
|
|
}
|
|
else {
|
|
ssl->options.processReply = getRecordLayerHeader;
|
|
continue;
|
|
}
|
|
|
|
/* in the CYASSL_SERVER case, run the old client hello */
|
|
case runProcessOldClientHello:
|
|
|
|
/* get sz bytes or return error */
|
|
if (!ssl->options.dtls) {
|
|
if ((ret = GetInputData(ssl, ssl->curSize)) < 0)
|
|
return ret;
|
|
} else {
|
|
#ifdef CYASSL_DTLS
|
|
/* read ahead may already have */
|
|
used = ssl->buffers.inputBuffer.length -
|
|
ssl->buffers.inputBuffer.idx;
|
|
if (used < ssl->curSize)
|
|
if ((ret = GetInputData(ssl, ssl->curSize)) < 0)
|
|
return ret;
|
|
#endif /* CYASSL_DTLS */
|
|
}
|
|
|
|
ret = ProcessOldClientHello(ssl, ssl->buffers.inputBuffer.buffer,
|
|
&ssl->buffers.inputBuffer.idx,
|
|
ssl->buffers.inputBuffer.length -
|
|
ssl->buffers.inputBuffer.idx,
|
|
ssl->curSize);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
else if (ssl->buffers.inputBuffer.idx ==
|
|
ssl->buffers.inputBuffer.length) {
|
|
ssl->options.processReply = doProcessInit;
|
|
return 0;
|
|
}
|
|
|
|
#endif /* NO_CYASSL_SERVER */
|
|
|
|
/* get the record layer header */
|
|
case getRecordLayerHeader:
|
|
|
|
ret = GetRecordHeader(ssl, ssl->buffers.inputBuffer.buffer,
|
|
&ssl->buffers.inputBuffer.idx,
|
|
&ssl->curRL, &ssl->curSize);
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
ssl->options.processReply = getData;
|
|
|
|
/* retrieve record layer data */
|
|
case getData:
|
|
|
|
/* get sz bytes or return error */
|
|
if (!ssl->options.dtls) {
|
|
if ((ret = GetInputData(ssl, ssl->curSize)) < 0)
|
|
return ret;
|
|
} else {
|
|
#ifdef CYASSL_DTLS
|
|
/* read ahead may already have */
|
|
used = ssl->buffers.inputBuffer.length -
|
|
ssl->buffers.inputBuffer.idx;
|
|
if (used < ssl->curSize)
|
|
if ((ret = GetInputData(ssl, ssl->curSize)) < 0)
|
|
return ret;
|
|
#endif
|
|
}
|
|
|
|
ssl->options.processReply = runProcessingOneMessage;
|
|
startIdx = ssl->buffers.inputBuffer.idx; /* in case > 1 msg per */
|
|
|
|
/* the record layer is here */
|
|
case runProcessingOneMessage:
|
|
|
|
if (ssl->keys.encryptionOn)
|
|
if (DecryptMessage(ssl, ssl->buffers.inputBuffer.buffer +
|
|
ssl->buffers.inputBuffer.idx,
|
|
ssl->curSize,
|
|
&ssl->buffers.inputBuffer.idx) < 0)
|
|
return DECRYPT_ERROR;
|
|
|
|
CYASSL_MSG("received record layer msg");
|
|
|
|
switch (ssl->curRL.type) {
|
|
case handshake :
|
|
/* debugging in DoHandShakeMsg */
|
|
if ((ret = DoHandShakeMsg(ssl,
|
|
ssl->buffers.inputBuffer.buffer,
|
|
&ssl->buffers.inputBuffer.idx,
|
|
ssl->buffers.inputBuffer.length))
|
|
!= 0)
|
|
return ret;
|
|
break;
|
|
|
|
case change_cipher_spec:
|
|
CYASSL_MSG("got CHANGE CIPHER SPEC");
|
|
#ifdef CYASSL_CALLBACKS
|
|
if (ssl->hsInfoOn)
|
|
AddPacketName("ChangeCipher", &ssl->handShakeInfo);
|
|
/* add record header back on info */
|
|
if (ssl->toInfoOn) {
|
|
AddPacketInfo("ChangeCipher", &ssl->timeoutInfo,
|
|
ssl->buffers.inputBuffer.buffer +
|
|
ssl->buffers.inputBuffer.idx - RECORD_HEADER_SZ,
|
|
1 + RECORD_HEADER_SZ, ssl->heap);
|
|
AddLateRecordHeader(&ssl->curRL, &ssl->timeoutInfo);
|
|
}
|
|
#endif
|
|
ssl->buffers.inputBuffer.idx++;
|
|
ssl->keys.encryptionOn = 1;
|
|
|
|
#ifdef CYASSL_DTLS
|
|
if (ssl->options.dtls)
|
|
ssl->keys.dtls_peer_epoch++;
|
|
#endif
|
|
|
|
#ifdef HAVE_LIBZ
|
|
if (ssl->options.usingCompression)
|
|
if ( (ret = InitStreams(ssl)) != 0)
|
|
return ret;
|
|
#endif
|
|
if (ssl->options.resuming && ssl->options.side ==
|
|
CLIENT_END)
|
|
BuildFinished(ssl, &ssl->verifyHashes, server);
|
|
else if (!ssl->options.resuming && ssl->options.side ==
|
|
SERVER_END)
|
|
BuildFinished(ssl, &ssl->verifyHashes, client);
|
|
break;
|
|
|
|
case application_data:
|
|
CYASSL_MSG("got app DATA");
|
|
if ((ret = DoApplicationData(ssl,
|
|
ssl->buffers.inputBuffer.buffer,
|
|
&ssl->buffers.inputBuffer.idx))
|
|
!= 0) {
|
|
CYASSL_ERROR(ret);
|
|
return ret;
|
|
}
|
|
break;
|
|
|
|
case alert:
|
|
CYASSL_MSG("got ALERT!");
|
|
if (DoAlert(ssl, ssl->buffers.inputBuffer.buffer,
|
|
&ssl->buffers.inputBuffer.idx, &type) == alert_fatal)
|
|
return FATAL_ERROR;
|
|
|
|
/* catch warnings that are handled as errors */
|
|
if (type == close_notify)
|
|
return ssl->error = ZERO_RETURN;
|
|
|
|
if (type == decrypt_error)
|
|
return FATAL_ERROR;
|
|
break;
|
|
|
|
default:
|
|
CYASSL_ERROR(UNKNOWN_RECORD_TYPE);
|
|
return UNKNOWN_RECORD_TYPE;
|
|
}
|
|
|
|
ssl->options.processReply = doProcessInit;
|
|
|
|
/* input exhausted? */
|
|
if (ssl->buffers.inputBuffer.idx == ssl->buffers.inputBuffer.length)
|
|
return 0;
|
|
/* more messages per record */
|
|
else if ((ssl->buffers.inputBuffer.idx - startIdx) < ssl->curSize) {
|
|
#ifdef CYASSL_DTLS
|
|
/* read-ahead but dtls doesn't bundle messages per record */
|
|
if (ssl->options.dtls) {
|
|
ssl->options.processReply = doProcessInit;
|
|
continue;
|
|
}
|
|
#endif
|
|
ssl->options.processReply = runProcessingOneMessage;
|
|
continue;
|
|
}
|
|
/* more records */
|
|
else {
|
|
ssl->options.processReply = doProcessInit;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
int SendChangeCipher(SSL* ssl)
|
|
{
|
|
byte *output;
|
|
int sendSz = RECORD_HEADER_SZ + ENUM_LEN;
|
|
int idx = RECORD_HEADER_SZ;
|
|
int ret;
|
|
|
|
#ifdef CYASSL_DTLS
|
|
if (ssl->options.dtls) {
|
|
sendSz += DTLS_RECORD_EXTRA;
|
|
idx += DTLS_RECORD_EXTRA;
|
|
}
|
|
#endif
|
|
|
|
/* check for avalaible size */
|
|
if ((ret = CheckAvalaibleSize(ssl, sendSz)) != 0)
|
|
return ret;
|
|
|
|
/* get ouput buffer */
|
|
output = ssl->buffers.outputBuffer.buffer +
|
|
ssl->buffers.outputBuffer.idx;
|
|
|
|
AddRecordHeader(output, 1, change_cipher_spec, ssl);
|
|
|
|
output[idx] = 1; /* turn it on */
|
|
|
|
#ifdef CYASSL_CALLBACKS
|
|
if (ssl->hsInfoOn) AddPacketName("ChangeCipher", &ssl->handShakeInfo);
|
|
if (ssl->toInfoOn)
|
|
AddPacketInfo("ChangeCipher", &ssl->timeoutInfo, output, sendSz,
|
|
ssl->heap);
|
|
#endif
|
|
ssl->buffers.outputBuffer.length += sendSz;
|
|
return SendBuffered(ssl);
|
|
}
|
|
|
|
|
|
static INLINE const byte* GetMacSecret(SSL* ssl, int verify)
|
|
{
|
|
if ( (ssl->options.side == CLIENT_END && !verify) ||
|
|
(ssl->options.side == SERVER_END && verify) )
|
|
return ssl->keys.client_write_MAC_secret;
|
|
else
|
|
return ssl->keys.server_write_MAC_secret;
|
|
}
|
|
|
|
|
|
static void Hmac(SSL* ssl, byte* digest, const byte* buffer, word32 sz,
|
|
int content, int verify)
|
|
{
|
|
byte result[SHA256_DIGEST_SIZE]; /* max possible sizes */
|
|
word32 digestSz = ssl->specs.hash_size; /* actual sizes */
|
|
word32 padSz = ssl->specs.pad_size;
|
|
|
|
Md5 md5;
|
|
Sha sha;
|
|
|
|
/* data */
|
|
byte seq[SEQ_SZ] = { 0x00, 0x00, 0x00, 0x00 };
|
|
byte conLen[ENUM_LEN + LENGTH_SZ]; /* content & length */
|
|
const byte* macSecret = GetMacSecret(ssl, verify);
|
|
|
|
conLen[0] = content;
|
|
c16toa((word16)sz, &conLen[ENUM_LEN]);
|
|
c32toa(GetSEQIncrement(ssl, verify), &seq[sizeof(word32)]);
|
|
|
|
if (ssl->specs.mac_algorithm == md5_mac) {
|
|
InitMd5(&md5);
|
|
/* inner */
|
|
Md5Update(&md5, macSecret, digestSz);
|
|
Md5Update(&md5, PAD1, padSz);
|
|
Md5Update(&md5, seq, SEQ_SZ);
|
|
Md5Update(&md5, conLen, sizeof(conLen));
|
|
/* buffer */
|
|
Md5Update(&md5, buffer, sz);
|
|
Md5Final(&md5, result);
|
|
/* outer */
|
|
Md5Update(&md5, macSecret, digestSz);
|
|
Md5Update(&md5, PAD2, padSz);
|
|
Md5Update(&md5, result, digestSz);
|
|
Md5Final(&md5, digest);
|
|
}
|
|
else {
|
|
InitSha(&sha);
|
|
/* inner */
|
|
ShaUpdate(&sha, macSecret, digestSz);
|
|
ShaUpdate(&sha, PAD1, padSz);
|
|
ShaUpdate(&sha, seq, SEQ_SZ);
|
|
ShaUpdate(&sha, conLen, sizeof(conLen));
|
|
/* buffer */
|
|
ShaUpdate(&sha, buffer, sz);
|
|
ShaFinal(&sha, result);
|
|
/* outer */
|
|
ShaUpdate(&sha, macSecret, digestSz);
|
|
ShaUpdate(&sha, PAD2, padSz);
|
|
ShaUpdate(&sha, result, digestSz);
|
|
ShaFinal(&sha, digest);
|
|
}
|
|
}
|
|
|
|
|
|
static void BuildMD5_CertVerify(SSL* ssl, byte* digest)
|
|
{
|
|
byte md5_result[MD5_DIGEST_SIZE];
|
|
|
|
/* make md5 inner */
|
|
Md5Update(&ssl->hashMd5, ssl->arrays.masterSecret, SECRET_LEN);
|
|
Md5Update(&ssl->hashMd5, PAD1, PAD_MD5);
|
|
Md5Final(&ssl->hashMd5, md5_result);
|
|
|
|
/* make md5 outer */
|
|
Md5Update(&ssl->hashMd5, ssl->arrays.masterSecret, SECRET_LEN);
|
|
Md5Update(&ssl->hashMd5, PAD2, PAD_MD5);
|
|
Md5Update(&ssl->hashMd5, md5_result, MD5_DIGEST_SIZE);
|
|
|
|
Md5Final(&ssl->hashMd5, digest);
|
|
}
|
|
|
|
|
|
static void BuildSHA_CertVerify(SSL* ssl, byte* digest)
|
|
{
|
|
byte sha_result[SHA_DIGEST_SIZE];
|
|
|
|
/* make sha inner */
|
|
ShaUpdate(&ssl->hashSha, ssl->arrays.masterSecret, SECRET_LEN);
|
|
ShaUpdate(&ssl->hashSha, PAD1, PAD_SHA);
|
|
ShaFinal(&ssl->hashSha, sha_result);
|
|
|
|
/* make sha outer */
|
|
ShaUpdate(&ssl->hashSha, ssl->arrays.masterSecret, SECRET_LEN);
|
|
ShaUpdate(&ssl->hashSha, PAD2, PAD_SHA);
|
|
ShaUpdate(&ssl->hashSha, sha_result, SHA_DIGEST_SIZE);
|
|
|
|
ShaFinal(&ssl->hashSha, digest);
|
|
}
|
|
|
|
|
|
static void BuildCertHashes(SSL* ssl, Hashes* hashes)
|
|
{
|
|
/* store current states, building requires get_digest which resets state */
|
|
Md5 md5 = ssl->hashMd5;
|
|
Sha sha = ssl->hashSha;
|
|
#ifndef NO_SHA256 /* for possible future changes */
|
|
Sha256 sha256;
|
|
if (IsAtLeastTLSv1_2(ssl))
|
|
sha256 = ssl->hashSha256;
|
|
#endif
|
|
|
|
if (ssl->options.tls) {
|
|
Md5Final(&ssl->hashMd5, hashes->md5);
|
|
ShaFinal(&ssl->hashSha, hashes->sha);
|
|
}
|
|
else {
|
|
BuildMD5_CertVerify(ssl, hashes->md5);
|
|
BuildSHA_CertVerify(ssl, hashes->sha);
|
|
}
|
|
|
|
/* restore */
|
|
ssl->hashMd5 = md5;
|
|
ssl->hashSha = sha;
|
|
#ifndef NO_SHA256
|
|
if (IsAtLeastTLSv1_2(ssl))
|
|
ssl->hashSha256 = sha256;
|
|
#endif
|
|
}
|
|
|
|
|
|
/* Build SSL Message, encrypted */
|
|
static int BuildMessage(SSL* ssl, byte* output, const byte* input, int inSz,
|
|
int type)
|
|
{
|
|
word32 digestSz = ssl->specs.hash_size;
|
|
word32 sz = RECORD_HEADER_SZ + inSz + digestSz;
|
|
word32 pad = 0, i;
|
|
word32 idx = RECORD_HEADER_SZ;
|
|
word32 ivSz = 0; /* TLSv1.1 IV */
|
|
word32 headerSz = RECORD_HEADER_SZ;
|
|
word16 size;
|
|
byte iv[AES_BLOCK_SIZE]; /* max size */
|
|
|
|
#ifdef CYASSL_DTLS
|
|
if (ssl->options.dtls) {
|
|
sz += DTLS_RECORD_EXTRA;
|
|
idx += DTLS_RECORD_EXTRA;
|
|
headerSz += DTLS_RECORD_EXTRA;
|
|
}
|
|
#endif
|
|
|
|
if (ssl->specs.cipher_type == block) {
|
|
word32 blockSz = ssl->specs.block_size;
|
|
if (ssl->options.tls1_1) {
|
|
ivSz = blockSz;
|
|
sz += ivSz;
|
|
RNG_GenerateBlock(&ssl->rng, iv, ivSz);
|
|
}
|
|
sz += 1; /* pad byte */
|
|
pad = (sz - headerSz) % blockSz;
|
|
pad = blockSz - pad;
|
|
sz += pad;
|
|
}
|
|
|
|
size = sz - headerSz; /* include mac and digest */
|
|
AddRecordHeader(output, size, type, ssl);
|
|
|
|
/* write to output */
|
|
if (ivSz) {
|
|
XMEMCPY(output + idx, iv, ivSz);
|
|
idx += ivSz;
|
|
}
|
|
XMEMCPY(output + idx, input, inSz);
|
|
idx += inSz;
|
|
|
|
if (type == handshake)
|
|
HashOutput(ssl, output, headerSz + inSz, ivSz);
|
|
ssl->hmac(ssl, output+idx, output + headerSz + ivSz, inSz, type, 0);
|
|
idx += digestSz;
|
|
|
|
if (ssl->specs.cipher_type == block)
|
|
for (i = 0; i <= pad; i++) output[idx++] = pad; /* pad byte gets */
|
|
/* pad value too */
|
|
Encrypt(ssl, output + headerSz, output + headerSz, size);
|
|
|
|
return sz;
|
|
}
|
|
|
|
|
|
int SendFinished(SSL* ssl)
|
|
{
|
|
int sendSz,
|
|
finishedSz = ssl->options.tls ? TLS_FINISHED_SZ :
|
|
FINISHED_SZ;
|
|
byte input[FINISHED_SZ + DTLS_HANDSHAKE_HEADER_SZ]; /* max */
|
|
byte *output;
|
|
Hashes* hashes;
|
|
int ret;
|
|
int headerSz = HANDSHAKE_HEADER_SZ;
|
|
|
|
|
|
#ifdef CYASSL_DTLS
|
|
if (ssl->options.dtls) {
|
|
headerSz += DTLS_HANDSHAKE_EXTRA;
|
|
ssl->keys.dtls_epoch++;
|
|
ssl->keys.dtls_sequence_number = 0; /* reset after epoch change */
|
|
}
|
|
#endif
|
|
|
|
/* check for avalaible size */
|
|
if ((ret = CheckAvalaibleSize(ssl, sizeof(input) + MAX_MSG_EXTRA)) != 0)
|
|
return ret;
|
|
|
|
/* get ouput buffer */
|
|
output = ssl->buffers.outputBuffer.buffer +
|
|
ssl->buffers.outputBuffer.idx;
|
|
|
|
AddHandShakeHeader(input, finishedSz, finished, ssl);
|
|
|
|
/* make finished hashes */
|
|
hashes = (Hashes*)&input[headerSz];
|
|
BuildFinished(ssl, hashes, ssl->options.side == CLIENT_END ? client :
|
|
server);
|
|
|
|
if ( (sendSz = BuildMessage(ssl, output, input, headerSz +
|
|
finishedSz, handshake)) == -1)
|
|
return BUILD_MSG_ERROR;
|
|
|
|
if (!ssl->options.resuming) {
|
|
AddSession(ssl); /* just try */
|
|
if (ssl->options.side == CLIENT_END)
|
|
BuildFinished(ssl, &ssl->verifyHashes, server);
|
|
else
|
|
ssl->options.handShakeState = HANDSHAKE_DONE;
|
|
}
|
|
else {
|
|
if (ssl->options.side == CLIENT_END)
|
|
ssl->options.handShakeState = HANDSHAKE_DONE;
|
|
else
|
|
BuildFinished(ssl, &ssl->verifyHashes, client);
|
|
}
|
|
|
|
#ifdef CYASSL_CALLBACKS
|
|
if (ssl->hsInfoOn) AddPacketName("Finished", &ssl->handShakeInfo);
|
|
if (ssl->toInfoOn)
|
|
AddPacketInfo("Finished", &ssl->timeoutInfo, output, sendSz,
|
|
ssl->heap);
|
|
#endif
|
|
|
|
ssl->buffers.outputBuffer.length += sendSz;
|
|
|
|
return SendBuffered(ssl);
|
|
}
|
|
|
|
|
|
int SendCertificate(SSL* ssl)
|
|
{
|
|
int sendSz, length, ret = 0;
|
|
word32 i = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ;
|
|
word32 certSz, listSz;
|
|
byte* output = 0;
|
|
|
|
if (ssl->options.usingPSK_cipher) return 0; /* not needed */
|
|
|
|
if (ssl->options.sendVerify == SEND_BLANK_CERT) {
|
|
certSz = 0;
|
|
length = CERT_HEADER_SZ;
|
|
listSz = 0;
|
|
}
|
|
else {
|
|
certSz = ssl->buffers.certificate.length;
|
|
/* list + cert size */
|
|
length = certSz + 2 * CERT_HEADER_SZ;
|
|
listSz = certSz + CERT_HEADER_SZ;
|
|
}
|
|
sendSz = length + RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ;
|
|
|
|
#ifdef CYASSL_DTLS
|
|
if (ssl->options.dtls) {
|
|
sendSz += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA;
|
|
i += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA;
|
|
}
|
|
#endif
|
|
|
|
/* check for avalaible size */
|
|
if ((ret = CheckAvalaibleSize(ssl, sendSz)) != 0)
|
|
return ret;
|
|
|
|
/* get ouput buffer */
|
|
output = ssl->buffers.outputBuffer.buffer +
|
|
ssl->buffers.outputBuffer.idx;
|
|
|
|
AddHeaders(output, length, certificate, ssl);
|
|
|
|
/* list total */
|
|
c32to24(listSz, output + i);
|
|
i += CERT_HEADER_SZ;
|
|
|
|
/* member */
|
|
if (certSz) {
|
|
c32to24(certSz, output + i);
|
|
i += CERT_HEADER_SZ;
|
|
XMEMCPY(output + i, ssl->buffers.certificate.buffer, certSz);
|
|
i += certSz;
|
|
}
|
|
HashOutput(ssl, output, sendSz, 0);
|
|
#ifdef CYASSL_CALLBACKS
|
|
if (ssl->hsInfoOn) AddPacketName("Certificate", &ssl->handShakeInfo);
|
|
if (ssl->toInfoOn)
|
|
AddPacketInfo("Certificate", &ssl->timeoutInfo, output, sendSz,
|
|
ssl->heap);
|
|
#endif
|
|
|
|
if (ssl->options.side == SERVER_END)
|
|
ssl->options.serverState = SERVER_CERT_COMPLETE;
|
|
|
|
ssl->buffers.outputBuffer.length += sendSz;
|
|
return SendBuffered(ssl);
|
|
}
|
|
|
|
|
|
int SendCertificateRequest(SSL* ssl)
|
|
{
|
|
byte *output;
|
|
int ret;
|
|
int sendSz;
|
|
word32 i = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ;
|
|
|
|
int typeTotal = 1; /* only rsa for now */
|
|
int reqSz = ENUM_LEN + typeTotal + REQ_HEADER_SZ; /* add auth later */
|
|
|
|
if (IsAtLeastTLSv1_2(ssl))
|
|
reqSz += LENGTH_SZ + HASH_SIG_SIZE;
|
|
|
|
if (ssl->options.usingPSK_cipher) return 0; /* not needed */
|
|
|
|
sendSz = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ + reqSz;
|
|
|
|
#ifdef CYASSL_DTLS
|
|
if (ssl->options.dtls) {
|
|
sendSz += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA;
|
|
i += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA;
|
|
}
|
|
#endif
|
|
/* check for avalaible size */
|
|
if ((ret = CheckAvalaibleSize(ssl, sendSz)) != 0)
|
|
return ret;
|
|
|
|
/* get ouput buffer */
|
|
output = ssl->buffers.outputBuffer.buffer + ssl->buffers.outputBuffer.idx;
|
|
|
|
AddHeaders(output, reqSz, certificate_request, ssl);
|
|
|
|
/* write to output */
|
|
output[i++] = typeTotal; /* # of types */
|
|
output[i++] = rsa_sign;
|
|
|
|
/* supported hash/sig */
|
|
if (IsAtLeastTLSv1_2(ssl)) {
|
|
c16toa(HASH_SIG_SIZE, &output[i]);
|
|
i += LENGTH_SZ;
|
|
|
|
output[i++] = sha_mac; /* hash */
|
|
output[i++] = rsa_sa_algo; /* sig */
|
|
}
|
|
|
|
c16toa(0, &output[i]); /* auth's */
|
|
i += REQ_HEADER_SZ;
|
|
|
|
HashOutput(ssl, output, sendSz, 0);
|
|
|
|
#ifdef CYASSL_CALLBACKS
|
|
if (ssl->hsInfoOn)
|
|
AddPacketName("CertificateRequest", &ssl->handShakeInfo);
|
|
if (ssl->toInfoOn)
|
|
AddPacketInfo("CertificateRequest", &ssl->timeoutInfo, output,
|
|
sendSz, ssl->heap);
|
|
#endif
|
|
ssl->buffers.outputBuffer.length += sendSz;
|
|
return SendBuffered(ssl);
|
|
}
|
|
|
|
|
|
int SendData(SSL* ssl, const void* buffer, int sz)
|
|
{
|
|
int sent = 0, /* plainText size */
|
|
sendSz,
|
|
ret;
|
|
|
|
if (ssl->error == WANT_WRITE)
|
|
ssl->error = 0;
|
|
|
|
if (ssl->options.handShakeState != HANDSHAKE_DONE) {
|
|
int err;
|
|
if ( (err = CyaSSL_negotiate(ssl)) != 0)
|
|
return err;
|
|
}
|
|
|
|
/* last time system socket output buffer was full, try again to send */
|
|
if (ssl->buffers.outputBuffer.length > 0) {
|
|
if ( (ssl->error = SendBuffered(ssl)) < 0) {
|
|
CYASSL_ERROR(ssl->error);
|
|
if (ssl->error == SOCKET_ERROR_E && ssl->options.connReset)
|
|
return 0; /* peer reset */
|
|
return ssl->error;
|
|
}
|
|
else {
|
|
/* advance sent to previous sent + plain size just sent */
|
|
sent = ssl->buffers.prevSent + ssl->buffers.plainSz;
|
|
CYASSL_MSG("sent write buffered data");
|
|
}
|
|
}
|
|
|
|
for (;;) {
|
|
int len = min(sz - sent, OUTPUT_RECORD_SIZE);
|
|
byte* out;
|
|
byte* sendBuffer = (byte*)buffer + sent; /* may switch on comp */
|
|
int buffSz = len; /* may switch on comp */
|
|
#ifdef HAVE_LIBZ
|
|
byte comp[MAX_RECORD_SIZE + MAX_COMP_EXTRA];
|
|
#endif
|
|
|
|
if (sent == sz) break;
|
|
|
|
#ifdef CYASSL_DTLS
|
|
if (ssl->options.dtls) {
|
|
len = min(len, MAX_UDP_SIZE);
|
|
buffSz = len;
|
|
}
|
|
#endif
|
|
|
|
/* check for avalaible size */
|
|
if ((ret = CheckAvalaibleSize(ssl, len + COMP_EXTRA +
|
|
MAX_MSG_EXTRA)) != 0)
|
|
return ret;
|
|
|
|
/* get ouput buffer */
|
|
out = ssl->buffers.outputBuffer.buffer +
|
|
ssl->buffers.outputBuffer.idx;
|
|
|
|
#ifdef HAVE_LIBZ
|
|
if (ssl->options.usingCompression) {
|
|
buffSz = Compress(ssl, sendBuffer, buffSz, comp, sizeof(comp));
|
|
if (buffSz < 0) {
|
|
return buffSz;
|
|
}
|
|
sendBuffer = comp;
|
|
}
|
|
#endif
|
|
sendSz = BuildMessage(ssl, out, sendBuffer, buffSz,
|
|
application_data);
|
|
|
|
ssl->buffers.outputBuffer.length += sendSz;
|
|
|
|
if ( (ret = SendBuffered(ssl)) < 0) {
|
|
CYASSL_ERROR(ret);
|
|
/* store for next call if WANT_WRITE or user embedSend() that
|
|
doesn't present like WANT_WRITE */
|
|
ssl->buffers.plainSz = len;
|
|
ssl->buffers.prevSent = sent;
|
|
if (ret == SOCKET_ERROR_E && ssl->options.connReset)
|
|
return 0; /* peer reset */
|
|
return ssl->error = ret;
|
|
}
|
|
|
|
sent += len;
|
|
|
|
/* only one message per attempt */
|
|
if (ssl->options.partialWrite == 1)
|
|
break;
|
|
}
|
|
|
|
return sent;
|
|
}
|
|
|
|
/* process input data */
|
|
int ReceiveData(SSL* ssl, byte* output, int sz)
|
|
{
|
|
int size;
|
|
|
|
CYASSL_ENTER("ReceiveData()");
|
|
|
|
if (ssl->error == WANT_READ)
|
|
ssl->error = 0;
|
|
|
|
if (ssl->options.handShakeState != HANDSHAKE_DONE) {
|
|
int err;
|
|
if ( (err = CyaSSL_negotiate(ssl)) != 0)
|
|
return err;
|
|
}
|
|
|
|
while (ssl->buffers.clearOutputBuffer.length == 0)
|
|
if ( (ssl->error = ProcessReply(ssl)) < 0) {
|
|
CYASSL_ERROR(ssl->error);
|
|
if (ssl->error == ZERO_RETURN) {
|
|
ssl->options.isClosed = 1;
|
|
return 0; /* no more data coming */
|
|
}
|
|
if (ssl->error == SOCKET_ERROR_E)
|
|
if (ssl->options.connReset || ssl->options.isClosed)
|
|
return 0; /* peer reset or closed */
|
|
return ssl->error;
|
|
}
|
|
|
|
if (sz < (int)ssl->buffers.clearOutputBuffer.length)
|
|
size = sz;
|
|
else
|
|
size = ssl->buffers.clearOutputBuffer.length;
|
|
|
|
XMEMCPY(output, ssl->buffers.clearOutputBuffer.buffer, size);
|
|
ssl->buffers.clearOutputBuffer.length -= size;
|
|
ssl->buffers.clearOutputBuffer.buffer += size;
|
|
|
|
if (ssl->buffers.clearOutputBuffer.length == 0 &&
|
|
ssl->buffers.inputBuffer.dynamicFlag)
|
|
ShrinkInputBuffer(ssl, NO_FORCED_FREE);
|
|
|
|
CYASSL_LEAVE("ReceiveData()", size);
|
|
return size;
|
|
}
|
|
|
|
|
|
/* send alert message */
|
|
int SendAlert(SSL* ssl, int severity, int type)
|
|
{
|
|
byte input[ALERT_SIZE];
|
|
byte *output;
|
|
int sendSz;
|
|
int ret;
|
|
|
|
/* if sendalert is called again for nonbloking */
|
|
if (ssl->options.sendAlertState != 0) {
|
|
ret = SendBuffered(ssl);
|
|
if (ret == 0)
|
|
ssl->options.sendAlertState = 0;
|
|
return ret;
|
|
}
|
|
|
|
/* check for avalaible size */
|
|
if ((ret = CheckAvalaibleSize(ssl, ALERT_SIZE + MAX_MSG_EXTRA)) != 0)
|
|
return ret;
|
|
|
|
/* get ouput buffer */
|
|
output = ssl->buffers.outputBuffer.buffer +
|
|
ssl->buffers.outputBuffer.idx;
|
|
|
|
input[0] = severity;
|
|
input[1] = type;
|
|
|
|
if (ssl->keys.encryptionOn)
|
|
sendSz = BuildMessage(ssl, output, input, sizeof(input), alert);
|
|
else {
|
|
RecordLayerHeader *const rl = (RecordLayerHeader*)output;
|
|
rl->type = alert;
|
|
rl->version = ssl->version;
|
|
c16toa(ALERT_SIZE, rl->length);
|
|
|
|
XMEMCPY(output + RECORD_HEADER_SZ, input, sizeof(input));
|
|
sendSz = RECORD_HEADER_SZ + sizeof(input);
|
|
}
|
|
|
|
#ifdef CYASSL_CALLBACKS
|
|
if (ssl->hsInfoOn)
|
|
AddPacketName("Alert", &ssl->handShakeInfo);
|
|
if (ssl->toInfoOn)
|
|
AddPacketInfo("Alert", &ssl->timeoutInfo, output, sendSz,ssl->heap);
|
|
#endif
|
|
|
|
ssl->buffers.outputBuffer.length += sendSz;
|
|
ssl->options.sendAlertState = 1;
|
|
|
|
return SendBuffered(ssl);
|
|
}
|
|
|
|
|
|
|
|
void SetErrorString(int error, char* buffer)
|
|
{
|
|
const int max = MAX_ERROR_SZ; /* shorthand */
|
|
|
|
#ifdef NO_ERROR_STRINGS
|
|
|
|
XSTRNCPY(buffer, "no support for error strings built in", max);
|
|
|
|
#else
|
|
|
|
/* pass to CTaoCrypt */
|
|
if (error < MAX_CODE_E && error > MIN_CODE_E) {
|
|
CTaoCryptErrorString(error, buffer);
|
|
return;
|
|
}
|
|
|
|
switch (error) {
|
|
|
|
case UNSUPPORTED_SUITE :
|
|
XSTRNCPY(buffer, "unsupported cipher suite", max);
|
|
break;
|
|
|
|
case PREFIX_ERROR :
|
|
XSTRNCPY(buffer, "bad index to key rounds", max);
|
|
break;
|
|
|
|
case MEMORY_ERROR :
|
|
XSTRNCPY(buffer, "out of memory", max);
|
|
break;
|
|
|
|
case VERIFY_FINISHED_ERROR :
|
|
XSTRNCPY(buffer, "verify problem on finished", max);
|
|
break;
|
|
|
|
case VERIFY_MAC_ERROR :
|
|
XSTRNCPY(buffer, "verify mac problem", max);
|
|
break;
|
|
|
|
case PARSE_ERROR :
|
|
XSTRNCPY(buffer, "parse error on header", max);
|
|
break;
|
|
|
|
case SIDE_ERROR :
|
|
XSTRNCPY(buffer, "wrong client/server type", max);
|
|
break;
|
|
|
|
case NO_PEER_CERT :
|
|
XSTRNCPY(buffer, "peer didn't send cert", max);
|
|
break;
|
|
|
|
case UNKNOWN_HANDSHAKE_TYPE :
|
|
XSTRNCPY(buffer, "weird handshake type", max);
|
|
break;
|
|
|
|
case SOCKET_ERROR_E :
|
|
XSTRNCPY(buffer, "error state on socket", max);
|
|
break;
|
|
|
|
case SOCKET_NODATA :
|
|
XSTRNCPY(buffer, "expected data, not there", max);
|
|
break;
|
|
|
|
case INCOMPLETE_DATA :
|
|
XSTRNCPY(buffer, "don't have enough data to complete task", max);
|
|
break;
|
|
|
|
case UNKNOWN_RECORD_TYPE :
|
|
XSTRNCPY(buffer, "unknown type in record hdr", max);
|
|
break;
|
|
|
|
case DECRYPT_ERROR :
|
|
XSTRNCPY(buffer, "error during decryption", max);
|
|
break;
|
|
|
|
case FATAL_ERROR :
|
|
XSTRNCPY(buffer, "revcd alert fatal error", max);
|
|
break;
|
|
|
|
case ENCRYPT_ERROR :
|
|
XSTRNCPY(buffer, "error during encryption", max);
|
|
break;
|
|
|
|
case FREAD_ERROR :
|
|
XSTRNCPY(buffer, "fread problem", max);
|
|
break;
|
|
|
|
case NO_PEER_KEY :
|
|
XSTRNCPY(buffer, "need peer's key", max);
|
|
break;
|
|
|
|
case NO_PRIVATE_KEY :
|
|
XSTRNCPY(buffer, "need the private key", max);
|
|
break;
|
|
|
|
case RSA_PRIVATE_ERROR :
|
|
XSTRNCPY(buffer, "error during rsa priv op", max);
|
|
break;
|
|
|
|
case MATCH_SUITE_ERROR :
|
|
XSTRNCPY(buffer, "can't match cipher suite", max);
|
|
break;
|
|
|
|
case BUILD_MSG_ERROR :
|
|
XSTRNCPY(buffer, "build message failure", max);
|
|
break;
|
|
|
|
case BAD_HELLO :
|
|
XSTRNCPY(buffer, "client hello malformed", max);
|
|
break;
|
|
|
|
case DOMAIN_NAME_MISMATCH :
|
|
XSTRNCPY(buffer, "peer subject name mismatch", max);
|
|
break;
|
|
|
|
case WANT_READ :
|
|
XSTRNCPY(buffer, "non-blocking socket wants data to be read", max);
|
|
break;
|
|
|
|
case NOT_READY_ERROR :
|
|
XSTRNCPY(buffer, "handshake layer not ready yet, complete first", max);
|
|
break;
|
|
|
|
case PMS_VERSION_ERROR :
|
|
XSTRNCPY(buffer, "premaster secret version mismatch error", max);
|
|
break;
|
|
|
|
case VERSION_ERROR :
|
|
XSTRNCPY(buffer, "record layer version error", max);
|
|
break;
|
|
|
|
case WANT_WRITE :
|
|
XSTRNCPY(buffer, "non-blocking socket write buffer full", max);
|
|
break;
|
|
|
|
case BUFFER_ERROR :
|
|
XSTRNCPY(buffer, "malformed buffer input error", max);
|
|
break;
|
|
|
|
case VERIFY_CERT_ERROR :
|
|
XSTRNCPY(buffer, "verify problem on certificate", max);
|
|
break;
|
|
|
|
case VERIFY_SIGN_ERROR :
|
|
XSTRNCPY(buffer, "verify problem based on signature", max);
|
|
break;
|
|
|
|
case CLIENT_ID_ERROR :
|
|
XSTRNCPY(buffer, "psk client identity error", max);
|
|
break;
|
|
|
|
case SERVER_HINT_ERROR:
|
|
XSTRNCPY(buffer, "psk server hint error", max);
|
|
break;
|
|
|
|
case PSK_KEY_ERROR:
|
|
XSTRNCPY(buffer, "psk key callback error", max);
|
|
break;
|
|
|
|
case NTRU_KEY_ERROR:
|
|
XSTRNCPY(buffer, "NTRU key error", max);
|
|
break;
|
|
|
|
case NTRU_DRBG_ERROR:
|
|
XSTRNCPY(buffer, "NTRU drbg error", max);
|
|
break;
|
|
|
|
case NTRU_ENCRYPT_ERROR:
|
|
XSTRNCPY(buffer, "NTRU encrypt error", max);
|
|
break;
|
|
|
|
case NTRU_DECRYPT_ERROR:
|
|
XSTRNCPY(buffer, "NTRU decrypt error", max);
|
|
break;
|
|
|
|
case ZLIB_INIT_ERROR:
|
|
XSTRNCPY(buffer, "zlib init error", max);
|
|
break;
|
|
|
|
case ZLIB_COMPRESS_ERROR:
|
|
XSTRNCPY(buffer, "zlib compress error", max);
|
|
break;
|
|
|
|
case ZLIB_DECOMPRESS_ERROR:
|
|
XSTRNCPY(buffer, "zlib decompress error", max);
|
|
break;
|
|
|
|
case GETTIME_ERROR:
|
|
XSTRNCPY(buffer, "gettimeofday() error", max);
|
|
break;
|
|
|
|
case GETITIMER_ERROR:
|
|
XSTRNCPY(buffer, "getitimer() error", max);
|
|
break;
|
|
|
|
case SIGACT_ERROR:
|
|
XSTRNCPY(buffer, "sigaction() error", max);
|
|
break;
|
|
|
|
case SETITIMER_ERROR:
|
|
XSTRNCPY(buffer, "setitimer() error", max);
|
|
break;
|
|
|
|
case LENGTH_ERROR:
|
|
XSTRNCPY(buffer, "record layer length error", max);
|
|
break;
|
|
|
|
case PEER_KEY_ERROR:
|
|
XSTRNCPY(buffer, "cant decode peer key", max);
|
|
break;
|
|
|
|
case ZERO_RETURN:
|
|
XSTRNCPY(buffer, "peer sent close notify alert", max);
|
|
break;
|
|
|
|
case ECC_CURVETYPE_ERROR:
|
|
XSTRNCPY(buffer, "Bad ECC Curve Type or unsupported", max);
|
|
break;
|
|
|
|
case ECC_CURVE_ERROR:
|
|
XSTRNCPY(buffer, "Bad ECC Curve or unsupported", max);
|
|
break;
|
|
|
|
case ECC_PEERKEY_ERROR:
|
|
XSTRNCPY(buffer, "Bad ECC Peer Key", max);
|
|
break;
|
|
|
|
case ECC_MAKEKEY_ERROR:
|
|
XSTRNCPY(buffer, "ECC Make Key failutre", max);
|
|
break;
|
|
|
|
case ECC_EXPORT_ERROR:
|
|
XSTRNCPY(buffer, "ECC Export Key failutre", max);
|
|
break;
|
|
|
|
case ECC_SHARED_ERROR:
|
|
XSTRNCPY(buffer, "ECC DHE shared failutre", max);
|
|
break;
|
|
|
|
default :
|
|
XSTRNCPY(buffer, "unknown error number", max);
|
|
}
|
|
|
|
#endif /* NO_ERROR_STRINGS */
|
|
}
|
|
|
|
|
|
|
|
/* be sure to add to cipher_name_idx too !!!! */
|
|
const char* const cipher_names[] =
|
|
{
|
|
#ifdef BUILD_SSL_RSA_WITH_RC4_128_SHA
|
|
"RC4-SHA",
|
|
#endif
|
|
|
|
#ifdef BUILD_SSL_RSA_WITH_RC4_128_MD5
|
|
"RC4-MD5",
|
|
#endif
|
|
|
|
#ifdef BUILD_SSL_RSA_WITH_3DES_EDE_CBC_SHA
|
|
"DES-CBC3-SHA",
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_RSA_WITH_AES_128_CBC_SHA
|
|
"AES128-SHA",
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_RSA_WITH_AES_256_CBC_SHA
|
|
"AES256-SHA",
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_DHE_RSA_WITH_AES_128_CBC_SHA
|
|
"DHE-RSA-AES128-SHA",
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_DHE_RSA_WITH_AES_256_CBC_SHA
|
|
"DHE-RSA-AES256-SHA",
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_PSK_WITH_AES_128_CBC_SHA
|
|
"PSK-AES128-CBC-SHA",
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_PSK_WITH_AES_256_CBC_SHA
|
|
"PSK-AES256-CBC-SHA",
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_RSA_WITH_HC_128_CBC_MD5
|
|
"HC128-MD5",
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_RSA_WITH_HC_128_CBC_SHA
|
|
"HC128-SHA",
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_RSA_WITH_RABBIT_CBC_SHA
|
|
"RABBIT-SHA",
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_NTRU_RSA_WITH_RC4_128_SHA
|
|
"NTRU-RC4-SHA",
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_NTRU_RSA_WITH_3DES_EDE_CBC_SHA
|
|
"NTRU-DES-CBC3-SHA",
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_NTRU_RSA_WITH_AES_128_CBC_SHA
|
|
"NTRU-AES128-SHA",
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_NTRU_RSA_WITH_AES_256_CBC_SHA
|
|
"NTRU-AES256-SHA",
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
|
|
"ECDHE-RSA-AES128-SHA",
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
|
|
"ECDHE-RSA-AES256-SHA",
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
|
|
"ECDHE-ECDSA-AES128-SHA",
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
|
|
"ECDHE-ECDSA-AES256-SHA",
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_ECDHE_RSA_WITH_RC4_128_SHA
|
|
"ECDHE-RSA-RC4-SHA",
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
|
|
"ECDHE-RSA-DES-CBC3-SHA",
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_RC4_128_SHA
|
|
"ECDHE-ECDSA-RC4-SHA",
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA
|
|
"ECDHE-ECDSA-DES-CBC3-SHA",
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_RSA_WITH_AES_128_CBC_SHA256
|
|
"AES128-SHA256",
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_RSA_WITH_AES_256_CBC_SHA256
|
|
"AES256-SHA256",
|
|
#endif
|
|
};
|
|
|
|
|
|
|
|
/* cipher suite number that matches above name table */
|
|
int cipher_name_idx[] =
|
|
{
|
|
|
|
#ifdef BUILD_SSL_RSA_WITH_RC4_128_SHA
|
|
SSL_RSA_WITH_RC4_128_SHA,
|
|
#endif
|
|
|
|
#ifdef BUILD_SSL_RSA_WITH_RC4_128_MD5
|
|
SSL_RSA_WITH_RC4_128_MD5,
|
|
#endif
|
|
|
|
#ifdef BUILD_SSL_RSA_WITH_3DES_EDE_CBC_SHA
|
|
SSL_RSA_WITH_3DES_EDE_CBC_SHA,
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_RSA_WITH_AES_128_CBC_SHA
|
|
TLS_RSA_WITH_AES_128_CBC_SHA,
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_RSA_WITH_AES_256_CBC_SHA
|
|
TLS_RSA_WITH_AES_256_CBC_SHA,
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_DHE_RSA_WITH_AES_128_CBC_SHA
|
|
TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_DHE_RSA_WITH_AES_256_CBC_SHA
|
|
TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_PSK_WITH_AES_128_CBC_SHA
|
|
TLS_PSK_WITH_AES_128_CBC_SHA,
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_PSK_WITH_AES_256_CBC_SHA
|
|
TLS_PSK_WITH_AES_256_CBC_SHA,
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_RSA_WITH_HC_128_CBC_MD5
|
|
TLS_RSA_WITH_HC_128_CBC_MD5,
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_RSA_WITH_HC_128_CBC_SHA
|
|
TLS_RSA_WITH_HC_128_CBC_SHA,
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_RSA_WITH_RABBIT_CBC_SHA
|
|
TLS_RSA_WITH_RABBIT_CBC_SHA,
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_NTRU_RSA_WITH_RC4_128_SHA
|
|
TLS_NTRU_RSA_WITH_RC4_128_SHA,
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_NTRU_RSA_WITH_3DES_EDE_CBC_SHA
|
|
TLS_NTRU_RSA_WITH_3DES_EDE_CBC_SHA,
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_NTRU_RSA_WITH_AES_128_CBC_SHA
|
|
TLS_NTRU_RSA_WITH_AES_128_CBC_SHA,
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_NTRU_RSA_WITH_AES_256_CBC_SHA
|
|
TLS_NTRU_RSA_WITH_AES_256_CBC_SHA,
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
|
|
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
|
|
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
|
|
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
|
|
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_ECDHE_RSA_WITH_RC4_128_SHA
|
|
TLS_ECDHE_RSA_WITH_RC4_128_SHA,
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
|
|
TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_RC4_128_SHA
|
|
TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA
|
|
TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA,
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_RSA_WITH_AES_128_CBC_SHA256
|
|
TLS_RSA_WITH_AES_128_CBC_SHA256,
|
|
#endif
|
|
|
|
#ifdef BUILD_TLS_RSA_WITH_AES_256_CBC_SHA256
|
|
TLS_RSA_WITH_AES_256_CBC_SHA256,
|
|
#endif
|
|
};
|
|
|
|
|
|
/* return true if set, else false */
|
|
/* only supports full name from cipher_name[] delimited by : */
|
|
int SetCipherList(SSL_CTX* ctx, const char* list)
|
|
{
|
|
int ret = 0, i;
|
|
char name[MAX_SUITE_NAME];
|
|
|
|
char needle[] = ":";
|
|
char* haystack = (char*)list;
|
|
char* prev;
|
|
|
|
const int suiteSz = sizeof(cipher_names) / sizeof(cipher_names[0]);
|
|
int idx = 0;
|
|
|
|
if (!list)
|
|
return 0;
|
|
|
|
if (*list == 0) return 1; /* CyaSSL default */
|
|
|
|
if (XSTRNCMP(haystack, "ALL", 3) == 0) return 1; /* CyaSSL defualt */
|
|
|
|
for(;;) {
|
|
size_t len;
|
|
prev = haystack;
|
|
haystack = XSTRSTR(haystack, needle);
|
|
|
|
if (!haystack) /* last cipher */
|
|
len = min(sizeof(name), XSTRLEN(prev));
|
|
else
|
|
len = min(sizeof(name), (size_t)(haystack - prev));
|
|
|
|
XSTRNCPY(name, prev, len);
|
|
name[(len == sizeof(name)) ? len - 1 : len] = 0;
|
|
|
|
for (i = 0; i < suiteSz; i++)
|
|
if (XSTRNCMP(name, cipher_names[i], sizeof(name)) == 0) {
|
|
if (XSTRSTR(name, "EC"))
|
|
ctx->suites.suites[idx++] = ECC_BYTE; /* ECC suite */
|
|
else
|
|
ctx->suites.suites[idx++] = 0x00; /* normal */
|
|
ctx->suites.suites[idx++] = cipher_name_idx[i];
|
|
|
|
if (!ret) ret = 1; /* found at least one */
|
|
break;
|
|
}
|
|
if (!haystack) break;
|
|
haystack++;
|
|
}
|
|
|
|
if (ret) {
|
|
ctx->suites.setSuites = 1;
|
|
ctx->suites.suiteSz = idx;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
#ifdef CYASSL_CALLBACKS
|
|
|
|
/* Initialisze HandShakeInfo */
|
|
void InitHandShakeInfo(HandShakeInfo* info)
|
|
{
|
|
int i;
|
|
|
|
info->cipherName[0] = 0;
|
|
for (i = 0; i < MAX_PACKETS_HANDSHAKE; i++)
|
|
info->packetNames[i][0] = 0;
|
|
info->numberPackets = 0;
|
|
info->negotiationError = 0;
|
|
}
|
|
|
|
/* Set Final HandShakeInfo parameters */
|
|
void FinishHandShakeInfo(HandShakeInfo* info, const SSL* ssl)
|
|
{
|
|
int i;
|
|
int sz = sizeof(cipher_name_idx)/sizeof(int);
|
|
|
|
for (i = 0; i < sz; i++)
|
|
if (ssl->options.cipherSuite == (byte)cipher_name_idx[i]) {
|
|
if (ssl->options.cipherSuite0 == ECC_BYTE)
|
|
continue; /* ECC suites at end */
|
|
XSTRNCPY(info->cipherName, cipher_names[i], MAX_CIPHERNAME_SZ);
|
|
break;
|
|
}
|
|
|
|
/* error max and min are negative numbers */
|
|
if (ssl->error <= MIN_PARAM_ERR && ssl->error >= MAX_PARAM_ERR)
|
|
info->negotiationError = ssl->error;
|
|
}
|
|
|
|
|
|
/* Add name to info packet names, increase packet name count */
|
|
void AddPacketName(const char* name, HandShakeInfo* info)
|
|
{
|
|
if (info->numberPackets < MAX_PACKETS_HANDSHAKE) {
|
|
XSTRNCPY(info->packetNames[info->numberPackets++], name,
|
|
MAX_PACKETNAME_SZ);
|
|
}
|
|
}
|
|
|
|
|
|
/* Initialisze TimeoutInfo */
|
|
void InitTimeoutInfo(TimeoutInfo* info)
|
|
{
|
|
int i;
|
|
|
|
info->timeoutName[0] = 0;
|
|
info->flags = 0;
|
|
|
|
for (i = 0; i < MAX_PACKETS_HANDSHAKE; i++) {
|
|
info->packets[i].packetName[0] = 0;
|
|
info->packets[i].timestamp.tv_sec = 0;
|
|
info->packets[i].timestamp.tv_usec = 0;
|
|
info->packets[i].bufferValue = 0;
|
|
info->packets[i].valueSz = 0;
|
|
}
|
|
info->numberPackets = 0;
|
|
info->timeoutValue.tv_sec = 0;
|
|
info->timeoutValue.tv_usec = 0;
|
|
}
|
|
|
|
|
|
/* Free TimeoutInfo */
|
|
void FreeTimeoutInfo(TimeoutInfo* info, void* heap)
|
|
{
|
|
int i;
|
|
for (i = 0; i < MAX_PACKETS_HANDSHAKE; i++)
|
|
if (info->packets[i].bufferValue) {
|
|
XFREE(info->packets[i].bufferValue, heap, DYNAMIC_TYPE_INFO);
|
|
info->packets[i].bufferValue = 0;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/* Add PacketInfo to TimeoutInfo */
|
|
void AddPacketInfo(const char* name, TimeoutInfo* info, const byte* data,
|
|
int sz, void* heap)
|
|
{
|
|
if (info->numberPackets < (MAX_PACKETS_HANDSHAKE - 1)) {
|
|
Timeval currTime;
|
|
|
|
/* may add name after */
|
|
if (name)
|
|
XSTRNCPY(info->packets[info->numberPackets].packetName, name,
|
|
MAX_PACKETNAME_SZ);
|
|
|
|
/* add data, put in buffer if bigger than static buffer */
|
|
info->packets[info->numberPackets].valueSz = sz;
|
|
if (sz < MAX_VALUE_SZ)
|
|
XMEMCPY(info->packets[info->numberPackets].value, data, sz);
|
|
else {
|
|
info->packets[info->numberPackets].bufferValue =
|
|
XMALLOC(sz, heap, DYNAMIC_TYPE_INFO);
|
|
if (!info->packets[info->numberPackets].bufferValue)
|
|
/* let next alloc catch, just don't fill, not fatal here */
|
|
info->packets[info->numberPackets].valueSz = 0;
|
|
else
|
|
XMEMCPY(info->packets[info->numberPackets].bufferValue,
|
|
data, sz);
|
|
}
|
|
gettimeofday(&currTime, 0);
|
|
info->packets[info->numberPackets].timestamp.tv_sec =
|
|
currTime.tv_sec;
|
|
info->packets[info->numberPackets].timestamp.tv_usec =
|
|
currTime.tv_usec;
|
|
info->numberPackets++;
|
|
}
|
|
}
|
|
|
|
|
|
/* Add packet name to previsouly added packet info */
|
|
void AddLateName(const char* name, TimeoutInfo* info)
|
|
{
|
|
/* make sure we have a valid previous one */
|
|
if (info->numberPackets > 0 && info->numberPackets <
|
|
MAX_PACKETS_HANDSHAKE) {
|
|
XSTRNCPY(info->packets[info->numberPackets - 1].packetName, name,
|
|
MAX_PACKETNAME_SZ);
|
|
}
|
|
}
|
|
|
|
/* Add record header to previsouly added packet info */
|
|
void AddLateRecordHeader(const RecordLayerHeader* rl, TimeoutInfo* info)
|
|
{
|
|
/* make sure we have a valid previous one */
|
|
if (info->numberPackets > 0 && info->numberPackets <
|
|
MAX_PACKETS_HANDSHAKE) {
|
|
if (info->packets[info->numberPackets - 1].bufferValue)
|
|
XMEMCPY(info->packets[info->numberPackets - 1].bufferValue, rl,
|
|
RECORD_HEADER_SZ);
|
|
else
|
|
XMEMCPY(info->packets[info->numberPackets - 1].value, rl,
|
|
RECORD_HEADER_SZ);
|
|
}
|
|
}
|
|
|
|
#endif /* CYASSL_CALLBACKS */
|
|
|
|
|
|
|
|
/* client only parts */
|
|
#ifndef NO_CYASSL_CLIENT
|
|
|
|
int SendClientHello(SSL* ssl)
|
|
{
|
|
byte *output;
|
|
word32 length, idx = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ;
|
|
int sendSz;
|
|
int idSz = ssl->options.resuming ? ID_LEN : 0;
|
|
int ret;
|
|
|
|
length = sizeof(ProtocolVersion) + RAN_LEN
|
|
+ idSz + ENUM_LEN
|
|
+ ssl->suites.suiteSz + SUITE_LEN
|
|
+ COMP_LEN + ENUM_LEN;
|
|
|
|
sendSz = length + HANDSHAKE_HEADER_SZ + RECORD_HEADER_SZ;
|
|
|
|
#ifdef CYASSL_DTLS
|
|
if (ssl->options.dtls) {
|
|
length += ENUM_LEN; /* cookie */
|
|
sendSz = length + DTLS_HANDSHAKE_HEADER_SZ + DTLS_RECORD_HEADER_SZ;
|
|
idx += DTLS_HANDSHAKE_EXTRA + DTLS_RECORD_EXTRA;
|
|
}
|
|
#endif
|
|
|
|
/* check for avalaible size */
|
|
if ((ret = CheckAvalaibleSize(ssl, sendSz)) != 0)
|
|
return ret;
|
|
|
|
/* get ouput buffer */
|
|
output = ssl->buffers.outputBuffer.buffer +
|
|
ssl->buffers.outputBuffer.idx;
|
|
|
|
AddHeaders(output, length, client_hello, ssl);
|
|
|
|
/* client hello, first version */
|
|
XMEMCPY(output + idx, &ssl->version, sizeof(ProtocolVersion));
|
|
idx += sizeof(ProtocolVersion);
|
|
ssl->chVersion = ssl->version; /* store in case changed */
|
|
|
|
/* then random */
|
|
if (ssl->options.connectState == CONNECT_BEGIN) {
|
|
RNG_GenerateBlock(&ssl->rng, output + idx, RAN_LEN);
|
|
|
|
/* store random */
|
|
XMEMCPY(ssl->arrays.clientRandom, output + idx, RAN_LEN);
|
|
} else {
|
|
#ifdef CYASSL_DTLS
|
|
/* send same random on hello again */
|
|
XMEMCPY(output + idx, ssl->arrays.clientRandom, RAN_LEN);
|
|
#endif
|
|
}
|
|
idx += RAN_LEN;
|
|
|
|
/* then session id */
|
|
output[idx++] = idSz;
|
|
if (idSz) {
|
|
XMEMCPY(output + idx, ssl->session.sessionID, ID_LEN);
|
|
idx += ID_LEN;
|
|
}
|
|
|
|
/* then DTLS cookie */
|
|
#ifdef CYASSL_DTLS
|
|
if (ssl->options.dtls) {
|
|
output[idx++] = 0;
|
|
}
|
|
#endif
|
|
/* then cipher suites */
|
|
c16toa(ssl->suites.suiteSz, output + idx);
|
|
idx += 2;
|
|
XMEMCPY(output + idx, &ssl->suites.suites, ssl->suites.suiteSz);
|
|
idx += ssl->suites.suiteSz;
|
|
|
|
/* last, compression */
|
|
output[idx++] = COMP_LEN;
|
|
if (ssl->options.usingCompression)
|
|
output[idx++] = ZLIB_COMPRESSION;
|
|
else
|
|
output[idx++] = NO_COMPRESSION;
|
|
|
|
HashOutput(ssl, output, sendSz, 0);
|
|
|
|
ssl->options.clientState = CLIENT_HELLO_COMPLETE;
|
|
|
|
#ifdef CYASSL_CALLBACKS
|
|
if (ssl->hsInfoOn) AddPacketName("ClientHello", &ssl->handShakeInfo);
|
|
if (ssl->toInfoOn)
|
|
AddPacketInfo("ClientHello", &ssl->timeoutInfo, output, sendSz,
|
|
ssl->heap);
|
|
#endif
|
|
|
|
ssl->buffers.outputBuffer.length += sendSz;
|
|
|
|
return SendBuffered(ssl);
|
|
}
|
|
|
|
|
|
static int DoHelloVerifyRequest(SSL* ssl, const byte* input,
|
|
word32* inOutIdx)
|
|
{
|
|
ProtocolVersion pv;
|
|
byte cookieSz;
|
|
|
|
#ifdef CYASSL_CALLBACKS
|
|
if (ssl->hsInfoOn) AddPacketName("HelloVerifyRequest",
|
|
&ssl->handShakeInfo);
|
|
if (ssl->toInfoOn) AddLateName("HelloVerifyRequest", &ssl->timeoutInfo);
|
|
#endif
|
|
XMEMCPY(&pv, input + *inOutIdx, sizeof(pv));
|
|
*inOutIdx += sizeof(pv);
|
|
|
|
cookieSz = input[(*inOutIdx)++];
|
|
|
|
if (cookieSz)
|
|
*inOutIdx += cookieSz; /* skip for now */
|
|
|
|
ssl->options.serverState = SERVER_HELLOVERIFYREQUEST_COMPLETE;
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int DoServerHello(SSL* ssl, const byte* input, word32* inOutIdx)
|
|
{
|
|
byte b;
|
|
byte compression;
|
|
ProtocolVersion pv;
|
|
word32 i = *inOutIdx;
|
|
|
|
#ifdef CYASSL_CALLBACKS
|
|
if (ssl->hsInfoOn) AddPacketName("ServerHello", &ssl->handShakeInfo);
|
|
if (ssl->toInfoOn) AddLateName("ServerHello", &ssl->timeoutInfo);
|
|
#endif
|
|
XMEMCPY(&pv, input + i, sizeof(pv));
|
|
i += sizeof(pv);
|
|
XMEMCPY(ssl->arrays.serverRandom, input + i, RAN_LEN);
|
|
i += RAN_LEN;
|
|
b = input[i++];
|
|
if (b) {
|
|
XMEMCPY(ssl->arrays.sessionID, input + i, b);
|
|
i += b;
|
|
}
|
|
ssl->options.cipherSuite0 = input[i++];
|
|
ssl->options.cipherSuite = input[i++];
|
|
compression = input[i++];
|
|
|
|
if (compression != ZLIB_COMPRESSION && ssl->options.usingCompression)
|
|
ssl->options.usingCompression = 0; /* turn off if server refused */
|
|
|
|
ssl->options.serverState = SERVER_HELLO_COMPLETE;
|
|
|
|
*inOutIdx = i;
|
|
|
|
if (ssl->options.resuming) {
|
|
if (XMEMCMP(ssl->arrays.sessionID, ssl->session.sessionID, ID_LEN)
|
|
== 0) {
|
|
if (SetCipherSpecs(ssl) == 0) {
|
|
int ret;
|
|
XMEMCPY(ssl->arrays.masterSecret, ssl->session.masterSecret,
|
|
SECRET_LEN);
|
|
if (ssl->options.tls)
|
|
ret = DeriveTlsKeys(ssl);
|
|
else
|
|
ret = DeriveKeys(ssl);
|
|
ssl->options.serverState = SERVER_HELLODONE_COMPLETE;
|
|
return ret;
|
|
}
|
|
else
|
|
return UNSUPPORTED_SUITE;
|
|
}
|
|
else
|
|
ssl->options.resuming = 0; /* server denied resumption try */
|
|
}
|
|
|
|
return SetCipherSpecs(ssl);
|
|
}
|
|
|
|
|
|
/* just read in and ignore for now TODO: */
|
|
static int DoCertificateRequest(SSL* ssl, const byte* input, word32*
|
|
inOutIdx)
|
|
{
|
|
word16 len;
|
|
|
|
#ifdef CYASSL_CALLBACKS
|
|
if (ssl->hsInfoOn)
|
|
AddPacketName("CertificateRequest", &ssl->handShakeInfo);
|
|
if (ssl->toInfoOn)
|
|
AddLateName("CertificateRequest", &ssl->timeoutInfo);
|
|
#endif
|
|
len = input[(*inOutIdx)++];
|
|
|
|
/* types, read in here */
|
|
*inOutIdx += len;
|
|
ato16(&input[*inOutIdx], &len);
|
|
*inOutIdx += LENGTH_SZ;
|
|
|
|
if (IsAtLeastTLSv1_2(ssl)) {
|
|
/* hash sig format */
|
|
*inOutIdx += len;
|
|
ato16(&input[*inOutIdx], &len);
|
|
*inOutIdx += LENGTH_SZ;
|
|
}
|
|
|
|
/* authorities */
|
|
while (len) {
|
|
word16 dnSz;
|
|
|
|
ato16(&input[*inOutIdx], &dnSz);
|
|
*inOutIdx += (REQUEST_HEADER + dnSz);
|
|
len -= dnSz + REQUEST_HEADER;
|
|
}
|
|
|
|
/* don't send client cert or cert verify if user hasn't provided
|
|
cert and private key */
|
|
if (ssl->buffers.certificate.buffer && ssl->buffers.key.buffer)
|
|
ssl->options.sendVerify = SEND_CERT;
|
|
else if (IsAtLeastTLSv1_2(ssl))
|
|
ssl->options.sendVerify = SEND_BLANK_CERT;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int DoServerKeyExchange(SSL* ssl, const byte* input, word32*
|
|
inOutIdx)
|
|
{
|
|
word16 sigLen;
|
|
word16 verifySz;
|
|
word16 length;
|
|
byte* signature;
|
|
|
|
sigLen = 0;
|
|
signature = 0;
|
|
|
|
/* keep start idx */
|
|
verifySz = *inOutIdx;
|
|
|
|
#ifdef CYASSL_CALLBACKS
|
|
if (ssl->hsInfoOn)
|
|
AddPacketName("ServerKeyExchange", &ssl->handShakeInfo);
|
|
if (ssl->toInfoOn)
|
|
AddLateName("ServerKeyExchange", &ssl->timeoutInfo);
|
|
#endif
|
|
|
|
#ifndef NO_PSK
|
|
if (ssl->specs.kea == psk_kea) {
|
|
ato16(&input[*inOutIdx], &length);
|
|
*inOutIdx += LENGTH_SZ;
|
|
XMEMCPY(ssl->arrays.server_hint, &input[*inOutIdx],
|
|
min(length, MAX_PSK_ID_LEN));
|
|
if (length < MAX_PSK_ID_LEN)
|
|
ssl->arrays.server_hint[length] = 0;
|
|
else
|
|
ssl->arrays.server_hint[MAX_PSK_ID_LEN - 1] = 0;
|
|
*inOutIdx += length;
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
#ifdef OPENSSL_EXTRA
|
|
if (ssl->specs.kea == diffie_hellman_kea)
|
|
{
|
|
/* p */
|
|
ato16(&input[*inOutIdx], &length);
|
|
*inOutIdx += LENGTH_SZ;
|
|
|
|
ssl->buffers.serverDH_P.buffer = (byte*) XMALLOC(length, ssl->heap,
|
|
DYNAMIC_TYPE_DH);
|
|
if (ssl->buffers.serverDH_P.buffer)
|
|
ssl->buffers.serverDH_P.length = length;
|
|
else
|
|
return MEMORY_ERROR;
|
|
XMEMCPY(ssl->buffers.serverDH_P.buffer, &input[*inOutIdx], length);
|
|
*inOutIdx += length;
|
|
|
|
/* g */
|
|
ato16(&input[*inOutIdx], &length);
|
|
*inOutIdx += LENGTH_SZ;
|
|
|
|
ssl->buffers.serverDH_G.buffer = (byte*) XMALLOC(length, ssl->heap,
|
|
DYNAMIC_TYPE_DH);
|
|
if (ssl->buffers.serverDH_G.buffer)
|
|
ssl->buffers.serverDH_G.length = length;
|
|
else
|
|
return MEMORY_ERROR;
|
|
XMEMCPY(ssl->buffers.serverDH_G.buffer, &input[*inOutIdx], length);
|
|
*inOutIdx += length;
|
|
|
|
/* pub */
|
|
ato16(&input[*inOutIdx], &length);
|
|
*inOutIdx += LENGTH_SZ;
|
|
|
|
ssl->buffers.serverDH_Pub.buffer = (byte*) XMALLOC(length, ssl->heap,
|
|
DYNAMIC_TYPE_DH);
|
|
if (ssl->buffers.serverDH_Pub.buffer)
|
|
ssl->buffers.serverDH_Pub.length = length;
|
|
else
|
|
return MEMORY_ERROR;
|
|
XMEMCPY(ssl->buffers.serverDH_Pub.buffer, &input[*inOutIdx], length);
|
|
*inOutIdx += length;
|
|
} /* dh_kea */
|
|
#endif /* OPENSSL_EXTRA */
|
|
|
|
#ifdef HAVE_ECC
|
|
if (ssl->specs.kea == ecc_diffie_hellman_kea)
|
|
{
|
|
byte b = input[*inOutIdx];
|
|
*inOutIdx += 1;
|
|
|
|
if (b != named_curve)
|
|
return ECC_CURVETYPE_ERROR;
|
|
|
|
*inOutIdx += 1; /* curve type, eat leading 0 */
|
|
b = input[*inOutIdx];
|
|
*inOutIdx += 1;
|
|
|
|
if (b != secp256r1 && b != secp384r1 && b != secp521r1 && b !=
|
|
secp160r1 && b != secp192r1 && b != secp224r1)
|
|
return ECC_CURVE_ERROR;
|
|
|
|
length = input[*inOutIdx];
|
|
*inOutIdx += 1;
|
|
|
|
if (ecc_import_x963(&input[*inOutIdx], length, &ssl->peerEccKey) != 0)
|
|
return ECC_PEERKEY_ERROR;
|
|
|
|
*inOutIdx += length;
|
|
ssl->peerEccKeyPresent = 1;
|
|
}
|
|
#endif /* HAVE_ECC */
|
|
|
|
#if defined(OPENSSL_EXTRA) || defined(HAVE_ECC)
|
|
{
|
|
Md5 md5;
|
|
Sha sha;
|
|
byte hash[FINISHED_SZ];
|
|
byte messageVerify[MAX_DH_SZ];
|
|
|
|
/* adjust from start idx */
|
|
verifySz = *inOutIdx - verifySz;
|
|
|
|
/* save message for hash verify */
|
|
if (verifySz > sizeof(messageVerify))
|
|
return BUFFER_ERROR;
|
|
XMEMCPY(messageVerify, &input[*inOutIdx - verifySz], verifySz);
|
|
|
|
if (IsAtLeastTLSv1_2(ssl)) {
|
|
/* just advance for now TODO: validate hash algo params */
|
|
*inOutIdx += LENGTH_SZ;
|
|
}
|
|
|
|
/* signature */
|
|
ato16(&input[*inOutIdx], &length);
|
|
*inOutIdx += LENGTH_SZ;
|
|
|
|
signature = (byte*)&input[*inOutIdx];
|
|
*inOutIdx += length;
|
|
sigLen = length;
|
|
|
|
/* verify signature */
|
|
|
|
/* md5 */
|
|
InitMd5(&md5);
|
|
Md5Update(&md5, ssl->arrays.clientRandom, RAN_LEN);
|
|
Md5Update(&md5, ssl->arrays.serverRandom, RAN_LEN);
|
|
Md5Update(&md5, messageVerify, verifySz);
|
|
Md5Final(&md5, hash);
|
|
|
|
/* sha */
|
|
InitSha(&sha);
|
|
ShaUpdate(&sha, ssl->arrays.clientRandom, RAN_LEN);
|
|
ShaUpdate(&sha, ssl->arrays.serverRandom, RAN_LEN);
|
|
ShaUpdate(&sha, messageVerify, verifySz);
|
|
ShaFinal(&sha, &hash[MD5_DIGEST_SIZE]);
|
|
|
|
/* rsa */
|
|
if (ssl->specs.sig_algo == rsa_sa_algo)
|
|
{
|
|
int ret;
|
|
byte* out;
|
|
|
|
if (!ssl->peerRsaKeyPresent)
|
|
return NO_PEER_KEY;
|
|
|
|
ret = RsaSSL_VerifyInline(signature, sigLen,&out, &ssl->peerRsaKey);
|
|
|
|
if (IsAtLeastTLSv1_2(ssl)) {
|
|
byte encodedSig[MAX_ENCODED_SIG_SZ];
|
|
word32 encSigSz;
|
|
byte* digest;
|
|
int hashType;
|
|
int digestSz;
|
|
|
|
/* sha1 for now */
|
|
digest = &hash[MD5_DIGEST_SIZE];
|
|
hashType = SHAh;
|
|
digestSz = SHA_DIGEST_SIZE;
|
|
|
|
encSigSz = EncodeSignature(encodedSig,digest,digestSz,hashType);
|
|
|
|
if (encSigSz != (word32)ret || XMEMCMP(out, encodedSig,
|
|
encSigSz) != 0)
|
|
return VERIFY_SIGN_ERROR;
|
|
}
|
|
else {
|
|
if (ret != sizeof(hash) || XMEMCMP(out, hash, sizeof(hash)))
|
|
return VERIFY_SIGN_ERROR;
|
|
}
|
|
}
|
|
#ifdef HAVE_ECC
|
|
/* ecdsa */
|
|
else if (ssl->specs.sig_algo == ecc_dsa_sa_algo) {
|
|
int verify = 0, ret;
|
|
if (!ssl->peerEccDsaKeyPresent)
|
|
return NO_PEER_KEY;
|
|
|
|
ret = ecc_verify_hash(signature, sigLen, &hash[MD5_DIGEST_SIZE],
|
|
SHA_DIGEST_SIZE, &verify, &ssl->peerEccDsaKey);
|
|
if (ret != 0 || verify == 0)
|
|
return VERIFY_SIGN_ERROR;
|
|
}
|
|
#endif /* HAVE_ECC */
|
|
else
|
|
return -1;
|
|
|
|
ssl->options.serverState = SERVER_KEYEXCHANGE_COMPLETE;
|
|
|
|
return 0;
|
|
|
|
}
|
|
#endif /* HAVE_OPENSSL or HAVE_ECC */
|
|
return -1; /* not supported by build */
|
|
}
|
|
|
|
|
|
int SendClientKeyExchange(SSL* ssl)
|
|
{
|
|
byte encSecret[MAX_NTRU_ENCRYPT_SZ];
|
|
word32 encSz = 0;
|
|
word32 idx = 0;
|
|
int ret = 0;
|
|
|
|
if (ssl->specs.kea == rsa_kea) {
|
|
RNG_GenerateBlock(&ssl->rng, ssl->arrays.preMasterSecret,
|
|
SECRET_LEN);
|
|
ssl->arrays.preMasterSecret[0] = ssl->chVersion.major;
|
|
ssl->arrays.preMasterSecret[1] = ssl->chVersion.minor;
|
|
ssl->arrays.preMasterSz = SECRET_LEN;
|
|
|
|
if (ssl->peerRsaKeyPresent == 0)
|
|
return NO_PEER_KEY;
|
|
|
|
ret = RsaPublicEncrypt(ssl->arrays.preMasterSecret, SECRET_LEN,
|
|
encSecret, sizeof(encSecret), &ssl->peerRsaKey,
|
|
&ssl->rng);
|
|
if (ret > 0) {
|
|
encSz = ret;
|
|
ret = 0; /* set success to 0 */
|
|
}
|
|
#ifdef OPENSSL_EXTRA
|
|
} else if (ssl->specs.kea == diffie_hellman_kea) {
|
|
buffer serverP = ssl->buffers.serverDH_P;
|
|
buffer serverG = ssl->buffers.serverDH_G;
|
|
buffer serverPub = ssl->buffers.serverDH_Pub;
|
|
byte priv[ENCRYPT_LEN];
|
|
word32 privSz;
|
|
DhKey key;
|
|
|
|
if (serverP.buffer == 0 || serverG.buffer == 0 ||
|
|
serverPub.buffer == 0)
|
|
return NO_PEER_KEY;
|
|
|
|
InitDhKey(&key);
|
|
ret = DhSetKey(&key, serverP.buffer, serverP.length,
|
|
serverG.buffer, serverG.length);
|
|
if (ret == 0)
|
|
/* for DH, encSecret is Yc, agree is pre-master */
|
|
ret = DhGenerateKeyPair(&key, &ssl->rng, priv, &privSz,
|
|
encSecret, &encSz);
|
|
if (ret == 0)
|
|
ret = DhAgree(&key, ssl->arrays.preMasterSecret,
|
|
&ssl->arrays.preMasterSz, priv, privSz,
|
|
serverPub.buffer, serverPub.length);
|
|
FreeDhKey(&key);
|
|
#endif /* OPENSSL_EXTRA */
|
|
#ifndef NO_PSK
|
|
} else if (ssl->specs.kea == psk_kea) {
|
|
byte* pms = ssl->arrays.preMasterSecret;
|
|
|
|
ssl->arrays.psk_keySz = ssl->options.client_psk_cb(ssl,
|
|
ssl->arrays.server_hint, ssl->arrays.client_identity,
|
|
MAX_PSK_ID_LEN, ssl->arrays.psk_key, MAX_PSK_KEY_LEN);
|
|
if (ssl->arrays.psk_keySz == 0 ||
|
|
ssl->arrays.psk_keySz > MAX_PSK_KEY_LEN)
|
|
return PSK_KEY_ERROR;
|
|
encSz = (word32)XSTRLEN(ssl->arrays.client_identity);
|
|
if (encSz > MAX_PSK_ID_LEN) return CLIENT_ID_ERROR;
|
|
XMEMCPY(encSecret, ssl->arrays.client_identity, encSz);
|
|
|
|
/* make psk pre master secret */
|
|
/* length of key + length 0s + length of key + key */
|
|
c16toa((word16)ssl->arrays.psk_keySz, pms);
|
|
pms += 2;
|
|
XMEMSET(pms, 0, ssl->arrays.psk_keySz);
|
|
pms += ssl->arrays.psk_keySz;
|
|
c16toa((word16)ssl->arrays.psk_keySz, pms);
|
|
pms += 2;
|
|
XMEMCPY(pms, ssl->arrays.psk_key, ssl->arrays.psk_keySz);
|
|
ssl->arrays.preMasterSz = ssl->arrays.psk_keySz * 2 + 4;
|
|
#endif /* NO_PSK */
|
|
#ifdef HAVE_NTRU
|
|
} else if (ssl->specs.kea == ntru_kea) {
|
|
word32 rc;
|
|
word16 cipherLen = sizeof(encSecret);
|
|
DRBG_HANDLE drbg;
|
|
static uint8_t const cyasslStr[] = {
|
|
'C', 'y', 'a', 'S', 'S', 'L', ' ', 'N', 'T', 'R', 'U'
|
|
};
|
|
|
|
RNG_GenerateBlock(&ssl->rng, ssl->arrays.preMasterSecret,
|
|
SECRET_LEN);
|
|
ssl->arrays.preMasterSz = SECRET_LEN;
|
|
|
|
if (ssl->peerNtruKeyPresent == 0)
|
|
return NO_PEER_KEY;
|
|
|
|
rc = crypto_drbg_instantiate(MAX_NTRU_BITS, cyasslStr,
|
|
sizeof(cyasslStr), GetEntropy, &drbg);
|
|
if (rc != DRBG_OK)
|
|
return NTRU_DRBG_ERROR;
|
|
|
|
rc = crypto_ntru_encrypt(drbg, ssl->peerNtruKeyLen,ssl->peerNtruKey,
|
|
ssl->arrays.preMasterSz,
|
|
ssl->arrays.preMasterSecret,
|
|
&cipherLen, encSecret);
|
|
crypto_drbg_uninstantiate(drbg);
|
|
if (rc != NTRU_OK)
|
|
return NTRU_ENCRYPT_ERROR;
|
|
|
|
encSz = cipherLen;
|
|
ret = 0;
|
|
#endif /* HAVE_NTRU */
|
|
#ifdef HAVE_ECC
|
|
} else if (ssl->specs.kea == ecc_diffie_hellman_kea) {
|
|
ecc_key myKey;
|
|
word32 size = sizeof(encSecret);
|
|
|
|
if (!ssl->peerEccKeyPresent || !ssl->peerEccKey.dp)
|
|
return NO_PEER_KEY;
|
|
|
|
ecc_init(&myKey);
|
|
ret = ecc_make_key(&ssl->rng, ssl->peerEccKey.dp->size, &myKey);
|
|
if (ret != 0)
|
|
return ECC_MAKEKEY_ERROR;
|
|
|
|
/* precede export with 1 byte length */
|
|
ret = ecc_export_x963(&myKey, encSecret + 1, &size);
|
|
encSecret[0] = size;
|
|
encSz = size + 1;
|
|
|
|
if (ret != 0)
|
|
ret = ECC_EXPORT_ERROR;
|
|
else {
|
|
size = sizeof(ssl->arrays.preMasterSecret);
|
|
ret = ecc_shared_secret(&myKey, &ssl->peerEccKey,
|
|
ssl->arrays.preMasterSecret, &size);
|
|
if (ret != 0)
|
|
ret = ECC_SHARED_ERROR;
|
|
}
|
|
|
|
ssl->arrays.preMasterSz = size;
|
|
ecc_free(&myKey);
|
|
#endif /* HAVE_ECC */
|
|
} else
|
|
return -1; /* unsupported kea */
|
|
|
|
if (ret == 0) {
|
|
byte *output;
|
|
int sendSz;
|
|
word32 tlsSz = 0;
|
|
|
|
if (ssl->options.tls || ssl->specs.kea == diffie_hellman_kea)
|
|
tlsSz = 2;
|
|
|
|
if (ssl->specs.kea == ecc_diffie_hellman_kea) /* always off */
|
|
tlsSz = 0;
|
|
|
|
sendSz = encSz + tlsSz + HANDSHAKE_HEADER_SZ + RECORD_HEADER_SZ;
|
|
idx = HANDSHAKE_HEADER_SZ + RECORD_HEADER_SZ;
|
|
|
|
#ifdef CYASSL_DTLS
|
|
if (ssl->options.dtls) {
|
|
sendSz += DTLS_HANDSHAKE_EXTRA + DTLS_RECORD_EXTRA;
|
|
idx += DTLS_HANDSHAKE_EXTRA + DTLS_RECORD_EXTRA;
|
|
}
|
|
#endif
|
|
|
|
/* check for avalaible size */
|
|
if ((ret = CheckAvalaibleSize(ssl, sendSz)) != 0)
|
|
return ret;
|
|
|
|
/* get ouput buffer */
|
|
output = ssl->buffers.outputBuffer.buffer +
|
|
ssl->buffers.outputBuffer.idx;
|
|
|
|
AddHeaders(output, encSz + tlsSz, client_key_exchange, ssl);
|
|
|
|
if (tlsSz) {
|
|
c16toa((word16)encSz, &output[idx]);
|
|
idx += 2;
|
|
}
|
|
XMEMCPY(output + idx, encSecret, encSz);
|
|
idx += encSz;
|
|
|
|
HashOutput(ssl, output, sendSz, 0);
|
|
|
|
#ifdef CYASSL_CALLBACKS
|
|
if (ssl->hsInfoOn)
|
|
AddPacketName("ClientKeyExchange", &ssl->handShakeInfo);
|
|
if (ssl->toInfoOn)
|
|
AddPacketInfo("ClientKeyExchange", &ssl->timeoutInfo,
|
|
output, sendSz, ssl->heap);
|
|
#endif
|
|
|
|
ssl->buffers.outputBuffer.length += sendSz;
|
|
|
|
ret = SendBuffered(ssl);
|
|
}
|
|
|
|
if (ret == 0 || ret == WANT_WRITE) {
|
|
int tmpRet = MakeMasterSecret(ssl);
|
|
if (tmpRet != 0)
|
|
ret = tmpRet; /* save WANT_WRITE unless more serious */
|
|
ssl->options.clientState = CLIENT_KEYEXCHANGE_COMPLETE;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int SendCertificateVerify(SSL* ssl)
|
|
{
|
|
byte *output;
|
|
int sendSz = 0, length, ret;
|
|
word32 idx = 0;
|
|
RsaKey key;
|
|
|
|
if (ssl->options.sendVerify == SEND_BLANK_CERT)
|
|
return 0; /* sent blank cert, can't verify */
|
|
|
|
/* check for avalaible size */
|
|
if ((ret = CheckAvalaibleSize(ssl, MAX_CERT_VERIFY_SZ)) != 0)
|
|
return ret;
|
|
|
|
/* get ouput buffer */
|
|
output = ssl->buffers.outputBuffer.buffer +
|
|
ssl->buffers.outputBuffer.idx;
|
|
|
|
BuildCertHashes(ssl, &ssl->certHashes);
|
|
|
|
/* TODO: when add DSS support check here */
|
|
InitRsaKey(&key, ssl->heap);
|
|
ret = RsaPrivateKeyDecode(ssl->buffers.key.buffer, &idx, &key,
|
|
ssl->buffers.key.length);
|
|
if (ret == 0) {
|
|
byte* verify = (byte*)&output[RECORD_HEADER_SZ +
|
|
HANDSHAKE_HEADER_SZ];
|
|
byte* signBuffer = ssl->certHashes.md5;
|
|
word32 signSz = sizeof(Hashes);
|
|
byte encodedSig[MAX_ENCODED_SIG_SZ];
|
|
word32 extraSz = 0; /* tls 1.2 hash/sig */
|
|
|
|
#ifdef CYASSL_DTLS
|
|
if (ssl->options.dtls)
|
|
verify += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA;
|
|
#endif
|
|
length = RsaEncryptSize(&key);
|
|
if (IsAtLeastTLSv1_2(ssl)) {
|
|
verify[0] = sha_mac;
|
|
verify[1] = rsa_sa_algo;
|
|
extraSz = HASH_SIG_SIZE;
|
|
}
|
|
c16toa((word16)length, verify + extraSz); /* prepend verify header*/
|
|
|
|
if (IsAtLeastTLSv1_2(ssl)) {
|
|
byte* digest;
|
|
int hashType;
|
|
int digestSz;
|
|
|
|
/* sha1 for now */
|
|
digest = ssl->certHashes.sha;
|
|
hashType = SHAh;
|
|
digestSz = SHA_DIGEST_SIZE;
|
|
|
|
signSz = EncodeSignature(encodedSig, digest, digestSz,hashType);
|
|
signBuffer = encodedSig;
|
|
}
|
|
|
|
ret = RsaSSL_Sign(signBuffer, signSz, verify + extraSz +
|
|
VERIFY_HEADER, ENCRYPT_LEN, &key, &ssl->rng);
|
|
|
|
if (ret > 0) {
|
|
ret = 0; /* reset */
|
|
|
|
AddHeaders(output, length + extraSz + VERIFY_HEADER,
|
|
certificate_verify, ssl);
|
|
|
|
sendSz = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ + length +
|
|
extraSz + VERIFY_HEADER;
|
|
#ifdef CYASSL_DTLS
|
|
if (ssl->options.dtls)
|
|
sendSz += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA;
|
|
#endif
|
|
HashOutput(ssl, output, sendSz, 0);
|
|
}
|
|
}
|
|
|
|
FreeRsaKey(&key);
|
|
|
|
if (ret == 0) {
|
|
#ifdef CYASSL_CALLBACKS
|
|
if (ssl->hsInfoOn)
|
|
AddPacketName("CertificateVerify", &ssl->handShakeInfo);
|
|
if (ssl->toInfoOn)
|
|
AddPacketInfo("CertificateVerify", &ssl->timeoutInfo,
|
|
output, sendSz, ssl->heap);
|
|
#endif
|
|
ssl->buffers.outputBuffer.length += sendSz;
|
|
return SendBuffered(ssl);
|
|
}
|
|
else
|
|
return ret;
|
|
}
|
|
|
|
|
|
|
|
#endif /* NO_CYASSL_CLIENT */
|
|
|
|
|
|
#ifndef NO_CYASSL_SERVER
|
|
|
|
int SendServerHello(SSL* ssl)
|
|
{
|
|
byte *output;
|
|
word32 length, idx = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ;
|
|
int sendSz;
|
|
int ret;
|
|
|
|
length = sizeof(ProtocolVersion) + RAN_LEN
|
|
+ ID_LEN + ENUM_LEN
|
|
+ SUITE_LEN
|
|
+ ENUM_LEN;
|
|
|
|
/* check for avalaible size */
|
|
if ((ret = CheckAvalaibleSize(ssl, MAX_HELLO_SZ)) != 0)
|
|
return ret;
|
|
|
|
/* get ouput buffer */
|
|
output = ssl->buffers.outputBuffer.buffer +
|
|
ssl->buffers.outputBuffer.idx;
|
|
|
|
sendSz = length + HANDSHAKE_HEADER_SZ + RECORD_HEADER_SZ;
|
|
AddHeaders(output, length, server_hello, ssl);
|
|
|
|
#ifdef CYASSL_DTLS
|
|
if (ssl->options.dtls) {
|
|
idx += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA;
|
|
sendSz += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA;
|
|
}
|
|
#endif
|
|
/* now write to output */
|
|
/* first version */
|
|
XMEMCPY(output + idx, &ssl->version, sizeof(ProtocolVersion));
|
|
idx += sizeof(ProtocolVersion);
|
|
|
|
/* then random */
|
|
if (!ssl->options.resuming)
|
|
RNG_GenerateBlock(&ssl->rng, ssl->arrays.serverRandom, RAN_LEN);
|
|
XMEMCPY(output + idx, ssl->arrays.serverRandom, RAN_LEN);
|
|
idx += RAN_LEN;
|
|
|
|
#ifdef SHOW_SECRETS
|
|
{
|
|
int j;
|
|
printf("server random: ");
|
|
for (j = 0; j < RAN_LEN; j++)
|
|
printf("%02x", ssl->arrays.serverRandom[j]);
|
|
printf("\n");
|
|
}
|
|
#endif
|
|
/* then session id */
|
|
output[idx++] = ID_LEN;
|
|
if (!ssl->options.resuming)
|
|
RNG_GenerateBlock(&ssl->rng, ssl->arrays.sessionID, ID_LEN);
|
|
XMEMCPY(output + idx, ssl->arrays.sessionID, ID_LEN);
|
|
idx += ID_LEN;
|
|
|
|
/* then cipher suite */
|
|
output[idx++] = ssl->options.cipherSuite0;
|
|
output[idx++] = ssl->options.cipherSuite;
|
|
|
|
/* last, compression */
|
|
if (ssl->options.usingCompression)
|
|
output[idx++] = ZLIB_COMPRESSION;
|
|
else
|
|
output[idx++] = NO_COMPRESSION;
|
|
|
|
ssl->buffers.outputBuffer.length += sendSz;
|
|
HashOutput(ssl, output, sendSz, 0);
|
|
|
|
#ifdef CYASSL_CALLBACKS
|
|
if (ssl->hsInfoOn)
|
|
AddPacketName("ServerHello", &ssl->handShakeInfo);
|
|
if (ssl->toInfoOn)
|
|
AddPacketInfo("ServerHello", &ssl->timeoutInfo, output, sendSz,
|
|
ssl->heap);
|
|
#endif
|
|
|
|
ssl->options.serverState = SERVER_HELLO_COMPLETE;
|
|
|
|
return SendBuffered(ssl);
|
|
}
|
|
|
|
|
|
#ifdef HAVE_ECC
|
|
|
|
byte SetCurveId(int size)
|
|
{
|
|
switch(size) {
|
|
case 20:
|
|
return secp160r1;
|
|
break;
|
|
case 24:
|
|
return secp192r1;
|
|
break;
|
|
case 28:
|
|
return secp224r1;
|
|
break;
|
|
case 32:
|
|
return secp256r1;
|
|
break;
|
|
case 48:
|
|
return secp384r1;
|
|
break;
|
|
case 66:
|
|
return secp521r1;
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
#endif /* HAVE_ECC */
|
|
|
|
|
|
int SendServerKeyExchange(SSL* ssl)
|
|
{
|
|
int ret = 0;
|
|
|
|
#ifndef NO_PSK
|
|
if (ssl->specs.kea == psk_kea)
|
|
{
|
|
byte *output;
|
|
word32 length, idx = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ;
|
|
int sendSz;
|
|
if (ssl->arrays.server_hint[0] == 0) return 0; /* don't send */
|
|
|
|
/* include size part */
|
|
length = (word32)XSTRLEN(ssl->arrays.server_hint);
|
|
if (length > MAX_PSK_ID_LEN) return SERVER_HINT_ERROR;
|
|
length += HINT_LEN_SZ;
|
|
sendSz = length + HANDSHAKE_HEADER_SZ + RECORD_HEADER_SZ;
|
|
|
|
#ifdef CYASSL_DTLS
|
|
if (ssl->options.dtls) {
|
|
sendSz += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA;
|
|
idx += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA;
|
|
}
|
|
#endif
|
|
/* check for avalaible size */
|
|
if ((ret = CheckAvalaibleSize(ssl, sendSz)) != 0)
|
|
return ret;
|
|
|
|
/* get ouput buffer */
|
|
output = ssl->buffers.outputBuffer.buffer +
|
|
ssl->buffers.outputBuffer.idx;
|
|
|
|
AddHeaders(output, length, server_key_exchange, ssl);
|
|
|
|
/* key data */
|
|
c16toa((word16)(length - HINT_LEN_SZ), output + idx);
|
|
idx += HINT_LEN_SZ;
|
|
XMEMCPY(output + idx, ssl->arrays.server_hint, length -HINT_LEN_SZ);
|
|
|
|
HashOutput(ssl, output, sendSz, 0);
|
|
|
|
#ifdef CYASSL_CALLBACKS
|
|
if (ssl->hsInfoOn)
|
|
AddPacketName("ServerKeyExchange", &ssl->handShakeInfo);
|
|
if (ssl->toInfoOn)
|
|
AddPacketInfo("ServerKeyExchange", &ssl->timeoutInfo,
|
|
output, sendSz, ssl->heap);
|
|
#endif
|
|
|
|
ssl->buffers.outputBuffer.length += sendSz;
|
|
ret = SendBuffered(ssl);
|
|
ssl->options.serverState = SERVER_KEYEXCHANGE_COMPLETE;
|
|
}
|
|
#endif /*NO_PSK */
|
|
|
|
#ifdef HAVE_ECC
|
|
if (ssl->specs.kea == ecc_diffie_hellman_kea)
|
|
{
|
|
byte *output;
|
|
word32 length, idx = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ;
|
|
int sendSz;
|
|
byte export[MAX_EXPORT_ECC_SZ];
|
|
word32 expSz = sizeof(export);
|
|
word32 sigSz;
|
|
word32 preSigSz, preSigIdx;
|
|
RsaKey rsaKey;
|
|
ecc_key dsaKey;
|
|
|
|
/* curve type, named curve, length(1) */
|
|
length = ENUM_LEN + CURVE_LEN + ENUM_LEN;
|
|
/* pub key size */
|
|
if (ecc_export_x963(&ssl->eccTempKey, export, &expSz) != 0)
|
|
return ECC_EXPORT_ERROR;
|
|
length += expSz;
|
|
|
|
preSigSz = length;
|
|
preSigIdx = idx;
|
|
|
|
InitRsaKey(&rsaKey, ssl->heap);
|
|
ecc_init(&dsaKey);
|
|
|
|
/* sig length */
|
|
length += LENGTH_SZ;
|
|
|
|
if (!ssl->buffers.key.buffer) {
|
|
FreeRsaKey(&rsaKey);
|
|
ecc_free(&dsaKey);
|
|
return NO_PRIVATE_KEY;
|
|
}
|
|
|
|
if (ssl->specs.sig_algo == rsa_sa_algo) {
|
|
/* rsa sig size */
|
|
word32 i = 0;
|
|
ret = RsaPrivateKeyDecode(ssl->buffers.key.buffer, &i,
|
|
&rsaKey, ssl->buffers.key.length);
|
|
if (ret != 0) return ret;
|
|
sigSz = RsaEncryptSize(&rsaKey);
|
|
}
|
|
else if (ssl->specs.sig_algo == ecc_dsa_sa_algo) {
|
|
/* ecdsa sig size */
|
|
word32 i = 0;
|
|
ret = EccPrivateKeyDecode(ssl->buffers.key.buffer, &i,
|
|
&dsaKey, ssl->buffers.key.length);
|
|
if (ret != 0) return ret;
|
|
sigSz = ecc_sig_size(&dsaKey);
|
|
}
|
|
else {
|
|
FreeRsaKey(&rsaKey);
|
|
ecc_free(&dsaKey);
|
|
return -1; /* unsupported type */
|
|
}
|
|
length += sigSz;
|
|
|
|
if (IsAtLeastTLSv1_2(ssl))
|
|
length += HASH_SIG_SIZE;
|
|
|
|
sendSz = length + HANDSHAKE_HEADER_SZ + RECORD_HEADER_SZ;
|
|
|
|
#ifdef CYASSL_DTLS
|
|
if (ssl->options.dtls) {
|
|
sendSz += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA;
|
|
idx += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA;
|
|
}
|
|
#endif
|
|
/* check for avalaible size */
|
|
if ((ret = CheckAvalaibleSize(ssl, sendSz)) != 0) {
|
|
FreeRsaKey(&rsaKey);
|
|
ecc_free(&dsaKey);
|
|
return ret;
|
|
}
|
|
|
|
/* get ouput buffer */
|
|
output = ssl->buffers.outputBuffer.buffer +
|
|
ssl->buffers.outputBuffer.idx;
|
|
|
|
AddHeaders(output, length, server_key_exchange, ssl);
|
|
|
|
/* key exchange data */
|
|
output[idx++] = named_curve;
|
|
output[idx++] = 0x00; /* leading zero */
|
|
output[idx++] = SetCurveId(ecc_size(&ssl->eccTempKey));
|
|
output[idx++] = expSz;
|
|
XMEMCPY(output + idx, export, expSz);
|
|
idx += expSz;
|
|
if (IsAtLeastTLSv1_2(ssl)) {
|
|
output[idx++] = sha_mac;
|
|
output[idx++] = ssl->specs.sig_algo;
|
|
}
|
|
c16toa(sigSz, output + idx);
|
|
idx += LENGTH_SZ;
|
|
|
|
/* do signature */
|
|
{
|
|
Md5 md5;
|
|
Sha sha;
|
|
byte hash[FINISHED_SZ];
|
|
byte* signBuffer = hash;
|
|
word32 signSz = sizeof(hash);
|
|
|
|
/* md5 */
|
|
InitMd5(&md5);
|
|
Md5Update(&md5, ssl->arrays.clientRandom, RAN_LEN);
|
|
Md5Update(&md5, ssl->arrays.serverRandom, RAN_LEN);
|
|
Md5Update(&md5, output + preSigIdx, preSigSz);
|
|
Md5Final(&md5, hash);
|
|
|
|
/* sha */
|
|
InitSha(&sha);
|
|
ShaUpdate(&sha, ssl->arrays.clientRandom, RAN_LEN);
|
|
ShaUpdate(&sha, ssl->arrays.serverRandom, RAN_LEN);
|
|
ShaUpdate(&sha, output + preSigIdx, preSigSz);
|
|
ShaFinal(&sha, &hash[MD5_DIGEST_SIZE]);
|
|
|
|
if (ssl->specs.sig_algo == rsa_sa_algo) {
|
|
byte encodedSig[MAX_ENCODED_SIG_SZ];
|
|
if (IsAtLeastTLSv1_2(ssl)) {
|
|
byte* digest;
|
|
int hashType;
|
|
int digestSz;
|
|
|
|
/* sha1 for now */
|
|
digest = &hash[MD5_DIGEST_SIZE];
|
|
hashType = SHAh;
|
|
digestSz = SHA_DIGEST_SIZE;
|
|
|
|
signSz = EncodeSignature(encodedSig, digest, digestSz,
|
|
hashType);
|
|
signBuffer = encodedSig;
|
|
}
|
|
ret = RsaSSL_Sign(signBuffer, signSz, output + idx, sigSz,
|
|
&rsaKey, &ssl->rng);
|
|
FreeRsaKey(&rsaKey);
|
|
if (ret > 0)
|
|
ret = 0; /* reset on success */
|
|
else
|
|
return ret;
|
|
}
|
|
else if (ssl->specs.sig_algo == ecc_dsa_sa_algo) {
|
|
word32 sz = sigSz;
|
|
|
|
FreeRsaKey(&rsaKey);
|
|
ret = ecc_sign_hash(&hash[MD5_DIGEST_SIZE], SHA_DIGEST_SIZE,
|
|
output + idx, &sz, &ssl->rng, &dsaKey);
|
|
}
|
|
}
|
|
|
|
HashOutput(ssl, output, sendSz, 0);
|
|
|
|
#ifdef CYASSL_CALLBACKS
|
|
if (ssl->hsInfoOn)
|
|
AddPacketName("ServerKeyExchange", &ssl->handShakeInfo);
|
|
if (ssl->toInfoOn)
|
|
AddPacketInfo("ServerKeyExchange", &ssl->timeoutInfo,
|
|
output, sendSz, ssl->heap);
|
|
#endif
|
|
|
|
ssl->buffers.outputBuffer.length += sendSz;
|
|
ret = SendBuffered(ssl);
|
|
ssl->options.serverState = SERVER_KEYEXCHANGE_COMPLETE;
|
|
}
|
|
#endif /* HAVE_ECC */
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int MatchSuite(SSL* ssl, Suites* peerSuites)
|
|
{
|
|
word16 i, j;
|
|
|
|
/* & 0x1 equivalent % 2 */
|
|
if (peerSuites->suiteSz == 0 || peerSuites->suiteSz & 0x1)
|
|
return MATCH_SUITE_ERROR;
|
|
|
|
/* start with best, if a match we are good */
|
|
for (i = 0; i < ssl->suites.suiteSz; i += 2)
|
|
for (j = 0; j < peerSuites->suiteSz; j += 2)
|
|
if (ssl->suites.suites[i] == peerSuites->suites[j] &&
|
|
ssl->suites.suites[i+1] == peerSuites->suites[j+1] ) {
|
|
|
|
ssl->options.cipherSuite0 = ssl->suites.suites[i];
|
|
ssl->options.cipherSuite = ssl->suites.suites[i+1];
|
|
return SetCipherSpecs(ssl);
|
|
}
|
|
|
|
return MATCH_SUITE_ERROR;
|
|
}
|
|
|
|
|
|
/* process alert, return level */
|
|
int ProcessOldClientHello(SSL* ssl, const byte* input, word32* inOutIdx,
|
|
word32 inSz, word16 sz)
|
|
{
|
|
word32 idx = *inOutIdx;
|
|
word16 sessionSz;
|
|
word16 randomSz;
|
|
word16 i, j;
|
|
ProtocolVersion pv;
|
|
Suites clSuites;
|
|
|
|
#ifdef CYASSL_CALLBACKS
|
|
if (ssl->hsInfoOn)
|
|
AddPacketName("ClientHello", &ssl->handShakeInfo);
|
|
if (ssl->toInfoOn)
|
|
AddLateName("ClientHello", &ssl->timeoutInfo);
|
|
#endif
|
|
|
|
/* manually hash input since different format */
|
|
Md5Update(&ssl->hashMd5, input + idx, sz);
|
|
ShaUpdate(&ssl->hashSha, input + idx, sz);
|
|
#ifndef NO_SHA256
|
|
if (IsAtLeastTLSv1_2(ssl))
|
|
Sha256Update(&ssl->hashSha256, input + idx, sz);
|
|
#endif
|
|
|
|
/* does this value mean client_hello? */
|
|
idx++;
|
|
|
|
/* version */
|
|
pv.major = input[idx++];
|
|
pv.minor = input[idx++];
|
|
ssl->chVersion = pv; /* store */
|
|
|
|
if (ssl->version.minor > 0 && pv.minor == 0) {
|
|
if (!ssl->options.downgrade)
|
|
return VERSION_ERROR;
|
|
/* turn off tls */
|
|
ssl->options.tls = 0;
|
|
ssl->options.tls1_1 = 0;
|
|
ssl->version.minor = 0;
|
|
InitSuites(&ssl->suites, ssl->version, ssl->options.haveDH, FALSE,
|
|
ssl->options.haveNTRU, ssl->options.haveECDSA,
|
|
ssl->ctx->method->side);
|
|
}
|
|
|
|
/* suite size */
|
|
ato16(&input[idx], &clSuites.suiteSz);
|
|
idx += 2;
|
|
|
|
if (clSuites.suiteSz > MAX_SUITE_SZ)
|
|
return BUFFER_ERROR;
|
|
|
|
/* session size */
|
|
ato16(&input[idx], &sessionSz);
|
|
idx += 2;
|
|
|
|
if (sessionSz > ID_LEN)
|
|
return BUFFER_ERROR;
|
|
|
|
/* random size */
|
|
ato16(&input[idx], &randomSz);
|
|
idx += 2;
|
|
|
|
if (randomSz > RAN_LEN)
|
|
return BUFFER_ERROR;
|
|
|
|
/* suites */
|
|
for (i = 0, j = 0; i < clSuites.suiteSz; i += 3) {
|
|
byte first = input[idx++];
|
|
if (!first) { /* implicit: skip sslv2 type */
|
|
XMEMCPY(&clSuites.suites[j], &input[idx], 2);
|
|
j += 2;
|
|
}
|
|
idx += 2;
|
|
}
|
|
clSuites.suiteSz = j;
|
|
|
|
/* session id */
|
|
if (sessionSz) {
|
|
XMEMCPY(ssl->arrays.sessionID, input + idx, sessionSz);
|
|
idx += sessionSz;
|
|
ssl->options.resuming = 1;
|
|
}
|
|
|
|
/* random */
|
|
if (randomSz < RAN_LEN)
|
|
XMEMSET(ssl->arrays.clientRandom, 0, RAN_LEN - randomSz);
|
|
XMEMCPY(&ssl->arrays.clientRandom[RAN_LEN - randomSz], input + idx,
|
|
randomSz);
|
|
idx += randomSz;
|
|
|
|
if (ssl->options.usingCompression)
|
|
ssl->options.usingCompression = 0; /* turn off */
|
|
|
|
ssl->options.clientState = CLIENT_HELLO_COMPLETE;
|
|
*inOutIdx = idx;
|
|
|
|
/* DoClientHello uses same resume code */
|
|
while (ssl->options.resuming) { /* let's try */
|
|
int ret;
|
|
SSL_SESSION* session = GetSession(ssl, ssl->arrays.masterSecret);
|
|
if (!session) {
|
|
ssl->options.resuming = 0;
|
|
break; /* session lookup failed */
|
|
}
|
|
if (MatchSuite(ssl, &clSuites) < 0)
|
|
return UNSUPPORTED_SUITE;
|
|
|
|
RNG_GenerateBlock(&ssl->rng, ssl->arrays.serverRandom, RAN_LEN);
|
|
if (ssl->options.tls)
|
|
ret = DeriveTlsKeys(ssl);
|
|
else
|
|
ret = DeriveKeys(ssl);
|
|
ssl->options.clientState = CLIENT_KEYEXCHANGE_COMPLETE;
|
|
|
|
return ret;
|
|
}
|
|
|
|
return MatchSuite(ssl, &clSuites);
|
|
}
|
|
|
|
|
|
static int DoClientHello(SSL* ssl, const byte* input, word32* inOutIdx,
|
|
word32 totalSz, word32 helloSz)
|
|
{
|
|
byte b;
|
|
ProtocolVersion pv;
|
|
Suites clSuites;
|
|
word32 i = *inOutIdx;
|
|
word32 begin = i;
|
|
|
|
#ifdef CYASSL_CALLBACKS
|
|
if (ssl->hsInfoOn) AddPacketName("ClientHello", &ssl->handShakeInfo);
|
|
if (ssl->toInfoOn) AddLateName("ClientHello", &ssl->timeoutInfo);
|
|
#endif
|
|
/* make sure can read up to session */
|
|
if (i + sizeof(pv) + RAN_LEN + ENUM_LEN > totalSz)
|
|
return INCOMPLETE_DATA;
|
|
|
|
XMEMCPY(&pv, input + i, sizeof(pv));
|
|
ssl->chVersion = pv; /* store */
|
|
i += sizeof(pv);
|
|
if (ssl->version.minor > 0 && pv.minor == 0) {
|
|
if (!ssl->options.downgrade)
|
|
return VERSION_ERROR;
|
|
/* turn off tls */
|
|
ssl->options.tls = 0;
|
|
ssl->options.tls1_1 = 0;
|
|
ssl->version.minor = 0;
|
|
InitSuites(&ssl->suites, ssl->version, ssl->options.haveDH, FALSE,
|
|
ssl->options.haveNTRU, ssl->options.haveECDSA,
|
|
ssl->ctx->method->side);
|
|
}
|
|
/* random */
|
|
XMEMCPY(ssl->arrays.clientRandom, input + i, RAN_LEN);
|
|
i += RAN_LEN;
|
|
|
|
#ifdef SHOW_SECRETS
|
|
{
|
|
int j;
|
|
printf("client random: ");
|
|
for (j = 0; j < RAN_LEN; j++)
|
|
printf("%02x", ssl->arrays.clientRandom[j]);
|
|
printf("\n");
|
|
}
|
|
#endif
|
|
/* session id */
|
|
b = input[i++];
|
|
if (b) {
|
|
if (i + ID_LEN > totalSz)
|
|
return INCOMPLETE_DATA;
|
|
XMEMCPY(ssl->arrays.sessionID, input + i, ID_LEN);
|
|
i += b;
|
|
ssl->options.resuming= 1; /* client wants to resume */
|
|
}
|
|
|
|
#ifdef CYASSL_DTLS
|
|
/* cookie */
|
|
if (ssl->options.dtls) {
|
|
b = input[i++];
|
|
if (b) {
|
|
if (b > MAX_COOKIE_LEN)
|
|
return BUFFER_ERROR;
|
|
if (i + b > totalSz)
|
|
return INCOMPLETE_DATA;
|
|
XMEMCPY(ssl->arrays.cookie, input + i, b);
|
|
i += b;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (i + LENGTH_SZ > totalSz)
|
|
return INCOMPLETE_DATA;
|
|
/* suites */
|
|
ato16(&input[i], &clSuites.suiteSz);
|
|
i += 2;
|
|
|
|
/* suites and comp len */
|
|
if (i + clSuites.suiteSz + ENUM_LEN > totalSz)
|
|
return INCOMPLETE_DATA;
|
|
if (clSuites.suiteSz > MAX_SUITE_SZ)
|
|
return BUFFER_ERROR;
|
|
XMEMCPY(clSuites.suites, input + i, clSuites.suiteSz);
|
|
i += clSuites.suiteSz;
|
|
|
|
b = input[i++]; /* comp len */
|
|
if (i + b > totalSz)
|
|
return INCOMPLETE_DATA;
|
|
|
|
if (ssl->options.usingCompression) {
|
|
int match = 0;
|
|
while (b--) {
|
|
byte comp = input[i++];
|
|
if (comp == ZLIB_COMPRESSION)
|
|
match = 1;
|
|
}
|
|
if (!match)
|
|
ssl->options.usingCompression = 0; /* turn off */
|
|
}
|
|
else
|
|
i += b; /* ignore, since we're not on */
|
|
|
|
ssl->options.clientState = CLIENT_HELLO_COMPLETE;
|
|
|
|
*inOutIdx = i;
|
|
if ( (i - begin) < helloSz)
|
|
*inOutIdx = begin + helloSz; /* skip extensions */
|
|
|
|
/* ProcessOld uses same resume code */
|
|
while (ssl->options.resuming) { /* let's try */
|
|
int ret;
|
|
SSL_SESSION* session = GetSession(ssl, ssl->arrays.masterSecret);
|
|
if (!session) {
|
|
ssl->options.resuming = 0;
|
|
break; /* session lookup failed */
|
|
}
|
|
if (MatchSuite(ssl, &clSuites) < 0)
|
|
return UNSUPPORTED_SUITE;
|
|
|
|
RNG_GenerateBlock(&ssl->rng, ssl->arrays.serverRandom, RAN_LEN);
|
|
if (ssl->options.tls)
|
|
ret = DeriveTlsKeys(ssl);
|
|
else
|
|
ret = DeriveKeys(ssl);
|
|
ssl->options.clientState = CLIENT_KEYEXCHANGE_COMPLETE;
|
|
|
|
return ret;
|
|
}
|
|
return MatchSuite(ssl, &clSuites);
|
|
}
|
|
|
|
|
|
static int DoCertificateVerify(SSL* ssl, byte* input, word32* inOutsz,
|
|
word32 totalSz)
|
|
{
|
|
word16 sz = 0;
|
|
word32 i = *inOutsz;
|
|
int ret = VERIFY_CERT_ERROR; /* start in error state */
|
|
byte* sig;
|
|
byte* out;
|
|
int outLen;
|
|
|
|
#ifdef CYASSL_CALLBACKS
|
|
if (ssl->hsInfoOn)
|
|
AddPacketName("CertificateVerify", &ssl->handShakeInfo);
|
|
if (ssl->toInfoOn)
|
|
AddLateName("CertificateVerify", &ssl->timeoutInfo);
|
|
#endif
|
|
if ( (i + VERIFY_HEADER) > totalSz)
|
|
return INCOMPLETE_DATA;
|
|
|
|
if (IsAtLeastTLSv1_2(ssl))
|
|
i += HASH_SIG_SIZE;
|
|
ato16(&input[i], &sz);
|
|
i += VERIFY_HEADER;
|
|
|
|
if ( (i + sz) > totalSz)
|
|
return INCOMPLETE_DATA;
|
|
|
|
if (sz > ENCRYPT_LEN)
|
|
return BUFFER_ERROR;
|
|
|
|
sig = &input[i];
|
|
*inOutsz = i + sz;
|
|
/* TODO: when add DSS support check here */
|
|
if (ssl->peerRsaKeyPresent != 0) {
|
|
outLen = RsaSSL_VerifyInline(sig, sz, &out, &ssl->peerRsaKey);
|
|
|
|
if (IsAtLeastTLSv1_2(ssl)) {
|
|
byte encodedSig[MAX_ENCODED_SIG_SZ];
|
|
word32 sigSz;
|
|
byte* digest;
|
|
int hashType;
|
|
int digestSz;
|
|
|
|
/* sha1 for now */
|
|
digest = ssl->certHashes.sha;
|
|
hashType = SHAh;
|
|
digestSz = SHA_DIGEST_SIZE;
|
|
|
|
sigSz = EncodeSignature(encodedSig, digest, digestSz, hashType);
|
|
|
|
if (outLen == (int)sigSz && XMEMCMP(out, encodedSig,sigSz) == 0)
|
|
ret = 0;
|
|
}
|
|
else {
|
|
if (outLen == sizeof(ssl->certHashes) && XMEMCMP(out,
|
|
ssl->certHashes.md5, sizeof(ssl->certHashes)) == 0)
|
|
ret = 0;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
int SendServerHelloDone(SSL* ssl)
|
|
{
|
|
byte *output;
|
|
int sendSz = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ;
|
|
int ret;
|
|
|
|
#ifdef CYASSL_DTLS
|
|
if (ssl->options.dtls)
|
|
sendSz += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA;
|
|
#endif
|
|
/* check for avalaible size */
|
|
if ((ret = CheckAvalaibleSize(ssl, sendSz)) != 0)
|
|
return ret;
|
|
|
|
/* get ouput buffer */
|
|
output = ssl->buffers.outputBuffer.buffer +
|
|
ssl->buffers.outputBuffer.idx;
|
|
|
|
AddHeaders(output, 0, server_hello_done, ssl);
|
|
|
|
HashOutput(ssl, output, sendSz, 0);
|
|
#ifdef CYASSL_CALLBACKS
|
|
if (ssl->hsInfoOn)
|
|
AddPacketName("ServerHelloDone", &ssl->handShakeInfo);
|
|
if (ssl->toInfoOn)
|
|
AddPacketInfo("ServerHelloDone", &ssl->timeoutInfo, output, sendSz,
|
|
ssl->heap);
|
|
#endif
|
|
ssl->options.serverState = SERVER_HELLODONE_COMPLETE;
|
|
|
|
ssl->buffers.outputBuffer.length += sendSz;
|
|
|
|
return SendBuffered(ssl);
|
|
}
|
|
|
|
|
|
int SendHelloVerifyRequest(SSL* ssl)
|
|
{
|
|
byte* output;
|
|
int length = VERSION_SZ + ENUM_LEN;
|
|
int idx = DTLS_RECORD_HEADER_SZ + DTLS_HANDSHAKE_HEADER_SZ;
|
|
int sendSz = length + idx;
|
|
int ret;
|
|
|
|
/* check for avalaible size */
|
|
if ((ret = CheckAvalaibleSize(ssl, sendSz)) != 0)
|
|
return ret;
|
|
|
|
/* get ouput buffer */
|
|
output = ssl->buffers.outputBuffer.buffer +
|
|
ssl->buffers.outputBuffer.idx;
|
|
|
|
AddHeaders(output, length, hello_verify_request, ssl);
|
|
|
|
XMEMCPY(output + idx, &ssl->chVersion, VERSION_SZ);
|
|
idx += VERSION_SZ;
|
|
output[idx++] = 0; /* no cookie for now */
|
|
|
|
HashOutput(ssl, output, sendSz, 0);
|
|
#ifdef CYASSL_CALLBACKS
|
|
if (ssl->hsInfoOn)
|
|
AddPacketName("HelloVerifyRequest", &ssl->handShakeInfo);
|
|
if (ssl->toInfoOn)
|
|
AddPacketInfo("HelloVerifyRequest", &ssl->timeoutInfo, output,
|
|
sendSz, ssl->heap);
|
|
#endif
|
|
ssl->options.serverState = SERVER_HELLOVERIFYREQUEST_COMPLETE;
|
|
|
|
ssl->buffers.outputBuffer.length += sendSz;
|
|
|
|
return SendBuffered(ssl);
|
|
}
|
|
|
|
|
|
static int DoClientKeyExchange(SSL* ssl, byte* input,
|
|
word32* inOutIdx)
|
|
{
|
|
int ret = 0;
|
|
word32 length = 0;
|
|
byte* out;
|
|
|
|
if (ssl->options.verifyPeer && ssl->options.failNoCert)
|
|
if (!ssl->options.havePeerCert) {
|
|
CYASSL_MSG("client didn't present peer cert");
|
|
return NO_PEER_CERT;
|
|
}
|
|
|
|
#ifdef CYASSL_CALLBACKS
|
|
if (ssl->hsInfoOn)
|
|
AddPacketName("ClientKeyExchange", &ssl->handShakeInfo);
|
|
if (ssl->toInfoOn)
|
|
AddLateName("ClientKeyExchange", &ssl->timeoutInfo);
|
|
#endif
|
|
if (ssl->specs.kea == rsa_kea) {
|
|
word32 idx = 0;
|
|
RsaKey key;
|
|
byte* tmp = 0;
|
|
|
|
InitRsaKey(&key, ssl->heap);
|
|
|
|
if (ssl->buffers.key.buffer)
|
|
ret = RsaPrivateKeyDecode(ssl->buffers.key.buffer, &idx, &key,
|
|
ssl->buffers.key.length);
|
|
else
|
|
return NO_PRIVATE_KEY;
|
|
|
|
if (ret == 0) {
|
|
length = RsaEncryptSize(&key);
|
|
ssl->arrays.preMasterSz = SECRET_LEN;
|
|
|
|
if (ssl->options.tls)
|
|
(*inOutIdx) += 2;
|
|
tmp = input + *inOutIdx;
|
|
*inOutIdx += length;
|
|
|
|
if (RsaPrivateDecryptInline(tmp, length, &out, &key) ==
|
|
SECRET_LEN) {
|
|
XMEMCPY(ssl->arrays.preMasterSecret, out, SECRET_LEN);
|
|
if (ssl->arrays.preMasterSecret[0] != ssl->chVersion.major
|
|
||
|
|
ssl->arrays.preMasterSecret[1] != ssl->chVersion.minor)
|
|
|
|
ret = PMS_VERSION_ERROR;
|
|
else
|
|
ret = MakeMasterSecret(ssl);
|
|
}
|
|
else
|
|
ret = RSA_PRIVATE_ERROR;
|
|
}
|
|
|
|
FreeRsaKey(&key);
|
|
#ifndef NO_PSK
|
|
} else if (ssl->specs.kea == psk_kea) {
|
|
byte* pms = ssl->arrays.preMasterSecret;
|
|
word16 ci_sz;
|
|
|
|
ato16(&input[*inOutIdx], &ci_sz);
|
|
*inOutIdx += LENGTH_SZ;
|
|
if (ci_sz > MAX_PSK_ID_LEN) return CLIENT_ID_ERROR;
|
|
|
|
XMEMCPY(ssl->arrays.client_identity, &input[*inOutIdx], ci_sz);
|
|
*inOutIdx += ci_sz;
|
|
ssl->arrays.client_identity[ci_sz] = 0;
|
|
|
|
ssl->arrays.psk_keySz = ssl->options.server_psk_cb(ssl,
|
|
ssl->arrays.client_identity, ssl->arrays.psk_key,
|
|
MAX_PSK_KEY_LEN);
|
|
if (ssl->arrays.psk_keySz == 0 ||
|
|
ssl->arrays.psk_keySz > MAX_PSK_KEY_LEN) return PSK_KEY_ERROR;
|
|
|
|
/* make psk pre master secret */
|
|
/* length of key + length 0s + length of key + key */
|
|
c16toa((word16)ssl->arrays.psk_keySz, pms);
|
|
pms += 2;
|
|
XMEMSET(pms, 0, ssl->arrays.psk_keySz);
|
|
pms += ssl->arrays.psk_keySz;
|
|
c16toa((word16)ssl->arrays.psk_keySz, pms);
|
|
pms += 2;
|
|
XMEMCPY(pms, ssl->arrays.psk_key, ssl->arrays.psk_keySz);
|
|
ssl->arrays.preMasterSz = ssl->arrays.psk_keySz * 2 + 4;
|
|
|
|
ret = MakeMasterSecret(ssl);
|
|
#endif /* NO_PSK */
|
|
#ifdef HAVE_NTRU
|
|
} else if (ssl->specs.kea == ntru_kea) {
|
|
word32 rc;
|
|
word16 cipherLen;
|
|
word16 plainLen = sizeof(ssl->arrays.preMasterSecret);
|
|
byte* tmp;
|
|
|
|
if (!ssl->buffers.key.buffer)
|
|
return NO_PRIVATE_KEY;
|
|
|
|
ato16(&input[*inOutIdx], &cipherLen);
|
|
*inOutIdx += LENGTH_SZ;
|
|
if (cipherLen > MAX_NTRU_ENCRYPT_SZ)
|
|
return NTRU_KEY_ERROR;
|
|
|
|
tmp = input + *inOutIdx;
|
|
rc = crypto_ntru_decrypt((word16)ssl->buffers.key.length,
|
|
ssl->buffers.key.buffer, cipherLen, tmp, &plainLen,
|
|
ssl->arrays.preMasterSecret);
|
|
|
|
if (rc != NTRU_OK || plainLen != SECRET_LEN)
|
|
return NTRU_DECRYPT_ERROR;
|
|
*inOutIdx += cipherLen;
|
|
|
|
ssl->arrays.preMasterSz = plainLen;
|
|
ret = MakeMasterSecret(ssl);
|
|
#endif /* HAVE_NTRU */
|
|
#ifdef HAVE_ECC
|
|
} else if (ssl->specs.kea == ecc_diffie_hellman_kea) {
|
|
word32 size;
|
|
word32 length = input[*inOutIdx]; /* one byte length */
|
|
*inOutIdx += 1;
|
|
|
|
ret = ecc_import_x963(&input[*inOutIdx], length, &ssl->peerEccKey);
|
|
if (ret != 0)
|
|
return ECC_PEERKEY_ERROR;
|
|
*inOutIdx += length;
|
|
ssl->peerEccKeyPresent = 1;
|
|
|
|
size = sizeof(ssl->arrays.preMasterSecret);
|
|
ret = ecc_shared_secret(&ssl->eccTempKey, &ssl->peerEccKey,
|
|
ssl->arrays.preMasterSecret, &size);
|
|
if (ret != 0)
|
|
return ECC_SHARED_ERROR;
|
|
ssl->arrays.preMasterSz = size;
|
|
ret = MakeMasterSecret(ssl);
|
|
#endif /* HAVE_ECC */
|
|
}
|
|
|
|
if (ret == 0) {
|
|
ssl->options.clientState = CLIENT_KEYEXCHANGE_COMPLETE;
|
|
if (ssl->options.verifyPeer)
|
|
BuildCertHashes(ssl, &ssl->certHashes);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
#endif /* NO_CYASSL_SERVER */
|
|
|
|
|
|
#ifdef SINGLE_THREADED
|
|
|
|
int InitMutex(CyaSSL_Mutex* m)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
|
|
int FreeMutex(CyaSSL_Mutex* m)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
|
|
int LockMutex(CyaSSL_Mutex* m)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
|
|
int UnLockMutex(CyaSSL_Mutex* m)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
#else /* MULTI_THREAD */
|
|
|
|
#ifdef USE_WINDOWS_API
|
|
|
|
int InitMutex(CyaSSL_Mutex* m)
|
|
{
|
|
InitializeCriticalSection(m);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int FreeMutex(CyaSSL_Mutex* m)
|
|
{
|
|
DeleteCriticalSection(m);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int LockMutex(CyaSSL_Mutex* m)
|
|
{
|
|
EnterCriticalSection(m);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int UnLockMutex(CyaSSL_Mutex* m)
|
|
{
|
|
LeaveCriticalSection(m);
|
|
return 0;
|
|
}
|
|
|
|
#elif defined(CYASSL_PTHREADS)
|
|
|
|
int InitMutex(CyaSSL_Mutex* m)
|
|
{
|
|
if (pthread_mutex_init(m, 0) == 0)
|
|
return 0;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
|
|
int FreeMutex(CyaSSL_Mutex* m)
|
|
{
|
|
if (pthread_mutex_destroy(m) == 0)
|
|
return 0;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
|
|
int LockMutex(CyaSSL_Mutex* m)
|
|
{
|
|
if (pthread_mutex_lock(m) == 0)
|
|
return 0;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
|
|
int UnLockMutex(CyaSSL_Mutex* m)
|
|
{
|
|
if (pthread_mutex_unlock(m) == 0)
|
|
return 0;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
#elif defined(THREADX)
|
|
|
|
int InitMutex(CyaSSL_Mutex* m)
|
|
{
|
|
if (tx_mutex_create(m, "CyaSSL Mutex", TX_NO_INHERIT) == 0)
|
|
return 0;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
|
|
int FreeMutex(CyaSSL_Mutex* m)
|
|
{
|
|
if (tx_mutex_delete(m) == 0)
|
|
return 0;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
|
|
int LockMutex(CyaSSL_Mutex* m)
|
|
{
|
|
if (tx_mutex_get(m, TX_WAIT_FOREVER) == 0)
|
|
return 0;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
|
|
int UnLockMutex(CyaSSL_Mutex* m)
|
|
{
|
|
if (tx_mutex_put(m) == 0)
|
|
return 0;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
#elif defined(MICRIUM)
|
|
|
|
int InitMutex(CyaSSL_Mutex* m)
|
|
{
|
|
#if (NET_SECURE_MGR_CFG_EN == DEF_ENABLED)
|
|
if (NetSecure_OS_MutexCreate(m) == 0)
|
|
return 0;
|
|
else
|
|
return -1;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
|
|
int FreeMutex(CyaSSL_Mutex* m)
|
|
{
|
|
#if (NET_SECURE_MGR_CFG_EN == DEF_ENABLED)
|
|
if (NetSecure_OS_FreeMutex(m) == 0)
|
|
return 0;
|
|
else
|
|
return -1;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
|
|
int LockMutex(CyaSSL_Mutex* m)
|
|
{
|
|
#if (NET_SECURE_MGR_CFG_EN == DEF_ENABLED)
|
|
if (NetSecure_OS_LockMutex(m) == 0)
|
|
return 0;
|
|
else
|
|
return -1;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
|
|
int UnLockMutex(CyaSSL_Mutex* m)
|
|
{
|
|
#if (NET_SECURE_MGR_CFG_EN == DEF_ENABLED)
|
|
if (NetSecure_OS_UnLockMutex(m) == 0)
|
|
return 0;
|
|
else
|
|
return -1;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
|
|
}
|
|
|
|
#endif /* USE_WINDOWS_API */
|
|
#endif /* SINGLE_THREADED */
|
|
|
|
|
|
#ifdef DEBUG_CYASSL
|
|
|
|
static int logging = 0;
|
|
|
|
|
|
int CyaSSL_Debugging_ON(void)
|
|
{
|
|
logging = 1;
|
|
return 0;
|
|
}
|
|
|
|
|
|
void CyaSSL_Debugging_OFF(void)
|
|
{
|
|
logging = 0;
|
|
}
|
|
|
|
|
|
#ifdef THREADX
|
|
int dc_log_printf(char*, ...);
|
|
#endif
|
|
|
|
void CYASSL_MSG(const char* msg)
|
|
{
|
|
if (logging) {
|
|
#ifdef THREADX
|
|
dc_log_printf("%s\n", msg);
|
|
#elif defined(MICRIUM)
|
|
#if (NET_SECURE_MGR_CFG_EN == DEF_ENABLED)
|
|
NetSecure_TraceOut((CPU_CHAR *)msg);
|
|
#endif
|
|
#else
|
|
fprintf(stderr, "%s\n", msg);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
void CYASSL_ENTER(const char* msg)
|
|
{
|
|
if (logging) {
|
|
char buffer[80];
|
|
sprintf(buffer, "CyaSSL Entering %s", msg);
|
|
CYASSL_MSG(buffer);
|
|
}
|
|
}
|
|
|
|
|
|
void CYASSL_LEAVE(const char* msg, int ret)
|
|
{
|
|
if (logging) {
|
|
char buffer[80];
|
|
sprintf(buffer, "CyaSSL Leaving %s, return %d", msg, ret);
|
|
CYASSL_MSG(buffer);
|
|
}
|
|
}
|
|
|
|
|
|
void CYASSL_ERROR(int error)
|
|
{
|
|
if (logging) {
|
|
char buffer[80];
|
|
sprintf(buffer, "CyaSSL error occured, error = %d", error);
|
|
CYASSL_MSG(buffer);
|
|
}
|
|
}
|
|
|
|
|
|
#else /* DEBUG_CYASSL */
|
|
|
|
int CyaSSL_Debugging_ON(void)
|
|
{
|
|
return -1; /* not compiled in */
|
|
}
|
|
|
|
|
|
void CyaSSL_Debugging_OFF(void)
|
|
{
|
|
/* already off */
|
|
}
|
|
|
|
#endif /* DEBUG_CYASSL */
|