| /* 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); |
| ShaUpdate(&sha, seq, SEQ_SZ); |
| ShaUpdate(&sha, conLen, sizeof(conLen)); |
| /* in buffer */ |
| ShaUpdate(&sha, in, sz); |
| ShaFinal(&sha, result); |
| /* outer */ |
| ShaUpdate(&sha, macSecret, digestSz); |
| ShaUpdate(&sha, PAD2, padSz); |
| ShaUpdate(&sha, result, digestSz); |
| ShaFinal(&sha, digest); |
| } |
| } |
| |
| |
| static void BuildMD5_CertVerify(CYASSL* 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(CYASSL* 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(CYASSL* ssl, Hashes* hashes) |
| { |
| /* store current states, building requires get_digest which resets state */ |
| Md5 md5 = ssl->hashMd5; |
| Sha sha = ssl->hashSha; |
| #ifndef NO_SHA256 /* for possible future changes */ |
| Sha256 sha256; |
| InitSha256(&sha256); |
| if (IsAtLeastTLSv1_2(ssl)) |
| sha256 = ssl->hashSha256; |
| #endif |
| |
| if (ssl->options.tls) { |
| Md5Final(&ssl->hashMd5, hashes->md5); |
| ShaFinal(&ssl->hashSha, hashes->sha); |
| } |
| else { |
| BuildMD5_CertVerify(ssl, hashes->md5); |
| BuildSHA_CertVerify(ssl, hashes->sha); |
| } |
| |
| /* restore */ |
| ssl->hashMd5 = md5; |
| ssl->hashSha = sha; |
| #ifndef NO_SHA256 |
| if (IsAtLeastTLSv1_2(ssl)) |
| ssl->hashSha256 = sha256; |
| #endif |
| } |
| |
| |
| /* Build SSL Message, encrypted */ |
| static int BuildMessage(CYASSL* 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; |
| } |
| |
| #ifdef BUILD_AESGCM |
| if (ssl->specs.cipher_type == aead) { |
| ivSz = AES_GCM_EXP_IV_SZ; |
| sz += (ivSz + 16 - digestSz); |
| AesGcmGetExpIV(&ssl->encrypt.aes, iv); |
| } |
| #endif |
| size = (word16)(sz - headerSz); /* include mac and digest */ |
| AddRecordHeader(output, size, (byte)type, ssl); |
| |
| /* write to output */ |
| if (ivSz) { |
| XMEMCPY(output + idx, iv, min(ivSz, sizeof(iv))); |
| idx += ivSz; |
| } |
| XMEMCPY(output + idx, input, inSz); |
| idx += inSz; |
| |
| if (type == handshake) |
| HashOutput(ssl, output, headerSz + inSz, ivSz); |
| if (ssl->specs.cipher_type != aead) { |
| 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++] = (byte)pad; /* pad byte gets pad value too */ |
| |
| Encrypt(ssl, output + headerSz, output + headerSz, size); |
| |
| return sz; |
| } |
| |
| |
| int SendFinished(CYASSL* 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.length; |
| |
| 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)) < 0) |
| return BUILD_MSG_ERROR; |
| |
| if (!ssl->options.resuming) { |
| #ifndef NO_SESSION_CACHE |
| AddSession(ssl); /* just try */ |
| #endif |
| 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(CYASSL* 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; |
| |
| /* may need to send rest of chain, already has leading size(s) */ |
| if (ssl->buffers.certChain.buffer) { |
| length += ssl->buffers.certChain.length; |
| listSz += ssl->buffers.certChain.length; |
| } |
| } |
| 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.length; |
| |
| 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; |
| |
| /* send rest of chain? */ |
| if (ssl->buffers.certChain.buffer) { |
| XMEMCPY(output + i, ssl->buffers.certChain.buffer, |
| ssl->buffers.certChain.length); |
| i += ssl->buffers.certChain.length; |
| } |
| } |
| 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; |
| if (ssl->options.groupMessages) |
| return 0; |
| else |
| return SendBuffered(ssl); |
| } |
| |
| |
| int SendCertificateRequest(CYASSL* ssl) |
| { |
| byte *output; |
| int ret; |
| int sendSz; |
| word32 i = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ; |
| |
| int typeTotal = 1; /* only rsa for now */ |
| int reqSz = ENUM_LEN + typeTotal + REQ_HEADER_SZ; /* add auth later */ |
| |
| if (IsAtLeastTLSv1_2(ssl)) |
| reqSz += LENGTH_SZ + HASH_SIG_SIZE; |
| |
| if (ssl->options.usingPSK_cipher) return 0; /* not needed */ |
| |
| sendSz = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ + reqSz; |
| |
| #ifdef CYASSL_DTLS |
| if (ssl->options.dtls) { |
| sendSz += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA; |
| i += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA; |
| } |
| #endif |
| /* check for avalaible size */ |
| if ((ret = CheckAvalaibleSize(ssl, sendSz)) != 0) |
| return ret; |
| |
| /* get ouput buffer */ |
| output = ssl->buffers.outputBuffer.buffer + |
| ssl->buffers.outputBuffer.length; |
| |
| AddHeaders(output, reqSz, certificate_request, ssl); |
| |
| /* write to output */ |
| output[i++] = (byte)typeTotal; /* # of types */ |
| output[i++] = rsa_sign; |
| |
| /* supported hash/sig */ |
| if (IsAtLeastTLSv1_2(ssl)) { |
| c16toa(HASH_SIG_SIZE, &output[i]); |
| i += LENGTH_SZ; |
| |
| output[i++] = sha_mac; /* hash */ |
| output[i++] = rsa_sa_algo; /* sig */ |
| } |
| |
| c16toa(0, &output[i]); /* auth's */ |
| i += REQ_HEADER_SZ; |
| |
| HashOutput(ssl, output, sendSz, 0); |
| |
| #ifdef CYASSL_CALLBACKS |
| if (ssl->hsInfoOn) |
| AddPacketName("CertificateRequest", &ssl->handShakeInfo); |
| if (ssl->toInfoOn) |
| AddPacketInfo("CertificateRequest", &ssl->timeoutInfo, output, |
| sendSz, ssl->heap); |
| #endif |
| ssl->buffers.outputBuffer.length += sendSz; |
| if (ssl->options.groupMessages) |
| return 0; |
| else |
| return SendBuffered(ssl); |
| } |
| |
| |
| int SendData(CYASSL* ssl, const void* data, 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; |
| CYASSL_MSG("handshake not complete, trying to finish"); |
| 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) { |
| CYASSL_MSG("output buffer was full, trying to send again"); |
| 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*)data + 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.length; |
| |
| #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) { |
| CYASSL_MSG("Paritial Write on, only sending one record"); |
| break; |
| } |
| } |
| |
| return sent; |
| } |
| |
| /* process input data */ |
| int ReceiveData(CYASSL* 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; |
| CYASSL_MSG("Handshake not complete, trying to finish"); |
| 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) { |
| CYASSL_MSG("Zero return, no more data coming"); |
| ssl->options.isClosed = 1; |
| return 0; /* no more data coming */ |
| } |
| if (ssl->error == SOCKET_ERROR_E) { |
| if (ssl->options.connReset || ssl->options.isClosed) { |
| CYASSL_MSG("Peer reset or closed, connection done"); |
| 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(CYASSL* 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.length; |
| |
| input[0] = (byte)severity; |
| input[1] = (byte)type; |
| |
| if (ssl->keys.encryptionOn) |
| sendSz = BuildMessage(ssl, output, input, ALERT_SIZE, 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, ALERT_SIZE); |
| sendSz = RECORD_HEADER_SZ + ALERT_SIZE; |
| } |
| |
| #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* str) |
| { |
| const int max = MAX_ERROR_SZ; /* shorthand */ |
| |
| #ifdef NO_ERROR_STRINGS |
| |
| XSTRNCPY(str, "no support for error strings built in", max); |
| |
| #else |
| |
| /* pass to CTaoCrypt */ |
| if (error < MAX_CODE_E && error > MIN_CODE_E) { |
| CTaoCryptErrorString(error, str); |
| return; |
| } |
| |
| switch (error) { |
| |
| case UNSUPPORTED_SUITE : |
| XSTRNCPY(str, "unsupported cipher suite", max); |
| break; |
| |
| case INPUT_CASE_ERROR : |
| XSTRNCPY(str, "input state error", max); |
| break; |
| |
| case PREFIX_ERROR : |
| XSTRNCPY(str, "bad index to key rounds", max); |
| break; |
| |
| case MEMORY_ERROR : |
| XSTRNCPY(str, "out of memory", max); |
| break; |
| |
| case VERIFY_FINISHED_ERROR : |
| XSTRNCPY(str, "verify problem on finished", max); |
| break; |
| |
| case VERIFY_MAC_ERROR : |
| XSTRNCPY(str, "verify mac problem", max); |
| break; |
| |
| case PARSE_ERROR : |
| XSTRNCPY(str, "parse error on header", max); |
| break; |
| |
| case SIDE_ERROR : |
| XSTRNCPY(str, "wrong client/server type", max); |
| break; |
| |
| case NO_PEER_CERT : |
| XSTRNCPY(str, "peer didn't send cert", max); |
| break; |
| |
| case UNKNOWN_HANDSHAKE_TYPE : |
| XSTRNCPY(str, "weird handshake type", max); |
| break; |
| |
| case SOCKET_ERROR_E : |
| XSTRNCPY(str, "error state on socket", max); |
| break; |
| |
| case SOCKET_NODATA : |
| XSTRNCPY(str, "expected data, not there", max); |
| break; |
| |
| case INCOMPLETE_DATA : |
| XSTRNCPY(str, "don't have enough data to complete task", max); |
| break; |
| |
| case UNKNOWN_RECORD_TYPE : |
| XSTRNCPY(str, "unknown type in record hdr", max); |
| break; |
| |
| case DECRYPT_ERROR : |
| XSTRNCPY(str, "error during decryption", max); |
| break; |
| |
| case FATAL_ERROR : |
| XSTRNCPY(str, "revcd alert fatal error", max); |
| break; |
| |
| case ENCRYPT_ERROR : |
| XSTRNCPY(str, "error during encryption", max); |
| break; |
| |
| case FREAD_ERROR : |
| XSTRNCPY(str, "fread problem", max); |
| break; |
| |
| case NO_PEER_KEY : |
| XSTRNCPY(str, "need peer's key", max); |
| break; |
| |
| case NO_PRIVATE_KEY : |
| XSTRNCPY(str, "need the private key", max); |
| break; |
| |
| case NO_DH_PARAMS : |
| XSTRNCPY(str, "server missing DH params", max); |
| break; |
| |
| case RSA_PRIVATE_ERROR : |
| XSTRNCPY(str, "error during rsa priv op", max); |
| break; |
| |
| case MATCH_SUITE_ERROR : |
| XSTRNCPY(str, "can't match cipher suite", max); |
| break; |
| |
| case BUILD_MSG_ERROR : |
| XSTRNCPY(str, "build message failure", max); |
| break; |
| |
| case BAD_HELLO : |
| XSTRNCPY(str, "client hello malformed", max); |
| break; |
| |
| case DOMAIN_NAME_MISMATCH : |
| XSTRNCPY(str, "peer subject name mismatch", max); |
| break; |
| |
| case WANT_READ : |
| XSTRNCPY(str, "non-blocking socket wants data to be read", max); |
| break; |
| |
| case NOT_READY_ERROR : |
| XSTRNCPY(str, "handshake layer not ready yet, complete first", max); |
| break; |
| |
| case PMS_VERSION_ERROR : |
| XSTRNCPY(str, "premaster secret version mismatch error", max); |
| break; |
| |
| case VERSION_ERROR : |
| XSTRNCPY(str, "record layer version error", max); |
| break; |
| |
| case WANT_WRITE : |
| XSTRNCPY(str, "non-blocking socket write buffer full", max); |
| break; |
| |
| case BUFFER_ERROR : |
| XSTRNCPY(str, "malformed buffer input error", max); |
| break; |
| |
| case VERIFY_CERT_ERROR : |
| XSTRNCPY(str, "verify problem on certificate", max); |
| break; |
| |
| case VERIFY_SIGN_ERROR : |
| XSTRNCPY(str, "verify problem based on signature", max); |
| break; |
| |
| case CLIENT_ID_ERROR : |
| XSTRNCPY(str, "psk client identity error", max); |
| break; |
| |
| case SERVER_HINT_ERROR: |
| XSTRNCPY(str, "psk server hint error", max); |
| break; |
| |
| case PSK_KEY_ERROR: |
| XSTRNCPY(str, "psk key callback error", max); |
| break; |
| |
| case NTRU_KEY_ERROR: |
| XSTRNCPY(str, "NTRU key error", max); |
| break; |
| |
| case NTRU_DRBG_ERROR: |
| XSTRNCPY(str, "NTRU drbg error", max); |
| break; |
| |
| case NTRU_ENCRYPT_ERROR: |
| XSTRNCPY(str, "NTRU encrypt error", max); |
| break; |
| |
| case NTRU_DECRYPT_ERROR: |
| XSTRNCPY(str, "NTRU decrypt error", max); |
| break; |
| |
| case ZLIB_INIT_ERROR: |
| XSTRNCPY(str, "zlib init error", max); |
| break; |
| |
| case ZLIB_COMPRESS_ERROR: |
| XSTRNCPY(str, "zlib compress error", max); |
| break; |
| |
| case ZLIB_DECOMPRESS_ERROR: |
| XSTRNCPY(str, "zlib decompress error", max); |
| break; |
| |
| case GETTIME_ERROR: |
| XSTRNCPY(str, "gettimeofday() error", max); |
| break; |
| |
| case GETITIMER_ERROR: |
| XSTRNCPY(str, "getitimer() error", max); |
| break; |
| |
| case SIGACT_ERROR: |
| XSTRNCPY(str, "sigaction() error", max); |
| break; |
| |
| case SETITIMER_ERROR: |
| XSTRNCPY(str, "setitimer() error", max); |
| break; |
| |
| case LENGTH_ERROR: |
| XSTRNCPY(str, "record layer length error", max); |
| break; |
| |
| case PEER_KEY_ERROR: |
| XSTRNCPY(str, "cant decode peer key", max); |
| break; |
| |
| case ZERO_RETURN: |
| XSTRNCPY(str, "peer sent close notify alert", max); |
| break; |
| |
| case ECC_CURVETYPE_ERROR: |
| XSTRNCPY(str, "Bad ECC Curve Type or unsupported", max); |
| break; |
| |
| case ECC_CURVE_ERROR: |
| XSTRNCPY(str, "Bad ECC Curve or unsupported", max); |
| break; |
| |
| case ECC_PEERKEY_ERROR: |
| XSTRNCPY(str, "Bad ECC Peer Key", max); |
| break; |
| |
| case ECC_MAKEKEY_ERROR: |
| XSTRNCPY(str, "ECC Make Key failure", max); |
| break; |
| |
| case ECC_EXPORT_ERROR: |
| XSTRNCPY(str, "ECC Export Key failure", max); |
| break; |
| |
| case ECC_SHARED_ERROR: |
| XSTRNCPY(str, "ECC DHE shared failure", max); |
| break; |
| |
| case BAD_MUTEX_ERROR: |
| XSTRNCPY(str, "Bad mutex, operation failed", max); |
| break; |
| |
| case NOT_CA_ERROR: |
| XSTRNCPY(str, "Not a CA by basic constraint error", max); |
| break; |
| |
| case BAD_PATH_ERROR: |
| XSTRNCPY(str, "Bad path for opendir error", max); |
| break; |
| |
| case BAD_CERT_MANAGER_ERROR: |
| XSTRNCPY(str, "Bad Cert Manager error", max); |
| break; |
| |
| case OCSP_CERT_REVOKED: |
| XSTRNCPY(str, "OCSP Cert revoked", max); |
| break; |
| |
| case CRL_CERT_REVOKED: |
| XSTRNCPY(str, "CRL Cert revoked", max); |
| break; |
| |
| case CRL_MISSING: |
| XSTRNCPY(str, "CRL missing, not loaded", max); |
| break; |
| |
| case MONITOR_RUNNING_E: |
| XSTRNCPY(str, "CRL monitor already running", max); |
| break; |
| |
| case THREAD_CREATE_E: |
| XSTRNCPY(str, "Thread creation problem", max); |
| break; |
| |
| case OCSP_NEED_URL: |
| XSTRNCPY(str, "OCSP need URL", max); |
| break; |
| |
| case OCSP_CERT_UNKNOWN: |
| XSTRNCPY(str, "OCSP Cert unknown", max); |
| break; |
| |
| case OCSP_LOOKUP_FAIL: |
| XSTRNCPY(str, "OCSP Responder lookup fail", max); |
| break; |
| |
| case MAX_CHAIN_ERROR: |
| XSTRNCPY(str, "Maximum Chain Depth Exceeded", max); |
| break; |
| |
| case COOKIE_ERROR: |
| XSTRNCPY(str, "DTLS Cookie Error", max); |
| break; |
| |
| default : |
| XSTRNCPY(str, "unknown error number", max); |
| } |
| |
| #endif /* NO_ERROR_STRINGS */ |
| } |
| |
| |
| |
| /* be sure to add to cipher_name_idx too !!!! */ |
| const char* const cipher_names[] = |
| { |
| #ifdef BUILD_SSL_RSA_WITH_RC4_128_SHA |
| "RC4-SHA", |
| #endif |
| |
| #ifdef BUILD_SSL_RSA_WITH_RC4_128_MD5 |
| "RC4-MD5", |
| #endif |
| |
| #ifdef BUILD_SSL_RSA_WITH_3DES_EDE_CBC_SHA |
| "DES-CBC3-SHA", |
| #endif |
| |
| #ifdef BUILD_TLS_RSA_WITH_AES_128_CBC_SHA |
| "AES128-SHA", |
| #endif |
| |
| #ifdef BUILD_TLS_RSA_WITH_AES_256_CBC_SHA |
| "AES256-SHA", |
| #endif |
| |
| #ifdef BUILD_TLS_DHE_RSA_WITH_AES_128_CBC_SHA |
| "DHE-RSA-AES128-SHA", |
| #endif |
| |
| #ifdef BUILD_TLS_DHE_RSA_WITH_AES_256_CBC_SHA |
| "DHE-RSA-AES256-SHA", |
| #endif |
| |
| #ifdef BUILD_TLS_PSK_WITH_AES_128_CBC_SHA |
| "PSK-AES128-CBC-SHA", |
| #endif |
| |
| #ifdef BUILD_TLS_PSK_WITH_AES_256_CBC_SHA |
| "PSK-AES256-CBC-SHA", |
| #endif |
| |
| #ifdef BUILD_TLS_RSA_WITH_HC_128_CBC_MD5 |
| "HC128-MD5", |
| #endif |
| |
| #ifdef BUILD_TLS_RSA_WITH_HC_128_CBC_SHA |
| "HC128-SHA", |
| #endif |
| |
| #ifdef BUILD_TLS_RSA_WITH_RABBIT_CBC_SHA |
| "RABBIT-SHA", |
| #endif |
| |
| #ifdef BUILD_TLS_NTRU_RSA_WITH_RC4_128_SHA |
| "NTRU-RC4-SHA", |
| #endif |
| |
| #ifdef BUILD_TLS_NTRU_RSA_WITH_3DES_EDE_CBC_SHA |
| "NTRU-DES-CBC3-SHA", |
| #endif |
| |
| #ifdef BUILD_TLS_NTRU_RSA_WITH_AES_128_CBC_SHA |
| "NTRU-AES128-SHA", |
| #endif |
| |
| #ifdef BUILD_TLS_NTRU_RSA_WITH_AES_256_CBC_SHA |
| "NTRU-AES256-SHA", |
| #endif |
| |
| #ifdef BUILD_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA |
| "ECDHE-RSA-AES128-SHA", |
| #endif |
| |
| #ifdef BUILD_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA |
| "ECDHE-RSA-AES256-SHA", |
| #endif |
| |
| #ifdef BUILD_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA |
| "ECDHE-ECDSA-AES128-SHA", |
| #endif |
| |
| #ifdef BUILD_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA |
| "ECDHE-ECDSA-AES256-SHA", |
| #endif |
| |
| #ifdef BUILD_TLS_ECDHE_RSA_WITH_RC4_128_SHA |
| "ECDHE-RSA-RC4-SHA", |
| #endif |
| |
| #ifdef BUILD_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA |
| "ECDHE-RSA-DES-CBC3-SHA", |
| #endif |
| |
| #ifdef BUILD_TLS_ECDHE_ECDSA_WITH_RC4_128_SHA |
| "ECDHE-ECDSA-RC4-SHA", |
| #endif |
| |
| #ifdef BUILD_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA |
| "ECDHE-ECDSA-DES-CBC3-SHA", |
| #endif |
| |
| #ifdef BUILD_TLS_RSA_WITH_AES_128_CBC_SHA256 |
| "AES128-SHA256", |
| #endif |
| |
| #ifdef BUILD_TLS_RSA_WITH_AES_256_CBC_SHA256 |
| "AES256-SHA256", |
| #endif |
| |
| #ifdef BUILD_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 |
| "DHE-RSA-AES128-SHA256", |
| #endif |
| |
| #ifdef BUILD_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 |
| "DHE-RSA-AES256-SHA256", |
| #endif |
| |
| #ifdef BUILD_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA |
| "ECDH-RSA-AES128-SHA", |
| #endif |
| |
| #ifdef BUILD_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA |
| "ECDH-RSA-AES256-SHA", |
| #endif |
| |
| #ifdef BUILD_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA |
| "ECDH-ECDSA-AES128-SHA", |
| #endif |
| |
| #ifdef BUILD_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA |
| "ECDH-ECDSA-AES256-SHA", |
| #endif |
| |
| #ifdef BUILD_TLS_ECDH_RSA_WITH_RC4_128_SHA |
| "ECDH-RSA-RC4-SHA", |
| #endif |
| |
| #ifdef BUILD_TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA |
| "ECDH-RSA-DES-CBC3-SHA", |
| #endif |
| |
| #ifdef BUILD_TLS_ECDH_ECDSA_WITH_RC4_128_SHA |
| "ECDH-ECDSA-RC4-SHA", |
| #endif |
| |
| #ifdef BUILD_TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA |
| "ECDH-ECDSA-DES-CBC3-SHA", |
| #endif |
| |
| #ifdef BUILD_TLS_RSA_WITH_AES_128_GCM_SHA256 |
| "AES128-GCM-SHA256", |
| #endif |
| |
| #ifdef BUILD_TLS_RSA_WITH_AES_256_GCM_SHA384 |
| "AES256-GCM-SHA384", |
| #endif |
| |
| #ifdef BUILD_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 |
| "DHE-RSA-AES128-GCM-SHA256", |
| #endif |
| |
| #ifdef BUILD_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 |
| "DHE-RSA-AES256-GCM-SHA384", |
| #endif |
| |
| #ifdef BUILD_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 |
| "ECDHE-RSA-AES128-GCM-SHA256", |
| #endif |
| |
| #ifdef BUILD_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 |
| "ECDHE-RSA-AES256-GCM-SHA384", |
| #endif |
| |
| #ifdef BUILD_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 |
| "ECDHE-ECDSA-AES128-GCM-SHA256", |
| #endif |
| |
| #ifdef BUILD_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 |
| "ECDHE-ECDSA-AES256-GCM-SHA384", |
| #endif |
| |
| #ifdef BUILD_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 |
| "ECDH-RSA-AES128-GCM-SHA256", |
| #endif |
| |
| #ifdef BUILD_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 |
| "ECDH-RSA-AES256-GCM-SHA384", |
| #endif |
| |
| #ifdef BUILD_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 |
| "ECDH-ECDSA-AES128-GCM-SHA256", |
| #endif |
| |
| #ifdef BUILD_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 |
| "ECDH-ECDSA-AES256-GCM-SHA384" |
| #endif |
| |
| }; |
| |
| |
| |
| /* cipher suite number that matches above name table */ |
| int cipher_name_idx[] = |
| { |
| |
| #ifdef BUILD_SSL_RSA_WITH_RC4_128_SHA |
| SSL_RSA_WITH_RC4_128_SHA, |
| #endif |
| |
| #ifdef BUILD_SSL_RSA_WITH_RC4_128_MD5 |
| SSL_RSA_WITH_RC4_128_MD5, |
| #endif |
| |
| #ifdef BUILD_SSL_RSA_WITH_3DES_EDE_CBC_SHA |
| SSL_RSA_WITH_3DES_EDE_CBC_SHA, |
| #endif |
| |
| #ifdef BUILD_TLS_RSA_WITH_AES_128_CBC_SHA |
| TLS_RSA_WITH_AES_128_CBC_SHA, |
| #endif |
| |
| #ifdef BUILD_TLS_RSA_WITH_AES_256_CBC_SHA |
| TLS_RSA_WITH_AES_256_CBC_SHA, |
| #endif |
| |
| #ifdef BUILD_TLS_DHE_RSA_WITH_AES_128_CBC_SHA |
| TLS_DHE_RSA_WITH_AES_128_CBC_SHA, |
| #endif |
| |
| #ifdef BUILD_TLS_DHE_RSA_WITH_AES_256_CBC_SHA |
| TLS_DHE_RSA_WITH_AES_256_CBC_SHA, |
| #endif |
| |
| #ifdef BUILD_TLS_PSK_WITH_AES_128_CBC_SHA |
| TLS_PSK_WITH_AES_128_CBC_SHA, |
| #endif |
| |
| #ifdef BUILD_TLS_PSK_WITH_AES_256_CBC_SHA |
| TLS_PSK_WITH_AES_256_CBC_SHA, |
| #endif |
| |
| #ifdef BUILD_TLS_RSA_WITH_HC_128_CBC_MD5 |
| TLS_RSA_WITH_HC_128_CBC_MD5, |
| #endif |
| |
| #ifdef BUILD_TLS_RSA_WITH_HC_128_CBC_SHA |
| TLS_RSA_WITH_HC_128_CBC_SHA, |
| #endif |
| |
| #ifdef BUILD_TLS_RSA_WITH_RABBIT_CBC_SHA |
| TLS_RSA_WITH_RABBIT_CBC_SHA, |
| #endif |
| |
| #ifdef BUILD_TLS_NTRU_RSA_WITH_RC4_128_SHA |
| TLS_NTRU_RSA_WITH_RC4_128_SHA, |
| #endif |
| |
| #ifdef BUILD_TLS_NTRU_RSA_WITH_3DES_EDE_CBC_SHA |
| TLS_NTRU_RSA_WITH_3DES_EDE_CBC_SHA, |
| #endif |
| |
| #ifdef BUILD_TLS_NTRU_RSA_WITH_AES_128_CBC_SHA |
| TLS_NTRU_RSA_WITH_AES_128_CBC_SHA, |
| #endif |
| |
| #ifdef BUILD_TLS_NTRU_RSA_WITH_AES_256_CBC_SHA |
| TLS_NTRU_RSA_WITH_AES_256_CBC_SHA, |
| #endif |
| |
| #ifdef BUILD_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA |
| TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, |
| #endif |
| |
| #ifdef BUILD_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA |
| TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, |
| #endif |
| |
| #ifdef BUILD_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA |
| TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, |
| #endif |
| |
| #ifdef BUILD_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA |
| TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, |
| #endif |
| |
| #ifdef BUILD_TLS_ECDHE_RSA_WITH_RC4_128_SHA |
| TLS_ECDHE_RSA_WITH_RC4_128_SHA, |
| #endif |
| |
| #ifdef BUILD_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA |
| TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, |
| #endif |
| |
| #ifdef BUILD_TLS_ECDHE_ECDSA_WITH_RC4_128_SHA |
| TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, |
| #endif |
| |
| #ifdef BUILD_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA |
| TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, |
| #endif |
| |
| #ifdef BUILD_TLS_RSA_WITH_AES_128_CBC_SHA256 |
| TLS_RSA_WITH_AES_128_CBC_SHA256, |
| #endif |
| |
| #ifdef BUILD_TLS_RSA_WITH_AES_256_CBC_SHA256 |
| TLS_RSA_WITH_AES_256_CBC_SHA256, |
| #endif |
| |
| #ifdef BUILD_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 |
| TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, |
| #endif |
| |
| #ifdef BUILD_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 |
| TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, |
| #endif |
| |
| #ifdef BUILD_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA |
| TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, |
| #endif |
| |
| #ifdef BUILD_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA |
| TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, |
| #endif |
| |
| #ifdef BUILD_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA |
| TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, |
| #endif |
| |
| #ifdef BUILD_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA |
| TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, |
| #endif |
| |
| #ifdef BUILD_TLS_ECDH_RSA_WITH_RC4_128_SHA |
| TLS_ECDH_RSA_WITH_RC4_128_SHA, |
| #endif |
| |
| #ifdef BUILD_TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA |
| TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, |
| #endif |
| |
| #ifdef BUILD_TLS_ECDH_ECDSA_WITH_RC4_128_SHA |
| TLS_ECDH_ECDSA_WITH_RC4_128_SHA, |
| #endif |
| |
| #ifdef BUILD_TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA |
| TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, |
| #endif |
| |
| #ifdef BUILD_TLS_RSA_WITH_AES_128_GCM_SHA256 |
| TLS_RSA_WITH_AES_128_GCM_SHA256, |
| #endif |
| |
| #ifdef BUILD_TLS_RSA_WITH_AES_256_GCM_SHA384 |
| TLS_RSA_WITH_AES_256_GCM_SHA384, |
| #endif |
| |
| #ifdef BUILD_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 |
| TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, |
| #endif |
| |
| #ifdef BUILD_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 |
| TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, |
| #endif |
| |
| #ifdef BUILD_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 |
| TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, |
| #endif |
| |
| #ifdef BUILD_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 |
| TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, |
| #endif |
| |
| #ifdef BUILD_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 |
| TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, |
| #endif |
| |
| #ifdef BUILD_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 |
| TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, |
| #endif |
| |
| #ifdef BUILD_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 |
| TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, |
| #endif |
| |
| #ifdef BUILD_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 |
| TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, |
| #endif |
| |
| #ifdef BUILD_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 |
| TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, |
| #endif |
| |
| #ifdef BUILD_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 |
| TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 |
| #endif |
| |
| }; |
| |
| |
| /* return true if set, else false */ |
| /* only supports full name from cipher_name[] delimited by : */ |
| int SetCipherList(Suites* s, 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(;;) { |
| word32 len; |
| prev = haystack; |
| haystack = XSTRSTR(haystack, needle); |
| |
| if (!haystack) /* last cipher */ |
| len = min(sizeof(name), XSTRLEN(prev)); |
| else |
| len = min(sizeof(name), (word32)(haystack - prev)); |
| |
| XSTRNCPY(name, prev, len); |
| name[(len == sizeof(name)) ? len - 1 : len] = 0; |
| |
| for (i = 0; i < suiteSz; i++) |
| if (XSTRNCMP(name, cipher_names[i], sizeof(name)) == 0) { |
| if (XSTRSTR(name, "EC")) |
| s->suites[idx++] = ECC_BYTE; /* ECC suite */ |
| else |
| s->suites[idx++] = 0x00; /* normal */ |
| s->suites[idx++] = (byte)cipher_name_idx[i]; |
| |
| if (!ret) ret = 1; /* found at least one */ |
| break; |
| } |
| if (!haystack) break; |
| haystack++; |
| } |
| |
| if (ret) { |
| s->setSuites = 1; |
| s->suiteSz = (word16)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 CYASSL* ssl) |
| { |
| int i; |
| int sz = sizeof(cipher_name_idx)/sizeof(int); |
| |
| for (i = 0; i < sz; i++) |
| if (ssl->options.cipherSuite == (byte)cipher_name_idx[i]) { |
| if (ssl->options.cipherSuite0 == ECC_BYTE) |
| continue; /* ECC suites at end */ |
| XSTRNCPY(info->cipherName, cipher_names[i], MAX_CIPHERNAME_SZ); |
| break; |
| } |
| |
| /* error max and min are negative numbers */ |
| if (ssl->error <= MIN_PARAM_ERR && ssl->error >= MAX_PARAM_ERR) |
| info->negotiationError = ssl->error; |
| } |
| |
| |
| /* Add name to info packet names, increase packet name count */ |
| void AddPacketName(const char* name, HandShakeInfo* info) |
| { |
| if (info->numberPackets < MAX_PACKETS_HANDSHAKE) { |
| XSTRNCPY(info->packetNames[info->numberPackets++], name, |
| MAX_PACKETNAME_SZ); |
| } |
| } |
| |
| |
| /* Initialisze TimeoutInfo */ |
| void InitTimeoutInfo(TimeoutInfo* info) |
| { |
| int i; |
| |
| info->timeoutName[0] = 0; |
| info->flags = 0; |
| |
| for (i = 0; i < MAX_PACKETS_HANDSHAKE; i++) { |
| info->packets[i].packetName[0] = 0; |
| info->packets[i].timestamp.tv_sec = 0; |
| info->packets[i].timestamp.tv_usec = 0; |
| info->packets[i].bufferValue = 0; |
| info->packets[i].valueSz = 0; |
| } |
| info->numberPackets = 0; |
| info->timeoutValue.tv_sec = 0; |
| info->timeoutValue.tv_usec = 0; |
| } |
| |
| |
| /* Free TimeoutInfo */ |
| void FreeTimeoutInfo(TimeoutInfo* info, void* heap) |
| { |
| int i; |
| for (i = 0; i < MAX_PACKETS_HANDSHAKE; i++) |
| if (info->packets[i].bufferValue) { |
| XFREE(info->packets[i].bufferValue, heap, DYNAMIC_TYPE_INFO); |
| info->packets[i].bufferValue = 0; |
| } |
| |
| } |
| |
| |
| /* Add PacketInfo to TimeoutInfo */ |
| void AddPacketInfo(const char* name, TimeoutInfo* info, const byte* data, |
| int sz, void* heap) |
| { |
| if (info->numberPackets < (MAX_PACKETS_HANDSHAKE - 1)) { |
| Timeval currTime; |
| |
| /* may add name after */ |
| if (name) |
| XSTRNCPY(info->packets[info->numberPackets].packetName, name, |
| MAX_PACKETNAME_SZ); |
| |
| /* add data, put in buffer if bigger than static buffer */ |
| info->packets[info->numberPackets].valueSz = sz; |
| if (sz < MAX_VALUE_SZ) |
| XMEMCPY(info->packets[info->numberPackets].value, data, sz); |
| else { |
| info->packets[info->numberPackets].bufferValue = |
| XMALLOC(sz, heap, DYNAMIC_TYPE_INFO); |
| if (!info->packets[info->numberPackets].bufferValue) |
| /* let next alloc catch, just don't fill, not fatal here */ |
| info->packets[info->numberPackets].valueSz = 0; |
| else |
| XMEMCPY(info->packets[info->numberPackets].bufferValue, |
| data, sz); |
| } |
| gettimeofday(&currTime, 0); |
| info->packets[info->numberPackets].timestamp.tv_sec = |
| currTime.tv_sec; |
| info->packets[info->numberPackets].timestamp.tv_usec = |
| currTime.tv_usec; |
| info->numberPackets++; |
| } |
| } |
| |
| |
| /* Add packet name to previsouly added packet info */ |
| void AddLateName(const char* name, TimeoutInfo* info) |
| { |
| /* make sure we have a valid previous one */ |
| if (info->numberPackets > 0 && info->numberPackets < |
| MAX_PACKETS_HANDSHAKE) { |
| XSTRNCPY(info->packets[info->numberPackets - 1].packetName, name, |
| MAX_PACKETNAME_SZ); |
| } |
| } |
| |
| /* Add record header to previsouly added packet info */ |
| void AddLateRecordHeader(const RecordLayerHeader* rl, TimeoutInfo* info) |
| { |
| /* make sure we have a valid previous one */ |
| if (info->numberPackets > 0 && info->numberPackets < |
| MAX_PACKETS_HANDSHAKE) { |
| if (info->packets[info->numberPackets - 1].bufferValue) |
| XMEMCPY(info->packets[info->numberPackets - 1].bufferValue, rl, |
| RECORD_HEADER_SZ); |
| else |
| XMEMCPY(info->packets[info->numberPackets - 1].value, rl, |
| RECORD_HEADER_SZ); |
| } |
| } |
| |
| #endif /* CYASSL_CALLBACKS */ |
| |
| |
| |
| /* client only parts */ |
| #ifndef NO_CYASSL_CLIENT |
| |
| int SendClientHello(CYASSL* 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; |
| |
| if (IsAtLeastTLSv1_2(ssl)) |
| length += HELLO_EXT_SZ; |
| |
| sendSz = length + HANDSHAKE_HEADER_SZ + RECORD_HEADER_SZ; |
| |
| #ifdef CYASSL_DTLS |
| if (ssl->options.dtls) { |
| length += ENUM_LEN; /* cookie */ |
| if (ssl->arrays.cookieSz != 0) length += ssl->arrays.cookieSz; |
| 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.length; |
| |
| 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++] = (byte)idSz; |
| if (idSz) { |
| XMEMCPY(output + idx, ssl->session.sessionID, ID_LEN); |
| idx += ID_LEN; |
| } |
| |
| /* then DTLS cookie */ |
| #ifdef CYASSL_DTLS |
| if (ssl->options.dtls) { |
| byte cookieSz = ssl->arrays.cookieSz; |
| |
| output[idx++] = cookieSz; |
| if (cookieSz) { |
| XMEMCPY(&output[idx], ssl->arrays.cookie, cookieSz); |
| idx += cookieSz; |
| } |
| } |
| #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; |
| |
| if (IsAtLeastTLSv1_2(ssl)) |
| { |
| /* add in the extensions length */ |
| c16toa(HELLO_EXT_LEN, output + idx); |
| idx += 2; |
| |
| c16toa(HELLO_EXT_SIG_ALGO, output + idx); |
| idx += 2; |
| c16toa(HELLO_EXT_SIGALGO_SZ, output + idx); |
| idx += 2; |
| /* This is a lazy list setup. Eventually, we'll need to support |
| * using other hash types or even other extensions. */ |
| c16toa(HELLO_EXT_SIGALGO_LEN, output + idx); |
| idx += 2; |
| output[idx++] = sha_mac; |
| output[idx++] = rsa_sa_algo; |
| output[idx++] = sha_mac; |
| output[idx++] = dsa_sa_algo; |
| output[idx++] = sha_mac; |
| output[idx++] = ecc_dsa_sa_algo; |
| } |
| |
| 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(CYASSL* 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) { |
| #ifdef CYASSL_DTLS |
| if (cookieSz < MAX_COOKIE_LEN) { |
| XMEMCPY(ssl->arrays.cookie, input + *inOutIdx, cookieSz); |
| ssl->arrays.cookieSz = cookieSz; |
| } |
| #endif |
| *inOutIdx += cookieSz; |
| } |
| |
| ssl->options.serverState = SERVER_HELLOVERIFYREQUEST_COMPLETE; |
| return 0; |
| } |
| |
| |
| static int DoServerHello(CYASSL* ssl, const byte* input, word32* inOutIdx, |
| word32 helloSz) |
| { |
| byte b; |
| byte compression; |
| ProtocolVersion pv; |
| word32 i = *inOutIdx; |
| word32 begin = i; |
| |
| #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); |
| if (pv.minor > ssl->version.minor) { |
| CYASSL_MSG("Server using higher version, fatal error"); |
| return VERSION_ERROR; |
| } |
| else if (pv.minor < ssl->version.minor) { |
| CYASSL_MSG("server using lower version"); |
| if (!ssl->options.downgrade) { |
| CYASSL_MSG(" no downgrade allowed, fatal error"); |
| return VERSION_ERROR; |
| } |
| else if (pv.minor == SSLv3_MINOR) { |
| /* turn off tls */ |
| CYASSL_MSG(" downgrading to SSLv3"); |
| ssl->options.tls = 0; |
| ssl->options.tls1_1 = 0; |
| ssl->version.minor = SSLv3_MINOR; |
| } |
| else if (pv.minor == TLSv1_MINOR) { |
| /* turn off tls 1.1+ */ |
| CYASSL_MSG(" downgrading to TLSv1"); |
| ssl->options.tls1_1 = 0; |
| ssl->version.minor = TLSv1_MINOR; |
| } |
| else if (pv.minor == TLSv1_1_MINOR) { |
| CYASSL_MSG(" downgrading to TLSv1.1"); |
| ssl->version.minor = TLSv1_1_MINOR; |
| } |
| } |
| XMEMCPY(ssl->arrays.serverRandom, input + i, RAN_LEN); |
| i += RAN_LEN; |
| b = input[i++]; |
| if (b) { |
| XMEMCPY(ssl->arrays.sessionID, input + i, min(b, ID_LEN)); |
| i += b; |
| ssl->options.haveSessionId = 1; |
| } |
| ssl->options.cipherSuite0 = input[i++]; |
| ssl->options.cipherSuite = input[i++]; |
| compression = input[i++]; |
| |
| if (compression != ZLIB_COMPRESSION && ssl->options.usingCompression) { |
| CYASSL_MSG("Server refused compression, turning off"); |
| ssl->options.usingCompression = 0; /* turn off if server refused */ |
| } |
| |
| *inOutIdx = i; |
| if ( (i - begin) < helloSz) |
| *inOutIdx = begin + helloSz; /* skip extensions */ |
| |
| ssl->options.serverState = SERVER_HELLO_COMPLETE; |
| |
| *inOutIdx = i; |
| |
| if (ssl->options.resuming) { |
| if (ssl->options.haveSessionId && XMEMCMP(ssl->arrays.sessionID, |
| ssl->session.sessionID, ID_LEN) == 0) { |
| if (SetCipherSpecs(ssl) == 0) { |
| int ret; |
| XMEMCPY(ssl->arrays.masterSecret, ssl->session.masterSecret, |
| SECRET_LEN); |
| if (ssl->options.tls) |
| ret = DeriveTlsKeys(ssl); |
| else |
| ret = DeriveKeys(ssl); |
| ssl->options.serverState = SERVER_HELLODONE_COMPLETE; |
| return ret; |
| } |
| else { |
| CYASSL_MSG("Unsupported cipher suite, DoServerHello"); |
| return UNSUPPORTED_SUITE; |
| } |
| } |
| else { |
| CYASSL_MSG("Server denied resumption attempt"); |
| ssl->options.resuming = 0; /* server denied resumption try */ |
| } |
| } |
| |
| return SetCipherSpecs(ssl); |
| } |
| |
| |
| /* just read in and ignore for now TODO: */ |
| static int DoCertificateRequest(CYASSL* ssl, const byte* input, word32* |
| inOutIdx) |
| { |
| word16 len; |
| |
| #ifdef CYASSL_CALLBACKS |
| if (ssl->hsInfoOn) |
| AddPacketName("CertificateRequest", &ssl->handShakeInfo); |
| if (ssl->toInfoOn) |
| AddLateName("CertificateRequest", &ssl->timeoutInfo); |
| #endif |
| len = input[(*inOutIdx)++]; |
| |
| /* types, read in here */ |
| *inOutIdx += len; |
| ato16(&input[*inOutIdx], &len); |
| *inOutIdx += LENGTH_SZ; |
| |
| if (IsAtLeastTLSv1_2(ssl)) { |
| /* hash sig format */ |
| *inOutIdx += len; |
| ato16(&input[*inOutIdx], &len); |
| *inOutIdx += LENGTH_SZ; |
| } |
| |
| /* authorities */ |
| while (len) { |
| word16 dnSz; |
| |
| ato16(&input[*inOutIdx], &dnSz); |
| *inOutIdx += (REQUEST_HEADER + dnSz); |
| len -= dnSz + REQUEST_HEADER; |
| } |
| |
| /* don't send client cert or cert verify if user hasn't provided |
| cert and private key */ |
| if (ssl->buffers.certificate.buffer && ssl->buffers.key.buffer) |
| ssl->options.sendVerify = SEND_CERT; |
| else if (IsAtLeastTLSv1_2(ssl)) |
| ssl->options.sendVerify = SEND_BLANK_CERT; |
| |
| return 0; |
| } |
| |
| |
| static int DoServerKeyExchange(CYASSL* ssl, const byte* input, |
| word32* inOutIdx) |
| { |
| #if defined(OPENSSL_EXTRA) || defined(HAVE_ECC) |
| word16 length = 0; |
| word16 sigLen = 0; |
| word16 verifySz = (word16)*inOutIdx; /* keep start idx */ |
| byte* signature = 0; |
| #endif |
| |
| (void)ssl; |
| (void)input; |
| (void)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 pskLen = 0; |
| ato16(&input[*inOutIdx], &pskLen); |
| *inOutIdx += LENGTH_SZ; |
| XMEMCPY(ssl->arrays.server_hint, &input[*inOutIdx], |
| min(pskLen, MAX_PSK_ID_LEN)); |
| if (pskLen < MAX_PSK_ID_LEN) |
| ssl->arrays.server_hint[pskLen] = 0; |
| else |
| ssl->arrays.server_hint[MAX_PSK_ID_LEN - 1] = 0; |
| *inOutIdx += pskLen; |
| |
| return 0; |
| } |
| #endif |
| #ifdef OPENSSL_EXTRA |
| if (ssl->specs.kea == diffie_hellman_kea) |
| { |
| /* p */ |
| ato16(&input[*inOutIdx], &length); |
| *inOutIdx += LENGTH_SZ; |
| |
| ssl->buffers.serverDH_P.buffer = (byte*) XMALLOC(length, ssl->heap, |
| DYNAMIC_TYPE_DH); |
| if (ssl->buffers.serverDH_P.buffer) |
| ssl->buffers.serverDH_P.length = length; |
| else |
| return MEMORY_ERROR; |
| XMEMCPY(ssl->buffers.serverDH_P.buffer, &input[*inOutIdx], length); |
| *inOutIdx += length; |
| |
| /* g */ |
| ato16(&input[*inOutIdx], &length); |
| *inOutIdx += LENGTH_SZ; |
| |
| ssl->buffers.serverDH_G.buffer = (byte*) XMALLOC(length, ssl->heap, |
| DYNAMIC_TYPE_DH); |
| if (ssl->buffers.serverDH_G.buffer) |
| ssl->buffers.serverDH_G.length = length; |
| else |
| return MEMORY_ERROR; |
| XMEMCPY(ssl->buffers.serverDH_G.buffer, &input[*inOutIdx], length); |
| *inOutIdx += length; |
| |
| /* pub */ |
| ato16(&input[*inOutIdx], &length); |
| *inOutIdx += LENGTH_SZ; |
| |
| ssl->buffers.serverDH_Pub.buffer = (byte*) XMALLOC(length, ssl->heap, |
| DYNAMIC_TYPE_DH); |
| if (ssl->buffers.serverDH_Pub.buffer) |
| ssl->buffers.serverDH_Pub.length = length; |
| else |
| return MEMORY_ERROR; |
| XMEMCPY(ssl->buffers.serverDH_Pub.buffer, &input[*inOutIdx], length); |
| *inOutIdx += length; |
| } /* dh_kea */ |
| #endif /* OPENSSL_EXTRA */ |
| |
| #ifdef HAVE_ECC |
| if (ssl->specs.kea == ecc_diffie_hellman_kea) |
| { |
| byte b = input[*inOutIdx]; |
| *inOutIdx += 1; |
| |
| if (b != named_curve) |
| return ECC_CURVETYPE_ERROR; |
| |
| *inOutIdx += 1; /* curve type, eat leading 0 */ |
| b = input[*inOutIdx]; |
| *inOutIdx += 1; |
| |
| if (b != secp256r1 && b != secp384r1 && b != secp521r1 && b != |
| secp160r1 && b != secp192r1 && b != secp224r1) |
| return ECC_CURVE_ERROR; |
| |
| length = input[*inOutIdx]; |
| *inOutIdx += 1; |
| |
| if (ecc_import_x963(&input[*inOutIdx], length, &ssl->peerEccKey) != 0) |
| return ECC_PEERKEY_ERROR; |
| |
| *inOutIdx += length; |
| ssl->peerEccKeyPresent = 1; |
| } |
| #endif /* HAVE_ECC */ |
| |
| #if defined(OPENSSL_EXTRA) || defined(HAVE_ECC) |
| { |
| Md5 md5; |
| Sha sha; |
| byte hash[FINISHED_SZ]; |
| byte messageVerify[MAX_DH_SZ]; |
| |
| /* adjust from start idx */ |
| verifySz = (word16)(*inOutIdx - verifySz); |
| |
| /* save message for hash verify */ |
| if (verifySz > sizeof(messageVerify)) |
| return BUFFER_ERROR; |
| XMEMCPY(messageVerify, &input[*inOutIdx - verifySz], verifySz); |
| |
| if (IsAtLeastTLSv1_2(ssl)) { |
| /* just advance for now TODO: validate hash algo params */ |
| *inOutIdx += LENGTH_SZ; |
| } |
| |
| /* signature */ |
| ato16(&input[*inOutIdx], &length); |
| *inOutIdx += LENGTH_SZ; |
| |
| signature = (byte*)&input[*inOutIdx]; |
| *inOutIdx += length; |
| sigLen = length; |
| |
| /* verify signature */ |
| |
| /* md5 */ |
| InitMd5(&md5); |
| Md5Update(&md5, ssl->arrays.clientRandom, RAN_LEN); |
| Md5Update(&md5, ssl->arrays.serverRandom, RAN_LEN); |
| Md5Update(&md5, messageVerify, verifySz); |
| Md5Final(&md5, hash); |
| |
| /* sha */ |
| InitSha(&sha); |
| ShaUpdate(&sha, ssl->arrays.clientRandom, RAN_LEN); |
| ShaUpdate(&sha, ssl->arrays.serverRandom, RAN_LEN); |
| ShaUpdate(&sha, messageVerify, verifySz); |
| ShaFinal(&sha, &hash[MD5_DIGEST_SIZE]); |
| |
| /* rsa */ |
| if (ssl->specs.sig_algo == rsa_sa_algo) |
| { |
| int ret; |
| byte* out; |
| |
| if (!ssl->peerRsaKeyPresent) |
| return NO_PEER_KEY; |
| |
| ret = RsaSSL_VerifyInline(signature, sigLen,&out, &ssl->peerRsaKey); |
| |
| if (IsAtLeastTLSv1_2(ssl)) { |
| byte encodedSig[MAX_ENCODED_SIG_SZ]; |
| word32 encSigSz; |
| byte* digest; |
| int typeH; |
| int digestSz; |
| |
| /* sha1 for now */ |
| digest = &hash[MD5_DIGEST_SIZE]; |
| typeH = SHAh; |
| digestSz = SHA_DIGEST_SIZE; |
| |
| encSigSz = EncodeSignature(encodedSig, digest, digestSz, typeH); |
| |
| if (encSigSz != (word32)ret || XMEMCMP(out, encodedSig, |
| min(encSigSz, MAX_ENCODED_SIG_SZ)) != 0) |
| return VERIFY_SIGN_ERROR; |
| } |
| else { |
| if (ret != sizeof(hash) || XMEMCMP(out, hash, sizeof(hash))) |
| return VERIFY_SIGN_ERROR; |
| } |
| } |
| #ifdef HAVE_ECC |
| /* ecdsa */ |
| else if (ssl->specs.sig_algo == ecc_dsa_sa_algo) { |
| int verify = 0, ret; |
| if (!ssl->peerEccDsaKeyPresent) |
| return NO_PEER_KEY; |
| |
| ret = ecc_verify_hash(signature, sigLen, &hash[MD5_DIGEST_SIZE], |
| SHA_DIGEST_SIZE, &verify, &ssl->peerEccDsaKey); |
| if (ret != 0 || verify == 0) |
| return VERIFY_SIGN_ERROR; |
| } |
| #endif /* HAVE_ECC */ |
| else |
| return ALGO_ID_E; |
| |
| ssl->options.serverState = SERVER_KEYEXCHANGE_COMPLETE; |
| |
| return 0; |
| |
| } |
| #else /* HAVE_OPENSSL or HAVE_ECC */ |
| return NOT_COMPILED_IN; /* not supported by build */ |
| #endif /* HAVE_OPENSSL or HAVE_ECC */ |
| } |
| |
| |
| int SendClientKeyExchange(CYASSL* 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 = 0; |
| DhKey key; |
| |
| if (serverP.buffer == 0 || serverG.buffer == 0 || |
| serverPub.buffer == 0) |
| return NO_PEER_KEY; |
| |
| InitDhKey(&key); |
| ret = DhSetKey(&key, serverP.buffer, serverP.length, |
| serverG.buffer, serverG.length); |
| if (ret == 0) |
| /* for DH, encSecret is Yc, agree is pre-master */ |
| ret = DhGenerateKeyPair(&key, &ssl->rng, priv, &privSz, |
| encSecret, &encSz); |
| if (ret == 0) |
| ret = DhAgree(&key, ssl->arrays.preMasterSecret, |
| &ssl->arrays.preMasterSz, priv, privSz, |
| serverPub.buffer, serverPub.length); |
| FreeDhKey(&key); |
| #endif /* OPENSSL_EXTRA */ |
| #ifndef NO_PSK |
| } else if (ssl->specs.kea == psk_kea) { |
| byte* pms = ssl->arrays.preMasterSecret; |
| |
| ssl->arrays.psk_keySz = ssl->options.client_psk_cb(ssl, |
| ssl->arrays.server_hint, ssl->arrays.client_identity, |
| MAX_PSK_ID_LEN, ssl->arrays.psk_key, MAX_PSK_KEY_LEN); |
| if (ssl->arrays.psk_keySz == 0 || |
| ssl->arrays.psk_keySz > MAX_PSK_KEY_LEN) |
| return PSK_KEY_ERROR; |
| encSz = (word32)XSTRLEN(ssl->arrays.client_identity); |
| if (encSz > MAX_PSK_ID_LEN) return CLIENT_ID_ERROR; |
| XMEMCPY(encSecret, ssl->arrays.client_identity, encSz); |
| |
| /* make psk pre master secret */ |
| /* length of key + length 0s + length of key + key */ |
| c16toa((word16)ssl->arrays.psk_keySz, pms); |
| pms += 2; |
| XMEMSET(pms, 0, ssl->arrays.psk_keySz); |
| pms += ssl->arrays.psk_keySz; |
| c16toa((word16)ssl->arrays.psk_keySz, pms); |
| pms += 2; |
| XMEMCPY(pms, ssl->arrays.psk_key, ssl->arrays.psk_keySz); |
| ssl->arrays.preMasterSz = ssl->arrays.psk_keySz * 2 + 4; |
| #endif /* NO_PSK */ |
| #ifdef HAVE_NTRU |
| } else if (ssl->specs.kea == ntru_kea) { |
| word32 rc; |
| word16 cipherLen = sizeof(encSecret); |
| DRBG_HANDLE drbg; |
| static uint8_t const cyasslStr[] = { |
| 'C', 'y', 'a', 'S', 'S', 'L', ' ', 'N', 'T', 'R', 'U' |
| }; |
| |
| RNG_GenerateBlock(&ssl->rng, ssl->arrays.preMasterSecret, |
| SECRET_LEN); |
| ssl->arrays.preMasterSz = SECRET_LEN; |
| |
| if (ssl->peerNtruKeyPresent == 0) |
| return NO_PEER_KEY; |
| |
| rc = crypto_drbg_instantiate(MAX_NTRU_BITS, cyasslStr, |
| sizeof(cyasslStr), GetEntropy, &drbg); |
| if (rc != DRBG_OK) |
| return NTRU_DRBG_ERROR; |
| |
| rc = crypto_ntru_encrypt(drbg, ssl->peerNtruKeyLen,ssl->peerNtruKey, |
| ssl->arrays.preMasterSz, |
| ssl->arrays.preMasterSecret, |
| &cipherLen, encSecret); |
| crypto_drbg_uninstantiate(drbg); |
| if (rc != NTRU_OK) |
| return NTRU_ENCRYPT_ERROR; |
| |
| encSz = cipherLen; |
| ret = 0; |
| #endif /* HAVE_NTRU */ |
| #ifdef HAVE_ECC |
| } else if (ssl->specs.kea == ecc_diffie_hellman_kea) { |
| ecc_key myKey; |
| ecc_key* peerKey = &myKey; |
| word32 size = sizeof(encSecret); |
| |
| if (ssl->specs.static_ecdh) { |
| /* TODO: EccDsa is really fixed Ecc change naming */ |
| if (!ssl->peerEccDsaKeyPresent || !ssl->peerEccDsaKey.dp) |
| return NO_PEER_KEY; |
| peerKey = &ssl->peerEccDsaKey; |
| } |
| else { |
| if (!ssl->peerEccKeyPresent || !ssl->peerEccKey.dp) |
| return NO_PEER_KEY; |
| peerKey = &ssl->peerEccKey; |
| } |
| |
| ecc_init(&myKey); |
| ret = ecc_make_key(&ssl->rng, peerKey->dp->size, &myKey); |
| if (ret != 0) |
| return ECC_MAKEKEY_ERROR; |
| |
| /* precede export with 1 byte length */ |
| ret = ecc_export_x963(&myKey, encSecret + 1, &size); |
| encSecret[0] = (byte)size; |
| encSz = size + 1; |
| |
| if (ret != 0) |
| ret = ECC_EXPORT_ERROR; |
| else { |
| size = sizeof(ssl->arrays.preMasterSecret); |
| ret = ecc_shared_secret(&myKey, peerKey, |
| ssl->arrays.preMasterSecret, &size); |
| if (ret != 0) |
| ret = ECC_SHARED_ERROR; |
| } |
| |
| ssl->arrays.preMasterSz = size; |
| ecc_free(&myKey); |
| #endif /* HAVE_ECC */ |
| } else |
| return ALGO_ID_E; /* unsupported kea */ |
| |
| if (ret == 0) { |
| byte *output; |
| int sendSz; |
| word32 tlsSz = 0; |
| |
| if (ssl->options.tls || ssl->specs.kea == diffie_hellman_kea) |
| tlsSz = 2; |
| |
| if (ssl->specs.kea == ecc_diffie_hellman_kea) /* always off */ |
| tlsSz = 0; |
| |
| sendSz = encSz + tlsSz + HANDSHAKE_HEADER_SZ + RECORD_HEADER_SZ; |
| idx = HANDSHAKE_HEADER_SZ + RECORD_HEADER_SZ; |
| |
| #ifdef CYASSL_DTLS |
| if (ssl->options.dtls) { |
| sendSz += DTLS_HANDSHAKE_EXTRA + DTLS_RECORD_EXTRA; |
| idx += DTLS_HANDSHAKE_EXTRA + DTLS_RECORD_EXTRA; |
| } |
| #endif |
| |
| /* check for avalaible size */ |
| if ((ret = CheckAvalaibleSize(ssl, sendSz)) != 0) |
| return ret; |
| |
| /* get ouput buffer */ |
| output = ssl->buffers.outputBuffer.buffer + |
| ssl->buffers.outputBuffer.length; |
| |
| 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; |
| |
| if (ssl->options.groupMessages) |
| ret = 0; |
| else |
| 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(CYASSL* ssl) |
| { |
| byte *output; |
| int sendSz = 0, length, ret; |
| word32 idx = 0; |
| word32 sigOutSz = 0; |
| RsaKey key; |
| int usingEcc = 0; |
| #ifdef HAVE_ECC |
| ecc_key eccKey; |
| #endif |
| |
| 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.length; |
| |
| BuildCertHashes(ssl, &ssl->certHashes); |
| |
| #ifdef HAVE_ECC |
| ecc_init(&eccKey); |
| #endif |
| InitRsaKey(&key, ssl->heap); |
| ret = RsaPrivateKeyDecode(ssl->buffers.key.buffer, &idx, &key, |
| ssl->buffers.key.length); |
| if (ret == 0) |
| sigOutSz = RsaEncryptSize(&key); |
| else { |
| #ifdef HAVE_ECC |
| CYASSL_MSG("Trying ECC client cert, RSA didn't work"); |
| |
| idx = 0; |
| ret = EccPrivateKeyDecode(ssl->buffers.key.buffer, &idx, &eccKey, |
| ssl->buffers.key.length); |
| if (ret == 0) { |
| CYASSL_MSG("Using ECC client cert"); |
| usingEcc = 1; |
| sigOutSz = ecc_sig_size(&eccKey); |
| } |
| else { |
| CYASSL_MSG("Bad client cert type"); |
| } |
| #endif |
| } |
| if (ret == 0) { |
| byte* verify = (byte*)&output[RECORD_HEADER_SZ + |
| HANDSHAKE_HEADER_SZ]; |
| byte* signBuffer = ssl->certHashes.md5; |
| word32 signSz = sizeof(Hashes); |
| byte encodedSig[MAX_ENCODED_SIG_SZ]; |
| word32 extraSz = 0; /* tls 1.2 hash/sig */ |
| |
| #ifdef CYASSL_DTLS |
| if (ssl->options.dtls) |
| verify += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA; |
| #endif |
| length = sigOutSz; |
| if (IsAtLeastTLSv1_2(ssl)) { |
| verify[0] = sha_mac; |
| verify[1] = usingEcc ? ecc_dsa_sa_algo : rsa_sa_algo; |
| extraSz = HASH_SIG_SIZE; |
| } |
| c16toa((word16)length, verify + extraSz); /* prepend verify header*/ |
| |
| if (usingEcc) { |
| #ifdef HAVE_ECC |
| word32 localSz = sigOutSz; |
| ret = ecc_sign_hash(signBuffer + MD5_DIGEST_SIZE, |
| SHA_DIGEST_SIZE, verify + extraSz + VERIFY_HEADER, |
| &localSz, &ssl->rng, &eccKey); |
| #endif |
| } |
| else { |
| if (IsAtLeastTLSv1_2(ssl)) { |
| byte* digest; |
| int typeH; |
| int digestSz; |
| |
| /* sha1 for now */ |
| digest = ssl->certHashes.sha; |
| typeH = SHAh; |
| digestSz = SHA_DIGEST_SIZE; |
| |
| signSz = EncodeSignature(encodedSig, digest,digestSz,typeH); |
| signBuffer = encodedSig; |
| } |
| |
| ret = RsaSSL_Sign(signBuffer, signSz, verify + extraSz + |
| VERIFY_HEADER, ENCRYPT_LEN, &key, &ssl->rng); |
| |
| if (ret > 0) |
| ret = 0; /* RSA reset */ |
| } |
| |
| if (ret == 0) { |
| AddHeaders(output, length + extraSz + VERIFY_HEADER, |
| certificate_verify, ssl); |
| |
| sendSz = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ + length + |
| extraSz + VERIFY_HEADER; |
| #ifdef CYASSL_DTLS |
| if (ssl->options.dtls) |
| sendSz += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA; |
| #endif |
| HashOutput(ssl, output, sendSz, 0); |
| } |
| } |
| |
| FreeRsaKey(&key); |
| #ifdef HAVE_ECC |
| ecc_free(&eccKey); |
| #endif |
| |
| 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; |
| if (ssl->options.groupMessages) |
| return 0; |
| else |
| return SendBuffered(ssl); |
| } |
| else |
| return ret; |
| } |
| |
| |
| |
| #endif /* NO_CYASSL_CLIENT */ |
| |
| |
| #ifndef NO_CYASSL_SERVER |
| |
| int SendServerHello(CYASSL* 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.length; |
| |
| sendSz = length + HANDSHAKE_HEADER_SZ + RECORD_HEADER_SZ; |
| AddHeaders(output, length, server_hello, ssl); |
| |
| #ifdef CYASSL_DTLS |
| if (ssl->options.dtls) { |
| idx += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA; |
| sendSz += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA; |
| } |
| #endif |
| /* now write to output */ |
| /* first version */ |
| XMEMCPY(output + idx, &ssl->version, sizeof(ProtocolVersion)); |
| idx += sizeof(ProtocolVersion); |
| |
| /* then random */ |
| if (!ssl->options.resuming) |
| RNG_GenerateBlock(&ssl->rng, ssl->arrays.serverRandom, RAN_LEN); |
| XMEMCPY(output + idx, ssl->arrays.serverRandom, RAN_LEN); |
| idx += RAN_LEN; |
| |
| #ifdef SHOW_SECRETS |
| { |
| int j; |
| printf("server random: "); |
| for (j = 0; j < RAN_LEN; j++) |
| printf("%02x", ssl->arrays.serverRandom[j]); |
| printf("\n"); |
| } |
| #endif |
| /* then session id */ |
| output[idx++] = ID_LEN; |
| if (!ssl->options.resuming) |
| RNG_GenerateBlock(&ssl->rng, ssl->arrays.sessionID, ID_LEN); |
| XMEMCPY(output + idx, ssl->arrays.sessionID, ID_LEN); |
| idx += ID_LEN; |
| |
| /* then cipher suite */ |
| output[idx++] = ssl->options.cipherSuite0; |
| output[idx++] = ssl->options.cipherSuite; |
| |
| /* last, compression */ |
| if (ssl->options.usingCompression) |
| output[idx++] = ZLIB_COMPRESSION; |
| else |
| output[idx++] = NO_COMPRESSION; |
| |
| ssl->buffers.outputBuffer.length += sendSz; |
| HashOutput(ssl, output, sendSz, 0); |
| |
| #ifdef CYASSL_CALLBACKS |
| if (ssl->hsInfoOn) |
| AddPacketName("ServerHello", &ssl->handShakeInfo); |
| if (ssl->toInfoOn) |
| AddPacketInfo("ServerHello", &ssl->timeoutInfo, output, sendSz, |
| ssl->heap); |
| #endif |
| |
| ssl->options.serverState = SERVER_HELLO_COMPLETE; |
| |
| if (ssl->options.groupMessages) |
| return 0; |
| else |
| return SendBuffered(ssl); |
| } |
| |
| |
| #ifdef HAVE_ECC |
| |
| static byte SetCurveId(int size) |
| { |
| switch(size) { |
| case 20: |
| return secp160r1; |
| break; |
| case 24: |
| return secp192r1; |
| break; |
| case 28: |
| return secp224r1; |
| break; |
| case 32: |
| return secp256r1; |
| break; |
| case 48: |
| return secp384r1; |
| break; |
| case 66: |
| return secp521r1; |
| break; |
| default: |
| return 0; |
| } |
| } |
| |
| #endif /* HAVE_ECC */ |
| |
| |
| int SendServerKeyExchange(CYASSL* ssl) |
| { |
| int ret = 0; |
| (void)ssl; |
| |
| #ifndef NO_PSK |
| if (ssl->specs.kea == psk_kea) |
| { |
| byte *output; |
| word32 length, idx = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ; |
| int sendSz; |
| if (ssl->arrays.server_hint[0] == 0) return 0; /* don't send */ |
| |
| /* include size part */ |
| length = (word32)XSTRLEN(ssl->arrays.server_hint); |
| if (length > MAX_PSK_ID_LEN) return SERVER_HINT_ERROR; |
| length += HINT_LEN_SZ; |
| sendSz = length + HANDSHAKE_HEADER_SZ + RECORD_HEADER_SZ; |
| |
| #ifdef CYASSL_DTLS |
| if (ssl->options.dtls) { |
| sendSz += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA; |
| idx += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA; |
| } |
| #endif |
| /* check for avalaible size */ |
| if ((ret = CheckAvalaibleSize(ssl, sendSz)) != 0) |
| return ret; |
| |
| /* get ouput buffer */ |
| output = ssl->buffers.outputBuffer.buffer + |
| ssl->buffers.outputBuffer.length; |
| |
| 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; |
| if (ssl->options.groupMessages) |
| ret = 0; |
| else |
| ret = SendBuffered(ssl); |
| ssl->options.serverState = SERVER_KEYEXCHANGE_COMPLETE; |
| } |
| #endif /*NO_PSK */ |
| |
| #ifdef HAVE_ECC |
| if (ssl->specs.kea == ecc_diffie_hellman_kea) |
| { |
| byte *output; |
| word32 length, idx = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ; |
| int sendSz; |
| byte exportBuf[MAX_EXPORT_ECC_SZ]; |
| word32 expSz = sizeof(exportBuf); |
| word32 sigSz; |
| word32 preSigSz, preSigIdx; |
| RsaKey rsaKey; |
| ecc_key dsaKey; |
| |
| if (ssl->specs.static_ecdh) { |
| CYASSL_MSG("Using Static ECDH, not sending ServerKeyExchagne"); |
| return 0; |
| } |
| |
| /* curve type, named curve, length(1) */ |
| length = ENUM_LEN + CURVE_LEN + ENUM_LEN; |
| /* pub key size */ |
| CYASSL_MSG("Using ephemeral ECDH"); |
| if (ecc_export_x963(&ssl->eccTempKey, exportBuf, &expSz) != 0) |
| return ECC_EXPORT_ERROR; |
| length += expSz; |
| |
| preSigSz = length; |
| preSigIdx = idx; |
| |
| InitRsaKey(&rsaKey, ssl->heap); |
| ecc_init(&dsaKey); |
| |
| /* sig length */ |
| length += LENGTH_SZ; |
| |
| if (!ssl->buffers.key.buffer) { |
| FreeRsaKey(&rsaKey); |
| ecc_free(&dsaKey); |
| return NO_PRIVATE_KEY; |
| } |
| |
| if (ssl->specs.sig_algo == rsa_sa_algo) { |
| /* rsa sig size */ |
| word32 i = 0; |
| ret = RsaPrivateKeyDecode(ssl->buffers.key.buffer, &i, |
| &rsaKey, ssl->buffers.key.length); |
| if (ret != 0) return ret; |
| sigSz = RsaEncryptSize(&rsaKey); |
| } |
| else if (ssl->specs.sig_algo == ecc_dsa_sa_algo) { |
| /* ecdsa sig size */ |
| word32 i = 0; |
| ret = EccPrivateKeyDecode(ssl->buffers.key.buffer, &i, |
| &dsaKey, ssl->buffers.key.length); |
| if (ret != 0) return ret; |
| sigSz = ecc_sig_size(&dsaKey); |
| } |
| else { |
| FreeRsaKey(&rsaKey); |
| ecc_free(&dsaKey); |
| return ALGO_ID_E; /* unsupported type */ |
| } |
| length += sigSz; |
| |
| if (IsAtLeastTLSv1_2(ssl)) |
| length += HASH_SIG_SIZE; |
| |
| sendSz = length + HANDSHAKE_HEADER_SZ + RECORD_HEADER_SZ; |
| |
| #ifdef CYASSL_DTLS |
| if (ssl->options.dtls) { |
| sendSz += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA; |
| idx += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA; |
| preSigIdx = idx; |
| } |
| #endif |
| /* check for avalaible size */ |
| if ((ret = CheckAvalaibleSize(ssl, sendSz)) != 0) { |
| FreeRsaKey(&rsaKey); |
| ecc_free(&dsaKey); |
| return ret; |
| } |
| |
| /* get ouput buffer */ |
| output = ssl->buffers.outputBuffer.buffer + |
| ssl->buffers.outputBuffer.length; |
| |
| AddHeaders(output, length, server_key_exchange, ssl); |
| |
| /* key exchange data */ |
| output[idx++] = named_curve; |
| output[idx++] = 0x00; /* leading zero */ |
| output[idx++] = SetCurveId(ecc_size(&ssl->eccTempKey)); |
| output[idx++] = (byte)expSz; |
| XMEMCPY(output + idx, exportBuf, expSz); |
| idx += expSz; |
| if (IsAtLeastTLSv1_2(ssl)) { |
| output[idx++] = sha_mac; |
| output[idx++] = ssl->specs.sig_algo; |
| } |
| c16toa((word16)sigSz, output + idx); |
| idx += LENGTH_SZ; |
| |
| /* do signature */ |
| { |
| Md5 md5; |
| Sha sha; |
| byte hash[FINISHED_SZ]; |
| byte* signBuffer = hash; |
| word32 signSz = sizeof(hash); |
| |
| /* md5 */ |
| InitMd5(&md5); |
| Md5Update(&md5, ssl->arrays.clientRandom, RAN_LEN); |
| Md5Update(&md5, ssl->arrays.serverRandom, RAN_LEN); |
| Md5Update(&md5, output + preSigIdx, preSigSz); |
| Md5Final(&md5, hash); |
| |
| /* sha */ |
| InitSha(&sha); |
| ShaUpdate(&sha, ssl->arrays.clientRandom, RAN_LEN); |
| ShaUpdate(&sha, ssl->arrays.serverRandom, RAN_LEN); |
| ShaUpdate(&sha, output + preSigIdx, preSigSz); |
| ShaFinal(&sha, &hash[MD5_DIGEST_SIZE]); |
| |
| if (ssl->specs.sig_algo == rsa_sa_algo) { |
| byte encodedSig[MAX_ENCODED_SIG_SZ]; |
| if (IsAtLeastTLSv1_2(ssl)) { |
| byte* digest; |
| int hType; |
| int digestSz; |
| |
| /* sha1 for now */ |
| digest = &hash[MD5_DIGEST_SIZE]; |
| hType = SHAh; |
| digestSz = SHA_DIGEST_SIZE; |
| |
| signSz = EncodeSignature(encodedSig, digest, digestSz, |
| hType); |
| signBuffer = encodedSig; |
| } |
| ret = RsaSSL_Sign(signBuffer, signSz, output + idx, sigSz, |
| &rsaKey, &ssl->rng); |
| FreeRsaKey(&rsaKey); |
| ecc_free(&dsaKey); |
| if (ret > 0) |
| ret = 0; /* reset on success */ |
| else |
| return ret; |
| } |
| else if (ssl->specs.sig_algo == ecc_dsa_sa_algo) { |
| word32 sz = sigSz; |
| |
| ret = ecc_sign_hash(&hash[MD5_DIGEST_SIZE], SHA_DIGEST_SIZE, |
| output + idx, &sz, &ssl->rng, &dsaKey); |
| FreeRsaKey(&rsaKey); |
| ecc_free(&dsaKey); |
| if (ret < 0) return ret; |
| } |
| } |
| |
| 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; |
| if (ssl->options.groupMessages) |
| ret = 0; |
| else |
| ret = SendBuffered(ssl); |
| ssl->options.serverState = SERVER_KEYEXCHANGE_COMPLETE; |
| } |
| #endif /* HAVE_ECC */ |
| |
| #ifdef OPENSSL_EXTRA |
| if (ssl->specs.kea == diffie_hellman_kea) { |
| byte *output; |
| word32 length = 0, idx = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ; |
| int sendSz; |
| word32 sigSz = 0, i = 0; |
| word32 preSigSz = 0, preSigIdx = 0; |
| RsaKey rsaKey; |
| DhKey dhKey; |
| |
| if (ssl->buffers.serverDH_P.buffer == NULL || |
| ssl->buffers.serverDH_G.buffer == NULL) |
| return NO_DH_PARAMS; |
| |
| if (ssl->buffers.serverDH_Pub.buffer == NULL) { |
| ssl->buffers.serverDH_Pub.buffer = (byte*)XMALLOC( |
| ssl->buffers.serverDH_P.length + 2, ssl->ctx->heap, |
| DYNAMIC_TYPE_DH); |
| if (ssl->buffers.serverDH_Pub.buffer == NULL) |
| return MEMORY_E; |
| } |
| |
| if (ssl->buffers.serverDH_Priv.buffer == NULL) { |
| ssl->buffers.serverDH_Priv.buffer = (byte*)XMALLOC( |
| ssl->buffers.serverDH_P.length + 2, ssl->ctx->heap, |
| DYNAMIC_TYPE_DH); |
| if (ssl->buffers.serverDH_Priv.buffer == NULL) |
| return MEMORY_E; |
| } |
| |
| InitDhKey(&dhKey); |
| ret = DhSetKey(&dhKey, ssl->buffers.serverDH_P.buffer, |
| ssl->buffers.serverDH_P.length, |
| ssl->buffers.serverDH_G.buffer, |
| ssl->buffers.serverDH_G.length); |
| if (ret == 0) |
| ret = DhGenerateKeyPair(&dhKey, &ssl->rng, |
| ssl->buffers.serverDH_Priv.buffer, |
| &ssl->buffers.serverDH_Priv.length, |
| ssl->buffers.serverDH_Pub.buffer, |
| &ssl->buffers.serverDH_Pub.length); |
| FreeDhKey(&dhKey); |
| |
| if (ret == 0) { |
| length = LENGTH_SZ * 3; /* p, g, pub */ |
| length += ssl->buffers.serverDH_P.length + |
| ssl->buffers.serverDH_G.length + |
| ssl->buffers.serverDH_Pub.length; |
| |
| preSigIdx = idx; |
| preSigSz = length; |
| |
| /* sig length */ |
| length += LENGTH_SZ; |
| |
| if (!ssl->buffers.key.buffer) |
| return NO_PRIVATE_KEY; |
| |
| InitRsaKey(&rsaKey, ssl->heap); |
| ret = RsaPrivateKeyDecode(ssl->buffers.key.buffer, &i, &rsaKey, |
| ssl->buffers.key.length); |
| if (ret == 0) { |
| sigSz = RsaEncryptSize(&rsaKey); |
| length += sigSz; |
| } |
| } |
| if (ret != 0) { |
| FreeRsaKey(&rsaKey); |
| return ret; |
| } |
| |
| if (IsAtLeastTLSv1_2(ssl)) |
| length += HASH_SIG_SIZE; |
| |
| sendSz = length + HANDSHAKE_HEADER_SZ + RECORD_HEADER_SZ; |
| |
| #ifdef CYASSL_DTLS |
| if (ssl->options.dtls) { |
| sendSz += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA; |
| idx += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA; |
| preSigIdx = idx; |
| } |
| #endif |
| /* check for avalaible size */ |
| if ((ret = CheckAvalaibleSize(ssl, sendSz)) != 0) { |
| FreeRsaKey(&rsaKey); |
| return ret; |
| } |
| |
| /* get ouput buffer */ |
| output = ssl->buffers.outputBuffer.buffer + |
| ssl->buffers.outputBuffer.length; |
| |
| AddHeaders(output, length, server_key_exchange, ssl); |
| |
| /* add p, g, pub */ |
| c16toa((word16)ssl->buffers.serverDH_P.length, output + idx); |
| idx += LENGTH_SZ; |
| XMEMCPY(output + idx, ssl->buffers.serverDH_P.buffer, |
| ssl->buffers.serverDH_P.length); |
| idx += ssl->buffers.serverDH_P.length; |
| |
| /* g */ |
| c16toa((word16)ssl->buffers.serverDH_G.length, output + idx); |
| idx += LENGTH_SZ; |
| XMEMCPY(output + idx, ssl->buffers.serverDH_G.buffer, |
| ssl->buffers.serverDH_G.length); |
| idx += ssl->buffers.serverDH_G.length; |
| |
| /* pub */ |
| c16toa((word16)ssl->buffers.serverDH_Pub.length, output + idx); |
| idx += LENGTH_SZ; |
| XMEMCPY(output + idx, ssl->buffers.serverDH_Pub.buffer, |
| ssl->buffers.serverDH_Pub.length); |
| idx += ssl->buffers.serverDH_Pub.length; |
| |
| /* Add signature */ |
| if (IsAtLeastTLSv1_2(ssl)) { |
| output[idx++] = sha_mac; |
| output[idx++] = ssl->specs.sig_algo; |
| } |
| /* size */ |
| c16toa((word16)sigSz, output + idx); |
| idx += LENGTH_SZ; |
| |
| /* do signature */ |
| { |
| Md5 md5; |
| Sha sha; |
| byte hash[FINISHED_SZ]; |
| byte* signBuffer = hash; |
| word32 signSz = sizeof(hash); |
| |
| /* md5 */ |
| InitMd5(&md5); |
| Md5Update(&md5, ssl->arrays.clientRandom, RAN_LEN); |
| Md5Update(&md5, ssl->arrays.serverRandom, RAN_LEN); |
| Md5Update(&md5, output + preSigIdx, preSigSz); |
| Md5Final(&md5, hash); |
| |
| /* sha */ |
| InitSha(&sha); |
| ShaUpdate(&sha, ssl->arrays.clientRandom, RAN_LEN); |
| ShaUpdate(&sha, ssl->arrays.serverRandom, RAN_LEN); |
| ShaUpdate(&sha, output + preSigIdx, preSigSz); |
| ShaFinal(&sha, &hash[MD5_DIGEST_SIZE]); |
| |
| if (ssl->specs.sig_algo == rsa_sa_algo) { |
| byte encodedSig[MAX_ENCODED_SIG_SZ]; |
| if (IsAtLeastTLSv1_2(ssl)) { |
| byte* digest; |
| int typeH; |
| int digestSz; |
| |
| /* sha1 for now */ |
| digest = &hash[MD5_DIGEST_SIZE]; |
| typeH = SHAh; |
| digestSz = SHA_DIGEST_SIZE; |
| |
| signSz = EncodeSignature(encodedSig, digest, digestSz, |
| typeH); |
| signBuffer = encodedSig; |
| } |
| ret = RsaSSL_Sign(signBuffer, signSz, output + idx, sigSz, |
| &rsaKey, &ssl->rng); |
| FreeRsaKey(&rsaKey); |
| if (ret > 0) |
| ret = 0; /* reset on success */ |
| else |
| return ret; |
| } |
| } |
| |
| 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; |
| if (ssl->options.groupMessages) |
| ret = 0; |
| else |
| ret = SendBuffered(ssl); |
| ssl->options.serverState = SERVER_KEYEXCHANGE_COMPLETE; |
| } |
| #endif /* OPENSSL_EXTRA */ |
| |
| return ret; |
| } |
| |
| |
| /* cipher requirements */ |
| enum { |
| REQUIRES_RSA, |
| REQUIRES_DHE, |
| REQUIRES_ECC_DSA, |
| REQUIRES_ECC_STATIC, |
| REQUIRES_PSK, |
| REQUIRES_NTRU, |
| REQUIRES_RSA_SIG |
| }; |
| |
| |
| |
| /* Does this cipher suite (first, second) have the requirement |
| an ephemeral key exchange will still require the key for signing |
| the key exchange so ECHDE_RSA requires an rsa key thus rsa_kea */ |
| static int CipherRequires(byte first, byte second, int requirement) |
| { |
| /* ECC extensions */ |
| if (first == ECC_BYTE) { |
| |
| switch (second) { |
| |
| case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA : |
| if (requirement == REQUIRES_RSA) |
| return 1; |
| break; |
| |
| case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA : |
| if (requirement == REQUIRES_ECC_STATIC) |
| return 1; |
| if (requirement == REQUIRES_RSA_SIG) |
| return 1; |
| break; |
| |
| case TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA : |
| if (requirement == REQUIRES_RSA) |
| return 1; |
| break; |
| |
| case TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA : |
| if (requirement == REQUIRES_ECC_STATIC) |
| return 1; |
| if (requirement == REQUIRES_RSA_SIG) |
| return 1; |
| break; |
| |
| case TLS_ECDHE_RSA_WITH_RC4_128_SHA : |
| if (requirement == REQUIRES_RSA) |
| return 1; |
| break; |
| |
| case TLS_ECDH_RSA_WITH_RC4_128_SHA : |
| if (requirement == REQUIRES_ECC_STATIC) |
| return 1; |
| if (requirement == REQUIRES_RSA_SIG) |
| return 1; |
| break; |
| |
| case TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA : |
| if (requirement == REQUIRES_ECC_DSA) |
| return 1; |
| break; |
| |
| case TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA : |
| if (requirement == REQUIRES_ECC_STATIC) |
| return 1; |
| break; |
| |
| case TLS_ECDHE_ECDSA_WITH_RC4_128_SHA : |
| if (requirement == REQUIRES_ECC_DSA) |
| return 1; |
| break; |
| |
| case TLS_ECDH_ECDSA_WITH_RC4_128_SHA : |
| if (requirement == REQUIRES_ECC_STATIC) |
| return 1; |
| break; |
| |
| case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA : |
| if (requirement == REQUIRES_RSA) |
| return 1; |
| break; |
| |
| case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA : |
| if (requirement == REQUIRES_ECC_STATIC) |
| return 1; |
| if (requirement == REQUIRES_RSA_SIG) |
| return 1; |
| break; |
| |
| case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA : |
| if (requirement == REQUIRES_ECC_DSA) |
| return 1; |
| break; |
| |
| case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA : |
| if (requirement == REQUIRES_ECC_STATIC) |
| return 1; |
| break; |
| |
| case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA : |
| if (requirement == REQUIRES_ECC_DSA) |
| return 1; |
| break; |
| |
| case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA : |
| if (requirement == REQUIRES_ECC_STATIC) |
| return 1; |
| break; |
| |
| case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 : |
| if (requirement == REQUIRES_ECC_DSA) |
| return 1; |
| break; |
| |
| case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 : |
| if (requirement == REQUIRES_ECC_DSA) |
| return 1; |
| break; |
| |
| case TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 : |
| if (requirement == REQUIRES_ECC_STATIC) |
| return 1; |
| break; |
| |
| case TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 : |
| if (requirement == REQUIRES_ECC_STATIC) |
| return 1; |
| break; |
| |
| case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 : |
| if (requirement == REQUIRES_RSA) |
| return 1; |
| break; |
| |
| case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 : |
| if (requirement == REQUIRES_RSA) |
| return 1; |
| break; |
| |
| case TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 : |
| if (requirement == REQUIRES_ECC_STATIC) |
| return 1; |
| if (requirement == REQUIRES_RSA_SIG) |
| return 1; |
| break; |
| |
| case TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 : |
| if (requirement == REQUIRES_ECC_STATIC) |
| return 1; |
| if (requirement == REQUIRES_RSA_SIG) |
| return 1; |
| break; |
| |
| default: |
| CYASSL_MSG("Unsupported cipher suite, CipherRequires ECC"); |
| return 0; |
| } /* switch */ |
| } /* if */ |
| if (first != ECC_BYTE) { /* normal suites */ |
| switch (second) { |
| |
| case SSL_RSA_WITH_RC4_128_SHA : |
| if (requirement == REQUIRES_RSA) |
| return 1; |
| break; |
| |
| case TLS_NTRU_RSA_WITH_RC4_128_SHA : |
| if (requirement == REQUIRES_NTRU) |
| return 1; |
| break; |
| |
| case SSL_RSA_WITH_RC4_128_MD5 : |
| if (requirement == REQUIRES_RSA) |
| return 1; |
| break; |
| |
| case SSL_RSA_WITH_3DES_EDE_CBC_SHA : |
| if (requirement == REQUIRES_RSA) |
| return 1; |
| break; |
| |
| case TLS_NTRU_RSA_WITH_3DES_EDE_CBC_SHA : |
| if (requirement == REQUIRES_NTRU) |
| return 1; |
| break; |
| |
| case TLS_RSA_WITH_AES_128_CBC_SHA : |
| if (requirement == REQUIRES_RSA) |
| return 1; |
| break; |
| |
| case TLS_RSA_WITH_AES_128_CBC_SHA256 : |
| if (requirement == REQUIRES_RSA) |
| return 1; |
| break; |
| |
| case TLS_NTRU_RSA_WITH_AES_128_CBC_SHA : |
| if (requirement == REQUIRES_NTRU) |
| return 1; |
| break; |
| |
| case TLS_RSA_WITH_AES_256_CBC_SHA : |
| if (requirement == REQUIRES_RSA) |
| return 1; |
| break; |
| |
| case TLS_RSA_WITH_AES_256_CBC_SHA256 : |
| if (requirement == REQUIRES_RSA) |
| return 1; |
| break; |
| |
| case TLS_NTRU_RSA_WITH_AES_256_CBC_SHA : |
| if (requirement == REQUIRES_NTRU) |
| return 1; |
| break; |
| |
| case TLS_PSK_WITH_AES_128_CBC_SHA : |
| if (requirement == REQUIRES_PSK) |
| return 1; |
| break; |
| |
| case TLS_PSK_WITH_AES_256_CBC_SHA : |
| if (requirement == REQUIRES_PSK) |
| return 1; |
| break; |
| |
| case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 : |
| if (requirement == REQUIRES_RSA) |
| return 1; |
| if (requirement == REQUIRES_DHE) |
| return 1; |
| break; |
| |
| case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 : |
| if (requirement == REQUIRES_RSA) |
| return 1; |
| if (requirement == REQUIRES_DHE) |
| return 1; |
| break; |
| |
| case TLS_DHE_RSA_WITH_AES_128_CBC_SHA : |
| if (requirement == REQUIRES_RSA) |
| return 1; |
| if (requirement == REQUIRES_DHE) |
| return 1; |
| break; |
| |
| case TLS_DHE_RSA_WITH_AES_256_CBC_SHA : |
| if (requirement == REQUIRES_RSA) |
| return 1; |
| if (requirement == REQUIRES_DHE) |
| return 1; |
| break; |
| |
| case TLS_RSA_WITH_HC_128_CBC_MD5 : |
| if (requirement == REQUIRES_RSA) |
| return 1; |
| break; |
| |
| case TLS_RSA_WITH_HC_128_CBC_SHA : |
| if (requirement == REQUIRES_RSA) |
| return 1; |
| break; |
| |
| case TLS_RSA_WITH_RABBIT_CBC_SHA : |
| if (requirement == REQUIRES_RSA) |
| return 1; |
| break; |
| |
| case TLS_RSA_WITH_AES_128_GCM_SHA256 : |
| case TLS_RSA_WITH_AES_256_GCM_SHA384 : |
| if (requirement == REQUIRES_RSA) |
| return 1; |
| break; |
| |
| case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 : |
| case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 : |
| if (requirement == REQUIRES_RSA) |
| return 1; |
| if (requirement == REQUIRES_DHE) |
| return 1; |
| break; |
| |
| default: |
| CYASSL_MSG("Unsupported cipher suite, CipherRequires"); |
| return 0; |
| } /* switch */ |
| } /* if ECC / Normal suites else */ |
| |
| return 0; |
| } |
| |
| |
| |
| |
| |
| /* Make sure cert/key are valid for this suite, true on success */ |
| static int VerifySuite(CYASSL* ssl, word16 idx) |
| { |
| int haveRSA = !ssl->options.haveStaticECC; |
| int havePSK = 0; |
| byte first = ssl->suites.suites[idx]; |
| byte second = ssl->suites.suites[idx+1]; |
| |
| CYASSL_ENTER("VerifySuite"); |
| |
| #ifndef NO_PSK |
| havePSK = ssl->options.havePSK; |
| #endif |
| |
| if (ssl->options.haveNTRU) |
| haveRSA = 0; |
| |
| if (CipherRequires(first, second, REQUIRES_RSA)) { |
| CYASSL_MSG("Requires RSA"); |
| if (haveRSA == 0) { |
| CYASSL_MSG("Don't have RSA"); |
| return 0; |
| } |
| } |
| |
| if (CipherRequires(first, second, REQUIRES_DHE)) { |
| CYASSL_MSG("Requires DHE"); |
| if (ssl->options.haveDH == 0) { |
| CYASSL_MSG("Don't have DHE"); |
| return 0; |
| } |
| } |
| |
| if (CipherRequires(first, second, REQUIRES_ECC_DSA)) { |
| CYASSL_MSG("Requires ECCDSA"); |
| if (ssl->options.haveECDSAsig == 0) { |
| CYASSL_MSG("Don't have ECCDSA"); |
| return 0; |
| } |
| } |
| |
| if (CipherRequires(first, second, REQUIRES_ECC_STATIC)) { |
| CYASSL_MSG("Requires static ECC"); |
| if (ssl->options.haveStaticECC == 0) { |
| CYASSL_MSG("Don't have static ECC"); |
| return 0; |
| } |
| } |
| |
| if (CipherRequires(first, second, REQUIRES_PSK)) { |
| CYASSL_MSG("Requires PSK"); |
| if (havePSK == 0) { |
| CYASSL_MSG("Don't have PSK"); |
| return 0; |
| } |
| } |
| |
| if (CipherRequires(first, second, REQUIRES_NTRU)) { |
| CYASSL_MSG("Requires NTRU"); |
| if (ssl->options.haveNTRU == 0) { |
| CYASSL_MSG("Don't have NTRU"); |
| return 0; |
| } |
| } |
| |
| if (CipherRequires(first, second, REQUIRES_RSA_SIG)) { |
| CYASSL_MSG("Requires RSA Signature"); |
| if (ssl->options.side == SERVER_END && ssl->options.haveECDSAsig == 1) { |
| CYASSL_MSG("Don't have RSA Signature"); |
| return 0; |
| } |
| } |
| |
| /* ECCDHE is always supported if ECC on */ |
| |
| return 1; |
| } |
| |
| |
| static int MatchSuite(CYASSL* ssl, Suites* peerSuites) |
| { |
| word16 i, j; |
| |
| CYASSL_ENTER("MatchSuite"); |
| |
| /* & 0x1 equivalent % 2 */ |
| if (peerSuites->suiteSz == 0 || peerSuites->suiteSz & 0x1) |
| return MATCH_SUITE_ERROR; |
| |
| /* start with best, if a match we are good */ |
| for (i = 0; i < ssl->suites.suiteSz; i += 2) |
| for (j = 0; j < peerSuites->suiteSz; j += 2) |
| if (ssl->suites.suites[i] == peerSuites->suites[j] && |
| ssl->suites.suites[i+1] == peerSuites->suites[j+1] ) { |
| |
| if (VerifySuite(ssl, i)) { |
| CYASSL_MSG("Verified suite validity"); |
| ssl->options.cipherSuite0 = ssl->suites.suites[i]; |
| ssl->options.cipherSuite = ssl->suites.suites[i+1]; |
| return SetCipherSpecs(ssl); |
| } |
| else { |
| CYASSL_MSG("Coult not verify suite validity, continue"); |
| } |
| } |
| |
| return MATCH_SUITE_ERROR; |
| } |
| |
| |
| /* process old style client hello, deprecate? */ |
| int ProcessOldClientHello(CYASSL* ssl, const byte* input, word32* inOutIdx, |
| word32 inSz, word16 sz) |
| { |
| word32 idx = *inOutIdx; |
| word16 sessionSz; |
| word16 randomSz; |
| word16 i, j; |
| ProtocolVersion pv; |
| Suites clSuites; |
| |
| (void)inSz; |
| CYASSL_MSG("Got old format client hello"); |
| #ifdef CYASSL_CALLBACKS |
| if (ssl->hsInfoOn) |
| AddPacketName("ClientHello", &ssl->handShakeInfo); |
| if (ssl->toInfoOn) |
| AddLateName("ClientHello", &ssl->timeoutInfo); |
| #endif |
| |
| /* manually hash input since different format */ |
| Md5Update(&ssl->hashMd5, input + idx, sz); |
| ShaUpdate(&ssl->hashSha, input + idx, sz); |
| #ifndef NO_SHA256 |
| if (IsAtLeastTLSv1_2(ssl)) |
| Sha256Update(&ssl->hashSha256, input + idx, sz); |
| #endif |
| |
| /* does this value mean client_hello? */ |
| idx++; |
| |
| /* version */ |
| pv.major = input[idx++]; |
| pv.minor = input[idx++]; |
| ssl->chVersion = pv; /* store */ |
| |
| if (ssl->version.minor > pv.minor) { |
| byte havePSK = 0; |
| if (!ssl->options.downgrade) { |
| CYASSL_MSG("Client trying to connect with lesser version"); |
| return VERSION_ERROR; |
| } |
| if (pv.minor == SSLv3_MINOR) { |
| /* turn off tls */ |
| CYASSL_MSG(" downgrading to SSLv3"); |
| ssl->options.tls = 0; |
| ssl->options.tls1_1 = 0; |
| ssl->version.minor = SSLv3_MINOR; |
| } |
| else if (pv.minor == TLSv1_MINOR) { |
| CYASSL_MSG(" downgrading to TLSv1"); |
| /* turn off tls 1.1+ */ |
| ssl->options.tls1_1 = 0; |
| ssl->version.minor = TLSv1_MINOR; |
| } |
| else if (pv.minor == TLSv1_1_MINOR) { |
| CYASSL_MSG(" downgrading to TLSv1.1"); |
| ssl->version.minor = TLSv1_1_MINOR; |
| } |
| #ifndef NO_PSK |
| havePSK = ssl->options.havePSK; |
| #endif |
| |
| InitSuites(&ssl->suites, ssl->version, ssl->options.haveDH, havePSK, |
| ssl->options.haveNTRU, ssl->options.haveECDSAsig, |
| ssl->options.haveStaticECC, ssl->options.side); |
| } |
| |
| /* suite size */ |
| ato16(&input[idx], &clSuites.suiteSz); |
| idx += 2; |
| |
| if (clSuites.suiteSz > MAX_SUITE_SZ) |
| return BUFFER_ERROR; |
| |
| /* session size */ |
| ato16(&input[idx], &sessionSz); |
| idx += 2; |
| |
| if (sessionSz > ID_LEN) |
| return BUFFER_ERROR; |
| |
| /* random size */ |
| ato16(&input[idx], &randomSz); |
| idx += 2; |
| |
| if (randomSz > RAN_LEN) |
| return BUFFER_ERROR; |
| |
| /* suites */ |
| for (i = 0, j = 0; i < clSuites.suiteSz; i += 3) { |
| byte first = input[idx++]; |
| if (!first) { /* implicit: skip sslv2 type */ |
| XMEMCPY(&clSuites.suites[j], &input[idx], 2); |
| j += 2; |
| } |
| idx += 2; |
| } |
| clSuites.suiteSz = j; |
| |
| /* session id */ |
| if (sessionSz) { |
| XMEMCPY(ssl->arrays.sessionID, input + idx, sessionSz); |
| idx += sessionSz; |
| ssl->options.resuming = 1; |
| } |
| |
| /* random */ |
| if (randomSz < RAN_LEN) |
| XMEMSET(ssl->arrays.clientRandom, 0, RAN_LEN - randomSz); |
| XMEMCPY(&ssl->arrays.clientRandom[RAN_LEN - randomSz], input + idx, |
| randomSz); |
| idx += randomSz; |
| |
| if (ssl->options.usingCompression) |
| ssl->options.usingCompression = 0; /* turn off */ |
| |
| ssl->options.clientState = CLIENT_HELLO_COMPLETE; |
| *inOutIdx = idx; |
| |
| ssl->options.haveSessionId = 1; |
| /* DoClientHello uses same resume code */ |
| while (ssl->options.resuming) { /* let's try */ |
| int ret; |
| CYASSL_SESSION* session = GetSession(ssl, ssl->arrays.masterSecret); |
| if (!session) { |
| ssl->options.resuming = 0; |
| break; /* session lookup failed */ |
| } |
| if (MatchSuite(ssl, &clSuites) < 0) { |
| CYASSL_MSG("Unsupported cipher suite, OldClientHello"); |
| return UNSUPPORTED_SUITE; |
| } |
| |
| RNG_GenerateBlock(&ssl->rng, ssl->arrays.serverRandom, RAN_LEN); |
| if (ssl->options.tls) |
| ret = DeriveTlsKeys(ssl); |
| else |
| ret = DeriveKeys(ssl); |
| ssl->options.clientState = CLIENT_KEYEXCHANGE_COMPLETE; |
| |
| return ret; |
| } |
| |
| return MatchSuite(ssl, &clSuites); |
| } |
| |
| |
| static int DoClientHello(CYASSL* 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 > pv.minor) { |
| byte havePSK = 0; |
| if (!ssl->options.downgrade) { |
| CYASSL_MSG("Client trying to connect with lesser version"); |
| return VERSION_ERROR; |
| } |
| if (pv.minor == SSLv3_MINOR) { |
| /* turn off tls */ |
| CYASSL_MSG(" downgrading to SSLv3"); |
| ssl->options.tls = 0; |
| ssl->options.tls1_1 = 0; |
| ssl->version.minor = SSLv3_MINOR; |
| } |
| else if (pv.minor == TLSv1_MINOR) { |
| /* turn off tls 1.1+ */ |
| CYASSL_MSG(" downgrading to TLSv1"); |
| ssl->options.tls1_1 = 0; |
| ssl->version.minor = TLSv1_MINOR; |
| } |
| else if (pv.minor == TLSv1_1_MINOR) { |
| CYASSL_MSG(" downgrading to TLSv1.1"); |
| ssl->version.minor = TLSv1_1_MINOR; |
| } |
| #ifndef NO_PSK |
| havePSK = ssl->options.havePSK; |
| #endif |
| InitSuites(&ssl->suites, ssl->version, ssl->options.haveDH, havePSK, |
| ssl->options.haveNTRU, ssl->options.haveECDSAsig, |
| ssl->options.haveStaticECC, ssl->options.side); |
| } |
| /* random */ |
| XMEMCPY(ssl->arrays.clientRandom, input + i, RAN_LEN); |
| i += RAN_LEN; |
| |
| #ifdef SHOW_SECRETS |
| { |
| int j; |
| printf("client random: "); |
| for (j = 0; j < RAN_LEN; j++) |
| printf("%02x", ssl->arrays.clientRandom[j]); |
| printf("\n"); |
| } |
| #endif |
| /* session id */ |
| b = input[i++]; |
| if (b) { |
| if (i + ID_LEN > totalSz) |
| return INCOMPLETE_DATA; |
| XMEMCPY(ssl->arrays.sessionID, input + i, ID_LEN); |
| i += b; |
| ssl->options.resuming= 1; /* client wants to resume */ |
| CYASSL_MSG("Client wants to resume session"); |
| } |
| |
| #ifdef CYASSL_DTLS |
| /* cookie */ |
| if (ssl->options.dtls) { |
| b = input[i++]; |
| if (b) { |
| byte cookie[MAX_COOKIE_LEN]; |
| byte cookieSz; |
| |
| if (b > MAX_COOKIE_LEN) |
| return BUFFER_ERROR; |
| if (i + b > totalSz) |
| return INCOMPLETE_DATA; |
| cookieSz = EmbedGenerateCookie(cookie, COOKIE_SZ, ssl); |
| if ((b != cookieSz) || XMEMCMP(cookie, input + i, b) != 0) |
| return COOKIE_ERROR; |
| 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) { |
| CYASSL_MSG("Not matching compression, turning off"); |
| 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 */ |
| |
| ssl->options.haveSessionId = 1; |
| /* ProcessOld uses same resume code */ |
| while (ssl->options.resuming) { /* let's try */ |
| int ret; |
| CYASSL_SESSION* session = GetSession(ssl, ssl->arrays.masterSecret); |
| if (!session) { |
| ssl->options.resuming = 0; |
| CYASSL_MSG("Session lookup for resume failed"); |
| break; /* session lookup failed */ |
| } |
| if (MatchSuite(ssl, &clSuites) < 0) { |
| CYASSL_MSG("Unsupported cipher suite, ClientHello"); |
| return UNSUPPORTED_SUITE; |
| } |
| |
| RNG_GenerateBlock(&ssl->rng, ssl->arrays.serverRandom, RAN_LEN); |
| if (ssl->options.tls) |
| ret = DeriveTlsKeys(ssl); |
| else |
| ret = DeriveKeys(ssl); |
| ssl->options.clientState = CLIENT_KEYEXCHANGE_COMPLETE; |
| |
| return ret; |
| } |
| return MatchSuite(ssl, &clSuites); |
| } |
| |
| |
| static int DoCertificateVerify(CYASSL* ssl, byte* input, word32* inOutsz, |
| word32 totalSz) |
| { |
| word16 sz = 0; |
| word32 i = *inOutsz; |
| int ret = VERIFY_CERT_ERROR; /* start in error state */ |
| byte* sig; |
| byte* out; |
| int outLen; |
| |
| #ifdef CYASSL_CALLBACKS |
| if (ssl->hsInfoOn) |
| AddPacketName("CertificateVerify", &ssl->handShakeInfo); |
| if (ssl->toInfoOn) |
| AddLateName("CertificateVerify", &ssl->timeoutInfo); |
| #endif |
| if ( (i + VERIFY_HEADER) > totalSz) |
| return INCOMPLETE_DATA; |
| |
| if (IsAtLeastTLSv1_2(ssl)) |
| i += HASH_SIG_SIZE; |
| ato16(&input[i], &sz); |
| i += VERIFY_HEADER; |
| |
| if ( (i + sz) > totalSz) |
| return INCOMPLETE_DATA; |
| |
| if (sz > ENCRYPT_LEN) |
| return BUFFER_ERROR; |
| |
| sig = &input[i]; |
| *inOutsz = i + sz; |
| |
| /* RSA */ |
| if (ssl->peerRsaKeyPresent != 0) { |
| CYASSL_MSG("Doing RSA peer cert verify"); |
| |
| outLen = RsaSSL_VerifyInline(sig, sz, &out, &ssl->peerRsaKey); |
| |
| if (IsAtLeastTLSv1_2(ssl)) { |
| byte encodedSig[MAX_ENCODED_SIG_SZ]; |
| word32 sigSz; |
| byte* digest; |
| int typeH; |
| int digestSz; |
| |
| /* sha1 for now */ |
| digest = ssl->certHashes.sha; |
| typeH = SHAh; |
| digestSz = SHA_DIGEST_SIZE; |
| |
| sigSz = EncodeSignature(encodedSig, digest, digestSz, typeH); |
| |
| if (outLen == (int)sigSz && XMEMCMP(out, encodedSig, |
| min(sigSz, MAX_ENCODED_SIG_SZ)) == 0) |
| ret = 0; /* verified */ |
| } |
| else { |
| if (outLen == sizeof(ssl->certHashes) && XMEMCMP(out, |
| &ssl->certHashes, sizeof(ssl->certHashes)) == 0) |
| ret = 0; /* verified */ |
| } |
| } |
| #ifdef HAVE_ECC |
| else if (ssl->peerEccDsaKeyPresent) { |
| int verify = 0; |
| int err = -1; |
| |
| CYASSL_MSG("Doing ECC peer cert verify"); |
| |
| err = ecc_verify_hash(sig, sz, ssl->certHashes.sha, SHA_DIGEST_SIZE, |
| &verify, &ssl->peerEccDsaKey); |
| |
| if (err == 0 && verify == 1) |
| ret = 0; /* verified */ |
| } |
| #endif |
| return ret; |
| } |
| |
| |
| int SendServerHelloDone(CYASSL* 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.length; |
| |
| 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); |
| } |
| |
| #ifdef CYASSL_DTLS |
| int SendHelloVerifyRequest(CYASSL* ssl) |
| { |
| byte* output; |
| byte cookieSz = COOKIE_SZ; |
| int length = VERSION_SZ + ENUM_LEN + cookieSz; |
| 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.length; |
| |
| AddHeaders(output, length, hello_verify_request, ssl); |
| |
| XMEMCPY(output + idx, &ssl->chVersion, VERSION_SZ); |
| idx += VERSION_SZ; |
| |
| output[idx++] = cookieSz; |
| if ((ret = EmbedGenerateCookie(output + idx, cookieSz, ssl)) < 0) |
| return ret; |
| |
| 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); |
| } |
| #endif |
| |
| static int DoClientKeyExchange(CYASSL* ssl, byte* input, |
| word32* inOutIdx) |
| { |
| int ret = 0; |
| word32 length = 0; |
| byte* out; |
| |
| if (ssl->options.verifyPeer && ssl->options.failNoCert) |
| if (!ssl->options.havePeerCert) { |
| CYASSL_MSG("client didn't present peer cert"); |
| return NO_PEER_CERT; |
| } |
| |
| #ifdef CYASSL_CALLBACKS |
| if (ssl->hsInfoOn) |
| AddPacketName("ClientKeyExchange", &ssl->handShakeInfo); |
| if (ssl->toInfoOn) |
| AddLateName("ClientKeyExchange", &ssl->timeoutInfo); |
| #endif |
| if (ssl->specs.kea == rsa_kea) { |
| word32 idx = 0; |
| RsaKey key; |
| byte* tmp = 0; |
| |
| InitRsaKey(&key, ssl->heap); |
| |
| if (ssl->buffers.key.buffer) |
| ret = RsaPrivateKeyDecode(ssl->buffers.key.buffer, &idx, &key, |
| ssl->buffers.key.length); |
| else |
| return NO_PRIVATE_KEY; |
| |
| if (ret == 0) { |
| length = RsaEncryptSize(&key); |
| ssl->arrays.preMasterSz = SECRET_LEN; |
| |
| if (ssl->options.tls) |
| (*inOutIdx) += 2; |
| tmp = input + *inOutIdx; |
| *inOutIdx += length; |
| |
| if (RsaPrivateDecryptInline(tmp, length, &out, &key) == |
| SECRET_LEN) { |
| XMEMCPY(ssl->arrays.preMasterSecret, out, SECRET_LEN); |
| if (ssl->arrays.preMasterSecret[0] != ssl->chVersion.major |
| || |
| ssl->arrays.preMasterSecret[1] != ssl->chVersion.minor) |
| |
| ret = PMS_VERSION_ERROR; |
| else |
| ret = MakeMasterSecret(ssl); |
| } |
| else |
| ret = RSA_PRIVATE_ERROR; |
| } |
| |
| FreeRsaKey(&key); |
| #ifndef NO_PSK |
| } else if (ssl->specs.kea == psk_kea) { |
| byte* pms = ssl->arrays.preMasterSecret; |
| word16 ci_sz; |
| |
| ato16(&input[*inOutIdx], &ci_sz); |
| *inOutIdx += LENGTH_SZ; |
| if (ci_sz > MAX_PSK_ID_LEN) return CLIENT_ID_ERROR; |
| |
| XMEMCPY(ssl->arrays.client_identity, &input[*inOutIdx], ci_sz); |
| *inOutIdx += ci_sz; |
| ssl->arrays.client_identity[ci_sz] = 0; |
| |
| ssl->arrays.psk_keySz = ssl->options.server_psk_cb(ssl, |
| ssl->arrays.client_identity, ssl->arrays.psk_key, |
| MAX_PSK_KEY_LEN); |
| if (ssl->arrays.psk_keySz == 0 || |
| ssl->arrays.psk_keySz > MAX_PSK_KEY_LEN) return PSK_KEY_ERROR; |
| |
| /* make psk pre master secret */ |
| /* length of key + length 0s + length of key + key */ |
| c16toa((word16)ssl->arrays.psk_keySz, pms); |
| pms += 2; |
| XMEMSET(pms, 0, ssl->arrays.psk_keySz); |
| pms += ssl->arrays.psk_keySz; |
| c16toa((word16)ssl->arrays.psk_keySz, pms); |
| pms += 2; |
| XMEMCPY(pms, ssl->arrays.psk_key, ssl->arrays.psk_keySz); |
| ssl->arrays.preMasterSz = ssl->arrays.psk_keySz * 2 + 4; |
| |
| ret = MakeMasterSecret(ssl); |
| #endif /* NO_PSK */ |
| #ifdef HAVE_NTRU |
| } else if (ssl->specs.kea == ntru_kea) { |
| word32 rc; |
| word16 cipherLen; |
| word16 plainLen = sizeof(ssl->arrays.preMasterSecret); |
| byte* tmp; |
| |
| if (!ssl->buffers.key.buffer) |
| return NO_PRIVATE_KEY; |
| |
| ato16(&input[*inOutIdx], &cipherLen); |
| *inOutIdx += LENGTH_SZ; |
| if (cipherLen > MAX_NTRU_ENCRYPT_SZ) |
| return NTRU_KEY_ERROR; |
| |
| tmp = input + *inOutIdx; |
| rc = crypto_ntru_decrypt((word16)ssl->buffers.key.length, |
| ssl->buffers.key.buffer, cipherLen, tmp, &plainLen, |
| ssl->arrays.preMasterSecret); |
| |
| if (rc != NTRU_OK || plainLen != SECRET_LEN) |
| return NTRU_DECRYPT_ERROR; |
| *inOutIdx += cipherLen; |
| |
| ssl->arrays.preMasterSz = plainLen; |
| ret = MakeMasterSecret(ssl); |
| #endif /* HAVE_NTRU */ |
| #ifdef HAVE_ECC |
| } else if (ssl->specs.kea == ecc_diffie_hellman_kea) { |
| word32 size; |
| word32 bLength = input[*inOutIdx]; /* one byte length */ |
| *inOutIdx += 1; |
| |
| ret = ecc_import_x963(&input[*inOutIdx], bLength, &ssl->peerEccKey); |
| if (ret != 0) |
| return ECC_PEERKEY_ERROR; |
| *inOutIdx += bLength; |
| ssl->peerEccKeyPresent = 1; |
| |
| size = sizeof(ssl->arrays.preMasterSecret); |
| if (ssl->specs.static_ecdh) { |
| ecc_key staticKey; |
| word32 i = 0; |
| |
| ecc_init(&staticKey); |
| ret = EccPrivateKeyDecode(ssl->buffers.key.buffer, &i, |
| &staticKey, ssl->buffers.key.length); |
| if (ret == 0) |
| ret = ecc_shared_secret(&staticKey, &ssl->peerEccKey, |
| ssl->arrays.preMasterSecret, &size); |
| ecc_free(&staticKey); |
| } |
| else |
| ret = ecc_shared_secret(&ssl->eccTempKey, &ssl->peerEccKey, |
| ssl->arrays.preMasterSecret, &size); |
| if (ret != 0) |
| return ECC_SHARED_ERROR; |
| ssl->arrays.preMasterSz = size; |
| ret = MakeMasterSecret(ssl); |
| #endif /* HAVE_ECC */ |
| #ifdef OPENSSL_EXTRA |
| } else if (ssl->specs.kea == diffie_hellman_kea) { |
| byte* clientPub; |
| word16 clientPubSz; |
| DhKey dhKey; |
| |
| ato16(&input[*inOutIdx], &clientPubSz); |
| *inOutIdx += LENGTH_SZ; |
| |
| clientPub = &input[*inOutIdx]; |
| *inOutIdx += clientPubSz; |
| |
| InitDhKey(&dhKey); |
| ret = DhSetKey(&dhKey, ssl->buffers.serverDH_P.buffer, |
| ssl->buffers.serverDH_P.length, |
| ssl->buffers.serverDH_G.buffer, |
| ssl->buffers.serverDH_G.length); |
| if (ret == 0) |
| ret = DhAgree(&dhKey, ssl->arrays.preMasterSecret, |
| &ssl->arrays.preMasterSz, |
| ssl->buffers.serverDH_Priv.buffer, |
| ssl->buffers.serverDH_Priv.length, |
| clientPub, clientPubSz); |
| FreeDhKey(&dhKey); |
| if (ret == 0) |
| ret = MakeMasterSecret(ssl); |
| #endif /* OPENSSL_EXTRA */ |
| } |
| |
| 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 */ |
| |
| #if defined(FREERTOS) |
| |
| int InitMutex(CyaSSL_Mutex* m) |
| { |
| int iReturn; |
| |
| *m = ( CyaSSL_Mutex ) xSemaphoreCreateMutex(); |
| if( *m != NULL ) |
| iReturn = 0; |
| else |
| iReturn = BAD_MUTEX_ERROR; |
| |
| return iReturn; |
| } |
| |
| int FreeMutex(CyaSSL_Mutex* m) |
| { |
| vSemaphoreDelete( *m ); |
| return 0; |
| } |
| |
| int LockMutex(CyaSSL_Mutex* m) |
| { |
| /* Assume an infinite block, or should there be zero block? */ |
| xSemaphoreTake( *m, portMAX_DELAY ); |
| return 0; |
| } |
| |
| int UnLockMutex(CyaSSL_Mutex* m) |
| { |
| xSemaphoreGive( *m ); |
| return 0; |
| } |
| |
| #elif defined(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 BAD_MUTEX_ERROR; |
| } |
| |
| |
| int FreeMutex(CyaSSL_Mutex* m) |
| { |
| if (pthread_mutex_destroy(m) == 0) |
| return 0; |
| else |
| return BAD_MUTEX_ERROR; |
| } |
| |
| |
| int LockMutex(CyaSSL_Mutex* m) |
| { |
| if (pthread_mutex_lock(m) == 0) |
| return 0; |
| else |
| return BAD_MUTEX_ERROR; |
| } |
| |
| |
| int UnLockMutex(CyaSSL_Mutex* m) |
| { |
| if (pthread_mutex_unlock(m) == 0) |
| return 0; |
| else |
| return BAD_MUTEX_ERROR; |
| } |
| |
| #elif defined(THREADX) |
| |
| int InitMutex(CyaSSL_Mutex* m) |
| { |
| if (tx_mutex_create(m, "CyaSSL Mutex", TX_NO_INHERIT) == 0) |
| return 0; |
| else |
| return BAD_MUTEX_ERROR; |
| } |
| |
| |
| int FreeMutex(CyaSSL_Mutex* m) |
| { |
| if (tx_mutex_delete(m) == 0) |
| return 0; |
| else |
| return BAD_MUTEX_ERROR; |
| } |
| |
| |
| int LockMutex(CyaSSL_Mutex* m) |
| { |
| if (tx_mutex_get(m, TX_WAIT_FOREVER) == 0) |
| return 0; |
| else |
| return BAD_MUTEX_ERROR; |
| } |
| |
| |
| int UnLockMutex(CyaSSL_Mutex* m) |
| { |
| if (tx_mutex_put(m) == 0) |
| return 0; |
| else |
| return BAD_MUTEX_ERROR; |
| } |
| |
| #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 BAD_MUTEX_ERROR; |
| #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 BAD_MUTEX_ERROR; |
| #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 BAD_MUTEX_ERROR; |
| #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 BAD_MUTEX_ERROR; |
| #else |
| return 0; |
| #endif |
| |
| } |
| |
| #endif /* USE_WINDOWS_API */ |
| #endif /* SINGLE_THREADED */ |