cya_u
Fork of CyaSSL-forEncrypt by
cyassl_int.c
- Committer:
- vbahl2
- Date:
- 2017-05-10
- Revision:
- 2:d0516dc143b1
- Parent:
- 0:5045d2638c29
File content as of revision 2:d0516dc143b1:
/* cyassl_int.c * * Copyright (C) 2006-2009 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 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 void INLINE 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->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 /* remove DH later if server didn't set, add psk later */ InitSuites(&ctx->suites, method->version, TRUE, FALSE, ctx->haveNTRU); 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) { word32 idx = 0; int tls = pv.major == 3 && pv.minor >= 1; (void)tls; /* shut up compiler */ #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) { 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) { 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) { 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) { suites->suites[idx++] = 0; suites->suites[idx++] = TLS_NTRU_RSA_WITH_3DES_EDE_CBC_SHA; } #endif #ifdef BUILD_TLS_DHE_RSA_WITH_AES_256_CBC_SHA if (tls && haveDH) { 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) { suites->suites[idx++] = 0; suites->suites[idx++] = TLS_DHE_RSA_WITH_AES_128_CBC_SHA; } #endif #ifdef BUILD_TLS_RSA_WITH_AES_256_CBC_SHA if (tls) { 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) { 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 suites->suites[idx++] = 0; suites->suites[idx++] = SSL_RSA_WITH_RC4_128_SHA; #endif #ifdef BUILD_SSL_RSA_WITH_RC4_128_MD5 suites->suites[idx++] = 0; suites->suites[idx++] = SSL_RSA_WITH_RC4_128_MD5; #endif #ifdef BUILD_SSL_RSA_WITH_3DES_EDE_CBC_SHA 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) { 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) { 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) { 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); 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.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 /* 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); else InitSuites(&ssl->suites, ssl->version, TRUE, havePSK, ssl->options.haveNTRU); } 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 } 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); } /* 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); } /* 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; if (ssl->options.tls) BuildTlsFinished(ssl, hashes, sender); else { BuildMD5(ssl, hashes, sender); BuildSHA(ssl, hashes, sender); } /* restore */ ssl->hashMd5 = md5; ssl->hashSha = sha; } static int DoCertificate(SSL* ssl, byte* input, word32* inOutIdx) { word32 listSz, i = *inOutIdx; int ret = 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 && ret == 0) { /* 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; #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); #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 FreeDecodedCert(&dCert); } 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[SHA_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[SHA_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 byte decomp[MAX_RECORD_SIZE + MAX_COMP_EXTRA]; 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[SHA_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 = 1500; /* 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; byte b0, b1; #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[SHA_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; 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; } /* 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 (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; 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; 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 }; /* 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 }; /* 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) { ctx->suites.suites[idx++] = 0x00; /* first byte always zero */ 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]) { 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.cipherSuite = input[++i]; compression = input[++i]; ++i; /* 2nd byte */ 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) { XMEMCPY(ssl->arrays.masterSecret, ssl->session.masterSecret, SECRET_LEN); if (ssl->options.tls) DeriveTlsKeys(ssl); else DeriveKeys(ssl); ssl->options.serverState = SERVER_HELLODONE_COMPLETE; return 0; } 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; /* 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) { #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) { word16 length; 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) { word16 length, verifySz, messageTotal = 6; /* pSz + gSz + pubSz */ byte messageVerify[MAX_DH_SZ]; byte* signature; byte hash[FINISHED_SZ]; Md5 md5; Sha sha; /* p */ ato16(&input[*inOutIdx], &length); *inOutIdx += LENGTH_SZ; messageTotal += length; 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; messageTotal += length; 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; messageTotal += length; 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; /* save message for hash verify */ if (messageTotal > sizeof(messageVerify)) return BUFFER_ERROR; XMEMCPY(messageVerify, &input[*inOutIdx - messageTotal], messageTotal); verifySz = messageTotal; /* signature */ ato16(&input[*inOutIdx], &length); *inOutIdx += LENGTH_SZ; signature = (byte*)&input[*inOutIdx]; *inOutIdx += 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 for now */ { int ret; byte* out; if (!ssl->peerRsaKeyPresent) return NO_PEER_KEY; ret = RsaSSL_VerifyInline(signature, length,&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 = &hash[MD5_DIGEST_SIZE]; hashType = SHAh; digestSz = SHA_DIGEST_SIZE; sigSz = EncodeSignature(encodedSig, digest, digestSz, hashType); if (sigSz != ret || XMEMCMP(out, encodedSig, sigSz) != 0) return VERIFY_SIGN_ERROR; } else { if (ret != sizeof(hash) || XMEMCMP(out, hash, sizeof(hash))) return VERIFY_SIGN_ERROR; } } ssl->options.serverState = SERVER_KEYEXCHANGE_COMPLETE; return 0; } /* dh_kea */ #endif /* OPENSSL_EXTRA */ 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 */ } 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; 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); #ifdef CYASSL_DTLS if (ssl->options.dtls) verify += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA; #endif length = RsaEncryptSize(&key); c16toa((word16)length, verify); /* prepend verify header */ if (IsAtLeastTLSv1_2(ssl)) { byte encodedSig[MAX_ENCODED_SIG_SZ]; 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 + VERIFY_HEADER, ENCRYPT_LEN, &key, &ssl->rng); if (ret > 0) { ret = 0; /* reset */ AddHeaders(output, length + VERIFY_HEADER, certificate_verify, ssl); sendSz = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ + length + 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++] = 0x00; 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); } int SendServerKeyExchange(SSL* ssl) { int ret = 0; if (ssl->specs.kea != psk_kea) return 0; #ifndef NO_PSK { 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 */ 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, Ciphers are at odd index since all SSL and TLS ciphers have 0x00 first byte */ for (i = 1; i < ssl->suites.suiteSz; i += 2) for (j = 1; j < peerSuites->suiteSz; j += 2) if (ssl->suites.suites[i] == peerSuites->suites[j]) { ssl->options.cipherSuite = ssl->suites.suites[i]; 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); /* 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); } /* 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 */ 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) DeriveTlsKeys(ssl); else DeriveKeys(ssl); ssl->options.clientState = CLIENT_KEYEXCHANGE_COMPLETE; return 0; } 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); } /* 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 */ 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) DeriveTlsKeys(ssl); else DeriveKeys(ssl); ssl->options.clientState = CLIENT_KEYEXCHANGE_COMPLETE; return 0; } 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; 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 == 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 */ } 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 */