| /* internal.c |
| * |
| * Copyright (C) 2006-2012 Sawtooth Consulting Ltd. |
| * |
| * This file is part of CyaSSL. |
| * |
| * CyaSSL is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * CyaSSL is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA |
| */ |
| |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| |
| #include <cyassl/internal.h> |
| #include <cyassl/error.h> |
| #include <cyassl/ctaocrypt/asn.h> |
| |
| #ifdef HAVE_LIBZ |
| #include "zlib.h" |
| #endif |
| |
| #ifdef HAVE_NTRU |
| #include "crypto_ntru.h" |
| #endif |
| |
| #if defined(DEBUG_CYASSL) || defined(SHOW_SECRETS) |
| #include <stdio.h> |
| #endif |
| |
| #ifdef __sun |
| #include <sys/filio.h> |
| #endif |
| |
| #define TRUE 1 |
| #define FALSE 0 |
| |
| |
| #if defined(OPENSSL_EXTRA) && defined(NO_DH) |
| #error OPENSSL_EXTRA needs DH, please remove NO_DH |
| #endif |
| |
| |
| #ifndef NO_CYASSL_CLIENT |
| static int DoHelloVerifyRequest(CYASSL* ssl, const byte* input, word32*); |
| static int DoServerHello(CYASSL* ssl, const byte* input, word32*, word32); |
| static int DoCertificateRequest(CYASSL* ssl, const byte* input, word32*); |
| static int DoServerKeyExchange(CYASSL* ssl, const byte* input, word32*); |
| #endif |
| |
| |
| #ifndef NO_CYASSL_SERVER |
| static int DoClientHello(CYASSL* ssl, const byte* input, word32*, word32, |
| word32); |
| static int DoCertificateVerify(CYASSL* ssl, byte*, word32*, word32); |
| static int DoClientKeyExchange(CYASSL* ssl, byte* input, word32*); |
| #endif |
| |
| typedef enum { |
| doProcessInit = 0, |
| #ifndef NO_CYASSL_SERVER |
| runProcessOldClientHello, |
| #endif |
| getRecordLayerHeader, |
| getData, |
| runProcessingOneMessage |
| } processReply; |
| |
| static void Hmac(CYASSL* ssl, byte* digest, const byte* buffer, word32 sz, |
| int content, int verify); |
| |
| static void BuildCertHashes(CYASSL* ssl, Hashes* hashes); |
| |
| |
| #ifndef min |
| |
| static INLINE word32 min(word32 a, word32 b) |
| { |
| return a > b ? b : a; |
| } |
| |
| #endif /* min */ |
| |
| |
| int IsTLS(const CYASSL* ssl) |
| { |
| if (ssl->version.major == SSLv3_MAJOR && ssl->version.minor >=TLSv1_MINOR) |
| return 1; |
| |
| return 0; |
| } |
| |
| |
| int IsAtLeastTLSv1_2(const CYASSL* ssl) |
| { |
| if (ssl->version.major == SSLv3_MAJOR && ssl->version.minor >=TLSv1_2_MINOR) |
| return 1; |
| |
| return 0; |
| } |
| |
| |
| #ifdef HAVE_NTRU |
| |
| static byte GetEntropy(ENTROPY_CMD cmd, byte* out) |
| { |
| /* TODO: add locking? */ |
| static RNG rng; |
| |
| if (cmd == INIT) { |
| int ret = InitRng(&rng); |
| if (ret == 0) |
| return 1; |
| else |
| return 0; |
| } |
| |
| if (out == NULL) |
| return 0; |
| |
| if (cmd == GET_BYTE_OF_ENTROPY) { |
| RNG_GenerateBlock(&rng, out, 1); |
| return 1; |
| } |
| |
| if (cmd == GET_NUM_BYTES_PER_BYTE_OF_ENTROPY) { |
| *out = 1; |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| #endif /* HAVE_NTRU */ |
| |
| /* used by ssl.c too */ |
| void c32to24(word32 in, word24 out) |
| { |
| out[0] = (in >> 16) & 0xff; |
| out[1] = (in >> 8) & 0xff; |
| out[2] = in & 0xff; |
| } |
| |
| |
| #ifdef CYASSL_DTLS |
| |
| static INLINE void c32to48(word32 in, byte out[6]) |
| { |
| out[0] = 0; |
| out[1] = 0; |
| out[2] = (in >> 24) & 0xff; |
| out[3] = (in >> 16) & 0xff; |
| out[4] = (in >> 8) & 0xff; |
| out[5] = in & 0xff; |
| } |
| |
| #endif /* CYASSL_DTLS */ |
| |
| |
| /* convert 16 bit integer to opaque */ |
| static INLINE void c16toa(word16 u16, byte* c) |
| { |
| c[0] = (u16 >> 8) & 0xff; |
| c[1] = u16 & 0xff; |
| } |
| |
| |
| /* convert 32 bit integer to opaque */ |
| static INLINE void c32toa(word32 u32, byte* c) |
| { |
| c[0] = (u32 >> 24) & 0xff; |
| c[1] = (u32 >> 16) & 0xff; |
| c[2] = (u32 >> 8) & 0xff; |
| c[3] = u32 & 0xff; |
| } |
| |
| |
| /* convert a 24 bit integer into a 32 bit one */ |
| static INLINE void c24to32(const word24 u24, word32* u32) |
| { |
| *u32 = 0; |
| *u32 = (u24[0] << 16) | (u24[1] << 8) | u24[2]; |
| } |
| |
| |
| /* convert opaque to 16 bit integer */ |
| static INLINE void ato16(const byte* c, word16* u16) |
| { |
| *u16 = 0; |
| *u16 = (c[0] << 8) | (c[1]); |
| } |
| |
| |
| #ifdef CYASSL_DTLS |
| |
| /* convert opaque to 32 bit integer */ |
| static INLINE void ato32(const byte* c, word32* u32) |
| { |
| *u32 = 0; |
| *u32 = (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3]; |
| } |
| |
| #endif /* CYASSL_DTLS */ |
| |
| |
| #ifdef HAVE_LIBZ |
| |
| /* alloc user allocs to work with zlib */ |
| static void* myAlloc(void* opaque, unsigned int item, unsigned int size) |
| { |
| (void)opaque; |
| return XMALLOC(item * size, opaque, DYNAMIC_TYPE_LIBZ); |
| } |
| |
| |
| static void myFree(void* opaque, void* memory) |
| { |
| (void)opaque; |
| XFREE(memory, opaque, DYNAMIC_TYPE_LIBZ); |
| } |
| |
| |
| /* init zlib comp/decomp streams, 0 on success */ |
| static int InitStreams(CYASSL* ssl) |
| { |
| ssl->c_stream.zalloc = (alloc_func)myAlloc; |
| ssl->c_stream.zfree = (free_func)myFree; |
| ssl->c_stream.opaque = (voidpf)ssl->heap; |
| |
| if (deflateInit(&ssl->c_stream, Z_DEFAULT_COMPRESSION) != Z_OK) |
| return ZLIB_INIT_ERROR; |
| |
| ssl->didStreamInit = 1; |
| |
| ssl->d_stream.zalloc = (alloc_func)myAlloc; |
| ssl->d_stream.zfree = (free_func)myFree; |
| ssl->d_stream.opaque = (voidpf)ssl->heap; |
| |
| if (inflateInit(&ssl->d_stream) != Z_OK) return ZLIB_INIT_ERROR; |
| |
| return 0; |
| } |
| |
| |
| static void FreeStreams(CYASSL* ssl) |
| { |
| if (ssl->didStreamInit) { |
| deflateEnd(&ssl->c_stream); |
| inflateEnd(&ssl->d_stream); |
| } |
| } |
| |
| |
| /* compress in to out, return out size or error */ |
| static int Compress(CYASSL* ssl, byte* in, int inSz, byte* out, int outSz) |
| { |
| int err; |
| int currTotal = ssl->c_stream.total_out; |
| |
| ssl->c_stream.next_in = in; |
| ssl->c_stream.avail_in = inSz; |
| ssl->c_stream.next_out = out; |
| ssl->c_stream.avail_out = outSz; |
| |
| err = deflate(&ssl->c_stream, Z_SYNC_FLUSH); |
| if (err != Z_OK && err != Z_STREAM_END) return ZLIB_COMPRESS_ERROR; |
| |
| return ssl->c_stream.total_out - currTotal; |
| } |
| |
| |
| /* decompress in to out, returnn out size or error */ |
| static int DeCompress(CYASSL* ssl, byte* in, int inSz, byte* out, int outSz) |
| { |
| int err; |
| int currTotal = ssl->d_stream.total_out; |
| |
| ssl->d_stream.next_in = in; |
| ssl->d_stream.avail_in = inSz; |
| ssl->d_stream.next_out = out; |
| ssl->d_stream.avail_out = outSz; |
| |
| err = inflate(&ssl->d_stream, Z_SYNC_FLUSH); |
| if (err != Z_OK && err != Z_STREAM_END) return ZLIB_DECOMPRESS_ERROR; |
| |
| return ssl->d_stream.total_out - currTotal; |
| } |
| |
| #endif /* HAVE_LIBZ */ |
| |
| |
| void InitSSL_Method(CYASSL_METHOD* method, ProtocolVersion pv) |
| { |
| method->version = pv; |
| method->side = CLIENT_END; |
| method->downgrade = 0; |
| } |
| |
| |
| /* Initialze SSL context, return 0 on success */ |
| int InitSSL_Ctx(CYASSL_CTX* ctx, CYASSL_METHOD* method) |
| { |
| ctx->method = method; |
| ctx->refCount = 1; /* so either CTX_free or SSL_free can release */ |
| ctx->certificate.buffer = 0; |
| ctx->certChain.buffer = 0; |
| ctx->privateKey.buffer = 0; |
| ctx->serverDH_P.buffer = 0; |
| ctx->serverDH_G.buffer = 0; |
| ctx->haveDH = 0; |
| ctx->haveNTRU = 0; /* start off */ |
| ctx->haveECDSAsig = 0; /* start off */ |
| ctx->haveStaticECC = 0; /* start off */ |
| ctx->heap = ctx; /* defaults to self */ |
| #ifndef NO_PSK |
| ctx->havePSK = 0; |
| ctx->server_hint[0] = 0; |
| ctx->client_psk_cb = 0; |
| ctx->server_psk_cb = 0; |
| #endif /* NO_PSK */ |
| #ifdef HAVE_ECC |
| ctx->eccTempKeySz = ECDHE_SIZE; |
| #endif |
| |
| #ifdef OPENSSL_EXTRA |
| ctx->passwd_cb = 0; |
| ctx->userdata = 0; |
| #endif /* OPENSSL_EXTRA */ |
| |
| ctx->timeout = DEFAULT_TIMEOUT; |
| |
| #ifndef CYASSL_USER_IO |
| ctx->CBIORecv = EmbedReceive; |
| ctx->CBIOSend = EmbedSend; |
| #else |
| /* user will set */ |
| ctx->CBIORecv = NULL; |
| ctx->CBIOSend = NULL; |
| #endif |
| ctx->partialWrite = 0; |
| ctx->verifyCallback = 0; |
| |
| ctx->cm = CyaSSL_CertManagerNew(); |
| #ifdef HAVE_NTRU |
| if (method->side == CLIENT_END) |
| ctx->haveNTRU = 1; /* always on cliet side */ |
| /* server can turn on by loading key */ |
| #endif |
| #ifdef HAVE_ECC |
| if (method->side == CLIENT_END) { |
| ctx->haveECDSAsig = 1; /* always on cliet side */ |
| ctx->haveStaticECC = 1; /* server can turn on by loading key */ |
| } |
| #endif |
| ctx->suites.setSuites = 0; /* user hasn't set yet */ |
| /* remove DH later if server didn't set, add psk later */ |
| InitSuites(&ctx->suites, method->version, TRUE, FALSE, ctx->haveNTRU, |
| ctx->haveECDSAsig, ctx->haveStaticECC, method->side); |
| ctx->verifyPeer = 0; |
| ctx->verifyNone = 0; |
| ctx->failNoCert = 0; |
| ctx->sessionCacheOff = 0; /* initially on */ |
| ctx->sessionCacheFlushOff = 0; /* initially on */ |
| ctx->sendVerify = 0; |
| ctx->quietShutdown = 0; |
| ctx->groupMessages = 0; |
| #ifdef HAVE_OCSP |
| CyaSSL_OCSP_Init(&ctx->ocsp); |
| #endif |
| |
| if (InitMutex(&ctx->countMutex) < 0) { |
| CYASSL_MSG("Mutex error on CTX init"); |
| return BAD_MUTEX_ERROR; |
| } |
| if (ctx->cm == NULL) { |
| CYASSL_MSG("Bad Cert Manager New"); |
| return BAD_CERT_MANAGER_ERROR; |
| } |
| return 0; |
| } |
| |
| |
| /* In case contexts are held in array and don't want to free actual ctx */ |
| void SSL_CtxResourceFree(CYASSL_CTX* ctx) |
| { |
| XFREE(ctx->serverDH_G.buffer, ctx->heap, DYNAMIC_TYPE_DH); |
| XFREE(ctx->serverDH_P.buffer, ctx->heap, DYNAMIC_TYPE_DH); |
| XFREE(ctx->privateKey.buffer, ctx->heap, DYNAMIC_TYPE_KEY); |
| XFREE(ctx->certificate.buffer, ctx->heap, DYNAMIC_TYPE_CERT); |
| XFREE(ctx->certChain.buffer, ctx->heap, DYNAMIC_TYPE_CERT); |
| XFREE(ctx->method, ctx->heap, DYNAMIC_TYPE_METHOD); |
| |
| CyaSSL_CertManagerFree(ctx->cm); |
| |
| #ifdef HAVE_OCSP |
| CyaSSL_OCSP_Cleanup(&ctx->ocsp); |
| #endif |
| } |
| |
| |
| void FreeSSL_Ctx(CYASSL_CTX* ctx) |
| { |
| int doFree = 0; |
| |
| if (LockMutex(&ctx->countMutex) != 0) { |
| CYASSL_MSG("Couldn't lock count mutex"); |
| return; |
| } |
| ctx->refCount--; |
| if (ctx->refCount == 0) |
| doFree = 1; |
| UnLockMutex(&ctx->countMutex); |
| |
| if (doFree) { |
| CYASSL_MSG("CTX ref count down to 0, doing full free"); |
| SSL_CtxResourceFree(ctx); |
| XFREE(ctx, ctx->heap, DYNAMIC_TYPE_CTX); |
| } |
| else { |
| (void)ctx; |
| CYASSL_MSG("CTX ref count not 0 yet, no free"); |
| } |
| } |
| |
| |
| void InitSuites(Suites* suites, ProtocolVersion pv, byte haveDH, byte havePSK, |
| byte haveNTRU, byte haveECDSAsig, byte haveStaticECC, int side) |
| { |
| word16 idx = 0; |
| int tls = pv.major == SSLv3_MAJOR && pv.minor >= TLSv1_MINOR; |
| int tls1_2 = pv.major == SSLv3_MAJOR && pv.minor >= TLSv1_2_MINOR; |
| int haveRSA = 1; |
| int haveRSAsig = 1; |
| |
| (void)tls; /* shut up compiler */ |
| (void)haveDH; |
| (void)havePSK; |
| (void)haveNTRU; |
| (void)haveStaticECC; |
| |
| if (suites->setSuites) |
| return; /* trust user settings, don't override */ |
| |
| if (side == SERVER_END && haveStaticECC) |
| haveRSA = 0; /* can't do RSA with ECDSA key */ |
| |
| if (side == SERVER_END && haveECDSAsig) |
| haveRSAsig = 0; /* can't have RSA sig if signed by ECDSA */ |
| |
| #ifdef CYASSL_DTLS |
| if (pv.major == DTLS_MAJOR && pv.minor == DTLS_MINOR) |
| tls = 1; |
| #endif |
| |
| #ifdef BUILD_TLS_NTRU_RSA_WITH_AES_256_CBC_SHA |
| if (tls && haveNTRU && haveRSA) { |
| suites->suites[idx++] = 0; |
| suites->suites[idx++] = TLS_NTRU_RSA_WITH_AES_256_CBC_SHA; |
| } |
| #endif |
| |
| #ifdef BUILD_TLS_NTRU_RSA_WITH_AES_128_CBC_SHA |
| if (tls && haveNTRU && haveRSA) { |
| suites->suites[idx++] = 0; |
| suites->suites[idx++] = TLS_NTRU_RSA_WITH_AES_128_CBC_SHA; |
| } |
| #endif |
| |
| #ifdef BUILD_TLS_NTRU_RSA_WITH_RC4_128_SHA |
| if (tls && haveNTRU && haveRSA) { |
| suites->suites[idx++] = 0; |
| suites->suites[idx++] = TLS_NTRU_RSA_WITH_RC4_128_SHA; |
| } |
| #endif |
| |
| #ifdef BUILD_TLS_NTRU_RSA_WITH_3DES_EDE_CBC_SHA |
| if (tls && haveNTRU && haveRSA) { |
| suites->suites[idx++] = 0; |
| suites->suites[idx++] = TLS_NTRU_RSA_WITH_3DES_EDE_CBC_SHA; |
| } |
| #endif |
| |
| #ifdef BUILD_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 |
| if (tls1_2 && haveStaticECC) { |
| suites->suites[idx++] = ECC_BYTE; |
| suites->suites[idx++] = TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384; |
| } |
| #endif |
| |
| #ifdef BUILD_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA |
| if (tls && haveStaticECC) { |
| suites->suites[idx++] = ECC_BYTE; |
| suites->suites[idx++] = TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA; |
| } |
| #endif |
| |
| #ifdef BUILD_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 |
| if (tls1_2 && haveECDSAsig && haveStaticECC) { |
| suites->suites[idx++] = ECC_BYTE; |
| suites->suites[idx++] = TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384; |
| } |
| #endif |
| |
| #ifdef BUILD_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA |
| if (tls && haveECDSAsig && haveStaticECC) { |
| suites->suites[idx++] = ECC_BYTE; |
| suites->suites[idx++] = TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA; |
| } |
| #endif |
| |
| #ifdef BUILD_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 |
| if (tls1_2 && haveStaticECC) { |
| suites->suites[idx++] = ECC_BYTE; |
| suites->suites[idx++] = TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256; |
| } |
| #endif |
| |
| #ifdef BUILD_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA |
| if (tls && haveStaticECC) { |
| suites->suites[idx++] = ECC_BYTE; |
| suites->suites[idx++] = TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA; |
| } |
| #endif |
| |
| #ifdef BUILD_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 |
| if (tls1_2 && haveECDSAsig && haveStaticECC) { |
| suites->suites[idx++] = ECC_BYTE; |
| suites->suites[idx++] = TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256; |
| } |
| #endif |
| |
| #ifdef BUILD_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA |
| if (tls && haveECDSAsig && haveStaticECC) { |
| suites->suites[idx++] = ECC_BYTE; |
| suites->suites[idx++] = TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA; |
| } |
| #endif |
| |
| #ifdef BUILD_TLS_ECDHE_ECDSA_WITH_RC4_128_SHA |
| if (tls && haveStaticECC) { |
| suites->suites[idx++] = ECC_BYTE; |
| suites->suites[idx++] = TLS_ECDHE_ECDSA_WITH_RC4_128_SHA; |
| } |
| #endif |
| |
| #ifdef BUILD_TLS_ECDH_ECDSA_WITH_RC4_128_SHA |
| if (tls && haveECDSAsig && haveStaticECC) { |
| suites->suites[idx++] = ECC_BYTE; |
| suites->suites[idx++] = TLS_ECDH_ECDSA_WITH_RC4_128_SHA; |
| } |
| #endif |
| |
| #ifdef BUILD_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA |
| if (tls && haveStaticECC) { |
| suites->suites[idx++] = ECC_BYTE; |
| suites->suites[idx++] = TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA; |
| } |
| #endif |
| |
| #ifdef BUILD_TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA |
| if (tls && haveECDSAsig && haveStaticECC) { |
| suites->suites[idx++] = ECC_BYTE; |
| suites->suites[idx++] = TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA; |
| } |
| #endif |
| |
| #ifdef BUILD_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 |
| if (tls1_2 && haveRSA) { |
| suites->suites[idx++] = ECC_BYTE; |
| suites->suites[idx++] = TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384; |
| } |
| #endif |
| |
| #ifdef BUILD_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA |
| if (tls && haveRSA) { |
| suites->suites[idx++] = ECC_BYTE; |
| suites->suites[idx++] = TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA; |
| } |
| #endif |
| |
| #ifdef BUILD_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 |
| if (tls1_2 && haveRSAsig && haveStaticECC) { |
| suites->suites[idx++] = ECC_BYTE; |
| suites->suites[idx++] = TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384; |
| } |
| #endif |
| |
| #ifdef BUILD_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA |
| if (tls && haveRSAsig && haveStaticECC) { |
| suites->suites[idx++] = ECC_BYTE; |
| suites->suites[idx++] = TLS_ECDH_RSA_WITH_AES_256_CBC_SHA; |
| } |
| #endif |
| |
| #ifdef BUILD_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 |
| if (tls1_2 && haveRSA) { |
| suites->suites[idx++] = ECC_BYTE; |
| suites->suites[idx++] = TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256; |
| } |
| #endif |
| |
| #ifdef BUILD_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA |
| if (tls && haveRSA) { |
| suites->suites[idx++] = ECC_BYTE; |
| suites->suites[idx++] = TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA; |
| } |
| #endif |
| |
| #ifdef BUILD_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 |
| if (tls1_2 && haveRSAsig && haveStaticECC) { |
| suites->suites[idx++] = ECC_BYTE; |
| suites->suites[idx++] = TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256; |
| } |
| #endif |
| |
| #ifdef BUILD_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA |
| if (tls && haveRSAsig && haveStaticECC) { |
| suites->suites[idx++] = ECC_BYTE; |
| suites->suites[idx++] = TLS_ECDH_RSA_WITH_AES_128_CBC_SHA; |
| } |
| #endif |
| |
| #ifdef BUILD_TLS_ECDHE_RSA_WITH_RC4_128_SHA |
| if (tls && haveRSA) { |
| suites->suites[idx++] = ECC_BYTE; |
| suites->suites[idx++] = TLS_ECDHE_RSA_WITH_RC4_128_SHA; |
| } |
| #endif |
| |
| #ifdef BUILD_TLS_ECDH_RSA_WITH_RC4_128_SHA |
| if (tls && haveRSAsig && haveStaticECC) { |
| suites->suites[idx++] = ECC_BYTE; |
| suites->suites[idx++] = TLS_ECDH_RSA_WITH_RC4_128_SHA; |
| } |
| #endif |
| |
| #ifdef BUILD_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA |
| if (tls && haveRSA) { |
| suites->suites[idx++] = ECC_BYTE; |
| suites->suites[idx++] = TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA; |
| } |
| #endif |
| |
| #ifdef BUILD_TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA |
| if (tls && haveRSAsig && haveStaticECC) { |
| suites->suites[idx++] = ECC_BYTE; |
| suites->suites[idx++] = TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA; |
| } |
| #endif |
| |
| #ifdef BUILD_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 |
| if (tls1_2 && haveDH && haveRSA) { |
| suites->suites[idx++] = 0; |
| suites->suites[idx++] = TLS_DHE_RSA_WITH_AES_256_GCM_SHA384; |
| } |
| #endif |
| |
| #ifdef BUILD_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 |
| if (tls1_2 && haveDH && haveRSA) { |
| suites->suites[idx++] = 0; |
| suites->suites[idx++] = TLS_DHE_RSA_WITH_AES_256_CBC_SHA256; |
| } |
| #endif |
| |
| #ifdef BUILD_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 |
| if (tls1_2 && haveDH && haveRSA) { |
| suites->suites[idx++] = 0; |
| suites->suites[idx++] = TLS_DHE_RSA_WITH_AES_128_GCM_SHA256; |
| } |
| #endif |
| |
| #ifdef BUILD_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 |
| if (tls1_2 && haveDH && haveRSA) { |
| suites->suites[idx++] = 0; |
| suites->suites[idx++] = TLS_DHE_RSA_WITH_AES_128_CBC_SHA256; |
| } |
| #endif |
| |
| #ifdef BUILD_TLS_DHE_RSA_WITH_AES_256_CBC_SHA |
| if (tls && haveDH && haveRSA) { |
| suites->suites[idx++] = 0; |
| suites->suites[idx++] = TLS_DHE_RSA_WITH_AES_256_CBC_SHA; |
| } |
| #endif |
| |
| #ifdef BUILD_TLS_DHE_RSA_WITH_AES_128_CBC_SHA |
| if (tls && haveDH && haveRSA) { |
| suites->suites[idx++] = 0; |
| suites->suites[idx++] = TLS_DHE_RSA_WITH_AES_128_CBC_SHA; |
| } |
| #endif |
| |
| #ifdef BUILD_TLS_RSA_WITH_AES_256_GCM_SHA384 |
| if (tls1_2 && haveRSA) { |
| suites->suites[idx++] = 0; |
| suites->suites[idx++] = TLS_RSA_WITH_AES_256_GCM_SHA384; |
| } |
| #endif |
| |
| #ifdef BUILD_TLS_RSA_WITH_AES_256_CBC_SHA256 |
| if (tls1_2 && haveRSA) { |
| suites->suites[idx++] = 0; |
| suites->suites[idx++] = TLS_RSA_WITH_AES_256_CBC_SHA256; |
| } |
| #endif |
| |
| #ifdef BUILD_TLS_RSA_WITH_AES_128_GCM_SHA256 |
| if (tls1_2 && haveRSA) { |
| suites->suites[idx++] = 0; |
| suites->suites[idx++] = TLS_RSA_WITH_AES_128_GCM_SHA256; |
| } |
| #endif |
| |
| #ifdef BUILD_TLS_RSA_WITH_AES_128_CBC_SHA256 |
| if (tls1_2 && haveRSA) { |
| suites->suites[idx++] = 0; |
| suites->suites[idx++] = TLS_RSA_WITH_AES_128_CBC_SHA256; |
| } |
| #endif |
| |
| #ifdef BUILD_TLS_RSA_WITH_AES_256_CBC_SHA |
| if (tls && haveRSA) { |
| suites->suites[idx++] = 0; |
| suites->suites[idx++] = TLS_RSA_WITH_AES_256_CBC_SHA; |
| } |
| #endif |
| |
| #ifdef BUILD_TLS_RSA_WITH_AES_128_CBC_SHA |
| if (tls && haveRSA) { |
| suites->suites[idx++] = 0; |
| suites->suites[idx++] = TLS_RSA_WITH_AES_128_CBC_SHA; |
| } |
| #endif |
| |
| #ifdef BUILD_TLS_PSK_WITH_AES_256_CBC_SHA |
| if (tls && havePSK) { |
| suites->suites[idx++] = 0; |
| suites->suites[idx++] = TLS_PSK_WITH_AES_256_CBC_SHA; |
| } |
| #endif |
| |
| #ifdef BUILD_TLS_PSK_WITH_AES_128_CBC_SHA |
| if (tls && havePSK) { |
| suites->suites[idx++] = 0; |
| suites->suites[idx++] = TLS_PSK_WITH_AES_128_CBC_SHA; |
| } |
| #endif |
| |
| #ifdef BUILD_SSL_RSA_WITH_RC4_128_SHA |
| if (haveRSA ) { |
| suites->suites[idx++] = 0; |
| suites->suites[idx++] = SSL_RSA_WITH_RC4_128_SHA; |
| } |
| #endif |
| |
| #ifdef BUILD_SSL_RSA_WITH_RC4_128_MD5 |
| if (haveRSA ) { |
| suites->suites[idx++] = 0; |
| suites->suites[idx++] = SSL_RSA_WITH_RC4_128_MD5; |
| } |
| #endif |
| |
| #ifdef BUILD_SSL_RSA_WITH_3DES_EDE_CBC_SHA |
| if (haveRSA ) { |
| suites->suites[idx++] = 0; |
| suites->suites[idx++] = SSL_RSA_WITH_3DES_EDE_CBC_SHA; |
| } |
| #endif |
| |
| #ifdef BUILD_TLS_RSA_WITH_HC_128_CBC_MD5 |
| if (tls && haveRSA) { |
| suites->suites[idx++] = 0; |
| suites->suites[idx++] = TLS_RSA_WITH_HC_128_CBC_MD5; |
| } |
| #endif |
| |
| #ifdef BUILD_TLS_RSA_WITH_HC_128_CBC_SHA |
| if (tls && haveRSA) { |
| suites->suites[idx++] = 0; |
| suites->suites[idx++] = TLS_RSA_WITH_HC_128_CBC_SHA; |
| } |
| #endif |
| |
| #ifdef BUILD_TLS_RSA_WITH_RABBIT_CBC_SHA |
| if (tls && haveRSA) { |
| suites->suites[idx++] = 0; |
| suites->suites[idx++] = TLS_RSA_WITH_RABBIT_CBC_SHA; |
| } |
| #endif |
| |
| suites->suiteSz = idx; |
| } |
| |
| |
| /* init everything to 0, NULL, default values before calling anything that may |
| fail so that desctructor has a "good" state to cleanup */ |
| int InitSSL(CYASSL* ssl, CYASSL_CTX* ctx) |
| { |
| int ret; |
| byte havePSK = 0; |
| |
| ssl->ctx = ctx; /* only for passing to calls, options could change */ |
| ssl->version = ctx->method->version; |
| ssl->suites = ctx->suites; |
| |
| #ifdef HAVE_LIBZ |
| ssl->didStreamInit = 0; |
| #endif |
| |
| ssl->buffers.certificate.buffer = 0; |
| ssl->buffers.key.buffer = 0; |
| ssl->buffers.certChain.buffer = 0; |
| ssl->buffers.inputBuffer.length = 0; |
| ssl->buffers.inputBuffer.idx = 0; |
| ssl->buffers.inputBuffer.buffer = ssl->buffers.inputBuffer.staticBuffer; |
| ssl->buffers.inputBuffer.bufferSize = STATIC_BUFFER_LEN; |
| ssl->buffers.inputBuffer.dynamicFlag = 0; |
| ssl->buffers.outputBuffer.length = 0; |
| ssl->buffers.outputBuffer.idx = 0; |
| ssl->buffers.outputBuffer.buffer = ssl->buffers.outputBuffer.staticBuffer; |
| ssl->buffers.outputBuffer.bufferSize = STATIC_BUFFER_LEN; |
| ssl->buffers.outputBuffer.dynamicFlag = 0; |
| ssl->buffers.domainName.buffer = 0; |
| ssl->buffers.serverDH_P.buffer = 0; |
| ssl->buffers.serverDH_G.buffer = 0; |
| ssl->buffers.serverDH_Pub.buffer = 0; |
| ssl->buffers.serverDH_Priv.buffer = 0; |
| ssl->buffers.clearOutputBuffer.buffer = 0; |
| ssl->buffers.clearOutputBuffer.length = 0; |
| ssl->buffers.prevSent = 0; |
| ssl->buffers.plainSz = 0; |
| |
| #ifdef OPENSSL_EXTRA |
| ssl->peerCert.derCert.buffer = NULL; |
| ssl->peerCert.altNames = NULL; |
| ssl->peerCert.altNamesNext = NULL; |
| #endif |
| |
| #ifdef HAVE_ECC |
| ssl->eccTempKeySz = ctx->eccTempKeySz; |
| ssl->peerEccKeyPresent = 0; |
| ssl->peerEccDsaKeyPresent = 0; |
| ssl->eccDsaKeyPresent = 0; |
| ssl->eccTempKeyPresent = 0; |
| ecc_init(&ssl->peerEccKey); |
| ecc_init(&ssl->peerEccDsaKey); |
| ecc_init(&ssl->eccDsaKey); |
| ecc_init(&ssl->eccTempKey); |
| #endif |
| |
| ssl->timeout = ctx->timeout; |
| ssl->rfd = -1; /* set to invalid descriptor */ |
| ssl->wfd = -1; |
| ssl->biord = 0; |
| ssl->biowr = 0; |
| |
| ssl->IOCB_ReadCtx = &ssl->rfd; /* prevent invalid pointer acess if not */ |
| ssl->IOCB_WriteCtx = &ssl->wfd; /* correctly set */ |
| |
| InitMd5(&ssl->hashMd5); |
| InitSha(&ssl->hashSha); |
| #ifndef NO_SHA256 |
| InitSha256(&ssl->hashSha256); |
| #endif |
| #ifdef CYASSL_SHA384 |
| InitSha384(&ssl->hashSha384); |
| #endif |
| InitRsaKey(&ssl->peerRsaKey, ctx->heap); |
| |
| ssl->verifyCallback = ctx->verifyCallback; |
| ssl->peerRsaKeyPresent = 0; |
| ssl->options.side = ctx->method->side; |
| ssl->options.downgrade = ctx->method->downgrade; |
| ssl->error = 0; |
| ssl->options.connReset = 0; |
| ssl->options.isClosed = 0; |
| ssl->options.closeNotify = 0; |
| ssl->options.sentNotify = 0; |
| ssl->options.usingCompression = 0; |
| if (ssl->options.side == SERVER_END) |
| ssl->options.haveDH = ctx->haveDH; |
| else |
| ssl->options.haveDH = 0; |
| ssl->options.haveNTRU = ctx->haveNTRU; |
| ssl->options.haveECDSAsig = ctx->haveECDSAsig; |
| ssl->options.haveStaticECC = ctx->haveStaticECC; |
| ssl->options.havePeerCert = 0; |
| ssl->options.usingPSK_cipher = 0; |
| ssl->options.sendAlertState = 0; |
| #ifndef NO_PSK |
| havePSK = ctx->havePSK; |
| ssl->options.havePSK = ctx->havePSK; |
| ssl->options.client_psk_cb = ctx->client_psk_cb; |
| ssl->options.server_psk_cb = ctx->server_psk_cb; |
| #endif /* NO_PSK */ |
| |
| ssl->options.serverState = NULL_STATE; |
| ssl->options.clientState = NULL_STATE; |
| ssl->options.connectState = CONNECT_BEGIN; |
| ssl->options.acceptState = ACCEPT_BEGIN; |
| ssl->options.handShakeState = NULL_STATE; |
| ssl->options.processReply = doProcessInit; |
| |
| #ifdef CYASSL_DTLS |
| ssl->keys.dtls_sequence_number = 0; |
| ssl->keys.dtls_peer_sequence_number = 0; |
| ssl->keys.dtls_handshake_number = 0; |
| ssl->keys.dtls_epoch = 0; |
| ssl->keys.dtls_peer_epoch = 0; |
| ssl->arrays.cookieSz = 0; |
| #endif |
| ssl->keys.encryptionOn = 0; /* initially off */ |
| ssl->options.sessionCacheOff = ctx->sessionCacheOff; |
| ssl->options.sessionCacheFlushOff = ctx->sessionCacheFlushOff; |
| |
| ssl->options.verifyPeer = ctx->verifyPeer; |
| ssl->options.verifyNone = ctx->verifyNone; |
| ssl->options.failNoCert = ctx->failNoCert; |
| ssl->options.sendVerify = ctx->sendVerify; |
| |
| ssl->options.resuming = 0; |
| ssl->options.haveSessionId = 0; |
| ssl->hmac = Hmac; /* default to SSLv3 */ |
| ssl->heap = ctx->heap; /* defaults to self */ |
| ssl->options.tls = 0; |
| ssl->options.tls1_1 = 0; |
| ssl->options.dtls = 0; |
| ssl->options.partialWrite = ctx->partialWrite; |
| ssl->options.quietShutdown = ctx->quietShutdown; |
| ssl->options.certOnly = 0; |
| ssl->options.groupMessages = ctx->groupMessages; |
| |
| /* ctx still owns certificate, certChain, key, dh, and cm */ |
| ssl->buffers.certificate = ctx->certificate; |
| ssl->buffers.certChain = ctx->certChain; |
| ssl->buffers.key = ctx->privateKey; |
| if (ssl->options.side == SERVER_END) { |
| ssl->buffers.serverDH_P = ctx->serverDH_P; |
| ssl->buffers.serverDH_G = ctx->serverDH_G; |
| } |
| ssl->buffers.weOwnCert = 0; |
| ssl->buffers.weOwnKey = 0; |
| ssl->buffers.weOwnDH = 0; |
| |
| #ifdef OPENSSL_EXTRA |
| ssl->peerCert.issuer.sz = 0; |
| ssl->peerCert.subject.sz = 0; |
| #endif |
| |
| #ifdef SESSION_CERTS |
| ssl->session.chain.count = 0; |
| #endif |
| |
| ssl->cipher.ssl = ssl; |
| |
| #ifdef FORTRESS |
| ssl->ex_data[0] = 0; |
| ssl->ex_data[1] = 0; |
| ssl->ex_data[2] = 0; |
| #endif |
| |
| #ifdef CYASSL_CALLBACKS |
| ssl->hsInfoOn = 0; |
| ssl->toInfoOn = 0; |
| #endif |
| |
| #ifndef NO_PSK |
| ssl->arrays.client_identity[0] = 0; |
| if (ctx->server_hint[0]) { /* set in CTX */ |
| XSTRNCPY(ssl->arrays.server_hint, ctx->server_hint, MAX_PSK_ID_LEN); |
| ssl->arrays.server_hint[MAX_PSK_ID_LEN - 1] = '\0'; |
| } |
| else |
| ssl->arrays.server_hint[0] = 0; |
| #endif /* NO_PSK */ |
| |
| /* all done with init, now can return errors, call other stuff */ |
| |
| /* increment CTX reference count */ |
| if (LockMutex(&ctx->countMutex) != 0) { |
| CYASSL_MSG("Couldn't lock CTX count mutex"); |
| return BAD_MUTEX_ERROR; |
| } |
| ctx->refCount++; |
| UnLockMutex(&ctx->countMutex); |
| |
| if ( (ret = InitRng(&ssl->rng)) != 0) |
| return ret; |
| |
| /* make sure server has cert and key unless using PSK */ |
| if (ssl->options.side == SERVER_END && !havePSK) |
| if (!ssl->buffers.certificate.buffer || !ssl->buffers.key.buffer) { |
| CYASSL_MSG("Server missing certificate and/or private key"); |
| return NO_PRIVATE_KEY; |
| } |
| |
| /* make sure server has DH parms, and add PSK if there, add NTRU too */ |
| if (ssl->options.side == SERVER_END) |
| InitSuites(&ssl->suites, ssl->version,ssl->options.haveDH, havePSK, |
| ssl->options.haveNTRU, ssl->options.haveECDSAsig, |
| ssl->options.haveStaticECC, ssl->options.side); |
| else |
| InitSuites(&ssl->suites, ssl->version, TRUE, havePSK, |
| ssl->options.haveNTRU, ssl->options.haveECDSAsig, |
| ssl->options.haveStaticECC, ssl->options.side); |
| |
| return 0; |
| } |
| |
| |
| /* In case holding SSL object in array and don't want to free actual ssl */ |
| void SSL_ResourceFree(CYASSL* ssl) |
| { |
| XFREE(ssl->buffers.serverDH_Priv.buffer, ssl->heap, DYNAMIC_TYPE_DH); |
| XFREE(ssl->buffers.serverDH_Pub.buffer, ssl->heap, DYNAMIC_TYPE_DH); |
| /* parameters (p,g) may be owned by ctx */ |
| if (ssl->buffers.weOwnDH || ssl->options.side == CLIENT_END) { |
| XFREE(ssl->buffers.serverDH_G.buffer, ssl->heap, DYNAMIC_TYPE_DH); |
| XFREE(ssl->buffers.serverDH_P.buffer, ssl->heap, DYNAMIC_TYPE_DH); |
| } |
| XFREE(ssl->buffers.domainName.buffer, ssl->heap, DYNAMIC_TYPE_DOMAIN); |
| |
| /* CYASSL_CTX always owns certChain */ |
| if (ssl->buffers.weOwnCert) |
| XFREE(ssl->buffers.certificate.buffer, ssl->heap, DYNAMIC_TYPE_CERT); |
| if (ssl->buffers.weOwnKey) |
| XFREE(ssl->buffers.key.buffer, ssl->heap, DYNAMIC_TYPE_KEY); |
| |
| FreeRsaKey(&ssl->peerRsaKey); |
| if (ssl->buffers.inputBuffer.dynamicFlag) |
| ShrinkInputBuffer(ssl, FORCED_FREE); |
| if (ssl->buffers.outputBuffer.dynamicFlag) |
| ShrinkOutputBuffer(ssl); |
| #if defined(OPENSSL_EXTRA) || defined(GOAHEAD_WS) |
| XFREE(ssl->peerCert.derCert.buffer, ssl->heap, DYNAMIC_TYPE_CERT); |
| if (ssl->peerCert.altNames) |
| FreeAltNames(ssl->peerCert.altNames, ssl->heap); |
| CyaSSL_BIO_free(ssl->biord); |
| if (ssl->biord != ssl->biowr) /* in case same as write */ |
| CyaSSL_BIO_free(ssl->biowr); |
| #endif |
| #ifdef HAVE_LIBZ |
| FreeStreams(ssl); |
| #endif |
| #ifdef HAVE_ECC |
| ecc_free(&ssl->peerEccKey); |
| ecc_free(&ssl->peerEccDsaKey); |
| ecc_free(&ssl->eccTempKey); |
| ecc_free(&ssl->eccDsaKey); |
| #endif |
| } |
| |
| |
| void FreeSSL(CYASSL* ssl) |
| { |
| FreeSSL_Ctx(ssl->ctx); /* will decrement and free underyling CTX if 0 */ |
| SSL_ResourceFree(ssl); |
| XFREE(ssl, ssl->heap, DYNAMIC_TYPE_SSL); |
| } |
| |
| |
| ProtocolVersion MakeSSLv3(void) |
| { |
| ProtocolVersion pv; |
| pv.major = SSLv3_MAJOR; |
| pv.minor = SSLv3_MINOR; |
| |
| return pv; |
| } |
| |
| |
| #ifdef CYASSL_DTLS |
| |
| ProtocolVersion MakeDTLSv1(void) |
| { |
| ProtocolVersion pv; |
| pv.major = DTLS_MAJOR; |
| pv.minor = DTLS_MINOR; |
| |
| return pv; |
| } |
| |
| #endif /* CYASSL_DTLS */ |
| |
| |
| |
| |
| #ifdef USE_WINDOWS_API |
| |
| timer_d Timer(void) |
| { |
| static int init = 0; |
| static LARGE_INTEGER freq; |
| LARGE_INTEGER count; |
| |
| if (!init) { |
| QueryPerformanceFrequency(&freq); |
| init = 1; |
| } |
| |
| QueryPerformanceCounter(&count); |
| |
| return (double)count.QuadPart / freq.QuadPart; |
| } |
| |
| |
| word32 LowResTimer(void) |
| { |
| return (word32)Timer(); |
| } |
| |
| |
| #elif defined(THREADX) |
| |
| #include "rtptime.h" |
| |
| word32 LowResTimer(void) |
| { |
| return (word32)rtp_get_system_sec(); |
| } |
| |
| |
| #elif defined(MICRIUM) |
| |
| word32 LowResTimer(void) |
| { |
| NET_SECURE_OS_TICK clk; |
| |
| #if (NET_SECURE_MGR_CFG_EN == DEF_ENABLED) |
| clk = NetSecure_OS_TimeGet(); |
| #endif |
| return (word32)clk; |
| } |
| |
| #elif defined(USER_TICKS) |
| |
| word32 LowResTimer(void) |
| { |
| /* |
| write your own clock tick function if don't want time(0) |
| needs second accuracy but doesn't have to correlated to EPOCH |
| */ |
| } |
| |
| #else /* !USE_WINDOWS_API && !THREADX && !MICRIUM && !USER_TICKS */ |
| |
| #include <time.h> |
| |
| word32 LowResTimer(void) |
| { |
| return time(0); |
| } |
| |
| |
| #endif /* USE_WINDOWS_API */ |
| |
| |
| /* add output to md5 and sha handshake hashes, exclude record header */ |
| static void HashOutput(CYASSL* ssl, const byte* output, int sz, int ivSz) |
| { |
| const byte* adj = output + RECORD_HEADER_SZ + ivSz; |
| sz -= RECORD_HEADER_SZ; |
| |
| #ifdef CYASSL_DTLS |
| if (ssl->options.dtls) { |
| adj += DTLS_RECORD_EXTRA; |
| sz -= DTLS_RECORD_EXTRA; |
| } |
| #endif |
| |
| Md5Update(&ssl->hashMd5, adj, sz); |
| ShaUpdate(&ssl->hashSha, adj, sz); |
| if (IsAtLeastTLSv1_2(ssl)) { |
| #ifndef NO_SHA256 |
| Sha256Update(&ssl->hashSha256, adj, sz); |
| #endif |
| #ifdef CYASSL_SHA384 |
| Sha384Update(&ssl->hashSha384, adj, sz); |
| #endif |
| } |
| } |
| |
| |
| /* add input to md5 and sha handshake hashes, include handshake header */ |
| static void HashInput(CYASSL* ssl, const byte* input, int sz) |
| { |
| const byte* adj = input - HANDSHAKE_HEADER_SZ; |
| sz += HANDSHAKE_HEADER_SZ; |
| |
| #ifdef CYASSL_DTLS |
| if (ssl->options.dtls) { |
| adj -= DTLS_HANDSHAKE_EXTRA; |
| sz += DTLS_HANDSHAKE_EXTRA; |
| } |
| #endif |
| |
| Md5Update(&ssl->hashMd5, adj, sz); |
| ShaUpdate(&ssl->hashSha, adj, sz); |
| if (IsAtLeastTLSv1_2(ssl)) { |
| #ifndef NO_SHA256 |
| Sha256Update(&ssl->hashSha256, adj, sz); |
| #endif |
| #ifdef CYASSL_SHA384 |
| Sha384Update(&ssl->hashSha384, adj, sz); |
| #endif |
| } |
| } |
| |
| |
| /* add record layer header for message */ |
| static void AddRecordHeader(byte* output, word32 length, byte type, CYASSL* ssl) |
| { |
| RecordLayerHeader* rl; |
| |
| /* record layer header */ |
| rl = (RecordLayerHeader*)output; |
| rl->type = type; |
| rl->version = ssl->version; /* type and version same in each */ |
| |
| if (!ssl->options.dtls) |
| c16toa((word16)length, rl->length); |
| else { |
| #ifdef CYASSL_DTLS |
| DtlsRecordLayerHeader* dtls; |
| |
| /* dtls record layer header extensions */ |
| dtls = (DtlsRecordLayerHeader*)output; |
| c16toa(ssl->keys.dtls_epoch, dtls->epoch); |
| c32to48(ssl->keys.dtls_sequence_number++, dtls->sequence_number); |
| c16toa((word16)length, dtls->length); |
| #endif |
| } |
| } |
| |
| |
| /* add handshake header for message */ |
| static void AddHandShakeHeader(byte* output, word32 length, byte type, |
| CYASSL* ssl) |
| { |
| HandShakeHeader* hs; |
| (void)ssl; |
| |
| /* handshake header */ |
| hs = (HandShakeHeader*)output; |
| hs->type = type; |
| c32to24(length, hs->length); /* type and length same for each */ |
| #ifdef CYASSL_DTLS |
| if (ssl->options.dtls) { |
| DtlsHandShakeHeader* dtls; |
| |
| /* dtls handshake header extensions */ |
| dtls = (DtlsHandShakeHeader*)output; |
| c16toa(ssl->keys.dtls_handshake_number++, dtls->message_seq); |
| c32to24(0, dtls->fragment_offset); |
| c32to24(length, dtls->fragment_length); |
| } |
| #endif |
| } |
| |
| |
| /* add both headers for handshake message */ |
| static void AddHeaders(byte* output, word32 length, byte type, CYASSL* ssl) |
| { |
| if (!ssl->options.dtls) { |
| AddRecordHeader(output, length + HANDSHAKE_HEADER_SZ, handshake, ssl); |
| AddHandShakeHeader(output + RECORD_HEADER_SZ, length, type, ssl); |
| } |
| #ifdef CYASSL_DTLS |
| else { |
| AddRecordHeader(output, length+DTLS_HANDSHAKE_HEADER_SZ, handshake,ssl); |
| AddHandShakeHeader(output + DTLS_RECORD_HEADER_SZ, length, type, ssl); |
| } |
| #endif |
| } |
| |
| |
| /* return bytes received, -1 on error */ |
| static int Receive(CYASSL* ssl, byte* buf, word32 sz) |
| { |
| int recvd; |
| |
| retry: |
| recvd = ssl->ctx->CBIORecv((char *)buf, (int)sz, ssl->IOCB_ReadCtx); |
| if (recvd < 0) |
| switch (recvd) { |
| case IO_ERR_GENERAL: /* general/unknown error */ |
| return -1; |
| |
| case IO_ERR_WANT_READ: /* want read, would block */ |
| return WANT_READ; |
| |
| case IO_ERR_CONN_RST: /* connection reset */ |
| ssl->options.connReset = 1; |
| return -1; |
| |
| case IO_ERR_ISR: /* interrupt */ |
| /* see if we got our timeout */ |
| #ifdef CYASSL_CALLBACKS |
| if (ssl->toInfoOn) { |
| struct itimerval timeout; |
| getitimer(ITIMER_REAL, &timeout); |
| if (timeout.it_value.tv_sec == 0 && |
| timeout.it_value.tv_usec == 0) { |
| XSTRNCPY(ssl->timeoutInfo.timeoutName, |
| "recv() timeout", MAX_TIMEOUT_NAME_SZ); |
| CYASSL_MSG("Got our timeout"); |
| return WANT_READ; |
| } |
| } |
| #endif |
| goto retry; |
| |
| case IO_ERR_CONN_CLOSE: /* peer closed connection */ |
| ssl->options.isClosed = 1; |
| return -1; |
| |
| default: |
| return recvd; |
| } |
| |
| return recvd; |
| } |
| |
| |
| /* Switch dynamic output buffer back to static, buffer is assumed clear */ |
| void ShrinkOutputBuffer(CYASSL* ssl) |
| { |
| CYASSL_MSG("Shrinking output buffer\n"); |
| XFREE(ssl->buffers.outputBuffer.buffer, ssl->heap, DYNAMIC_TYPE_OUT_BUFFER); |
| ssl->buffers.outputBuffer.buffer = ssl->buffers.outputBuffer.staticBuffer; |
| ssl->buffers.outputBuffer.bufferSize = STATIC_BUFFER_LEN; |
| ssl->buffers.outputBuffer.dynamicFlag = 0; |
| } |
| |
| |
| /* Switch dynamic input buffer back to static, keep any remaining input */ |
| /* forced free means cleaning up */ |
| void ShrinkInputBuffer(CYASSL* ssl, int forcedFree) |
| { |
| int usedLength = ssl->buffers.inputBuffer.length - |
| ssl->buffers.inputBuffer.idx; |
| if (!forcedFree && usedLength > STATIC_BUFFER_LEN) |
| return; |
| |
| CYASSL_MSG("Shrinking input buffer\n"); |
| |
| if (!forcedFree && usedLength) |
| XMEMCPY(ssl->buffers.inputBuffer.staticBuffer, |
| ssl->buffers.inputBuffer.buffer + ssl->buffers.inputBuffer.idx, |
| usedLength); |
| |
| XFREE(ssl->buffers.inputBuffer.buffer, ssl->heap, DYNAMIC_TYPE_IN_BUFFER); |
| ssl->buffers.inputBuffer.buffer = ssl->buffers.inputBuffer.staticBuffer; |
| ssl->buffers.inputBuffer.bufferSize = STATIC_BUFFER_LEN; |
| ssl->buffers.inputBuffer.dynamicFlag = 0; |
| ssl->buffers.inputBuffer.idx = 0; |
| ssl->buffers.inputBuffer.length = usedLength; |
| } |
| |
| |
| int SendBuffered(CYASSL* ssl) |
| { |
| while (ssl->buffers.outputBuffer.length > 0) { |
| int sent = ssl->ctx->CBIOSend((char*)ssl->buffers.outputBuffer.buffer + |
| ssl->buffers.outputBuffer.idx, |
| (int)ssl->buffers.outputBuffer.length, |
| ssl->IOCB_WriteCtx); |
| if (sent < 0) { |
| switch (sent) { |
| |
| case IO_ERR_WANT_WRITE: /* would block */ |
| return WANT_WRITE; |
| |
| case IO_ERR_CONN_RST: /* connection reset */ |
| ssl->options.connReset = 1; |
| break; |
| |
| case IO_ERR_ISR: /* interrupt */ |
| /* see if we got our timeout */ |
| #ifdef CYASSL_CALLBACKS |
| if (ssl->toInfoOn) { |
| struct itimerval timeout; |
| getitimer(ITIMER_REAL, &timeout); |
| if (timeout.it_value.tv_sec == 0 && |
| timeout.it_value.tv_usec == 0) { |
| XSTRNCPY(ssl->timeoutInfo.timeoutName, |
| "send() timeout", MAX_TIMEOUT_NAME_SZ); |
| CYASSL_MSG("Got our timeout"); |
| return WANT_WRITE; |
| } |
| } |
| #endif |
| continue; |
| |
| case IO_ERR_CONN_CLOSE: /* epipe / conn closed, same as reset */ |
| ssl->options.connReset = 1; |
| break; |
| |
| default: |
| return SOCKET_ERROR_E; |
| } |
| |
| return SOCKET_ERROR_E; |
| } |
| |
| ssl->buffers.outputBuffer.idx += sent; |
| ssl->buffers.outputBuffer.length -= sent; |
| } |
| |
| ssl->buffers.outputBuffer.idx = 0; |
| |
| if (ssl->buffers.outputBuffer.dynamicFlag) |
| ShrinkOutputBuffer(ssl); |
| |
| return 0; |
| } |
| |
| |
| /* Grow the output buffer */ |
| static INLINE int GrowOutputBuffer(CYASSL* ssl, int size) |
| { |
| byte* tmp = (byte*) XMALLOC(size + ssl->buffers.outputBuffer.length, |
| ssl->heap, DYNAMIC_TYPE_OUT_BUFFER); |
| CYASSL_MSG("growing output buffer\n"); |
| |
| if (!tmp) return MEMORY_E; |
| |
| if (ssl->buffers.outputBuffer.length) |
| XMEMCPY(tmp, ssl->buffers.outputBuffer.buffer, |
| ssl->buffers.outputBuffer.length); |
| |
| if (ssl->buffers.outputBuffer.dynamicFlag) |
| XFREE(ssl->buffers.outputBuffer.buffer, ssl->heap, |
| DYNAMIC_TYPE_OUT_BUFFER); |
| ssl->buffers.outputBuffer.dynamicFlag = 1; |
| ssl->buffers.outputBuffer.buffer = tmp; |
| ssl->buffers.outputBuffer.bufferSize = size + |
| ssl->buffers.outputBuffer.length; |
| return 0; |
| } |
| |
| |
| /* Grow the input buffer, should only be to read cert or big app data */ |
| static INLINE int GrowInputBuffer(CYASSL* ssl, int size, int usedLength) |
| { |
| byte* tmp = (byte*) XMALLOC(size + usedLength, ssl->heap, |
| DYNAMIC_TYPE_IN_BUFFER); |
| CYASSL_MSG("growing input buffer\n"); |
| |
| if (!tmp) return MEMORY_E; |
| |
| if (usedLength) |
| XMEMCPY(tmp, ssl->buffers.inputBuffer.buffer + |
| ssl->buffers.inputBuffer.idx, usedLength); |
| |
| if (ssl->buffers.inputBuffer.dynamicFlag) |
| XFREE(ssl->buffers.inputBuffer.buffer,ssl->heap,DYNAMIC_TYPE_IN_BUFFER); |
| |
| ssl->buffers.inputBuffer.dynamicFlag = 1; |
| ssl->buffers.inputBuffer.buffer = tmp; |
| ssl->buffers.inputBuffer.bufferSize = size + usedLength; |
| ssl->buffers.inputBuffer.idx = 0; |
| ssl->buffers.inputBuffer.length = usedLength; |
| |
| return 0; |
| } |
| |
| |
| /* check avalaible size into output buffer, make room if needed */ |
| static INLINE int CheckAvalaibleSize(CYASSL *ssl, int size) |
| { |
| if (ssl->buffers.outputBuffer.bufferSize - ssl->buffers.outputBuffer.length |
| < (word32)size) { |
| if (GrowOutputBuffer(ssl, size) < 0) |
| return MEMORY_E; |
| } |
| |
| return 0; |
| } |
| |
| /* do all verify and sanity checks on record header */ |
| static int GetRecordHeader(CYASSL* ssl, const byte* input, word32* inOutIdx, |
| RecordLayerHeader* rh, word16 *size) |
| { |
| if (!ssl->options.dtls) { |
| XMEMCPY(rh, input + *inOutIdx, RECORD_HEADER_SZ); |
| *inOutIdx += RECORD_HEADER_SZ; |
| ato16(rh->length, size); |
| } |
| else { |
| #ifdef CYASSL_DTLS |
| /* type and version in same sport */ |
| XMEMCPY(rh, input + *inOutIdx, ENUM_LEN + VERSION_SZ); |
| *inOutIdx += ENUM_LEN + VERSION_SZ; |
| *inOutIdx += 4; /* skip epoch and first 2 seq bytes for now */ |
| ato32(input + *inOutIdx, &ssl->keys.dtls_peer_sequence_number); |
| *inOutIdx += 4; /* advance past rest of seq */ |
| ato16(input + *inOutIdx, size); |
| *inOutIdx += LENGTH_SZ; |
| #endif |
| } |
| |
| /* catch version mismatch */ |
| if (rh->version.major != ssl->version.major || |
| rh->version.minor != ssl->version.minor) { |
| |
| if (ssl->options.side == SERVER_END && |
| ssl->options.acceptState == ACCEPT_BEGIN) |
| CYASSL_MSG("Client attempting to connect with different version"); |
| else if (ssl->options.side == CLIENT_END && ssl->options.downgrade && |
| ssl->options.connectState < FIRST_REPLY_DONE) |
| CYASSL_MSG("Server attempting to accept with different version"); |
| else { |
| CYASSL_MSG("SSL version error"); |
| return VERSION_ERROR; /* only use requested version */ |
| } |
| } |
| |
| /* record layer length check */ |
| if (*size > (MAX_RECORD_SIZE + MAX_COMP_EXTRA + MAX_MSG_EXTRA)) |
| return LENGTH_ERROR; |
| |
| /* verify record type here as well */ |
| switch ((enum ContentType)rh->type) { |
| case handshake: |
| case change_cipher_spec: |
| case application_data: |
| case alert: |
| break; |
| case no_type: |
| default: |
| CYASSL_MSG("Unknown Record Type"); |
| return UNKNOWN_RECORD_TYPE; |
| } |
| |
| return 0; |
| } |
| |
| |
| static int GetHandShakeHeader(CYASSL* ssl, const byte* input, word32* inOutIdx, |
| byte *type, word32 *size) |
| { |
| const byte *ptr = input + *inOutIdx; |
| (void)ssl; |
| *inOutIdx += HANDSHAKE_HEADER_SZ; |
| |
| #ifdef CYASSL_DTLS |
| if (ssl->options.dtls) |
| *inOutIdx += DTLS_HANDSHAKE_EXTRA; |
| #endif |
| |
| *type = ptr[0]; |
| c24to32(&ptr[1], size); |
| |
| return 0; |
| } |
| |
| |
| /* fill with MD5 pad size since biggest required */ |
| static const byte PAD1[PAD_MD5] = |
| { 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, |
| 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, |
| 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, |
| 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, |
| 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, |
| 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36 |
| }; |
| static const byte PAD2[PAD_MD5] = |
| { 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, |
| 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, |
| 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, |
| 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, |
| 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, |
| 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c |
| }; |
| |
| /* calculate MD5 hash for finished */ |
| static void BuildMD5(CYASSL* ssl, Hashes* hashes, const byte* sender) |
| { |
| byte md5_result[MD5_DIGEST_SIZE]; |
| |
| /* make md5 inner */ |
| Md5Update(&ssl->hashMd5, sender, SIZEOF_SENDER); |
| Md5Update(&ssl->hashMd5, ssl->arrays.masterSecret, SECRET_LEN); |
| Md5Update(&ssl->hashMd5, PAD1, PAD_MD5); |
| Md5Final(&ssl->hashMd5, md5_result); |
| |
| /* make md5 outer */ |
| Md5Update(&ssl->hashMd5, ssl->arrays.masterSecret, SECRET_LEN); |
| Md5Update(&ssl->hashMd5, PAD2, PAD_MD5); |
| Md5Update(&ssl->hashMd5, md5_result, MD5_DIGEST_SIZE); |
| |
| Md5Final(&ssl->hashMd5, hashes->md5); |
| } |
| |
| |
| /* calculate SHA hash for finished */ |
| static void BuildSHA(CYASSL* ssl, Hashes* hashes, const byte* sender) |
| { |
| byte sha_result[SHA_DIGEST_SIZE]; |
| |
| /* make sha inner */ |
| ShaUpdate(&ssl->hashSha, sender, SIZEOF_SENDER); |
| ShaUpdate(&ssl->hashSha, ssl->arrays.masterSecret, SECRET_LEN); |
| ShaUpdate(&ssl->hashSha, PAD1, PAD_SHA); |
| ShaFinal(&ssl->hashSha, sha_result); |
| |
| /* make sha outer */ |
| ShaUpdate(&ssl->hashSha, ssl->arrays.masterSecret, SECRET_LEN); |
| ShaUpdate(&ssl->hashSha, PAD2, PAD_SHA); |
| ShaUpdate(&ssl->hashSha, sha_result, SHA_DIGEST_SIZE); |
| |
| ShaFinal(&ssl->hashSha, hashes->sha); |
| } |
| |
| |
| static void BuildFinished(CYASSL* ssl, Hashes* hashes, const byte* sender) |
| { |
| /* store current states, building requires get_digest which resets state */ |
| Md5 md5 = ssl->hashMd5; |
| Sha sha = ssl->hashSha; |
| #ifndef NO_SHA256 |
| Sha256 sha256; |
| #endif |
| #ifdef CYASSL_SHA384 |
| Sha384 sha384; |
| #endif |
| |
| #ifndef NO_SHA256 |
| InitSha256(&sha256); |
| if (IsAtLeastTLSv1_2(ssl)) |
| sha256 = ssl->hashSha256; |
| #endif |
| #ifdef CYASSL_SHA384 |
| InitSha384(&sha384); |
| if (IsAtLeastTLSv1_2(ssl)) |
| sha384 = ssl->hashSha384; |
| #endif |
| |
| if (ssl->options.tls) |
| BuildTlsFinished(ssl, hashes, sender); |
| else { |
| BuildMD5(ssl, hashes, sender); |
| BuildSHA(ssl, hashes, sender); |
| } |
| |
| /* restore */ |
| ssl->hashMd5 = md5; |
| ssl->hashSha = sha; |
| if (IsAtLeastTLSv1_2(ssl)) { |
| #ifndef NO_SHA256 |
| ssl->hashSha256 = sha256; |
| #endif |
| #ifdef CYASSL_SHA384 |
| ssl->hashSha384 = sha384; |
| #endif |
| } |
| } |
| |
| |
| static int DoCertificate(CYASSL* ssl, byte* input, word32* inOutIdx) |
| { |
| word32 listSz, i = *inOutIdx; |
| int ret = 0; |
| int anyError = 0; |
| int totalCerts = 0; /* number of certs in certs buffer */ |
| int count; |
| char domain[ASN_NAME_MAX]; |
| buffer certs[MAX_CHAIN_DEPTH]; |
| |
| #ifdef CYASSL_CALLBACKS |
| if (ssl->hsInfoOn) AddPacketName("Certificate", &ssl->handShakeInfo); |
| if (ssl->toInfoOn) AddLateName("Certificate", &ssl->timeoutInfo); |
| #endif |
| c24to32(&input[i], &listSz); |
| i += CERT_HEADER_SZ; |
| |
| CYASSL_MSG("Loading peer's cert chain"); |
| /* first put cert chain into buffer so can verify top down |
| we're sent bottom up */ |
| while (listSz) { |
| /* cert size */ |
| word32 certSz; |
| |
| if (totalCerts >= MAX_CHAIN_DEPTH) |
| return MAX_CHAIN_ERROR; |
| |
| c24to32(&input[i], &certSz); |
| i += CERT_HEADER_SZ; |
| |
| if (listSz > MAX_RECORD_SIZE || certSz > MAX_RECORD_SIZE) |
| return BUFFER_E; |
| |
| certs[totalCerts].length = certSz; |
| certs[totalCerts].buffer = input + i; |
| |
| #ifdef SESSION_CERTS |
| if (ssl->session.chain.count < MAX_CHAIN_DEPTH && |
| certSz < MAX_X509_SIZE) { |
| ssl->session.chain.certs[ssl->session.chain.count].length = certSz; |
| XMEMCPY(ssl->session.chain.certs[ssl->session.chain.count].buffer, |
| input + i, certSz); |
| ssl->session.chain.count++; |
| } else { |
| CYASSL_MSG("Couldn't store chain cert for session"); |
| } |
| #endif |
| |
| i += certSz; |
| listSz -= certSz + CERT_HEADER_SZ; |
| |
| totalCerts++; |
| CYASSL_MSG(" Put another cert into chain"); |
| } |
| |
| count = totalCerts; |
| |
| /* verify up to peer's first */ |
| while (count > 1) { |
| buffer myCert = certs[count - 1]; |
| DecodedCert dCert; |
| |
| InitDecodedCert(&dCert, myCert.buffer, myCert.length, ssl->heap); |
| ret = ParseCertRelative(&dCert, CERT_TYPE, !ssl->options.verifyNone, |
| ssl->ctx->cm); |
| if (ret == 0 && dCert.isCA == 0) { |
| CYASSL_MSG("Chain cert is not a CA, not adding as one"); |
| } |
| else if (ret == 0 && ssl->options.verifyNone) { |
| CYASSL_MSG("Chain cert not verified by option, not adding as CA"); |
| } |
| else if (ret == 0 && !AlreadySigner(ssl->ctx->cm, dCert.subjectHash)) { |
| buffer add; |
| add.length = myCert.length; |
| add.buffer = (byte*)XMALLOC(myCert.length, ssl->heap, |
| DYNAMIC_TYPE_CA); |
| CYASSL_MSG("Adding CA from chain"); |
| |
| if (add.buffer == NULL) |
| return MEMORY_E; |
| XMEMCPY(add.buffer, myCert.buffer, myCert.length); |
| |
| ret = AddCA(ssl->ctx->cm, add, CYASSL_CHAIN_CA, |
| ssl->ctx->verifyPeer); |
| if (ret == 1) ret = 0; /* SSL_SUCCESS for external */ |
| } |
| else if (ret != 0) { |
| CYASSL_MSG("Failed to verify CA from chain"); |
| } |
| else { |
| CYASSL_MSG("Verified CA from chain and already had it"); |
| } |
| |
| #ifdef HAVE_CRL |
| if (ret == 0 && ssl->ctx->cm->crlEnabled && ssl->ctx->cm->crlCheckAll) { |
| CYASSL_MSG("Doing Non Leaf CRL check"); |
| ret = CheckCertCRL(ssl->ctx->cm->crl, &dCert); |
| |
| if (ret != 0) { |
| CYASSL_MSG("\tCRL check not ok"); |
| } |
| } |
| #endif /* HAVE_CRL */ |
| |
| if (ret != 0 && anyError == 0) |
| anyError = ret; /* save error from last time */ |
| |
| FreeDecodedCert(&dCert); |
| count--; |
| } |
| |
| /* peer's, may not have one if blank client cert sent by TLSv1.2 */ |
| if (count) { |
| buffer myCert = certs[0]; |
| DecodedCert dCert; |
| int fatal = 0; |
| |
| CYASSL_MSG("Veriying Peer's cert"); |
| |
| InitDecodedCert(&dCert, myCert.buffer, myCert.length, ssl->heap); |
| ret = ParseCertRelative(&dCert, CERT_TYPE, !ssl->options.verifyNone, |
| ssl->ctx->cm); |
| if (ret == 0) { |
| CYASSL_MSG("Verified Peer's cert"); |
| fatal = 0; |
| } |
| else if (ret == ASN_PARSE_E) { |
| CYASSL_MSG("Got Peer cert ASN PARSE ERROR, fatal"); |
| fatal = 1; |
| } |
| else { |
| CYASSL_MSG("Failed to verify Peer's cert"); |
| if (ssl->verifyCallback) { |
| CYASSL_MSG("\tCallback override availalbe, will continue"); |
| fatal = 0; |
| } |
| else { |
| CYASSL_MSG("\tNo callback override availalbe, fatal"); |
| fatal = 1; |
| } |
| } |
| |
| #ifdef HAVE_OCSP |
| ret = CyaSSL_OCSP_Lookup_Cert(&ssl->ctx->ocsp, &dCert); |
| if (ret != 0) { |
| CYASSL_MSG("\tOCSP Lookup not ok"); |
| fatal = 0; |
| } |
| #endif |
| |
| #ifdef HAVE_CRL |
| if (fatal == 0 && ssl->ctx->cm->crlEnabled) { |
| CYASSL_MSG("Doing Leaf CRL check"); |
| ret = CheckCertCRL(ssl->ctx->cm->crl, &dCert); |
| |
| if (ret != 0) { |
| CYASSL_MSG("\tCRL check not ok"); |
| fatal = 0; |
| } |
| } |
| #endif /* HAVE_CRL */ |
| |
| #ifdef OPENSSL_EXTRA |
| /* set X509 format for peer cert even if fatal */ |
| XSTRNCPY(ssl->peerCert.issuer.name, dCert.issuer, ASN_NAME_MAX); |
| ssl->peerCert.issuer.name[ASN_NAME_MAX - 1] = '\0'; |
| ssl->peerCert.issuer.sz = (int)XSTRLEN(ssl->peerCert.issuer.name) + 1; |
| |
| XSTRNCPY(ssl->peerCert.subject.name, dCert.subject, ASN_NAME_MAX); |
| ssl->peerCert.subject.name[ASN_NAME_MAX - 1] = '\0'; |
| ssl->peerCert.subject.sz = (int)XSTRLEN(ssl->peerCert.subject.name) + 1; |
| |
| XMEMCPY(ssl->peerCert.serial, dCert.serial, EXTERNAL_SERIAL_SIZE); |
| ssl->peerCert.serialSz = dCert.serialSz; |
| if (dCert.subjectCNLen < ASN_NAME_MAX) { |
| XMEMCPY(ssl->peerCert.subjectCN,dCert.subjectCN,dCert.subjectCNLen); |
| ssl->peerCert.subjectCN[dCert.subjectCNLen] = '\0'; |
| } |
| else |
| ssl->peerCert.subjectCN[0] = '\0'; |
| |
| /* store cert for potential retrieval */ |
| ssl->peerCert.derCert.buffer = (byte*)XMALLOC(myCert.length, ssl->heap, |
| DYNAMIC_TYPE_CERT); |
| if (ssl->peerCert.derCert.buffer == NULL) { |
| ret = MEMORY_E; |
| fatal = 1; |
| } |
| else { |
| XMEMCPY(ssl->peerCert.derCert.buffer, myCert.buffer, myCert.length); |
| ssl->peerCert.derCert.length = myCert.length; |
| } |
| |
| ssl->peerCert.altNames = dCert.altNames; |
| dCert.altNames = NULL; /* takes ownership */ |
| ssl->peerCert.altNamesNext = ssl->peerCert.altNames; /* index hint */ |
| #endif |
| |
| if (fatal) { |
| FreeDecodedCert(&dCert); |
| ssl->error = ret; |
| return ret; |
| } |
| ssl->options.havePeerCert = 1; |
| |
| /* store for callback use */ |
| if (dCert.subjectCNLen < ASN_NAME_MAX) { |
| XMEMCPY(domain, dCert.subjectCN, dCert.subjectCNLen); |
| domain[dCert.subjectCNLen] = '\0'; |
| } |
| else |
| domain[0] = '\0'; |
| |
| if (!ssl->options.verifyNone && ssl->buffers.domainName.buffer) |
| if (XSTRNCMP((char*)ssl->buffers.domainName.buffer, |
| dCert.subjectCN, |
| ssl->buffers.domainName.length - 1)) { |
| ret = DOMAIN_NAME_MISMATCH; /* try to get peer key still */ |
| } |
| |
| /* decode peer key */ |
| if (dCert.keyOID == RSAk) { |
| word32 idx = 0; |
| if (RsaPublicKeyDecode(dCert.publicKey, &idx, |
| &ssl->peerRsaKey, dCert.pubKeySize) != 0) { |
| ret = PEER_KEY_ERROR; |
| } |
| else |
| ssl->peerRsaKeyPresent = 1; |
| } |
| #ifdef HAVE_NTRU |
| else if (dCert.keyOID == NTRUk) { |
| if (dCert.pubKeySize > sizeof(ssl->peerNtruKey)) { |
| ret = PEER_KEY_ERROR; |
| } |
| else { |
| XMEMCPY(ssl->peerNtruKey, dCert.publicKey, dCert.pubKeySize); |
| ssl->peerNtruKeyLen = (word16)dCert.pubKeySize; |
| ssl->peerNtruKeyPresent = 1; |
| } |
| } |
| #endif /* HAVE_NTRU */ |
| #ifdef HAVE_ECC |
| else if (dCert.keyOID == ECDSAk) { |
| if (ecc_import_x963(dCert.publicKey, dCert.pubKeySize, |
| &ssl->peerEccDsaKey) != 0) { |
| ret = PEER_KEY_ERROR; |
| } |
| else |
| ssl->peerEccDsaKeyPresent = 1; |
| } |
| #endif /* HAVE_ECC */ |
| |
| FreeDecodedCert(&dCert); |
| } |
| |
| if (anyError != 0 && ret == 0) |
| ret = anyError; |
| |
| if (ret == 0 && ssl->options.side == CLIENT_END) |
| ssl->options.serverState = SERVER_CERT_COMPLETE; |
| |
| if (ret != 0) { |
| if (!ssl->options.verifyNone) { |
| int why = bad_certificate; |
| if (ret == ASN_AFTER_DATE_E || ret == ASN_BEFORE_DATE_E) |
| why = certificate_expired; |
| if (ssl->verifyCallback) { |
| int ok; |
| CYASSL_X509_STORE_CTX store; |
| |
| store.error = ret; |
| store.error_depth = totalCerts; |
| store.domain = domain; |
| #ifdef OPENSSL_EXTRA |
| store.current_cert = &ssl->peerCert; |
| #else |
| store.current_cert = NULL; |
| #endif |
| #ifdef FORTRESS |
| store.ex_data = ssl; |
| #endif |
| ok = ssl->verifyCallback(0, &store); |
| if (ok) { |
| CYASSL_MSG("Verify callback overriding error!"); |
| ret = 0; |
| } |
| } |
| if (ret != 0) { |
| SendAlert(ssl, alert_fatal, why); /* try to send */ |
| ssl->options.isClosed = 1; |
| } |
| } |
| ssl->error = ret; |
| } |
| #ifdef FORTRESS |
| else { |
| if (ssl->verifyCallback) { |
| int ok; |
| CYASSL_X509_STORE_CTX store; |
| |
| store.error = ret; |
| store.error_depth = totalCerts; |
| store.domain = domain; |
| store.current_cert = &ssl->peerCert; |
| store.ex_data = ssl; |
| |
| ok = ssl->verifyCallback(1, &store); |
| if (!ok) { |
| CYASSL_MSG("Verify callback overriding valid certificate!"); |
| ret = -1; |
| SendAlert(ssl, alert_fatal, bad_certificate); |
| ssl->options.isClosed = 1; |
| } |
| } |
| } |
| #endif |
| |
| *inOutIdx = i; |
| return ret; |
| } |
| |
| |
| static int DoHelloRequest(CYASSL* ssl, const byte* input, word32* inOutIdx) |
| { |
| if (ssl->keys.encryptionOn) { |
| const byte* mac; |
| int padSz = ssl->keys.encryptSz - HANDSHAKE_HEADER_SZ - |
| ssl->specs.hash_size; |
| byte verify[SHA256_DIGEST_SIZE]; |
| |
| ssl->hmac(ssl, verify, input + *inOutIdx - HANDSHAKE_HEADER_SZ, |
| HANDSHAKE_HEADER_SZ, handshake, 1); |
| /* read mac and fill */ |
| mac = input + *inOutIdx; |
| *inOutIdx += ssl->specs.hash_size; |
| |
| if (ssl->options.tls1_1 && ssl->specs.cipher_type == block) |
| padSz -= ssl->specs.block_size; |
| |
| *inOutIdx += padSz; |
| |
| /* verify */ |
| if (XMEMCMP(mac, verify, ssl->specs.hash_size)) { |
| CYASSL_MSG(" hello_request verify mac error"); |
| return VERIFY_MAC_ERROR; |
| } |
| } |
| |
| return SendAlert(ssl, alert_warning, no_renegotiation); |
| } |
| |
| |
| int DoFinished(CYASSL* ssl, const byte* input, word32* inOutIdx, int sniff) |
| { |
| byte verifyMAC[SHA256_DIGEST_SIZE]; |
| int finishedSz = ssl->options.tls ? TLS_FINISHED_SZ : FINISHED_SZ; |
| int headerSz = HANDSHAKE_HEADER_SZ; |
| word32 macSz = finishedSz + HANDSHAKE_HEADER_SZ, |
| idx = *inOutIdx, |
| padSz = ssl->keys.encryptSz - HANDSHAKE_HEADER_SZ - finishedSz - |
| ssl->specs.hash_size; |
| const byte* mac; |
| |
| #ifdef CYASSL_DTLS |
| if (ssl->options.dtls) { |
| headerSz += DTLS_HANDSHAKE_EXTRA; |
| macSz += DTLS_HANDSHAKE_EXTRA; |
| padSz -= DTLS_HANDSHAKE_EXTRA; |
| } |
| #endif |
| |
| #ifdef CYASSL_CALLBACKS |
| if (ssl->hsInfoOn) AddPacketName("Finished", &ssl->handShakeInfo); |
| if (ssl->toInfoOn) AddLateName("Finished", &ssl->timeoutInfo); |
| #endif |
| if (sniff == NO_SNIFF) { |
| if (XMEMCMP(input + idx, &ssl->verifyHashes, finishedSz)) { |
| CYASSL_MSG("Verify finished error on hashes"); |
| return VERIFY_FINISHED_ERROR; |
| } |
| } |
| |
| if (ssl->specs.cipher_type != aead) { |
| ssl->hmac(ssl, verifyMAC, input + idx - headerSz, macSz, |
| handshake, 1); |
| idx += finishedSz; |
| |
| /* read mac and fill */ |
| mac = input + idx; |
| idx += ssl->specs.hash_size; |
| |
| if (ssl->options.tls1_1 && ssl->specs.cipher_type == block) |
| padSz -= ssl->specs.block_size; |
| |
| idx += padSz; |
| |
| /* verify mac */ |
| if (XMEMCMP(mac, verifyMAC, ssl->specs.hash_size)) { |
| CYASSL_MSG("Verify finished error on mac"); |
| return VERIFY_MAC_ERROR; |
| } |
| } |
| else { |
| idx += (finishedSz + AEAD_AUTH_TAG_SZ); |
| } |
| |
| if (ssl->options.side == CLIENT_END) { |
| ssl->options.serverState = SERVER_FINISHED_COMPLETE; |
| if (!ssl->options.resuming) |
| ssl->options.handShakeState = HANDSHAKE_DONE; |
| } |
| else { |
| ssl->options.clientState = CLIENT_FINISHED_COMPLETE; |
| if (ssl->options.resuming) |
| ssl->options.handShakeState = HANDSHAKE_DONE; |
| } |
| |
| *inOutIdx = idx; |
| return 0; |
| } |
| |
| |
| static int DoHandShakeMsg(CYASSL* ssl, byte* input, word32* inOutIdx, |
| word32 totalSz) |
| { |
| byte type; |
| word32 size; |
| int ret = 0; |
| |
| CYASSL_ENTER("DoHandShakeMsg()"); |
| |
| if (GetHandShakeHeader(ssl, input, inOutIdx, &type, &size) != 0) |
| return PARSE_ERROR; |
| |
| if (*inOutIdx + size > totalSz) |
| return INCOMPLETE_DATA; |
| |
| HashInput(ssl, input + *inOutIdx, size); |
| #ifdef CYASSL_CALLBACKS |
| /* add name later, add on record and handshake header part back on */ |
| if (ssl->toInfoOn) { |
| int add = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ; |
| AddPacketInfo(0, &ssl->timeoutInfo, input + *inOutIdx - add, |
| size + add, ssl->heap); |
| AddLateRecordHeader(&ssl->curRL, &ssl->timeoutInfo); |
| } |
| #endif |
| |
| switch (type) { |
| |
| case hello_request: |
| CYASSL_MSG("processing hello request"); |
| ret = DoHelloRequest(ssl, input, inOutIdx); |
| break; |
| |
| #ifndef NO_CYASSL_CLIENT |
| case hello_verify_request: |
| CYASSL_MSG("processing hello verify request"); |
| ret = DoHelloVerifyRequest(ssl, input,inOutIdx); |
| break; |
| |
| case server_hello: |
| CYASSL_MSG("processing server hello"); |
| ret = DoServerHello(ssl, input, inOutIdx, size); |
| break; |
| |
| case certificate_request: |
| CYASSL_MSG("processing certificate request"); |
| ret = DoCertificateRequest(ssl, input, inOutIdx); |
| break; |
| |
| case server_key_exchange: |
| CYASSL_MSG("processing server key exchange"); |
| ret = DoServerKeyExchange(ssl, input, inOutIdx); |
| break; |
| #endif |
| |
| case certificate: |
| CYASSL_MSG("processing certificate"); |
| ret = DoCertificate(ssl, input, inOutIdx); |
| break; |
| |
| case server_hello_done: |
| CYASSL_MSG("processing server hello done"); |
| #ifdef CYASSL_CALLBACKS |
| if (ssl->hsInfoOn) |
| AddPacketName("ServerHelloDone", &ssl->handShakeInfo); |
| if (ssl->toInfoOn) |
| AddLateName("ServerHelloDone", &ssl->timeoutInfo); |
| #endif |
| ssl->options.serverState = SERVER_HELLODONE_COMPLETE; |
| break; |
| |
| case finished: |
| CYASSL_MSG("processing finished"); |
| ret = DoFinished(ssl, input, inOutIdx, NO_SNIFF); |
| break; |
| |
| #ifndef NO_CYASSL_SERVER |
| case client_hello: |
| CYASSL_MSG("processing client hello"); |
| ret = DoClientHello(ssl, input, inOutIdx, totalSz, size); |
| break; |
| |
| case client_key_exchange: |
| CYASSL_MSG("processing client key exchange"); |
| ret = DoClientKeyExchange(ssl, input, inOutIdx); |
| break; |
| |
| case certificate_verify: |
| CYASSL_MSG("processing certificate verify"); |
| ret = DoCertificateVerify(ssl, input, inOutIdx, totalSz); |
| break; |
| |
| #endif |
| |
| default: |
| CYASSL_MSG("Unknown handshake message type"); |
| ret = UNKNOWN_HANDSHAKE_TYPE; |
| } |
| |
| CYASSL_LEAVE("DoHandShakeMsg()", ret); |
| return ret; |
| } |
| |
| |
| static INLINE word32 GetSEQIncrement(CYASSL* ssl, int verify) |
| { |
| if (verify) |
| return ssl->keys.peer_sequence_number++; |
| else |
| return ssl->keys.sequence_number++; |
| } |
| |
| |
| static INLINE void Encrypt(CYASSL* ssl, byte* out, const byte* input, word32 sz) |
| { |
| switch (ssl->specs.bulk_cipher_algorithm) { |
| #ifdef BUILD_ARC4 |
| case rc4: |
| Arc4Process(&ssl->encrypt.arc4, out, input, sz); |
| break; |
| #endif |
| |
| #ifdef BUILD_DES3 |
| case triple_des: |
| Des3_CbcEncrypt(&ssl->encrypt.des3, out, input, sz); |
| break; |
| #endif |
| |
| #ifdef BUILD_AES |
| case aes: |
| #ifdef CYASSL_AESNI |
| if ((word)input % 16) { |
| byte buffer[MAX_RECORD_SIZE + MAX_COMP_EXTRA+MAX_MSG_EXTRA]; |
| XMEMCPY(buffer, input, sz); |
| AesCbcEncrypt(&ssl->encrypt.aes, buffer, buffer, sz); |
| XMEMCPY(out, buffer, sz); |
| break; |
| } |
| #endif |
| AesCbcEncrypt(&ssl->encrypt.aes, out, input, sz); |
| break; |
| #endif |
| |
| #ifdef BUILD_AESGCM |
| case aes_gcm: |
| { |
| byte additional[AES_BLOCK_SIZE]; |
| |
| XMEMSET(additional, 0, AES_BLOCK_SIZE); |
| |
| /* sequence number field is 64-bits, we only use 32-bits */ |
| c32toa(GetSEQIncrement(ssl, 0), |
| additional + AEAD_SEQ_OFFSET); |
| |
| /* Store the type, version. Unfortunately, they are in |
| * the input buffer ahead of the plaintext. */ |
| XMEMCPY(additional + AEAD_TYPE_OFFSET, input - 5, 3); |
| |
| /* Store the length of the plain text minus the explicit |
| * IV length minus the authentication tag size. */ |
| c16toa(sz - AES_GCM_EXP_IV_SZ - AEAD_AUTH_TAG_SZ, |
| additional + AEAD_LEN_OFFSET); |
| AesGcmEncrypt(&ssl->encrypt.aes, |
| out + AES_GCM_EXP_IV_SZ, input + AES_GCM_EXP_IV_SZ, |
| sz - AES_GCM_EXP_IV_SZ - AEAD_AUTH_TAG_SZ, |
| out + sz - AEAD_AUTH_TAG_SZ, AEAD_AUTH_TAG_SZ, |
| additional, AEAD_AUTH_DATA_SZ); |
| AesGcmIncExpIV(&ssl->encrypt.aes); |
| } |
| break; |
| #endif |
| |
| #ifdef HAVE_HC128 |
| case hc128: |
| Hc128_Process(&ssl->encrypt.hc128, out, input, sz); |
| break; |
| #endif |
| |
| #ifdef BUILD_RABBIT |
| case rabbit: |
| RabbitProcess(&ssl->encrypt.rabbit, out, input, sz); |
| break; |
| #endif |
| |
| default: |
| CYASSL_MSG("CyaSSL Encrypt programming error"); |
| } |
| } |
| |
| |
| static INLINE int Decrypt(CYASSL* ssl, byte* plain, const byte* input, |
| word32 sz) |
| { |
| switch (ssl->specs.bulk_cipher_algorithm) { |
| #ifdef BUILD_ARC4 |
| case rc4: |
| Arc4Process(&ssl->decrypt.arc4, plain, input, sz); |
| break; |
| #endif |
| |
| #ifdef BUILD_DES3 |
| case triple_des: |
| Des3_CbcDecrypt(&ssl->decrypt.des3, plain, input, sz); |
| break; |
| #endif |
| |
| #ifdef BUILD_AES |
| case aes: |
| AesCbcDecrypt(&ssl->decrypt.aes, plain, input, sz); |
| break; |
| #endif |
| |
| #ifdef BUILD_AESGCM |
| case aes_gcm: |
| { |
| byte additional[AES_BLOCK_SIZE]; |
| |
| AesGcmSetExpIV(&ssl->decrypt.aes, input); |
| XMEMSET(additional, 0, AES_BLOCK_SIZE); |
| |
| /* sequence number field is 64-bits, we only use 32-bits */ |
| c32toa(GetSEQIncrement(ssl, 1), additional + AEAD_SEQ_OFFSET); |
| |
| additional[AEAD_TYPE_OFFSET] = ssl->curRL.type; |
| additional[AEAD_VMAJ_OFFSET] = ssl->curRL.version.major; |
| additional[AEAD_VMIN_OFFSET] = ssl->curRL.version.minor; |
| |
| c16toa(sz - AES_GCM_EXP_IV_SZ - AEAD_AUTH_TAG_SZ, |
| additional + AEAD_LEN_OFFSET); |
| if (AesGcmDecrypt(&ssl->decrypt.aes, |
| plain + AES_GCM_EXP_IV_SZ, |
| input + AES_GCM_EXP_IV_SZ, |
| sz - AES_GCM_EXP_IV_SZ - AEAD_AUTH_TAG_SZ, |
| input + sz - AEAD_AUTH_TAG_SZ, AEAD_AUTH_TAG_SZ, |
| additional, AEAD_AUTH_DATA_SZ) < 0) { |
| SendAlert(ssl, alert_fatal, bad_record_mac); |
| return VERIFY_MAC_ERROR; |
| } |
| break; |
| } |
| #endif |
| |
| #ifdef HAVE_HC128 |
| case hc128: |
| Hc128_Process(&ssl->decrypt.hc128, plain, input, sz); |
| break; |
| #endif |
| |
| #ifdef BUILD_RABBIT |
| case rabbit: |
| RabbitProcess(&ssl->decrypt.rabbit, plain, input, sz); |
| break; |
| #endif |
| |
| default: |
| CYASSL_MSG("CyaSSL Decrypt programming error"); |
| } |
| return 0; |
| } |
| |
| |
| /* decrypt input message in place */ |
| static int DecryptMessage(CYASSL* ssl, byte* input, word32 sz, word32* idx) |
| { |
| int decryptResult = Decrypt(ssl, input, input, sz); |
| |
| if (decryptResult == 0) |
| { |
| ssl->keys.encryptSz = sz; |
| if (ssl->options.tls1_1 && ssl->specs.cipher_type == block) |
| *idx += ssl->specs.block_size; /* go past TLSv1.1 IV */ |
| if (ssl->specs.cipher_type == aead) |
| *idx += AES_GCM_EXP_IV_SZ; |
| } |
| |
| return decryptResult; |
| } |
| |
| |
| int DoApplicationData(CYASSL* ssl, byte* input, word32* inOutIdx) |
| { |
| word32 msgSz = ssl->keys.encryptSz; |
| word32 pad = 0, |
| padByte = 0, |
| idx = *inOutIdx, |
| digestSz = ssl->specs.hash_size; |
| int dataSz; |
| int ivExtra = 0; |
| byte* rawData = input + idx; /* keep current for hmac */ |
| #ifdef HAVE_LIBZ |
| byte decomp[MAX_RECORD_SIZE + MAX_COMP_EXTRA]; |
| #endif |
| |
| byte verify[SHA256_DIGEST_SIZE]; |
| const byte* mac; |
| |
| if (ssl->specs.cipher_type == block) { |
| if (ssl->options.tls1_1) |
| ivExtra = ssl->specs.block_size; |
| pad = *(input + idx + msgSz - ivExtra - 1); |
| padByte = 1; |
| } |
| if (ssl->specs.cipher_type == aead) { |
| ivExtra = AES_GCM_EXP_IV_SZ; |
| digestSz = AEAD_AUTH_TAG_SZ; |
| } |
| |
| dataSz = msgSz - ivExtra - digestSz - pad - padByte; |
| if (dataSz < 0) { |
| CYASSL_MSG("App data buffer error, malicious input?"); |
| return BUFFER_ERROR; |
| } |
| |
| /* read data */ |
| if (dataSz) { |
| int rawSz = dataSz; /* keep raw size for hmac */ |
| |
| if (ssl->specs.cipher_type != aead) |
| ssl->hmac(ssl, verify, rawData, rawSz, application_data, 1); |
| |
| #ifdef HAVE_LIBZ |
| if (ssl->options.usingCompression) { |
| dataSz = DeCompress(ssl, rawData, dataSz, decomp, sizeof(decomp)); |
| if (dataSz < 0) return dataSz; |
| } |
| #endif |
| |
| if (ssl->options.usingCompression) |
| idx += rawSz; |
| else |
| idx += dataSz; |
| |
| ssl->buffers.clearOutputBuffer.buffer = rawData; |
| ssl->buffers.clearOutputBuffer.length = dataSz; |
| } |
| |
| /* read mac and fill */ |
| mac = input + idx; |
| idx += digestSz; |
| |
| idx += pad; |
| if (padByte) |
| idx++; |
| |
| /* verify */ |
| if (dataSz) { |
| if (ssl->specs.cipher_type != aead && XMEMCMP(mac, verify, digestSz)) { |
| CYASSL_MSG("App data verify mac error"); |
| return VERIFY_MAC_ERROR; |
| } |
| } |
| else |
| GetSEQIncrement(ssl, 1); /* even though no data, increment verify */ |
| |
| #ifdef HAVE_LIBZ |
| /* decompress could be bigger, overwrite after verify */ |
| if (ssl->options.usingCompression) |
| XMEMMOVE(rawData, decomp, dataSz); |
| #endif |
| |
| *inOutIdx = idx; |
| return 0; |
| } |
| |
| |
| /* process alert, return level */ |
| static int DoAlert(CYASSL* ssl, byte* input, word32* inOutIdx, int* type) |
| { |
| byte level; |
| |
| #ifdef CYASSL_CALLBACKS |
| if (ssl->hsInfoOn) |
| AddPacketName("Alert", &ssl->handShakeInfo); |
| if (ssl->toInfoOn) |
| /* add record header back on to info + 2 byte level, data */ |
| AddPacketInfo("Alert", &ssl->timeoutInfo, input + *inOutIdx - |
| RECORD_HEADER_SZ, 2 + RECORD_HEADER_SZ, ssl->heap); |
| #endif |
| level = input[(*inOutIdx)++]; |
| *type = (int)input[(*inOutIdx)++]; |
| |
| CYASSL_MSG("Got alert"); |
| if (*type == close_notify) { |
| CYASSL_MSG(" close notify"); |
| ssl->options.closeNotify = 1; |
| } |
| CYASSL_ERROR(*type); |
| |
| if (ssl->keys.encryptionOn) { |
| if (ssl->specs.cipher_type != aead) { |
| int aSz = ALERT_SIZE; |
| const byte* mac; |
| byte verify[SHA256_DIGEST_SIZE]; |
| int padSz = ssl->keys.encryptSz - aSz - ssl->specs.hash_size; |
| |
| ssl->hmac(ssl, verify, input + *inOutIdx - aSz, aSz, alert, 1); |
| |
| /* read mac and fill */ |
| mac = input + *inOutIdx; |
| *inOutIdx += (ssl->specs.hash_size + padSz); |
| |
| /* verify */ |
| if (XMEMCMP(mac, verify, ssl->specs.hash_size)) { |
| CYASSL_MSG(" alert verify mac error"); |
| return VERIFY_MAC_ERROR; |
| } |
| } |
| else { |
| *inOutIdx += AEAD_AUTH_TAG_SZ; |
| } |
| } |
| |
| return level; |
| } |
| |
| static int GetInputData(CYASSL *ssl, word32 size) |
| { |
| int in; |
| int inSz; |
| int maxLength; |
| int usedLength; |
| |
| |
| /* check max input length */ |
| usedLength = ssl->buffers.inputBuffer.length - ssl->buffers.inputBuffer.idx; |
| maxLength = ssl->buffers.inputBuffer.bufferSize - usedLength; |
| inSz = (int)(size - usedLength); /* from last partial read */ |
| |
| #ifdef CYASSL_DTLS |
| if (ssl->options.dtls) |
| inSz = MAX_MTU; /* read ahead up to MTU */ |
| #endif |
| |
| if (inSz > maxLength) { |
| if (GrowInputBuffer(ssl, size, usedLength) < 0) |
| return MEMORY_E; |
| } |
| |
| if (inSz <= 0) |
| return BUFFER_ERROR; |
| |
| /* Put buffer data at start if not there */ |
| if (usedLength > 0 && ssl->buffers.inputBuffer.idx != 0) |
| XMEMMOVE(ssl->buffers.inputBuffer.buffer, |
| ssl->buffers.inputBuffer.buffer + ssl->buffers.inputBuffer.idx, |
| usedLength); |
| |
| /* remove processed data */ |
| ssl->buffers.inputBuffer.idx = 0; |
| ssl->buffers.inputBuffer.length = usedLength; |
| |
| /* read data from network */ |
| do { |
| in = Receive(ssl, |
| ssl->buffers.inputBuffer.buffer + |
| ssl->buffers.inputBuffer.length, |
| inSz); |
| if (in == -1) |
| return SOCKET_ERROR_E; |
| |
| if (in == WANT_READ) |
| return WANT_READ; |
| |
| ssl->buffers.inputBuffer.length += in; |
| inSz -= in; |
| |
| } while (ssl->buffers.inputBuffer.length < size); |
| |
| return 0; |
| } |
| |
| /* process input requests, return 0 is done, 1 is call again to complete, and |
| negative number is error */ |
| int ProcessReply(CYASSL* ssl) |
| { |
| int ret, type, readSz; |
| word32 startIdx = 0; |
| #ifndef NO_CYASSL_SERVER |
| byte b0, b1; |
| #endif |
| #ifdef CYASSL_DTLS |
| int used; |
| #endif |
| |
| for (;;) { |
| switch ((processReply)ssl->options.processReply) { |
| |
| /* in the CYASSL_SERVER case, get the first byte for detecting |
| * old client hello */ |
| case doProcessInit: |
| |
| readSz = RECORD_HEADER_SZ; |
| |
| #ifdef CYASSL_DTLS |
| if (ssl->options.dtls) |
| readSz = DTLS_RECORD_HEADER_SZ; |
| #endif |
| |
| /* get header or return error */ |
| if (!ssl->options.dtls) { |
| if ((ret = GetInputData(ssl, readSz)) < 0) |
| return ret; |
| } else { |
| #ifdef CYASSL_DTLS |
| /* read ahead may already have header */ |
| used = ssl->buffers.inputBuffer.length - |
| ssl->buffers.inputBuffer.idx; |
| if (used < readSz) |
| if ((ret = GetInputData(ssl, readSz)) < 0) |
| return ret; |
| #endif |
| } |
| |
| #ifndef NO_CYASSL_SERVER |
| |
| /* see if sending SSLv2 client hello */ |
| if ( ssl->options.side == SERVER_END && |
| ssl->options.clientState == NULL_STATE && |
| ssl->buffers.inputBuffer.buffer[ssl->buffers.inputBuffer.idx] |
| != handshake) { |
| ssl->options.processReply = runProcessOldClientHello; |
| |
| /* how many bytes need ProcessOldClientHello */ |
| b0 = |
| ssl->buffers.inputBuffer.buffer[ssl->buffers.inputBuffer.idx++]; |
| b1 = |
| ssl->buffers.inputBuffer.buffer[ssl->buffers.inputBuffer.idx++]; |
| ssl->curSize = ((b0 & 0x7f) << 8) | b1; |
| } |
| else { |
| ssl->options.processReply = getRecordLayerHeader; |
| continue; |
| } |
| |
| /* in the CYASSL_SERVER case, run the old client hello */ |
| case runProcessOldClientHello: |
| |
| /* get sz bytes or return error */ |
| if (!ssl->options.dtls) { |
| if ((ret = GetInputData(ssl, ssl->curSize)) < 0) |
| return ret; |
| } else { |
| #ifdef CYASSL_DTLS |
| /* read ahead may already have */ |
| used = ssl->buffers.inputBuffer.length - |
| ssl->buffers.inputBuffer.idx; |
| if (used < ssl->curSize) |
| if ((ret = GetInputData(ssl, ssl->curSize)) < 0) |
| return ret; |
| #endif /* CYASSL_DTLS */ |
| } |
| |
| ret = ProcessOldClientHello(ssl, ssl->buffers.inputBuffer.buffer, |
| &ssl->buffers.inputBuffer.idx, |
| ssl->buffers.inputBuffer.length - |
| ssl->buffers.inputBuffer.idx, |
| ssl->curSize); |
| if (ret < 0) |
| return ret; |
| |
| else if (ssl->buffers.inputBuffer.idx == |
| ssl->buffers.inputBuffer.length) { |
| ssl->options.processReply = doProcessInit; |
| return 0; |
| } |
| |
| #endif /* NO_CYASSL_SERVER */ |
| |
| /* get the record layer header */ |
| case getRecordLayerHeader: |
| |
| ret = GetRecordHeader(ssl, ssl->buffers.inputBuffer.buffer, |
| &ssl->buffers.inputBuffer.idx, |
| &ssl->curRL, &ssl->curSize); |
| if (ret != 0) |
| return ret; |
| |
| ssl->options.processReply = getData; |
| |
| /* retrieve record layer data */ |
| case getData: |
| |
| /* get sz bytes or return error */ |
| if (!ssl->options.dtls) { |
| if ((ret = GetInputData(ssl, ssl->curSize)) < 0) |
| return ret; |
| } else { |
| #ifdef CYASSL_DTLS |
| /* read ahead may already have */ |
| used = ssl->buffers.inputBuffer.length - |
| ssl->buffers.inputBuffer.idx; |
| if (used < ssl->curSize) |
| if ((ret = GetInputData(ssl, ssl->curSize)) < 0) |
| return ret; |
| #endif |
| } |
| |
| ssl->options.processReply = runProcessingOneMessage; |
| startIdx = ssl->buffers.inputBuffer.idx; /* in case > 1 msg per */ |
| |
| /* the record layer is here */ |
| case runProcessingOneMessage: |
| |
| if (ssl->keys.encryptionOn) |
| if (DecryptMessage(ssl, ssl->buffers.inputBuffer.buffer + |
| ssl->buffers.inputBuffer.idx, |
| ssl->curSize, |
| &ssl->buffers.inputBuffer.idx) < 0) |
| return DECRYPT_ERROR; |
| |
| CYASSL_MSG("received record layer msg"); |
| |
| switch (ssl->curRL.type) { |
| case handshake : |
| /* debugging in DoHandShakeMsg */ |
| if ((ret = DoHandShakeMsg(ssl, |
| ssl->buffers.inputBuffer.buffer, |
| &ssl->buffers.inputBuffer.idx, |
| ssl->buffers.inputBuffer.length)) |
| != 0) |
| return ret; |
| break; |
| |
| case change_cipher_spec: |
| CYASSL_MSG("got CHANGE CIPHER SPEC"); |
| #ifdef CYASSL_CALLBACKS |
| if (ssl->hsInfoOn) |
| AddPacketName("ChangeCipher", &ssl->handShakeInfo); |
| /* add record header back on info */ |
| if (ssl->toInfoOn) { |
| AddPacketInfo("ChangeCipher", &ssl->timeoutInfo, |
| ssl->buffers.inputBuffer.buffer + |
| ssl->buffers.inputBuffer.idx - RECORD_HEADER_SZ, |
| 1 + RECORD_HEADER_SZ, ssl->heap); |
| AddLateRecordHeader(&ssl->curRL, &ssl->timeoutInfo); |
| } |
| #endif |
| ssl->buffers.inputBuffer.idx++; |
| ssl->keys.encryptionOn = 1; |
| |
| #ifdef CYASSL_DTLS |
| if (ssl->options.dtls) |
| ssl->keys.dtls_peer_epoch++; |
| #endif |
| |
| #ifdef HAVE_LIBZ |
| if (ssl->options.usingCompression) |
| if ( (ret = InitStreams(ssl)) != 0) |
| return ret; |
| #endif |
| if (ssl->options.resuming && ssl->options.side == |
| CLIENT_END) |
| BuildFinished(ssl, &ssl->verifyHashes, server); |
| else if (!ssl->options.resuming && ssl->options.side == |
| SERVER_END) |
| BuildFinished(ssl, &ssl->verifyHashes, client); |
| break; |
| |
| case application_data: |
| CYASSL_MSG("got app DATA"); |
| if ((ret = DoApplicationData(ssl, |
| ssl->buffers.inputBuffer.buffer, |
| &ssl->buffers.inputBuffer.idx)) |
| != 0) { |
| CYASSL_ERROR(ret); |
| return ret; |
| } |
| break; |
| |
| case alert: |
| CYASSL_MSG("got ALERT!"); |
| if (DoAlert(ssl, ssl->buffers.inputBuffer.buffer, |
| &ssl->buffers.inputBuffer.idx, &type) == alert_fatal) |
| return FATAL_ERROR; |
| |
| /* catch warnings that are handled as errors */ |
| if (type == close_notify) |
| return ssl->error = ZERO_RETURN; |
| |
| if (type == decrypt_error) |
| return FATAL_ERROR; |
| break; |
| |
| default: |
| CYASSL_ERROR(UNKNOWN_RECORD_TYPE); |
| return UNKNOWN_RECORD_TYPE; |
| } |
| |
| ssl->options.processReply = doProcessInit; |
| |
| /* input exhausted? */ |
| if (ssl->buffers.inputBuffer.idx == ssl->buffers.inputBuffer.length) |
| return 0; |
| /* more messages per record */ |
| else if ((ssl->buffers.inputBuffer.idx - startIdx) < ssl->curSize) { |
| CYASSL_MSG("More messages in record"); |
| #ifdef CYASSL_DTLS |
| /* read-ahead but dtls doesn't bundle messages per record */ |
| if (ssl->options.dtls) { |
| ssl->options.processReply = doProcessInit; |
| continue; |
| } |
| #endif |
| ssl->options.processReply = runProcessingOneMessage; |
| continue; |
| } |
| /* more records */ |
| else { |
| CYASSL_MSG("More records in input"); |
| ssl->options.processReply = doProcessInit; |
| continue; |
| } |
| default: |
| CYASSL_MSG("Bad process input state, programming error"); |
| return INPUT_CASE_ERROR; |
| } |
| } |
| } |
| |
| |
| int SendChangeCipher(CYASSL* ssl) |
| { |
| byte *output; |
| int sendSz = RECORD_HEADER_SZ + ENUM_LEN; |
| int idx = RECORD_HEADER_SZ; |
| int ret; |
| |
| #ifdef CYASSL_DTLS |
| if (ssl->options.dtls) { |
| sendSz += DTLS_RECORD_EXTRA; |
| idx += DTLS_RECORD_EXTRA; |
| } |
| #endif |
| |
| /* check for avalaible size */ |
| if ((ret = CheckAvalaibleSize(ssl, sendSz)) != 0) |
| return ret; |
| |
| /* get ouput buffer */ |
| output = ssl->buffers.outputBuffer.buffer + |
| ssl->buffers.outputBuffer.length; |
| |
| AddRecordHeader(output, 1, change_cipher_spec, ssl); |
| |
| output[idx] = 1; /* turn it on */ |
| |
| #ifdef CYASSL_CALLBACKS |
| if (ssl->hsInfoOn) AddPacketName("ChangeCipher", &ssl->handShakeInfo); |
| if (ssl->toInfoOn) |
| AddPacketInfo("ChangeCipher", &ssl->timeoutInfo, output, sendSz, |
| ssl->heap); |
| #endif |
| ssl->buffers.outputBuffer.length += sendSz; |
| |
| if (ssl->options.groupMessages) |
| return 0; |
| else |
| return SendBuffered(ssl); |
| } |
| |
| |
| static INLINE const byte* GetMacSecret(CYASSL* ssl, int verify) |
| { |
| if ( (ssl->options.side == CLIENT_END && !verify) || |
| (ssl->options.side == SERVER_END && verify) ) |
| return ssl->keys.client_write_MAC_secret; |
| else |
| return ssl->keys.server_write_MAC_secret; |
| } |
| |
| |
| static void Hmac(CYASSL* ssl, byte* digest, const byte* in, word32 sz, |
| int content, int verify) |
| { |
| byte result[SHA256_DIGEST_SIZE]; /* max possible sizes */ |
| word32 digestSz = ssl->specs.hash_size; /* actual sizes */ |
| word32 padSz = ssl->specs.pad_size; |
| |
| Md5 md5; |
| Sha sha; |
| |
| /* data */ |
| byte seq[SEQ_SZ] = { 0x00, 0x00, 0x00, 0x00 }; |
| byte conLen[ENUM_LEN + LENGTH_SZ]; /* content & length */ |
| const byte* macSecret = GetMacSecret(ssl, verify); |
| |
| conLen[0] = (byte)content; |
| c16toa((word16)sz, &conLen[ENUM_LEN]); |
| c32toa(GetSEQIncrement(ssl, verify), &seq[sizeof(word32)]); |
| |
| if (ssl->specs.mac_algorithm == md5_mac) { |
| InitMd5(&md5); |
| /* inner */ |
| Md5Update(&md5, macSecret, digestSz); |
| Md5Update(&md5, PAD1, padSz); |
| Md5Update(&md5, seq, SEQ_SZ); |
| Md5Update(&md5, conLen, sizeof(conLen)); |
| /* in buffer */ |
| Md5Update(&md5, in, sz); |
| Md5Final(&md5, result); |
| /* outer */ |
| Md5Update(&md5, macSecret, digestSz); |
| Md5Update(&md5, PAD2, padSz); |
| Md5Update(&md5, result, digestSz); |
| Md5Final(&md5, digest); |
| } |
| else { |
| InitSha(&sha); |
| /* inner */ |
| ShaUpdate(&sha, macSecret, digestSz); |
| ShaUpdate(&sha, PAD1, padSz); |
| |