From d2003bb8b7bedbfc04904c1465e132575776b20a Mon Sep 17 00:00:00 2001 From: toddouska Date: Tue, 21 May 2013 14:37:50 -0700 Subject: [PATCH] merge in sni --- configure.ac | 13 + ctaocrypt/src/memory.c | 2 + ctaocrypt/src/tfm.c | 2 +- cyassl/ctaocrypt/settings.h | 1 - cyassl/ctaocrypt/types.h | 51 ++-- cyassl/error.h | 167 +++++------ cyassl/internal.h | 82 ++++- cyassl/ssl.h | 7 + examples/client/client.c | 22 +- examples/server/server.c | 19 +- src/internal.c | 84 +++++- src/ssl.c | 21 ++ src/tls.c | 580 ++++++++++++++++++++++++++++++++++++ tests/api.c | 38 +++ tests/unit.h | 50 ++++ 15 files changed, 1012 insertions(+), 127 deletions(-) diff --git a/configure.ac b/configure.ac index c1d02e8af..c6bcddb4e 100644 --- a/configure.ac +++ b/configure.ac @@ -1049,6 +1049,18 @@ then AC_MSG_ERROR([cannot enable ntru and small, ntru requires TLS which small turns off.]) fi +# SNI +AC_ARG_ENABLE([sni], + [ --enable-sni Enable SNI (default: disabled)], + [ ENABLED_SNI=$enableval ], + [ ENABLED_SNI=no ] + ) + +if test "x$ENABLED_SNI" = "xyes" +then + AM_CFLAGS="$AM_CFLAGS -DHAVE_TLS_EXTENSIONS -DHAVE_SNI" +fi + #valgrind AC_ARG_ENABLE([valgrind], @@ -1415,6 +1427,7 @@ echo " * CRL-MONITOR: $ENABLED_CRL_MONITOR" echo " * Persistent session cache: $ENABLED_SAVESESSION" echo " * Persistent cert cache: $ENABLED_SAVECERT" echo " * NTRU: $ENABLED_NTRU" +echo " * SNI: $ENABLED_SNI" echo " * valgrind unit tests: $ENABLED_VALGRIND" echo " * LIBZ: $ENABLED_LIBZ" echo " * Examples: $ENABLED_EXAMPLES" diff --git a/ctaocrypt/src/memory.c b/ctaocrypt/src/memory.c index ce80ae373..b9743fbcd 100644 --- a/ctaocrypt/src/memory.c +++ b/ctaocrypt/src/memory.c @@ -25,6 +25,8 @@ #include +/* submitted by eof */ + #ifdef USE_CYASSL_MEMORY #include diff --git a/ctaocrypt/src/tfm.c b/ctaocrypt/src/tfm.c index a07ee198a..9e39405c9 100644 --- a/ctaocrypt/src/tfm.c +++ b/ctaocrypt/src/tfm.c @@ -26,7 +26,7 @@ */ /** - * Edited by Moisés Guimarães (moises.guimaraes@phoebus.com.br) + * Edited by Moisés Guimarães (moisesguimaraesm@gmail.com) * to fit CyaSSL's needs. */ diff --git a/cyassl/ctaocrypt/settings.h b/cyassl/ctaocrypt/settings.h index a3693d86e..bf2653b74 100644 --- a/cyassl/ctaocrypt/settings.h +++ b/cyassl/ctaocrypt/settings.h @@ -89,7 +89,6 @@ #ifdef MICROCHIP_PIC32 #define SIZEOF_LONG_LONG 8 #define SINGLE_THREADED - #define CYASSL_USER_IO #define NO_WRITEV #define NO_DEV_RANDOM #define NO_FILESYSTEM diff --git a/cyassl/ctaocrypt/types.h b/cyassl/ctaocrypt/types.h index 6f0282798..9d367feab 100644 --- a/cyassl/ctaocrypt/types.h +++ b/cyassl/ctaocrypt/types.h @@ -202,30 +202,30 @@ enum { /* memory allocation types for user hints */ enum { - DYNAMIC_TYPE_CA = 1, - DYNAMIC_TYPE_CERT = 2, - DYNAMIC_TYPE_KEY = 3, - DYNAMIC_TYPE_FILE = 4, - DYNAMIC_TYPE_SUBJECT_CN = 5, - DYNAMIC_TYPE_PUBLIC_KEY = 6, - DYNAMIC_TYPE_SIGNER = 7, - DYNAMIC_TYPE_NONE = 8, - DYNAMIC_TYPE_BIGINT = 9, - DYNAMIC_TYPE_RSA = 10, - DYNAMIC_TYPE_METHOD = 11, - DYNAMIC_TYPE_OUT_BUFFER = 12, - DYNAMIC_TYPE_IN_BUFFER = 13, - DYNAMIC_TYPE_INFO = 14, - DYNAMIC_TYPE_DH = 15, - DYNAMIC_TYPE_DOMAIN = 16, - DYNAMIC_TYPE_SSL = 17, - DYNAMIC_TYPE_CTX = 18, - DYNAMIC_TYPE_WRITEV = 19, - DYNAMIC_TYPE_OPENSSL = 20, - DYNAMIC_TYPE_DSA = 21, - DYNAMIC_TYPE_CRL = 22, - DYNAMIC_TYPE_REVOKED = 23, - DYNAMIC_TYPE_CRL_ENTRY = 24, + DYNAMIC_TYPE_CA = 1, + DYNAMIC_TYPE_CERT = 2, + DYNAMIC_TYPE_KEY = 3, + DYNAMIC_TYPE_FILE = 4, + DYNAMIC_TYPE_SUBJECT_CN = 5, + DYNAMIC_TYPE_PUBLIC_KEY = 6, + DYNAMIC_TYPE_SIGNER = 7, + DYNAMIC_TYPE_NONE = 8, + DYNAMIC_TYPE_BIGINT = 9, + DYNAMIC_TYPE_RSA = 10, + DYNAMIC_TYPE_METHOD = 11, + DYNAMIC_TYPE_OUT_BUFFER = 12, + DYNAMIC_TYPE_IN_BUFFER = 13, + DYNAMIC_TYPE_INFO = 14, + DYNAMIC_TYPE_DH = 15, + DYNAMIC_TYPE_DOMAIN = 16, + DYNAMIC_TYPE_SSL = 17, + DYNAMIC_TYPE_CTX = 18, + DYNAMIC_TYPE_WRITEV = 19, + DYNAMIC_TYPE_OPENSSL = 20, + DYNAMIC_TYPE_DSA = 21, + DYNAMIC_TYPE_CRL = 22, + DYNAMIC_TYPE_REVOKED = 23, + DYNAMIC_TYPE_CRL_ENTRY = 24, DYNAMIC_TYPE_CERT_MANAGER = 25, DYNAMIC_TYPE_CRL_MONITOR = 26, DYNAMIC_TYPE_OCSP_STATUS = 27, @@ -243,7 +243,8 @@ enum { DYNAMIC_TYPE_DTLS_MSG = 39, DYNAMIC_TYPE_CAVIUM_TMP = 40, DYNAMIC_TYPE_CAVIUM_RSA = 41, - DYNAMIC_TYPE_X509 = 42 + DYNAMIC_TYPE_X509 = 42, + DYNAMIC_TYPE_TLSX = 43 }; /* stack protection */ diff --git a/cyassl/error.h b/cyassl/error.h index 0854813d9..3f60642c9 100644 --- a/cyassl/error.h +++ b/cyassl/error.h @@ -30,94 +30,95 @@ #endif enum CyaSSL_ErrorCodes { - INPUT_CASE_ERROR = -201, /* process input state error */ - PREFIX_ERROR = -202, /* bad index to key rounds */ - MEMORY_ERROR = -203, /* out of memory */ - VERIFY_FINISHED_ERROR = -204, /* verify problem on finished */ - VERIFY_MAC_ERROR = -205, /* verify mac problem */ - PARSE_ERROR = -206, /* parse error on header */ - UNKNOWN_HANDSHAKE_TYPE = -207, /* weird handshake type */ - SOCKET_ERROR_E = -208, /* error state on socket */ - SOCKET_NODATA = -209, /* expected data, not there */ - INCOMPLETE_DATA = -210, /* don't have enough data to - complete task */ - UNKNOWN_RECORD_TYPE = -211, /* unknown type in record hdr */ - DECRYPT_ERROR = -212, /* error during decryption */ - FATAL_ERROR = -213, /* recvd alert fatal error */ - ENCRYPT_ERROR = -214, /* error during encryption */ - FREAD_ERROR = -215, /* fread problem */ - NO_PEER_KEY = -216, /* need peer's key */ - NO_PRIVATE_KEY = -217, /* need the private key */ - RSA_PRIVATE_ERROR = -218, /* error during rsa priv op */ - NO_DH_PARAMS = -219, /* server missing DH params */ - BUILD_MSG_ERROR = -220, /* build message failure */ + INPUT_CASE_ERROR = -201, /* process input state error */ + PREFIX_ERROR = -202, /* bad index to key rounds */ + MEMORY_ERROR = -203, /* out of memory */ + VERIFY_FINISHED_ERROR = -204, /* verify problem on finished */ + VERIFY_MAC_ERROR = -205, /* verify mac problem */ + PARSE_ERROR = -206, /* parse error on header */ + UNKNOWN_HANDSHAKE_TYPE = -207, /* weird handshake type */ + SOCKET_ERROR_E = -208, /* error state on socket */ + SOCKET_NODATA = -209, /* expected data, not there */ + INCOMPLETE_DATA = -210, /* don't have enough data to + complete task */ + UNKNOWN_RECORD_TYPE = -211, /* unknown type in record hdr */ + DECRYPT_ERROR = -212, /* error during decryption */ + FATAL_ERROR = -213, /* recvd alert fatal error */ + ENCRYPT_ERROR = -214, /* error during encryption */ + FREAD_ERROR = -215, /* fread problem */ + NO_PEER_KEY = -216, /* need peer's key */ + NO_PRIVATE_KEY = -217, /* need the private key */ + RSA_PRIVATE_ERROR = -218, /* error during rsa priv op */ + NO_DH_PARAMS = -219, /* server missing DH params */ + BUILD_MSG_ERROR = -220, /* build message failure */ - BAD_HELLO = -221, /* client hello malformed */ - DOMAIN_NAME_MISMATCH = -222, /* peer subject name mismatch */ - WANT_READ = -223, /* want read, call again */ - NOT_READY_ERROR = -224, /* handshake layer not ready */ - PMS_VERSION_ERROR = -225, /* pre m secret version error */ - VERSION_ERROR = -226, /* record layer version error */ - WANT_WRITE = -227, /* want write, call again */ - BUFFER_ERROR = -228, /* malformed buffer input */ - VERIFY_CERT_ERROR = -229, /* verify cert error */ - VERIFY_SIGN_ERROR = -230, /* verify sign error */ - CLIENT_ID_ERROR = -231, /* psk client identity error */ - SERVER_HINT_ERROR = -232, /* psk server hint error */ - PSK_KEY_ERROR = -233, /* psk key error */ - ZLIB_INIT_ERROR = -234, /* zlib init error */ - ZLIB_COMPRESS_ERROR = -235, /* zlib compression error */ - ZLIB_DECOMPRESS_ERROR = -236, /* zlib decompression error */ + BAD_HELLO = -221, /* client hello malformed */ + DOMAIN_NAME_MISMATCH = -222, /* peer subject name mismatch */ + WANT_READ = -223, /* want read, call again */ + NOT_READY_ERROR = -224, /* handshake layer not ready */ + PMS_VERSION_ERROR = -225, /* pre m secret version error */ + VERSION_ERROR = -226, /* record layer version error */ + WANT_WRITE = -227, /* want write, call again */ + BUFFER_ERROR = -228, /* malformed buffer input */ + VERIFY_CERT_ERROR = -229, /* verify cert error */ + VERIFY_SIGN_ERROR = -230, /* verify sign error */ + CLIENT_ID_ERROR = -231, /* psk client identity error */ + SERVER_HINT_ERROR = -232, /* psk server hint error */ + PSK_KEY_ERROR = -233, /* psk key error */ + ZLIB_INIT_ERROR = -234, /* zlib init error */ + ZLIB_COMPRESS_ERROR = -235, /* zlib compression error */ + ZLIB_DECOMPRESS_ERROR = -236, /* zlib decompression error */ - GETTIME_ERROR = -237, /* gettimeofday failed ??? */ - GETITIMER_ERROR = -238, /* getitimer failed ??? */ - SIGACT_ERROR = -239, /* sigaction failed ??? */ - SETITIMER_ERROR = -240, /* setitimer failed ??? */ - LENGTH_ERROR = -241, /* record layer length error */ - PEER_KEY_ERROR = -242, /* can't decode peer key */ - ZERO_RETURN = -243, /* peer sent close notify */ - SIDE_ERROR = -244, /* wrong client/server type */ - NO_PEER_CERT = -245, /* peer didn't send key */ - NTRU_KEY_ERROR = -246, /* NTRU key error */ - NTRU_DRBG_ERROR = -247, /* NTRU drbg error */ - NTRU_ENCRYPT_ERROR = -248, /* NTRU encrypt error */ - NTRU_DECRYPT_ERROR = -249, /* NTRU decrypt error */ - ECC_CURVETYPE_ERROR = -250, /* Bad ECC Curve Type */ - ECC_CURVE_ERROR = -251, /* Bad ECC Curve */ - ECC_PEERKEY_ERROR = -252, /* Bad Peer ECC Key */ - ECC_MAKEKEY_ERROR = -253, /* Bad Make ECC Key */ - ECC_EXPORT_ERROR = -254, /* Bad ECC Export Key */ - ECC_SHARED_ERROR = -255, /* Bad ECC Shared Secret */ - BAD_MUTEX_ERROR = -256, /* Bad mutex */ - NOT_CA_ERROR = -257, /* Not a CA cert error */ - BAD_PATH_ERROR = -258, /* Bad path for opendir */ - BAD_CERT_MANAGER_ERROR = -259, /* Bad Cert Manager */ - OCSP_CERT_REVOKED = -260, /* OCSP Certificate revoked */ - CRL_CERT_REVOKED = -261, /* CRL Certificate revoked */ - CRL_MISSING = -262, /* CRL Not loaded */ - MONITOR_RUNNING_E = -263, /* CRL Monitor already running */ - THREAD_CREATE_E = -264, /* Thread Create Error */ - OCSP_NEED_URL = -265, /* OCSP need an URL for lookup */ - OCSP_CERT_UNKNOWN = -266, /* OCSP responder doesn't know */ - OCSP_LOOKUP_FAIL = -267, /* OCSP lookup not successful */ - MAX_CHAIN_ERROR = -268, /* max chain depth exceeded */ - COOKIE_ERROR = -269, /* dtls cookie error */ - SEQUENCE_ERROR = -270, /* dtls sequence error */ - SUITES_ERROR = -271, /* suites pointer error */ - SSL_NO_PEM_HEADER = -272, /* no PEM header found */ - OUT_OF_ORDER_E = -273, /* out of order message */ - BAD_KEA_TYPE_E = -274, /* bad KEA type found */ - SANITY_CIPHER_E = -275, /* sanity check on cipher error */ - RECV_OVERFLOW_E = -276, /* RXCB returned more than rqed */ - GEN_COOKIE_E = -277, /* Generate Cookie Error */ - NO_PEER_VERIFY = -278, /* Need peer cert verify Error */ - FWRITE_ERROR = -279, /* fwrite problem */ - CACHE_MATCH_ERROR = -280, /* cache hdr match err */ + GETTIME_ERROR = -237, /* gettimeofday failed ??? */ + GETITIMER_ERROR = -238, /* getitimer failed ??? */ + SIGACT_ERROR = -239, /* sigaction failed ??? */ + SETITIMER_ERROR = -240, /* setitimer failed ??? */ + LENGTH_ERROR = -241, /* record layer length error */ + PEER_KEY_ERROR = -242, /* can't decode peer key */ + ZERO_RETURN = -243, /* peer sent close notify */ + SIDE_ERROR = -244, /* wrong client/server type */ + NO_PEER_CERT = -245, /* peer didn't send key */ + NTRU_KEY_ERROR = -246, /* NTRU key error */ + NTRU_DRBG_ERROR = -247, /* NTRU drbg error */ + NTRU_ENCRYPT_ERROR = -248, /* NTRU encrypt error */ + NTRU_DECRYPT_ERROR = -249, /* NTRU decrypt error */ + ECC_CURVETYPE_ERROR = -250, /* Bad ECC Curve Type */ + ECC_CURVE_ERROR = -251, /* Bad ECC Curve */ + ECC_PEERKEY_ERROR = -252, /* Bad Peer ECC Key */ + ECC_MAKEKEY_ERROR = -253, /* Bad Make ECC Key */ + ECC_EXPORT_ERROR = -254, /* Bad ECC Export Key */ + ECC_SHARED_ERROR = -255, /* Bad ECC Shared Secret */ + BAD_MUTEX_ERROR = -256, /* Bad mutex */ + NOT_CA_ERROR = -257, /* Not a CA cert error */ + BAD_PATH_ERROR = -258, /* Bad path for opendir */ + BAD_CERT_MANAGER_ERROR = -259, /* Bad Cert Manager */ + OCSP_CERT_REVOKED = -260, /* OCSP Certificate revoked */ + CRL_CERT_REVOKED = -261, /* CRL Certificate revoked */ + CRL_MISSING = -262, /* CRL Not loaded */ + MONITOR_RUNNING_E = -263, /* CRL Monitor already running */ + THREAD_CREATE_E = -264, /* Thread Create Error */ + OCSP_NEED_URL = -265, /* OCSP need an URL for lookup */ + OCSP_CERT_UNKNOWN = -266, /* OCSP responder doesn't know */ + OCSP_LOOKUP_FAIL = -267, /* OCSP lookup not successful */ + MAX_CHAIN_ERROR = -268, /* max chain depth exceeded */ + COOKIE_ERROR = -269, /* dtls cookie error */ + SEQUENCE_ERROR = -270, /* dtls sequence error */ + SUITES_ERROR = -271, /* suites pointer error */ + SSL_NO_PEM_HEADER = -272, /* no PEM header found */ + OUT_OF_ORDER_E = -273, /* out of order message */ + BAD_KEA_TYPE_E = -274, /* bad KEA type found */ + SANITY_CIPHER_E = -275, /* sanity check on cipher error */ + RECV_OVERFLOW_E = -276, /* RXCB returned more than rqed */ + GEN_COOKIE_E = -277, /* Generate Cookie Error */ + NO_PEER_VERIFY = -278, /* Need peer cert verify Error */ + FWRITE_ERROR = -279, /* fwrite problem */ + CACHE_MATCH_ERROR = -280, /* chache hdr match error */ + UNKNOWN_SNI_HOST_NAME_E = -281, /* Unrecognized host name Error */ /* add strings to SetErrorString !!!!! */ /* begin negotiation parameter errors */ - UNSUPPORTED_SUITE = -290, /* unsupported cipher suite */ - MATCH_SUITE_ERROR = -291 /* can't match cipher suite */ + UNSUPPORTED_SUITE = -290, /* unsupported cipher suite */ + MATCH_SUITE_ERROR = -291 /* can't match cipher suite */ /* end negotiation parameter errors only 10 for now */ /* add strings to SetErrorString !!!!! */ }; diff --git a/cyassl/internal.h b/cyassl/internal.h index d161e48d9..89f34b5a6 100644 --- a/cyassl/internal.h +++ b/cyassl/internal.h @@ -592,18 +592,20 @@ enum Misc { COOKIE_SZ = 20, /* use a 20 byte cookie */ SUITE_LEN = 2, /* cipher suite sz length */ ENUM_LEN = 1, /* always a byte */ + OPAQUE16_LEN = 2, /* always 2 bytes */ COMP_LEN = 1, /* compression length */ CURVE_LEN = 2, /* ecc named curve length */ SERVER_ID_LEN = 20, /* server session id length */ - HANDSHAKE_HEADER_SZ = 4, /* type + length(3) */ - RECORD_HEADER_SZ = 5, /* type + version + len(2) */ - CERT_HEADER_SZ = 3, /* always 3 bytes */ - REQ_HEADER_SZ = 2, /* cert request header sz */ - HINT_LEN_SZ = 2, /* length of hint size field */ - HELLO_EXT_SZ = 8, /* total length of the lazy hello extensions */ - HELLO_EXT_LEN = 6, /* length of the lazy hello extensions */ - HELLO_EXT_SIGALGO_SZ = 2, /* length of signature algo extension */ + HANDSHAKE_HEADER_SZ = 4, /* type + length(3) */ + RECORD_HEADER_SZ = 5, /* type + version + len(2) */ + CERT_HEADER_SZ = 3, /* always 3 bytes */ + REQ_HEADER_SZ = 2, /* cert request header sz */ + HINT_LEN_SZ = 2, /* length of hint size field */ + HELLO_EXT_TYPE_SZ = 2, /* length of a hello extension type */ + HELLO_EXT_SZ = 8, /* total length of the lazy hello extensions */ + HELLO_EXT_LEN = 6, /* length of the lazy hello extensions */ + HELLO_EXT_SIGALGO_SZ = 2, /* length of signature algo extension */ HELLO_EXT_SIGALGO_MAX = 32, /* number of items in the signature algo list */ DTLS_HANDSHAKE_HEADER_SZ = 12, /* normal + seq(2) + offset(3) + length(3) */ @@ -1110,6 +1112,61 @@ typedef struct CYASSL_DTLS_CTX { int fd; } CYASSL_DTLS_CTX; +/* RFC 6066 TLS Extensions */ +#ifdef HAVE_TLS_EXTENSIONS + +typedef enum { + SERVER_NAME_INDICATION = 0,/* + MAX_FRAGMENT_LENGTH = 1, + CLIENT_CERTIFICATE_URL = 2, + TRUSTED_CA_KEYS = 3, + TRUNCATED_HMAC = 4, + STATUS_REQUEST = 5, + SIGNATURE_ALGORITHMS = 13,*/ +} TLSX_Type; + +typedef struct TLSX { + TLSX_Type type; /* Extension Type */ + void* data; /* Extension Data */ + byte resp; /* IsResponse Flag */ + struct TLSX* next; /* List Behavior */ +} TLSX; + +CYASSL_LOCAL TLSX* TLSX_Find(TLSX* list, TLSX_Type type); +CYASSL_LOCAL void TLSX_FreeAll(TLSX* list); + +#ifndef NO_CYASSL_CLIENT +CYASSL_LOCAL word16 TLSX_GetRequestSize(CYASSL* ssl); +CYASSL_LOCAL word16 TLSX_WriteRequest(CYASSL* ssl, byte* output); +#endif + +#ifndef NO_CYASSL_SERVER +CYASSL_LOCAL word16 TLSX_GetResponseSize(CYASSL* ssl); +CYASSL_LOCAL word16 TLSX_WriteResponse(CYASSL* ssl, byte* output); +#endif + +CYASSL_LOCAL int TLSX_Parse(CYASSL* ssl, byte* input, word16 length, + byte isRequest, Suites *suites); + +/* Server Name Indication */ +#ifdef HAVE_SNI + +typedef enum { + HOST_NAME = 0 +} SNI_Type; + +typedef struct SNI { + SNI_Type type; /* SNI Type */ + union { char* host_name; } data; /* SNI Data */ + struct SNI* next; /* List Behavior */ +} SNI; + +CYASSL_LOCAL int TLSX_UseSNI(TLSX** extensions, byte type, const void* data, + word16 size); + +#endif /* HAVE_SNI */ + +#endif /* HAVE_TLS_EXTENSIONS */ /* CyaSSL context type */ struct CYASSL_CTX { @@ -1167,6 +1224,9 @@ struct CYASSL_CTX { #ifdef HAVE_CAVIUM int devId; /* cavium device id to use */ #endif +#ifdef HAVE_TLS_EXTENSIONS + TLSX* extensions; /* RFC 6066 TLS Extensions data */ +#endif }; @@ -1709,6 +1769,9 @@ struct CYASSL { #endif #ifdef HAVE_CAVIUM int devId; /* cavium device id to use */ +#endif +#ifdef HAVE_TLS_EXTENSIONS + TLSX* extensions; /* RFC 6066 TLS Extensions data */ #endif CYASSL_ALERT_HISTORY alert_history; }; @@ -1829,7 +1892,8 @@ enum AlertDescription { illegal_parameter = 47, decrypt_error = 51, protocol_version = 70, - no_renegotiation = 100 + no_renegotiation = 100, + unrecognized_name = 112 }; diff --git a/cyassl/ssl.h b/cyassl/ssl.h index 1db414ba2..338564950 100644 --- a/cyassl/ssl.h +++ b/cyassl/ssl.h @@ -929,6 +929,13 @@ CYASSL_API void CyaSSL_FreeArrays(CYASSL*); CYASSL_API int CyaSSL_UseCavium(CYASSL*, int devId); CYASSL_API int CyaSSL_CTX_UseCavium(CYASSL_CTX*, int devId); +/* tls extensions */ +#ifdef HAVE_SNI +CYASSL_API int CyaSSL_UseSNI(CYASSL* ssl, unsigned char type, const void* data, + unsigned short size); +CYASSL_API int CyaSSL_CTX_UseSNI(CYASSL_CTX* ctx, unsigned char type, + const void* data, unsigned short size); +#endif #define CYASSL_CRL_MONITOR 0x01 /* monitor this dir flag */ #define CYASSL_CRL_START_MON 0x02 /* start monitoring flag */ diff --git a/examples/client/client.c b/examples/client/client.c index 1183f78b5..df01b8645 100644 --- a/examples/client/client.c +++ b/examples/client/client.c @@ -32,6 +32,8 @@ #include +#include + #if !defined(CYASSL_TRACK_MEMORY) && !defined(NO_MAIN_DRIVER) /* in case memory tracker wants stats */ #define CYASSL_TRACK_MEMORY @@ -130,6 +132,7 @@ static void Usage(void) #ifdef SHOW_SIZES printf("-z Print structure sizes\n"); #endif + printf("-S Use Host Name Indication\n"); } @@ -178,6 +181,10 @@ THREAD_RETURN CYASSL_THREAD client_test(void* args) char* ourCert = (char*)cliCert; char* ourKey = (char*)cliKey; +#ifdef HAVE_SNI + char* sniHostName = NULL; +#endif + int argc = ((func_args*)args)->argc; char** argv = ((func_args*)args)->argv; @@ -193,7 +200,7 @@ THREAD_RETURN CYASSL_THREAD client_test(void* args) (void)sslResume; (void)trackMemory; - while ((ch = mygetopt(argc, argv, "?gdusmNrtfxh:p:v:l:A:c:k:b:z")) != -1) { + while ((ch = mygetopt(argc, argv, "?gdusmNrtfxh:p:v:l:A:c:k:b:zS:")) != -1){ switch (ch) { case '?' : Usage(); @@ -292,6 +299,12 @@ THREAD_RETURN CYASSL_THREAD client_test(void* args) #endif break; + case 'S' : + #ifdef HAVE_SNI + sniHostName = myoptarg; + #endif + break; + default: Usage(); exit(MY_EX_USAGE); @@ -358,6 +371,7 @@ THREAD_RETURN CYASSL_THREAD client_test(void* args) default: err_sys("Bad SSL version"); + break; } if (method == NULL) @@ -445,6 +459,12 @@ THREAD_RETURN CYASSL_THREAD client_test(void* args) CyaSSL_CTX_UseCavium(ctx, CAVIUM_DEV_ID); #endif +#ifdef HAVE_SNI + if (sniHostName) + if (CyaSSL_CTX_UseSNI(ctx, 0, sniHostName, XSTRLEN(sniHostName))) + err_sys("UseSNI failed"); +#endif + if (benchmark) { /* time passed in number of connects give average */ int times = benchmark; diff --git a/examples/server/server.c b/examples/server/server.c index 002467cc9..da6fccf45 100644 --- a/examples/server/server.c +++ b/examples/server/server.c @@ -122,6 +122,7 @@ static void Usage(void) " add -v 2 for DTLSv1 (default), -v 3 for DTLSv1.2\n"); printf("-f Fewer packets/group messages\n"); printf("-N Use Non-blocking sockets\n"); + printf("-S Use Host Name Indication\n"); } #ifdef CYASSL_MDK_SHELL @@ -159,6 +160,10 @@ THREAD_RETURN CYASSL_THREAD server_test(void* args) int argc = ((func_args*)args)->argc; char** argv = ((func_args*)args)->argv; +#ifdef HAVE_SNI + char* sniHostName = NULL; +#endif + ((func_args*)args)->return_code = -1; /* error state */ #ifdef NO_RSA @@ -168,7 +173,7 @@ THREAD_RETURN CYASSL_THREAD server_test(void* args) #endif (void)trackMemory; - while ((ch = mygetopt(argc, argv, "?dbstnNufp:v:l:A:c:k:")) != -1) { + while ((ch = mygetopt(argc, argv, "?dbstnNufp:v:l:A:c:k:S:")) != -1) { switch (ch) { case '?' : Usage(); @@ -240,6 +245,12 @@ THREAD_RETURN CYASSL_THREAD server_test(void* args) nonBlocking = 1; break; + case 'S' : + #ifdef HAVE_SNI + sniHostName = myoptarg; + #endif + break; + default: Usage(); exit(MY_EX_USAGE); @@ -396,6 +407,12 @@ THREAD_RETURN CYASSL_THREAD server_test(void* args) } #endif +#ifdef HAVE_SNI + if (sniHostName) + if (CyaSSL_CTX_UseSNI(ctx, 0, sniHostName, XSTRLEN(sniHostName))) + err_sys("UseSNI failed"); +#endif + ssl = SSL_new(ctx); if (ssl == NULL) err_sys("unable to get SSL"); diff --git a/src/internal.c b/src/internal.c index 92e7d4f78..2dc99aab9 100644 --- a/src/internal.c +++ b/src/internal.c @@ -421,6 +421,9 @@ int InitSSL_Ctx(CYASSL_CTX* ctx, CYASSL_METHOD* method) #ifdef HAVE_CAVIUM ctx->devId = NO_CAVIUM_DEVICE; #endif +#ifdef HAVE_TLS_EXTENSIONS + ctx->extensions = NULL; +#endif if (InitMutex(&ctx->countMutex) < 0) { CYASSL_MSG("Mutex error on CTX init"); @@ -452,6 +455,9 @@ void SSL_CtxResourceFree(CYASSL_CTX* ctx) #ifdef HAVE_OCSP CyaSSL_OCSP_Cleanup(&ctx->ocsp); #endif +#ifdef HAVE_TLS_EXTENSIONS + TLSX_FreeAll(ctx->extensions); +#endif } @@ -1436,6 +1442,10 @@ int InitSSL(CYASSL* ssl, CYASSL_CTX* ctx) ssl->devId = ctx->devId; #endif +#ifdef HAVE_TLS_EXTENSIONS + ssl->extensions = NULL; +#endif + ssl->rng = NULL; ssl->arrays = NULL; @@ -1655,6 +1665,9 @@ void SSL_ResourceFree(CYASSL* ssl) XFREE(ssl->eccDsaKey, ssl->heap, DYNAMIC_TYPE_ECC); } #endif +#ifdef HAVE_TLS_EXTENSIONS + TLSX_FreeAll(ssl->extensions); +#endif } @@ -5802,6 +5815,10 @@ void SetErrorString(int error, char* str) XSTRNCPY(str, "Cache restore header match Error", max); break; + case UNKNOWN_SNI_HOST_NAME_E: + XSTRNCPY(str, "Unrecognized host name Error", max); + break; + default : XSTRNCPY(str, "unknown error number", max); } @@ -6693,9 +6710,13 @@ int SetCipherList(Suites* s, const char* list) + ssl->suites->suiteSz + SUITE_LEN + COMP_LEN + ENUM_LEN; +#ifdef HAVE_TLS_EXTENSIONS + length += TLSX_GetRequestSize(ssl); +#else if (IsAtLeastTLSv1_2(ssl) && ssl->suites->hashSigAlgoSz) { length += ssl->suites->hashSigAlgoSz + HELLO_EXT_SZ; } +#endif sendSz = length + HANDSHAKE_HEADER_SZ + RECORD_HEADER_SZ; #ifdef CYASSL_DTLS @@ -6768,6 +6789,11 @@ int SetCipherList(Suites* s, const char* list) else output[idx++] = NO_COMPRESSION; +#ifdef HAVE_TLS_EXTENSIONS + idx += TLSX_WriteRequest(ssl, output + idx); + + (void)idx; /* suppress analyzer warning, keep idx current */ +#else if (IsAtLeastTLSv1_2(ssl) && ssl->suites->hashSigAlgoSz) { int i; @@ -6785,6 +6811,7 @@ int SetCipherList(Suites* s, const char* list) output[idx] = ssl->suites->hashSigAlgo[i]; } } +#endif #ifdef CYASSL_DTLS if (ssl->options.dtls) { @@ -6906,8 +6933,29 @@ int SetCipherList(Suites* s, const char* list) } *inOutIdx = i; - if ( (i - begin) < helloSz) - *inOutIdx = begin + helloSz; /* skip extensions */ + if ( (i - begin) < helloSz) { +#ifdef HAVE_TLS_EXTENSIONS + if (IsTLS(ssl)) { + int ret = 0; + word16 totalExtSz; + Suites clSuites; /* just for compatibility right now */ + + ato16(&input[i], &totalExtSz); + i += LENGTH_SZ; + if (totalExtSz > helloSz + begin - i) + return INCOMPLETE_DATA; + + if ((ret = TLSX_Parse(ssl, (byte *) input + i, + totalExtSz, 0, &clSuites))) + return ret; + + i += totalExtSz; + *inOutIdx = i; + } + else +#endif + *inOutIdx = begin + helloSz; /* skip extensions */ + } ssl->options.serverState = SERVER_HELLO_COMPLETE; @@ -7779,7 +7827,11 @@ int SetCipherList(Suites* s, const char* list) + SUITE_LEN + ENUM_LEN; - /* check for available size */ +#ifdef HAVE_TLS_EXTENSIONS + length += TLSX_GetResponseSize(ssl); +#endif + + /* check for avalaible size */ if ((ret = CheckAvailableSize(ssl, MAX_HELLO_SZ)) != 0) return ret; @@ -7827,11 +7879,17 @@ int SetCipherList(Suites* s, const char* list) output[idx++] = ssl->options.cipherSuite0; output[idx++] = ssl->options.cipherSuite; - /* last, compression */ + /* then compression */ if (ssl->options.usingCompression) output[idx++] = ZLIB_COMPRESSION; else output[idx++] = NO_COMPRESSION; + + /* last, extensions */ +#ifdef HAVE_TLS_EXTENSIONS + if (IsTLS(ssl)) + TLSX_WriteResponse(ssl, output + idx); +#endif ssl->buffers.outputBuffer.length += sendSz; #ifdef CYASSL_DTLS @@ -9324,11 +9382,14 @@ int SetCipherList(Suites* s, const char* list) else i += b; /* ignore, since we're not on */ - ssl->options.clientState = CLIENT_HELLO_COMPLETE; - *inOutIdx = i; if ( (i - begin) < helloSz) { +#ifdef HAVE_TLS_EXTENSIONS + if (IsTLS(ssl)) { + int ret = 0; +#else if (IsAtLeastTLSv1_2(ssl)) { +#endif /* Process the hello extension. Skip unsupported. */ word16 totalExtSz; @@ -9336,6 +9397,14 @@ int SetCipherList(Suites* s, const char* list) i += LENGTH_SZ; if (totalExtSz > helloSz + begin - i) return INCOMPLETE_DATA; + +#ifdef HAVE_TLS_EXTENSIONS + if ((ret = TLSX_Parse(ssl, (byte *) input + i, + totalExtSz, 1, &clSuites))) + return ret; + + i += totalExtSz; +#else while (totalExtSz) { word16 extId, extSz; @@ -9361,11 +9430,14 @@ int SetCipherList(Suites* s, const char* list) totalExtSz -= LENGTH_SZ + EXT_ID_SZ + extSz; } +#endif *inOutIdx = i; } else *inOutIdx = begin + helloSz; /* skip extensions */ } + + ssl->options.clientState = CLIENT_HELLO_COMPLETE; ssl->options.haveSessionId = 1; /* ProcessOld uses same resume code */ diff --git a/src/ssl.c b/src/ssl.c index 88b59bf61..aaf384066 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -509,6 +509,27 @@ int CyaSSL_CTX_UseCavium(CYASSL_CTX* ctx, int devId) #endif /* HAVE_CAVIUM */ +#ifdef HAVE_SNI + +int CyaSSL_UseSNI(CYASSL* ssl, unsigned char type, const void* data, + unsigned short size) +{ + if (ssl == NULL) + return BAD_FUNC_ARG; + + return TLSX_UseSNI(&ssl->extensions, type, data, size); +} + +int CyaSSL_CTX_UseSNI(CYASSL_CTX* ctx, unsigned char type, const void* data, + unsigned short size) +{ + if (ctx == NULL) + return BAD_FUNC_ARG; + + return TLSX_UseSNI(&ctx->extensions, type, data, size); +} + +#endif /* HAVE_SNI */ #ifndef CYASSL_LEANPSK int CyaSSL_send(CYASSL* ssl, const void* data, int sz, int flags) diff --git a/src/tls.c b/src/tls.c index 0e8021efd..64f17a818 100644 --- a/src/tls.c +++ b/src/tls.c @@ -370,6 +370,12 @@ static INLINE void c16toa(word16 u16, byte* c) c[1] = u16 & 0xff; } +/* convert opaque to 16 bit integer */ +static INLINE void ato16(const byte* c, word16* u16) +{ + *u16 = (c[0] << 8) | (c[1]); +} + /* convert 32 bit integer to opaque */ static INLINE void c32toa(word32 u32, byte* c) @@ -484,6 +490,580 @@ void TLS_hmac(CYASSL* ssl, byte* digest, const byte* in, word32 sz, HmacFinal(&hmac, digest); } +#ifdef HAVE_TLS_EXTENSIONS + +static int TLSX_Append(TLSX** list, TLSX_Type type) +{ + TLSX* extension; + + if (list == NULL) /* won't check type since this function is static */ + return BAD_FUNC_ARG; + + if ((extension = XMALLOC(sizeof(TLSX), 0, DYNAMIC_TYPE_TLSX)) == NULL) + return MEMORY_E; + + extension->type = type; + extension->data = NULL; + extension->resp = 0; + extension->next = *list; + *list = extension; + + return 0; +} + +#ifndef NO_CYASSL_SERVER + +static void TLSX_SetResponse(CYASSL* ssl, TLSX_Type type) +{ + TLSX *ext = TLSX_Find(ssl->extensions, type); + + if (ext) + ext->resp = 1; +} + +#endif + +/* SNI - Server Name Indication */ + +#ifdef HAVE_SNI + +static void TLSX_SNI_Free(SNI* sni) +{ + if (sni) { + switch (sni->type) { + case HOST_NAME: + XFREE(sni->data.host_name, 0, DYNAMIC_TYPE_TLSX); + break; + } + + XFREE(sni, 0, DYNAMIC_TYPE_TLSX); + } +} + +static void TLSX_SNI_FreeAll(SNI* list) +{ + SNI* sni; + + while ((sni = list)) { + list = sni->next; + TLSX_SNI_Free(sni); + } +} + +static int TLSX_SNI_Append(SNI** list, SNI_Type type, const void* data, + word16 size) +{ + SNI* sni; + + if (list == NULL) + return BAD_FUNC_ARG; + + if ((sni = XMALLOC(sizeof(SNI), 0, DYNAMIC_TYPE_TLSX)) == NULL) + return MEMORY_E; + + switch (type) { + case HOST_NAME: { + sni->data.host_name = XMALLOC(size + 1, 0, DYNAMIC_TYPE_TLSX); + + if (sni->data.host_name) { + XSTRNCPY(sni->data.host_name, (const char*) data, size); + sni->data.host_name[size] = 0; + } else { + XFREE(sni, 0, DYNAMIC_TYPE_TLSX); + return MEMORY_E; + } + } + break; + + default: /* invalid type */ + XFREE(sni, 0, DYNAMIC_TYPE_TLSX); + return BAD_FUNC_ARG; + break; + } + + sni->type = type; + sni->next = *list; + *list = sni; + + return 0; +} + +static word16 TLSX_SNI_GetSize(SNI* list) +{ + SNI* sni; + word16 length = OPAQUE16_LEN; /* list length */ + + while ((sni = list)) { + list = sni->next; + + length += ENUM_LEN + OPAQUE16_LEN; /* sni type + sni length */ + + switch (sni->type) { + case HOST_NAME: + length += XSTRLEN((char*) sni->data.host_name); + break; + } + } + + return length; +} + +static word16 TLSX_SNI_Write(SNI* list, byte* output) +{ + SNI* sni; + word16 length = 0; + word16 offset = OPAQUE16_LEN; /* list length offset */ + + while ((sni = list)) { + list = sni->next; + + output[offset++] = sni->type; /* sni type */ + + switch (sni->type) { + case HOST_NAME: + length = XSTRLEN((char*) sni->data.host_name); + + c16toa(length, output + offset); /* sni length */ + offset += OPAQUE16_LEN; + + XMEMCPY(output + offset, sni->data.host_name, length); + + offset += length; + break; + } + } + + c16toa(offset - OPAQUE16_LEN, output); /* writing list length */ + + return offset; +} + +static SNI* TLSX_SNI_Find(SNI *list, SNI_Type type) +{ + SNI *sni = list; + + while (sni && sni->type != type) + sni = sni->next; + + return sni; +} + +static int TLSX_SNI_Parse(CYASSL* ssl, byte* input, word16 length, + byte isRequest) +{ +#ifndef NO_CYASSL_SERVER + word16 size = 0; + word16 offset = 0; +#endif + + TLSX *extension = TLSX_Find(ssl->extensions, SERVER_NAME_INDICATION); + + if (!extension) + extension = TLSX_Find(ssl->ctx->extensions, SERVER_NAME_INDICATION); + + if (!extension || !extension->data) { + if (!isRequest) { + CYASSL_MSG("Unexpected SNI response from server"); + } + + return 0; /* not using SNI */ + } + + if (!isRequest) { + if (length) { + CYASSL_MSG("SNI response should be empty!"); + } + + return 0; /* nothing to do */ + } + +#ifndef NO_CYASSL_SERVER + + if (OPAQUE16_LEN > length) + return INCOMPLETE_DATA; + + ato16(input, &size); + offset += OPAQUE16_LEN; + + /* validating sni list length */ + if (length != OPAQUE16_LEN + size) + return INCOMPLETE_DATA; + + for (size = 0; offset < length; offset += size) { + SNI *sni; + SNI_Type type = input[offset++]; + + if (offset + OPAQUE16_LEN > length) + return INCOMPLETE_DATA; + + ato16(input + offset, &size); + offset += OPAQUE16_LEN; + + if (offset + size > length) + return INCOMPLETE_DATA; + + if (!(sni = TLSX_SNI_Find((SNI *) extension->data, type))) { + continue; /* not using this SNI type */ + } + + switch(type) { + case HOST_NAME: + if (XSTRNCMP(sni->data.host_name, + (const char *) input + offset, size)) { + SendAlert(ssl, alert_fatal, unrecognized_name); + + return UNKNOWN_SNI_HOST_NAME_E; + } else { + int r = TLSX_UseSNI(&ssl->extensions, type, (byte *) "", 0); + + if (r) return r; /* throw error */ + } + break; + } + + TLSX_SetResponse(ssl, SERVER_NAME_INDICATION); + } + +#endif + + return 0; +} + +int TLSX_UseSNI(TLSX** extensions, byte type, const void* data, word16 size) +{ + TLSX* extension = NULL; + SNI* sni = NULL; + int ret = 0; + + if (extensions == NULL || data == NULL) + return BAD_FUNC_ARG; + + if ((ret = TLSX_SNI_Append(&sni, type, data, size)) != 0) + return ret; + + extension = *extensions; + + /* find SNI extension if it already exists. */ + while (extension && extension->type != SERVER_NAME_INDICATION) + extension = extension->next; + + /* push new SNI extension if it doesn't exists. */ + if (!extension) { + if ((ret = TLSX_Append(extensions, SERVER_NAME_INDICATION)) != 0) { + TLSX_SNI_Free(sni); + return ret; + } + + extension = *extensions; + } + + /* push new SNI object to extension data. */ + sni->next = (SNI*) extension->data; + extension->data = (void*) sni; + + /* look for another server name of the same type to remove (replacement) */ + while ((sni = sni->next)) { + if (sni->next && sni->next->type == type) { + SNI *next = sni->next; + + sni->next = next->next; + TLSX_SNI_Free(next); + + break; + } + } + + return 0; +} + +#define SNI_FREE_ALL TLSX_SNI_FreeAll +#define SNI_GET_SIZE TLSX_SNI_GetSize +#define SNI_WRITE TLSX_SNI_Write +#define SNI_PARSE TLSX_SNI_Parse + +#else + +#define SNI_FREE_ALL(x) +#define SNI_GET_SIZE(x) 0 +#define SNI_WRITE(x) 0 +#define SNI_PARSE(x) 0 + +#endif /* HAVE_SNI */ + +TLSX* TLSX_Find(TLSX* list, TLSX_Type type) +{ + TLSX* extension = list; + + while (extension && extension->type != type) + extension = extension->next; + + return extension; +} + +void TLSX_FreeAll(TLSX* list) +{ + TLSX* extension; + + while ((extension = list)) { + list = extension->next; + + switch (extension->type) { + case SERVER_NAME_INDICATION: + SNI_FREE_ALL((SNI *) extension->data); + break; + } + + XFREE(extension, 0, DYNAMIC_TYPE_TLSX); + } +} + +#define IS_OFF(cemaphor, light) \ + ((cemaphor)[(light) / 8] ^ (0x01 >> ((light) % 8))) + +#define TURN_ON(cemaphor, light) \ + ((cemaphor)[(light) / 8] |= (0x01 >> ((light) % 8))) + +static word16 TLSX_GetSize(TLSX* list, byte* cemaphor, byte isRequest) +{ + TLSX* extension; + word16 length = 0; + + while ((extension = list)) { + list = extension->next; + + if (!isRequest && !extension->resp) + continue; /* skip! */ + + if (IS_OFF(cemaphor, extension->type)) { + /* type + data length */ + length += HELLO_EXT_TYPE_SZ + OPAQUE16_LEN; + + switch (extension->type) { + case SERVER_NAME_INDICATION: + if (isRequest) + length += SNI_GET_SIZE((SNI *) extension->data); + break; + } + + TURN_ON(cemaphor, extension->type); + } + } + + return length; +} + +static word16 TLSX_Write(TLSX* list, byte* output, byte* cemaphor, + byte isRequest) +{ + TLSX* extension; + word16 offset = 0; + word16 length_offset = 0; + + while ((extension = list)) { + list = extension->next; + + if (!isRequest && !extension->resp) + continue; /* skip! */ + + if (IS_OFF(cemaphor, extension->type)) { + /* extension type */ + c16toa(extension->type, output + offset); + offset += HELLO_EXT_TYPE_SZ + OPAQUE16_LEN; + length_offset = offset; + + /* extension data should be written internally */ + switch (extension->type) { + case SERVER_NAME_INDICATION: + if (isRequest) + offset += SNI_WRITE((SNI *) extension->data, + output + offset); + break; + } + + /* writing extension data length */ + c16toa(offset - length_offset, + output + length_offset - OPAQUE16_LEN); + + TURN_ON(cemaphor, extension->type); + } + } + + return offset; +} + +#ifndef NO_CYASSL_CLIENT + +word16 TLSX_GetRequestSize(CYASSL* ssl) +{ + word16 length = 0; + + if (ssl && IsTLS(ssl)) { + byte cemaphor[16] = {0}; + + if (ssl->extensions) + length += TLSX_GetSize(ssl->extensions, cemaphor, 1); + + if (ssl->ctx && ssl->ctx->extensions) + length += TLSX_GetSize(ssl->ctx->extensions, cemaphor, 1); + + if (IsAtLeastTLSv1_2(ssl) && ssl->suites->hashSigAlgoSz) + length += ssl->suites->hashSigAlgoSz + HELLO_EXT_LEN; + } + + if (length) + length += OPAQUE16_LEN; /* for total length storage */ + + return length; +} + +word16 TLSX_WriteRequest(CYASSL* ssl, byte* output) +{ + word16 offset = 0; + + if (ssl && IsTLS(ssl) && output) { + byte cemaphor[16] = {0}; + + offset += OPAQUE16_LEN; /* extensions length */ + + if (ssl->extensions) + offset += TLSX_Write(ssl->extensions, output + offset, + cemaphor, 1); + + if (ssl->ctx && ssl->ctx->extensions) + offset += TLSX_Write(ssl->ctx->extensions, output + offset, + cemaphor, 1); + + if (IsAtLeastTLSv1_2(ssl) && ssl->suites->hashSigAlgoSz) + { + int i; + /* extension type */ + c16toa(HELLO_EXT_SIG_ALGO, output + offset); + offset += HELLO_EXT_TYPE_SZ; + + /* extension data length */ + c16toa(OPAQUE16_LEN + ssl->suites->hashSigAlgoSz, output + offset); + offset += OPAQUE16_LEN; + + /* sig algos length */ + c16toa(ssl->suites->hashSigAlgoSz, output + offset); + offset += OPAQUE16_LEN; + + /* sig algos */ + for (i = 0; i < ssl->suites->hashSigAlgoSz; i++, offset++) + output[offset] = ssl->suites->hashSigAlgo[i]; + } + + if (offset > OPAQUE16_LEN) + c16toa(offset - OPAQUE16_LEN, output); /* extensions length */ + } + + return offset; +} + +#endif /* NO_CYASSL_CLIENT */ + +#ifndef NO_CYASSL_SERVER + +word16 TLSX_GetResponseSize(CYASSL* ssl) +{ + word16 length = 0; + byte cemaphor[16] = {0}; + + if (ssl && IsTLS(ssl)) + length += TLSX_GetSize(ssl->extensions, cemaphor, 0); + + /* All the response data is set at the ssl object only, so no ctx here. */ + + if (length) + length += OPAQUE16_LEN; /* for total length storage */ + + return length; +} + +word16 TLSX_WriteResponse(CYASSL *ssl, byte* output) +{ + word16 offset = 0; + + if (ssl && IsTLS(ssl) && output) { + byte cemaphor[16] = {0}; + + offset += OPAQUE16_LEN; /* extensions length */ + + offset += TLSX_Write(ssl->extensions, output + offset, cemaphor, 0); + + if (offset > OPAQUE16_LEN) + c16toa(offset - OPAQUE16_LEN, output); /* extensions length */ + } + + return offset; +} + +#endif /* NO_CYASSL_SERVER */ + +int TLSX_Parse(CYASSL* ssl, byte* input, word16 length, byte isRequest, + Suites *suites) +{ + int ret = 0; + word16 offset = 0; + + if (!ssl || !input || !suites) + return BAD_FUNC_ARG; + + while (ret == 0 && offset < length) { + word16 type; + word16 size; + + if (length - offset < HELLO_EXT_TYPE_SZ + OPAQUE16_LEN) + return INCOMPLETE_DATA; + + ato16(input + offset, &type); + offset += HELLO_EXT_TYPE_SZ; + + ato16(input + offset, &size); + offset += OPAQUE16_LEN; + + if (offset + size > length) + return INCOMPLETE_DATA; + + switch (type) { + case SERVER_NAME_INDICATION: + ret = SNI_PARSE(ssl, input + offset, size, isRequest); + break; + + case HELLO_EXT_SIG_ALGO: + if (isRequest) { + /* do not mess with offset inside the switch! */ + if (IsAtLeastTLSv1_2(ssl)) { + ato16(input + offset, &suites->hashSigAlgoSz); + + if (suites->hashSigAlgoSz > size - OPAQUE16_LEN) + return INCOMPLETE_DATA; + + XMEMCPY(suites->hashSigAlgo, + input + offset + OPAQUE16_LEN, + min(suites->hashSigAlgoSz, + HELLO_EXT_SIGALGO_MAX)); + } + } else { + CYASSL_MSG("Servers MUST NOT send SIG ALGO extension."); + } + + break; + } + + /* offset should be updated here! */ + offset += size; + } + + return ret; +} + +/* undefining cemaphor macros */ +#undef IS_OFF +#undef TURN_ON + +#endif /* HAVE_TLS_EXTENSIONS */ + #ifndef NO_CYASSL_CLIENT diff --git a/tests/api.c b/tests/api.c index 3dbb22801..fe17fa562 100644 --- a/tests/api.c +++ b/tests/api.c @@ -47,6 +47,11 @@ static int test_client_CyaSSL_new(void); static int test_CyaSSL_read_write(void); #endif /* NO_RSA */ #endif /* NO_FILESYSTEM */ +#ifdef HAVE_TLS_EXTENSIONS +#ifdef HAVE_SNI +static void test_CyaSSL_UseSNI(void); +#endif /* HAVE_TLS_EXTENSIONS */ +#endif /* HAVE_SNI */ /* test function helpers */ static int test_method(CYASSL_METHOD *method, const char *name); @@ -91,6 +96,11 @@ int ApiTest(void) test_CyaSSL_read_write(); #endif /* NO_RSA */ #endif /* NO_FILESYSTEM */ +#ifdef HAVE_TLS_EXTENSIONS +#ifdef HAVE_SNI + test_CyaSSL_UseSNI(); +#endif /* HAVE_SNI */ +#endif /* HAVE_TLS_EXTENSIONS */ test_CyaSSL_Cleanup(); printf(" End API Tests\n"); @@ -211,6 +221,34 @@ int test_CyaSSL_CTX_new(CYASSL_METHOD *method) return TEST_SUCCESS; } +#ifdef HAVE_TLS_EXTENSIONS +#ifdef HAVE_SNI +void test_CyaSSL_UseSNI(void) +{ + CYASSL_CTX *ctx = CyaSSL_CTX_new(CyaSSLv23_client_method()); + CYASSL *ssl = CyaSSL_new(ctx); + + AssertNotNull(ctx); + AssertNotNull(ssl); + + /* error cases */ + AssertIntNE(0, CyaSSL_CTX_UseSNI(NULL, 0, (void *) "ctx", XSTRLEN("ctx"))); + AssertIntNE(0, CyaSSL_UseSNI( NULL, 0, (void *) "ssl", XSTRLEN("ssl"))); + AssertIntNE(0, CyaSSL_CTX_UseSNI(ctx, -1, (void *) "ctx", XSTRLEN("ctx"))); + AssertIntNE(0, CyaSSL_UseSNI( ssl, -1, (void *) "ssl", XSTRLEN("ssl"))); + AssertIntNE(0, CyaSSL_CTX_UseSNI(ctx, 0, (void *) NULL, XSTRLEN("ctx"))); + AssertIntNE(0, CyaSSL_UseSNI( ssl, 0, (void *) NULL, XSTRLEN("ssl"))); + + /* success case */ + AssertIntEQ(0, CyaSSL_CTX_UseSNI(ctx, 0, (void *) "ctx", XSTRLEN("ctx"))); + AssertIntEQ(0, CyaSSL_UseSNI( ssl, 0, (void *) "ssl", XSTRLEN("ssl"))); + + CyaSSL_free(ssl); + CyaSSL_CTX_free(ctx); +} +#endif /* HAVE_SNI */ +#endif /* HAVE_TLS_EXTENSIONS */ + #if !defined(NO_FILESYSTEM) && !defined(NO_CERTS) /* Helper for testing CyaSSL_CTX_use_certificate_file() */ int test_ucf(CYASSL_CTX *ctx, const char* file, int type, int cond, diff --git a/tests/unit.h b/tests/unit.h index 097069bd2..f20f52ff7 100644 --- a/tests/unit.h +++ b/tests/unit.h @@ -5,6 +5,56 @@ #include /* thread and tcp stuff */ +#define Fail(description, result) do { \ + printf("\nERROR - %s line %d failed with:", __FILE__, __LINE__); \ + printf("\n\n test: "); printf description; \ + printf("\n\n result: "); printf result; \ + abort(); \ +} while(0) + +#define Assert(test, description, result) if (!(test)) Fail(description, result) + +#define AssertTrue(x) Assert( (x), ("%s is true", #x), (#x " => FALSE")) +#define AssertFalse(x) Assert(!(x), ("%s is false", #x), (#x " => TRUE")) +#define AssertNotNull(x) Assert( (x), ("%s is not null", #x), (#x " => NULL")) + +#define AssertNull(x) do { \ + void* _x = (void *) (x); \ + \ + Assert(!_x, ("%s is null", #x), (#x " => %p", _x)); \ +} while(0) + +#define AssertInt(x, y, op, er) do { \ + int _x = x; \ + int _y = y; \ + \ + Assert(_x op _y, ("%s " #op " %s", #x, #y), ("%d " #er " %d", _x, _y)); \ +} while(0) + +#define AssertIntEQ(x, y) AssertInt(x, y, ==, !=) +#define AssertIntNE(x, y) AssertInt(x, y, !=, ==) +#define AssertIntGT(x, y) AssertInt(x, y, >, <=) +#define AssertIntLT(x, y) AssertInt(x, y, <, >=) +#define AssertIntGE(x, y) AssertInt(x, y, >=, <) +#define AssertIntLE(x, y) AssertInt(x, y, <=, >) + +#define AssertStr(x, y, op, er) do { \ + const char* _x = x; \ + const char* _y = y; \ + int _z = strcmp(_x, _y); \ + \ + Assert(_z op 0, ("%s " #op " %s", #x, #y), \ + ("\"%s\" " #er " \"%s\"", _x, _y));\ +} while(0) + +#define AssertStrEQ(x, y) AssertStr(x, y, ==, !=) +#define AssertStrNE(x, y) AssertStr(x, y, !=, ==) +#define AssertStrGT(x, y) AssertStr(x, y, >, <=) +#define AssertStrLT(x, y) AssertStr(x, y, <, >=) +#define AssertStrGE(x, y) AssertStr(x, y, >=, <) +#define AssertStrLE(x, y) AssertStr(x, y, <=, >) + + int ApiTest(void); int SuiteTest(void); int HashTest(void);