Browse Source

DTLS 1.3: allow the server to operate without maintaining state

Juliusz Sosinowicz 1 year ago
parent
commit
c6aa4fc526
5 changed files with 137 additions and 26 deletions
  1. 12 0
      src/dtls13.c
  2. 23 0
      src/internal.c
  3. 10 2
      src/tls.c
  4. 91 24
      src/tls13.c
  5. 1 0
      wolfcrypt/src/kdf.c

+ 12 - 0
src/dtls13.c

@@ -1388,6 +1388,18 @@ static int _Dtls13HandshakeRecv(WOLFSSL* ssl, byte* input, word32 size,
     if (frag_off + frag_length > message_length)
         return BUFFER_ERROR;
 
+    if (handshake_type == client_hello &&
+            /* Only when receiving an unverified ClientHello */
+            ssl->options.serverState < SERVER_HELLO_COMPLETE) {
+        /* To be able to operate in stateless mode, we assume the ClientHello
+         * is in order and we use its Handshake Message number and Sequence
+         * Number for our Tx. */
+        ssl->keys.dtls_expected_peer_handshake_number =
+                ssl->keys.dtls_handshake_number =
+                        ssl->keys.dtls_peer_handshake_number;
+        ssl->dtls13Epochs[0].nextSeqNumber = ssl->keys.curSeq;
+    }
+
     ret = Dtls13RtxMsgRecvd(ssl, (enum HandShakeType)handshake_type, frag_off);
     if (ret != 0)
         return ret;

+ 23 - 0
src/internal.c

@@ -8788,6 +8788,14 @@ static int EdDSA_Update(WOLFSSL* ssl, const byte* data, int sz)
 int HashRaw(WOLFSSL* ssl, const byte* data, int sz)
 {
     int ret = 0;
+#ifdef WOLFSSL_DEBUG_TLS
+    byte digest[WC_MAX_DIGEST_SIZE];
+
+    WOLFSSL_MSG("HashRaw:");
+    WOLFSSL_MSG("Data:");
+    WOLFSSL_BUFFER(data, sz);
+    WOLFSSL_MSG("Hashes:");
+#endif
 
     (void)data;
     (void)sz;
@@ -8817,16 +8825,31 @@ int HashRaw(WOLFSSL* ssl, const byte* data, int sz)
         ret = wc_Sha256Update(&ssl->hsHashes->hashSha256, data, sz);
         if (ret != 0)
             return ret;
+    #ifdef WOLFSSL_DEBUG_TLS
+        WOLFSSL_MSG("Sha256");
+        wc_Sha256GetHash(&ssl->hsHashes->hashSha256, digest);
+        WOLFSSL_BUFFER(digest, WC_SHA224_DIGEST_SIZE);
+    #endif
     #endif
     #ifdef WOLFSSL_SHA384
         ret = wc_Sha384Update(&ssl->hsHashes->hashSha384, data, sz);
         if (ret != 0)
             return ret;
+    #ifdef WOLFSSL_DEBUG_TLS
+        WOLFSSL_MSG("Sha384");
+        wc_Sha384GetHash(&ssl->hsHashes->hashSha384, digest);
+        WOLFSSL_BUFFER(digest, WC_SHA384_DIGEST_SIZE);
+    #endif
     #endif
     #ifdef WOLFSSL_SHA512
         ret = wc_Sha512Update(&ssl->hsHashes->hashSha512, data, sz);
         if (ret != 0)
             return ret;
+    #ifdef WOLFSSL_DEBUG_TLS
+        WOLFSSL_MSG("Sha512");
+        wc_Sha512GetHash(&ssl->hsHashes->hashSha512, digest);
+        WOLFSSL_BUFFER(digest, WC_SHA512_DIGEST_SIZE);
+    #endif
     #endif
     #if !defined(WOLFSSL_NO_CLIENT_AUTH) && \
                ((defined(HAVE_ED25519) && !defined(NO_ED25519_CLIENT_AUTH)) || \

+ 10 - 2
src/tls.c

@@ -6167,8 +6167,16 @@ static int TLSX_Cookie_Parse(WOLFSSL* ssl, const byte* input, word16 length,
 
     /* client_hello */
     extension = TLSX_Find(ssl->extensions, TLSX_COOKIE);
-    if (extension == NULL)
-        return HRR_COOKIE_ERROR;
+    if (extension == NULL) {
+#ifdef WOLFSSL_DTLS13
+        if (ssl->options.dtls)
+            /* TODO: Should we allow a ClientHello with a valid cookie even if
+             *       the cookie wasn't sent by this WOLFSSL object? */
+            return TLSX_Cookie_Use(ssl, input + idx, len, NULL, 0, 0);
+        else
+#endif
+            return HRR_COOKIE_ERROR;
+    }
 
     cookie = (Cookie*)extension->data;
     if (cookie->len != len || XMEMCMP(&cookie->data, input + idx, len) != 0)

+ 91 - 24
src/tls13.c

@@ -2889,7 +2889,7 @@ int RestartHandshakeHash(WOLFSSL* ssl)
 #endif
 
 #if defined(WOLFSSL_SEND_HRR_COOKIE) && !defined(NO_WOLFSSL_SERVER)
-    if (ssl->options.sendCookie) {
+    if (ssl->options.sendCookie && ssl->options.side == WOLFSSL_SERVER_END) {
         byte   cookie[OPAQUE8_LEN + WC_MAX_DIGEST_SIZE + OPAQUE16_LEN * 2];
         TLSX*  ext;
         word32 idx = 0;
@@ -4833,6 +4833,10 @@ static int RestartHandshakeHashWithCookie(WOLFSSL* ssl, Cookie* cookie)
         return ret;
     if ((ret = HashRaw(ssl, header, sizeof(header))) != 0)
         return ret;
+#ifdef WOLFSSL_DEBUG_TLS
+    WOLFSSL_MSG("Restart Hash from Cookie");
+    WOLFSSL_BUFFER(cookieData + idx, hashSz);
+#endif
     if ((ret = HashRaw(ssl, cookieData + idx, hashSz)) != 0)
         return ret;
 
@@ -5282,20 +5286,45 @@ int DoTls13ClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
 
 #if defined(WOLFSSL_SEND_HRR_COOKIE)
     if (ssl->options.sendCookie &&
-              ssl->options.serverState == SERVER_HELLO_RETRY_REQUEST_COMPLETE) {
-        TLSX* ext;
-
-        if ((ext = TLSX_Find(ssl->extensions, TLSX_COOKIE)) == NULL)
-            ERROR_OUT(HRR_COOKIE_ERROR, exit_dch);
-
-        /* Ensure the cookie came from client and isn't the one in the
-        * response - HelloRetryRequest.
-        */
-        if (ext->resp == 1)
-            ERROR_OUT(HRR_COOKIE_ERROR, exit_dch);
-        ret = RestartHandshakeHashWithCookie(ssl, (Cookie*)ext->data);
-        if (ret != 0)
-            goto exit_dch;
+            (ssl->options.serverState == SERVER_HELLO_RETRY_REQUEST_COMPLETE
+#ifdef WOLFSSL_DTLS13
+                    /* Always check for a valid cookie since we may have already
+                     * sent a HRR but we reset the state. */
+                    || ssl->options.dtls
+#endif
+                    )) {
+        TLSX* ext = TLSX_Find(ssl->extensions, TLSX_COOKIE);
+
+        if (ext != NULL) {
+            /* Ensure the cookie came from client and isn't the one in the
+            * response - HelloRetryRequest.
+            */
+            if (ext->resp == 0) {
+                ret = RestartHandshakeHashWithCookie(ssl, (Cookie*)ext->data);
+#ifdef WOLFSSL_DTLS13
+                /* Send a new cookie request */
+                if (ret == HRR_COOKIE_ERROR && ssl->options.dtls)
+                    ssl->options.serverState = NULL_STATE;
+                else
+#endif
+                if (ret != 0)
+                    goto exit_dch;
+                ssl->options.serverState = SERVER_HELLO_COMPLETE;
+            }
+            else {
+#ifdef WOLFSSL_DTLS13
+                if (ssl->options.dtls)
+                    ssl->options.serverState = NULL_STATE;
+                else
+#endif
+                    ERROR_OUT(HRR_COOKIE_ERROR, exit_dch);
+            }
+        }
+        else
+#ifdef WOLFSSL_DTLS13
+            if (!ssl->options.dtls)
+#endif
+                ERROR_OUT(HRR_COOKIE_ERROR, exit_dch);
     }
 #endif
 
@@ -5424,9 +5453,9 @@ int DoTls13ClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
     /* We are using DTLSv13 and set the HRR cookie secret, use the cookie to
        perform a return-routability check. */
     if (ret == 0 && ssl->options.dtls && ssl->options.sendCookie &&
-        ssl->options.serverState != SERVER_HELLO_RETRY_REQUEST_COMPLETE) {
+        ssl->options.serverState < SERVER_HELLO_RETRY_REQUEST_COMPLETE) {
 
-        /* ssl->options.serverState != SERVER_HELLO_RETRY_REQUEST_COMPLETE
+        /* ssl->options.serverState < SERVER_HELLO_RETRY_REQUEST_COMPLETE
            so the client already provided a good KeyShareEntry. In this case
            we don't add the KEY_SHARE extension to the HelloRetryRequest or
            in the Cookie. The RFC8446 forbids to select a supported group
@@ -5556,18 +5585,27 @@ int SendTls13ServerHello(WOLFSSL* ssl, byte extMsgType)
     if (ret != 0)
         return ret;
 
-
-#ifdef WOLFSSL_DTLS13
-    if (ssl->options.dtls) {
-        ret = Dtls13HashHandshake(ssl,
-                                  output + Dtls13GetRlHeaderLength(0) ,
-                                  sendSz - Dtls13GetRlHeaderLength(0));
+#ifdef WOLFSSL_SEND_HRR_COOKIE
+    if (ssl->options.sendCookie && extMsgType == hello_retry_request) {
+        /* Reset the hashes from here. We will be able to restart the hashes
+         * from the cookie in RestartHandshakeHashWithCookie */
+        ret = InitHandshakeHashes(ssl);
     }
     else
+#endif
+    {
+#ifdef WOLFSSL_DTLS13
+        if (ssl->options.dtls) {
+            ret = Dtls13HashHandshake(ssl,
+                                      output + Dtls13GetRlHeaderLength(0) ,
+                                      sendSz - Dtls13GetRlHeaderLength(0));
+        }
+        else
 #endif /* WOLFSSL_DTLS13 */
         {
             ret = HashOutput(ssl, output, sendSz, 0);
         }
+    }
 
     if (ret != 0)
         return ret;
@@ -5598,7 +5636,6 @@ int SendTls13ServerHello(WOLFSSL* ssl, byte extMsgType)
     ssl->buffers.outputBuffer.length += sendSz;
 
     if (!ssl->options.groupMessages || extMsgType != server_hello)
-
         ret = SendBuffered(ssl);
 
     WOLFSSL_LEAVE("SendTls13ServerHello", ret);
@@ -10754,6 +10791,36 @@ int wolfSSL_accept_TLSv13(WOLFSSL* ssl)
                     WOLFSSL_ERROR(ssl->error);
                     return WOLFSSL_FATAL_ERROR;
                 }
+#ifdef WOLFSSL_DTLS13
+                if (ssl->options.dtls && wolfSSL_dtls_get_using_nonblock(ssl)) {
+                    /* Reset the state so that we can statelessly await the
+                     * ClientHello that contains the cookie. Return a WANT_READ
+                     * to the user so that we don't drop UDP messages in the
+                     * network callbacks. */
+
+                    /* Reset DTLS window */
+                    w64Zero(&ssl->dtls13Epochs[0].nextSeqNumber);
+                    w64Zero(&ssl->dtls13Epochs[0].nextPeerSeqNumber);
+                    XMEMSET(ssl->dtls13Epochs[0].window, 0,
+                            sizeof(ssl->dtls13Epochs[0].window));
+
+                    ssl->keys.dtls_expected_peer_handshake_number = 0;
+                    ssl->keys.dtls_handshake_number = 0;
+
+                    ssl->msgsReceived.got_client_hello = 0;
+
+                    /* Reset states */
+                    ssl->options.serverState = NULL_STATE;
+                    ssl->options.clientState = NULL_STATE;
+                    ssl->options.connectState = CONNECT_BEGIN;
+                    ssl->options.acceptState  = ACCEPT_BEGIN;
+                    ssl->options.handShakeState  = NULL_STATE;
+
+                    ssl->error = WANT_READ;
+                    WOLFSSL_ERROR(ssl->error);
+                    return WOLFSSL_FATAL_ERROR;
+                }
+#endif /* WOLFSSL_DTLS13 */
             }
 
             ssl->options.acceptState = TLS13_ACCEPT_HELLO_RETRY_REQUEST_DONE;

+ 1 - 0
wolfcrypt/src/kdf.c

@@ -463,6 +463,7 @@ int wc_PRF_TLS(byte* digest, word32 digLen, const byte* secret, word32 secLen,
         WOLFSSL_BUFFER(prk, prkLen);
         WOLFSSL_MSG("  Info");
         WOLFSSL_BUFFER(data, idx);
+        WOLFSSL_MSG_EX("  Digest %d", digest);
 #endif
 
         ret = wc_HKDF_Expand(digest, prk, prkLen, data, idx, okm, okmLen);