no need to check for DTLS
[gnutls.git] / lib / gnutls_dtls.c
blob7a78f8fccf379f1d74ed2ddbe9c3a669f2920dc3
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>
38 #include <algorithms.h>
40 /* returns a-b in ms */
41 unsigned int
42 _dtls_timespec_sub_ms (struct timespec *a, struct timespec *b)
44 return (a->tv_sec * 1000 + a->tv_nsec / (1000 * 1000) -
45 (b->tv_sec * 1000 + b->tv_nsec / (1000 * 1000)));
48 void
49 _dtls_async_timer_delete (gnutls_session_t session)
51 if (session->internals.dtls.async_term != 0)
53 _gnutls_dtls_log ("DTLS[%p]: Deinitializing previous handshake state.\n", session);
54 session->internals.dtls.async_term = 0; /* turn off "timer" */
56 _dtls_reset_hsk_state(session);
57 _gnutls_handshake_io_buffer_clear (session);
58 _gnutls_epoch_gc(session);
62 /* This function fragments and transmits a previously buffered
63 * outgoing message. It accepts mtu_data which is a buffer to
64 * be reused (should be set to NULL initially).
66 static inline int
67 transmit_message (gnutls_session_t session,
68 mbuffer_st *bufel, uint8_t **buf)
70 uint8_t *data, *mtu_data;
71 int ret = 0;
72 unsigned int offset, frag_len, data_size;
73 const unsigned int mtu = gnutls_dtls_get_data_mtu(session) - DTLS_HANDSHAKE_HEADER_SIZE;
75 if (bufel->type == GNUTLS_CHANGE_CIPHER_SPEC)
77 _gnutls_dtls_log ("DTLS[%p]: Sending Packet[%u] fragment %s(%d)\n",
78 session, bufel->handshake_sequence,
79 _gnutls_handshake2str (bufel->htype),
80 bufel->htype);
82 return _gnutls_send_int (session, bufel->type, -1,
83 bufel->epoch,
84 _mbuffer_get_uhead_ptr(bufel),
85 _mbuffer_get_uhead_size(bufel), 0);
88 if (*buf == NULL) *buf = gnutls_malloc(mtu + DTLS_HANDSHAKE_HEADER_SIZE);
89 if (*buf == NULL)
90 return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
92 mtu_data = *buf;
94 data = _mbuffer_get_udata_ptr( bufel);
95 data_size = _mbuffer_get_udata_size(bufel);
97 /* Write fixed headers
100 /* Handshake type */
101 mtu_data[0] = (uint8_t) bufel->htype;
103 /* Total length */
104 _gnutls_write_uint24 (data_size, &mtu_data[1]);
106 /* Handshake sequence */
107 _gnutls_write_uint16 (bufel->handshake_sequence, &mtu_data[4]);
109 /* Chop up and send handshake message into mtu-size pieces. */
110 for (offset=0; offset <= data_size; offset += mtu)
112 /* Calculate fragment length */
113 if(offset + mtu > data_size)
114 frag_len = data_size - offset;
115 else
116 frag_len = mtu;
118 /* Fragment offset */
119 _gnutls_write_uint24 (offset, &mtu_data[6]);
121 /* Fragment length */
122 _gnutls_write_uint24 (frag_len, &mtu_data[9]);
124 memcpy (&mtu_data[DTLS_HANDSHAKE_HEADER_SIZE], data+offset, frag_len);
126 _gnutls_dtls_log ("DTLS[%p]: Sending Packet[%u] fragment %s(%d) with "
127 "length: %u, offset: %u, fragment length: %u\n",
128 session, bufel->handshake_sequence,
129 _gnutls_handshake2str (bufel->htype),
130 bufel->htype, data_size, offset, frag_len);
132 ret = _gnutls_send_int (session, bufel->type, bufel->htype,
133 bufel->epoch, mtu_data, DTLS_HANDSHAKE_HEADER_SIZE + frag_len, 0);
134 if (ret < 0)
136 gnutls_assert();
137 break;
141 return ret;
144 static int drop_usage_count(gnutls_session_t session, mbuffer_head_st *const send_buffer)
146 int ret;
147 mbuffer_st *cur;
149 for (cur = send_buffer->head;
150 cur != NULL; cur = cur->next)
152 ret = _gnutls_epoch_refcount_dec(session, cur->epoch);
153 if (ret < 0)
154 return gnutls_assert_val(ret);
157 return 0;
160 /* This function is to be called from record layer once
161 * a handshake replay is detected. It will make sure
162 * it transmits only once per few seconds. Otherwise
163 * it is the same as _dtls_transmit().
165 int _dtls_retransmit(gnutls_session_t session)
167 return _dtls_transmit(session);
170 /* Checks whether the received packet contains a handshake
171 * packet with sequence higher that the previously received.
172 * It must be called only when an actual packet has been
173 * received.
175 * Returns: 0 if expected, negative value otherwise.
177 static int is_next_hpacket_expected(gnutls_session_t session)
179 int ret;
181 /* htype is arbitrary */
182 ret = _gnutls_recv_in_buffers(session, GNUTLS_HANDSHAKE, GNUTLS_HANDSHAKE_FINISHED);
183 if (ret < 0)
184 return gnutls_assert_val(ret);
186 ret = _gnutls_parse_record_buffered_msgs(session);
187 if (ret < 0)
188 return gnutls_assert_val(ret);
190 if (session->internals.handshake_recv_buffer_size > 0)
191 return 0;
192 else
193 return gnutls_assert_val(GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET);
196 void _dtls_reset_hsk_state(gnutls_session_t session)
198 session->internals.dtls.flight_init = 0;
199 drop_usage_count(session, &session->internals.handshake_send_buffer);
200 _mbuffer_head_clear(&session->internals.handshake_send_buffer);
204 #define UPDATE_TIMER { \
205 session->internals.dtls.actual_retrans_timeout_ms *= 2; \
206 session->internals.dtls.actual_retrans_timeout_ms %= MAX_DTLS_TIMEOUT; \
209 #define RESET_TIMER \
210 session->internals.dtls.actual_retrans_timeout_ms = session->internals.dtls.retrans_timeout_ms
212 #define TIMER_WINDOW session->internals.dtls.actual_retrans_timeout_ms
214 /* This function transmits the flight that has been previously
215 * buffered.
217 * This function is called from the handshake layer and calls the
218 * record layer.
221 _dtls_transmit (gnutls_session_t session)
223 int ret;
224 uint8_t* buf = NULL;
225 unsigned int timeout;
227 /* PREPARING -> SENDING state transition */
228 mbuffer_head_st *const send_buffer =
229 &session->internals.handshake_send_buffer;
230 mbuffer_st *cur;
231 gnutls_handshake_description_t last_type = 0;
232 unsigned int diff;
233 struct timespec now;
235 gettime(&now);
237 /* If we have already sent a flight and we are operating in a
238 * non blocking way, check if it is time to retransmit or just
239 * return.
241 if (session->internals.dtls.flight_init != 0 && session->internals.dtls.blocking == 0)
243 /* just in case previous run was interrupted */
244 ret = _gnutls_io_write_flush (session);
245 if (ret < 0)
247 gnutls_assert();
248 goto cleanup;
251 if (session->internals.dtls.last_flight == 0 || !_dtls_is_async(session))
253 /* check for ACK */
254 ret = _gnutls_io_check_recv(session, 0);
255 if (ret == GNUTLS_E_TIMEDOUT)
257 /* if no retransmission is required yet just return
259 if (_dtls_timespec_sub_ms(&now, &session->internals.dtls.last_retransmit) < TIMER_WINDOW)
261 gnutls_assert();
262 goto nb_timeout;
265 else /* received something */
267 if (ret == 0)
269 ret = is_next_hpacket_expected(session);
270 if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
271 goto nb_timeout;
272 if (ret < 0 && ret != GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET)
274 gnutls_assert();
275 goto cleanup;
277 if (ret == 0) goto end_flight;
278 /* if ret == GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET retransmit */
280 else
281 goto nb_timeout;
288 timeout = TIMER_WINDOW;
290 diff = _dtls_timespec_sub_ms(&now, &session->internals.dtls.handshake_start_time);
291 if (diff >= session->internals.dtls.total_timeout_ms)
293 _gnutls_dtls_log("Session timeout: %u ms\n", diff);
294 ret = gnutls_assert_val(GNUTLS_E_TIMEDOUT);
295 goto end_flight;
298 diff = _dtls_timespec_sub_ms(&now, &session->internals.dtls.last_retransmit);
299 if (session->internals.dtls.flight_init == 0 || diff >= TIMER_WINDOW)
301 _gnutls_dtls_log ("DTLS[%p]: %sStart of flight transmission.\n", session, (session->internals.dtls.flight_init == 0)?"":"re-");
302 for (cur = send_buffer->head;
303 cur != NULL; cur = cur->next)
305 ret = transmit_message (session, cur, &buf);
306 if (ret < 0)
308 gnutls_assert();
309 goto end_flight;
312 last_type = cur->htype;
314 gettime(&session->internals.dtls.last_retransmit);
316 if (session->internals.dtls.flight_init == 0)
318 session->internals.dtls.flight_init = 1;
319 RESET_TIMER;
320 timeout = TIMER_WINDOW;
322 if (last_type == GNUTLS_HANDSHAKE_FINISHED)
324 /* On the last flight we cannot ensure retransmission
325 * from here. _dtls_wait_and_retransmit() is being called
326 * by handshake.
328 session->internals.dtls.last_flight = 1;
330 else
331 session->internals.dtls.last_flight = 0;
333 else
335 UPDATE_TIMER;
339 ret = _gnutls_io_write_flush (session);
340 if (ret < 0)
342 ret = gnutls_assert_val(ret);
343 goto cleanup;
346 /* last message in handshake -> no ack */
347 if (session->internals.dtls.last_flight != 0)
349 /* we don't wait here. We just return 0 and
350 * if a retransmission occurs because peer didn't receive it
351 * we rely on the record or handshake
352 * layer calling this function again.
354 ret = 0;
355 goto cleanup;
357 else /* all other messages -> implicit ack (receive of next flight) */
359 if (session->internals.dtls.blocking != 0)
360 ret = _gnutls_io_check_recv(session, timeout);
361 else
363 ret = _gnutls_io_check_recv(session, 0);
364 if (ret == GNUTLS_E_TIMEDOUT)
366 goto nb_timeout;
370 if (ret == 0)
372 ret = is_next_hpacket_expected(session);
373 if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
374 goto nb_timeout;
376 if (ret == GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET)
378 ret = GNUTLS_E_TIMEDOUT;
379 goto keep_up;
381 if (ret < 0)
383 gnutls_assert();
384 goto cleanup;
386 goto end_flight;
390 keep_up:
391 gettime(&now);
392 } while(ret == GNUTLS_E_TIMEDOUT);
394 if (ret < 0)
396 ret = gnutls_assert_val(ret);
397 goto end_flight;
400 ret = 0;
402 end_flight:
403 _gnutls_dtls_log ("DTLS[%p]: End of flight transmission.\n", session);
404 _dtls_reset_hsk_state(session);
406 cleanup:
407 if (buf != NULL)
408 gnutls_free(buf);
410 /* SENDING -> WAITING state transition */
411 return ret;
413 nb_timeout:
414 if (buf != NULL)
415 gnutls_free(buf);
417 RETURN_DTLS_EAGAIN_OR_TIMEOUT(session, ret);
420 /* Waits for the last flight or retransmits
421 * the previous on timeout. Returns 0 on success.
423 int _dtls_wait_and_retransmit(gnutls_session_t session)
425 int ret;
427 if (session->internals.dtls.blocking != 0)
428 ret = _gnutls_io_check_recv(session, TIMER_WINDOW);
429 else
430 ret = _gnutls_io_check_recv(session, 0);
432 if (ret == GNUTLS_E_TIMEDOUT)
434 ret = _dtls_retransmit(session);
435 if (ret == 0)
437 RETURN_DTLS_EAGAIN_OR_TIMEOUT(session, 0);
439 else
440 return gnutls_assert_val(ret);
443 RESET_TIMER;
444 return 0;
448 #define window_table rp->record_sw
449 #define window_size rp->record_sw_size
451 /* FIXME: could we modify that code to avoid using
452 * uint64_t?
455 static void rot_window(struct record_parameters_st * rp, int places)
457 window_size -= places;
458 memmove(window_table, &window_table[places], window_size*sizeof(window_table[0]));
461 #define MOVE_SIZE 20
462 /* Checks if a sequence number is not replayed. If replayed
463 * returns a negative error code, otherwise zero.
465 int _dtls_record_check(struct record_parameters_st *rp, uint64 * _seq)
467 uint64_t seq = 0, diff;
468 unsigned int i, offset = 0;
470 for (i=2;i<8;i++)
472 seq <<= 8;
473 seq |= _seq->i[i] & 0xff;
476 if (window_size == 0)
478 window_size = 1;
479 window_table[0] = seq;
480 return 0;
483 if (seq <= window_table[0])
485 return -1;
488 if (window_size == DTLS_RECORD_WINDOW_SIZE)
490 rot_window(rp, MOVE_SIZE);
493 if (seq < window_table[window_size-1])
495 /* is between first and last */
496 diff = window_table[window_size-1] - seq;
498 if (diff >= window_size)
499 return -1;
501 offset = window_size-1-diff;
503 if (window_table[offset] == seq)
504 return -1;
505 else
506 window_table[offset] = seq;
508 else /* seq >= last */
510 if (seq == window_table[window_size-1])
512 return -1;
515 diff = seq - window_table[window_size-1];
516 if (diff <= DTLS_RECORD_WINDOW_SIZE - window_size)
517 { /* fits in our empty space */
518 offset = diff + window_size-1;
520 window_table[offset] = seq;
521 window_size = offset + 1;
523 else
525 if (diff > DTLS_RECORD_WINDOW_SIZE/2)
526 { /* difference is too big */
527 window_table[DTLS_RECORD_WINDOW_SIZE-1] = seq;
528 window_size = DTLS_RECORD_WINDOW_SIZE;
530 else
532 rot_window(rp, diff);
533 offset = diff + window_size-1;
534 window_table[offset] = seq;
535 window_size = offset + 1;
539 return 0;
544 * gnutls_dtls_set_timeouts:
545 * @session: is a #gnutls_session_t structure.
546 * @retrans_timeout: The time at which a retransmission will occur in milliseconds
547 * @total_timeout: The time at which the connection will be aborted, in milliseconds.
549 * This function will set the timeouts required for the DTLS handshake
550 * protocol. The retransmission timeout is the time after which a
551 * message from the peer is not received, the previous messages will
552 * be retransmitted. The total timeout is the time after which the
553 * handshake will be aborted with %GNUTLS_E_TIMEDOUT.
555 * The DTLS protocol recommends the values of 1 sec and 60 seconds
556 * respectively.
558 * If the retransmission timeout is zero then the handshake will operate
559 * in a non-blocking way, i.e., return %GNUTLS_E_AGAIN.
561 * Since: 3.0
563 void gnutls_dtls_set_timeouts (gnutls_session_t session, unsigned int retrans_timeout,
564 unsigned int total_timeout)
566 session->internals.dtls.retrans_timeout_ms = retrans_timeout;
567 session->internals.dtls.total_timeout_ms = total_timeout;
571 * gnutls_dtls_set_mtu:
572 * @session: is a #gnutls_session_t structure.
573 * @mtu: The maximum transfer unit of the transport
575 * This function will set the maximum transfer unit of the transport
576 * that DTLS packets are sent over. Note that this should exclude
577 * the IP (or IPv6) and UDP headers. So for DTLS over IPv6 on an
578 * Ethenet device with MTU 1500, the DTLS MTU set with this function
579 * would be 1500 - 40 (IPV6 header) - 8 (UDP header) = 1452.
581 * Since: 3.0
583 void gnutls_dtls_set_mtu (gnutls_session_t session, unsigned int mtu)
585 session->internals.dtls.mtu = mtu;
588 /* returns overhead imposed by the record layer (encryption/compression)
589 * etc. It does not include the record layer headers, since the caller
590 * needs to cope with rounding to multiples of blocksize, and the header
591 * is outside that.
593 * blocksize: will contain the block size when padding may be required or 1
595 * It may return a negative error code on error.
597 static int record_overhead_rt(gnutls_session_t session, unsigned int *blocksize)
599 record_parameters_st *params;
600 int total = 0, ret, iv_size;
602 if (session->internals.initial_negotiation_completed == 0)
603 return GNUTLS_E_INVALID_REQUEST;
605 ret = _gnutls_epoch_get (session, EPOCH_WRITE_CURRENT, &params);
606 if (ret < 0)
607 return gnutls_assert_val(ret);
609 /* requires padding */
610 iv_size = _gnutls_cipher_get_iv_size(params->cipher_algorithm);
612 if (_gnutls_cipher_is_block (params->cipher_algorithm) == CIPHER_BLOCK)
614 *blocksize = iv_size;
616 total += iv_size; /* iv_size == block_size in DTLS */
618 /* We always pad with at least one byte; never 0. */
619 total++;
621 else
623 *blocksize = 1;
626 if (params->mac_algorithm == GNUTLS_MAC_AEAD)
627 total += _gnutls_cipher_get_tag_size(params->cipher_algorithm);
628 else
630 ret = _gnutls_hmac_get_algo_len(params->mac_algorithm);
631 if (ret < 0)
632 return gnutls_assert_val(ret);
633 total+=ret;
636 if (params->compression_algorithm != GNUTLS_COMP_NULL)
637 total += EXTRA_COMP_SIZE;
639 return total;
643 * gnutls_dtls_get_data_mtu:
644 * @session: is a #gnutls_session_t structure.
646 * This function will return the actual maximum transfer unit for
647 * application data. I.e. DTLS headers are subtracted from the
648 * actual MTU.
650 * Returns: the maximum allowed transfer unit.
652 * Since: 3.0
654 unsigned int gnutls_dtls_get_data_mtu (gnutls_session_t session)
656 int mtu = session->internals.dtls.mtu;
657 unsigned int blocksize = 1;
658 int overhead;
660 mtu -= RECORD_HEADER_SIZE(session);
662 overhead = record_overhead_rt(session, &blocksize);
663 if (overhead < 0)
664 return mtu;
666 if (blocksize)
667 mtu -= mtu % blocksize;
669 return mtu - overhead;
673 * gnutls_dtls_set_data_mtu:
674 * @session: is a #gnutls_session_t structure.
675 * @mtu: The maximum unencrypted transfer unit of the session
677 * This function will set the maximum size of the *unencrypted* records
678 * which will be sent over a DTLS session. It is equivalent to calculating
679 * the DTLS packet overhead with the current encryption parameters, and
680 * calling gnutls_dtls_set_mtu() with that value. In particular, this means
681 * that you may need to call this function again after any negotiation or
682 * renegotiation, in order to ensure that the MTU is still sufficient to
683 * account for the new protocol overhead.
685 * Returns: %GNUTLS_E_SUCCESS (0) on success, or a negative error code.
687 * Since: 3.1
689 int gnutls_dtls_set_data_mtu (gnutls_session_t session, unsigned int mtu)
691 unsigned int blocksize;
692 int overhead = _gnutls_record_overhead_rt(session, &blocksize);
694 /* You can't call this until the session is actually running */
695 if (overhead < 0)
696 return GNUTLS_E_INVALID_SESSION;
698 /* Add the overhead inside the encrypted part */
699 mtu += overhead;
701 /* Round it up to the next multiple of blocksize */
702 mtu += blocksize - 1;
703 mtu -= mtu % blocksize;
705 /* Add the *unencrypted header size */
706 mtu += RECORD_HEADER_SIZE(session);
708 gnutls_dtls_set_mtu(session, mtu);
709 return GNUTLS_E_SUCCESS;
713 * gnutls_dtls_get_mtu:
714 * @session: is a #gnutls_session_t structure.
716 * This function will return the MTU size as set with
717 * gnutls_dtls_set_mtu(). This is not the actual MTU
718 * of data you can transmit. Use gnutls_dtls_get_data_mtu()
719 * for that reason.
721 * Returns: the set maximum transfer unit.
723 * Since: 3.0
725 unsigned int gnutls_dtls_get_mtu (gnutls_session_t session)
727 return session->internals.dtls.mtu;
731 * gnutls_dtls_get_timeout:
732 * @session: is a #gnutls_session_t structure.
734 * This function will return the milliseconds remaining
735 * for a retransmission of the previously sent handshake
736 * message. This function is useful when DTLS is used in
737 * non-blocking mode, to estimate when to call gnutls_handshake()
738 * if no packets have been received.
740 * Returns: the remaining time in milliseconds.
742 * Since: 3.0
744 unsigned int gnutls_dtls_get_timeout (gnutls_session_t session)
746 struct timespec now;
747 unsigned int diff;
749 gettime(&now);
751 diff = _dtls_timespec_sub_ms(&now, &session->internals.dtls.last_retransmit);
752 if (diff >= TIMER_WINDOW)
753 return 0;
754 else
755 return TIMER_WINDOW - diff;
758 #define COOKIE_SIZE 16
759 #define COOKIE_MAC_SIZE 16
761 /* MAC
762 * 16 bytes
764 * total 19 bytes
767 #define C_HASH GNUTLS_MAC_SHA1
768 #define C_HASH_SIZE 20
771 * gnutls_dtls_cookie_send:
772 * @key: is a random key to be used at cookie generation
773 * @client_data: contains data identifying the client (i.e. address)
774 * @client_data_size: The size of client's data
775 * @prestate: The previous cookie returned by gnutls_dtls_cookie_verify()
776 * @ptr: A transport pointer to be used by @push_func
777 * @push_func: A function that will be used to reply
779 * This function can be used to prevent denial of service
780 * attacks to a DTLS server by requiring the client to
781 * reply using a cookie sent by this function. That way
782 * it can be ensured that a client we allocated resources
783 * for (i.e. #gnutls_session_t) is the one that the
784 * original incoming packet was originated from.
786 * Returns: the number of bytes sent, or a negative error code.
788 * Since: 3.0
790 int gnutls_dtls_cookie_send(gnutls_datum_t* key, void* client_data, size_t client_data_size,
791 gnutls_dtls_prestate_st* prestate,
792 gnutls_transport_ptr_t ptr, gnutls_push_func push_func)
794 uint8_t hvr[20+DTLS_HANDSHAKE_HEADER_SIZE+COOKIE_SIZE];
795 int hvr_size = 0, ret;
796 uint8_t digest[C_HASH_SIZE];
798 if (key == NULL || key->data == NULL || key->size == 0)
799 return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
801 /* send
802 * struct {
803 * ContentType type - 1 byte GNUTLS_HANDSHAKE;
804 * ProtocolVersion version; - 2 bytes (254,255)
805 * uint16 epoch; - 2 bytes (0, 0)
806 * uint48 sequence_number; - 4 bytes (0,0,0,0)
807 * uint16 length; - 2 bytes (COOKIE_SIZE+1+2)+DTLS_HANDSHAKE_HEADER_SIZE
808 * uint8_t fragment[DTLSPlaintext.length];
809 * } DTLSPlaintext;
812 * struct {
813 * HandshakeType msg_type; 1 byte - GNUTLS_HANDSHAKE_HELLO_VERIFY_REQUEST
814 * uint24 length; - COOKIE_SIZE+3
815 * uint16 message_seq; - 2 bytes (0,0)
816 * uint24 fragment_offset; - 3 bytes (0,0,0)
817 * uint24 fragment_length; - same as length
820 * struct {
821 * ProtocolVersion server_version;
822 * uint8_t cookie<0..32>;
823 * } HelloVerifyRequest;
826 hvr[hvr_size++] = GNUTLS_HANDSHAKE;
827 /* version */
828 hvr[hvr_size++] = 254;
829 hvr[hvr_size++] = 255;
831 /* epoch + seq */
832 memset(&hvr[hvr_size], 0, 8);
833 hvr_size += 7;
834 hvr[hvr_size++] = prestate->record_seq;
836 /* length */
837 _gnutls_write_uint16(DTLS_HANDSHAKE_HEADER_SIZE+COOKIE_SIZE+3, &hvr[hvr_size]);
838 hvr_size += 2;
840 /* now handshake headers */
841 hvr[hvr_size++] = GNUTLS_HANDSHAKE_HELLO_VERIFY_REQUEST;
842 _gnutls_write_uint24(COOKIE_SIZE+3, &hvr[hvr_size]);
843 hvr_size += 3;
845 /* handshake seq */
846 hvr[hvr_size++] = 0;
847 hvr[hvr_size++] = prestate->hsk_write_seq;
849 _gnutls_write_uint24(0, &hvr[hvr_size]);
850 hvr_size += 3;
852 _gnutls_write_uint24(COOKIE_SIZE+3, &hvr[hvr_size]);
853 hvr_size += 3;
855 /* version */
856 hvr[hvr_size++] = 254;
857 hvr[hvr_size++] = 255;
858 hvr[hvr_size++] = COOKIE_SIZE;
860 ret = _gnutls_hmac_fast(C_HASH, key->data, key->size, client_data, client_data_size, digest);
861 if (ret < 0)
862 return gnutls_assert_val(ret);
864 memcpy(&hvr[hvr_size], digest, COOKIE_MAC_SIZE);
865 hvr_size+= COOKIE_MAC_SIZE;
867 ret = push_func(ptr, hvr, hvr_size);
868 if (ret < 0)
869 ret = GNUTLS_E_PUSH_ERROR;
871 return ret;
875 * gnutls_dtls_cookie_verify:
876 * @key: is a random key to be used at cookie generation
877 * @client_data: contains data identifying the client (i.e. address)
878 * @client_data_size: The size of client's data
879 * @_msg: An incoming message that initiates a connection.
880 * @msg_size: The size of the message.
881 * @prestate: The cookie of this client.
883 * This function will verify an incoming message for
884 * a valid cookie. If a valid cookie is returned then
885 * it should be associated with the session using
886 * gnutls_dtls_prestate_set();
888 * Returns: %GNUTLS_E_SUCCESS (0) on success, or a negative error code.
890 * Since: 3.0
892 int gnutls_dtls_cookie_verify(gnutls_datum_t* key,
893 void* client_data, size_t client_data_size,
894 void* _msg, size_t msg_size, gnutls_dtls_prestate_st* prestate)
896 gnutls_datum_t cookie;
897 int ret;
898 unsigned int pos, sid_size;
899 uint8_t * msg = _msg;
900 uint8_t digest[C_HASH_SIZE];
902 if (key == NULL || key->data == NULL || key->size == 0)
903 return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
905 /* format:
906 * version - 2 bytes
907 * random - 32 bytes
908 * session_id - 1 byte length + content
909 * cookie - 1 byte length + content
912 pos = 34+DTLS_RECORD_HEADER_SIZE+DTLS_HANDSHAKE_HEADER_SIZE;
914 if (msg_size < pos+1)
915 return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
917 sid_size = msg[pos++];
919 if (sid_size > 32 || msg_size < pos+sid_size+1)
920 return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
922 pos += sid_size;
923 cookie.size = msg[pos++];
925 if (msg_size < pos+cookie.size+1)
926 return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
928 cookie.data = &msg[pos];
929 if (cookie.size != COOKIE_SIZE)
931 if (cookie.size > 0) _gnutls_audit_log(NULL, "Received cookie with illegal size %d. Expected %d\n", (int)cookie.size, COOKIE_SIZE);
932 return gnutls_assert_val(GNUTLS_E_BAD_COOKIE);
935 ret = _gnutls_hmac_fast(C_HASH, key->data, key->size, client_data, client_data_size, digest);
936 if (ret < 0)
937 return gnutls_assert_val(ret);
939 if (memcmp(digest, cookie.data, COOKIE_MAC_SIZE) != 0)
940 return gnutls_assert_val(GNUTLS_E_BAD_COOKIE);
942 prestate->record_seq = msg[10]; /* client's record seq */
943 prestate->hsk_read_seq = msg[DTLS_RECORD_HEADER_SIZE+5]; /* client's hsk seq */
944 prestate->hsk_write_seq = 0;/* we always send zero for this msg */
946 return 0;
950 * gnutls_dtls_prestate_set:
951 * @session: a new session
952 * @prestate: contains the client's prestate
954 * This function will associate the prestate acquired by
955 * the cookie authentication with the client, with the newly
956 * established session.
958 * Since: 3.0
960 void gnutls_dtls_prestate_set(gnutls_session_t session, gnutls_dtls_prestate_st* prestate)
962 record_parameters_st *params;
963 int ret;
965 if (prestate == NULL)
966 return;
968 /* we do not care about read_params, since we accept anything
969 * the peer sends.
971 ret = _gnutls_epoch_get (session, EPOCH_WRITE_CURRENT, &params);
972 if (ret < 0)
973 return;
975 params->write.sequence_number.i[7] = prestate->record_seq;
977 session->internals.dtls.hsk_read_seq = prestate->hsk_read_seq;
978 session->internals.dtls.hsk_write_seq = prestate->hsk_write_seq + 1;
982 * gnutls_record_get_discarded:
983 * @session: is a #gnutls_session_t structure.
985 * Returns the number of discarded packets in a
986 * DTLS connection.
988 * Returns: The number of discarded packets.
990 * Since: 3.0
992 unsigned int gnutls_record_get_discarded (gnutls_session_t session)
994 return session->internals.dtls.packets_dropped;