tinydtls
0.8.1
|
#include "tinydtls.h"
#include "dtls_config.h"
#include "dtls_time.h"
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "uthash.h"
#include "debug.h"
#include "numeric.h"
#include "netq.h"
#include "dtls.h"
#include "alert.h"
#include "session.h"
#include "prng.h"
#include "sha2/sha2.h"
Go to the source code of this file.
Macros | |
#define | dtls_set_version(H, V) dtls_int_to_uint16((H)->version, (V)) |
#define | dtls_set_content_type(H, V) ((H)->content_type = (V) & 0xff) |
#define | dtls_set_length(H, V) ((H)->length = (V)) |
#define | dtls_get_content_type(H) ((H)->content_type & 0xff) |
#define | dtls_get_version(H) dtls_uint16_to_int((H)->version) |
#define | dtls_get_epoch(H) dtls_uint16_to_int((H)->epoch) |
#define | dtls_get_sequence_number(H) dtls_uint48_to_ulong((H)->sequence_number) |
#define | dtls_get_fragment_length(H) dtls_uint24_to_int((H)->fragment_length) |
#define | HASH_FIND_PEER(head, sess, out) HASH_FIND(hh,head,sess,sizeof(session_t),out) |
#define | HASH_ADD_PEER(head, sess, add) HASH_ADD(hh,head,sess,sizeof(session_t),add) |
#define | HASH_DEL_PEER(head, delptr) HASH_DELETE(hh,head,delptr) |
#define | DTLS_RH_LENGTH sizeof(dtls_record_header_t) |
#define | DTLS_HS_LENGTH sizeof(dtls_handshake_header_t) |
#define | DTLS_CH_LENGTH sizeof(dtls_client_hello_t) /* no variable length fields! */ |
#define | DTLS_COOKIE_LENGTH_MAX 32 |
#define | DTLS_CH_LENGTH_MAX sizeof(dtls_client_hello_t) + DTLS_COOKIE_LENGTH_MAX + 12 + 26 |
#define | DTLS_HV_LENGTH sizeof(dtls_hello_verify_t) |
#define | DTLS_SH_LENGTH (2 + DTLS_RANDOM_LENGTH + 1 + 2 + 1) |
#define | DTLS_CE_LENGTH (3 + 3 + 27 + DTLS_EC_KEY_SIZE + DTLS_EC_KEY_SIZE) |
#define | DTLS_SKEXEC_LENGTH (1 + 2 + 1 + 1 + DTLS_EC_KEY_SIZE + DTLS_EC_KEY_SIZE + 1 + 1 + 2 + 70) |
#define | DTLS_SKEXECPSK_LENGTH_MIN 2 |
#define | DTLS_SKEXECPSK_LENGTH_MAX 2 + DTLS_PSK_MAX_CLIENT_IDENTITY_LEN |
#define | DTLS_CKXPSK_LENGTH_MIN 2 |
#define | DTLS_CKXEC_LENGTH (1 + 1 + DTLS_EC_KEY_SIZE + DTLS_EC_KEY_SIZE) |
#define | DTLS_CV_LENGTH (1 + 1 + 2 + 1 + 1 + 1 + 1 + DTLS_EC_KEY_SIZE + 1 + 1 + DTLS_EC_KEY_SIZE) |
#define | DTLS_FIN_LENGTH 12 |
#define | HS_HDR_LENGTH DTLS_RH_LENGTH + DTLS_HS_LENGTH |
#define | HV_HDR_LENGTH HS_HDR_LENGTH + DTLS_HV_LENGTH |
#define | HIGH(V) (((V) >> 8) & 0xff) |
#define | LOW(V) ((V) & 0xff) |
#define | DTLS_RECORD_HEADER(M) ((dtls_record_header_t *)(M)) |
#define | DTLS_HANDSHAKE_HEADER(M) ((dtls_handshake_header_t *)(M)) |
#define | HANDSHAKE(M) ((dtls_handshake_header_t *)((M) + DTLS_RH_LENGTH)) |
#define | CLIENTHELLO(M) ((dtls_client_hello_t *)((M) + HS_HDR_LENGTH)) |
#define | SKIP_VAR_FIELD(P, L, T) |
#define | PRF_LABEL(Label) prf_label_##Label |
#define | PRF_LABEL_SIZE(Label) (sizeof(PRF_LABEL(Label)) - 1) |
#define | CALL(Context, which,...) |
#define | A_DATA_LEN 13 |
#define | MUST_HASH(Type, Data, Length) |
#define | mycookie (buf + DTLS_HV_LENGTH) |
#define | R_KEY_OFFSET (1 + 1 + 2 + 1 + 1 + 1 + 1) |
#define | S_KEY_OFFSET(len_s) (R_KEY_OFFSET + (len_s) + 1 + 1) |
#define | A_DATA_LEN 13 |
Functions | |
static dtls_context_t * | malloc_context () |
static void | free_context (dtls_context_t *context) |
void | dtls_init () |
static int | dtls_send_multi (dtls_context_t *ctx, dtls_peer_t *peer, dtls_security_parameters_t *security, session_t *session, unsigned char type, uint8 *buf_array[], size_t buf_len_array[], size_t buf_array_len) |
static int | dtls_send (dtls_context_t *ctx, dtls_peer_t *peer, unsigned char type, uint8 *buf, size_t buflen) |
static void | dtls_stop_retransmission (dtls_context_t *context, dtls_peer_t *peer) |
dtls_peer_t * | dtls_get_peer (const dtls_context_t *ctx, const session_t *session) |
static void | dtls_add_peer (dtls_context_t *ctx, dtls_peer_t *peer) |
int | dtls_write (struct dtls_context_t *ctx, session_t *dst, uint8 *buf, size_t len) |
static int | dtls_get_cookie (uint8 *msg, size_t msglen, uint8 **cookie) |
static int | dtls_create_cookie (dtls_context_t *ctx, session_t *session, uint8 *msg, size_t msglen, uint8 *cookie, int *clen) |
static unsigned int | is_record (uint8 *msg, size_t msglen) |
static uint8 * | dtls_set_record_header (uint8 type, dtls_security_parameters_t *security, uint8 *buf) |
static uint8 * | dtls_set_handshake_header (uint8 type, dtls_peer_t *peer, int length, int frag_offset, int frag_length, uint8 *buf) |
static int | is_tls_ecdhe_ecdsa_with_aes_128_ccm_8 (dtls_cipher_t cipher) |
static int | is_tls_psk_with_aes_128_ccm_8 (dtls_cipher_t cipher) |
static int | is_psk_supported (dtls_context_t *ctx) |
static int | is_ecdsa_supported (dtls_context_t *ctx, int is_client) |
static int | is_ecdsa_client_auth_supported (dtls_context_t *ctx) |
static int | known_cipher (dtls_context_t *ctx, dtls_cipher_t code, int is_client) |
static void | dtls_debug_keyblock (dtls_security_parameters_t *config) |
static char * | dtls_handshake_type_to_name (int type) |
static int | calculate_key_block (dtls_context_t *ctx, dtls_handshake_parameters_t *handshake, dtls_peer_t *peer, session_t *session, dtls_peer_type role) |
static int | verify_ext_eliptic_curves (uint8 *data, size_t data_length) |
static int | verify_ext_cert_type (uint8 *data, size_t data_length) |
static int | verify_ext_ec_point_formats (uint8 *data, size_t data_length) |
static int | dtls_check_tls_extension (dtls_peer_t *peer, uint8 *data, size_t data_length, int client_hello) |
static int | dtls_update_parameters (dtls_context_t *ctx, dtls_peer_t *peer, uint8 *data, size_t data_length) |
static int | check_client_keyexchange (dtls_context_t *ctx, dtls_handshake_parameters_t *handshake, uint8 *data, size_t length) |
static void | update_hs_hash (dtls_peer_t *peer, uint8 *data, size_t length) |
static void | copy_hs_hash (dtls_peer_t *peer, dtls_hash_ctx *hs_hash) |
static size_t | finalize_hs_hash (dtls_peer_t *peer, uint8 *buf) |
static void | clear_hs_hash (dtls_peer_t *peer) |
static int | check_finished (dtls_context_t *ctx, dtls_peer_t *peer, uint8 *data, size_t data_length) |
static int | dtls_prepare_record (dtls_peer_t *peer, dtls_security_parameters_t *security, unsigned char type, uint8 *data_array[], size_t data_len_array[], size_t data_array_len, uint8 *sendbuf, size_t *rlen) |
static int | dtls_send_handshake_msg_hash (dtls_context_t *ctx, dtls_peer_t *peer, session_t *session, uint8 header_type, uint8 *data, size_t data_length, int add_hash) |
static int | dtls_send_handshake_msg (dtls_context_t *ctx, dtls_peer_t *peer, uint8 header_type, uint8 *data, size_t data_length) |
static int | dtls_send_alert (dtls_context_t *ctx, dtls_peer_t *peer, dtls_alert_level_t level, dtls_alert_t description) |
int | dtls_close (dtls_context_t *ctx, const session_t *remote) |
static void | dtls_destroy_peer (dtls_context_t *ctx, dtls_peer_t *peer, int unlink) |
static int | dtls_verify_peer (dtls_context_t *ctx, dtls_peer_t *peer, session_t *session, uint8 *data, size_t data_length) |
static int | dtls_check_ecdsa_signature_elem (uint8 *data, size_t data_length, unsigned char **result_r, unsigned char **result_s) |
static int | check_client_certificate_verify (dtls_context_t *ctx, dtls_peer_t *peer, uint8 *data, size_t data_length) |
static int | dtls_send_server_hello (dtls_context_t *ctx, dtls_peer_t *peer) |
static int | dtls_send_certificate_ecdsa (dtls_context_t *ctx, dtls_peer_t *peer, const dtls_ecdsa_key_t *key) |
static uint8 * | dtls_add_ecdsa_signature_elem (uint8 *p, uint32_t *point_r, uint32_t *point_s) |
static int | dtls_send_server_key_exchange_ecdh (dtls_context_t *ctx, dtls_peer_t *peer, const dtls_ecdsa_key_t *key) |
static int | dtls_send_server_key_exchange_psk (dtls_context_t *ctx, dtls_peer_t *peer, const unsigned char *psk_hint, size_t len) |
static int | dtls_send_server_certificate_request (dtls_context_t *ctx, dtls_peer_t *peer) |
static int | dtls_send_server_hello_done (dtls_context_t *ctx, dtls_peer_t *peer) |
static int | dtls_send_server_hello_msgs (dtls_context_t *ctx, dtls_peer_t *peer) |
static int | dtls_send_ccs (dtls_context_t *ctx, dtls_peer_t *peer) |
static int | dtls_send_client_key_exchange (dtls_context_t *ctx, dtls_peer_t *peer) |
static int | dtls_send_certificate_verify_ecdh (dtls_context_t *ctx, dtls_peer_t *peer, const dtls_ecdsa_key_t *key) |
static int | dtls_send_finished (dtls_context_t *ctx, dtls_peer_t *peer, const unsigned char *label, size_t labellen) |
static int | dtls_send_client_hello (dtls_context_t *ctx, dtls_peer_t *peer, uint8 cookie[], size_t cookie_length) |
static int | check_server_hello (dtls_context_t *ctx, dtls_peer_t *peer, uint8 *data, size_t data_length) |
static int | check_server_hello_verify_request (dtls_context_t *ctx, dtls_peer_t *peer, uint8 *data, size_t data_length) |
static int | check_server_certificate (dtls_context_t *ctx, dtls_peer_t *peer, uint8 *data, size_t data_length) |
static int | check_server_key_exchange_ecdsa (dtls_context_t *ctx, dtls_peer_t *peer, uint8 *data, size_t data_length) |
static int | check_server_key_exchange_psk (dtls_context_t *ctx, dtls_peer_t *peer, uint8 *data, size_t data_length) |
static int | check_certificate_request (dtls_context_t *ctx, dtls_peer_t *peer, uint8 *data, size_t data_length) |
static int | check_server_hellodone (dtls_context_t *ctx, dtls_peer_t *peer, uint8 *data, size_t data_length) |
static int | decrypt_verify (dtls_peer_t *peer, uint8 *packet, size_t length, uint8 **cleartext) |
static int | dtls_send_hello_request (dtls_context_t *ctx, dtls_peer_t *peer) |
int | dtls_renegotiate (dtls_context_t *ctx, const session_t *dst) |
static int | handle_handshake_msg (dtls_context_t *ctx, dtls_peer_t *peer, session_t *session, const dtls_peer_type role, const dtls_state_t state, uint8 *data, size_t data_length) |
static int | handle_handshake (dtls_context_t *ctx, dtls_peer_t *peer, session_t *session, const dtls_peer_type role, const dtls_state_t state, uint8 *data, size_t data_length) |
static int | handle_ccs (dtls_context_t *ctx, dtls_peer_t *peer, uint8 *record_header, uint8 *data, size_t data_length) |
static int | handle_alert (dtls_context_t *ctx, dtls_peer_t *peer, uint8 *record_header, uint8 *data, size_t data_length) |
static int | dtls_alert_send_from_err (dtls_context_t *ctx, dtls_peer_t *peer, session_t *session, int err) |
int | dtls_handle_message (dtls_context_t *ctx, session_t *session, uint8 *msg, int msglen) |
dtls_context_t * | dtls_new_context (void *app_data) |
void | dtls_free_context (dtls_context_t *ctx) |
int | dtls_connect_peer (dtls_context_t *ctx, dtls_peer_t *peer) |
int | dtls_connect (dtls_context_t *ctx, const session_t *dst) |
static void | dtls_retransmit (dtls_context_t *context, netq_t *node) |
void | dtls_check_retransmit (dtls_context_t *context, clock_time_t *next) |
Variables | |
static const unsigned char | prf_label_master [] = "master secret" |
static const unsigned char | prf_label_key [] = "key expansion" |
static const unsigned char | prf_label_client [] = "client" |
static const unsigned char | prf_label_server [] = "server" |
static const unsigned char | prf_label_finished [] = " finished" |
static const unsigned char | cert_asn1_header [] |
static uint8 | compression_methods [] |
#define A_DATA_LEN 13 |
#define A_DATA_LEN 13 |
#define CALL | ( | Context, | |
which, | |||
... | |||
) |
#define CLIENTHELLO | ( | M | ) | ((dtls_client_hello_t *)((M) + HS_HDR_LENGTH)) |
#define DTLS_CE_LENGTH (3 + 3 + 27 + DTLS_EC_KEY_SIZE + DTLS_EC_KEY_SIZE) |
#define DTLS_CH_LENGTH sizeof(dtls_client_hello_t) /* no variable length fields! */ |
#define DTLS_CH_LENGTH_MAX sizeof(dtls_client_hello_t) + DTLS_COOKIE_LENGTH_MAX + 12 + 26 |
#define DTLS_CKXEC_LENGTH (1 + 1 + DTLS_EC_KEY_SIZE + DTLS_EC_KEY_SIZE) |
#define DTLS_CV_LENGTH (1 + 1 + 2 + 1 + 1 + 1 + 1 + DTLS_EC_KEY_SIZE + 1 + 1 + DTLS_EC_KEY_SIZE) |
#define dtls_get_epoch | ( | H | ) | dtls_uint16_to_int((H)->epoch) |
#define dtls_get_fragment_length | ( | H | ) | dtls_uint24_to_int((H)->fragment_length) |
#define dtls_get_sequence_number | ( | H | ) | dtls_uint48_to_ulong((H)->sequence_number) |
#define dtls_get_version | ( | H | ) | dtls_uint16_to_int((H)->version) |
#define DTLS_HANDSHAKE_HEADER | ( | M | ) | ((dtls_handshake_header_t *)(M)) |
#define DTLS_HS_LENGTH sizeof(dtls_handshake_header_t) |
#define DTLS_HV_LENGTH sizeof(dtls_hello_verify_t) |
#define DTLS_RECORD_HEADER | ( | M | ) | ((dtls_record_header_t *)(M)) |
#define DTLS_RH_LENGTH sizeof(dtls_record_header_t) |
#define dtls_set_content_type | ( | H, | |
V | |||
) | ((H)->content_type = (V) & 0xff) |
#define dtls_set_version | ( | H, | |
V | |||
) | dtls_int_to_uint16((H)->version, (V)) |
#define DTLS_SH_LENGTH (2 + DTLS_RANDOM_LENGTH + 1 + 2 + 1) |
#define DTLS_SKEXEC_LENGTH (1 + 2 + 1 + 1 + DTLS_EC_KEY_SIZE + DTLS_EC_KEY_SIZE + 1 + 1 + 2 + 70) |
#define DTLS_SKEXECPSK_LENGTH_MAX 2 + DTLS_PSK_MAX_CLIENT_IDENTITY_LEN |
#define HANDSHAKE | ( | M | ) | ((dtls_handshake_header_t *)((M) + DTLS_RH_LENGTH)) |
#define HASH_DEL_PEER | ( | head, | |
delptr | |||
) | HASH_DELETE(hh,head,delptr) |
#define HS_HDR_LENGTH DTLS_RH_LENGTH + DTLS_HS_LENGTH |
#define HV_HDR_LENGTH HS_HDR_LENGTH + DTLS_HV_LENGTH |
#define MUST_HASH | ( | Type, | |
Data, | |||
Length | |||
) |
Returns true if the message Data
is a handshake message that must be included in the calculation of verify_data in the Finished message.
Type | The message type. Only handshake messages but the initial Client Hello and Hello Verify Request are included in the hash, |
Data | The PDU to examine. |
Length | The length of Data . |
1
if Data
must be included in hash, 0
otherwise. #define mycookie (buf + DTLS_HV_LENGTH) |
#define PRF_LABEL_SIZE | ( | Label | ) | (sizeof(PRF_LABEL(Label)) - 1) |
#define R_KEY_OFFSET (1 + 1 + 2 + 1 + 1 + 1 + 1) |
#define S_KEY_OFFSET | ( | len_s | ) | (R_KEY_OFFSET + (len_s) + 1 + 1) |
#define SKIP_VAR_FIELD | ( | P, | |
L, | |||
T | |||
) |
|
static |
|
static |
|
static |
|
inlinestatic |
|
static |
Checks if record
+ data
contain a Finished message with valid verify_data.
ctx | The current DTLS context. |
peer | The remote peer of the security association. |
data | The cleartext payload of the message. |
data_length | Actual length of data . |
0
if the Finished message is valid, negative
number otherwise.
|
static |
|
static |
|
static |
|
static |
|
static |
|
static |
|
inlinestatic |
|
static |
|
static |
|
static |
|
static |
|
static |
void dtls_check_retransmit | ( | dtls_context_t * | context, |
clock_time_t * | next | ||
) |
Checks sendqueue of given DTLS context object for any outstanding packets to be transmitted.
context | The DTLS context object to use. |
next | If not NULL, next is filled with the timestamp of the next scheduled retransmission, or 0 when no packets are waiting. |
|
static |
int dtls_close | ( | dtls_context_t * | ctx, |
const session_t * | remote | ||
) |
int dtls_connect | ( | dtls_context_t * | ctx, |
const session_t * | dst | ||
) |
Establishes a DTLS channel with the specified remote peer dst
. This function returns 0
if that channel already exists, a value greater than zero when a new ClientHello message was sent, and a value less than zero on error.
ctx | The DTLS context to use. |
dst | The remote party to connect to. |
int dtls_connect_peer | ( | dtls_context_t * | ctx, |
dtls_peer_t * | peer | ||
) |
Establishes a DTLS channel with the specified remote peer. This function returns 0
if that channel already exists, a value greater than zero when a new ClientHello message was sent, and a value less than zero on error.
ctx | The DTLS context to use. |
peer | The peer object that describes the session. |
|
static |
|
static |
|
static |
void dtls_free_context | ( | dtls_context_t * | ctx | ) |
dtls_peer_t* dtls_get_peer | ( | const dtls_context_t * | context, |
const session_t * | session | ||
) |
Check if session
is associated with a peer object in context
. This function returns a pointer to the peer if found, NULL otherwise.
context | The DTLS context to search. |
session | The remote address and local interface |
session
or NULL if none exists. int dtls_handle_message | ( | dtls_context_t * | ctx, |
session_t * | session, | ||
uint8 * | msg, | ||
int | msglen | ||
) |
|
static |
returns the name of the goven handshake type number. see IANA for a full list of types: https://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-7
void dtls_init | ( | ) |
dtls_context_t* dtls_new_context | ( | void * | app_data | ) |
Creates a new context object. The storage allocated for the new object must be released with dtls_free_context().
|
static |
Prepares the payload given in data
for sending with dtls_send(). The data
is encrypted and compressed according to the current security parameters of peer
. The result of this operation is put into sendbuf
with a prepended record header of type type
ready for sending. As some cipher suites add a MAC before encryption, data
must be large enough to hold this data as well (usually dtls_kb_digest_size(CURRENT_CONFIG(peer))
.
peer | The remote peer the packet will be sent to. |
security | The encryption paramater used to encrypt |
type | The content type of this record. |
data_array | Array with payloads in correct order. |
data_len_array | sizes of the payloads in correct order. |
data_array_len | The number of payloads given. |
sendbuf | The output buffer where the encrypted record will be placed. |
rlen | This parameter must be initialized with the maximum size of sendbuf and will be updated to hold the actual size of the stored packet on success. On error, the value of rlen is undefined. |
length of additional_data for the AEAD cipher which consists of seq_num(2+6) + type(1) + version(2) + length(2)
int dtls_renegotiate | ( | dtls_context_t * | ctx, |
const session_t * | dst | ||
) |
|
static |
|
static |
Sends the fragment of length buflen
given in buf
to the specified peer
. The data will be MAC-protected and encrypted according to the selected cipher and split into one or more DTLS records of the specified type
. This function returns the number of bytes that were sent, or -1
if an error occurred.
ctx | The DTLS context to use. |
peer | The remote peer. |
type | The content type of the record. |
buf | The data to send. |
buflen | The actual length of buf . |
|
inlinestatic |
|
inlinestatic |
|
static |
|
static |
|
static |
|
static |
|
static |
|
static |
|
static |
|
static |
|
static |
Sends the data passed in buf
as a DTLS record of type type
to the given peer. The data will be encrypted and compressed according to the security parameters for peer
.
ctx | The DTLS context in effect. |
peer | The remote party where the packet is sent. |
type | The content type of this record. |
buf | The data to send. |
buflen | The number of bytes to send from buf . |
|
static |
|
static |
|
static |
|
static |
|
static |
|
static |
|
inlinestatic |
|
inlinestatic |
Initializes buf
as record header. The caller must ensure that buf
is capable of holding at least sizeof(dtls_record_header_t)
bytes. Increments sequence number counter of security
.
|
static |
|
static |
Parses the ClientHello from the client and updates the internal handshake parameters with the new data for the given peer
. When the ClientHello handshake message in data
does not contain a cipher suite or compression method, it is copied from the the current security parameters.
ctx | The current DTLS context. |
peer | The remote peer whose security parameters are about to change. |
data | The handshake message with a ClientHello. |
data_length | The actual size of data . |
-Something
if an error occurred, 0
on success.
|
static |
Checks a received Client Hello message for a valid cookie. When the Client Hello contains no cookie, the function fails and a Hello Verify Request is sent to the peer (using the write callback function registered with ctx
). The return value is -1
on error, 0
when undecided, and 1
if the Client Hello was good.
ctx | The DTLS context. |
peer | The remote party we are talking to, if any. |
session | Transport address of the remote peer. |
msg | The received datagram. |
msglen | Length of msg . |
1
if msg is a Client Hello with a valid cookie, 0
or -1
otherwise. int dtls_write | ( | struct dtls_context_t * | ctx, |
session_t * | session, | ||
uint8 * | buf, | ||
size_t | len | ||
) |
Writes the application data given in buf
to the peer specified by session
.
ctx | The DTLS context to use. |
session | The remote transport address and local interface. |
buf | The data to write. |
len | The actual length of data . |
-1
on error.
|
inlinestatic |
|
inlinestatic |
|
static |
|
static |
|
static |
|
static |
|
inlinestatic |
|
inlinestatic |
|
inlinestatic |
|
static |
|
inlinestatic |
|
inlinestatic |
|
static |
|
inlinestatic |
|
inlinestatic |
|
static |
|
static |
|
static |
|
static |
|
static |
only one compression method is currently defined
|
static |
|
static |