summaryrefslogtreecommitdiff
path: root/lib/handshake-msg.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/handshake-msg.c')
-rw-r--r--lib/handshake-msg.c222
1 files changed, 222 insertions, 0 deletions
diff --git a/lib/handshake-msg.c b/lib/handshake-msg.c
new file mode 100644
index 0000000000..aff65ba15f
--- /dev/null
+++ b/lib/handshake-msg.c
@@ -0,0 +1,222 @@
+#include "gnutls_int.h"
+#include "handshake.h"
+#include "handshake-msg.h"
+#include "mbuffers.h"
+
+#define CHECK_SIZE(ll) \
+ if ((session->internals.max_handshake_data_buffer_size > 0) && \
+ (((ll) + session->internals.handshake_hash_buffer.length) > \
+ session->internals.max_handshake_data_buffer_size)) { \
+ _gnutls_debug_log("Handshake buffer length is %u (max: %u)\n", (unsigned)((ll) + session->internals.handshake_hash_buffer.length), (unsigned)session->internals.max_handshake_data_buffer_size); \
+ return gnutls_assert_val(GNUTLS_E_HANDSHAKE_TOO_LARGE); \
+ }
+
+struct handshake_msg_st
+{
+ gnutls_handshake_description_t type;
+ size_t committed_bytes;
+};
+
+int _gnutls_handshake_msg_init(struct handshake_msg_st **out,
+ gnutls_handshake_description_t type,
+ gnutls_session_t session)
+{
+ struct handshake_msg_st *hs = _gnutls_calloc(1, sizeof(struct handshake_msg_st));
+
+ hs->type = type;
+
+ *out = hs;
+ return GNUTLS_E_SUCCESS;
+}
+
+void _gnutls_handshake_msg_deinit(struct handshake_msg_st **hs)
+{
+ gnutls_free(*hs);
+ *hs = NULL;
+}
+
+/* This function add the handshake headers and the
+ * handshake data to the handshake hash buffers. Needed
+ * for the finished messages calculations.
+ */
+int
+_gnutls_handshake_hash_add_recvd(gnutls_session_t session,
+ gnutls_handshake_description_t recv_type,
+ uint8_t * header, uint16_t header_size,
+ uint8_t * dataptr, uint32_t datalen)
+{
+ int ret;
+ const version_entry_st *vers = get_version(session);
+
+ if (unlikely(vers == NULL))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ if ((vers->id != GNUTLS_DTLS0_9 &&
+ recv_type == GNUTLS_HANDSHAKE_HELLO_VERIFY_REQUEST) ||
+ recv_type == GNUTLS_HANDSHAKE_HELLO_REQUEST)
+ return 0;
+
+ CHECK_SIZE(header_size + datalen);
+
+ session->internals.handshake_hash_buffer_prev_len =
+ session->internals.handshake_hash_buffer.length;
+
+ if (vers->id != GNUTLS_DTLS0_9) {
+ ret =
+ _gnutls_buffer_append_data(&session->internals.
+ handshake_hash_buffer,
+ header, header_size);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+ }
+ if (datalen > 0) {
+ ret =
+ _gnutls_buffer_append_data(&session->internals.
+ handshake_hash_buffer,
+ dataptr, datalen);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+ }
+
+ /* save the size until client KX. That is because the TLS
+ * session hash is calculated up to this message.
+ */
+ if (recv_type == GNUTLS_HANDSHAKE_CLIENT_KEY_EXCHANGE)
+ session->internals.handshake_hash_buffer_client_kx_len =
+ session->internals.handshake_hash_buffer.length;
+ if (recv_type == GNUTLS_HANDSHAKE_FINISHED && session->security_parameters.entity == GNUTLS_CLIENT)
+ session->internals.handshake_hash_buffer_server_finished_len =
+ session->internals.handshake_hash_buffer.length;
+
+ return 0;
+}
+
+/* This function will store the handshake message we sent.
+ */
+int
+_gnutls_handshake_hash_add_sent(gnutls_session_t session,
+ gnutls_handshake_description_t type,
+ uint8_t * dataptr, uint32_t datalen)
+{
+ int ret;
+ const version_entry_st *vers = get_version(session);
+
+ if (unlikely(vers == NULL))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ /* We don't check for GNUTLS_HANDSHAKE_HELLO_VERIFY_REQUEST because it
+ * is not sent via that channel.
+ */
+ if (type != GNUTLS_HANDSHAKE_HELLO_REQUEST) {
+ CHECK_SIZE(datalen);
+
+ if (vers->id == GNUTLS_DTLS0_9) {
+ /* Old DTLS doesn't include the header in the MAC */
+ if (datalen < 12) {
+ gnutls_assert();
+ return GNUTLS_E_INTERNAL_ERROR;
+ }
+ dataptr += 12;
+ datalen -= 12;
+
+ if (datalen == 0)
+ return 0;
+ }
+
+ ret =
+ _gnutls_buffer_append_data(&session->internals.
+ handshake_hash_buffer,
+ dataptr, datalen);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ if (type == GNUTLS_HANDSHAKE_CLIENT_KEY_EXCHANGE)
+ session->internals.handshake_hash_buffer_client_kx_len =
+ session->internals.handshake_hash_buffer.length;
+ if (type == GNUTLS_HANDSHAKE_FINISHED && session->security_parameters.entity == GNUTLS_SERVER)
+ session->internals.handshake_hash_buffer_server_finished_len =
+ session->internals.handshake_hash_buffer.length;
+
+ return 0;
+ }
+
+ return 0;
+}
+
+static int
+handshake_commit(gnutls_session_t session,
+ struct handshake_msg_st *hs,
+ uint8_t *data, uint32_t datasize,
+ size_t head_skip_bytes, size_t header_length)
+{
+ int ret;
+ size_t to_commit, offset, prev_committed_bytes;
+
+ /* This message is not taken into account for the hash */
+ if (hs->type == GNUTLS_HANDSHAKE_HELLO_REQUEST)
+ return GNUTLS_E_SUCCESS;
+
+ prev_committed_bytes = hs->committed_bytes;
+
+ if (datasize > hs->committed_bytes) {
+ to_commit = datasize - hs->committed_bytes - head_skip_bytes;
+ offset = hs->committed_bytes + head_skip_bytes;
+
+ ret = _gnutls_handshake_hash_add_sent(session, hs->type,
+ data + offset,
+ to_commit);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ hs->committed_bytes += to_commit;
+ }
+
+ if (hs->committed_bytes > prev_committed_bytes) {
+ /* Update type field */
+ session->internals.handshake_hash_buffer.data[0] = (uint8_t) hs->type;
+ /* Update size field */
+ _gnutls_write_uint24(hs->committed_bytes - header_length,
+ &session->internals.handshake_hash_buffer.data[1]);
+ }
+
+ return GNUTLS_E_SUCCESS;
+}
+
+int _gnutls_handshake_msg_commit_from_buffer(gnutls_session_t session,
+ struct handshake_msg_st *hs,
+ gnutls_buffer_st *buf,
+ size_t head_skip_bytes,
+ size_t header_length)
+{
+ uint8_t *data;
+ uint32_t datasize;
+
+ if (!hs || !buf)
+ return GNUTLS_E_INTERNAL_ERROR;
+
+ data = buf->data;
+ datasize = buf->length;
+
+ return handshake_commit(session, hs,
+ data, datasize,
+ head_skip_bytes, header_length);
+}
+
+int _gnutls_handshake_msg_commit_from_mbuffer(gnutls_session_t session,
+ struct handshake_msg_st *hs,
+ mbuffer_st *bufel,
+ size_t head_skip_bytes, size_t header_length)
+{
+ uint8_t *data;
+ uint32_t datasize;
+
+ if (!hs || !bufel)
+ return GNUTLS_E_INTERNAL_ERROR;
+
+ data = _mbuffer_get_uhead_ptr(bufel);
+ datasize = _mbuffer_get_udata_size(bufel) + _mbuffer_get_uhead_size(bufel);
+
+ return handshake_commit(session, hs,
+ data, datasize,
+ head_skip_bytes, header_length);
+}