s2n_quic_support.c 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. /*
  2. * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License").
  5. * You may not use this file except in compliance with the License.
  6. * A copy of the License is located at
  7. *
  8. * http://aws.amazon.com/apache2.0
  9. *
  10. * or in the "license" file accompanying this file. This file is distributed
  11. * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
  12. * express or implied. See the License for the specific language governing
  13. * permissions and limitations under the License.
  14. */
  15. #include "tls/s2n_quic_support.h"
  16. #include "tls/s2n_connection.h"
  17. #include "tls/s2n_tls.h"
  18. #include "tls/s2n_tls13.h"
  19. #include "utils/s2n_mem.h"
  20. #include "utils/s2n_safety.h"
  21. /* When reading and writing records with TCP, S2N sets its input and output buffers
  22. * to the maximum record fragment size to prevent resizing those buffers later.
  23. *
  24. * However, because S2N with QUIC reads and writes messages instead of records,
  25. * the "maximum size" for the input and output buffers would be the maximum message size: 64k.
  26. * Since most messages are MUCH smaller than that (<3k), setting the buffer that large is wasteful.
  27. *
  28. * Instead, we intentionally choose a smaller size and accept that an abnormally large message
  29. * could cause the buffer to resize. */
  30. #define S2N_EXPECTED_QUIC_MESSAGE_SIZE S2N_DEFAULT_FRAGMENT_LENGTH
  31. S2N_RESULT s2n_read_in_bytes(struct s2n_connection *conn, struct s2n_stuffer *output, uint32_t length);
  32. int s2n_config_enable_quic(struct s2n_config *config)
  33. {
  34. POSIX_ENSURE_REF(config);
  35. config->quic_enabled = true;
  36. return S2N_SUCCESS;
  37. }
  38. int s2n_connection_enable_quic(struct s2n_connection *conn)
  39. {
  40. POSIX_ENSURE_REF(conn);
  41. POSIX_GUARD_RESULT(s2n_connection_validate_tls13_support(conn));
  42. conn->quic_enabled = true;
  43. return S2N_SUCCESS;
  44. }
  45. bool s2n_connection_is_quic_enabled(struct s2n_connection *conn)
  46. {
  47. return (conn && conn->quic_enabled) || (conn && conn->config && conn->config->quic_enabled);
  48. }
  49. int s2n_connection_set_quic_transport_parameters(struct s2n_connection *conn,
  50. const uint8_t *data_buffer, uint16_t data_len)
  51. {
  52. POSIX_ENSURE_REF(conn);
  53. POSIX_GUARD(s2n_free(&conn->our_quic_transport_parameters));
  54. POSIX_GUARD(s2n_alloc(&conn->our_quic_transport_parameters, data_len));
  55. POSIX_CHECKED_MEMCPY(conn->our_quic_transport_parameters.data, data_buffer, data_len);
  56. return S2N_SUCCESS;
  57. }
  58. int s2n_connection_get_quic_transport_parameters(struct s2n_connection *conn,
  59. const uint8_t **data_buffer, uint16_t *data_len)
  60. {
  61. POSIX_ENSURE_REF(conn);
  62. POSIX_ENSURE_REF(data_buffer);
  63. POSIX_ENSURE_REF(data_len);
  64. *data_buffer = conn->peer_quic_transport_parameters.data;
  65. *data_len = conn->peer_quic_transport_parameters.size;
  66. return S2N_SUCCESS;
  67. }
  68. int s2n_connection_set_secret_callback(struct s2n_connection *conn, s2n_secret_cb cb_func, void *ctx)
  69. {
  70. POSIX_ENSURE_REF(conn);
  71. POSIX_ENSURE_REF(cb_func);
  72. conn->secret_cb = cb_func;
  73. conn->secret_cb_context = ctx;
  74. return S2N_SUCCESS;
  75. }
  76. /* Currently we need an API that quic can call to process post-handshake messages. Ideally
  77. * we could re-use the s2n_recv API but that function needs to be refactored to support quic.
  78. * For now we just call this API.
  79. */
  80. int s2n_recv_quic_post_handshake_message(struct s2n_connection *conn, s2n_blocked_status *blocked)
  81. {
  82. POSIX_ENSURE_REF(conn);
  83. *blocked = S2N_BLOCKED_ON_READ;
  84. uint8_t message_type = 0;
  85. /* This function uses the stuffer conn->handshake.io to read in the header. This stuffer is also used
  86. * for sending post-handshake messages. This could cause a concurrency issue if we start both sending
  87. * and receiving post-handshake messages while quic is enabled. Currently there's no post-handshake
  88. * message that is both sent and received in quic (servers only send session tickets
  89. * and clients only receive session tickets.) Therefore it is safe for us
  90. * to use the stuffer here.
  91. */
  92. POSIX_GUARD_RESULT(s2n_quic_read_handshake_message(conn, &message_type));
  93. /* The only post-handshake messages we support from QUIC currently are session tickets */
  94. POSIX_ENSURE(message_type == TLS_SERVER_NEW_SESSION_TICKET, S2N_ERR_UNSUPPORTED_WITH_QUIC);
  95. POSIX_GUARD_RESULT(s2n_post_handshake_process(conn, &conn->in, message_type));
  96. *blocked = S2N_NOT_BLOCKED;
  97. return S2N_SUCCESS;
  98. }
  99. /* When using QUIC, S2N reads unencrypted handshake messages instead of encrypted records.
  100. * This method sets up the S2N input buffers to match the results of using s2n_read_full_record.
  101. */
  102. S2N_RESULT s2n_quic_read_handshake_message(struct s2n_connection *conn, uint8_t *message_type)
  103. {
  104. RESULT_ENSURE_REF(conn);
  105. /* Allocate stuffer space now so that we don't have to realloc later in the handshake. */
  106. RESULT_GUARD_POSIX(s2n_stuffer_resize_if_empty(&conn->in, S2N_EXPECTED_QUIC_MESSAGE_SIZE));
  107. RESULT_GUARD(s2n_read_in_bytes(conn, &conn->handshake.io, TLS_HANDSHAKE_HEADER_LENGTH));
  108. uint32_t message_len;
  109. RESULT_GUARD(s2n_handshake_parse_header(&conn->handshake.io, message_type, &message_len));
  110. RESULT_GUARD_POSIX(s2n_stuffer_reread(&conn->handshake.io));
  111. RESULT_ENSURE(message_len < S2N_MAXIMUM_HANDSHAKE_MESSAGE_LENGTH, S2N_ERR_BAD_MESSAGE);
  112. RESULT_GUARD(s2n_read_in_bytes(conn, &conn->in, message_len));
  113. return S2N_RESULT_OK;
  114. }
  115. /* When using QUIC, S2N writes unencrypted handshake messages instead of encrypted records.
  116. * This method sets up the S2N output buffer to match the result of using s2n_record_write.
  117. */
  118. S2N_RESULT s2n_quic_write_handshake_message(struct s2n_connection *conn)
  119. {
  120. RESULT_ENSURE_REF(conn);
  121. /* Allocate stuffer space now so that we don't have to realloc later in the handshake. */
  122. RESULT_GUARD_POSIX(s2n_stuffer_resize_if_empty(&conn->out, S2N_EXPECTED_QUIC_MESSAGE_SIZE));
  123. RESULT_GUARD_POSIX(s2n_stuffer_copy(&conn->handshake.io, &conn->out,
  124. s2n_stuffer_data_available(&conn->handshake.io)));
  125. return S2N_RESULT_OK;
  126. }