Add gnutls_dtls_set_data_mtu()
[gnutls.git] / lib / gnutls_dtls.c
blob04aa646f7d84c71c1dbe7df546b85c8d41f147c6
1 /*
2 * Copyright (C) 2009-2012 Free Software Foundation, Inc.
4 * Authors: Jonathan Bastien-Filiatrault
5 * Nikos Mavrogiannopoulos
7 * This file is part of GNUTLS.
9 * The GNUTLS library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * as published by the Free Software Foundation; either version 3 of
12 * the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>
24 /* Functions that relate to DTLS retransmission and reassembly.
27 #include "gnutls_int.h"
28 #include "gnutls_errors.h"
29 #include "debug.h"
30 #include "gnutls_dtls.h"
31 #include "gnutls_record.h"
32 #include <gnutls_mbuffers.h>
33 #include <gnutls_buffers.h>
34 #include <gnutls_constate.h>
35 #include <gnutls_state.h>
36 #include <gnutls/dtls.h>
37 #include <timespec.h>
39 /* returns a-b in ms */
40 unsigned int
41 _dtls_timespec_sub_ms (struct timespec *a, struct timespec *b)
43 return (a->tv_sec * 1000 + a->tv_nsec / (1000 * 1000) -
44 (b->tv_sec * 1000 + b->tv_nsec / (1000 * 1000)));
47 void
48 _dtls_async_timer_delete (gnutls_session_t session)
50 if (session->internals.dtls.async_term != 0)
52 _gnutls_dtls_log ("DTLS[%p]: Deinitializing previous handshake state.\n", session);
53 session->internals.dtls.async_term = 0; /* turn off "timer" */
55 _dtls_reset_hsk_state(session);
56 _gnutls_handshake_io_buffer_clear (session);
57 _gnutls_epoch_gc(session);
61 /* This function fragments and transmits a previously buffered
62 * outgoing message. It accepts mtu_data which is a buffer to
63 * be reused (should be set to NULL initially).
65 static inline int
66 transmit_message (gnutls_session_t session,
67 mbuffer_st *bufel, uint8_t **buf)
69 uint8_t *data, *mtu_data;
70 int ret = 0;
71 unsigned int offset, frag_len, data_size;
72 const unsigned int mtu = gnutls_dtls_get_data_mtu(session) - DTLS_HANDSHAKE_HEADER_SIZE;
74 if (bufel->type == GNUTLS_CHANGE_CIPHER_SPEC)
76 _gnutls_dtls_log ("DTLS[%p]: Sending Packet[%u] fragment %s(%d)\n",
77 session, bufel->handshake_sequence,
78 _gnutls_handshake2str (bufel->htype),
79 bufel->htype);
81 return _gnutls_send_int (session, bufel->type, -1,
82 bufel->epoch,
83 _mbuffer_get_uhead_ptr(bufel),
84 _mbuffer_get_uhead_size(bufel), 0);
87 if (*buf == NULL) *buf = gnutls_malloc(mtu + DTLS_HANDSHAKE_HEADER_SIZE);
88 if (*buf == NULL)
89 return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
91 mtu_data = *buf;
93 data = _mbuffer_get_udata_ptr( bufel);
94 data_size = _mbuffer_get_udata_size(bufel);
96 /* Write fixed headers
99 /* Handshake type */
100 mtu_data[0] = (uint8_t) bufel->htype;
102 /* Total length */
103 _gnutls_write_uint24 (data_size, &mtu_data[1]);
105 /* Handshake sequence */
106 _gnutls_write_uint16 (bufel->handshake_sequence, &mtu_data[4]);
108 /* Chop up and send handshake message into mtu-size pieces. */
109 for (offset=0; offset <= data_size; offset += mtu)
111 /* Calculate fragment length */
112 if(offset + mtu > data_size)
113 frag_len = data_size - offset;
114 else
115 frag_len = mtu;
117 /* Fragment offset */
118 _gnutls_write_uint24 (offset, &mtu_data[6]);
120 /* Fragment length */
121 _gnutls_write_uint24 (frag_len, &mtu_data[9]);
123 memcpy (&mtu_data[DTLS_HANDSHAKE_HEADER_SIZE], data+offset, frag_len);
125 _gnutls_dtls_log ("DTLS[%p]: Sending Packet[%u] fragment %s(%d) with "
126 "length: %u, offset: %u, fragment length: %u\n",
127 session, bufel->handshake_sequence,
128 _gnutls_handshake2str (bufel->htype),
129 bufel->htype, data_size, offset, frag_len);
131 ret = _gnutls_send_int (session, bufel->type, bufel->htype,
132 bufel->epoch, mtu_data, DTLS_HANDSHAKE_HEADER_SIZE + frag_len, 0);
133 if (ret < 0)
135 gnutls_assert();
136 break;
140 return ret;
143 static int drop_usage_count(gnutls_session_t session, mbuffer_head_st *const send_buffer)
145 int ret;
146 mbuffer_st *cur;
148 for (cur = send_buffer->head;
149 cur != NULL; cur = cur->next)
151 ret = _gnutls_epoch_refcount_dec(session, cur->epoch);
152 if (ret < 0)
153 return gnutls_assert_val(ret);
156 return 0;
159 /* This function is to be called from record layer once
160 * a handshake replay is detected. It will make sure
161 * it transmits only once per few seconds. Otherwise
162 * it is the same as _dtls_transmit().
164 int _dtls_retransmit(gnutls_session_t session)
166 return _dtls_transmit(session);
169 /* Checks whether the received packet contains a handshake
170 * packet with sequence higher that the previously received.
171 * It must be called only when an actual packet has been
172 * received.
174 * Returns: 0 if expected, negative value otherwise.
176 static int is_next_hpacket_expected(gnutls_session_t session)
178 int ret;
180 /* htype is arbitrary */
181 ret = _gnutls_recv_in_buffers(session, GNUTLS_HANDSHAKE, GNUTLS_HANDSHAKE_FINISHED);
182 if (ret < 0)
183 return gnutls_assert_val(ret);
185 ret = _gnutls_parse_record_buffered_msgs(session);
186 if (ret < 0)
187 return gnutls_assert_val(ret);
189 if (session->internals.handshake_recv_buffer_size > 0)
190 return 0;
191 else
192 return gnutls_assert_val(GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET);
195 void _dtls_reset_hsk_state(gnutls_session_t session)
197 session->internals.dtls.flight_init = 0;
198 drop_usage_count(session, &session->internals.handshake_send_buffer);
199 _mbuffer_head_clear(&session->internals.handshake_send_buffer);
203 #define UPDATE_TIMER { \
204 session->internals.dtls.actual_retrans_timeout_ms *= 2; \
205 session->internals.dtls.actual_retrans_timeout_ms %= MAX_DTLS_TIMEOUT; \
208 #define RESET_TIMER \
209 session->internals.dtls.actual_retrans_timeout_ms = session->internals.dtls.retrans_timeout_ms
211 #define TIMER_WINDOW session->internals.dtls.actual_retrans_timeout_ms
213 /* This function transmits the flight that has been previously
214 * buffered.
216 * This function is called from the handshake layer and calls the
217 * record layer.
220 _dtls_transmit (gnutls_session_t session)
222 int ret;
223 uint8_t* buf = NULL;
224 unsigned int timeout;
226 /* PREPARING -> SENDING state transition */
227 mbuffer_head_st *const send_buffer =
228 &session->internals.handshake_send_buffer;
229 mbuffer_st *cur;
230 gnutls_handshake_description_t last_type = 0;
231 unsigned int diff;
232 struct timespec now;
234 gettime(&now);
236 /* If we have already sent a flight and we are operating in a
237 * non blocking way, check if it is time to retransmit or just
238 * return.
240 if (session->internals.dtls.flight_init != 0 && session->internals.dtls.blocking == 0)
242 /* just in case previous run was interrupted */
243 ret = _gnutls_io_write_flush (session);
244 if (ret < 0)
246 gnutls_assert();
247 goto cleanup;
250 if (session->internals.dtls.last_flight == 0 || !_dtls_is_async(session))
252 /* check for ACK */
253 ret = _gnutls_io_check_recv(session, 0);
254 if (ret == GNUTLS_E_TIMEDOUT)
256 /* if no retransmission is required yet just return
258 if (_dtls_timespec_sub_ms(&now, &session->internals.dtls.last_retransmit) < TIMER_WINDOW)
260 gnutls_assert();
261 goto nb_timeout;
264 else /* received something */
266 if (ret == 0)
268 ret = is_next_hpacket_expected(session);
269 if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
270 goto nb_timeout;
271 if (ret < 0 && ret != GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET)
273 gnutls_assert();
274 goto cleanup;
276 if (ret == 0) goto end_flight;
277 /* if ret == GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET retransmit */
279 else
280 goto nb_timeout;
287 timeout = TIMER_WINDOW;
289 diff = _dtls_timespec_sub_ms(&now, &session->internals.dtls.handshake_start_time);
290 if (diff >= session->internals.dtls.total_timeout_ms)
292 _gnutls_dtls_log("Session timeout: %u ms\n", diff);
293 ret = gnutls_assert_val(GNUTLS_E_TIMEDOUT);
294 goto end_flight;
297 diff = _dtls_timespec_sub_ms(&now, &session->internals.dtls.last_retransmit);
298 if (session->internals.dtls.flight_init == 0 || diff >= TIMER_WINDOW)
300 _gnutls_dtls_log ("DTLS[%p]: %sStart of flight transmission.\n", session, (session->internals.dtls.flight_init == 0)?"":"re-");
301 for (cur = send_buffer->head;
302 cur != NULL; cur = cur->next)
304 ret = transmit_message (session, cur, &buf);
305 if (ret < 0)
307 gnutls_assert();
308 goto end_flight;
311 last_type = cur->htype;
313 gettime(&session->internals.dtls.last_retransmit);
315 if (session->internals.dtls.flight_init == 0)
317 session->internals.dtls.flight_init = 1;
318 RESET_TIMER;
319 timeout = TIMER_WINDOW;
321 if (last_type == GNUTLS_HANDSHAKE_FINISHED)
323 /* On the last flight we cannot ensure retransmission
324 * from here. _dtls_wait_and_retransmit() is being called
325 * by handshake.
327 session->internals.dtls.last_flight = 1;
329 else
330 session->internals.dtls.last_flight = 0;
332 else
334 UPDATE_TIMER;
338 ret = _gnutls_io_write_flush (session);
339 if (ret < 0)
341 ret = gnutls_assert_val(ret);
342 goto cleanup;
345 /* last message in handshake -> no ack */
346 if (session->internals.dtls.last_flight != 0)
348 /* we don't wait here. We just return 0 and
349 * if a retransmission occurs because peer didn't receive it
350 * we rely on the record or handshake
351 * layer calling this function again.
353 ret = 0;
354 goto cleanup;
356 else /* all other messages -> implicit ack (receive of next flight) */
358 if (session->internals.dtls.blocking != 0)
359 ret = _gnutls_io_check_recv(session, timeout);
360 else
362 ret = _gnutls_io_check_recv(session, 0);
363 if (ret == GNUTLS_E_TIMEDOUT)
365 goto nb_timeout;
369 if (ret == 0)
371 ret = is_next_hpacket_expected(session);
372 if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
373 goto nb_timeout;
375 if (ret == GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET)
377 ret = GNUTLS_E_TIMEDOUT;
378 goto keep_up;
380 if (ret < 0)
382 gnutls_assert();
383 goto cleanup;
385 goto end_flight;
389 keep_up:
390 gettime(&now);
391 } while(ret == GNUTLS_E_TIMEDOUT);
393 if (ret < 0)
395 ret = gnutls_assert_val(ret);
396 goto end_flight;
399 ret = 0;
401 end_flight:
402 _gnutls_dtls_log ("DTLS[%p]: End of flight transmission.\n", session);
403 _dtls_reset_hsk_state(session);
405 cleanup:
406 if (buf != NULL)
407 gnutls_free(buf);
409 /* SENDING -> WAITING state transition */
410 return ret;
412 nb_timeout:
413 if (buf != NULL)
414 gnutls_free(buf);
416 RETURN_DTLS_EAGAIN_OR_TIMEOUT(session, ret);
419 /* Waits for the last flight or retransmits
420 * the previous on timeout. Returns 0 on success.
422 int _dtls_wait_and_retransmit(gnutls_session_t session)
424 int ret;
426 if (session->internals.dtls.blocking != 0)
427 ret = _gnutls_io_check_recv(session, TIMER_WINDOW);
428 else
429 ret = _gnutls_io_check_recv(session, 0);
431 if (ret == GNUTLS_E_TIMEDOUT)
433 ret = _dtls_retransmit(session);
434 if (ret == 0)
436 RETURN_DTLS_EAGAIN_OR_TIMEOUT(session, 0);
438 else
439 return gnutls_assert_val(ret);
442 RESET_TIMER;
443 return 0;
447 #define window_table rp->record_sw
448 #define window_size rp->record_sw_size
450 /* FIXME: could we modify that code to avoid using
451 * uint64_t?
454 static void rot_window(struct record_parameters_st * rp, int places)
456 window_size -= places;
457 memmove(window_table, &window_table[places], window_size*sizeof(window_table[0]));
460 #define MOVE_SIZE 20
461 /* Checks if a sequence number is not replayed. If replayed
462 * returns a negative error code, otherwise zero.
464 int _dtls_record_check(struct record_parameters_st *rp, uint64 * _seq)
466 uint64_t seq = 0, diff;
467 unsigned int i, offset = 0;
469 for (i=2;i<8;i++)
471 seq <<= 8;
472 seq |= _seq->i[i] & 0xff;
475 if (window_size == 0)
477 window_size = 1;
478 window_table[0] = seq;
479 return 0;
482 if (seq <= window_table[0])
484 return -1;
487 if (window_size == DTLS_RECORD_WINDOW_SIZE)
489 rot_window(rp, MOVE_SIZE);
492 if (seq < window_table[window_size-1])
494 /* is between first and last */
495 diff = window_table[window_size-1] - seq;
497 if (diff >= window_size)
498 return -1;
500 offset = window_size-1-diff;
502 if (window_table[offset] == seq)
503 return -1;
504 else
505 window_table[offset] = seq;
507 else /* seq >= last */
509 if (seq == window_table[window_size-1])
511 return -1;
514 diff = seq - window_table[window_size-1];
515 if (diff <= DTLS_RECORD_WINDOW_SIZE - window_size)
516 { /* fits in our empty space */
517 offset = diff + window_size-1;
519 window_table[offset] = seq;
520 window_size = offset + 1;
522 else
524 if (diff > DTLS_RECORD_WINDOW_SIZE/2)
525 { /* difference is too big */
526 window_table[DTLS_RECORD_WINDOW_SIZE-1] = seq;
527 window_size = DTLS_RECORD_WINDOW_SIZE;
529 else
531 rot_window(rp, diff);
532 offset = diff + window_size-1;
533 window_table[offset] = seq;
534 window_size = offset + 1;
538 return 0;
543 * gnutls_dtls_set_timeouts:
544 * @session: is a #gnutls_session_t structure.
545 * @retrans_timeout: The time at which a retransmission will occur in milliseconds
546 * @total_timeout: The time at which the connection will be aborted, in milliseconds.
548 * This function will set the timeouts required for the DTLS handshake
549 * protocol. The retransmission timeout is the time after which a
550 * message from the peer is not received, the previous messages will
551 * be retransmitted. The total timeout is the time after which the
552 * handshake will be aborted with %GNUTLS_E_TIMEDOUT.
554 * The DTLS protocol recommends the values of 1 sec and 60 seconds
555 * respectively.
557 * If the retransmission timeout is zero then the handshake will operate
558 * in a non-blocking way, i.e., return %GNUTLS_E_AGAIN.
560 * Since: 3.0
562 void gnutls_dtls_set_timeouts (gnutls_session_t session, unsigned int retrans_timeout,
563 unsigned int total_timeout)
565 session->internals.dtls.retrans_timeout_ms = retrans_timeout;
566 session->internals.dtls.total_timeout_ms = total_timeout;
570 * gnutls_dtls_set_mtu:
571 * @session: is a #gnutls_session_t structure.
572 * @mtu: The maximum transfer unit of the interface
574 * This function will set the maximum transfer unit of the interface
575 * that DTLS packets are expected to leave from.
577 * Since: 3.0
579 void gnutls_dtls_set_mtu (gnutls_session_t session, unsigned int mtu)
581 session->internals.dtls.mtu = mtu;
584 /* returns overhead imposed by the record layer (encryption/compression)
585 * etc. It does not include the record layer headers, since the caller
586 * needs to cope with rounding to multiples of blocksize, and the header
587 * is outside that.
589 * blocksize: will contain the block size when padding may be required or 1
591 * It may return a negative error code on error.
593 static int _gnutls_record_overhead_rt(gnutls_session_t session, unsigned int *blocksize)
595 record_parameters_st *params;
596 int total = 0, ret, iv_size;
598 if (session->internals.initial_negotiation_completed == 0)
599 return GNUTLS_E_INVALID_REQUEST;
601 ret = _gnutls_epoch_get (session, EPOCH_WRITE_CURRENT, &params);
602 if (ret < 0)
603 return gnutls_assert_val(ret);
605 /* requires padding */
606 iv_size = _gnutls_cipher_get_iv_size(params->cipher_algorithm);
608 if (_gnutls_cipher_is_block (params->cipher_algorithm) == CIPHER_BLOCK)
610 *blocksize = iv_size;
612 if (!IS_DTLS(session))
613 total += MAX_PAD_SIZE;
614 else
615 total += iv_size; /* iv_size == block_size */
617 else
619 *blocksize = 1;
622 if (params->mac_algorithm == GNUTLS_MAC_AEAD)
623 total += _gnutls_cipher_get_tag_size(params->cipher_algorithm);
624 else
626 ret = _gnutls_hmac_get_algo_len(params->mac_algorithm);
627 if (ret < 0)
628 return gnutls_assert_val(ret);
629 total+=ret;
632 if (params->compression_algorithm != GNUTLS_COMP_NULL)
633 total += EXTRA_COMP_SIZE;
635 /* We always pad with at least one byte; never 0. */
636 total++;
638 return total;
642 * gnutls_dtls_get_data_mtu:
643 * @session: is a #gnutls_session_t structure.
645 * This function will return the actual maximum transfer unit for
646 * application data. I.e. DTLS headers are subtracted from the
647 * actual MTU.
649 * Returns: the maximum allowed transfer unit.
651 * Since: 3.0
653 unsigned int gnutls_dtls_get_data_mtu (gnutls_session_t session)
655 int mtu = session->internals.dtls.mtu;
656 int blocksize = 0;
657 int overhead;
659 mtu -= RECORD_HEADER_SIZE(session);
661 overhead = _gnutls_record_overhead_rt(session, &blocksize);
662 if (overhead < 0)
663 return mtu;
665 if (blocksize)
666 mtu -= mtu % blocksize;
668 return mtu - overhead;
672 * gnutls_dtls_set_data_mtu:
673 * @session: is a #gnutls_session_t structure.
674 * @mtu: The maximum unencrypted transfer unit of the session
676 * This function will set the maximum size of the *unencrypted* records
677 * which will be sent over a DTLS session. It is equivalent to calculating
678 * the DTLS packet overhead with the current encryption parameters, and
679 * calling gnutls_dtls_set_mtu() with that value. In particular, this means
680 * that you may need to call this function again after any negotiation or
681 * renegotiation, in order to ensure that the MTU is still sufficient to
682 * account for the new protocol overhead.
684 * Returns: %GNUTLS_E_SUCCESS (0) on success, or a negative error code.
686 * Since: 3.1
688 int gnutls_dtls_set_data_mtu (gnutls_session_t session, unsigned int mtu)
690 int blocksize;
691 int overhead = _gnutls_record_overhead_rt(session, &blocksize);
693 /* You can't call this until the session is actually running */
694 if (overhead < 0)
695 return GNUTLS_E_INVALID_SESSION;
697 /* Add the overhead inside the encrypted part */
698 mtu += overhead;
700 /* Round it up to the next multiple of blocksize */
701 mtu += blocksize - 1;
702 mtu -= mtu % blocksize;
704 /* Add the *unencrypted header size */
705 mtu += RECORD_HEADER_SIZE(session);
707 gnutls_dtls_set_mtu(session, mtu);
708 return GNUTLS_E_SUCCESS;
712 * gnutls_dtls_get_mtu:
713 * @session: is a #gnutls_session_t structure.
715 * This function will return the MTU size as set with
716 * gnutls_dtls_set_mtu(). This is not the actual MTU
717 * of data you can transmit. Use gnutls_dtls_get_data_mtu()
718 * for that reason.
720 * Returns: the set maximum transfer unit.
722 * Since: 3.0
724 unsigned int gnutls_dtls_get_mtu (gnutls_session_t session)
726 return session->internals.dtls.mtu;
730 * gnutls_dtls_get_timeout:
731 * @session: is a #gnutls_session_t structure.
733 * This function will return the milliseconds remaining
734 * for a retransmission of the previously sent handshake
735 * message. This function is useful when DTLS is used in
736 * non-blocking mode, to estimate when to call gnutls_handshake()
737 * if no packets have been received.
739 * Returns: the remaining time in milliseconds.
741 * Since: 3.0
743 unsigned int gnutls_dtls_get_timeout (gnutls_session_t session)
745 struct timespec now;
746 unsigned int diff;
748 gettime(&now);
750 diff = _dtls_timespec_sub_ms(&now, &session->internals.dtls.last_retransmit);
751 if (diff >= TIMER_WINDOW)
752 return 0;
753 else
754 return TIMER_WINDOW - diff;
757 #define COOKIE_SIZE 16
758 #define COOKIE_MAC_SIZE 16
760 /* MAC
761 * 16 bytes
763 * total 19 bytes
766 #define C_HASH GNUTLS_MAC_SHA1
767 #define C_HASH_SIZE 20
770 * gnutls_dtls_cookie_send:
771 * @key: is a random key to be used at cookie generation
772 * @client_data: contains data identifying the client (i.e. address)
773 * @client_data_size: The size of client's data
774 * @prestate: The previous cookie returned by gnutls_dtls_cookie_verify()
775 * @ptr: A transport pointer to be used by @push_func
776 * @push_func: A function that will be used to reply
778 * This function can be used to prevent denial of service
779 * attacks to a DTLS server by requiring the client to
780 * reply using a cookie sent by this function. That way
781 * it can be ensured that a client we allocated resources
782 * for (i.e. #gnutls_session_t) is the one that the
783 * original incoming packet was originated from.
785 * Returns: the number of bytes sent, or a negative error code.
787 * Since: 3.0
789 int gnutls_dtls_cookie_send(gnutls_datum_t* key, void* client_data, size_t client_data_size,
790 gnutls_dtls_prestate_st* prestate,
791 gnutls_transport_ptr_t ptr, gnutls_push_func push_func)
793 uint8_t hvr[20+DTLS_HANDSHAKE_HEADER_SIZE+COOKIE_SIZE];
794 int hvr_size = 0, ret;
795 uint8_t digest[C_HASH_SIZE];
797 if (key == NULL || key->data == NULL || key->size == 0)
798 return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
800 /* send
801 * struct {
802 * ContentType type - 1 byte GNUTLS_HANDSHAKE;
803 * ProtocolVersion version; - 2 bytes (254,255)
804 * uint16 epoch; - 2 bytes (0, 0)
805 * uint48 sequence_number; - 4 bytes (0,0,0,0)
806 * uint16 length; - 2 bytes (COOKIE_SIZE+1+2)+DTLS_HANDSHAKE_HEADER_SIZE
807 * uint8_t fragment[DTLSPlaintext.length];
808 * } DTLSPlaintext;
811 * struct {
812 * HandshakeType msg_type; 1 byte - GNUTLS_HANDSHAKE_HELLO_VERIFY_REQUEST
813 * uint24 length; - COOKIE_SIZE+3
814 * uint16 message_seq; - 2 bytes (0,0)
815 * uint24 fragment_offset; - 3 bytes (0,0,0)
816 * uint24 fragment_length; - same as length
819 * struct {
820 * ProtocolVersion server_version;
821 * uint8_t cookie<0..32>;
822 * } HelloVerifyRequest;
825 hvr[hvr_size++] = GNUTLS_HANDSHAKE;
826 /* version */
827 hvr[hvr_size++] = 254;
828 hvr[hvr_size++] = 255;
830 /* epoch + seq */
831 memset(&hvr[hvr_size], 0, 8);
832 hvr_size += 7;
833 hvr[hvr_size++] = prestate->record_seq;
835 /* length */
836 _gnutls_write_uint16(DTLS_HANDSHAKE_HEADER_SIZE+COOKIE_SIZE+3, &hvr[hvr_size]);
837 hvr_size += 2;
839 /* now handshake headers */
840 hvr[hvr_size++] = GNUTLS_HANDSHAKE_HELLO_VERIFY_REQUEST;
841 _gnutls_write_uint24(COOKIE_SIZE+3, &hvr[hvr_size]);
842 hvr_size += 3;
844 /* handshake seq */
845 hvr[hvr_size++] = 0;
846 hvr[hvr_size++] = prestate->hsk_write_seq;
848 _gnutls_write_uint24(0, &hvr[hvr_size]);
849 hvr_size += 3;
851 _gnutls_write_uint24(COOKIE_SIZE+3, &hvr[hvr_size]);
852 hvr_size += 3;
854 /* version */
855 hvr[hvr_size++] = 254;
856 hvr[hvr_size++] = 255;
857 hvr[hvr_size++] = COOKIE_SIZE;
859 ret = _gnutls_hmac_fast(C_HASH, key->data, key->size, client_data, client_data_size, digest);
860 if (ret < 0)
861 return gnutls_assert_val(ret);
863 memcpy(&hvr[hvr_size], digest, COOKIE_MAC_SIZE);
864 hvr_size+= COOKIE_MAC_SIZE;
866 ret = push_func(ptr, hvr, hvr_size);
867 if (ret < 0)
868 ret = GNUTLS_E_PUSH_ERROR;
870 return ret;
874 * gnutls_dtls_cookie_verify:
875 * @key: is a random key to be used at cookie generation
876 * @client_data: contains data identifying the client (i.e. address)
877 * @client_data_size: The size of client's data
878 * @_msg: An incoming message that initiates a connection.
879 * @msg_size: The size of the message.
880 * @prestate: The cookie of this client.
882 * This function will verify an incoming message for
883 * a valid cookie. If a valid cookie is returned then
884 * it should be associated with the session using
885 * gnutls_dtls_prestate_set();
887 * Returns: %GNUTLS_E_SUCCESS (0) on success, or a negative error code.
889 * Since: 3.0
891 int gnutls_dtls_cookie_verify(gnutls_datum_t* key,
892 void* client_data, size_t client_data_size,
893 void* _msg, size_t msg_size, gnutls_dtls_prestate_st* prestate)
895 gnutls_datum_t cookie;
896 int ret;
897 unsigned int pos, sid_size;
898 uint8_t * msg = _msg;
899 uint8_t digest[C_HASH_SIZE];
901 if (key == NULL || key->data == NULL || key->size == 0)
902 return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
904 /* format:
905 * version - 2 bytes
906 * random - 32 bytes
907 * session_id - 1 byte length + content
908 * cookie - 1 byte length + content
911 pos = 34+DTLS_RECORD_HEADER_SIZE+DTLS_HANDSHAKE_HEADER_SIZE;
913 if (msg_size < pos+1)
914 return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
916 sid_size = msg[pos++];
918 if (sid_size > 32 || msg_size < pos+sid_size+1)
919 return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
921 pos += sid_size;
922 cookie.size = msg[pos++];
924 if (msg_size < pos+cookie.size+1)
925 return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
927 cookie.data = &msg[pos];
928 if (cookie.size != COOKIE_SIZE)
930 if (cookie.size > 0) _gnutls_audit_log(NULL, "Received cookie with illegal size %d. Expected %d\n", (int)cookie.size, COOKIE_SIZE);
931 return gnutls_assert_val(GNUTLS_E_BAD_COOKIE);
934 ret = _gnutls_hmac_fast(C_HASH, key->data, key->size, client_data, client_data_size, digest);
935 if (ret < 0)
936 return gnutls_assert_val(ret);
938 if (memcmp(digest, cookie.data, COOKIE_MAC_SIZE) != 0)
939 return gnutls_assert_val(GNUTLS_E_BAD_COOKIE);
941 prestate->record_seq = msg[10]; /* client's record seq */
942 prestate->hsk_read_seq = msg[DTLS_RECORD_HEADER_SIZE+5]; /* client's hsk seq */
943 prestate->hsk_write_seq = 0;/* we always send zero for this msg */
945 return 0;
949 * gnutls_dtls_prestate_set:
950 * @session: a new session
951 * @prestate: contains the client's prestate
953 * This function will associate the prestate acquired by
954 * the cookie authentication with the client, with the newly
955 * established session.
957 * Since: 3.0
959 void gnutls_dtls_prestate_set(gnutls_session_t session, gnutls_dtls_prestate_st* prestate)
961 record_parameters_st *params;
962 int ret;
964 if (prestate == NULL)
965 return;
967 /* we do not care about read_params, since we accept anything
968 * the peer sends.
970 ret = _gnutls_epoch_get (session, EPOCH_WRITE_CURRENT, &params);
971 if (ret < 0)
972 return;
974 params->write.sequence_number.i[7] = prestate->record_seq;
976 session->internals.dtls.hsk_read_seq = prestate->hsk_read_seq;
977 session->internals.dtls.hsk_write_seq = prestate->hsk_write_seq + 1;
981 * gnutls_record_get_discarded:
982 * @session: is a #gnutls_session_t structure.
984 * Returns the number of discarded packets in a
985 * DTLS connection.
987 * Returns: The number of discarded packets.
989 * Since: 3.0
991 unsigned int gnutls_record_get_discarded (gnutls_session_t session)
993 return session->internals.dtls.packets_dropped;