]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
QUIC DHS: (Server support) Add server state machine for DHS
authorHugo Landau <hlandau@openssl.org>
Tue, 22 Nov 2022 13:26:51 +0000 (13:26 +0000)
committerHugo Landau <hlandau@openssl.org>
Thu, 19 Jan 2023 13:17:39 +0000 (13:17 +0000)
Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/19734)

ssl/quic/quic_dummy_handshake.c

index f3e5bcee2b2fe4ca2ddd4ffeb2254ebb7b7b2bb2..9c37db98b25e6767c3fa156eedf477cc713b4e21 100644 (file)
 #define QUIC_DHS_MSG_TYPE_CERT_VERIFY   0x0F
 #define QUIC_DHS_MSG_TYPE_FINISHED      0x14
 
-#define QUIC_DHS_STATE_INITIAL              0
-#define QUIC_DHS_STATE_SENT_CH              1
-#define QUIC_DHS_STATE_RECEIVED_SH          2
-#define QUIC_DHS_STATE_RECEIVED_EE_HDR      8
-#define QUIC_DHS_STATE_RECEIVED_EE          3
-#define QUIC_DHS_STATE_RECEIVED_CERT        4
-#define QUIC_DHS_STATE_RECEIVED_CERT_VERIFY 5
-#define QUIC_DHS_STATE_RECEIVED_FINISHED    6
-#define QUIC_DHS_STATE_SENT_FINISHED        7
-
-#define QUIC_DHS_STATE_ERROR        0xFF
+#define QUIC_DHS_CLIENT_STATE_INITIAL               0x00
+#define QUIC_DHS_CLIENT_STATE_SENT_CH               0x01
+#define QUIC_DHS_CLIENT_STATE_RECEIVED_SH           0x02
+#define QUIC_DHS_CLIENT_STATE_RECEIVED_EE_HDR       0x08
+#define QUIC_DHS_CLIENT_STATE_RECEIVED_EE           0x03
+#define QUIC_DHS_CLIENT_STATE_RECEIVED_CERT         0x04
+#define QUIC_DHS_CLIENT_STATE_RECEIVED_CERT_VERIFY  0x05
+#define QUIC_DHS_CLIENT_STATE_RECEIVED_FINISHED     0x06
+#define QUIC_DHS_CLIENT_STATE_SENT_FINISHED         0x07
+#define QUIC_DHS_CLIENT_STATE_ERROR                 0xFF
+
+#define QUIC_DHS_SERVER_STATE_INITIAL               0x00
+#define QUIC_DHS_SERVER_STATE_RECEIVED_CH_HDR       0x01
+#define QUIC_DHS_SERVER_STATE_RECEIVED_CH           0x02
+#define QUIC_DHS_SERVER_STATE_SENT_SH               0x03
+#define QUIC_DHS_SERVER_STATE_SENT_EE               0x04
+#define QUIC_DHS_SERVER_STATE_SENT_CERT             0x05
+#define QUIC_DHS_SERVER_STATE_SENT_CERT_VERIFY      0x06
+#define QUIC_DHS_SERVER_STATE_SENT_FINISHED         0x07
+#define QUIC_DHS_SERVER_STATE_RECEIVED_FINISHED     0x08
+#define QUIC_DHS_SERVER_STATE_ERROR                 0xFF
 
 struct quic_dhs_st {
     QUIC_DHS_ARGS   args;
@@ -39,7 +49,7 @@ struct quic_dhs_st {
     size_t          local_transport_params_len;
     unsigned char   rx_hdr[4];
     size_t          rx_hdr_bytes_read;
-    size_t          rx_ee_bytes_read;
+    size_t          rx_bytes_read;
 };
 
 QUIC_DHS *ossl_quic_dhs_new(const QUIC_DHS_ARGS *args)
@@ -54,8 +64,7 @@ QUIC_DHS *ossl_quic_dhs_new(const QUIC_DHS_ARGS *args)
     if (dhs == NULL)
         return NULL;
 
-    dhs->args   = *args;
-    dhs->state  = QUIC_DHS_STATE_INITIAL;
+    dhs->args = *args;
     return dhs;
 }
 
@@ -68,6 +77,7 @@ void ossl_quic_dhs_free(QUIC_DHS *dhs)
     OPENSSL_free(dhs);
 }
 
+/* Send a handshake message. */
 static int dhs_send(QUIC_DHS *dhs, unsigned char type,
                     const void *buf, size_t buf_len)
 {
@@ -97,6 +107,11 @@ static int dhs_send(QUIC_DHS *dhs, unsigned char type,
     return 1;
 }
 
+/*
+ * Receive header of a handshake message. Length of body is written to
+ * *frame_len, and if non-zero this call should be followed by one or more calls
+ * to dhs_recv_body.
+ */
 static int dhs_recv_sof(QUIC_DHS *dhs, uint32_t *type, size_t *frame_len)
 {
     size_t bytes_read = 0;
@@ -130,6 +145,7 @@ static int dhs_recv_sof(QUIC_DHS *dhs, uint32_t *type, size_t *frame_len)
     return 1;
 }
 
+/* Receive the body of an incoming message, or part thereof. */
 static int dhs_recv_body(QUIC_DHS *dhs, unsigned char *buf, size_t buf_len,
                          size_t *bytes_read)
 {
@@ -151,7 +167,7 @@ static const unsigned char default_1rtt_write[32] = {43, 1};
 int ossl_quic_dhs_set_transport_params(QUIC_DHS *dhs, const unsigned char *transport_params,
                                        size_t transport_params_len)
 {
-    if (!dhs->args.is_server && dhs->state != QUIC_DHS_STATE_INITIAL)
+    if (!dhs->args.is_server && dhs->state != QUIC_DHS_CLIENT_STATE_INITIAL)
         return 0;
 
     dhs->local_transport_params       = transport_params;
@@ -159,7 +175,8 @@ int ossl_quic_dhs_set_transport_params(QUIC_DHS *dhs, const unsigned char *trans
     return 1;
 }
 
-int ossl_quic_dhs_tick(QUIC_DHS *dhs)
+/* Tick implementation for the client role. */
+static int dhs_tick_client(QUIC_DHS *dhs)
 {
     int ret;
     uint32_t type;
@@ -167,21 +184,21 @@ int ossl_quic_dhs_tick(QUIC_DHS *dhs)
 
     for (;;) {
         switch (dhs->state) {
-            case QUIC_DHS_STATE_INITIAL:
+            case QUIC_DHS_CLIENT_STATE_INITIAL:
                 /* We need to send a CH */
                 if (!dhs_send(dhs, QUIC_DHS_MSG_TYPE_CH,
                               dhs->local_transport_params,
                               dhs->local_transport_params_len))
                     return 0;
 
-                dhs->state = QUIC_DHS_STATE_SENT_CH;
+                dhs->state = QUIC_DHS_CLIENT_STATE_SENT_CH;
                 break;
 
-            case QUIC_DHS_STATE_SENT_CH:
+            case QUIC_DHS_CLIENT_STATE_SENT_CH:
                 ret = dhs_recv_sof(dhs, &type, &frame_len);
                 if (ret == 1) {
                     if (type == QUIC_DHS_MSG_TYPE_SH && frame_len == 0) {
-                        dhs->state = QUIC_DHS_STATE_RECEIVED_SH;
+                        dhs->state = QUIC_DHS_CLIENT_STATE_RECEIVED_SH;
 
                         if (!dhs->args.yield_secret_cb(QUIC_ENC_LEVEL_HANDSHAKE,
                                                        /*TX=*/0,
@@ -189,8 +206,10 @@ int ossl_quic_dhs_tick(QUIC_DHS *dhs)
                                                        NULL,
                                                        default_handshake_read,
                                                        sizeof(default_handshake_read),
-                                                       dhs->args.yield_secret_cb_arg))
+                                                       dhs->args.yield_secret_cb_arg)) {
+                            dhs->state = QUIC_DHS_CLIENT_STATE_ERROR;
                             return 0;
+                        }
 
                         if (!dhs->args.yield_secret_cb(QUIC_ENC_LEVEL_HANDSHAKE,
                                                        /*TX=*/1,
@@ -198,10 +217,13 @@ int ossl_quic_dhs_tick(QUIC_DHS *dhs)
                                                        NULL,
                                                        default_handshake_write,
                                                        sizeof(default_handshake_write),
-                                                       dhs->args.yield_secret_cb_arg))
+                                                       dhs->args.yield_secret_cb_arg)) {
+                            dhs->state = QUIC_DHS_CLIENT_STATE_ERROR;
                             return 0;
+                        }
 
                     } else {
+                        dhs->state = QUIC_DHS_CLIENT_STATE_ERROR;
                         return 0; /* error state, unexpected type */
                     }
                 } else if (ret == 2) {
@@ -211,18 +233,19 @@ int ossl_quic_dhs_tick(QUIC_DHS *dhs)
                 }
                 break;
 
-            case QUIC_DHS_STATE_RECEIVED_SH:
+            case QUIC_DHS_CLIENT_STATE_RECEIVED_SH:
                 ret = dhs_recv_sof(dhs, &type, &frame_len);
                 if (ret == 1) {
                     if (type == QUIC_DHS_MSG_TYPE_EE) {
-                        dhs->state = QUIC_DHS_STATE_RECEIVED_EE_HDR;
-                        dhs->rx_ee_bytes_read               = 0;
+                        dhs->state = QUIC_DHS_CLIENT_STATE_RECEIVED_EE_HDR;
+                        dhs->rx_bytes_read               = 0;
                         dhs->remote_transport_params_len    = frame_len;
                         dhs->remote_transport_params
                             = OPENSSL_malloc(dhs->remote_transport_params_len);
                         if (dhs->remote_transport_params == NULL)
                             return 0;
                     } else {
+                        dhs->state = QUIC_DHS_CLIENT_STATE_ERROR;
                         return 0; /* error state, unexpected type */
                     }
                 } else if (ret == 2) {
@@ -232,19 +255,21 @@ int ossl_quic_dhs_tick(QUIC_DHS *dhs)
                 }
                 break;
 
-            case QUIC_DHS_STATE_RECEIVED_EE_HDR:
-                ret = dhs_recv_body(dhs, dhs->remote_transport_params + dhs->rx_ee_bytes_read,
-                                    dhs->remote_transport_params_len - dhs->rx_ee_bytes_read,
+            case QUIC_DHS_CLIENT_STATE_RECEIVED_EE_HDR:
+                ret = dhs_recv_body(dhs, dhs->remote_transport_params + dhs->rx_bytes_read,
+                                    dhs->remote_transport_params_len - dhs->rx_bytes_read,
                                     &bytes_read);
                 if (ret == 1) {
-                    dhs->rx_ee_bytes_read += bytes_read;
+                    dhs->rx_bytes_read += bytes_read;
                     if (bytes_read == dhs->remote_transport_params_len) {
                         if (!dhs->args.got_transport_params_cb(dhs->remote_transport_params,
                                                                dhs->remote_transport_params_len,
-                                                               dhs->args.got_transport_params_cb_arg))
+                                                               dhs->args.got_transport_params_cb_arg)) {
+                            dhs->state = QUIC_DHS_CLIENT_STATE_ERROR;
                             return 0;
+                        }
 
-                        dhs->state = QUIC_DHS_STATE_RECEIVED_EE;
+                        dhs->state = QUIC_DHS_CLIENT_STATE_RECEIVED_EE;
                     }
                 } else if (ret == 2) {
                     return 1; /* no more data yet, not an error */
@@ -253,14 +278,16 @@ int ossl_quic_dhs_tick(QUIC_DHS *dhs)
                 }
                 break;
 
-            case QUIC_DHS_STATE_RECEIVED_EE:
+            case QUIC_DHS_CLIENT_STATE_RECEIVED_EE:
                 /* Expect Cert */
                 ret = dhs_recv_sof(dhs, &type, &frame_len);
                 if (ret == 1) {
-                    if (type == QUIC_DHS_MSG_TYPE_CERT && frame_len == 0)
-                        dhs->state = QUIC_DHS_STATE_RECEIVED_CERT;
-                    else
+                    if (type == QUIC_DHS_MSG_TYPE_CERT && frame_len == 0) {
+                        dhs->state = QUIC_DHS_CLIENT_STATE_RECEIVED_CERT;
+                    } else {
+                        dhs->state = QUIC_DHS_CLIENT_STATE_ERROR;
                         return 0; /* error state, unexpected type */
+                    }
                 } else if (ret == 2) {
                     return 1; /* no more data yet, not an error */
                 } else {
@@ -268,14 +295,16 @@ int ossl_quic_dhs_tick(QUIC_DHS *dhs)
                 }
                 break;
 
-            case QUIC_DHS_STATE_RECEIVED_CERT:
+            case QUIC_DHS_CLIENT_STATE_RECEIVED_CERT:
                 /* Expect CertVerify */
                 ret = dhs_recv_sof(dhs, &type, &frame_len);
                 if (ret == 1) {
-                    if (type == QUIC_DHS_MSG_TYPE_CERT_VERIFY && frame_len == 0)
-                        dhs->state = QUIC_DHS_STATE_RECEIVED_CERT_VERIFY;
-                    else
+                    if (type == QUIC_DHS_MSG_TYPE_CERT_VERIFY && frame_len == 0) {
+                        dhs->state = QUIC_DHS_CLIENT_STATE_RECEIVED_CERT_VERIFY;
+                    } else {
+                        dhs->state = QUIC_DHS_CLIENT_STATE_ERROR;
                         return 0; /* error state, unexpected type */
+                    }
                 } else if (ret == 2) {
                     return 1; /* no more data yet, not an error */
                 } else {
@@ -283,14 +312,16 @@ int ossl_quic_dhs_tick(QUIC_DHS *dhs)
                 }
                 break;
 
-            case QUIC_DHS_STATE_RECEIVED_CERT_VERIFY:
+            case QUIC_DHS_CLIENT_STATE_RECEIVED_CERT_VERIFY:
                 /* Expect Finished */
                 ret = dhs_recv_sof(dhs, &type, &frame_len);
                 if (ret == 1) {
-                    if (type == QUIC_DHS_MSG_TYPE_FINISHED && frame_len == 0)
-                        dhs->state = QUIC_DHS_STATE_RECEIVED_FINISHED;
-                    else
+                    if (type == QUIC_DHS_MSG_TYPE_FINISHED && frame_len == 0) {
+                        dhs->state = QUIC_DHS_CLIENT_STATE_RECEIVED_FINISHED;
+                    } else {
+                        dhs->state = QUIC_DHS_CLIENT_STATE_ERROR;
                         return 0; /* error state, unexpected type */
+                    }
                 } else if (ret == 2) {
                     return 1; /* no more data yet, not an error */
                 } else {
@@ -298,12 +329,12 @@ int ossl_quic_dhs_tick(QUIC_DHS *dhs)
                 }
                 break;
 
-            case QUIC_DHS_STATE_RECEIVED_FINISHED:
+            case QUIC_DHS_CLIENT_STATE_RECEIVED_FINISHED:
                 /* Send Finished */
                 if (!dhs_send(dhs, QUIC_DHS_MSG_TYPE_FINISHED, NULL, 0))
                     return 0;
 
-                dhs->state = QUIC_DHS_STATE_SENT_FINISHED;
+                dhs->state = QUIC_DHS_CLIENT_STATE_SENT_FINISHED;
 
                 if (!dhs->args.yield_secret_cb(QUIC_ENC_LEVEL_1RTT,
                                                /*TX=*/0,
@@ -330,7 +361,200 @@ int ossl_quic_dhs_tick(QUIC_DHS *dhs)
                 dhs->local_transport_params_len   = 0;
                 break;
 
-            case QUIC_DHS_STATE_SENT_FINISHED:
+            case QUIC_DHS_CLIENT_STATE_SENT_FINISHED:
+                /* Nothing to do, handshake complete. */
+                return 1;
+
+            default:
+                return 0; /* error state */
+        }
+    }
+
+    return 1;
+}
+
+/* Tick implementation for the server role. */
+static int dhs_tick_server(QUIC_DHS *dhs)
+{
+    int ret;
+    uint32_t type;
+    size_t frame_len, bytes_read = 0;
+
+    for (;;) {
+        switch (dhs->state) {
+            case QUIC_DHS_SERVER_STATE_INITIAL:
+                /* Waiting for ClientHello message */
+                ret = dhs_recv_sof(dhs, &type, &frame_len);
+                if (ret == 1) {
+                    if (type == QUIC_DHS_MSG_TYPE_CH && frame_len > 0) {
+                        dhs->state = QUIC_DHS_SERVER_STATE_RECEIVED_CH_HDR;
+                        dhs->rx_bytes_read = 0;
+                        dhs->remote_transport_params_len = frame_len;
+                        dhs->remote_transport_params
+                            = OPENSSL_malloc(dhs->remote_transport_params_len);
+
+                        if (dhs->remote_transport_params == NULL)
+                            return 0;
+
+                    } else {
+                        dhs->state = QUIC_DHS_SERVER_STATE_ERROR;
+                        return 0; /* error state, unexpected type */
+                    }
+                } else if (ret == 2) {
+                    return 1; /* no more data yet, not an error */
+                } else {
+                    return 0;
+                }
+
+                break;
+
+            case QUIC_DHS_SERVER_STATE_RECEIVED_CH_HDR:
+                ret = dhs_recv_body(dhs, dhs->remote_transport_params + dhs->rx_bytes_read,
+                                    dhs->remote_transport_params_len - dhs->rx_bytes_read,
+                                    &bytes_read);
+                if (ret == 1) {
+                    dhs->rx_bytes_read += bytes_read;
+                    if (dhs->rx_bytes_read == dhs->remote_transport_params_len) {
+                        if (!dhs->args.got_transport_params_cb(dhs->remote_transport_params,
+                                                               dhs->remote_transport_params_len,
+                                                               dhs->args.got_transport_params_cb_arg)) {
+                            dhs->state = QUIC_DHS_SERVER_STATE_ERROR;
+                            return 0;
+                        }
+
+                        dhs->state = QUIC_DHS_SERVER_STATE_RECEIVED_CH;
+                    }
+                } else if (ret == 2) {
+                    return 1; /* no more data yet, not an error */
+                } else {
+                    return 0;
+                }
+                break;
+
+            case QUIC_DHS_SERVER_STATE_RECEIVED_CH:
+                /* We need to send a ServerHello message */
+                if (!dhs_send(dhs, QUIC_DHS_MSG_TYPE_SH, NULL, 0))
+                    return 0;
+
+                /*
+                 * Our send function automatically sends at the highest
+                 * available EL so we need to do this after sending the SH.
+                 */
+                if (!dhs->args.yield_secret_cb(QUIC_ENC_LEVEL_HANDSHAKE,
+                                               /* (Keys reversed - server) */
+                                               /*TX=*/0,
+                                               QRL_SUITE_AES128GCM,
+                                               NULL,
+                                               default_handshake_write,
+                                               sizeof(default_handshake_write),
+                                               dhs->args.yield_secret_cb_arg)) {
+                    dhs->state = QUIC_DHS_SERVER_STATE_ERROR;
+                    return 0;
+                }
+
+                if (!dhs->args.yield_secret_cb(QUIC_ENC_LEVEL_HANDSHAKE,
+                                               /* (Keys reversed - server) */
+                                               /*TX=*/1,
+                                               QRL_SUITE_AES128GCM,
+                                               NULL,
+                                               default_handshake_read,
+                                               sizeof(default_handshake_read),
+                                               dhs->args.yield_secret_cb_arg)) {
+                    dhs->state = QUIC_DHS_SERVER_STATE_ERROR;
+                    return 0;
+                }
+
+                dhs->state = QUIC_DHS_SERVER_STATE_SENT_SH;
+                break;
+
+            case QUIC_DHS_SERVER_STATE_SENT_SH:
+                /* We need to send an EncryptedExtensions message */
+                if (dhs->local_transport_params == NULL
+                    || dhs->local_transport_params_len == 0)
+                    return 0;
+
+                if (!dhs_send(dhs, QUIC_DHS_MSG_TYPE_EE,
+                              dhs->local_transport_params,
+                              dhs->local_transport_params_len))
+                    return 0;
+
+                dhs->local_transport_params       = NULL;
+                dhs->local_transport_params_len   = 0;
+
+                dhs->state = QUIC_DHS_SERVER_STATE_SENT_EE;
+                break;
+
+            case QUIC_DHS_SERVER_STATE_SENT_EE:
+                /* We need to send a Certificate message */
+                if (!dhs_send(dhs, QUIC_DHS_MSG_TYPE_CERT, NULL, 0))
+                    return 0;
+
+                dhs->state = QUIC_DHS_SERVER_STATE_SENT_CERT;
+                break;
+
+            case QUIC_DHS_SERVER_STATE_SENT_CERT:
+                /* We need to send a CertificateVerify message */
+                if (!dhs_send(dhs, QUIC_DHS_MSG_TYPE_CERT_VERIFY, NULL, 0))
+                    return 0;
+
+                dhs->state = QUIC_DHS_SERVER_STATE_SENT_CERT_VERIFY;
+                break;
+
+            case QUIC_DHS_SERVER_STATE_SENT_CERT_VERIFY:
+                /* We need to send a Finished message */
+                if (!dhs_send(dhs, QUIC_DHS_MSG_TYPE_FINISHED, NULL, 0))
+                    return 0;
+
+                dhs->state = QUIC_DHS_SERVER_STATE_SENT_FINISHED;
+                break;
+
+            case QUIC_DHS_SERVER_STATE_SENT_FINISHED:
+                /* We are waiting for a Finished message. */
+                ret = dhs_recv_sof(dhs, &type, &frame_len);
+                if (ret == 1) {
+                    if (type == QUIC_DHS_MSG_TYPE_FINISHED && frame_len == 0) {
+                        if (!dhs->args.yield_secret_cb(QUIC_ENC_LEVEL_1RTT,
+                                                       /* (Keys reversed - server) */
+                                                       /*TX=*/0,
+                                                       QRL_SUITE_AES128GCM,
+                                                       NULL,
+                                                       default_1rtt_write,
+                                                       sizeof(default_1rtt_write),
+                                                       dhs->args.yield_secret_cb_arg)) {
+                            dhs->state = QUIC_DHS_SERVER_STATE_ERROR;
+                            return 0;
+                        }
+
+                        if (!dhs->args.yield_secret_cb(QUIC_ENC_LEVEL_1RTT,
+                                                       /* (Keys reversed - server) */
+                                                       /*TX=*/1,
+                                                       QRL_SUITE_AES128GCM,
+                                                       NULL,
+                                                       default_1rtt_read,
+                                                       sizeof(default_1rtt_read),
+                                                       dhs->args.yield_secret_cb_arg)) {
+                            dhs->state = QUIC_DHS_SERVER_STATE_ERROR;
+                            return 0;
+                        }
+
+                        if (!dhs->args.handshake_complete_cb(dhs->args.handshake_complete_cb_arg)) {
+                            dhs->state = QUIC_DHS_SERVER_STATE_ERROR;
+                            return 0;
+                        }
+
+                        dhs->state = QUIC_DHS_SERVER_STATE_RECEIVED_FINISHED;
+                    } else {
+                        dhs->state = QUIC_DHS_SERVER_STATE_ERROR;
+                        return 0; /* error state, unexpected type */
+                    }
+                } else if (ret == 2) {
+                    return 1; /* no more data yet, not an error */
+                } else {
+                    return 0;
+                }
+                break;
+
+            case QUIC_DHS_SERVER_STATE_RECEIVED_FINISHED:
                 /* Nothing to do, handshake complete. */
                 return 1;
 
@@ -341,3 +565,11 @@ int ossl_quic_dhs_tick(QUIC_DHS *dhs)
 
     return 1;
 }
+
+int ossl_quic_dhs_tick(QUIC_DHS *dhs)
+{
+    if (dhs->args.is_server)
+        return dhs_tick_server(dhs);
+    else
+        return dhs_tick_client(dhs);
+}