diff --git a/src/dtls.c b/src/dtls.c index 542d1585a..326b63d17 100644 --- a/src/dtls.c +++ b/src/dtls.c @@ -73,6 +73,151 @@ void DtlsResetState(WOLFSSL* ssl) sizeof(ssl->keys.peerSeq->prevWindow)); } +#if !defined(NO_WOLFSSL_SERVER) + +#if defined(NO_SHA) && defined(NO_SHA256) +#error "DTLS needs either SHA or SHA-256" +#endif /* NO_SHA && NO_SHA256 */ + +#if !defined(NO_SHA) && defined(NO_SHA256) +#define DTLS_COOKIE_TYPE WC_SHA +#define DTLS_COOKIE_SZ WC_SHA_DIGEST_SIZE +#endif /* !NO_SHA && NO_SHA256 */ + +#ifndef NO_SHA256 +#define DTLS_COOKIE_TYPE WC_SHA256 +#define DTLS_COOKIE_SZ WC_SHA256_DIGEST_SIZE +#endif /* !NO_SHA256 */ + +typedef struct WolfSSL_ConstVector { + word32 size; + const byte* elements; +} WolfSSL_ConstVector; + +typedef struct WolfSSL_CH { + ProtocolVersion* pv; + const byte* random; + WolfSSL_ConstVector sessionId; + WolfSSL_ConstVector cookie; + WolfSSL_ConstVector cipherSuite; + WolfSSL_ConstVector compression; + WolfSSL_ConstVector extension; + word32 length; +} WolfSSL_CH; + +static int ReadVector8(const byte* input, WolfSSL_ConstVector* v) +{ + v->size = *input; + v->elements = input + OPAQUE8_LEN; + return v->size + OPAQUE8_LEN; +} + +static int ReadVector16(const byte* input, WolfSSL_ConstVector* v) +{ + word16 size16; + ato16(input, &size16); + v->size = (word32)size16; + v->elements = input + OPAQUE16_LEN; + return v->size + OPAQUE16_LEN; +} + +static int CreateDtlsCookie(WOLFSSL* ssl, const WolfSSL_CH* ch, byte* cookie) +{ + Hmac cookieHmac; + int ret; + + ret = wc_HmacInit(&cookieHmac, ssl->heap, ssl->devId); + if (ret != 0) + return ret; + ret = wc_HmacSetKey(&cookieHmac, DTLS_COOKIE_TYPE, + ssl->buffers.dtlsCookieSecret.buffer, + ssl->buffers.dtlsCookieSecret.length); + if (ret != 0) + goto out; + ret = wc_HmacUpdate(&cookieHmac, (const byte*)ssl->buffers.dtlsCtx.peer.sa, + ssl->buffers.dtlsCtx.peer.sz); + if (ret != 0) + goto out; + ret = wc_HmacUpdate(&cookieHmac, (byte*)ch->pv, OPAQUE16_LEN); + if (ret != 0) + goto out; + ret = wc_HmacUpdate(&cookieHmac, (byte*)ch->random, RAN_LEN); + if (ret != 0) + goto out; + ret = wc_HmacUpdate(&cookieHmac, (byte*)ch->sessionId.elements, + ch->sessionId.size); + if (ret != 0) + goto out; + ret = wc_HmacUpdate(&cookieHmac, (byte*)ch->cipherSuite.elements, + ch->cipherSuite.size); + if (ret != 0) + goto out; + ret = wc_HmacUpdate(&cookieHmac, (byte*)ch->compression.elements, + ch->compression.size); + if (ret != 0) + goto out; + ret = wc_HmacFinal(&cookieHmac, cookie); + +out: + wc_HmacFree(&cookieHmac); + return ret; +} + +static int ParseClientHello(const byte* input, word32 helloSz, WolfSSL_CH* ch) +{ + word32 idx = 0; + + /* protocol version, random and session id length check */ + if (OPAQUE16_LEN + RAN_LEN + OPAQUE8_LEN > helloSz) + return BUFFER_ERROR; + + ch->pv = (ProtocolVersion*)(input + idx); + idx += OPAQUE16_LEN; + ch->random = (byte*)(input + idx); + idx += RAN_LEN; + idx += ReadVector8(input + idx, &ch->sessionId); + if (idx > helloSz - OPAQUE8_LEN) + return BUFFER_ERROR; + idx += ReadVector8(input + idx, &ch->cookie); + if (idx > helloSz - OPAQUE16_LEN) + return BUFFER_ERROR; + idx += ReadVector16(input + idx, &ch->cipherSuite); + if (idx > helloSz - OPAQUE8_LEN) + return BUFFER_ERROR; + idx += ReadVector8(input + idx, &ch->compression); + if (idx > helloSz - OPAQUE16_LEN) + return BUFFER_ERROR; + idx += ReadVector16(input + idx, &ch->extension); + if (idx > helloSz) + return BUFFER_ERROR; + ch->length = idx; + return 0; +} + +int DoClientHelloStateless(WOLFSSL* ssl, const byte* input, word32* inOutIdx, + word32 helloSz, byte* process) +{ + byte cookie[DTLS_COOKIE_SZ]; + int ret; + WolfSSL_CH ch; + + *process = 1; + ret = ParseClientHello(input + *inOutIdx, helloSz, &ch); + if (ret != 0) + return ret; + ret = CreateDtlsCookie(ssl, &ch, cookie); + if (ret != 0) + return ret; + if (ch.cookie.size != DTLS_COOKIE_SZ || + XMEMCMP(ch.cookie.elements, cookie, DTLS_COOKIE_SZ) != 0) { + *process = 0; + ret = SendHelloVerifyRequest(ssl, cookie, DTLS_COOKIE_SZ); + } + + return ret; +} +#endif /* !defined(NO_WOLFSSL_SERVER) */ + #if defined(WOLFSSL_DTLS_CID) typedef struct ConnectionID { @@ -426,5 +571,4 @@ int wolfSSL_dtls_cid_get_tx(WOLFSSL* ssl, unsigned char* buf, } #endif /* WOLFSSL_DTLS_CID */ - #endif /* WOLFSSL_DTLS */ diff --git a/src/internal.c b/src/internal.c index 55dc9b270..a45afa0d9 100644 --- a/src/internal.c +++ b/src/internal.c @@ -65,13 +65,6 @@ * may be received by a client. To support detecting this, peek will * return WOLFSSL_ERROR_WANT_READ. * This define turns off this behaviour. - * WOLFSSL_DTLS_NO_HVR_ON_RESUME - * If defined, a DTLS server will not do a cookie exchange on successful - * client resumption: the resumption will be faster (one RTT less) and - * will consume less bandwidth (one ClientHello and one HelloVerifyRequest - * less). On the other hand, if a valid SessionID is collected, forged - * clientHello messages will consume resources on the server. - * This define is turned off by default. * WOLFSSL_HOSTNAME_VERIFY_ALT_NAME_ONLY * Verify hostname/ip address using alternate name (SAN) only and do not * use the common name. Forces use of the alternate name, so certificates @@ -178,11 +171,6 @@ WOLFSSL_CALLBACKS needs LARGE_STATIC_BUFFERS, please add LARGE_STATIC_BUFFERS static int DoCertificateVerify(WOLFSSL* ssl, byte* input, word32* inOutIdx, word32 size); #endif - #ifdef WOLFSSL_DTLS - static int SendHelloVerifyRequest(WOLFSSL* ssl, - const byte* cookie, byte cookieSz); - #endif /* WOLFSSL_DTLS */ - #endif /* !NO_WOLFSSL_SERVER */ #endif /* !WOLFSSL_NO_TLS12 */ @@ -32921,17 +32909,6 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx, int ret = 0; byte lesserVersion; -#ifdef WOLFSSL_DTLS - Hmac cookieHmac; - byte newCookie[MAX_COOKIE_LEN]; - byte peerCookie[MAX_COOKIE_LEN]; - byte peerCookieSz = 0; - byte cookieType; - byte cookieSz = 0; - - XMEMSET(&cookieHmac, 0, sizeof(Hmac)); -#endif /* WOLFSSL_DTLS */ - WOLFSSL_START(WC_FUNC_CLIENT_HELLO_DO); WOLFSSL_ENTER("DoClientHello"); @@ -32939,6 +32916,21 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx, if (ssl->hsInfoOn) AddPacketName(ssl, "ClientHello"); if (ssl->toInfoOn) AddLateName("ClientHello", &ssl->timeoutInfo); #endif + /* do not change state in the SSL object before the next region of code + * to be able to statelessly compute a DTLS cookie */ +#ifdef WOLFSSL_DTLS + if (IsDtlsNotSctpMode(ssl) && IsDtlsNotSrtpMode(ssl) && !IsSCR(ssl)) { + byte process = 0; + ret = DoClientHelloStateless(ssl, input, inOutIdx, helloSz, + &process); + if (ret != 0 || !process) { + *inOutIdx += helloSz; + DtlsResetState(ssl); + return ret; + } + } +#endif /* WOLFSSL_DTLS */ + /* protocol version, random and session id length check */ if (OPAQUE16_LEN + RAN_LEN + OPAQUE8_LEN > helloSz) return BUFFER_ERROR; @@ -32960,29 +32952,6 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx, * Client Hello. */ ssl->keys.dtls_handshake_number = ssl->keys.dtls_peer_handshake_number; - - #if defined(NO_SHA) && defined(NO_SHA256) - #error "DTLS needs either SHA or SHA-256" - #endif /* NO_SHA && NO_SHA256 */ - - #if !defined(NO_SHA) && defined(NO_SHA256) - cookieType = WC_SHA; - cookieSz = WC_SHA_DIGEST_SIZE; - #endif /* NO_SHA */ - #ifndef NO_SHA256 - cookieType = WC_SHA256; - cookieSz = WC_SHA256_DIGEST_SIZE; - #endif /* NO_SHA256 */ - ret = wc_HmacSetKey(&cookieHmac, cookieType, - ssl->buffers.dtlsCookieSecret.buffer, - ssl->buffers.dtlsCookieSecret.length); - if (ret != 0) goto out; - ret = wc_HmacUpdate(&cookieHmac, - (const byte*)ssl->buffers.dtlsCtx.peer.sa, - ssl->buffers.dtlsCtx.peer.sz); - if (ret != 0) goto out; - ret = wc_HmacUpdate(&cookieHmac, input + i, OPAQUE16_LEN); - if (ret != 0) goto out; } #endif /* WOLFSSL_DTLS */ i += OPAQUE16_LEN; @@ -33152,12 +33121,6 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx, /* random */ XMEMCPY(ssl->arrays->clientRandom, input + i, RAN_LEN); -#ifdef WOLFSSL_DTLS - if (IsDtlsNotSctpMode(ssl) && IsDtlsNotSrtpMode(ssl) && !IsSCR(ssl)) { - ret = wc_HmacUpdate(&cookieHmac, input + i, RAN_LEN); - if (ret != 0) goto out; - } -#endif /* WOLFSSL_DTLS */ i += RAN_LEN; #ifdef SHOW_SECRETS @@ -33187,13 +33150,6 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx, } XMEMCPY(ssl->arrays->sessionID, input + i, b); -#ifdef WOLFSSL_DTLS - if (IsDtlsNotSctpMode(ssl) && IsDtlsNotSrtpMode(ssl) && - !IsSCR(ssl)) { - ret = wc_HmacUpdate(&cookieHmac, input + i - 1, b + 1); - if (ret != 0) goto out; - } -#endif /* WOLFSSL_DTLS */ ssl->arrays->sessionIDSz = b; i += b; ssl->options.resuming = 1; /* client wants to resume */ @@ -33205,10 +33161,10 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx, goto out; } - #ifdef WOLFSSL_DTLS +#ifdef WOLFSSL_DTLS /* cookie */ if (ssl->options.dtls) { - + word8 peerCookieSz; if ((i - begin) + OPAQUE8_LEN > helloSz) { ret = BUFFER_ERROR; goto out; @@ -33227,12 +33183,10 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx, goto out; } - XMEMCPY(peerCookie, input + i, peerCookieSz); - i += peerCookieSz; } } - #endif +#endif /* WOLFSSL_DTLS */ /* suites */ if ((i - begin) + OPAQUE16_LEN > helloSz) { @@ -33302,14 +33256,6 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx, } #endif -#ifdef WOLFSSL_DTLS - if (IsDtlsNotSctpMode(ssl) && IsDtlsNotSrtpMode(ssl) && !IsSCR(ssl)) { - ret = wc_HmacUpdate(&cookieHmac, - input + i - OPAQUE16_LEN, - clSuites->suiteSz + OPAQUE16_LEN); - if (ret != 0) goto out; - } -#endif /* WOLFSSL_DTLS */ i += clSuites->suiteSz; clSuites->hashSigAlgoSz = 0; @@ -33330,38 +33276,6 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx, goto out; } -#ifdef WOLFSSL_DTLS - if (IsDtlsNotSctpMode(ssl) && IsDtlsNotSrtpMode(ssl) && !IsSCR(ssl)) { - - ret = wc_HmacUpdate(&cookieHmac, input + i - 1, b + 1); - if (ret != 0) goto out; - ret = wc_HmacFinal(&cookieHmac, newCookie); - if (ret != 0) goto out; - - /* If a cookie callback is set, call it to overwrite the cookie. - * This should be deprecated. The code now calculates the cookie - * using an HMAC as expected. */ - if (ssl->ctx->CBIOCookie != NULL && - ssl->ctx->CBIOCookie(ssl, newCookie, cookieSz, - ssl->IOCB_CookieCtx) != cookieSz) { - ret = COOKIE_ERROR; - goto out; - } - - -#ifndef WOLFSSL_DTLS_NO_HVR_ON_RESUME - if (peerCookieSz != cookieSz || - XMEMCMP(peerCookie, newCookie, cookieSz) != 0) { - *inOutIdx += helloSz; - ret = SendHelloVerifyRequest(ssl, newCookie, cookieSz); - goto out; - } -#endif /* !WOLFSSL_DTLS_NO_HVR_ON_RESUME */ - - - } -#endif /* WOLFSSL_DTLS */ - { /* compression match types */ int matchNo = 0; @@ -33546,20 +33460,6 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx, } } -#if defined(WOLFSSL_DTLS) && defined(WOLFSSL_DTLS_NO_HVR_ON_RESUME) - if (IsDtlsNotSctpMode(ssl) && IsDtlsNotSrtpMode(ssl) && !IsSCR(ssl)) { - - if (!ssl->options.resuming) { - /* resume failed, check the cookie */ - if (peerCookieSz != cookieSz || - XMEMCMP(peerCookie, newCookie, cookieSz) != 0) { - *inOutIdx = begin + helloSz; - ret = SendHelloVerifyRequest(ssl, newCookie, cookieSz); - goto out; - } - } - } -#endif /* WOLFSSL_DTLS && WOLFSSL_DTLS_NO_HVR_ON_RESUME */ #if defined(HAVE_TLS_EXTENSIONS) && defined(HAVE_DH_DEFAULT_PARAMS) #if defined(HAVE_FFDHE) && defined(HAVE_SUPPORTED_CURVES) @@ -33604,9 +33504,6 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx, out: -#ifdef WOLFSSL_DTLS - wc_HmacFree(&cookieHmac); -#endif #ifdef WOLFSSL_SMALL_STACK if (clSuites != NULL) XFREE(clSuites, ssl->heap, DYNAMIC_TYPE_SUITES); @@ -35210,8 +35107,8 @@ static int DefTicketEncCb(WOLFSSL* ssl, byte key_name[WOLFSSL_TICKET_NAME_SZ], #ifdef WOLFSSL_DTLS /* handle generation of DTLS hello_verify_request (3) */ - static int SendHelloVerifyRequest(WOLFSSL* ssl, - const byte* cookie, byte cookieSz) + int SendHelloVerifyRequest(WOLFSSL* ssl, + const byte* cookie, byte cookieSz) { byte* output; int length = VERSION_SZ + ENUM_LEN + cookieSz; diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 3ee22f472..ce8ceac69 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -5683,6 +5683,13 @@ WOLFSSL_LOCAL int cipherExtraData(WOLFSSL* ssl); word32 *inOutIdx, byte *type, word32 *size, word32 *fragOffset, word32 *fragSz, word32 totalSz); WOLFSSL_LOCAL int DtlsMsgDrain(WOLFSSL *ssl); + WOLFSSL_LOCAL int SendHelloVerifyRequest(WOLFSSL* ssl, + const byte* cookie, byte cookieSz); + +#if !defined(NO_WOLFSSL_SERVER) + WOLFSSL_LOCAL int DoClientHelloStateless(WOLFSSL* ssl, const byte* input, + word32* inOutIdx, word32 helloSz, byte *process); +#endif /* !defined(NO_WOLFSSL_SERVER) */ #endif /* WOLFSSL_DTLS */ #if defined(HAVE_SECURE_RENEGOTIATION) && defined(WOLFSSL_DTLS)