| Ronald Cron | b372b2e | 2024-03-13 14:10:58 +0100 | [diff] [blame] | 1 |  | 
| Ronald Cron | d514d9c | 2024-03-13 15:19:38 +0100 | [diff] [blame] | 2 | Writing early data | 
|  | 3 | ------------------ | 
| Ronald Cron | b372b2e | 2024-03-13 14:10:58 +0100 | [diff] [blame] | 4 |  | 
|  | 5 | An application function to write and send a buffer of data to a server through | 
|  | 6 | TLS may plausibly look like: | 
|  | 7 |  | 
|  | 8 | ``` | 
| Ronald Cron | 0fce958 | 2024-03-13 14:22:19 +0100 | [diff] [blame] | 9 | int write_data(mbedtls_ssl_context *ssl, | 
|  | 10 | const unsigned char *data_to_write, | 
|  | 11 | size_t data_to_write_len, | 
|  | 12 | size_t *data_written) | 
| Ronald Cron | b372b2e | 2024-03-13 14:10:58 +0100 | [diff] [blame] | 13 | { | 
| Ronald Cron | 0fce958 | 2024-03-13 14:22:19 +0100 | [diff] [blame] | 14 | int ret; | 
| Ronald Cron | b372b2e | 2024-03-13 14:10:58 +0100 | [diff] [blame] | 15 | *data_written = 0; | 
|  | 16 |  | 
| Ronald Cron | 0fce958 | 2024-03-13 14:22:19 +0100 | [diff] [blame] | 17 | while (*data_written < data_to_write_len) { | 
|  | 18 | ret = mbedtls_ssl_write(ssl, data_to_write + *data_written, | 
|  | 19 | data_to_write_len - *data_written); | 
| Ronald Cron | b372b2e | 2024-03-13 14:10:58 +0100 | [diff] [blame] | 20 |  | 
| Ronald Cron | 0fce958 | 2024-03-13 14:22:19 +0100 | [diff] [blame] | 21 | if (ret < 0 && | 
| Ronald Cron | b372b2e | 2024-03-13 14:10:58 +0100 | [diff] [blame] | 22 | ret != MBEDTLS_ERR_SSL_WANT_READ && | 
| Ronald Cron | 0fce958 | 2024-03-13 14:22:19 +0100 | [diff] [blame] | 23 | ret != MBEDTLS_ERR_SSL_WANT_WRITE) { | 
|  | 24 | return ret; | 
| Ronald Cron | b372b2e | 2024-03-13 14:10:58 +0100 | [diff] [blame] | 25 | } | 
|  | 26 |  | 
|  | 27 | *data_written += ret; | 
|  | 28 | } | 
|  | 29 |  | 
| Ronald Cron | 0fce958 | 2024-03-13 14:22:19 +0100 | [diff] [blame] | 30 | return 0; | 
| Ronald Cron | b372b2e | 2024-03-13 14:10:58 +0100 | [diff] [blame] | 31 | } | 
|  | 32 | ``` | 
|  | 33 | where ssl is the SSL context to use, data_to_write the address of the data | 
|  | 34 | buffer and data_to_write_len the number of data bytes. The handshake may | 
|  | 35 | not be completed, not even started for the SSL context ssl when the function is | 
|  | 36 | called and in that case the mbedtls_ssl_write() API takes care transparently of | 
|  | 37 | completing the handshake before to write and send data to the server. The | 
| Ronald Cron | 0fce958 | 2024-03-13 14:22:19 +0100 | [diff] [blame] | 38 | mbedtls_ssl_write() may not be able to write and send all data in one go thus | 
| Ronald Cron | b372b2e | 2024-03-13 14:10:58 +0100 | [diff] [blame] | 39 | the need for a loop calling it as long as there are still data to write and | 
|  | 40 | send. | 
|  | 41 |  | 
|  | 42 | An application function to write and send early data and only early data, | 
|  | 43 | data sent during the first flight of client messages while the handshake is in | 
|  | 44 | its initial phase, would look completely similar but the call to | 
|  | 45 | mbedtls_ssl_write_early_data() instead of mbedtls_ssl_write(). | 
|  | 46 | ``` | 
| Ronald Cron | 0fce958 | 2024-03-13 14:22:19 +0100 | [diff] [blame] | 47 | int write_early_data(mbedtls_ssl_context *ssl, | 
|  | 48 | const unsigned char *data_to_write, | 
|  | 49 | size_t data_to_write_len, | 
|  | 50 | size_t *data_written) | 
| Ronald Cron | b372b2e | 2024-03-13 14:10:58 +0100 | [diff] [blame] | 51 | { | 
| Ronald Cron | 0fce958 | 2024-03-13 14:22:19 +0100 | [diff] [blame] | 52 | int ret; | 
| Ronald Cron | b372b2e | 2024-03-13 14:10:58 +0100 | [diff] [blame] | 53 | *data_written = 0; | 
|  | 54 |  | 
| Ronald Cron | 0fce958 | 2024-03-13 14:22:19 +0100 | [diff] [blame] | 55 | while (*data_written < data_to_write_len) { | 
|  | 56 | ret = mbedtls_ssl_write_early_data(ssl, data_to_write + *data_written, | 
|  | 57 | data_to_write_len - *data_written); | 
| Ronald Cron | b372b2e | 2024-03-13 14:10:58 +0100 | [diff] [blame] | 58 |  | 
| Ronald Cron | 0fce958 | 2024-03-13 14:22:19 +0100 | [diff] [blame] | 59 | if (ret < 0 && | 
| Ronald Cron | b372b2e | 2024-03-13 14:10:58 +0100 | [diff] [blame] | 60 | ret != MBEDTLS_ERR_SSL_WANT_READ && | 
| Ronald Cron | 0fce958 | 2024-03-13 14:22:19 +0100 | [diff] [blame] | 61 | ret != MBEDTLS_ERR_SSL_WANT_WRITE) { | 
|  | 62 | return ret; | 
| Ronald Cron | b372b2e | 2024-03-13 14:10:58 +0100 | [diff] [blame] | 63 | } | 
|  | 64 |  | 
|  | 65 | *data_written += ret; | 
|  | 66 | } | 
|  | 67 |  | 
| Ronald Cron | 0fce958 | 2024-03-13 14:22:19 +0100 | [diff] [blame] | 68 | return 0; | 
| Ronald Cron | b372b2e | 2024-03-13 14:10:58 +0100 | [diff] [blame] | 69 | } | 
|  | 70 | ``` | 
|  | 71 | Note that compared to write_data(), write_early_data() can also return | 
|  | 72 | MBEDTLS_ERR_SSL_CANNOT_WRITE_EARLY_DATA and that should be handled | 
|  | 73 | specifically by the user of write_early_data(). A fresh SSL context (typically | 
|  | 74 | just after a call to mbedtls_ssl_setup() or mbedtls_ssl_session_reset()) would | 
|  | 75 | be expected when calling `write_early_data`. | 
|  | 76 |  | 
|  | 77 | All together, code to write and send a buffer of data as long as possible as | 
|  | 78 | early data and then as standard post-handshake application data could | 
|  | 79 | plausibly look like: | 
|  | 80 |  | 
|  | 81 | ``` | 
| Ronald Cron | 0fce958 | 2024-03-13 14:22:19 +0100 | [diff] [blame] | 82 | ret = write_early_data(ssl, | 
|  | 83 | data_to_write, | 
|  | 84 | data_to_write_len, | 
|  | 85 | &early_data_written); | 
|  | 86 | if (ret < 0 && | 
|  | 87 | ret != MBEDTLS_ERR_SSL_CANNOT_WRITE_EARLY_DATA) { | 
| Ronald Cron | b372b2e | 2024-03-13 14:10:58 +0100 | [diff] [blame] | 88 | goto error; | 
|  | 89 | } | 
|  | 90 |  | 
| Ronald Cron | 0fce958 | 2024-03-13 14:22:19 +0100 | [diff] [blame] | 91 | ret = write_data(ssl, | 
|  | 92 | data_to_write + early_data_written, | 
|  | 93 | data_to_write_len - early_data_written, | 
|  | 94 | &data_written); | 
|  | 95 | if (ret < 0) { | 
| Ronald Cron | b372b2e | 2024-03-13 14:10:58 +0100 | [diff] [blame] | 96 | goto error; | 
| Ronald Cron | 0fce958 | 2024-03-13 14:22:19 +0100 | [diff] [blame] | 97 | } | 
| Ronald Cron | b372b2e | 2024-03-13 14:10:58 +0100 | [diff] [blame] | 98 |  | 
|  | 99 | data_written += early_data_written; | 
|  | 100 | ``` | 
|  | 101 |  | 
|  | 102 | Finally, taking into account that the server may reject early data, application | 
|  | 103 | code to write and send a buffer of data could plausibly look like: | 
|  | 104 | ``` | 
| Ronald Cron | 0fce958 | 2024-03-13 14:22:19 +0100 | [diff] [blame] | 105 | ret = write_early_data(ssl, | 
|  | 106 | data_to_write, | 
|  | 107 | data_to_write_len, | 
|  | 108 | &early_data_written); | 
|  | 109 | if (ret < 0 && | 
|  | 110 | ret != MBEDTLS_ERR_SSL_CANNOT_WRITE_EARLY_DATA) { | 
| Ronald Cron | b372b2e | 2024-03-13 14:10:58 +0100 | [diff] [blame] | 111 | goto error; | 
|  | 112 | } | 
|  | 113 |  | 
|  | 114 | /* | 
| Ronald Cron | 0fce958 | 2024-03-13 14:22:19 +0100 | [diff] [blame] | 115 | * Make sure the handshake is completed as it is a requisite of | 
| Ronald Cron | b372b2e | 2024-03-13 14:10:58 +0100 | [diff] [blame] | 116 | * mbedtls_ssl_get_early_data_status(). | 
|  | 117 | */ | 
| Ronald Cron | 0fce958 | 2024-03-13 14:22:19 +0100 | [diff] [blame] | 118 | while (!mbedtls_ssl_is_handshake_over(ssl)) { | 
|  | 119 | ret = mbedtls_ssl_handshake(ssl); | 
|  | 120 | if (ret < 0 && | 
| Ronald Cron | b372b2e | 2024-03-13 14:10:58 +0100 | [diff] [blame] | 121 | ret != MBEDTLS_ERR_SSL_WANT_READ && | 
| Ronald Cron | 0fce958 | 2024-03-13 14:22:19 +0100 | [diff] [blame] | 122 | ret != MBEDTLS_ERR_SSL_WANT_WRITE) { | 
| Ronald Cron | b372b2e | 2024-03-13 14:10:58 +0100 | [diff] [blame] | 123 | goto error; | 
|  | 124 | } | 
|  | 125 | } | 
|  | 126 |  | 
| Ronald Cron | 0fce958 | 2024-03-13 14:22:19 +0100 | [diff] [blame] | 127 | ret = mbedtls_ssl_get_early_data_status(ssl); | 
|  | 128 | if (ret < 0) { | 
| Ronald Cron | b372b2e | 2024-03-13 14:10:58 +0100 | [diff] [blame] | 129 | goto error; | 
| Ronald Cron | 0fce958 | 2024-03-13 14:22:19 +0100 | [diff] [blame] | 130 | } | 
| Ronald Cron | b372b2e | 2024-03-13 14:10:58 +0100 | [diff] [blame] | 131 |  | 
| Ronald Cron | 0fce958 | 2024-03-13 14:22:19 +0100 | [diff] [blame] | 132 | if (ret == MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED) { | 
| Ronald Cron | b372b2e | 2024-03-13 14:10:58 +0100 | [diff] [blame] | 133 | early_data_written = 0; | 
| Ronald Cron | 0fce958 | 2024-03-13 14:22:19 +0100 | [diff] [blame] | 134 | } | 
| Ronald Cron | b372b2e | 2024-03-13 14:10:58 +0100 | [diff] [blame] | 135 |  | 
| Ronald Cron | 0fce958 | 2024-03-13 14:22:19 +0100 | [diff] [blame] | 136 | ret = write_data(ssl, | 
|  | 137 | data_to_write + early_data_written, | 
|  | 138 | data_to_write_len - early_data_written, | 
|  | 139 | &data_written); | 
|  | 140 | if (ret < 0) { | 
| Ronald Cron | b372b2e | 2024-03-13 14:10:58 +0100 | [diff] [blame] | 141 | goto error; | 
| Ronald Cron | 0fce958 | 2024-03-13 14:22:19 +0100 | [diff] [blame] | 142 | } | 
| Ronald Cron | b372b2e | 2024-03-13 14:10:58 +0100 | [diff] [blame] | 143 |  | 
|  | 144 | data_written += early_data_written; | 
|  | 145 | ``` | 
|  | 146 |  | 
| Ronald Cron | d514d9c | 2024-03-13 15:19:38 +0100 | [diff] [blame] | 147 | Reading early data | 
|  | 148 | ------------------ | 
|  | 149 | Mbed TLS provides the mbedtls_ssl_read_early_data() API to read the early data | 
|  | 150 | that a TLS 1.3 server might receive during the TLS 1.3 handshake. | 
|  | 151 |  | 
|  | 152 | While establishing a TLS 1.3 connection with a client using a combination | 
|  | 153 | of the mbedtls_ssl_handshake(), mbedtls_ssl_read() and mbedtls_ssl_write() APIs, | 
|  | 154 | the reception of early data is signaled by an API returning the | 
|  | 155 | MBEDTLS_ERR_SSL_RECEIVED_EARLY_DATA error code. Early data can then be read | 
|  | 156 | with the mbedtls_ssl_read_early_data() API. | 
|  | 157 |  | 
|  | 158 | For example, a typical code to establish a TLS connection, where ssl is the SSL | 
|  | 159 | context to use: | 
| Ronald Cron | b372b2e | 2024-03-13 14:10:58 +0100 | [diff] [blame] | 160 | ``` | 
| Ronald Cron | d514d9c | 2024-03-13 15:19:38 +0100 | [diff] [blame] | 161 | while ((int ret = mbedtls_ssl_handshake(&ssl)) != 0) { | 
| Ronald Cron | b372b2e | 2024-03-13 14:10:58 +0100 | [diff] [blame] | 162 |  | 
| Ronald Cron | d514d9c | 2024-03-13 15:19:38 +0100 | [diff] [blame] | 163 | if (ret < 0 && | 
|  | 164 | ret != MBEDTLS_ERR_SSL_WANT_READ && | 
|  | 165 | ret != MBEDTLS_ERR_SSL_WANT_WRITE) { | 
|  | 166 | break; | 
| Ronald Cron | b372b2e | 2024-03-13 14:10:58 +0100 | [diff] [blame] | 167 | } | 
| Ronald Cron | b372b2e | 2024-03-13 14:10:58 +0100 | [diff] [blame] | 168 | } | 
|  | 169 | ``` | 
| Ronald Cron | d514d9c | 2024-03-13 15:19:38 +0100 | [diff] [blame] | 170 | could be adapted to handle early data in the following way: | 
|  | 171 | ``` | 
|  | 172 | size_t data_read_len = 0; | 
|  | 173 | while ((ret = mbedtls_ssl_handshake(&ssl)) != 0) { | 
|  | 174 |  | 
|  | 175 | if (ret == MBEDTLS_ERR_SSL_RECEIVED_EARLY_DATA) { | 
|  | 176 | ret = mbedtls_ssl_read_early_data(&ssl, | 
|  | 177 | buffer + data_read_len, | 
|  | 178 | sizeof(buffer) - data_read_len); | 
|  | 179 | if (ret < 0) { | 
|  | 180 | break; | 
|  | 181 | } | 
|  | 182 | data_read_len += ret; | 
|  | 183 | continue; | 
|  | 184 | } | 
|  | 185 |  | 
|  | 186 | if (ret < 0 && | 
|  | 187 | ret != MBEDTLS_ERR_SSL_WANT_READ && | 
|  | 188 | ret != MBEDTLS_ERR_SSL_WANT_WRITE) { | 
|  | 189 | break; | 
|  | 190 | } | 
|  | 191 | } | 
|  | 192 | ``` |