From 8bd6a1e727764988e0b5993290a5303c3e17d17c Mon Sep 17 00:00:00 2001 From: Sean Parkinson Date: Fri, 23 Jun 2017 16:26:54 +1000 Subject: [PATCH 1/3] Add TLS v1.3 Cookie extension support Experimental stateless cookie --- examples/server/server.c | 20 +- scripts/tls13.test | 16 ++ src/internal.c | 11 +- src/tls.c | 404 ++++++++++++++++++++++++++++++------- src/tls13.c | 422 +++++++++++++++++++++++++++++++++------ wolfssl/error-ssl.h | 3 +- wolfssl/internal.h | 33 ++- wolfssl/ssl.h | 2 + 8 files changed, 764 insertions(+), 147 deletions(-) diff --git a/examples/server/server.c b/examples/server/server.c index 475be9263..99827cde5 100644 --- a/examples/server/server.c +++ b/examples/server/server.c @@ -474,6 +474,9 @@ THREAD_RETURN CYASSL_THREAD server_test(void* args) #ifdef WOLFSSL_EARLY_DATA int earlyData = 0; #endif +#ifdef WOLFSSL_HRR_COOKIE + int hrrCookie = 0; +#endif #ifdef WOLFSSL_STATIC_MEMORY #if (defined(HAVE_ECC) && !defined(ALT_ECC_SIZE)) \ @@ -518,10 +521,11 @@ THREAD_RETURN CYASSL_THREAD server_test(void* args) #ifdef WOLFSSL_VXWORKS useAnyAddr = 1; #else - /* Not Used: h, m, t, y, z, F, J, M, T, V, W, X, Y */ + /* Not Used: h, m, t, y, z, F, M, T, V, W, X, Y */ while ((ch = mygetopt(argc, argv, "?" "abc:defgijk:l:nop:q:rsuv:wx" - "A:B:C:D:E:GHIKL:NO:PQR:S:UYZ:""0")) != -1) { + "A:B:C:D:E:GHIJKL:NO:PQR:S:UYZ:" + "0")) != -1) { switch (ch) { case '?' : Usage(); @@ -746,6 +750,12 @@ THREAD_RETURN CYASSL_THREAD server_test(void* args) #endif break; + case 'J' : + #ifdef WOLFSSL_HRR_COOKIE + hrrCookie = 1; + #endif + break; + case '0' : #ifdef WOLFSSL_EARLY_DATA earlyData = 1; @@ -1083,6 +1093,12 @@ THREAD_RETURN CYASSL_THREAD server_test(void* args) wolfSSL_KeepArrays(ssl); #endif +#ifdef WOLFSSL_HRR_COOKIE + if (hrrCookie && wolfSSL_send_hrr_cookie(ssl, NULL, 0) != SSL_SUCCESS) { + err_sys("unable to set use of cookie with HRR msg"); + } +#endif + #if defined(WOLFSSL_STATIC_MEMORY) && defined(DEBUG_WOLFSSL) { WOLFSSL_MEM_STATS mem_stats; diff --git a/scripts/tls13.test b/scripts/tls13.test index 95c7044ca..0632c4f8e 100755 --- a/scripts/tls13.test +++ b/scripts/tls13.test @@ -95,6 +95,22 @@ if [ $RESULT -ne 0 ]; then fi echo "" +# Use HelloRetryRequest with TLS v1.3 server / TLS v1.3 client using cookie +echo -e "\n\nTLS v1.3 HelloRetryRequest with cookie" +port=0 +./examples/server/server -v 4 -J -R $ready_file -p $port & +server_pid=$! +create_port +./examples/client/client -v 4 -J -p $port +RESULT=$? +remove_ready_file +if [ $RESULT -ne 0 ]; then + echo -e "\n\nTLS v1.3 HelloRetryRequest with cookie not working" + do_cleanup + exit 1 +fi +echo "" + # Use HelloRetryRequest with TLS v1.3 server / TLS v1.3 client - SHA384. echo -e "\n\nTLS v1.3 HelloRetryRequest - SHA384" port=0 diff --git a/src/internal.c b/src/internal.c index 64626f0eb..8078144f5 100755 --- a/src/internal.c +++ b/src/internal.c @@ -4550,6 +4550,10 @@ void SSL_ResourceFree(WOLFSSL* ssl) ShrinkInputBuffer(ssl, FORCED_FREE); if (ssl->buffers.outputBuffer.dynamicFlag) ShrinkOutputBuffer(ssl); +#if defined(WOLFSSL_HRR_COOKIE) && !defined(NO_WOLFSSL_SERVER) + XFREE(ssl->buffers.tls13CookieSecret.buffer, ssl->heap, + DYNAMIC_TYPE_COOKIE_PWD); +#endif #ifdef WOLFSSL_DTLS DtlsMsgPoolReset(ssl); if (ssl->dtls_rx_msg_list != NULL) { @@ -13934,6 +13938,9 @@ const char* wolfSSL_ERR_reason_error_string(unsigned long e) case POST_HAND_AUTH_ERROR: return "Client will not do post handshake authentication"; + case HRR_COOKIE_ERROR: + return "Cookie does not match one sent in HelloRetryRequest"; + default : return "unknown error number"; } @@ -15742,7 +15749,7 @@ void PickHashSigAlgo(WOLFSSL* ssl, const byte* hashSigAlgo, if (QSH_Init(ssl) != 0) return MEMORY_E; #endif - extSz = TLSX_GetRequestSize(ssl); + extSz = TLSX_GetRequestSize(ssl, client_hello); if (extSz != 0) length += extSz; #else @@ -15835,7 +15842,7 @@ void PickHashSigAlgo(WOLFSSL* ssl, const byte* hashSigAlgo, output[idx++] = NO_COMPRESSION; #ifdef HAVE_TLS_EXTENSIONS - idx += TLSX_WriteRequest(ssl, output + idx); + idx += TLSX_WriteRequest(ssl, output + idx, client_hello); (void)idx; /* suppress analyzer warning, keep idx current */ #else diff --git a/src/tls.c b/src/tls.c index 76a83f914..1b5636eef 100755 --- a/src/tls.c +++ b/src/tls.c @@ -4452,6 +4452,170 @@ static int TLSX_SetSupportedVersions(TLSX** extensions, const void* data, #endif /* WOLFSSL_TLS13 */ +#if defined(WOLFSSL_TLS13) + +/******************************************************************************/ +/* Cookie */ +/******************************************************************************/ + +/* Free the cookie data. + * + * cookie Cookie data. + * heap The heap used for allocation. + */ +static void TLSX_Cookie_FreeAll(Cookie* cookie, void* heap) +{ + (void)heap; + + if (cookie != NULL) + XFREE(cookie, heap, DYNAMIC_TYPE_TLSX); +} + +/* Get the size of the encoded Cookie extension. + * In messages: ClientHello and HelloRetryRequest. + * + * cookie The cookie to write. + * msgType The type of the message this extension is being written into. + * returns the number of bytes of the encoded Cookie extension. + */ +static word16 TLSX_Cookie_GetSize(Cookie* cookie, byte msgType) +{ + if (msgType == client_hello || msgType == hello_retry_request) + return OPAQUE16_LEN + cookie->len; + + return SANITY_MSG_E; +} + +/* Writes the Cookie extension into the output buffer. + * Assumes that the the output buffer is big enough to hold data. + * In messages: ClientHello and HelloRetryRequest. + * + * cookie The cookie to write. + * output The buffer to write into. + * msgType The type of the message this extension is being written into. + * returns the number of bytes written into the buffer. + */ +static word16 TLSX_Cookie_Write(Cookie* cookie, byte* output, byte msgType) +{ + if (msgType == client_hello || msgType == hello_retry_request) { + c16toa(cookie->len, output); + output += OPAQUE16_LEN; + XMEMCPY(output, &cookie->data, cookie->len); + return OPAQUE16_LEN + cookie->len; + } + + return SANITY_MSG_E; +} + +/* Parse the Cookie extension. + * In messages: ClientHello and HelloRetryRequest. + * + * ssl The SSL/TLS object. + * input The extension data. + * length The length of the extension data. + * msgType The type of the message this extension is being parsed from. + * returns 0 on success and other values indicate failure. + */ +static int TLSX_Cookie_Parse(WOLFSSL* ssl, byte* input, word16 length, + byte msgType) +{ + word16 len; + word16 idx = 0; + TLSX* extension; + Cookie* cookie; + + if (msgType != client_hello && msgType != hello_retry_request) + return SANITY_MSG_E; + + /* Message contains length and Cookie which must be at least one byte + * in length. + */ + if (length < OPAQUE16_LEN + 1) + return BUFFER_E; + ato16(input + idx, &len); + idx += OPAQUE16_LEN; + if (length - idx != len) + return BUFFER_E; + + if (msgType == hello_retry_request) + return TLSX_Cookie_Use(ssl, input + idx, len, NULL, 0, 0); + + /* client_hello */ + extension = TLSX_Find(ssl->extensions, TLSX_COOKIE); + if (extension == NULL) + return HRR_COOKIE_ERROR; + + cookie = (Cookie*)extension->data; + if (cookie->len != len || XMEMCMP(&cookie->data, input + idx, len) != 0) + return HRR_COOKIE_ERROR; + + /* Request seen. */ + extension->resp = 0; + + return 0; +} + +/* Use the data to create a new Cookie object in the extensions. + * + * ssl SSL/TLS object. + * data Cookie data. + * len Length of cookie data in bytes. + * mac MAC data. + * macSz Length of MAC data in bytes. + * resp Indicates the extension will go into a response (HelloRetryRequest). + * returns 0 on success and other values indicate failure. + */ +int TLSX_Cookie_Use(WOLFSSL* ssl, byte* data, word16 len, byte* mac, + byte macSz, int resp) +{ + int ret = 0; + TLSX* extension; + Cookie* cookie; + + /* Find the cookie extension if it exists. */ + extension = TLSX_Find(ssl->extensions, TLSX_COOKIE); + if (extension == NULL) { + /* Push new cookie extension. */ + ret = TLSX_Push(&ssl->extensions, TLSX_COOKIE, NULL, ssl->heap); + if (ret != 0) + return ret; + + extension = TLSX_Find(ssl->extensions, TLSX_COOKIE); + if (extension == NULL) + return MEMORY_E; + } + + /* The Cookie structure has one byte for cookie data already. */ + cookie = (Cookie*)XMALLOC(sizeof(Cookie) + len + macSz - 1, ssl->heap, + DYNAMIC_TYPE_TLSX); + if (cookie == NULL) + return MEMORY_E; + + cookie->len = len + macSz; + XMEMCPY(&cookie->data, data, len); + if (mac != NULL) + XMEMCPY(&cookie->data + len, mac, macSz); + + extension->data = (void*)cookie; + extension->resp = resp; + + return 0; +} + +#define CKE_FREE_ALL TLSX_Cookie_FreeAll +#define CKE_GET_SIZE TLSX_Cookie_GetSize +#define CKE_WRITE TLSX_Cookie_Write +#define CKE_PARSE TLSX_Cookie_Parse + +#else + +#define CKE_FREE_ALL(a, b) 0 +#define CKE_GET_SIZE(a, b) 0 +#define CKE_WRITE(a, b, c) 0 +#define CKE_PARSE(a, b, c, d) 0 + +#endif + /******************************************************************************/ /* Sugnature Algorithms */ /******************************************************************************/ @@ -6376,7 +6540,8 @@ int TLSX_PskKeModes_Use(WOLFSSL* ssl, byte modes) * Only in ClientHello. * * msgType The type of the message this extension is being written into. - * returns the number of bytes of the encoded key share extension. + * returns the number of bytes of the encoded Post-Hanshake Authentication + * extension. */ static word16 TLSX_PostHandAuth_GetSize(byte msgType) { @@ -6478,7 +6643,7 @@ static int TLSX_PostHandAuth_Use(WOLFSSL* ssl) * In messages: ClientHello, EncryptedExtensions and NewSessionTicket. * * msgType The type of the message this extension is being written into. - * returns the number of bytes of the encoded key share extension. + * returns the number of bytes of the encoded Early Data Indication extension. */ static word16 TLSX_EarlyData_GetSize(byte msgType) { @@ -6561,10 +6726,10 @@ int TLSX_EarlyData_Use(WOLFSSL* ssl, word32 max) int ret = 0; TLSX* extension; - /* Find the early extension if it exists. */ + /* Find the early data extension if it exists. */ extension = TLSX_Find(ssl->extensions, TLSX_EARLY_DATA); if (extension == NULL) { - /* Push new early extension. */ + /* Push new early data extension. */ ret = TLSX_Push(&ssl->extensions, TLSX_EARLY_DATA, NULL, ssl->heap); if (ret != 0) return ret; @@ -6665,6 +6830,10 @@ void TLSX_FreeAll(TLSX* list, void* heap) case TLSX_SUPPORTED_VERSIONS: break; + case TLSX_COOKIE: + CKE_FREE_ALL((Cookie*)extension->data, heap); + break; + case TLSX_KEY_SHARE: KS_FREE_ALL((KeyShareEntry*)extension->data, heap); break; @@ -6782,6 +6951,10 @@ static word16 TLSX_GetSize(TLSX* list, byte* semaphore, byte msgType) length += SV_GET_SIZE(extension->data); break; + case TLSX_COOKIE: + length += CKE_GET_SIZE((Cookie*)extension->data, msgType); + break; + case TLSX_KEY_SHARE: length += KS_GET_SIZE((KeyShareEntry*)extension->data, msgType); break; @@ -6919,6 +7092,12 @@ static word16 TLSX_Write(TLSX* list, byte* output, byte* semaphore, offset += SV_WRITE(extension->data, output + offset); break; + case TLSX_COOKIE: + WOLFSSL_MSG("Cookie extension to write"); + offset += CKE_WRITE((Cookie*)extension->data, output + offset, + msgType); + break; + case TLSX_KEY_SHARE: WOLFSSL_MSG("Key Share extension to write"); offset += KS_WRITE((KeyShareEntry*)extension->data, @@ -7531,13 +7710,14 @@ int TLSX_PopulateExtensions(WOLFSSL* ssl, byte isServer) #ifndef NO_WOLFSSL_CLIENT /** Tells the buffered size of extensions to be sent into the client hello. */ -word16 TLSX_GetRequestSize(WOLFSSL* ssl) +word16 TLSX_GetRequestSize(WOLFSSL* ssl, byte msgType) { word16 length = 0; + byte semaphore[SEMAPHORE_SIZE] = {0}; - if (TLSX_SupportExtensions(ssl)) { - byte semaphore[SEMAPHORE_SIZE] = {0}; - + if (!TLSX_SupportExtensions(ssl)) + return 0; + if (msgType == client_hello) { EC_VALIDATE_REQUEST(ssl, semaphore); QSH_VALIDATE_REQUEST(ssl, semaphore); WOLF_STK_VALIDATE_REQUEST(ssl); @@ -7547,28 +7727,43 @@ word16 TLSX_GetRequestSize(WOLFSSL* ssl) if (!IsAtLeastTLSv1_2(ssl)) TURN_ON(semaphore, TLSX_ToSemaphore(TLSX_SUPPORTED_VERSIONS)); if (!IsAtLeastTLSv1_3(ssl->version)) { - TURN_ON(semaphore, TLSX_ToSemaphore(TLSX_SUPPORTED_VERSIONS)); TURN_ON(semaphore, TLSX_ToSemaphore(TLSX_KEY_SHARE)); #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) TURN_ON(semaphore, TLSX_ToSemaphore(TLSX_PRE_SHARED_KEY)); TURN_ON(semaphore, TLSX_ToSemaphore(TLSX_PSK_KEY_EXCHANGE_MODES)); + #endif + #ifdef WOLFSSL_EARLY_DATA + TURN_ON(semaphore, TLSX_ToSemaphore(TLSX_EARLY_DATA)); + #endif + TURN_ON(semaphore, TLSX_ToSemaphore(TLSX_COOKIE)); + #ifdef WOLFSSL_POST_HANDSHAKE_AUTH + TURN_ON(semaphore, TLSX_ToSemaphore(TLSX_POST_HANDSHAKE_AUTH)); #endif } #endif - - if (ssl->extensions) - length += TLSX_GetSize(ssl->extensions, semaphore, client_hello); - - if (ssl->ctx && ssl->ctx->extensions) { - length += TLSX_GetSize(ssl->ctx->extensions, semaphore, - client_hello); - } - -#ifdef HAVE_EXTENDED_MASTER - if (ssl->options.haveEMS) - length += HELLO_EXT_SZ; -#endif } +#ifdef WOLFSSL_TLS13 + #ifndef NO_CERTS + else if (msgType == certificate_request) { + XMEMSET(semaphore, 0xff, SEMAPHORE_SIZE); + TURN_OFF(semaphore, TLSX_ToSemaphore(TLSX_SIGNATURE_ALGORITHMS)); + TURN_OFF(semaphore, TLSX_ToSemaphore(TLSX_STATUS_REQUEST)); + /* TODO: TLSX_SIGNED_CERTIFICATE_TIMESTAMP, + * TLSX_CERTIFICATE_AUTHORITIES, OID_FILTERS + */ + } + #endif +#endif + + if (ssl->extensions) + length += TLSX_GetSize(ssl->extensions, semaphore, msgType); + if (ssl->ctx && ssl->ctx->extensions) + length += TLSX_GetSize(ssl->ctx->extensions, semaphore, msgType); + +#ifdef HAVE_EXTENDED_MASTER + if (msgType == client_hello && ssl->options.haveEMS) + length += HELLO_EXT_SZ; +#endif if (length) length += OPAQUE16_LEN; /* for total length storage. */ @@ -7577,65 +7772,91 @@ word16 TLSX_GetRequestSize(WOLFSSL* ssl) } /** Writes the extensions to be sent into the client hello. */ -word16 TLSX_WriteRequest(WOLFSSL* ssl, byte* output) +word16 TLSX_WriteRequest(WOLFSSL* ssl, byte* output, byte msgType) { word16 offset = 0; + byte semaphore[SEMAPHORE_SIZE] = {0}; - if (TLSX_SupportExtensions(ssl) && output) { - byte semaphore[SEMAPHORE_SIZE] = {0}; + if (!TLSX_SupportExtensions(ssl) || output == NULL) + return 0; - offset += OPAQUE16_LEN; /* extensions length */ + offset += OPAQUE16_LEN; /* extensions length */ + if (msgType == client_hello) { EC_VALIDATE_REQUEST(ssl, semaphore); WOLF_STK_VALIDATE_REQUEST(ssl); QSH_VALIDATE_REQUEST(ssl, semaphore); if (ssl->suites->hashSigAlgoSz == 0) TURN_ON(semaphore, TLSX_ToSemaphore(TLSX_SIGNATURE_ALGORITHMS)); -#if defined(WOLFSSL_TLS13) +#ifdef WOLFSSL_TLS13 if (!IsAtLeastTLSv1_2(ssl)) TURN_ON(semaphore, TLSX_ToSemaphore(TLSX_SUPPORTED_VERSIONS)); if (!IsAtLeastTLSv1_3(ssl->version)) { - TURN_ON(semaphore, TLSX_ToSemaphore(TLSX_SUPPORTED_VERSIONS)); TURN_ON(semaphore, TLSX_ToSemaphore(TLSX_KEY_SHARE)); #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) TURN_ON(semaphore, TLSX_ToSemaphore(TLSX_PSK_KEY_EXCHANGE_MODES)); + #endif + #ifdef WOLFSSL_EARLY_DATA + TURN_ON(semaphore, TLSX_ToSemaphore(TLSX_EARLY_DATA)); + #endif + TURN_ON(semaphore, TLSX_ToSemaphore(TLSX_COOKIE)); + #ifdef WOLFSSL_POST_HANDSHAKE_AUTH + TURN_ON(semaphore, TLSX_ToSemaphore(TLSX_POST_HANDSHAKE_AUTH)); #endif } #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) + /* Must write Pre-shared Key extension at the end in TLS v1.3. + * Must not write out Pre-shared Key extension in earlier versions of + * protocol. + */ TURN_ON(semaphore, TLSX_ToSemaphore(TLSX_PRE_SHARED_KEY)); #endif #endif + } +#ifdef WOLFSSL_TLS13 + #ifndef NO_CERT + else if (msgType == certificate_request) { + XMEMSET(semaphore, 0xff, SEMAPHORE_SIZE); + TURN_OFF(semaphore, TLSX_ToSemaphore(TLSX_SIGNATURE_ALGORITHMS)); + TURN_OFF(semaphore, TLSX_ToSemaphore(TLSX_STATUS_REQUEST)); + /* TODO: TLSX_SIGNED_CERTIFICATE_TIMESTAMP, + * TLSX_CERTIFICATE_AUTHORITIES, TLSX_OID_FILTERS + */ + } + #endif +#endif - if (ssl->extensions) - offset += TLSX_Write(ssl->extensions, output + offset, semaphore, - client_hello); - - if (ssl->ctx && ssl->ctx->extensions) - offset += TLSX_Write(ssl->ctx->extensions, output + offset, - semaphore, client_hello); + if (ssl->extensions) { + offset += TLSX_Write(ssl->extensions, output + offset, semaphore, + msgType); + } + if (ssl->ctx && ssl->ctx->extensions) { + offset += TLSX_Write(ssl->ctx->extensions, output + offset, semaphore, + msgType); + } #ifdef HAVE_EXTENDED_MASTER - if (ssl->options.haveEMS) { - c16toa(HELLO_EXT_EXTMS, output + offset); - offset += HELLO_EXT_TYPE_SZ; - c16toa(0, output + offset); - offset += HELLO_EXT_SZ_SZ; - } + if (msgType == client_hello && ssl->options.haveEMS) { + c16toa(HELLO_EXT_EXTMS, output + offset); + offset += HELLO_EXT_TYPE_SZ; + c16toa(0, output + offset); + offset += HELLO_EXT_SZ_SZ; + } #endif #ifdef WOLFSSL_TLS13 - if (IsAtLeastTLSv1_3(ssl->version)) { #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) - TURN_OFF(semaphore, TLSX_ToSemaphore(TLSX_PRE_SHARED_KEY)); + if (msgType == client_hello && IsAtLeastTLSv1_3(ssl->version)) { + /* Write out what we can of Pre-shared key extension. */ + TURN_OFF(semaphore, TLSX_ToSemaphore(TLSX_PRE_SHARED_KEY)); + offset += TLSX_Write(ssl->extensions, output + offset, semaphore, + client_hello); + } #endif - offset += TLSX_Write(ssl->extensions, output + offset, semaphore, - client_hello); - } #endif - if (offset > OPAQUE16_LEN) - c16toa(offset - OPAQUE16_LEN, output); /* extensions length */ - } + if (offset > OPAQUE16_LEN || msgType != client_hello) + c16toa(offset - OPAQUE16_LEN, output); /* extensions length */ return offset; } @@ -7653,14 +7874,20 @@ word16 TLSX_GetResponseSize(WOLFSSL* ssl, byte msgType) switch (msgType) { case server_hello: #ifdef WOLFSSL_TLS13 - case hello_retry_request: - if (ssl->options.tls1_3) { - XMEMSET(semaphore, 0xff, SEMAPHORE_SIZE); - TURN_OFF(semaphore, TLSX_ToSemaphore(TLSX_KEY_SHARE)); + if (ssl->options.tls1_3) { + XMEMSET(semaphore, 0xff, SEMAPHORE_SIZE); + TURN_OFF(semaphore, TLSX_ToSemaphore(TLSX_KEY_SHARE)); #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) - TURN_OFF(semaphore, TLSX_ToSemaphore(TLSX_PRE_SHARED_KEY)); + TURN_OFF(semaphore, TLSX_ToSemaphore(TLSX_PRE_SHARED_KEY)); #endif - } + } +#endif + break; +#ifdef WOLFSSL_TLS13 + case hello_retry_request: + XMEMSET(semaphore, 0xff, SEMAPHORE_SIZE); + TURN_OFF(semaphore, TLSX_ToSemaphore(TLSX_KEY_SHARE)); + TURN_OFF(semaphore, TLSX_ToSemaphore(TLSX_COOKIE)); #endif break; #ifdef WOLFSSL_TLS13 @@ -7671,19 +7898,22 @@ word16 TLSX_GetResponseSize(WOLFSSL* ssl, byte msgType) TURN_ON(semaphore, TLSX_ToSemaphore(TLSX_PRE_SHARED_KEY)); #endif break; - #ifndef NO_CERTS - case certificate_request: - XMEMSET(semaphore, 0xff, SEMAPHORE_SIZE); - TURN_OFF(semaphore, TLSX_ToSemaphore(TLSX_SIGNATURE_ALGORITHMS)); + #ifdef WOLFSSL_EARLY_DATA + case session_ticket: + if (ssl->options.tls1_3) { + XMEMSET(semaphore, 0xff, SEMAPHORE_SIZE); + TURN_OFF(semaphore, TLSX_ToSemaphore(TLSX_EARLY_DATA)); + } break; #endif - #ifdef WOLFSSL_EARLY_DATA - case session_ticket: - if (ssl->options.tls1_3) { - XMEMSET(semaphore, 0xff, SEMAPHORE_SIZE); - TURN_OFF(semaphore, TLSX_ToSemaphore(TLSX_EARLY_DATA)); - } - break; + #ifndef NO_CERT + case certificate: + XMEMSET(semaphore, 0xff, SEMAPHORE_SIZE); + TURN_OFF(semaphore, TLSX_ToSemaphore(TLSX_STATUS_REQUEST)); + /* TODO: TLSX_SIGNED_CERTIFICATE_TIMESTAMP, + * TLSX_SERVER_CERTIFICATE_TYPE + */ + break; #endif #endif } @@ -7707,7 +7937,7 @@ word16 TLSX_GetResponseSize(WOLFSSL* ssl, byte msgType) /* All the response data is set at the ssl object only, so no ctx here. */ - if (length || (msgType != server_hello)) + if (length || msgType != server_hello) length += OPAQUE16_LEN; /* for total length storage. */ return length; @@ -7724,7 +7954,6 @@ word16 TLSX_WriteResponse(WOLFSSL *ssl, byte* output, byte msgType) switch (msgType) { case server_hello: #ifdef WOLFSSL_TLS13 - case hello_retry_request: if (ssl->options.tls1_3) { XMEMSET(semaphore, 0xff, SEMAPHORE_SIZE); TURN_OFF(semaphore, TLSX_ToSemaphore(TLSX_KEY_SHARE)); @@ -7734,6 +7963,12 @@ word16 TLSX_WriteResponse(WOLFSSL *ssl, byte* output, byte msgType) } #endif break; +#ifdef WOLFSSL_TLS13 + case hello_retry_request: + XMEMSET(semaphore, 0xff, SEMAPHORE_SIZE); + TURN_OFF(semaphore, TLSX_ToSemaphore(TLSX_KEY_SHARE)); +#endif + break; #ifdef WOLFSSL_TLS13 case encrypted_extensions: TURN_ON(semaphore, TLSX_ToSemaphore(TLSX_SESSION_TICKET)); @@ -7743,10 +7978,12 @@ word16 TLSX_WriteResponse(WOLFSSL *ssl, byte* output, byte msgType) #endif break; #ifndef NO_CERTS - case certificate_request: + case certificate: XMEMSET(semaphore, 0xff, SEMAPHORE_SIZE); - TURN_OFF(semaphore, - TLSX_ToSemaphore(TLSX_SIGNATURE_ALGORITHMS)); + TURN_OFF(semaphore, TLSX_ToSemaphore(TLSX_STATUS_REQUEST)); + /* TODO: TLSX_SIGNED_CERTIFICATE_TIMESTAMP, + * TLSX_SERVER_CERTIFICATE_TYPE + */ break; #endif #ifdef WOLFSSL_EARLY_DATA @@ -7765,6 +8002,15 @@ word16 TLSX_WriteResponse(WOLFSSL *ssl, byte* output, byte msgType) offset += TLSX_Write(ssl->extensions, output + offset, semaphore, msgType); +#ifdef WOLFSSL_TLS13 + if (msgType == hello_retry_request) { + XMEMSET(semaphore, 0xff, SEMAPHORE_SIZE); + TURN_OFF(semaphore, TLSX_ToSemaphore(TLSX_COOKIE)); + offset += TLSX_Write(ssl->extensions, output + offset, semaphore, + msgType); + } +#endif + #ifdef HAVE_EXTENDED_MASTER if (ssl->options.haveEMS && msgType == server_hello) { c16toa(HELLO_EXT_EXTMS, output + offset); @@ -7774,7 +8020,7 @@ word16 TLSX_WriteResponse(WOLFSSL *ssl, byte* output, byte msgType) } #endif - if (offset > OPAQUE16_LEN || msgType == encrypted_extensions) + if (offset > OPAQUE16_LEN || msgType != server_hello) c16toa(offset - OPAQUE16_LEN, output); /* extensions length */ } @@ -7977,6 +8223,20 @@ int TLSX_Parse(WOLFSSL* ssl, byte* input, word16 length, byte msgType, ret = SV_PARSE(ssl, input + offset, size); break; + case TLSX_COOKIE: + WOLFSSL_MSG("Cookie extension received"); + + if (!IsAtLeastTLSv1_3(ssl->version)) + break; + + if (IsAtLeastTLSv1_3(ssl->version) && + msgType != client_hello && + msgType != hello_retry_request) { + return EXT_NOT_ALLOWED; + } + ret = CKE_PARSE(ssl, input + offset, size, msgType); + break; + case TLSX_KEY_SHARE: WOLFSSL_MSG("Key Share extension received"); diff --git a/src/tls13.c b/src/tls13.c index 7943a9cfa..4bf455e27 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -1366,60 +1366,6 @@ static int HashInputRaw(WOLFSSL* ssl, const byte* input, int sz) } #endif -#ifndef WOLFSSL_TLS13_DRAFT_18 -/* The offset into MessageHash of the low byte of the length field. */ -#define MSG_HASH_LEN_OFFSET 3 - -/* Restart the Hanshake hash with a hash of the previous messages. - * - * ssl The SSL/TLS object. - * returns 0 on success, otherwise failure. - */ -static int RestartHandshakeHash(WOLFSSL* ssl) -{ - int ret; - Hashes hashes; - byte header[] = { message_hash, 0, 0, 0 }; - byte* hash = NULL; - - ret = BuildCertHashes(ssl, &hashes); - if (ret != 0) - return ret; - ret = InitHandshakeHashes(ssl); - if (ret != 0) - return ret; - switch (ssl->specs.mac_algorithm) { - #ifndef NO_SHA256 - case sha256_mac: - header[MSG_HASH_LEN_OFFSET] = SHA256_DIGEST_SIZE; - hash = hashes.sha256; - break; - #endif - #ifdef WOLFSSL_SHA384 - case sha384_mac: - header[MSG_HASH_LEN_OFFSET] = SHA384_DIGEST_SIZE; - hash = hashes.sha384; - break; - #endif - #ifdef WOLFSSL_SHA512 - case sha512_mac: - header[MSG_HASH_LEN_OFFSET] = SHA512_DIGEST_SIZE; - hash = hashes.sha512; - break; - #endif - } - - WOLFSSL_MSG("Restart Hash"); - WOLFSSL_BUFFER(hash, header[3]); - - ret = HashOutputRaw(ssl, header, sizeof(header)); - if (ret != 0) - return ret; - return HashOutputRaw(ssl, hash, header[MSG_HASH_LEN_OFFSET]); -} -#endif - - /* Extract the handshake header information. * * ssl The SSL/TLS object. @@ -2348,7 +2294,7 @@ int SendTls13ClientHello(WOLFSSL* ssl) return MEMORY_E; #endif /* Include length of TLS extensions. */ - length += TLSX_GetRequestSize(ssl); + length += TLSX_GetRequestSize(ssl, client_hello); /* Total message size. */ sendSz = length + HANDSHAKE_HEADER_SZ + RECORD_HEADER_SZ; @@ -2396,7 +2342,7 @@ int SendTls13ClientHello(WOLFSSL* ssl) output[idx++] = NO_COMPRESSION; /* Write out extensions for a request. */ - idx += TLSX_WriteRequest(ssl, output + idx); + idx += TLSX_WriteRequest(ssl, output + idx, client_hello); #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) /* Resumption has a specific set of extensions and binder is calculated @@ -2429,6 +2375,117 @@ int SendTls13ClientHello(WOLFSSL* ssl) return ret; } +#ifndef WOLFSSL_TLS13_DRAFT_18 +#ifdef WOLFSSL_HRR_COOKIE +/* Create Cookie extension using the hash of the first ClientHello. + * + * ssl SSL/TLS object. + * hash The hash data. + * hashSz The size of the hash data in bytes. + * returns 0 on success, otherwise failure. + */ +static int CreateCookie(WOLFSSL* ssl, byte* hash, byte hashSz) +{ + int ret; + byte mac[MAX_DIGEST_SIZE]; + Hmac cookieHmac; + byte cookieType; + byte macSz; + +#if !defined(NO_SHA) && defined(NO_SHA256) + cookieType = SHA; + macSz = SHA_DIGEST_SIZE; +#endif /* NO_SHA */ +#ifndef NO_SHA256 + cookieType = SHA256; + macSz = SHA256_DIGEST_SIZE; +#endif /* NO_SHA256 */ + + ret = wc_HmacSetKey(&cookieHmac, cookieType, + ssl->buffers.tls13CookieSecret.buffer, + ssl->buffers.tls13CookieSecret.length); + if (ret != 0) + return ret; + if ((ret = wc_HmacUpdate(&cookieHmac, hash, hashSz)) != 0) + return ret; + if ((ret = wc_HmacFinal(&cookieHmac, mac)) != 0) + return ret; + + /* The cookie data is the hash and the integrity check. */ + return TLSX_Cookie_Use(ssl, hash, hashSz, mac, macSz, 1); +} +#endif + +/* Restart the Hanshake hash with a hash of the previous messages. + * + * ssl The SSL/TLS object. + * returns 0 on success, otherwise failure. + */ +static int RestartHandshakeHash(WOLFSSL* ssl) +{ + int ret; + Hashes hashes; + byte header[HANDSHAKE_HEADER_SZ]; + byte* hash = NULL; + byte hashSz = 0; + + ret = BuildCertHashes(ssl, &hashes); + if (ret != 0) + return ret; + switch (ssl->specs.mac_algorithm) { + #ifndef NO_SHA256 + case sha256_mac: + hash = hashes.sha256; + break; + #endif + #ifdef WOLFSSL_SHA384 + case sha384_mac: + hash = hashes.sha384; + break; + #endif + #ifdef WOLFSSL_SHA512 + case sha512_mac: + hash = hashes.sha512; + break; + #endif + } + hashSz = ssl->specs.hash_size; + AddTls13HandShakeHeader(header, hashSz, 0, 0, message_hash, ssl); + + WOLFSSL_MSG("Restart Hash"); + WOLFSSL_BUFFER(hash, hashSz); + +#ifdef WOLFSSL_HRR_COOKIE + if (ssl->options.sendCookie) { + byte cookie[OPAQUE8_LEN + MAX_DIGEST_SIZE + OPAQUE16_LEN * 2]; + TLSX* ext; + word32 idx = 0; + + /* Cookie Data = Hash Len | Hash | CS | KeyShare Group */ + cookie[idx++] = hashSz; + XMEMCPY(cookie + idx, hash, hashSz); + idx += hashSz; + cookie[idx++] = ssl->options.cipherSuite0; + cookie[idx++] = ssl->options.cipherSuite; + if ((ext = TLSX_Find(ssl->extensions, TLSX_KEY_SHARE)) != NULL) { + KeyShareEntry* kse = (KeyShareEntry*)ext->data; + c16toa(kse->group, cookie + idx); + idx += OPAQUE16_LEN; + } + return CreateCookie(ssl, cookie, idx); + } +#endif + + ret = InitHandshakeHashes(ssl); + if (ret != 0) + return ret; + ret = HashOutputRaw(ssl, header, sizeof(header)); + if (ret != 0) + return ret; + return HashOutputRaw(ssl, hash, hashSz); +} +#endif + /* Parse and handle a HelloRetryRequest message. * Only a client will receive this message. * @@ -3059,6 +3116,154 @@ static int DoPreSharedKeys(WOLFSSL* ssl, const byte* input, word32 helloSz, } #endif +#if !defined(WOLFSSL_TLS13_DRAFT_18) && defined(WOLFSSL_HRR_COOKIE) +/* Check that the Cookie data's integrity. + * + * ssl SSL/TLS object. + * cookie The cookie data - hash and MAC. + * cookieSz The length of the cookie data in bytes. + * returns Length of the hash on success, otherwise failure. + */ +static int CheckCookie(WOLFSSL* ssl, byte* cookie, byte cookieSz) +{ + int ret; + byte mac[MAX_DIGEST_SIZE]; + Hmac cookieHmac; + byte cookieType; + byte macSz; + +#if !defined(NO_SHA) && defined(NO_SHA256) + cookieType = SHA; + macSz = SHA_DIGEST_SIZE; +#endif /* NO_SHA */ +#ifndef NO_SHA256 + cookieType = SHA256; + macSz = SHA256_DIGEST_SIZE; +#endif /* NO_SHA256 */ + + if (cookieSz < ssl->specs.hash_size + macSz) + return HRR_COOKIE_ERROR; + cookieSz -= macSz; + + ret = wc_HmacSetKey(&cookieHmac, cookieType, + ssl->buffers.tls13CookieSecret.buffer, + ssl->buffers.tls13CookieSecret.length); + if (ret != 0) + return ret; + if ((ret = wc_HmacUpdate(&cookieHmac, cookie, cookieSz)) != 0) + return ret; + if ((ret = wc_HmacFinal(&cookieHmac, mac)) != 0) + return ret; + + if (XMEMCMP(cookie + cookieSz, mac, macSz) != 0) + return HRR_COOKIE_ERROR; + return cookieSz; +} + +/* Length of the KeyShare Extension */ +#define HRR_KEY_SHARE_SZ (OPAQUE16_LEN + OPAQUE16_LEN + OPAQUE16_LEN) +/* Length of the Cookie Extension excluding cookie data */ +#define HRR_COOKIE_HDR_SZ (OPAQUE16_LEN + OPAQUE16_LEN + OPAQUE16_LEN) +/* PV | CipherSuite | Ext Len */ +#define HRR_BODY_SZ (OPAQUE16_LEN + OPAQUE16_LEN + OPAQUE16_LEN) +/* HH | PV | CipherSuite | Ext Len | Key Share | Cookie */ +#define MAX_HRR_SZ (HANDSHAKE_HEADER_SZ + \ + HRR_BODY_SZ + \ + HRR_KEY_SHARE_SZ + \ + HRR_COOKIE_HDR_SZ) +/* Restart the Hanshake hash from the cookie value. + * + * ssl SSL/TLS object. + * cookie Cookie data from client. + * returns 0 on success, otherwise failure. + */ +static int RestartHandshakeHashWithCookie(WOLFSSL* ssl, Cookie* cookie) +{ + byte header[HANDSHAKE_HEADER_SZ]; + byte hrr[MAX_HRR_SZ]; + int hrrIdx; + word32 idx; + byte hashSz; + byte* cookieData; + byte cookieDataSz; + word16 length; + int keyShareExt = 0; + int ret; + + cookieDataSz = ret = CheckCookie(ssl, &cookie->data, cookie->len); + if (ret < 0) + return ret; + hashSz = cookie->data; + cookieData = &cookie->data; + idx = OPAQUE8_LEN; + + /* Restart handshake hash with synthetic message hash. */ + AddTls13HandShakeHeader(header, hashSz, 0, 0, message_hash, ssl); + if ((ret = InitHandshakeHashes(ssl)) != 0) + return ret; + if ((ret = HashOutputRaw(ssl, header, sizeof(header))) != 0) + return ret; + if ((ret = HashOutputRaw(ssl, cookieData + idx, hashSz)) != 0) + return ret; + + /* Reconstruct the HelloRetryMessage for handshake hash. */ + length = HRR_BODY_SZ + HRR_COOKIE_HDR_SZ + cookie->len; + if (cookieDataSz > hashSz + OPAQUE16_LEN) { + keyShareExt = 1; + length += HRR_KEY_SHARE_SZ; + } + AddTls13HandShakeHeader(hrr, length, 0, 0, hello_retry_request, ssl); + + idx += hashSz; + hrrIdx = HANDSHAKE_HEADER_SZ; + /* TODO: [TLS13] Replace existing code with code in comment. + * Use the TLS v1.3 draft version for now. + * + * Change to: + * hrr[hrrIdx++] = ssl->version.major; + * hrr[hrrIdx++] = ssl->version.minor; + */ + /* The negotiated protocol version. */ + hrr[hrrIdx++] = TLS_DRAFT_MAJOR; + hrr[hrrIdx++] = TLS_DRAFT_MINOR; + /* Cipher Suite */ + hrr[hrrIdx++] = cookieData[idx++]; + hrr[hrrIdx++] = cookieData[idx++]; + + /* Extensions' length */ + length -= HRR_BODY_SZ; + c16toa(length, hrr + hrrIdx); + hrrIdx += 2; + /* Optional KeyShare Extension */ + if (keyShareExt) { + c16toa(TLSX_KEY_SHARE, hrr + hrrIdx); + hrrIdx += 2; + c16toa(OPAQUE16_LEN, hrr + hrrIdx); + hrrIdx += 2; + hrr[hrrIdx++] = cookieData[idx++]; + hrr[hrrIdx++] = cookieData[idx++]; + } + /* Mandatory Cookie Extension */ + c16toa(TLSX_COOKIE, hrr + hrrIdx); + hrrIdx += 2; + c16toa(cookie->len + OPAQUE16_LEN, hrr + hrrIdx); + hrrIdx += 2; + c16toa(cookie->len, hrr + hrrIdx); + hrrIdx += 2; + +#ifdef WOLFSSL_DEBUG_TLS + WOLFSSL_MSG("Reconstucted HelloRetryRequest"); + WOLFSSL_BUFFER(hrr, hrrIdx); + WOLFSSL_MSG("Cookie"); + WOLFSSL_BUFFER(cookieData, cookie->len); +#endif + + if ((ret = HashOutputRaw(ssl, hrr, hrrIdx)) != 0) + return ret; + return HashOutputRaw(ssl, cookieData, cookie->len); +} +#endif + /* Handle a ClientHello handshake message. * If the protocol version in the message is not TLS v1.3 or higher, use * DoClientHello() @@ -3182,6 +3387,23 @@ static int DoTls13ClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, if (TLSX_Find(ssl->extensions, TLSX_SUPPORTED_VERSIONS) == NULL) ssl->version.minor = pv.minor; +#ifdef WOLFSSL_HRR_COOKIE + if (ssl->options.sendCookie && + ssl->options.serverState == SERVER_HELLO_RETRY_REQUEST) { + TLSX* ext; + + if ((ext = TLSX_Find(ssl->extensions, TLSX_COOKIE)) == NULL) + return HRR_COOKIE_ERROR; + /* Ensure the cookie came from client and isn't the one in the response + * - HelloRetryRequest. + */ + if (ext->resp == 1) + return HRR_COOKIE_ERROR; + ret = RestartHandshakeHashWithCookie(ssl, (Cookie*)ext->data); + if (ret != 0) + return ret; + } +#endif ssl->options.sendVerify = SEND_CERT; @@ -3244,6 +3466,11 @@ int SendTls13HelloRetryRequest(WOLFSSL* ssl) WOLFSSL_ENTER("SendTls13HelloRetryRequest"); +#ifndef WOLFSSL_TLS13_DRAFT_18 + if ((ret = RestartHandshakeHash(ssl)) < 0) + return ret; +#endif + /* Get the length of the extensions that will be written. */ len = TLSX_GetResponseSize(ssl, hello_retry_request); /* There must be extensions sent to indicate what client needs to do. */ @@ -3294,11 +3521,6 @@ int SendTls13HelloRetryRequest(WOLFSSL* ssl) } #endif -#ifndef WOLFSSL_TLS13_DRAFT_18 - if ((ret = RestartHandshakeHash(ssl)) < 0) - return ret; -#endif - if ((ret = HashOutput(ssl, output, idx, 0)) != 0) return ret; @@ -3504,6 +3726,9 @@ static int SendTls13CertificateRequest(WOLFSSL* ssl, byte* reqCtx, int sendSz; word32 i; int reqSz; +#ifndef WOLFSSL_TLS13_DRAFT_18 + TLSX* ext; +#endif WOLFSSL_ENTER("SendTls13CertificateRequest"); @@ -3549,9 +3774,12 @@ static int SendTls13CertificateRequest(WOLFSSL* ssl, byte* reqCtx, c16toa(0, &output[i]); /* auth's */ i += REQ_HEADER_SZ; #else + ext = TLSX_Find(ssl->extensions, TLSX_SIGNATURE_ALGORITHMS); + ext->resp = 0; + i = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ; reqSz = OPAQUE8_LEN + reqCtxLen + - TLSX_GetResponseSize(ssl, certificate_request); + TLSX_GetRequestSize(ssl, certificate_request); sendSz = i + reqSz; /* Always encrypted and make room for padding. */ @@ -3576,7 +3804,7 @@ static int SendTls13CertificateRequest(WOLFSSL* ssl, byte* reqCtx, } /* Certificate extensions. */ - i += TLSX_WriteResponse(ssl, output + i, certificate_request); + i += TLSX_WriteRequest(ssl, output + i, certificate_request); #endif /* Always encrypted. */ @@ -6060,7 +6288,7 @@ int DoTls13HandShakeMsgType(WOLFSSL* ssl, byte* input, word32* inOutIdx, if (ret == 0 && type != client_hello && type != session_ticket && - type != key_update && ssl->error != WC_PENDING_E) { + type != key_update && ssl->error != WC_PENDING_E) { ret = HashInput(ssl, input + inIdx, size); } @@ -6446,6 +6674,72 @@ int wolfSSL_connect_TLSv13(WOLFSSL* ssl) } } +#if defined(WOLFSSL_HRR_COOKIE) && !defined(NO_WOLFSSL_SERVER) +/* Send a cookie with the HelloRetryRequest to avoid storing state. + * + * ssl SSL/TLS object. + * secret Secret to use when generating integrity check for cookie. + * A value of NULL indicates to generate a new random secret. + * secretSz Size of secret data in bytes. + * Use a value of 0 to indicate use of default size. + * returns BAD_FUNC_ARG when ssl is NULL, not using TLS v1.3, or called on a + * client; SSL_SUCCESS on success and otherwise failure. + */ +int wolfSSL_send_hrr_cookie(WOLFSSL* ssl, const unsigned char* secret, + unsigned int secretSz) +{ + int ret; + + if (ssl == NULL || !IsAtLeastTLSv1_3(ssl->version) || + ssl->options.side == WOLFSSL_CLIENT_END) + return BAD_FUNC_ARG; + + if (secretSz == 0) { + #if !defined(NO_SHA) && defined(NO_SHA256) + secretSz = SHA_DIGEST_SIZE; + #endif /* NO_SHA */ + #ifndef NO_SHA256 + secretSz = SHA256_DIGEST_SIZE; + #endif /* NO_SHA256 */ + } + + if (secretSz != ssl->buffers.tls13CookieSecret.length) { + byte* newSecret; + + if (ssl->buffers.tls13CookieSecret.buffer != NULL) { + ForceZero(ssl->buffers.tls13CookieSecret.buffer, + ssl->buffers.tls13CookieSecret.length); + XFREE(ssl->buffers.tls13CookieSecret.buffer, + ssl->heap, DYNAMIC_TYPE_COOKIE_PWD); + } + + newSecret = (byte*)XMALLOC(secretSz, ssl->heap,DYNAMIC_TYPE_COOKIE_PWD); + if (newSecret == NULL) { + ssl->buffers.tls13CookieSecret.buffer = NULL; + ssl->buffers.tls13CookieSecret.length = 0; + WOLFSSL_MSG("couldn't allocate new cookie secret"); + return MEMORY_ERROR; + } + ssl->buffers.tls13CookieSecret.buffer = newSecret; + ssl->buffers.tls13CookieSecret.length = secretSz; + } + + /* If the supplied secret is NULL, randomly generate a new secret. */ + if (secret == NULL) { + ret = wc_RNG_GenerateBlock(ssl->rng, + ssl->buffers.tls13CookieSecret.buffer, secretSz); + if (ret < 0) + return ret; + } + else + XMEMCPY(ssl->buffers.tls13CookieSecret.buffer, secret, secretSz); + + ssl->options.sendCookie = 1; + + return SSL_SUCCESS; +} +#endif + /* Create a key share entry from group. * Generates a key pair. * diff --git a/wolfssl/error-ssl.h b/wolfssl/error-ssl.h index c6976ab87..09d843699 100644 --- a/wolfssl/error-ssl.h +++ b/wolfssl/error-ssl.h @@ -168,7 +168,8 @@ enum wolfSSL_ErrorCodes { MATCH_SUITE_ERROR = -501, /* can't match cipher suite */ COMPRESSION_ERROR = -502, /* compression mismatch */ KEY_SHARE_ERROR = -503, /* key share mismatch */ - POST_HAND_AUTH_ERROR = -504 /* client won't do post-hand auth */ + POST_HAND_AUTH_ERROR = -504, /* client won't do post-hand auth */ + HRR_COOKIE_ERROR = -505 /* HRR msg cookie mismatch */ /* end negotiation parameter errors only 10 for now */ /* add strings to wolfSSL_ERR_reason_error_string in internal.c !!!!! */ diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 81a2b38f1..befc50d59 100755 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -1789,6 +1789,7 @@ typedef enum { TLSX_EARLY_DATA = 0x002a, #endif TLSX_SUPPORTED_VERSIONS = 0x002b, + TLSX_COOKIE = 0x002c, #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) TLSX_PSK_KEY_EXCHANGE_MODES = 0x002d, #endif @@ -1813,8 +1814,9 @@ WOLFSSL_LOCAL int TLSX_SupportExtensions(WOLFSSL* ssl); WOLFSSL_LOCAL int TLSX_PopulateExtensions(WOLFSSL* ssl, byte isRequest); #ifndef NO_WOLFSSL_CLIENT -WOLFSSL_LOCAL word16 TLSX_GetRequestSize(WOLFSSL* ssl); -WOLFSSL_LOCAL word16 TLSX_WriteRequest(WOLFSSL* ssl, byte* output); +WOLFSSL_LOCAL word16 TLSX_GetRequestSize(WOLFSSL* ssl, byte msgType); +WOLFSSL_LOCAL word16 TLSX_WriteRequest(WOLFSSL* ssl, byte* output, + byte msgType); #endif #ifndef NO_WOLFSSL_SERVER @@ -2063,6 +2065,16 @@ WOLFSSL_LOCAL int TLSX_ValidateQSHScheme(TLSX** extensions, word16 name); #endif /* HAVE_QSH */ #ifdef WOLFSSL_TLS13 +/* Cookie extension information - cookie data. */ +typedef struct Cookie { + word16 len; + byte data; +} Cookie; + +WOLFSSL_LOCAL int TLSX_Cookie_Use(WOLFSSL* ssl, byte* data, word16 len, + byte* mac, byte macSz, int resp); + + /* Key Share - TLS v1.3 Specification */ /* The KeyShare extension information - entry in a linked list. */ @@ -2080,6 +2092,7 @@ WOLFSSL_LOCAL int TLSX_KeyShare_Use(WOLFSSL* ssl, word16 group, word16 len, WOLFSSL_LOCAL int TLSX_KeyShare_Empty(WOLFSSL* ssl); WOLFSSL_LOCAL int TLSX_KeyShare_Establish(WOLFSSL* ssl); + #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) /* The PreSharedKey extension information - entry in a linked list. */ typedef struct PreSharedKey { @@ -2106,6 +2119,7 @@ WOLFSSL_LOCAL int TLSX_PreSharedKey_Use(WOLFSSL* ssl, byte* identity, byte resumption, PreSharedKey **preSharedKey); +/* The possible Pre-Shared Key key exchange modes. */ enum PskKeyExchangeMode { PSK_KE, PSK_DHE_KE @@ -2123,6 +2137,7 @@ WOLFSSL_LOCAL int TLSX_EarlyData_Use(WOLFSSL* ssl, word32 max); #endif #endif /* HAVE_SESSION_TICKET || !NO_PSK */ + /* The types of keys to derive for. */ enum DeriveKeyType { no_key, @@ -2196,13 +2211,13 @@ struct WOLFSSL_CTX { byte groupMessages; /* group handshake messages before sending */ byte minDowngrade; /* minimum downgrade version */ byte haveEMS; /* have extended master secret extension */ - byte useClientOrder; /* Use client's cipher preference order */ + byte useClientOrder:1; /* Use client's cipher preference order */ #ifdef WOLFSSL_TLS13 - byte noTicketTls13; /* Server won't create new Ticket */ - byte noPskDheKe; /* Don't use (EC)DHE with PSK */ + byte noTicketTls13:1; /* Server won't create new Ticket */ + byte noPskDheKe:1; /* Don't use (EC)DHE with PSK */ #endif #if defined(WOLFSSL_TLS13) && defined(WOLFSSL_POST_HANDSHAKE_AUTH) - byte postHandshakeAuth;/* Post-handshake authentication supported. */ + byte postHandshakeAuth:1; /* Post-handshake auth supported. */ #endif #if defined(WOLFSSL_SCTP) && defined(WOLFSSL_DTLS) byte dtlsSctp; /* DTLS-over-SCTP mode */ @@ -2676,6 +2691,9 @@ typedef struct Buffers { int certChainCnt; #endif #endif +#ifdef WOLFSSL_HRR_COOKIE + buffer tls13CookieSecret; /* HRR cookie secret */ +#endif #ifdef WOLFSSL_DTLS WOLFSSL_DTLS_CTX dtlsCtx; /* DTLS connection context */ #ifndef NO_WOLFSSL_SERVER @@ -2808,6 +2826,9 @@ typedef struct Options { word16 postHandshakeAuth:1;/* Client send post_handshake_auth * extendion. */ #endif +#if defined(WOLFSSL_TLS13) && !defined(NO_WOLFSSL_SERVER) + word16 sendCookie:1; /* Server creates a Cookie in HRR */ +#endif /* need full byte values for this section */ byte processReply; /* nonblocking resume */ diff --git a/wolfssl/ssl.h b/wolfssl/ssl.h index d93d500b7..671ac473f 100644 --- a/wolfssl/ssl.h +++ b/wolfssl/ssl.h @@ -404,6 +404,8 @@ WOLFSSL_API int wolfSSL_read(WOLFSSL*, void*, int); WOLFSSL_API int wolfSSL_peek(WOLFSSL*, void*, int); WOLFSSL_API int wolfSSL_accept(WOLFSSL*); #ifdef WOLFSSL_TLS13 +WOLFSSL_API int wolfSSL_send_hrr_cookie(WOLFSSL* ssl, + const unsigned char* secret, unsigned int secretSz); WOLFSSL_API int wolfSSL_CTX_no_ticket_TLSv13(WOLFSSL_CTX* ctx); WOLFSSL_API int wolfSSL_no_ticket_TLSv13(WOLFSSL* ssl); WOLFSSL_API int wolfSSL_CTX_no_dhe_psk(WOLFSSL_CTX* ctx); From 9ca1903ac51bd5ee7aaf9a2c74a2f057c0ee29d1 Mon Sep 17 00:00:00 2001 From: Sean Parkinson Date: Tue, 27 Jun 2017 08:37:55 +1000 Subject: [PATCH 2/3] Change define name for sending HRR Cookie --- examples/server/server.c | 6 +++--- src/internal.c | 2 +- src/tls13.c | 10 +++++----- wolfssl/internal.h | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/examples/server/server.c b/examples/server/server.c index 99827cde5..de3da45a7 100644 --- a/examples/server/server.c +++ b/examples/server/server.c @@ -474,7 +474,7 @@ THREAD_RETURN CYASSL_THREAD server_test(void* args) #ifdef WOLFSSL_EARLY_DATA int earlyData = 0; #endif -#ifdef WOLFSSL_HRR_COOKIE +#ifdef WOLFSSL_SEND_HRR_COOKIE int hrrCookie = 0; #endif @@ -751,7 +751,7 @@ THREAD_RETURN CYASSL_THREAD server_test(void* args) break; case 'J' : - #ifdef WOLFSSL_HRR_COOKIE + #ifdef WOLFSSL_SEND_HRR_COOKIE hrrCookie = 1; #endif break; @@ -1093,7 +1093,7 @@ THREAD_RETURN CYASSL_THREAD server_test(void* args) wolfSSL_KeepArrays(ssl); #endif -#ifdef WOLFSSL_HRR_COOKIE +#ifdef WOLFSSL_SEND_HRR_COOKIE if (hrrCookie && wolfSSL_send_hrr_cookie(ssl, NULL, 0) != SSL_SUCCESS) { err_sys("unable to set use of cookie with HRR msg"); } diff --git a/src/internal.c b/src/internal.c index 8078144f5..c215465f8 100755 --- a/src/internal.c +++ b/src/internal.c @@ -4550,7 +4550,7 @@ void SSL_ResourceFree(WOLFSSL* ssl) ShrinkInputBuffer(ssl, FORCED_FREE); if (ssl->buffers.outputBuffer.dynamicFlag) ShrinkOutputBuffer(ssl); -#if defined(WOLFSSL_HRR_COOKIE) && !defined(NO_WOLFSSL_SERVER) +#if defined(WOLFSSL_SEND_HRR_COOKIE) && !defined(NO_WOLFSSL_SERVER) XFREE(ssl->buffers.tls13CookieSecret.buffer, ssl->heap, DYNAMIC_TYPE_COOKIE_PWD); #endif diff --git a/src/tls13.c b/src/tls13.c index 4bf455e27..a13722f85 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -2376,7 +2376,7 @@ int SendTls13ClientHello(WOLFSSL* ssl) } #ifndef WOLFSSL_TLS13_DRAFT_18 -#ifdef WOLFSSL_HRR_COOKIE +#ifdef WOLFSSL_SEND_HRR_COOKIE /* Create Cookie extension using the hash of the first ClientHello. * * ssl SSL/TLS object. @@ -2455,7 +2455,7 @@ static int RestartHandshakeHash(WOLFSSL* ssl) WOLFSSL_MSG("Restart Hash"); WOLFSSL_BUFFER(hash, hashSz); -#ifdef WOLFSSL_HRR_COOKIE +#ifdef WOLFSSL_SEND_HRR_COOKIE if (ssl->options.sendCookie) { byte cookie[OPAQUE8_LEN + MAX_DIGEST_SIZE + OPAQUE16_LEN * 2]; TLSX* ext; @@ -3116,7 +3116,7 @@ static int DoPreSharedKeys(WOLFSSL* ssl, const byte* input, word32 helloSz, } #endif -#if !defined(WOLFSSL_TLS13_DRAFT_18) && defined(WOLFSSL_HRR_COOKIE) +#if !defined(WOLFSSL_TLS13_DRAFT_18) && defined(WOLFSSL_SEND_HRR_COOKIE) /* Check that the Cookie data's integrity. * * ssl SSL/TLS object. @@ -3387,7 +3387,7 @@ static int DoTls13ClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, if (TLSX_Find(ssl->extensions, TLSX_SUPPORTED_VERSIONS) == NULL) ssl->version.minor = pv.minor; -#ifdef WOLFSSL_HRR_COOKIE +#ifdef WOLFSSL_SEND_HRR_COOKIE if (ssl->options.sendCookie && ssl->options.serverState == SERVER_HELLO_RETRY_REQUEST) { TLSX* ext; @@ -6674,7 +6674,7 @@ int wolfSSL_connect_TLSv13(WOLFSSL* ssl) } } -#if defined(WOLFSSL_HRR_COOKIE) && !defined(NO_WOLFSSL_SERVER) +#if defined(WOLFSSL_SEND_HRR_COOKIE) && !defined(NO_WOLFSSL_SERVER) /* Send a cookie with the HelloRetryRequest to avoid storing state. * * ssl SSL/TLS object. diff --git a/wolfssl/internal.h b/wolfssl/internal.h index befc50d59..50ae73dd4 100755 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -2691,7 +2691,7 @@ typedef struct Buffers { int certChainCnt; #endif #endif -#ifdef WOLFSSL_HRR_COOKIE +#ifdef WOLFSSL_SEND_HRR_COOKIE buffer tls13CookieSecret; /* HRR cookie secret */ #endif #ifdef WOLFSSL_DTLS From 7aee92110b728ffef1f4147656db32a3cc77dc49 Mon Sep 17 00:00:00 2001 From: Sean Parkinson Date: Tue, 27 Jun 2017 08:52:53 +1000 Subject: [PATCH 3/3] Code review fixes Also put in configuration option for sending HRR Cookie extension with state. --- configure.ac | 17 +++++++++++++++++ examples/server/server.c | 3 +++ src/tls13.c | 2 +- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index fe8ebb80a..9666232d7 100644 --- a/configure.ac +++ b/configure.ac @@ -297,6 +297,22 @@ then fi +# Post-handshake Authentication +AC_ARG_ENABLE([hrrcookie], + [AS_HELP_STRING([--enable-hrrcookie],[Enable the server to send Cookie Extension in HRR with state (default: disabled)])], + [ ENABLED_SEND_HRR_COOKIE=$enableval ], + [ ENABLED_SEND_HRR_COOKIE=no ] + ) +if test "$ENABLED_SEND_HRR_COOKIE" = "yes" +then + if test "x$ENABLED_TLS13" = "xno" + then + AC_MSG_ERROR([cannot enable hrrcookie without enabling tls13.]) + fi + AM_CFLAGS="-DWOLFSSL_SEND_HRR_COOKIE $AM_CFLAGS" +fi + + AC_ARG_ENABLE([rng], [AS_HELP_STRING([--enable-rng],[Enable compiling and using RNG (default: enabled)])], [ ENABLED_RNG=$enableval ], @@ -3798,6 +3814,7 @@ echo " * TLS v1.3: $ENABLED_TLS13" echo " * TLS v1.3 Draft 18: $ENABLED_TLS13_DRAFT18" echo " * Post-handshake Auth: $ENABLED_TLS13_POST_AUTH" echo " * Early Data: $ENABLED_TLS13_EARLY_DATA" +echo " * Send State in HRR Cookie: $ENABLED_SEND_HRR_COOKIE" echo " * OCSP: $ENABLED_OCSP" echo " * OCSP Stapling: $ENABLED_CERTIFICATE_STATUS_REQUEST" echo " * OCSP Stapling v2: $ENABLED_CERTIFICATE_STATUS_REQUEST_V2" diff --git a/examples/server/server.c b/examples/server/server.c index de3da45a7..ca19788f8 100644 --- a/examples/server/server.c +++ b/examples/server/server.c @@ -379,6 +379,9 @@ static void Usage(void) #ifdef WOLFSSL_POST_HANDSHAKE_AUTH printf("-Q Request certificate from client post-handshake\n"); #endif +#ifdef WOLFSSL_SEND_HRR_COOKIE + printf("-J Server sends Cookie Extension containing state\n"); +#endif #endif #ifdef WOLFSSL_EARLY_DATA printf("-0 Early data read from client (0-RTT handshake)\n"); diff --git a/src/tls13.c b/src/tls13.c index a13722f85..6ee7f2bcc 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -3155,7 +3155,7 @@ static int CheckCookie(WOLFSSL* ssl, byte* cookie, byte cookieSz) if ((ret = wc_HmacFinal(&cookieHmac, mac)) != 0) return ret; - if (XMEMCMP(cookie + cookieSz, mac, macSz) != 0) + if (ConstantCompare(cookie + cookieSz, mac, macSz) != 0) return HRR_COOKIE_ERROR; return cookieSz; }