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