2 * Copyright (C) 2009-2012 Free Software Foundation, Inc.
3 * Copyright (C) 2013 Nikos Mavrogiannopoulos
5 * Authors: Jonathan Bastien-Filiatrault
6 * Nikos Mavrogiannopoulos
8 * This file is part of GNUTLS.
10 * The GNUTLS library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public License
12 * as published by the Free Software Foundation; either version 2.1 of
13 * the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with this program. If not, see <http://www.gnu.org/licenses/>
25 /* Functions that relate to DTLS retransmission and reassembly.
28 #include "gnutls_int.h"
29 #include "gnutls_errors.h"
31 #include "gnutls_dtls.h"
32 #include "gnutls_record.h"
33 #include <gnutls_mbuffers.h>
34 #include <gnutls_buffers.h>
35 #include <gnutls_constate.h>
36 #include <gnutls_state.h>
37 #include <gnutls/dtls.h>
38 #include <algorithms.h>
40 void _dtls_async_timer_delete(gnutls_session_t session)
42 if (session->internals.dtls.async_term != 0) {
44 ("DTLS[%p]: Deinitializing previous handshake state.\n",
46 session->internals.dtls.async_term = 0; /* turn off "timer" */
48 _dtls_reset_hsk_state(session);
49 _gnutls_handshake_io_buffer_clear(session);
50 _gnutls_epoch_gc(session);
54 /* This function fragments and transmits a previously buffered
55 * outgoing message. It accepts mtu_data which is a buffer to
56 * be reused (should be set to NULL initially).
59 transmit_message(gnutls_session_t session,
60 mbuffer_st * bufel, uint8_t ** buf)
62 uint8_t *data, *mtu_data;
64 unsigned int offset, frag_len, data_size;
65 const unsigned int mtu =
66 gnutls_dtls_get_data_mtu(session) - DTLS_HANDSHAKE_HEADER_SIZE;
68 if (bufel->type == GNUTLS_CHANGE_CIPHER_SPEC) {
70 ("DTLS[%p]: Sending Packet[%u] fragment %s(%d)\n",
71 session, bufel->handshake_sequence,
72 _gnutls_handshake2str(bufel->htype), bufel->htype);
74 return _gnutls_send_int(session, bufel->type, -1,
76 _mbuffer_get_uhead_ptr(bufel),
77 _mbuffer_get_uhead_size(bufel), 0);
81 *buf = gnutls_malloc(mtu + DTLS_HANDSHAKE_HEADER_SIZE);
83 return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
87 data = _mbuffer_get_udata_ptr(bufel);
88 data_size = _mbuffer_get_udata_size(bufel);
90 /* Write fixed headers
94 mtu_data[0] = (uint8_t) bufel->htype;
97 _gnutls_write_uint24(data_size, &mtu_data[1]);
99 /* Handshake sequence */
100 _gnutls_write_uint16(bufel->handshake_sequence, &mtu_data[4]);
102 /* Chop up and send handshake message into mtu-size pieces. */
103 for (offset = 0; offset <= data_size; offset += mtu) {
104 /* Calculate fragment length */
105 if (offset + mtu > data_size)
106 frag_len = data_size - offset;
110 /* Fragment offset */
111 _gnutls_write_uint24(offset, &mtu_data[6]);
113 /* Fragment length */
114 _gnutls_write_uint24(frag_len, &mtu_data[9]);
116 memcpy(&mtu_data[DTLS_HANDSHAKE_HEADER_SIZE],
117 data + offset, frag_len);
120 ("DTLS[%p]: Sending Packet[%u] fragment %s(%d) with "
121 "length: %u, offset: %u, fragment length: %u\n",
122 session, bufel->handshake_sequence,
123 _gnutls_handshake2str(bufel->htype), bufel->htype,
124 data_size, offset, frag_len);
126 ret = _gnutls_send_int(session, bufel->type, bufel->htype,
127 bufel->epoch, mtu_data,
128 DTLS_HANDSHAKE_HEADER_SIZE +
139 static int drop_usage_count(gnutls_session_t session,
140 mbuffer_head_st * const send_buffer)
145 for (cur = send_buffer->head; cur != NULL; cur = cur->next) {
146 ret = _gnutls_epoch_refcount_dec(session, cur->epoch);
148 return gnutls_assert_val(ret);
155 /* Checks whether the received packet contains a handshake
156 * packet with sequence higher that the previously received.
157 * It must be called only when an actual packet has been
160 * Returns: 0 if expected, negative value otherwise.
162 static int is_next_hpacket_expected(gnutls_session_t session)
166 /* htype is arbitrary */
168 _gnutls_recv_in_buffers(session, GNUTLS_HANDSHAKE,
169 GNUTLS_HANDSHAKE_FINISHED, 0);
171 return gnutls_assert_val(ret);
173 ret = _gnutls_parse_record_buffered_msgs(session);
175 return gnutls_assert_val(ret);
177 if (session->internals.handshake_recv_buffer_size > 0)
182 (GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET);
185 void _dtls_reset_hsk_state(gnutls_session_t session)
187 session->internals.dtls.flight_init = 0;
188 drop_usage_count(session,
189 &session->internals.handshake_send_buffer);
190 _mbuffer_head_clear(&session->internals.handshake_send_buffer);
194 #define UPDATE_TIMER { \
195 session->internals.dtls.actual_retrans_timeout_ms *= 2; \
196 session->internals.dtls.actual_retrans_timeout_ms %= MAX_DTLS_TIMEOUT; \
199 #define RESET_TIMER \
200 session->internals.dtls.actual_retrans_timeout_ms = session->internals.dtls.retrans_timeout_ms
202 #define TIMER_WINDOW session->internals.dtls.actual_retrans_timeout_ms
204 /* This function transmits the flight that has been previously
207 * This function is called from the handshake layer and calls the
210 int _dtls_transmit(gnutls_session_t session)
214 unsigned int timeout;
216 /* PREPARING -> SENDING state transition */
217 mbuffer_head_st *const send_buffer =
218 &session->internals.handshake_send_buffer;
220 gnutls_handshake_description_t last_type = 0;
226 /* If we have already sent a flight and we are operating in a
227 * non blocking way, check if it is time to retransmit or just
230 if (session->internals.dtls.flight_init != 0
231 && session->internals.dtls.blocking == 0) {
232 /* just in case previous run was interrupted */
233 ret = _gnutls_io_write_flush(session);
239 if (session->internals.dtls.last_flight == 0
240 || !_dtls_is_async(session)) {
242 ret = _gnutls_io_check_recv(session, 0);
243 if (ret == GNUTLS_E_TIMEDOUT) {
244 /* if no retransmission is required yet just return
248 &session->internals.dtls.
249 last_retransmit) < TIMER_WINDOW) {
253 } else { /* received something */
257 is_next_hpacket_expected
259 if (ret == GNUTLS_E_AGAIN
260 || ret == GNUTLS_E_INTERRUPTED)
264 GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET)
271 /* if ret == GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET retransmit */
279 timeout = TIMER_WINDOW;
282 timespec_sub_ms(&now,
283 &session->internals.dtls.
284 handshake_start_time);
285 if (diff >= session->internals.dtls.total_timeout_ms) {
286 _gnutls_dtls_log("Session timeout: %u ms\n", diff);
287 ret = gnutls_assert_val(GNUTLS_E_TIMEDOUT);
292 timespec_sub_ms(&now,
293 &session->internals.dtls.
295 if (session->internals.dtls.flight_init == 0
296 || diff >= TIMER_WINDOW) {
298 ("DTLS[%p]: %sStart of flight transmission.\n",
300 (session->internals.dtls.flight_init ==
302 for (cur = send_buffer->head; cur != NULL;
304 ret = transmit_message(session, cur, &buf);
310 last_type = cur->htype;
312 gettime(&session->internals.dtls.last_retransmit);
314 if (session->internals.dtls.flight_init == 0) {
315 session->internals.dtls.flight_init = 1;
317 timeout = TIMER_WINDOW;
319 if (last_type == GNUTLS_HANDSHAKE_FINISHED) {
320 /* On the last flight we cannot ensure retransmission
321 * from here. _dtls_wait_and_retransmit() is being called
324 session->internals.dtls.
327 session->internals.dtls.
334 ret = _gnutls_io_write_flush(session);
336 ret = gnutls_assert_val(ret);
340 /* last message in handshake -> no ack */
341 if (session->internals.dtls.last_flight != 0) {
342 /* we don't wait here. We just return 0 and
343 * if a retransmission occurs because peer didn't receive it
344 * we rely on the record or handshake
345 * layer calling this function again.
349 } else { /* all other messages -> implicit ack (receive of next flight) */
351 if (session->internals.dtls.blocking != 0)
353 _gnutls_io_check_recv(session,
356 ret = _gnutls_io_check_recv(session, 0);
357 if (ret == GNUTLS_E_TIMEDOUT) {
363 ret = is_next_hpacket_expected(session);
364 if (ret == GNUTLS_E_AGAIN
365 || ret == GNUTLS_E_INTERRUPTED)
369 GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET) {
370 ret = GNUTLS_E_TIMEDOUT;
383 } while (ret == GNUTLS_E_TIMEDOUT);
386 ret = gnutls_assert_val(ret);
393 _gnutls_dtls_log("DTLS[%p]: End of flight transmission.\n",
395 _dtls_reset_hsk_state(session);
401 /* SENDING -> WAITING state transition */
408 RETURN_DTLS_EAGAIN_OR_TIMEOUT(session, ret);
411 /* Waits for the last flight or retransmits
412 * the previous on timeout. Returns 0 on success.
414 int _dtls_wait_and_retransmit(gnutls_session_t session)
418 if (session->internals.dtls.blocking != 0)
419 ret = _gnutls_io_check_recv(session, TIMER_WINDOW);
421 ret = _gnutls_io_check_recv(session, 0);
423 if (ret == GNUTLS_E_TIMEDOUT) {
424 ret = _dtls_retransmit(session);
426 RETURN_DTLS_EAGAIN_OR_TIMEOUT(session, 0);
428 return gnutls_assert_val(ret);
435 #define window_table rp->record_sw
436 #define window_size rp->record_sw_size
437 #define window_head_idx rp->record_sw_head_idx
439 static void slide_window(struct record_parameters_st *rp,
442 unsigned int old_head = window_head_idx;
444 if (places < window_size) {
445 window_head_idx += places;
446 window_head_idx %= DTLS_RECORD_WINDOW_SIZE;
448 window_table[window_head_idx] =
449 window_table[old_head] + places;
451 unsigned int last_idx =
452 (window_head_idx + window_size - 1) % window_size;
453 window_table[window_head_idx] = window_table[last_idx];
457 /* Checks if a sequence number is not replayed. If replayed
458 * returns a negative error code, otherwise zero.
460 int _dtls_record_check(struct record_parameters_st *rp, uint64 * _seq)
462 uint64_t seq = 0, diff;
463 unsigned int i, offset = 0;
464 unsigned int last_idx;
466 for (i = 2; i < 8; i++) {
468 seq |= _seq->i[i] & 0xff;
471 /* only two values allowed in window_size */
472 if (window_size == 0) {
475 last_idx = window_size - 1;
476 window_table[last_idx] = window_table[window_head_idx] =
481 last_idx = (window_head_idx + window_size - 1) % window_size;
483 if (seq <= window_table[window_head_idx]) {
487 if (seq <= window_table[last_idx]) {
488 /* is between first and last */
489 diff = window_table[last_idx] - seq;
491 if (diff >= window_size) {
495 if (diff > last_idx) {
496 diff = diff - last_idx;
497 offset = window_size - 1 - diff;
499 offset = last_idx - diff;
501 if (window_table[offset] == seq) {
504 window_table[offset] = seq;
505 } else { /* seq > last */
507 diff = seq - window_table[last_idx];
509 if (window_size + diff <= DTLS_RECORD_WINDOW_SIZE) {
512 if (window_size < DTLS_RECORD_WINDOW_SIZE) {
514 DTLS_RECORD_WINDOW_SIZE - window_size;
515 window_size = DTLS_RECORD_WINDOW_SIZE;
520 slide_window(rp, diff);
523 offset = (window_head_idx + window_size - 1) % window_size;
524 window_table[offset] = seq;
531 * gnutls_dtls_set_timeouts:
532 * @session: is a #gnutls_session_t structure.
533 * @retrans_timeout: The time at which a retransmission will occur in milliseconds
534 * @total_timeout: The time at which the connection will be aborted, in milliseconds.
536 * This function will set the timeouts required for the DTLS handshake
537 * protocol. The retransmission timeout is the time after which a
538 * message from the peer is not received, the previous messages will
539 * be retransmitted. The total timeout is the time after which the
540 * handshake will be aborted with %GNUTLS_E_TIMEDOUT.
542 * The DTLS protocol recommends the values of 1 sec and 60 seconds
545 * If the retransmission timeout is zero then the handshake will operate
546 * in a non-blocking way, i.e., return %GNUTLS_E_AGAIN.
548 * To disable retransmissions set a @retrans_timeout larger than the @total_timeout.
552 void gnutls_dtls_set_timeouts(gnutls_session_t session,
553 unsigned int retrans_timeout,
554 unsigned int total_timeout)
556 session->internals.dtls.retrans_timeout_ms = retrans_timeout;
557 session->internals.dtls.total_timeout_ms = total_timeout;
561 * gnutls_dtls_set_mtu:
562 * @session: is a #gnutls_session_t structure.
563 * @mtu: The maximum transfer unit of the transport
565 * This function will set the maximum transfer unit of the transport
566 * that DTLS packets are sent over. Note that this should exclude
567 * the IP (or IPv6) and UDP headers. So for DTLS over IPv6 on an
568 * Ethenet device with MTU 1500, the DTLS MTU set with this function
569 * would be 1500 - 40 (IPV6 header) - 8 (UDP header) = 1452.
573 void gnutls_dtls_set_mtu(gnutls_session_t session, unsigned int mtu)
575 session->internals.dtls.mtu = MIN(mtu, DEFAULT_MAX_RECORD_SIZE);
578 static int record_overhead(const cipher_entry_st * cipher,
579 const mac_entry_st * mac,
580 gnutls_compression_method_t comp)
585 if (_gnutls_cipher_is_block(cipher) == CIPHER_BLOCK) {
586 t = _gnutls_cipher_get_explicit_iv_size(cipher);
590 t = _gnutls_cipher_get_block_size(cipher);
594 if (mac->id == GNUTLS_MAC_AEAD) {
595 total += AEAD_EXPLICIT_DATA_SIZE;
596 total += _gnutls_cipher_get_tag_size(cipher);
598 ret = _gnutls_mac_get_algo_len(mac);
599 if (unlikely(ret < 0))
605 if (comp != GNUTLS_COMP_NULL)
606 total += EXTRA_COMP_SIZE;
612 * gnutls_est_record_overhead_size:
613 * @version: is a #gnutls_protocol_t value
614 * @cipher: is a #gnutls_cipher_algorithm_t value
615 * @mac: is a #gnutls_mac_algorithm_t value
616 * @comp: is a #gnutls_compression_method_t value
617 * @flags: must be zero
619 * This function will return the set size in bytes of the overhead
620 * due to TLS (or DTLS) per record.
622 * Note that this function may provide inacurate values when TLS
623 * extensions that modify the record format are negotiated. In these
624 * cases a more accurate value can be obtained using gnutls_record_overhead_size()
625 * after a completed handshake.
629 size_t gnutls_est_record_overhead_size(gnutls_protocol_t version,
630 gnutls_cipher_algorithm_t cipher,
631 gnutls_mac_algorithm_t mac,
632 gnutls_compression_method_t comp,
635 const cipher_entry_st *c;
636 const mac_entry_st *m;
637 const version_entry_st *v;
640 c = cipher_to_entry(cipher);
644 m = mac_to_entry(mac);
648 v = version_to_entry(version);
652 if (v->transport == GNUTLS_STREAM)
653 total = TLS_RECORD_HEADER_SIZE;
655 total = DTLS_RECORD_HEADER_SIZE;
657 total += record_overhead(c, m, comp);
662 /* returns overhead imposed by the record layer (encryption/compression)
663 * etc. It does not include the record layer headers, since the caller
664 * needs to cope with rounding to multiples of blocksize, and the header
667 * blocksize: will contain the block size when padding may be required or 1
669 * It may return a negative error code on error.
671 static int record_overhead_rt(gnutls_session_t session)
673 record_parameters_st *params;
676 if (session->internals.initial_negotiation_completed == 0)
677 return GNUTLS_E_INVALID_REQUEST;
679 ret = _gnutls_epoch_get(session, EPOCH_WRITE_CURRENT, ¶ms);
681 return gnutls_assert_val(ret);
683 /* requires padding */
684 return record_overhead(params->cipher, params->mac,
685 params->compression_algorithm);
689 * gnutls_record_overhead_size:
690 * @session: is #gnutls_session_t
692 * This function will return the set size in bytes of the overhead
693 * due to TLS (or DTLS) per record.
697 size_t gnutls_record_overhead_size(gnutls_session_t session)
699 const version_entry_st *v = get_version(session);
702 if (v->transport == GNUTLS_STREAM)
703 total = TLS_RECORD_HEADER_SIZE;
705 total = DTLS_RECORD_HEADER_SIZE;
707 total += record_overhead_rt(session);
715 * gnutls_dtls_get_data_mtu:
716 * @session: is a #gnutls_session_t structure.
718 * This function will return the actual maximum transfer unit for
719 * application data. I.e. DTLS headers are subtracted from the
720 * actual MTU which is set using gnutls_dtls_set_mtu().
722 * Returns: the maximum allowed transfer unit.
726 unsigned int gnutls_dtls_get_data_mtu(gnutls_session_t session)
728 int mtu = session->internals.dtls.mtu;
731 mtu -= RECORD_HEADER_SIZE(session);
733 overhead = record_overhead_rt(session);
737 return mtu - overhead;
741 * gnutls_dtls_set_data_mtu:
742 * @session: is a #gnutls_session_t structure.
743 * @mtu: The maximum unencrypted transfer unit of the session
745 * This function will set the maximum size of the *unencrypted* records
746 * which will be sent over a DTLS session. It is equivalent to calculating
747 * the DTLS packet overhead with the current encryption parameters, and
748 * calling gnutls_dtls_set_mtu() with that value. In particular, this means
749 * that you may need to call this function again after any negotiation or
750 * renegotiation, in order to ensure that the MTU is still sufficient to
751 * account for the new protocol overhead.
753 * In most cases you only need to call gnutls_dtls_set_mtu() with
754 * the maximum MTU of your transport layer.
756 * Returns: %GNUTLS_E_SUCCESS (0) on success, or a negative error code.
760 int gnutls_dtls_set_data_mtu(gnutls_session_t session, unsigned int mtu)
762 int overhead = record_overhead_rt(session);
764 /* You can't call this until the session is actually running */
766 return GNUTLS_E_INVALID_SESSION;
768 /* Add the overhead inside the encrypted part */
771 /* Add the *unencrypted header size */
772 mtu += RECORD_HEADER_SIZE(session);
774 gnutls_dtls_set_mtu(session, mtu);
775 return GNUTLS_E_SUCCESS;
779 * gnutls_dtls_get_mtu:
780 * @session: is a #gnutls_session_t structure.
782 * This function will return the MTU size as set with
783 * gnutls_dtls_set_mtu(). This is not the actual MTU
784 * of data you can transmit. Use gnutls_dtls_get_data_mtu()
787 * Returns: the set maximum transfer unit.
791 unsigned int gnutls_dtls_get_mtu(gnutls_session_t session)
793 return session->internals.dtls.mtu;
797 * gnutls_dtls_get_timeout:
798 * @session: is a #gnutls_session_t structure.
800 * This function will return the milliseconds remaining
801 * for a retransmission of the previously sent handshake
802 * message. This function is useful when DTLS is used in
803 * non-blocking mode, to estimate when to call gnutls_handshake()
804 * if no packets have been received.
806 * Returns: the remaining time in milliseconds.
810 unsigned int gnutls_dtls_get_timeout(gnutls_session_t session)
818 timespec_sub_ms(&now,
819 &session->internals.dtls.last_retransmit);
820 if (diff >= TIMER_WINDOW)
823 return TIMER_WINDOW - diff;
826 #define COOKIE_SIZE 16
827 #define COOKIE_MAC_SIZE 16
835 #define C_HASH GNUTLS_MAC_SHA1
836 #define C_HASH_SIZE 20
839 * gnutls_dtls_cookie_send:
840 * @key: is a random key to be used at cookie generation
841 * @client_data: contains data identifying the client (i.e. address)
842 * @client_data_size: The size of client's data
843 * @prestate: The previous cookie returned by gnutls_dtls_cookie_verify()
844 * @ptr: A transport pointer to be used by @push_func
845 * @push_func: A function that will be used to reply
847 * This function can be used to prevent denial of service
848 * attacks to a DTLS server by requiring the client to
849 * reply using a cookie sent by this function. That way
850 * it can be ensured that a client we allocated resources
851 * for (i.e. #gnutls_session_t) is the one that the
852 * original incoming packet was originated from.
854 * This function must be called at the first incoming packet,
855 * prior to allocating any resources and must be succeeded
856 * by gnutls_dtls_cookie_verify().
858 * Returns: the number of bytes sent, or a negative error code.
862 int gnutls_dtls_cookie_send(gnutls_datum_t * key, void *client_data,
863 size_t client_data_size,
864 gnutls_dtls_prestate_st * prestate,
865 gnutls_transport_ptr_t ptr,
866 gnutls_push_func push_func)
868 uint8_t hvr[20 + DTLS_HANDSHAKE_HEADER_SIZE + COOKIE_SIZE];
869 int hvr_size = 0, ret;
870 uint8_t digest[C_HASH_SIZE];
872 if (key == NULL || key->data == NULL || key->size == 0)
873 return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
877 * ContentType type - 1 byte GNUTLS_HANDSHAKE;
878 * ProtocolVersion version; - 2 bytes (254,255)
879 * uint16 epoch; - 2 bytes (0, 0)
880 * uint48 sequence_number; - 4 bytes (0,0,0,0)
881 * uint16 length; - 2 bytes (COOKIE_SIZE+1+2)+DTLS_HANDSHAKE_HEADER_SIZE
882 * uint8_t fragment[DTLSPlaintext.length];
887 * HandshakeType msg_type; 1 byte - GNUTLS_HANDSHAKE_HELLO_VERIFY_REQUEST
888 * uint24 length; - COOKIE_SIZE+3
889 * uint16 message_seq; - 2 bytes (0,0)
890 * uint24 fragment_offset; - 3 bytes (0,0,0)
891 * uint24 fragment_length; - same as length
895 * ProtocolVersion server_version;
896 * uint8_t cookie<0..32>;
897 * } HelloVerifyRequest;
900 hvr[hvr_size++] = GNUTLS_HANDSHAKE;
902 hvr[hvr_size++] = 254;
903 hvr[hvr_size++] = 255;
906 memset(&hvr[hvr_size], 0, 8);
908 hvr[hvr_size++] = prestate->record_seq;
911 _gnutls_write_uint16(DTLS_HANDSHAKE_HEADER_SIZE + COOKIE_SIZE + 3,
915 /* now handshake headers */
916 hvr[hvr_size++] = GNUTLS_HANDSHAKE_HELLO_VERIFY_REQUEST;
917 _gnutls_write_uint24(COOKIE_SIZE + 3, &hvr[hvr_size]);
922 hvr[hvr_size++] = prestate->hsk_write_seq;
924 _gnutls_write_uint24(0, &hvr[hvr_size]);
927 _gnutls_write_uint24(COOKIE_SIZE + 3, &hvr[hvr_size]);
931 hvr[hvr_size++] = 254;
932 hvr[hvr_size++] = 255;
933 hvr[hvr_size++] = COOKIE_SIZE;
936 _gnutls_mac_fast(C_HASH, key->data, key->size, client_data,
937 client_data_size, digest);
939 return gnutls_assert_val(ret);
941 memcpy(&hvr[hvr_size], digest, COOKIE_MAC_SIZE);
942 hvr_size += COOKIE_MAC_SIZE;
944 ret = push_func(ptr, hvr, hvr_size);
946 ret = GNUTLS_E_PUSH_ERROR;
952 * gnutls_dtls_cookie_verify:
953 * @key: is a random key to be used at cookie generation
954 * @client_data: contains data identifying the client (i.e. address)
955 * @client_data_size: The size of client's data
956 * @_msg: An incoming message that initiates a connection.
957 * @msg_size: The size of the message.
958 * @prestate: The cookie of this client.
960 * This function will verify the received message for
961 * a valid cookie. If a valid cookie is returned then
962 * it should be associated with the session using
963 * gnutls_dtls_prestate_set();
965 * This function must be called after gnutls_dtls_cookie_send().
967 * Returns: %GNUTLS_E_SUCCESS (0) on success, or a negative error code.
971 int gnutls_dtls_cookie_verify(gnutls_datum_t * key,
972 void *client_data, size_t client_data_size,
973 void *_msg, size_t msg_size,
974 gnutls_dtls_prestate_st * prestate)
976 gnutls_datum_t cookie;
978 unsigned int pos, sid_size;
980 uint8_t digest[C_HASH_SIZE];
982 if (key == NULL || key->data == NULL || key->size == 0)
983 return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
988 * session_id - 1 byte length + content
989 * cookie - 1 byte length + content
992 pos = 34 + DTLS_RECORD_HEADER_SIZE + DTLS_HANDSHAKE_HEADER_SIZE;
994 if (msg_size < pos + 1)
996 gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
998 sid_size = msg[pos++];
1000 if (sid_size > 32 || msg_size < pos + sid_size + 1)
1002 gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
1005 cookie.size = msg[pos++];
1007 if (msg_size < pos + cookie.size + 1)
1009 gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
1011 cookie.data = &msg[pos];
1012 if (cookie.size != COOKIE_SIZE) {
1013 if (cookie.size > 0)
1014 _gnutls_audit_log(NULL,
1015 "Received cookie with illegal size %d. Expected %d\n",
1016 (int) cookie.size, COOKIE_SIZE);
1017 return gnutls_assert_val(GNUTLS_E_BAD_COOKIE);
1021 _gnutls_mac_fast(C_HASH, key->data, key->size, client_data,
1022 client_data_size, digest);
1024 return gnutls_assert_val(ret);
1026 if (memcmp(digest, cookie.data, COOKIE_MAC_SIZE) != 0)
1027 return gnutls_assert_val(GNUTLS_E_BAD_COOKIE);
1029 prestate->record_seq = msg[10]; /* client's record seq */
1030 prestate->hsk_read_seq = msg[DTLS_RECORD_HEADER_SIZE + 5]; /* client's hsk seq */
1031 prestate->hsk_write_seq = 0; /* we always send zero for this msg */
1037 * gnutls_dtls_prestate_set:
1038 * @session: a new session
1039 * @prestate: contains the client's prestate
1041 * This function will associate the prestate acquired by
1042 * the cookie authentication with the client, with the newly
1043 * established session.
1045 * This functions must be called after a successful gnutls_dtls_cookie_verify()
1046 * and should be succeeded by the actual DTLS handshake using gnutls_handshake().
1050 void gnutls_dtls_prestate_set(gnutls_session_t session,
1051 gnutls_dtls_prestate_st * prestate)
1053 record_parameters_st *params;
1056 if (prestate == NULL)
1059 /* we do not care about read_params, since we accept anything
1062 ret = _gnutls_epoch_get(session, EPOCH_WRITE_CURRENT, ¶ms);
1066 params->write.sequence_number.i[7] = prestate->record_seq;
1068 session->internals.dtls.hsk_read_seq = prestate->hsk_read_seq;
1069 session->internals.dtls.hsk_write_seq =
1070 prestate->hsk_write_seq + 1;
1074 * gnutls_record_get_discarded:
1075 * @session: is a #gnutls_session_t structure.
1077 * Returns the number of discarded packets in a
1080 * Returns: The number of discarded packets.
1084 unsigned int gnutls_record_get_discarded(gnutls_session_t session)
1086 return session->internals.dtls.packets_dropped;