blob: ff73e0bfdec08edc312732d30592c248c7e9b618 [file] [log] [blame] [edit]
/* internal.c
*
* Copyright (C) 2006-2012 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
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <cyassl/internal.h>
#include <cyassl/error.h>
#include <cyassl/ctaocrypt/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
#ifndef NO_CYASSL_CLIENT
static int DoHelloVerifyRequest(CYASSL* ssl, const byte* input, word32*);
static int DoServerHello(CYASSL* ssl, const byte* input, word32*, word32);
static int DoCertificateRequest(CYASSL* ssl, const byte* input, word32*);
static int DoServerKeyExchange(CYASSL* ssl, const byte* input, word32*);
#endif
#ifndef NO_CYASSL_SERVER
static int DoClientHello(CYASSL* ssl, const byte* input, word32*, word32,
word32);
static int DoCertificateVerify(CYASSL* ssl, byte*, word32*, word32);
static int DoClientKeyExchange(CYASSL* ssl, byte* input, word32*);
#endif
typedef enum {
doProcessInit = 0,
#ifndef NO_CYASSL_SERVER
runProcessOldClientHello,
#endif
getRecordLayerHeader,
getData,
runProcessingOneMessage
} processReply;
static void Hmac(CYASSL* ssl, byte* digest, const byte* buffer, word32 sz,
int content, int verify);
static void BuildCertHashes(CYASSL* ssl, Hashes* hashes);
#ifndef min
static INLINE word32 min(word32 a, word32 b)
{
return a > b ? b : a;
}
#endif /* min */
int IsTLS(const CYASSL* ssl)
{
if (ssl->version.major == SSLv3_MAJOR && ssl->version.minor >=TLSv1_MINOR)
return 1;
return 0;
}
int IsAtLeastTLSv1_2(const CYASSL* 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 */
/* used by ssl.c too */
void c32to24(word32 in, word24 out)
{
out[0] = (in >> 16) & 0xff;
out[1] = (in >> 8) & 0xff;
out[2] = in & 0xff;
}
#ifdef CYASSL_DTLS
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;
}
#endif /* CYASSL_DTLS */
/* 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]);
}
#ifdef CYASSL_DTLS
/* 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];
}
#endif /* CYASSL_DTLS */
#ifdef HAVE_LIBZ
/* alloc user allocs to work with zlib */
static void* myAlloc(void* opaque, unsigned int item, unsigned int size)
{
(void)opaque;
return XMALLOC(item * size, opaque, DYNAMIC_TYPE_LIBZ);
}
static void myFree(void* opaque, void* memory)
{
(void)opaque;
XFREE(memory, opaque, DYNAMIC_TYPE_LIBZ);
}
/* init zlib comp/decomp streams, 0 on success */
static int InitStreams(CYASSL* 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, Z_DEFAULT_COMPRESSION) != 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(CYASSL* ssl)
{
if (ssl->didStreamInit) {
deflateEnd(&ssl->c_stream);
inflateEnd(&ssl->d_stream);
}
}
/* compress in to out, return out size or error */
static int Compress(CYASSL* ssl, byte* in, int inSz, byte* out, int outSz)
{
int err;
int currTotal = ssl->c_stream.total_out;
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;
}
/* decompress in to out, returnn out size or error */
static int DeCompress(CYASSL* ssl, byte* in, int inSz, byte* out, int outSz)
{
int err;
int currTotal = ssl->d_stream.total_out;
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(CYASSL_METHOD* method, ProtocolVersion pv)
{
method->version = pv;
method->side = CLIENT_END;
method->downgrade = 0;
}
/* Initialze SSL context, return 0 on success */
int InitSSL_Ctx(CYASSL_CTX* ctx, CYASSL_METHOD* method)
{
ctx->method = method;
ctx->refCount = 1; /* so either CTX_free or SSL_free can release */
ctx->certificate.buffer = 0;
ctx->certChain.buffer = 0;
ctx->privateKey.buffer = 0;
ctx->serverDH_P.buffer = 0;
ctx->serverDH_G.buffer = 0;
ctx->haveDH = 0;
ctx->haveNTRU = 0; /* start off */
ctx->haveECDSAsig = 0; /* start off */
ctx->haveStaticECC = 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 HAVE_ECC
ctx->eccTempKeySz = ECDHE_SIZE;
#endif
#ifdef OPENSSL_EXTRA
ctx->passwd_cb = 0;
ctx->userdata = 0;
#endif /* OPENSSL_EXTRA */
ctx->timeout = DEFAULT_TIMEOUT;
#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->cm = CyaSSL_CertManagerNew();
#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->haveECDSAsig = 1; /* always on cliet side */
ctx->haveStaticECC = 1; /* server can turn on by loading key */
}
#endif
ctx->suites.setSuites = 0; /* user hasn't set yet */
/* remove DH later if server didn't set, add psk later */
InitSuites(&ctx->suites, method->version, TRUE, FALSE, ctx->haveNTRU,
ctx->haveECDSAsig, ctx->haveStaticECC, 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;
ctx->groupMessages = 0;
#ifdef HAVE_OCSP
CyaSSL_OCSP_Init(&ctx->ocsp);
#endif
if (InitMutex(&ctx->countMutex) < 0) {
CYASSL_MSG("Mutex error on CTX init");
return BAD_MUTEX_ERROR;
}
if (ctx->cm == NULL) {
CYASSL_MSG("Bad Cert Manager New");
return BAD_CERT_MANAGER_ERROR;
}
return 0;
}
/* In case contexts are held in array and don't want to free actual ctx */
void SSL_CtxResourceFree(CYASSL_CTX* ctx)
{
XFREE(ctx->serverDH_G.buffer, ctx->heap, DYNAMIC_TYPE_DH);
XFREE(ctx->serverDH_P.buffer, ctx->heap, DYNAMIC_TYPE_DH);
XFREE(ctx->privateKey.buffer, ctx->heap, DYNAMIC_TYPE_KEY);
XFREE(ctx->certificate.buffer, ctx->heap, DYNAMIC_TYPE_CERT);
XFREE(ctx->certChain.buffer, ctx->heap, DYNAMIC_TYPE_CERT);
XFREE(ctx->method, ctx->heap, DYNAMIC_TYPE_METHOD);
CyaSSL_CertManagerFree(ctx->cm);
#ifdef HAVE_OCSP
CyaSSL_OCSP_Cleanup(&ctx->ocsp);
#endif
}
void FreeSSL_Ctx(CYASSL_CTX* ctx)
{
int doFree = 0;
if (LockMutex(&ctx->countMutex) != 0) {
CYASSL_MSG("Couldn't lock count mutex");
return;
}
ctx->refCount--;
if (ctx->refCount == 0)
doFree = 1;
UnLockMutex(&ctx->countMutex);
if (doFree) {
CYASSL_MSG("CTX ref count down to 0, doing full free");
SSL_CtxResourceFree(ctx);
XFREE(ctx, ctx->heap, DYNAMIC_TYPE_CTX);
}
else {
(void)ctx;
CYASSL_MSG("CTX ref count not 0 yet, no free");
}
}
void InitSuites(Suites* suites, ProtocolVersion pv, byte haveDH, byte havePSK,
byte haveNTRU, byte haveECDSAsig, byte haveStaticECC, int side)
{
word16 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;
int haveRSAsig = 1;
(void)tls; /* shut up compiler */
(void)haveDH;
(void)havePSK;
(void)haveNTRU;
(void)haveStaticECC;
if (suites->setSuites)
return; /* trust user settings, don't override */
if (side == SERVER_END && haveStaticECC)
haveRSA = 0; /* can't do RSA with ECDSA key */
if (side == SERVER_END && haveECDSAsig)
haveRSAsig = 0; /* can't have RSA sig if signed by ECDSA */
#ifdef CYASSL_DTLS
if (pv.major == DTLS_MAJOR && pv.minor == DTLS_MINOR)
tls = 1;
#endif
#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_GCM_SHA384
if (tls1_2 && haveStaticECC) {
suites->suites[idx++] = ECC_BYTE;
suites->suites[idx++] = TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384;
}
#endif
#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
if (tls && haveStaticECC) {
suites->suites[idx++] = ECC_BYTE;
suites->suites[idx++] = TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA;
}
#endif
#ifdef BUILD_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384
if (tls1_2 && haveECDSAsig && haveStaticECC) {
suites->suites[idx++] = ECC_BYTE;
suites->suites[idx++] = TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384;
}
#endif
#ifdef BUILD_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA
if (tls && haveECDSAsig && haveStaticECC) {
suites->suites[idx++] = ECC_BYTE;
suites->suites[idx++] = TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA;
}
#endif
#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
if (tls1_2 && haveStaticECC) {
suites->suites[idx++] = ECC_BYTE;
suites->suites[idx++] = TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256;
}
#endif
#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
if (tls && haveStaticECC) {
suites->suites[idx++] = ECC_BYTE;
suites->suites[idx++] = TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA;
}
#endif
#ifdef BUILD_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256
if (tls1_2 && haveECDSAsig && haveStaticECC) {
suites->suites[idx++] = ECC_BYTE;
suites->suites[idx++] = TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256;
}
#endif
#ifdef BUILD_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA
if (tls && haveECDSAsig && haveStaticECC) {
suites->suites[idx++] = ECC_BYTE;
suites->suites[idx++] = TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA;
}
#endif
#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_RC4_128_SHA
if (tls && haveStaticECC) {
suites->suites[idx++] = ECC_BYTE;
suites->suites[idx++] = TLS_ECDHE_ECDSA_WITH_RC4_128_SHA;
}
#endif
#ifdef BUILD_TLS_ECDH_ECDSA_WITH_RC4_128_SHA
if (tls && haveECDSAsig && haveStaticECC) {
suites->suites[idx++] = ECC_BYTE;
suites->suites[idx++] = TLS_ECDH_ECDSA_WITH_RC4_128_SHA;
}
#endif
#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA
if (tls && haveStaticECC) {
suites->suites[idx++] = ECC_BYTE;
suites->suites[idx++] = TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA;
}
#endif
#ifdef BUILD_TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA
if (tls && haveECDSAsig && haveStaticECC) {
suites->suites[idx++] = ECC_BYTE;
suites->suites[idx++] = TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA;
}
#endif
#ifdef BUILD_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
if (tls1_2 && haveRSA) {
suites->suites[idx++] = ECC_BYTE;
suites->suites[idx++] = TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384;
}
#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_ECDH_RSA_WITH_AES_256_GCM_SHA384
if (tls1_2 && haveRSAsig && haveStaticECC) {
suites->suites[idx++] = ECC_BYTE;
suites->suites[idx++] = TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384;
}
#endif
#ifdef BUILD_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA
if (tls && haveRSAsig && haveStaticECC) {
suites->suites[idx++] = ECC_BYTE;
suites->suites[idx++] = TLS_ECDH_RSA_WITH_AES_256_CBC_SHA;
}
#endif
#ifdef BUILD_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
if (tls1_2 && haveRSA) {
suites->suites[idx++] = ECC_BYTE;
suites->suites[idx++] = TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256;
}
#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_ECDH_RSA_WITH_AES_128_GCM_SHA256
if (tls1_2 && haveRSAsig && haveStaticECC) {
suites->suites[idx++] = ECC_BYTE;
suites->suites[idx++] = TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256;
}
#endif
#ifdef BUILD_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA
if (tls && haveRSAsig && haveStaticECC) {
suites->suites[idx++] = ECC_BYTE;
suites->suites[idx++] = TLS_ECDH_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_ECDH_RSA_WITH_RC4_128_SHA
if (tls && haveRSAsig && haveStaticECC) {
suites->suites[idx++] = ECC_BYTE;
suites->suites[idx++] = TLS_ECDH_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_ECDH_RSA_WITH_3DES_EDE_CBC_SHA
if (tls && haveRSAsig && haveStaticECC) {
suites->suites[idx++] = ECC_BYTE;
suites->suites[idx++] = TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA;
}
#endif
#ifdef BUILD_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
if (tls1_2 && haveDH && haveRSA) {
suites->suites[idx++] = 0;
suites->suites[idx++] = TLS_DHE_RSA_WITH_AES_256_GCM_SHA384;
}
#endif
#ifdef BUILD_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
if (tls1_2 && haveDH && haveRSA) {
suites->suites[idx++] = 0;
suites->suites[idx++] = TLS_DHE_RSA_WITH_AES_256_CBC_SHA256;
}
#endif
#ifdef BUILD_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
if (tls1_2 && haveDH && haveRSA) {
suites->suites[idx++] = 0;
suites->suites[idx++] = TLS_DHE_RSA_WITH_AES_128_GCM_SHA256;
}
#endif
#ifdef BUILD_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
if (tls1_2 && haveDH && haveRSA) {
suites->suites[idx++] = 0;
suites->suites[idx++] = TLS_DHE_RSA_WITH_AES_128_CBC_SHA256;
}
#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_GCM_SHA384
if (tls1_2 && haveRSA) {
suites->suites[idx++] = 0;
suites->suites[idx++] = TLS_RSA_WITH_AES_256_GCM_SHA384;
}
#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_GCM_SHA256
if (tls1_2 && haveRSA) {
suites->suites[idx++] = 0;
suites->suites[idx++] = TLS_RSA_WITH_AES_128_GCM_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;
}
/* init everything to 0, NULL, default values before calling anything that may
fail so that desctructor has a "good" state to cleanup */
int InitSSL(CYASSL* ssl, CYASSL_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.certChain.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;
#ifdef OPENSSL_EXTRA
ssl->peerCert.derCert.buffer = NULL;
ssl->peerCert.altNames = NULL;
ssl->peerCert.altNamesNext = NULL;
#endif
#ifdef HAVE_ECC
ssl->eccTempKeySz = ctx->eccTempKeySz;
ssl->peerEccKeyPresent = 0;
ssl->peerEccDsaKeyPresent = 0;
ssl->eccDsaKeyPresent = 0;
ssl->eccTempKeyPresent = 0;
ecc_init(&ssl->peerEccKey);
ecc_init(&ssl->peerEccDsaKey);
ecc_init(&ssl->eccDsaKey);
ecc_init(&ssl->eccTempKey);
#endif
ssl->timeout = ctx->timeout;
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 */
InitMd5(&ssl->hashMd5);
InitSha(&ssl->hashSha);
#ifndef NO_SHA256
InitSha256(&ssl->hashSha256);
#endif
#ifdef CYASSL_SHA384
InitSha384(&ssl->hashSha384);
#endif
InitRsaKey(&ssl->peerRsaKey, ctx->heap);
ssl->verifyCallback = ctx->verifyCallback;
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;
if (ssl->options.side == SERVER_END)
ssl->options.haveDH = ctx->haveDH;
else
ssl->options.haveDH = 0;
ssl->options.haveNTRU = ctx->haveNTRU;
ssl->options.haveECDSAsig = ctx->haveECDSAsig;
ssl->options.haveStaticECC = ctx->haveStaticECC;
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;
ssl->arrays.cookieSz = 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->options.haveSessionId = 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->options.certOnly = 0;
ssl->options.groupMessages = ctx->groupMessages;
/* ctx still owns certificate, certChain, key, dh, and cm */
ssl->buffers.certificate = ctx->certificate;
ssl->buffers.certChain = ctx->certChain;
ssl->buffers.key = ctx->privateKey;
if (ssl->options.side == SERVER_END) {
ssl->buffers.serverDH_P = ctx->serverDH_P;
ssl->buffers.serverDH_G = ctx->serverDH_G;
}
ssl->buffers.weOwnCert = 0;
ssl->buffers.weOwnKey = 0;
ssl->buffers.weOwnDH = 0;
#ifdef OPENSSL_EXTRA
ssl->peerCert.issuer.sz = 0;
ssl->peerCert.subject.sz = 0;
#endif
#ifdef SESSION_CERTS
ssl->session.chain.count = 0;
#endif
ssl->cipher.ssl = ssl;
#ifdef FORTRESS
ssl->ex_data[0] = 0;
ssl->ex_data[1] = 0;
ssl->ex_data[2] = 0;
#endif
#ifdef CYASSL_CALLBACKS
ssl->hsInfoOn = 0;
ssl->toInfoOn = 0;
#endif
#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);
ssl->arrays.server_hint[MAX_PSK_ID_LEN - 1] = '\0';
}
else
ssl->arrays.server_hint[0] = 0;
#endif /* NO_PSK */
/* all done with init, now can return errors, call other stuff */
/* increment CTX reference count */
if (LockMutex(&ctx->countMutex) != 0) {
CYASSL_MSG("Couldn't lock CTX count mutex");
return BAD_MUTEX_ERROR;
}
ctx->refCount++;
UnLockMutex(&ctx->countMutex);
if ( (ret = InitRng(&ssl->rng)) != 0)
return ret;
/* 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) {
CYASSL_MSG("Server missing certificate and/or private key");
return NO_PRIVATE_KEY;
}
/* make sure server has DH parms, and add PSK if there, add NTRU too */
if (ssl->options.side == SERVER_END)
InitSuites(&ssl->suites, ssl->version,ssl->options.haveDH, havePSK,
ssl->options.haveNTRU, ssl->options.haveECDSAsig,
ssl->options.haveStaticECC, ssl->options.side);
else
InitSuites(&ssl->suites, ssl->version, TRUE, havePSK,
ssl->options.haveNTRU, ssl->options.haveECDSAsig,
ssl->options.haveStaticECC, ssl->options.side);
return 0;
}
/* In case holding SSL object in array and don't want to free actual ssl */
void SSL_ResourceFree(CYASSL* ssl)
{
XFREE(ssl->buffers.serverDH_Priv.buffer, ssl->heap, DYNAMIC_TYPE_DH);
XFREE(ssl->buffers.serverDH_Pub.buffer, ssl->heap, DYNAMIC_TYPE_DH);
/* parameters (p,g) may be owned by ctx */
if (ssl->buffers.weOwnDH || ssl->options.side == CLIENT_END) {
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);
/* CYASSL_CTX always owns certChain */
if (ssl->buffers.weOwnCert)
XFREE(ssl->buffers.certificate.buffer, ssl->heap, DYNAMIC_TYPE_CERT);
if (ssl->buffers.weOwnKey)
XFREE(ssl->buffers.key.buffer, ssl->heap, DYNAMIC_TYPE_KEY);
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)
XFREE(ssl->peerCert.derCert.buffer, ssl->heap, DYNAMIC_TYPE_CERT);
if (ssl->peerCert.altNames)
FreeAltNames(ssl->peerCert.altNames, ssl->heap);
CyaSSL_BIO_free(ssl->biord);
if (ssl->biord != ssl->biowr) /* in case same as write */
CyaSSL_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(CYASSL* ssl)
{
FreeSSL_Ctx(ssl->ctx); /* will decrement and free underyling CTX if 0 */
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(CYASSL* ssl, const byte* output, int sz, int ivSz)
{
const byte* adj = output + RECORD_HEADER_SZ + ivSz;
sz -= RECORD_HEADER_SZ;
#ifdef CYASSL_DTLS
if (ssl->options.dtls) {
adj += DTLS_RECORD_EXTRA;
sz -= DTLS_RECORD_EXTRA;
}
#endif
Md5Update(&ssl->hashMd5, adj, sz);
ShaUpdate(&ssl->hashSha, adj, sz);
if (IsAtLeastTLSv1_2(ssl)) {
#ifndef NO_SHA256
Sha256Update(&ssl->hashSha256, adj, sz);
#endif
#ifdef CYASSL_SHA384
Sha384Update(&ssl->hashSha384, adj, sz);
#endif
}
}
/* add input to md5 and sha handshake hashes, include handshake header */
static void HashInput(CYASSL* ssl, const byte* input, int sz)
{
const byte* adj = input - HANDSHAKE_HEADER_SZ;
sz += HANDSHAKE_HEADER_SZ;
#ifdef CYASSL_DTLS
if (ssl->options.dtls) {
adj -= DTLS_HANDSHAKE_EXTRA;
sz += DTLS_HANDSHAKE_EXTRA;
}
#endif
Md5Update(&ssl->hashMd5, adj, sz);
ShaUpdate(&ssl->hashSha, adj, sz);
if (IsAtLeastTLSv1_2(ssl)) {
#ifndef NO_SHA256
Sha256Update(&ssl->hashSha256, adj, sz);
#endif
#ifdef CYASSL_SHA384
Sha384Update(&ssl->hashSha384, adj, sz);
#endif
}
}
/* add record layer header for message */
static void AddRecordHeader(byte* output, word32 length, byte type, CYASSL* 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,
CYASSL* ssl)
{
HandShakeHeader* hs;
(void)ssl;
/* 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, CYASSL* ssl)
{
if (!ssl->options.dtls) {
AddRecordHeader(output, length + HANDSHAKE_HEADER_SZ, handshake, ssl);
AddHandShakeHeader(output + RECORD_HEADER_SZ, length, type, ssl);
}
#ifdef CYASSL_DTLS
else {
AddRecordHeader(output, length+DTLS_HANDSHAKE_HEADER_SZ, handshake,ssl);
AddHandShakeHeader(output + DTLS_RECORD_HEADER_SZ, length, type, ssl);
}
#endif
}
/* return bytes received, -1 on error */
static int Receive(CYASSL* ssl, byte* buf, word32 sz)
{
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);
CYASSL_MSG("Got our timeout");
return WANT_READ;
}
}
#endif
goto retry;
case IO_ERR_CONN_CLOSE: /* peer closed connection */
ssl->options.isClosed = 1;
return -1;
default:
return recvd;
}
return recvd;
}
/* Switch dynamic output buffer back to static, buffer is assumed clear */
void ShrinkOutputBuffer(CYASSL* 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(CYASSL* 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(CYASSL* 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);
CYASSL_MSG("Got our timeout");
return WANT_WRITE;
}
}
#endif
continue;
case IO_ERR_CONN_CLOSE: /* epipe / conn closed, same as reset */
ssl->options.connReset = 1;
break;
default:
return SOCKET_ERROR_E;
}
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 */
static INLINE int GrowOutputBuffer(CYASSL* 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 MEMORY_E;
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(CYASSL* 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 MEMORY_E;
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 output buffer, make room if needed */
static INLINE int CheckAvalaibleSize(CYASSL *ssl, int size)
{
if (ssl->buffers.outputBuffer.bufferSize - ssl->buffers.outputBuffer.length
< (word32)size) {
if (GrowOutputBuffer(ssl, size) < 0)
return MEMORY_E;
}
return 0;
}
/* do all verify and sanity checks on record header */
static int GetRecordHeader(CYASSL* 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.acceptState == ACCEPT_BEGIN)
CYASSL_MSG("Client attempting to connect with different version");
else if (ssl->options.side == CLIENT_END && ssl->options.downgrade &&
ssl->options.connectState < FIRST_REPLY_DONE)
CYASSL_MSG("Server attempting to accept with different version");
else {
CYASSL_MSG("SSL version error");
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;
case no_type:
default:
CYASSL_MSG("Unknown Record Type");
return UNKNOWN_RECORD_TYPE;
}
return 0;
}
static int GetHandShakeHeader(CYASSL* ssl, const byte* input, word32* inOutIdx,
byte *type, word32 *size)
{
const byte *ptr = input + *inOutIdx;
(void)ssl;
*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(CYASSL* 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(CYASSL* 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(CYASSL* 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;
#endif
#ifdef CYASSL_SHA384
Sha384 sha384;
#endif
#ifndef NO_SHA256
InitSha256(&sha256);
if (IsAtLeastTLSv1_2(ssl))
sha256 = ssl->hashSha256;
#endif
#ifdef CYASSL_SHA384
InitSha384(&sha384);
if (IsAtLeastTLSv1_2(ssl))
sha384 = ssl->hashSha384;
#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;
if (IsAtLeastTLSv1_2(ssl)) {
#ifndef NO_SHA256
ssl->hashSha256 = sha256;
#endif
#ifdef CYASSL_SHA384
ssl->hashSha384 = sha384;
#endif
}
}
static int DoCertificate(CYASSL* ssl, byte* input, word32* inOutIdx)
{
word32 listSz, i = *inOutIdx;
int ret = 0;
int anyError = 0;
int totalCerts = 0; /* number of certs in certs buffer */
int count;
char domain[ASN_NAME_MAX];
buffer certs[MAX_CHAIN_DEPTH];
#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;
CYASSL_MSG("Loading peer's cert chain");
/* first put cert chain into buffer so can verify top down
we're sent bottom up */
while (listSz) {
/* cert size */
word32 certSz;
if (totalCerts >= MAX_CHAIN_DEPTH)
return MAX_CHAIN_ERROR;
c24to32(&input[i], &certSz);
i += CERT_HEADER_SZ;
if (listSz > MAX_RECORD_SIZE || certSz > MAX_RECORD_SIZE)
return BUFFER_E;
certs[totalCerts].length = certSz;
certs[totalCerts].buffer = input + i;
#ifdef SESSION_CERTS
if (ssl->session.chain.count < MAX_CHAIN_DEPTH &&
certSz < MAX_X509_SIZE) {
ssl->session.chain.certs[ssl->session.chain.count].length = certSz;
XMEMCPY(ssl->session.chain.certs[ssl->session.chain.count].buffer,
input + i, certSz);
ssl->session.chain.count++;
} else {
CYASSL_MSG("Couldn't store chain cert for session");
}
#endif
i += certSz;
listSz -= certSz + CERT_HEADER_SZ;
totalCerts++;
CYASSL_MSG(" Put another cert into chain");
}
count = totalCerts;
/* verify up to peer's first */
while (count > 1) {
buffer myCert = certs[count - 1];
DecodedCert dCert;
InitDecodedCert(&dCert, myCert.buffer, myCert.length, ssl->heap);
ret = ParseCertRelative(&dCert, CERT_TYPE, !ssl->options.verifyNone,
ssl->ctx->cm);
if (ret == 0 && dCert.isCA == 0) {
CYASSL_MSG("Chain cert is not a CA, not adding as one");
}
else if (ret == 0 && ssl->options.verifyNone) {
CYASSL_MSG("Chain cert not verified by option, not adding as CA");
}
else if (ret == 0 && !AlreadySigner(ssl->ctx->cm, dCert.subjectHash)) {
buffer add;
add.length = myCert.length;
add.buffer = (byte*)XMALLOC(myCert.length, ssl->heap,
DYNAMIC_TYPE_CA);
CYASSL_MSG("Adding CA from chain");
if (add.buffer == NULL)
return MEMORY_E;
XMEMCPY(add.buffer, myCert.buffer, myCert.length);
ret = AddCA(ssl->ctx->cm, add, CYASSL_CHAIN_CA,
ssl->ctx->verifyPeer);
if (ret == 1) ret = 0; /* SSL_SUCCESS for external */
}
else if (ret != 0) {
CYASSL_MSG("Failed to verify CA from chain");
}
else {
CYASSL_MSG("Verified CA from chain and already had it");
}
#ifdef HAVE_CRL
if (ret == 0 && ssl->ctx->cm->crlEnabled && ssl->ctx->cm->crlCheckAll) {
CYASSL_MSG("Doing Non Leaf CRL check");
ret = CheckCertCRL(ssl->ctx->cm->crl, &dCert);
if (ret != 0) {
CYASSL_MSG("\tCRL check not ok");
}
}
#endif /* HAVE_CRL */
if (ret != 0 && anyError == 0)
anyError = ret; /* save error from last time */
FreeDecodedCert(&dCert);
count--;
}
/* peer's, may not have one if blank client cert sent by TLSv1.2 */
if (count) {
buffer myCert = certs[0];
DecodedCert dCert;
int fatal = 0;
CYASSL_MSG("Veriying Peer's cert");
InitDecodedCert(&dCert, myCert.buffer, myCert.length, ssl->heap);
ret = ParseCertRelative(&dCert, CERT_TYPE, !ssl->options.verifyNone,
ssl->ctx->cm);
if (ret == 0) {
CYASSL_MSG("Verified Peer's cert");
fatal = 0;
}
else if (ret == ASN_PARSE_E) {
CYASSL_MSG("Got Peer cert ASN PARSE ERROR, fatal");
fatal = 1;
}
else {
CYASSL_MSG("Failed to verify Peer's cert");
if (ssl->verifyCallback) {
CYASSL_MSG("\tCallback override availalbe, will continue");
fatal = 0;
}
else {
CYASSL_MSG("\tNo callback override availalbe, fatal");
fatal = 1;
}
}
#ifdef HAVE_OCSP
ret = CyaSSL_OCSP_Lookup_Cert(&ssl->ctx->ocsp, &dCert);
if (ret != 0) {
CYASSL_MSG("\tOCSP Lookup not ok");
fatal = 0;
}
#endif
#ifdef HAVE_CRL
if (fatal == 0 && ssl->ctx->cm->crlEnabled) {
CYASSL_MSG("Doing Leaf CRL check");
ret = CheckCertCRL(ssl->ctx->cm->crl, &dCert);
if (ret != 0) {
CYASSL_MSG("\tCRL check not ok");
fatal = 0;
}
}
#endif /* HAVE_CRL */
#ifdef OPENSSL_EXTRA
/* set X509 format for peer cert even if fatal */
XSTRNCPY(ssl->peerCert.issuer.name, dCert.issuer, ASN_NAME_MAX);
ssl->peerCert.issuer.name[ASN_NAME_MAX - 1] = '\0';
ssl->peerCert.issuer.sz = (int)XSTRLEN(ssl->peerCert.issuer.name) + 1;
XSTRNCPY(ssl->peerCert.subject.name, dCert.subject, ASN_NAME_MAX);
ssl->peerCert.subject.name[ASN_NAME_MAX - 1] = '\0';
ssl->peerCert.subject.sz = (int)XSTRLEN(ssl->peerCert.subject.name) + 1;
XMEMCPY(ssl->peerCert.serial, dCert.serial, EXTERNAL_SERIAL_SIZE);
ssl->peerCert.serialSz = dCert.serialSz;
if (dCert.subjectCNLen < ASN_NAME_MAX) {
XMEMCPY(ssl->peerCert.subjectCN,dCert.subjectCN,dCert.subjectCNLen);
ssl->peerCert.subjectCN[dCert.subjectCNLen] = '\0';
}
else
ssl->peerCert.subjectCN[0] = '\0';
/* store cert for potential retrieval */
ssl->peerCert.derCert.buffer = (byte*)XMALLOC(myCert.length, ssl->heap,
DYNAMIC_TYPE_CERT);
if (ssl->peerCert.derCert.buffer == NULL) {
ret = MEMORY_E;
fatal = 1;
}
else {
XMEMCPY(ssl->peerCert.derCert.buffer, myCert.buffer, myCert.length);
ssl->peerCert.derCert.length = myCert.length;
}
ssl->peerCert.altNames = dCert.altNames;
dCert.altNames = NULL; /* takes ownership */
ssl->peerCert.altNamesNext = ssl->peerCert.altNames; /* index hint */
#endif
if (fatal) {
FreeDecodedCert(&dCert);
ssl->error = ret;
return ret;
}
ssl->options.havePeerCert = 1;
/* store for callback use */
if (dCert.subjectCNLen < ASN_NAME_MAX) {
XMEMCPY(domain, dCert.subjectCN, dCert.subjectCNLen);
domain[dCert.subjectCNLen] = '\0';
}
else
domain[0] = '\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) {
word32 idx = 0;
if (RsaPublicKeyDecode(dCert.publicKey, &idx,
&ssl->peerRsaKey, dCert.pubKeySize) != 0) {
ret = PEER_KEY_ERROR;
}
else
ssl->peerRsaKeyPresent = 1;
}
#ifdef HAVE_NTRU
else if (dCert.keyOID == NTRUk) {
if (dCert.pubKeySize > sizeof(ssl->peerNtruKey)) {
ret = PEER_KEY_ERROR;
}
else {
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;
}
else
ssl->peerEccDsaKeyPresent = 1;
}
#endif /* HAVE_ECC */
FreeDecodedCert(&dCert);
}
if (anyError != 0 && ret == 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->verifyCallback) {
int ok;
CYASSL_X509_STORE_CTX store;
store.error = ret;
store.error_depth = totalCerts;
store.domain = domain;
#ifdef OPENSSL_EXTRA
store.current_cert = &ssl->peerCert;
#else
store.current_cert = NULL;
#endif
#ifdef FORTRESS
store.ex_data = ssl;
#endif
ok = ssl->verifyCallback(0, &store);
if (ok) {
CYASSL_MSG("Verify callback overriding error!");
ret = 0;
}
}
if (ret != 0) {
SendAlert(ssl, alert_fatal, why); /* try to send */
ssl->options.isClosed = 1;
}
}
ssl->error = ret;
}
#ifdef FORTRESS
else {
if (ssl->verifyCallback) {
int ok;
CYASSL_X509_STORE_CTX store;
store.error = ret;
store.error_depth = totalCerts;
store.domain = domain;
store.current_cert = &ssl->peerCert;
store.ex_data = ssl;
ok = ssl->verifyCallback(1, &store);
if (!ok) {
CYASSL_MSG("Verify callback overriding valid certificate!");
ret = -1;
SendAlert(ssl, alert_fatal, bad_certificate);
ssl->options.isClosed = 1;
}
}
}
#endif
*inOutIdx = i;
return ret;
}
static int DoHelloRequest(CYASSL* ssl, const byte* input, word32* inOutIdx)
{
if (ssl->keys.encryptionOn) {
const byte* mac;
int padSz = ssl->keys.encryptSz - HANDSHAKE_HEADER_SZ -
ssl->specs.hash_size;
byte verify[SHA256_DIGEST_SIZE];
ssl->hmac(ssl, verify, input + *inOutIdx - HANDSHAKE_HEADER_SZ,
HANDSHAKE_HEADER_SZ, handshake, 1);
/* read mac and fill */
mac = input + *inOutIdx;
*inOutIdx += ssl->specs.hash_size;
if (ssl->options.tls1_1 && ssl->specs.cipher_type == block)
padSz -= ssl->specs.block_size;
*inOutIdx += padSz;
/* verify */
if (XMEMCMP(mac, verify, ssl->specs.hash_size)) {
CYASSL_MSG(" hello_request verify mac error");
return VERIFY_MAC_ERROR;
}
}
return SendAlert(ssl, alert_warning, no_renegotiation);
}
int DoFinished(CYASSL* 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)) {
CYASSL_MSG("Verify finished error on hashes");
return VERIFY_FINISHED_ERROR;
}
}
if (ssl->specs.cipher_type != aead) {
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)) {
CYASSL_MSG("Verify finished error on mac");
return VERIFY_MAC_ERROR;
}
}
else {
idx += (finishedSz + AEAD_AUTH_TAG_SZ);
}
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(CYASSL* 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) {
case hello_request:
CYASSL_MSG("processing hello request");
ret = DoHelloRequest(ssl, input, inOutIdx);
break;
#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, size);
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:
CYASSL_MSG("Unknown handshake message type");
ret = UNKNOWN_HANDSHAKE_TYPE;
}
CYASSL_LEAVE("DoHandShakeMsg()", ret);
return ret;
}
static INLINE word32 GetSEQIncrement(CYASSL* ssl, int verify)
{
if (verify)
return ssl->keys.peer_sequence_number++;
else
return ssl->keys.sequence_number++;
}
static INLINE void Encrypt(CYASSL* 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_AESGCM
case aes_gcm:
{
byte additional[AES_BLOCK_SIZE];
XMEMSET(additional, 0, AES_BLOCK_SIZE);
/* sequence number field is 64-bits, we only use 32-bits */
c32toa(GetSEQIncrement(ssl, 0),
additional + AEAD_SEQ_OFFSET);
/* Store the type, version. Unfortunately, they are in
* the input buffer ahead of the plaintext. */
XMEMCPY(additional + AEAD_TYPE_OFFSET, input - 5, 3);
/* Store the length of the plain text minus the explicit
* IV length minus the authentication tag size. */
c16toa(sz - AES_GCM_EXP_IV_SZ - AEAD_AUTH_TAG_SZ,
additional + AEAD_LEN_OFFSET);
AesGcmEncrypt(&ssl->encrypt.aes,
out + AES_GCM_EXP_IV_SZ, input + AES_GCM_EXP_IV_SZ,
sz - AES_GCM_EXP_IV_SZ - AEAD_AUTH_TAG_SZ,
out + sz - AEAD_AUTH_TAG_SZ, AEAD_AUTH_TAG_SZ,
additional, AEAD_AUTH_DATA_SZ);
AesGcmIncExpIV(&ssl->encrypt.aes);
}
break;
#endif
#ifdef HAVE_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
default:
CYASSL_MSG("CyaSSL Encrypt programming error");
}
}
static INLINE int Decrypt(CYASSL* 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_AESGCM
case aes_gcm:
{
byte additional[AES_BLOCK_SIZE];
AesGcmSetExpIV(&ssl->decrypt.aes, input);
XMEMSET(additional, 0, AES_BLOCK_SIZE);
/* sequence number field is 64-bits, we only use 32-bits */
c32toa(GetSEQIncrement(ssl, 1), additional + AEAD_SEQ_OFFSET);
additional[AEAD_TYPE_OFFSET] = ssl->curRL.type;
additional[AEAD_VMAJ_OFFSET] = ssl->curRL.version.major;
additional[AEAD_VMIN_OFFSET] = ssl->curRL.version.minor;
c16toa(sz - AES_GCM_EXP_IV_SZ - AEAD_AUTH_TAG_SZ,
additional + AEAD_LEN_OFFSET);
if (AesGcmDecrypt(&ssl->decrypt.aes,
plain + AES_GCM_EXP_IV_SZ,
input + AES_GCM_EXP_IV_SZ,
sz - AES_GCM_EXP_IV_SZ - AEAD_AUTH_TAG_SZ,
input + sz - AEAD_AUTH_TAG_SZ, AEAD_AUTH_TAG_SZ,
additional, AEAD_AUTH_DATA_SZ) < 0) {
SendAlert(ssl, alert_fatal, bad_record_mac);
return VERIFY_MAC_ERROR;
}
break;
}
#endif
#ifdef HAVE_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
default:
CYASSL_MSG("CyaSSL Decrypt programming error");
}
return 0;
}
/* decrypt input message in place */
static int DecryptMessage(CYASSL* ssl, byte* input, word32 sz, word32* idx)
{
int decryptResult = Decrypt(ssl, input, input, sz);
if (decryptResult == 0)
{
ssl->keys.encryptSz = sz;
if (ssl->options.tls1_1 && ssl->specs.cipher_type == block)
*idx += ssl->specs.block_size; /* go past TLSv1.1 IV */
if (ssl->specs.cipher_type == aead)
*idx += AES_GCM_EXP_IV_SZ;
}
return decryptResult;
}
int DoApplicationData(CYASSL* 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;
}
if (ssl->specs.cipher_type == aead) {
ivExtra = AES_GCM_EXP_IV_SZ;
digestSz = AEAD_AUTH_TAG_SZ;
}
dataSz = msgSz - ivExtra - digestSz - pad - padByte;
if (dataSz < 0) {
CYASSL_MSG("App data buffer error, malicious input?");
return BUFFER_ERROR;
}
/* read data */
if (dataSz) {
int rawSz = dataSz; /* keep raw size for hmac */
if (ssl->specs.cipher_type != aead)
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++;
/* verify */
if (dataSz) {
if (ssl->specs.cipher_type != aead && XMEMCMP(mac, verify, digestSz)) {
CYASSL_MSG("App data verify mac error");
return VERIFY_MAC_ERROR;
}
}
else
GetSEQIncrement(ssl, 1); /* even though no data, increment verify */
#ifdef HAVE_LIBZ
/* decompress could be bigger, overwrite after verify */
if (ssl->options.usingCompression)
XMEMMOVE(rawData, decomp, dataSz);
#endif
*inOutIdx = idx;
return 0;
}
/* process alert, return level */
static int DoAlert(CYASSL* 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)++];
CYASSL_MSG("Got alert");
if (*type == close_notify) {
CYASSL_MSG(" close notify");
ssl->options.closeNotify = 1;
}
CYASSL_ERROR(*type);
if (ssl->keys.encryptionOn) {
if (ssl->specs.cipher_type != aead) {
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)) {
CYASSL_MSG(" alert verify mac error");
return VERIFY_MAC_ERROR;
}
}
else {
*inOutIdx += AEAD_AUTH_TAG_SZ;
}
}
return level;
}
static int GetInputData(CYASSL *ssl, word32 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);
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(CYASSL* 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) {
CYASSL_MSG("More messages in record");
#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 {
CYASSL_MSG("More records in input");
ssl->options.processReply = doProcessInit;
continue;
}
default:
CYASSL_MSG("Bad process input state, programming error");
return INPUT_CASE_ERROR;
}
}
}
int SendChangeCipher(CYASSL* 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.length;
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;
if (ssl->options.groupMessages)
return 0;
else
return SendBuffered(ssl);
}
static INLINE const byte* GetMacSecret(CYASSL* 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(CYASSL* ssl, byte* digest, const byte* in, 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] = (byte)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));
/* in buffer */
Md5Update(&md5, in, 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);