blob: d0a8c338aec071b9ddda0b0367d926e868d729a2 [file] [log] [blame]
/*******************************************************************************
*
* Copyright (c) 2011, 2012, 2013, 2014, 2015 Olaf Bergmann (TZI) and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Eclipse Distribution License v. 1.0 which accompanies this distribution.
*
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Olaf Bergmann - initial API and implementation
* Hauke Mehrtens - memory optimization, ECC integration
* Achim Kraus - session recovery
* Sachin Agrawal - rehandshake support
*
*******************************************************************************/
#include "tinydtls.h"
#include "dtls_time.h"
#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_ASSERT_H
#include <assert.h>
#endif
#ifndef WITH_CONTIKI
#include <stdlib.h>
#include "global.h"
#endif /* WITH_CONTIKI */
#ifdef HAVE_INTTYPES_H
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
#else
# ifndef PRIu64
# define PRIu64 "llu"
# endif
# ifndef PRIx64
# define PRIx64 "llx"
# endif
#endif /* HAVE_INTTYPES_H */
#include "utlist.h"
#ifndef DTLS_PEERS_NOHASH
#include "uthash.h"
#endif /* DTLS_PEERS_NOHASH */
#include "dtls_debug.h"
#include "numeric.h"
#include "netq.h"
#include "dtls.h"
#include "alert.h"
#include "session.h"
#include "prng.h"
#ifdef WITH_SHA256
# include "sha2/sha2.h"
#endif
#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)
#ifdef DTLS_PEERS_NOHASH
#define FIND_PEER(head,sess,out) \
do { \
dtls_peer_t * tmp; \
(out) = NULL; \
LL_FOREACH((head), tmp) { \
if (dtls_session_equals(&tmp->session, (sess))) { \
(out) = tmp; \
break; \
} \
} \
} while (0)
#define DEL_PEER(head,delptr) \
if ((head) != NULL && (delptr) != NULL) { \
LL_DELETE(head,delptr); \
}
#define ADD_PEER(head,sess,add) \
LL_PREPEND(ctx->peers, peer);
#else /* DTLS_PEERS_NOHASH */
#define FIND_PEER(head,sess,out) \
HASH_FIND(hh,head,sess,sizeof(session_t),out)
#define ADD_PEER(head,sess,add) \
HASH_ADD(hh,head,sess,sizeof(session_t),add)
#define DEL_PEER(head,delptr) \
if ((head) != NULL && (delptr) != NULL) { \
HASH_DELETE(hh,head,delptr); \
}
#endif /* DTLS_PEERS_NOHASH */
#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))
/* The length check here should work because dtls_*_to_int() works on
* unsigned char. Otherwise, broken messages could cause severe
* trouble. Note that this macro jumps out of the current program flow
* when the message is too short. Beware!
*/
#define SKIP_VAR_FIELD(P,L,T) { \
if (L < dtls_ ## T ## _to_int(P) + sizeof(T)) \
goto error; \
L -= dtls_ ## T ## _to_int(P) + sizeof(T); \
P += dtls_ ## T ## _to_int(P) + sizeof(T); \
}
/* some constants for the PRF */
#define PRF_LABEL(Label) prf_label_##Label
#define PRF_LABEL_SIZE(Label) (sizeof(PRF_LABEL(Label)) - 1)
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";
#ifdef DTLS_ECC
/* first part of Raw public key, the is the start of the Subject Public Key */
static const unsigned char cert_asn1_header[] = {
0x30, 0x59, /* SEQUENCE, length 89 bytes */
0x30, 0x13, /* SEQUENCE, length 19 bytes */
0x06, 0x07, /* OBJECT IDENTIFIER ecPublicKey (1 2 840 10045 2 1) */
0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01,
0x06, 0x08, /* OBJECT IDENTIFIER prime256v1 (1 2 840 10045 3 1 7) */
0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07,
0x03, 0x42, 0x00, /* BIT STRING, length 66 bytes, 0 bits unused */
0x04 /* uncompressed, followed by the r und s values of the public key */
};
#endif /* DTLS_ECC */
#ifdef WITH_CONTIKI
PROCESS(dtls_retransmit_process, "DTLS retransmit process");
static dtls_context_t the_dtls_context;
static inline dtls_context_t *
malloc_context(void) {
return &the_dtls_context;
}
static inline void
free_context(dtls_context_t *context) {
}
#else /* WITH_CONTIKI */
static inline dtls_context_t *
malloc_context(void) {
return (dtls_context_t *)malloc(sizeof(dtls_context_t));
}
static inline void
free_context(dtls_context_t *context) {
free(context);
}
#endif
void
dtls_init(void) {
dtls_clock_init();
crypto_init();
netq_init();
peer_init();
}
/* Calls cb_alert() with given arguments if defined, otherwise an
* error message is logged and the result is -1. This is just an
* internal helper.
*/
#define CALL(Context, which, ...) \
((Context)->h && (Context)->h->which \
? (Context)->h->which((Context), ##__VA_ARGS__) \
: -1)
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);
/**
* Sends the fragment of length \p buflen given in \p buf to the
* specified \p 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 \p type. This function returns the number
* of bytes that were sent, or \c -1 if an error occurred.
*
* \param ctx The DTLS context to use.
* \param peer The remote peer.
* \param type The content type of the record.
* \param buf The data to send.
* \param buflen The actual length of \p buf.
* \return Less than zero on error, the number of bytes written otherwise.
*/
static int
dtls_send(dtls_context_t *ctx, dtls_peer_t *peer, unsigned char type,
uint8 *buf, size_t buflen) {
return dtls_send_multi(ctx, peer, dtls_security_params(peer), &peer->session,
type, &buf, &buflen, 1);
}
/**
* Stops ongoing retransmissions of handshake messages for @p peer.
*/
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) {
dtls_peer_t *p;
FIND_PEER(ctx->peers, session, p);
return p;
}
/**
* Adds @p peer to list of peers in @p ctx. This function returns @c 0
* on success, or a negative value on error (e.g. due to insufficient
* storage).
*/
static int
dtls_add_peer(dtls_context_t *ctx, dtls_peer_t *peer) {
ADD_PEER(ctx->peers, session, peer);
return 0;
}
int
dtls_write(struct dtls_context_t *ctx,
session_t *dst, uint8 *buf, size_t len) {
dtls_peer_t *peer = dtls_get_peer(ctx, dst);
/* Check if peer connection already exists */
if (!peer) { /* no ==> create one */
int res;
/* dtls_connect() returns a value greater than zero if a new
* connection attempt is made, 0 for session reuse. */
res = dtls_connect(ctx, dst);
return (res >= 0) ? 0 : res;
} else { /* a session exists, check if it is in state connected */
if (peer->state != DTLS_STATE_CONNECTED) {
return 0;
} else {
return dtls_send(ctx, peer, DTLS_CT_APPLICATION_DATA, buf, len);
}
}
}
static int
dtls_get_cookie(uint8 *msg, size_t msglen, uint8 **cookie) {
/* To access the cookie, we have to determine the session id's
* length and skip the whole thing. */
if (msglen < DTLS_HS_LENGTH + DTLS_CH_LENGTH + sizeof(uint8))
return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
if (dtls_uint16_to_int(msg + DTLS_HS_LENGTH) != DTLS_VERSION)
return dtls_alert_fatal_create(DTLS_ALERT_PROTOCOL_VERSION);
msglen -= DTLS_HS_LENGTH + DTLS_CH_LENGTH;
msg += DTLS_HS_LENGTH + DTLS_CH_LENGTH;
SKIP_VAR_FIELD(msg, msglen, uint8); /* skip session id */
if (msglen < (*msg & 0xff) + sizeof(uint8))
return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
*cookie = msg + sizeof(uint8);
return dtls_uint8_to_int(msg);
error:
return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
}
static int
dtls_create_cookie(dtls_context_t *ctx,
session_t *session,
uint8 *msg, size_t msglen,
uint8 *cookie, int *clen) {
unsigned char buf[DTLS_HMAC_MAX];
size_t e;
int len;
/* create cookie with HMAC-SHA256 over:
* - SECRET
* - session parameters (only IP address?)
* - client version
* - random gmt and bytes
* - session id
* - cipher_suites
* - compression method
*/
/* We use our own buffer as hmac_context instead of a dynamic buffer
* created by dtls_hmac_new() to separate storage space for cookie
* creation from storage that is used in real sessions. Note that
* the buffer size must fit with the default hash algorithm (see
* implementation of dtls_hmac_context_new()). */
dtls_hmac_context_t hmac_context;
dtls_hmac_init(&hmac_context, ctx->cookie_secret, DTLS_COOKIE_SECRET_LENGTH);
dtls_hmac_update(&hmac_context,
(unsigned char *)&session->addr, session->size);
/* feed in the beginning of the Client Hello up to and including the
session id */
e = sizeof(dtls_client_hello_t);
e += (*(msg + DTLS_HS_LENGTH + e) & 0xff) + sizeof(uint8);
if (e + DTLS_HS_LENGTH > msglen)
return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
dtls_hmac_update(&hmac_context, msg + DTLS_HS_LENGTH, e);
/* skip cookie bytes and length byte */
e += *(uint8 *)(msg + DTLS_HS_LENGTH + e) & 0xff;
e += sizeof(uint8);
if (e + DTLS_HS_LENGTH > msglen)
return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
dtls_hmac_update(&hmac_context,
msg + DTLS_HS_LENGTH + e,
dtls_get_fragment_length(DTLS_HANDSHAKE_HEADER(msg)) - e);
len = dtls_hmac_finalize(&hmac_context, buf);
if (len < *clen) {
memset(cookie + len, 0, *clen - len);
*clen = len;
}
memcpy(cookie, buf, *clen);
return 0;
}
#ifdef DTLS_CHECK_CONTENTTYPE
/* used to check if a received datagram contains a DTLS message */
static char const content_types[] = {
DTLS_CT_CHANGE_CIPHER_SPEC,
DTLS_CT_ALERT,
DTLS_CT_HANDSHAKE,
DTLS_CT_APPLICATION_DATA,
0 /* end marker */
};
#endif
/**
* Checks if \p msg points to a valid DTLS record. If
*
*/
static unsigned int
is_record(uint8 *msg, size_t msglen) {
unsigned int rlen = 0;
if (msglen >= DTLS_RH_LENGTH /* FIXME allow empty records? */
#ifdef DTLS_CHECK_CONTENTTYPE
&& strchr(content_types, msg[0])
#endif
&& msg[1] == HIGH(DTLS_VERSION)
&& msg[2] == LOW(DTLS_VERSION))
{
rlen = DTLS_RH_LENGTH +
dtls_uint16_to_int(DTLS_RECORD_HEADER(msg)->length);
/* we do not accept wrong length field in record header */
if (rlen > msglen)
rlen = 0;
}
return rlen;
}
/**
* Initializes \p buf as record header. The caller must ensure that \p
* buf is capable of holding at least \c sizeof(dtls_record_header_t)
* bytes. Increments sequence number counter of \p security.
* \return pointer to the next byte after the written header.
* The length will be set to 0 and has to be changed before sending.
*/
static inline uint8 *
dtls_set_record_header(uint8 type, dtls_security_parameters_t *security,
uint8 *buf) {
dtls_int_to_uint8(buf, type);
buf += sizeof(uint8);
dtls_int_to_uint16(buf, DTLS_VERSION);
buf += sizeof(uint16);
if (security) {
dtls_int_to_uint16(buf, security->epoch);
buf += sizeof(uint16);
dtls_int_to_uint48(buf, security->rseq);
buf += sizeof(uint48);
/* increment record sequence counter by 1 */
security->rseq++;
} else {
memset(buf, 0, sizeof(uint16) + sizeof(uint48));
buf += sizeof(uint16) + sizeof(uint48);
}
memset(buf, 0, sizeof(uint16));
return buf + sizeof(uint16);
}
/**
* Initializes \p buf as handshake header. The caller must ensure that \p
* buf is capable of holding at least \c sizeof(dtls_handshake_header_t)
* bytes. Increments message sequence number counter of \p peer.
* \return pointer to the next byte after \p buf
*/
static inline uint8 *
dtls_set_handshake_header(uint8 type, dtls_peer_t *peer,
int length,
int frag_offset, int frag_length,
uint8 *buf) {
dtls_int_to_uint8(buf, type);
buf += sizeof(uint8);
dtls_int_to_uint24(buf, length);
buf += sizeof(uint24);
if (peer && peer->handshake_params) {
/* and copy the result to buf */
dtls_int_to_uint16(buf, peer->handshake_params->hs_state.mseq_s);
/* increment handshake message sequence counter by 1 */
peer->handshake_params->hs_state.mseq_s++;
} else {
memset(buf, 0, sizeof(uint16));
}
buf += sizeof(uint16);
dtls_int_to_uint24(buf, frag_offset);
buf += sizeof(uint24);
dtls_int_to_uint24(buf, frag_length);
buf += sizeof(uint24);
return buf;
}
/** only one compression method is currently defined */
static uint8 compression_methods[] = {
TLS_COMPRESSION_NULL
};
/** returns true if the cipher matches TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 */
static inline int is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(dtls_cipher_t cipher)
{
#ifdef DTLS_ECC
return cipher == TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8;
#else
(void)cipher;
return 0;
#endif /* DTLS_ECC */
}
/** returns true if the cipher matches TLS_PSK_WITH_AES_128_CCM_8 */
static inline int is_tls_psk_with_aes_128_ccm_8(dtls_cipher_t cipher)
{
#ifdef DTLS_PSK
return cipher == TLS_PSK_WITH_AES_128_CCM_8;
#else
return 0;
#endif /* DTLS_PSK */
}
/** returns true if the application is configured for psk */
static inline int is_psk_supported(dtls_context_t *ctx)
{
#ifdef DTLS_PSK
return ctx && ctx->h && ctx->h->get_psk_info;
#else
return 0;
#endif /* DTLS_PSK */
}
/** returns true if the application is configured for ecdhe_ecdsa */
static inline int is_ecdsa_supported(dtls_context_t *ctx, int is_client)
{
#ifdef DTLS_ECC
return ctx && ctx->h && ((!is_client && ctx->h->get_ecdsa_key) ||
(is_client && ctx->h->verify_ecdsa_key));
#else
(void)ctx;
(void)is_client;
return 0;
#endif /* DTLS_ECC */
}
/** Returns true if the application is configured for ecdhe_ecdsa with
* client authentication */
static inline int is_ecdsa_client_auth_supported(dtls_context_t *ctx)
{
#ifdef DTLS_ECC
return ctx && ctx->h && ctx->h->get_ecdsa_key && ctx->h->verify_ecdsa_key;
#else
(void)ctx;
return 0;
#endif /* DTLS_ECC */
}
/**
* Returns @c 1 if @p code is a cipher suite other than @c
* TLS_NULL_WITH_NULL_NULL that we recognize.
*
* @param ctx The current DTLS context
* @param code The cipher suite identifier to check
* @param is_client 1 for a dtls client, 0 for server
* @return @c 1 iff @p code is recognized,
*/
static int
known_cipher(dtls_context_t *ctx, dtls_cipher_t code, int is_client) {
int psk;
int ecdsa;
psk = is_psk_supported(ctx);
ecdsa = is_ecdsa_supported(ctx, is_client);
return (psk && is_tls_psk_with_aes_128_ccm_8(code)) ||
(ecdsa && is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(code));
}
/**
* This method detects if we already have a established DTLS session with
* peer and the peer is attempting to perform a fresh handshake by sending
* messages with epoch = 0. This is to handle situations mentioned in
* RFC 6347 - section 4.2.8.
*
* @param msg The packet received from Client
* @param msglen Packet length
* @param peer peer who is the sender for this packet
* @return @c 1 if this is a rehandshake attempt by
* client
*/
static int
hs_attempt_with_existing_peer(uint8_t *msg, size_t msglen,
dtls_peer_t *peer)
{
(void)msglen;
if ((peer) && (peer->state == DTLS_STATE_CONNECTED)) {
if (msg[0] == DTLS_CT_HANDSHAKE) {
uint16_t msg_epoch = dtls_uint16_to_int(DTLS_RECORD_HEADER(msg)->epoch);
if (msg_epoch == 0) {
dtls_handshake_header_t * hs_header = DTLS_HANDSHAKE_HEADER(msg + DTLS_RH_LENGTH);
if (hs_header->msg_type == DTLS_HT_CLIENT_HELLO ||
hs_header->msg_type == DTLS_HT_HELLO_REQUEST) {
return 1;
}
}
}
}
return 0;
}
/** Dump out the cipher keys and IVs used for the symetric cipher. */
static void dtls_debug_keyblock(dtls_security_parameters_t *config)
{
dtls_debug("key_block (%d bytes):\n", dtls_kb_size(config, peer->role));
dtls_debug_dump(" client_MAC_secret",
dtls_kb_client_mac_secret(config, peer->role),
dtls_kb_mac_secret_size(config, peer->role));
dtls_debug_dump(" server_MAC_secret",
dtls_kb_server_mac_secret(config, peer->role),
dtls_kb_mac_secret_size(config, peer->role));
dtls_debug_dump(" client_write_key",
dtls_kb_client_write_key(config, peer->role),
dtls_kb_key_size(config, peer->role));
dtls_debug_dump(" server_write_key",
dtls_kb_server_write_key(config, peer->role),
dtls_kb_key_size(config, peer->role));
dtls_debug_dump(" client_IV",
dtls_kb_client_iv(config, peer->role),
dtls_kb_iv_size(config, peer->role));
dtls_debug_dump(" server_IV",
dtls_kb_server_iv(config, peer->role),
dtls_kb_iv_size(config, peer->role));
}
/** 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
*/
static char *dtls_handshake_type_to_name(int type)
{
switch (type) {
case DTLS_HT_HELLO_REQUEST:
return "hello_request";
case DTLS_HT_CLIENT_HELLO:
return "client_hello";
case DTLS_HT_SERVER_HELLO:
return "server_hello";
case DTLS_HT_HELLO_VERIFY_REQUEST:
return "hello_verify_request";
case DTLS_HT_CERTIFICATE:
return "certificate";
case DTLS_HT_SERVER_KEY_EXCHANGE:
return "server_key_exchange";
case DTLS_HT_CERTIFICATE_REQUEST:
return "certificate_request";
case DTLS_HT_SERVER_HELLO_DONE:
return "server_hello_done";
case DTLS_HT_CERTIFICATE_VERIFY:
return "certificate_verify";
case DTLS_HT_CLIENT_KEY_EXCHANGE:
return "client_key_exchange";
case DTLS_HT_FINISHED:
return "finished";
default:
return "unknown";
}
}
/**
* Calculate the pre master secret and after that calculate the master-secret.
*/
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) {
unsigned char *pre_master_secret;
int pre_master_len = 0;
dtls_security_parameters_t *security = dtls_security_params_next(peer);
uint8 master_secret[DTLS_MASTER_SECRET_LENGTH];
(void)role; /* The macro dtls_kb_size() does not use role. */
if (!security) {
return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
}
pre_master_secret = security->key_block;
switch (handshake->cipher) {
#ifdef DTLS_PSK
case TLS_PSK_WITH_AES_128_CCM_8: {
unsigned char psk[DTLS_PSK_MAX_KEY_LEN];
int len;
len = CALL(ctx, get_psk_info, session, DTLS_PSK_KEY,
handshake->keyx.psk.identity,
handshake->keyx.psk.id_length,
psk, DTLS_PSK_MAX_KEY_LEN);
if (len < 0) {
dtls_crit("no psk key for session available\n");
return len;
}
/* Temporarily use the key_block storage space for the pre master secret. */
pre_master_len = dtls_psk_pre_master_secret(psk, len,
pre_master_secret,
MAX_KEYBLOCK_LENGTH);
dtls_debug_hexdump("psk", psk, len);
memset(psk, 0, DTLS_PSK_MAX_KEY_LEN);
if (pre_master_len < 0) {
dtls_crit("the psk was too long, for the pre master secret\n");
return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
}
break;
}
#endif /* DTLS_PSK */
#ifdef DTLS_ECC
case TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8: {
pre_master_len = dtls_ecdh_pre_master_secret(handshake->keyx.ecdsa.own_eph_priv,
handshake->keyx.ecdsa.other_eph_pub_x,
handshake->keyx.ecdsa.other_eph_pub_y,
sizeof(handshake->keyx.ecdsa.own_eph_priv),
pre_master_secret,
MAX_KEYBLOCK_LENGTH);
if (pre_master_len < 0) {
dtls_crit("the curve was too long, for the pre master secret\n");
return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
}
break;
}
#endif /* DTLS_ECC */
case TLS_NULL_WITH_NULL_NULL:
assert(!"calculate_key_block: tried to use NULL cipher\n");
return dtls_alert_fatal_create(DTLS_ALERT_INSUFFICIENT_SECURITY);
/* The following cases cover the enum symbols that are not
* included in this build. These must be kept just above the
* default case as they do nothing but fall through.
*/
#ifndef DTLS_PSK
case TLS_PSK_WITH_AES_128_CCM_8:
/* fall through to default */
#endif /* !DTLS_PSK */
#ifndef DTLS_ECC
case TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8:
/* fall through to default */
#endif /* !DTLS_ECC */
default:
dtls_crit("calculate_key_block: unknown cipher %x04 \n", handshake->cipher);
return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
}
dtls_debug_dump("client_random", handshake->tmp.random.client, DTLS_RANDOM_LENGTH);
dtls_debug_dump("server_random", handshake->tmp.random.server, DTLS_RANDOM_LENGTH);
dtls_debug_dump("pre_master_secret", pre_master_secret, pre_master_len);
dtls_prf(pre_master_secret, pre_master_len,
PRF_LABEL(master), PRF_LABEL_SIZE(master),
handshake->tmp.random.client, DTLS_RANDOM_LENGTH,
handshake->tmp.random.server, DTLS_RANDOM_LENGTH,
master_secret,
DTLS_MASTER_SECRET_LENGTH);
dtls_debug_dump("master_secret", master_secret, DTLS_MASTER_SECRET_LENGTH);
/* create key_block from master_secret
* key_block = PRF(master_secret,
"key expansion" + tmp.random.server + tmp.random.client) */
dtls_prf(master_secret,
DTLS_MASTER_SECRET_LENGTH,
PRF_LABEL(key), PRF_LABEL_SIZE(key),
handshake->tmp.random.server, DTLS_RANDOM_LENGTH,
handshake->tmp.random.client, DTLS_RANDOM_LENGTH,
security->key_block,
dtls_kb_size(security, role));
memcpy(handshake->tmp.master_secret, master_secret, DTLS_MASTER_SECRET_LENGTH);
dtls_debug_keyblock(security);
security->cipher = handshake->cipher;
security->compression = handshake->compression;
security->rseq = 0;
return 0;
}
/* TODO: add a generic method which iterates over a list and searches for a specific key */
static int verify_ext_eliptic_curves(uint8 *data, size_t data_length) {
int i, curve_name;
/* length of curve list */
i = dtls_uint16_to_int(data);
data += sizeof(uint16);
if (i + sizeof(uint16) != data_length) {
dtls_warn("the list of the supported elliptic curves should be tls extension length - 2\n");
return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
}
for (i = data_length - sizeof(uint16); i > 0; i -= sizeof(uint16)) {
/* check if this curve is supported */
curve_name = dtls_uint16_to_int(data);
data += sizeof(uint16);
if (curve_name == TLS_EXT_ELLIPTIC_CURVES_SECP256R1)
return 0;
}
dtls_warn("no supported elliptic curve found\n");
return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
}
static int verify_ext_cert_type(uint8 *data, size_t data_length) {
int i, cert_type;
/* length of cert type list */
i = dtls_uint8_to_int(data);
data += sizeof(uint8);
if (i + sizeof(uint8) != data_length) {
dtls_warn("the list of the supported certificate types should be tls extension length - 1\n");
return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
}
for (i = data_length - sizeof(uint8); i > 0; i -= sizeof(uint8)) {
/* check if this cert type is supported */
cert_type = dtls_uint8_to_int(data);
data += sizeof(uint8);
if (cert_type == TLS_CERT_TYPE_RAW_PUBLIC_KEY)
return 0;
}
dtls_warn("no supported certificate type found\n");
return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
}
static int verify_ext_ec_point_formats(uint8 *data, size_t data_length) {
int i, cert_type;
/* length of ec_point_formats list */
i = dtls_uint8_to_int(data);
data += sizeof(uint8);
if (i + sizeof(uint8) != data_length) {
dtls_warn("the list of the supported ec_point_formats should be tls extension length - 1\n");
return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
}
for (i = data_length - sizeof(uint8); i > 0; i -= sizeof(uint8)) {
/* check if this ec_point_format is supported */
cert_type = dtls_uint8_to_int(data);
data += sizeof(uint8);
if (cert_type == TLS_EXT_EC_POINT_FORMATS_UNCOMPRESSED)
return 0;
}
dtls_warn("no supported ec_point_format found\n");
return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
}
/*
* Check for some TLS Extensions used by the ECDHE_ECDSA cipher.
*/
static int
dtls_check_tls_extension(dtls_peer_t *peer,
uint8 *data, size_t data_length, int client_hello)
{
uint16_t i, j;
int ext_elliptic_curve = 0;
int ext_client_cert_type = 0;
int ext_server_cert_type = 0;
int ext_ec_point_formats = 0;
dtls_handshake_parameters_t *handshake = peer->handshake_params;
if (data_length < sizeof(uint16)) {
/* no tls extensions specified */
if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(handshake->cipher)) {
goto error;
}
return 0;
}
/* get the length of the tls extension list */
j = dtls_uint16_to_int(data);
data += sizeof(uint16);
data_length -= sizeof(uint16);
if (data_length < j)
goto error;
/* check for TLS extensions needed for this cipher */
while (data_length) {
if (data_length < sizeof(uint16) * 2)
goto error;
/* get the tls extension type */
i = dtls_uint16_to_int(data);
data += sizeof(uint16);
data_length -= sizeof(uint16);
/* get the length of the tls extension */
j = dtls_uint16_to_int(data);
data += sizeof(uint16);
data_length -= sizeof(uint16);
if (data_length < j)
goto error;
switch (i) {
case TLS_EXT_ELLIPTIC_CURVES:
ext_elliptic_curve = 1;
if (verify_ext_eliptic_curves(data, j))
goto error;
break;
case TLS_EXT_CLIENT_CERTIFICATE_TYPE:
ext_client_cert_type = 1;
if (client_hello) {
if (verify_ext_cert_type(data, j))
goto error;
} else {
if (dtls_uint8_to_int(data) != TLS_CERT_TYPE_RAW_PUBLIC_KEY)
goto error;
}
break;
case TLS_EXT_SERVER_CERTIFICATE_TYPE:
ext_server_cert_type = 1;
if (client_hello) {
if (verify_ext_cert_type(data, j))
goto error;
} else {
if (dtls_uint8_to_int(data) != TLS_CERT_TYPE_RAW_PUBLIC_KEY)
goto error;
}
break;
case TLS_EXT_EC_POINT_FORMATS:
ext_ec_point_formats = 1;
if (verify_ext_ec_point_formats(data, j))
goto error;
break;
case TLS_EXT_ENCRYPT_THEN_MAC:
/* As only AEAD cipher suites are currently available, this
* extension can be skipped.
*/
dtls_info("skipped encrypt-then-mac extension\n");
break;
default:
dtls_warn("unsupported tls extension: %i\n", i);
break;
}
data += j;
data_length -= j;
}
if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(handshake->cipher) && client_hello) {
if (!ext_elliptic_curve || !ext_client_cert_type || !ext_server_cert_type
|| !ext_ec_point_formats) {
dtls_warn("not all required tls extensions found in client hello\n");
goto error;
}
} else if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(handshake->cipher) && !client_hello) {
if (!ext_client_cert_type || !ext_server_cert_type) {
dtls_warn("not all required tls extensions found in server hello\n");
goto error;
}
}
return 0;
error:
if (client_hello && peer->state == DTLS_STATE_CONNECTED) {
return dtls_alert_create(DTLS_ALERT_LEVEL_WARNING, DTLS_ALERT_NO_RENEGOTIATION);
} else {
return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
}
}
/**
* Parses the ClientHello from the client and updates the internal handshake
* parameters with the new data for the given \p peer. When the ClientHello
* handshake message in \p data does not contain a cipher suite or
* compression method, it is copied from the the current security parameters.
*
* \param ctx The current DTLS context.
* \param peer The remote peer whose security parameters are about to change.
* \param data The handshake message with a ClientHello.
* \param data_length The actual size of \p data.
* \return \c -Something if an error occurred, \c 0 on success.
*/
static int
dtls_update_parameters(dtls_context_t *ctx,
dtls_peer_t *peer,
uint8 *data, size_t data_length) {
int i;
unsigned int j;
int ok;
dtls_handshake_parameters_t *config = peer->handshake_params;
dtls_security_parameters_t *security = dtls_security_params(peer);
assert(config);
assert(data_length > DTLS_HS_LENGTH + DTLS_CH_LENGTH);
/* skip the handshake header and client version information */
data += DTLS_HS_LENGTH + sizeof(uint16);
data_length -= DTLS_HS_LENGTH + sizeof(uint16);
/* store client random in config */
memcpy(config->tmp.random.client, data, DTLS_RANDOM_LENGTH);
data += DTLS_RANDOM_LENGTH;
data_length -= DTLS_RANDOM_LENGTH;
/* Caution: SKIP_VAR_FIELD may jump to error: */
SKIP_VAR_FIELD(data, data_length, uint8); /* skip session id */
SKIP_VAR_FIELD(data, data_length, uint8); /* skip cookie */
i = dtls_uint16_to_int(data);
if (data_length < i + sizeof(uint16)) {
/* Looks like we do not have a cipher nor compression. This is ok
* for renegotiation, but not for the initial handshake. */
if (!security || security->cipher == TLS_NULL_WITH_NULL_NULL)
goto error;
config->cipher = security->cipher;
config->compression = security->compression;
return 0;
}
data += sizeof(uint16);
data_length -= sizeof(uint16) + i;
ok = 0;
while (i && !ok) {
config->cipher = dtls_uint16_to_int(data);
ok = known_cipher(ctx, config->cipher, 0);
i -= sizeof(uint16);
data += sizeof(uint16);
}
/* skip remaining ciphers */
data += i;
if (!ok) {
/* reset config cipher to a well-defined value */
config->cipher = TLS_NULL_WITH_NULL_NULL;
dtls_warn("No matching cipher found\n");
goto error;
}
if (data_length < sizeof(uint8)) {
/* no compression specified, take the current compression method */
if (security)
config->compression = security->compression;
else
config->compression = TLS_COMPRESSION_NULL;
return 0;
}
i = dtls_uint8_to_int(data);
if (data_length < i + sizeof(uint8))
goto error;
data += sizeof(uint8);
data_length -= sizeof(uint8) + i;
ok = 0;
while (i && !ok) {
for (j = 0; j < sizeof(compression_methods) / sizeof(uint8); ++j)
if (dtls_uint8_to_int(data) == compression_methods[j]) {
config->compression = compression_methods[j];
ok = 1;
}
i -= sizeof(uint8);
data += sizeof(uint8);
}
if (!ok) {
/* reset config cipher to a well-defined value */
goto error;
}
return dtls_check_tls_extension(peer, data, data_length, 1);
error:
if (peer->state == DTLS_STATE_CONNECTED) {
return dtls_alert_create(DTLS_ALERT_LEVEL_WARNING, DTLS_ALERT_NO_RENEGOTIATION);
} else {
return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
}
}
/**
* Parse the ClientKeyExchange and update the internal handshake state with
* the new data.
*/
static inline int
check_client_keyexchange(dtls_context_t *ctx,
dtls_handshake_parameters_t *handshake,
uint8 *data, size_t length) {
(void)ctx;
#ifdef DTLS_ECC
if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(handshake->cipher)) {
if (length < DTLS_HS_LENGTH + DTLS_CKXEC_LENGTH) {
dtls_debug("The client key exchange is too short\n");
return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
}
data += DTLS_HS_LENGTH;
if (dtls_uint8_to_int(data) != 1 + 2 * DTLS_EC_KEY_SIZE) {
dtls_alert("expected 65 bytes long public point\n");
return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
}
data += sizeof(uint8);
if (dtls_uint8_to_int(data) != 4) {
dtls_alert("expected uncompressed public point\n");
return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
}
data += sizeof(uint8);
memcpy(handshake->keyx.ecdsa.other_eph_pub_x, data,
sizeof(handshake->keyx.ecdsa.other_eph_pub_x));
data += sizeof(handshake->keyx.ecdsa.other_eph_pub_x);
memcpy(handshake->keyx.ecdsa.other_eph_pub_y, data,
sizeof(handshake->keyx.ecdsa.other_eph_pub_y));
data += sizeof(handshake->keyx.ecdsa.other_eph_pub_y);
}
#endif /* DTLS_ECC */
#ifdef DTLS_PSK
if (is_tls_psk_with_aes_128_ccm_8(handshake->cipher)) {
int id_length;
if (length < DTLS_HS_LENGTH + DTLS_CKXPSK_LENGTH_MIN) {
dtls_debug("The client key exchange is too short\n");
return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
}
data += DTLS_HS_LENGTH;
id_length = dtls_uint16_to_int(data);
data += sizeof(uint16);
if (DTLS_HS_LENGTH + DTLS_CKXPSK_LENGTH_MIN + id_length != length) {
dtls_debug("The identity has a wrong length\n");
return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
}
if (id_length > DTLS_PSK_MAX_CLIENT_IDENTITY_LEN) {
dtls_warn("please use a smaller client identity\n");
return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
}
handshake->keyx.psk.id_length = id_length;
memcpy(handshake->keyx.psk.identity, data, id_length);
}
#endif /* DTLS_PSK */
return 0;
}
static inline void
update_hs_hash(dtls_peer_t *peer, uint8 *data, size_t length) {
dtls_debug_dump("add MAC data", data, length);
dtls_hash_update(&peer->handshake_params->hs_state.hs_hash, data, length);
}
static void
copy_hs_hash(dtls_peer_t *peer, dtls_hash_ctx *hs_hash) {
memcpy(hs_hash, &peer->handshake_params->hs_state.hs_hash,
sizeof(peer->handshake_params->hs_state.hs_hash));
}
static inline size_t
finalize_hs_hash(dtls_peer_t *peer, uint8 *buf) {
return dtls_hash_finalize(buf, &peer->handshake_params->hs_state.hs_hash);
}
static inline void
clear_hs_hash(dtls_peer_t *peer) {
assert(peer);
dtls_debug("clear MAC\n");
dtls_hash_init(&peer->handshake_params->hs_state.hs_hash);
}
/**
* Checks if \p record + \p data contain a Finished message with valid
* verify_data.
*
* \param ctx The current DTLS context.
* \param peer The remote peer of the security association.
* \param data The cleartext payload of the message.
* \param data_length Actual length of \p data.
* \return \c 0 if the Finished message is valid, \c negative number otherwise.
*/
static int
check_finished(dtls_context_t *ctx, dtls_peer_t *peer,
uint8 *data, size_t data_length) {
size_t digest_length, label_size;
const unsigned char *label;
unsigned char buf[DTLS_HMAC_MAX];
(void)ctx;
if (data_length < DTLS_HS_LENGTH + DTLS_FIN_LENGTH)
return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
/* Use a union here to ensure that sufficient stack space is
* reserved. As statebuf and verify_data are not used at the same
* time, we can re-use the storage safely.
*/
union {
unsigned char statebuf[DTLS_HASH_CTX_SIZE];
unsigned char verify_data[DTLS_FIN_LENGTH];
} b;
/* temporarily store hash status for roll-back after finalize */
memcpy(b.statebuf, &peer->handshake_params->hs_state.hs_hash, DTLS_HASH_CTX_SIZE);
digest_length = finalize_hs_hash(peer, buf);
/* clear_hash(); */
/* restore hash status */
memcpy(&peer->handshake_params->hs_state.hs_hash, b.statebuf, DTLS_HASH_CTX_SIZE);
if (peer->role == DTLS_CLIENT) {
label = PRF_LABEL(server);
label_size = PRF_LABEL_SIZE(server);
} else { /* server */
label = PRF_LABEL(client);
label_size = PRF_LABEL_SIZE(client);
}
dtls_prf(peer->handshake_params->tmp.master_secret,
DTLS_MASTER_SECRET_LENGTH,
label, label_size,
PRF_LABEL(finished), PRF_LABEL_SIZE(finished),
buf, digest_length,
b.verify_data, sizeof(b.verify_data));
dtls_debug_dump("d:", data + DTLS_HS_LENGTH, sizeof(b.verify_data));
dtls_debug_dump("v:", b.verify_data, sizeof(b.verify_data));
/* compare verify data and create DTLS alert code when they differ */
return equals(data + DTLS_HS_LENGTH, b.verify_data, sizeof(b.verify_data))
? 0
: dtls_alert_create(DTLS_ALERT_LEVEL_FATAL, DTLS_ALERT_HANDSHAKE_FAILURE);
}
/**
* Prepares the payload given in \p data for sending with
* dtls_send(). The \p data is encrypted and compressed according to
* the current security parameters of \p peer. The result of this
* operation is put into \p sendbuf with a prepended record header of
* type \p type ready for sending. As some cipher suites add a MAC
* before encryption, \p data must be large enough to hold this data
* as well (usually \c dtls_kb_digest_size(CURRENT_CONFIG(peer)).
*
* \param peer The remote peer the packet will be sent to.
* \param security The encryption paramater used to encrypt
* \param type The content type of this record.
* \param data_array Array with payloads in correct order.
* \param data_len_array sizes of the payloads in correct order.
* \param data_array_len The number of payloads given.
* \param sendbuf The output buffer where the encrypted record
* will be placed.
* \param rlen This parameter must be initialized with the
* maximum size of \p sendbuf and will be updated
* to hold the actual size of the stored packet
* on success. On error, the value of \p rlen is
* undefined.
* \return Less than zero on error, or greater than zero success.
*/
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) {
uint8 *p, *start;
int res;
unsigned int i;
if (*rlen < DTLS_RH_LENGTH) {
dtls_alert("The sendbuf (%zu bytes) is too small\n", *rlen);
return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
}
p = dtls_set_record_header(type, security, sendbuf);
start = p;
if (!security || security->cipher == TLS_NULL_WITH_NULL_NULL) {
/* no cipher suite */
res = 0;
for (i = 0; i < data_array_len; i++) {
/* check the minimum that we need for packets that are not encrypted */
if (*rlen < res + DTLS_RH_LENGTH + data_len_array[i]) {
dtls_debug("dtls_prepare_record: send buffer too small\n");
return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
}
memcpy(p, data_array[i], data_len_array[i]);
p += data_len_array[i];
res += data_len_array[i];
}
} else { /* TLS_PSK_WITH_AES_128_CCM_8 or TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 */
/**
* length of additional_data for the AEAD cipher which consists of
* seq_num(2+6) + type(1) + version(2) + length(2)
*/
#define A_DATA_LEN 13
unsigned char nonce[DTLS_CCM_BLOCKSIZE];
unsigned char A_DATA[A_DATA_LEN];
if (is_tls_psk_with_aes_128_ccm_8(security->cipher)) {
dtls_debug("dtls_prepare_record(): encrypt using TLS_PSK_WITH_AES_128_CCM_8\n");
} else if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(security->cipher)) {
dtls_debug("dtls_prepare_record(): encrypt using TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8\n");
} else {
dtls_debug("dtls_prepare_record(): encrypt using unknown cipher\n");
}
/* set nonce
from RFC 6655:
The "nonce" input to the AEAD algorithm is exactly that of [RFC5288]:
the "nonce" SHALL be 12 bytes long and is constructed as follows:
(this is an example of a "partially explicit" nonce; see Section
3.2.1 in [RFC5116]).
struct {
opaque salt[4];
opaque nonce_explicit[8];
} CCMNonce;
[...]
In DTLS, the 64-bit seq_num is the 16-bit epoch concatenated with the
48-bit seq_num.
When the nonce_explicit is equal to the sequence number, the CCMNonce
will have the structure of the CCMNonceExample given below.
struct {
uint32 client_write_IV; // low order 32-bits
uint64 seq_num; // TLS sequence number
} CCMClientNonce.
struct {
uint32 server_write_IV; // low order 32-bits
uint64 seq_num; // TLS sequence number
} CCMServerNonce.
struct {
case client:
CCMClientNonce;
case server:
CCMServerNonce:
} CCMNonceExample;
*/
memcpy(p, &DTLS_RECORD_HEADER(sendbuf)->epoch, 8);
p += 8;
res = 8;
for (i = 0; i < data_array_len; i++) {
/* check the minimum that we need for packets that are not encrypted */
if (*rlen < res + DTLS_RH_LENGTH + data_len_array[i]) {
dtls_debug("dtls_prepare_record: send buffer too small\n");
return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
}
memcpy(p, data_array[i], data_len_array[i]);
p += data_len_array[i];
res += data_len_array[i];
}
memset(nonce, 0, DTLS_CCM_BLOCKSIZE);
memcpy(nonce, dtls_kb_local_iv(security, peer->role),
dtls_kb_iv_size(security, peer->role));
memcpy(nonce + dtls_kb_iv_size(security, peer->role), start, 8); /* epoch + seq_num */
dtls_debug_dump("nonce:", nonce, DTLS_CCM_BLOCKSIZE);
dtls_debug_dump("key:", dtls_kb_local_write_key(security, peer->role),
dtls_kb_key_size(security, peer->role));
/* re-use N to create additional data according to RFC 5246, Section 6.2.3.3:
*
* additional_data = seq_num + TLSCompressed.type +
* TLSCompressed.version + TLSCompressed.length;
*/
memcpy(A_DATA, &DTLS_RECORD_HEADER(sendbuf)->epoch, 8); /* epoch and seq_num */
memcpy(A_DATA + 8, &DTLS_RECORD_HEADER(sendbuf)->content_type, 3); /* type and version */
dtls_int_to_uint16(A_DATA + 11, res - 8); /* length */
res = dtls_encrypt(start + 8, res - 8, start + 8, nonce,
dtls_kb_local_write_key(security, peer->role),
dtls_kb_key_size(security, peer->role),
A_DATA, A_DATA_LEN);
if (res < 0)
return res;
res += 8; /* increment res by size of nonce_explicit */
dtls_debug_dump("message:", start, res);
}
/* fix length of fragment in sendbuf */
dtls_int_to_uint16(sendbuf + 11, res);
*rlen = DTLS_RH_LENGTH + res;
return 0;
}
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)
{
uint8 buf[DTLS_HS_LENGTH];
uint8 *data_array[2];
size_t data_len_array[2];
int i = 0;
dtls_security_parameters_t *security = peer ? dtls_security_params(peer) : NULL;
dtls_set_handshake_header(header_type, peer, data_length, 0,
data_length, buf);
if (add_hash) {
update_hs_hash(peer, buf, sizeof(buf));
}
data_array[i] = buf;
data_len_array[i] = sizeof(buf);
i++;
if (data != NULL) {
if (add_hash) {
update_hs_hash(peer, data, data_length);
}
data_array[i] = data;
data_len_array[i] = data_length;
i++;
}
dtls_debug("send handshake packet of type: %s (%i)\n",
dtls_handshake_type_to_name(header_type), header_type);
return dtls_send_multi(ctx, peer, security, session, DTLS_CT_HANDSHAKE,
data_array, data_len_array, i);
}
static int
dtls_send_handshake_msg(dtls_context_t *ctx,
dtls_peer_t *peer,
uint8 header_type,
uint8 *data, size_t data_length)
{
return dtls_send_handshake_msg_hash(ctx, peer, &peer->session,
header_type, data, data_length, 1);
}
/**
* Returns true if the message @p Data is a handshake message that
* must be included in the calculation of verify_data in the Finished
* message.
*
* @param Type The message type. Only handshake messages but the initial
* Client Hello and Hello Verify Request are included in the hash,
* @param Data The PDU to examine.
* @param Length The length of @p Data.
*
* @return @c 1 if @p Data must be included in hash, @c 0 otherwise.
*
* @hideinitializer
*/
#define MUST_HASH(Type, Data, Length) \
((Type) == DTLS_CT_HANDSHAKE && \
((Data) != NULL) && ((Length) > 0) && \
((Data)[0] != DTLS_HT_HELLO_VERIFY_REQUEST) && \
((Data)[0] != DTLS_HT_CLIENT_HELLO || \
((Length) >= HS_HDR_LENGTH && \
(dtls_uint16_to_int(DTLS_RECORD_HEADER(Data)->epoch > 0) || \
(dtls_uint16_to_int(HANDSHAKE(Data)->message_seq) > 0)))))
/**
* Sends the data passed in @p buf as a DTLS record of type @p type to
* the given peer. The data will be encrypted and compressed according
* to the security parameters for @p peer.
*
* @param ctx The DTLS context in effect.
* @param peer The remote party where the packet is sent.
* @param type The content type of this record.
* @param buf The data to send.
* @param buflen The number of bytes to send from @p buf.
* @return Less than zero in case of an error or the number of
* bytes that have been sent otherwise.
*/
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)
{
/* We cannot use ctx->sendbuf here as it is reserved for collecting
* the input for this function, i.e. buf == ctx->sendbuf.
*
* TODO: check if we can use the receive buf here. This would mean
* that we might not be able to handle multiple records stuffed in
* one UDP datagram */
unsigned char sendbuf[DTLS_MAX_BUF];
size_t len = sizeof(sendbuf);
int res;
unsigned int i;
size_t overall_len = 0;
res = dtls_prepare_record(peer, security, type, buf_array, buf_len_array, buf_array_len, sendbuf, &len);
if (res < 0)
return res;
/* if (peer && MUST_HASH(peer, type, buf, buflen)) */
/* update_hs_hash(peer, buf, buflen); */
dtls_debug_hexdump("send header", sendbuf, sizeof(dtls_record_header_t));
for (i = 0; i < buf_array_len; i++) {
dtls_debug_hexdump("send unencrypted", buf_array[i], buf_len_array[i]);
overall_len += buf_len_array[i];
}
if ((type == DTLS_CT_HANDSHAKE && buf_array[0][0] != DTLS_HT_HELLO_VERIFY_REQUEST) ||
type == DTLS_CT_CHANGE_CIPHER_SPEC) {
/* copy handshake messages other than HelloVerify into retransmit buffer */
netq_t *n = netq_node_new(overall_len);
if (n) {
dtls_tick_t now;
dtls_ticks(&now);
n->t = now + 2 * CLOCK_SECOND;
n->retransmit_cnt = 0;
n->timeout = 2 * CLOCK_SECOND;
n->peer = peer;
n->epoch = (security) ? security->epoch : 0;
n->type = type;
n->length = 0;
for (i = 0; i < buf_array_len; i++) {
memcpy(n->data + n->length, buf_array[i], buf_len_array[i]);
n->length += buf_len_array[i];
}
if (!netq_insert_node(&ctx->sendqueue, n)) {
dtls_warn("cannot add packet to retransmit buffer\n");
netq_node_free(n);
#ifdef WITH_CONTIKI
} else {
/* must set timer within the context of the retransmit process */
PROCESS_CONTEXT_BEGIN(&dtls_retransmit_process);
etimer_set(&ctx->retransmit_timer, n->timeout);
PROCESS_CONTEXT_END(&dtls_retransmit_process);
#else /* WITH_CONTIKI */
dtls_debug("copied to sendqueue\n");
#endif /* WITH_CONTIKI */
}
} else
dtls_warn("retransmit buffer full\n");
}
/* FIXME: copy to peer's sendqueue (after fragmentation if
* necessary) and initialize retransmit timer */
res = CALL(ctx, write, session, sendbuf, len);
/* Guess number of bytes application data actually sent:
* dtls_prepare_record() tells us in len the number of bytes to
* send, res will contain the bytes actually sent. */
return res <= 0 ? res : (int)(overall_len - (len - (unsigned int)res));
}
static inline int
dtls_send_alert(dtls_context_t *ctx, dtls_peer_t *peer, dtls_alert_level_t level,
dtls_alert_t description) {
uint8_t msg[] = { level, description };
dtls_send(ctx, peer, DTLS_CT_ALERT, msg, sizeof(msg));
return 0;
}
int
dtls_close(dtls_context_t *ctx, const session_t *remote) {
int res = -1;
dtls_peer_t *peer;
peer = dtls_get_peer(ctx, remote);
if (peer) {
res = dtls_send_alert(ctx, peer, DTLS_ALERT_LEVEL_FATAL, DTLS_ALERT_CLOSE_NOTIFY);
/* indicate tear down */
peer->state = DTLS_STATE_CLOSING;
}
return res;
}
static void dtls_destroy_peer(dtls_context_t *ctx, dtls_peer_t *peer, int unlink)
{
if (peer->state != DTLS_STATE_CLOSED && peer->state != DTLS_STATE_CLOSING)
dtls_close(ctx, &peer->session);
if (unlink) {
DEL_PEER(ctx->peers, peer);
dtls_dsrv_log_addr(DTLS_LOG_DEBUG, "removed peer", &peer->session);
}
dtls_free_peer(peer);
}
/**
* 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 \p ctx). The return value is \c -1 on error, \c 0 when
* undecided, and \c 1 if the Client Hello was good.
*
* \param ctx The DTLS context.
* \param peer The remote party we are talking to, if any.
* \param session Transport address of the remote peer.
* \param state Current state of the connection.
* \param msg The received datagram.
* \param msglen Length of \p msg.
* \return \c 1 if msg is a Client Hello with a valid cookie, \c 0 or
* \c -1 otherwise.
*/
static int
dtls_verify_peer(dtls_context_t *ctx,
dtls_peer_t *peer,
session_t *session,
const dtls_state_t state,
uint8 *data, size_t data_length)
{
uint8 buf[DTLS_HV_LENGTH + DTLS_COOKIE_LENGTH];
uint8 *p = buf;
int len = DTLS_COOKIE_LENGTH;
uint8 *cookie = NULL;
int err;
#undef mycookie
#define mycookie (buf + DTLS_HV_LENGTH)
/* Store cookie where we can reuse it for the HelloVerify request. */
err = dtls_create_cookie(ctx, session, data, data_length, mycookie, &len);
if (err < 0)
return err;
dtls_debug_dump("create cookie", mycookie, len);
assert(len == DTLS_COOKIE_LENGTH);
/* Perform cookie check. */
len = dtls_get_cookie(data, data_length, &cookie);
if (len < 0) {
dtls_warn("error while fetching the cookie, err: %i\n", err);
return err;
}
dtls_debug_dump("compare with cookie", cookie, len);
/* check if cookies match */
if (len == DTLS_COOKIE_LENGTH && memcmp(cookie, mycookie, len) == 0) {
dtls_debug("found matching cookie\n");
return 0;
}
if (len > 0) {
dtls_debug_dump("invalid cookie", cookie, len);
} else {
dtls_debug("cookie len is 0!\n");
}
/* ClientHello did not contain any valid cookie, hence we send a
* HelloVerify request. */
dtls_int_to_uint16(p, DTLS_VERSION);
p += sizeof(uint16);
dtls_int_to_uint8(p, DTLS_COOKIE_LENGTH);
p += sizeof(uint8);
assert(p == mycookie);
p += DTLS_COOKIE_LENGTH;
/* TODO use the same record sequence number as in the ClientHello,
see 4.2.1. Denial-of-Service Countermeasures */
err = dtls_send_handshake_msg_hash(ctx,
state == DTLS_STATE_CONNECTED ? peer : NULL,
session,
DTLS_HT_HELLO_VERIFY_REQUEST,
buf, p - buf, 0);
if (err < 0) {
dtls_warn("cannot send HelloVerify request\n");
}
return err; /* HelloVerify is sent, now we cannot do anything but wait */
#undef mycookie
}
#ifdef DTLS_ECC
static int
dtls_check_ecdsa_signature_elem(uint8 *data, size_t data_length,
unsigned char **result_r,
unsigned char **result_s)
{
int i;
uint8 *data_orig = data;
if (dtls_uint8_to_int(data) != TLS_EXT_SIG_HASH_ALGO_SHA256) {
dtls_alert("only sha256 is supported in certificate verify\n");
return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
}
data += sizeof(uint8);
data_length -= sizeof(uint8);
if (dtls_uint8_to_int(data) != TLS_EXT_SIG_HASH_ALGO_ECDSA) {
dtls_alert("only ecdsa signature is supported in client verify\n");
return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
}
data += sizeof(uint8);
data_length -= sizeof(uint8);
if (data_length < dtls_uint16_to_int(data)) {
dtls_alert("signature length wrong\n");
return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
}
data += sizeof(uint16);
data_length -= sizeof(uint16);
if (dtls_uint8_to_int(data) != 0x30) {
dtls_alert("wrong ASN.1 struct, expected SEQUENCE\n");
return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
}
data += sizeof(uint8);
data_length -= sizeof(uint8);
if (data_length < dtls_uint8_to_int(data)) {
dtls_alert("signature length wrong\n");
return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
}
data += sizeof(uint8);
data_length -= sizeof(uint8);
if (dtls_uint8_to_int(data) != 0x02) {
dtls_alert("wrong ASN.1 struct, expected Integer\n");
return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
}
data += sizeof(uint8);
data_length -= sizeof(uint8);
i = dtls_uint8_to_int(data);
data += sizeof(uint8);
data_length -= sizeof(uint8);
/* Sometimes these values have a leeding 0 byte */
*result_r = data + i - DTLS_EC_KEY_SIZE;
data += i;
data_length -= i;
if (dtls_uint8_to_int(data) != 0x02) {
dtls_alert("wrong ASN.1 struct, expected Integer\n");
return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
}
data += sizeof(uint8);
data_length -= sizeof(uint8);
i = dtls_uint8_to_int(data);
data += sizeof(uint8);
data_length -= sizeof(uint8);
/* Sometimes these values have a leeding 0 byte */
*result_s = data + i - DTLS_EC_KEY_SIZE;
data += i;
data_length -= i;
return data - data_orig;
}
static int
check_client_certificate_verify(dtls_context_t *ctx,
dtls_peer_t *peer,
uint8 *data, size_t data_length)
{
dtls_handshake_parameters_t *config = peer->handshake_params;
int ret;
unsigned char *result_r;
unsigned char *result_s;
dtls_hash_ctx hs_hash;
unsigned char sha256hash[DTLS_HMAC_DIGEST_SIZE];
assert(is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(config->cipher));
data += DTLS_HS_LENGTH;
if (data_length < DTLS_HS_LENGTH + DTLS_CV_LENGTH) {
dtls_alert("the packet length does not match the expected\n");
return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
}
ret = dtls_check_ecdsa_signature_elem(data, data_length, &result_r, &result_s);
if (ret < 0) {
return ret;
}
data += ret;
data_length -= ret;
copy_hs_hash(peer, &hs_hash);
dtls_hash_finalize(sha256hash, &hs_hash);
ret = dtls_ecdsa_verify_sig_hash(config->keyx.ecdsa.other_pub_x, config->keyx.ecdsa.other_pub_y,
sizeof(config->keyx.ecdsa.other_pub_x),
sha256hash, sizeof(sha256hash),
result_r, result_s);
if (ret < 0) {
dtls_alert("wrong signature err: %i\n", ret);
return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
}
return 0;
}
#endif /* DTLS_ECC */
static int
dtls_send_server_hello(dtls_context_t *ctx, dtls_peer_t *peer)
{
/* Ensure that the largest message to create fits in our source
* buffer. (The size of the destination buffer is checked by the
* encoding function, so we do not need to guess.) */
uint8 buf[DTLS_SH_LENGTH + 2 + 5 + 5 + 8 + 6];
uint8 *p;
int ecdsa;
uint8 extension_size;
dtls_handshake_parameters_t *handshake = peer->handshake_params;
dtls_tick_t now;
ecdsa = is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(handshake->cipher);
extension_size = (ecdsa) ? 2 + 5 + 5 + 6 : 0;
/* Handshake header */
p = buf;
/* ServerHello */
dtls_int_to_uint16(p, DTLS_VERSION);
p += sizeof(uint16);
/* Set server random: First 4 bytes are the server's Unix timestamp,
* followed by 28 bytes of generate random data. */
dtls_ticks(&now);
dtls_int_to_uint32(handshake->tmp.random.server, now / CLOCK_SECOND);
dtls_prng(handshake->tmp.random.server + 4, 28);
memcpy(p, handshake->tmp.random.server, DTLS_RANDOM_LENGTH);
p += DTLS_RANDOM_LENGTH;
*p++ = 0; /* no session id */
if (handshake->cipher != TLS_NULL_WITH_NULL_NULL) {
/* selected cipher suite */
dtls_int_to_uint16(p, handshake->cipher);
p += sizeof(uint16);
/* selected compression method */
*p++ = compression_methods[handshake->compression];
}
if (extension_size) {
/* length of the extensions */
dtls_int_to_uint16(p, extension_size - 2);
p += sizeof(uint16);
}
if (ecdsa) {
/* client certificate type extension */
dtls_int_to_uint16(p, TLS_EXT_CLIENT_CERTIFICATE_TYPE);
p += sizeof(uint16);
/* length of this extension type */
dtls_int_to_uint16(p, 1);
p += sizeof(uint16);
dtls_int_to_uint8(p, TLS_CERT_TYPE_RAW_PUBLIC_KEY);
p += sizeof(uint8);
/* client certificate type extension */
dtls_int_to_uint16(p, TLS_EXT_SERVER_CERTIFICATE_TYPE);
p += sizeof(uint16);
/* length of this extension type */
dtls_int_to_uint16(p, 1);
p += sizeof(uint16);
dtls_int_to_uint8(p, TLS_CERT_TYPE_RAW_PUBLIC_KEY);
p += sizeof(uint8);
/* ec_point_formats */
dtls_int_to_uint16(p, TLS_EXT_EC_POINT_FORMATS);
p += sizeof(uint16);
/* length of this extension type */
dtls_int_to_uint16(p, 2);
p += sizeof(uint16);
/* number of supported formats */
dtls_int_to_uint8(p, 1);
p += sizeof(uint8);
dtls_int_to_uint8(p, TLS_EXT_EC_POINT_FORMATS_UNCOMPRESSED);
p += sizeof(uint8);
}
assert((buf <= p) && ((unsigned int)(p - buf) <= sizeof(buf)));
/* TODO use the same record sequence number as in the ClientHello,
see 4.2.1. Denial-of-Service Countermeasures */
return dtls_send_handshake_msg(ctx, peer, DTLS_HT_SERVER_HELLO,
buf, p - buf);
}
#ifdef DTLS_ECC
#define DTLS_EC_SUBJECTPUBLICKEY_SIZE (2 * DTLS_EC_KEY_SIZE + sizeof(cert_asn1_header))
static int
dtls_send_certificate_ecdsa(dtls_context_t *ctx, dtls_peer_t *peer,
const dtls_ecdsa_key_t *key)
{
uint8 buf[DTLS_CE_LENGTH];
uint8 *p;
/* Certificate
*
* Start message construction at beginning of buffer. */
p = buf;
/* length of this certificate */
dtls_int_to_uint24(p, DTLS_EC_SUBJECTPUBLICKEY_SIZE);
p += sizeof(uint24);
memcpy(p, &cert_asn1_header, sizeof(cert_asn1_header));
p += sizeof(cert_asn1_header);
memcpy(p, key->pub_key_x, DTLS_EC_KEY_SIZE);
p += DTLS_EC_KEY_SIZE;
memcpy(p, key->pub_key_y, DTLS_EC_KEY_SIZE);
p += DTLS_EC_KEY_SIZE;
assert(p - buf <= sizeof(buf));
return dtls_send_handshake_msg(ctx, peer, DTLS_HT_CERTIFICATE,
buf, p - buf);
}
static uint8 *
dtls_add_ecdsa_signature_elem(uint8 *p, uint32_t *point_r, uint32_t *point_s)
{
int len_r;
int len_s;
#define R_KEY_OFFSET (1 + 1 + 2 + 1 + 1 + 1 + 1)
#define S_KEY_OFFSET(len_s) (R_KEY_OFFSET + (len_s) + 1 + 1)
/* store the pointer to the r component of the signature and make space */
len_r = dtls_ec_key_from_uint32_asn1(point_r, DTLS_EC_KEY_SIZE, p + R_KEY_OFFSET);
len_s = dtls_ec_key_from_uint32_asn1(point_s, DTLS_EC_KEY_SIZE, p + S_KEY_OFFSET(len_r));
#undef R_KEY_OFFSET
#undef S_KEY_OFFSET
/* sha256 */
dtls_int_to_uint8(p, TLS_EXT_SIG_HASH_ALGO_SHA256);
p += sizeof(uint8);
/* ecdsa */
dtls_int_to_uint8(p, TLS_EXT_SIG_HASH_ALGO_ECDSA);
p += sizeof(uint8);
/* length of signature */
dtls_int_to_uint16(p, len_r + len_s + 2 + 2 + 2);
p += sizeof(uint16);
/* ASN.1 SEQUENCE */
dtls_int_to_uint8(p, 0x30);
p += sizeof(uint8);
dtls_int_to_uint8(p, len_r + len_s + 2 + 2);
p += sizeof(uint8);
/* ASN.1 Integer r */
dtls_int_to_uint8(p, 0x02);
p += sizeof(uint8);
dtls_int_to_uint8(p, len_r);
p += sizeof(uint8);
/* the pint r was added here */
p += len_r;
/* ASN.1 Integer s */
dtls_int_to_uint8(p, 0x02);
p += sizeof(uint8);
dtls_int_to_uint8(p, len_s);
p += sizeof(uint8);
/* the pint s was added here */
p += len_s;
return p;
}
static int
dtls_send_server_key_exchange_ecdh(dtls_context_t *ctx, dtls_peer_t *peer,
const dtls_ecdsa_key_t *key)
{
/* The ASN.1 Integer representation of an 32 byte unsigned int could be
* 33 bytes long add space for that */
uint8 buf[DTLS_SKEXEC_LENGTH + 2];
uint8 *p;
uint8 *key_params;
uint8 *ephemeral_pub_x;
uint8 *ephemeral_pub_y;
uint32_t point_r[9];
uint32_t point_s[9];
dtls_handshake_parameters_t *config = peer->handshake_params;
/* ServerKeyExchange
*
* Start message construction at beginning of buffer. */
p = buf;
key_params = p;
/* ECCurveType curve_type: named_curve */
dtls_int_to_uint8(p, 3);
p += sizeof(uint8);
/* NamedCurve namedcurve: secp256r1 */
dtls_int_to_uint16(p, TLS_EXT_ELLIPTIC_CURVES_SECP256R1);
p += sizeof(uint16);
dtls_int_to_uint8(p, 1 + 2 * DTLS_EC_KEY_SIZE);
p += sizeof(uint8);
/* This should be an uncompressed point, but I do not have access to the spec. */
dtls_int_to_uint8(p, 4);
p += sizeof(uint8);
/* store the pointer to the x component of the pub key and make space */
ephemeral_pub_x = p;
p += DTLS_EC_KEY_SIZE;
/* store the pointer to the y component of the pub key and make space */
ephemeral_pub_y = p;
p += DTLS_EC_KEY_SIZE;
dtls_ecdsa_generate_key(config->keyx.ecdsa.own_eph_priv,
ephemeral_pub_x, ephemeral_pub_y,
DTLS_EC_KEY_SIZE);
/* sign the ephemeral and its paramaters */
dtls_ecdsa_create_sig(key->priv_key, DTLS_EC_KEY_SIZE,
config->tmp.random.client, DTLS_RANDOM_LENGTH,
config->tmp.random.server, DTLS_RANDOM_LENGTH,
key_params, p - key_params,
point_r, point_s);
p = dtls_add_ecdsa_signature_elem(p, point_r, point_s);
assert(p - buf <= sizeof(buf));
return dtls_send_handshake_msg(ctx, peer, DTLS_HT_SERVER_KEY_EXCHANGE,
buf, p - buf);
}
#endif /* DTLS_ECC */
#ifdef DTLS_PSK
static int
dtls_send_server_key_exchange_psk(dtls_context_t *ctx, dtls_peer_t *peer,
const unsigned char *psk_hint, size_t len)
{
uint8 buf[DTLS_SKEXECPSK_LENGTH_MAX];
uint8 *p;
p = buf;
assert(len <= DTLS_PSK_MAX_CLIENT_IDENTITY_LEN);
if (len > DTLS_PSK_MAX_CLIENT_IDENTITY_LEN) {
/* should never happen */
dtls_warn("psk identity hint is too long\n");
return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
}
dtls_int_to_uint16(p, len);
p += sizeof(uint16);
memcpy(p, psk_hint, len);
p += len;
assert((buf <= p) && ((unsigned int)(p - buf) <= sizeof(buf)));
return dtls_send_handshake_msg(ctx, peer, DTLS_HT_SERVER_KEY_EXCHANGE,
buf, p - buf);
}
#endif /* DTLS_PSK */
#ifdef DTLS_ECC
static int
dtls_send_server_certificate_request(dtls_context_t *ctx, dtls_peer_t *peer)
{
uint8 buf[8];
uint8 *p;
/* ServerHelloDone
*
* Start message construction at beginning of buffer. */
p = buf;
/* certificate_types */
dtls_int_to_uint8(p, 1);
p += sizeof(uint8);
/* ecdsa_sign */
dtls_int_to_uint8(p, TLS_CLIENT_CERTIFICATE_TYPE_ECDSA_SIGN);
p += sizeof(uint8);
/* supported_signature_algorithms */
dtls_int_to_uint16(p, 2);
p += sizeof(uint16);
/* sha256 */
dtls_int_to_uint8(p, TLS_EXT_SIG_HASH_ALGO_SHA256);
p += sizeof(uint8);
/* ecdsa */
dtls_int_to_uint8(p, TLS_EXT_SIG_HASH_ALGO_ECDSA);
p += sizeof(uint8);
/* certificate_authoritiess */
dtls_int_to_uint16(p, 0);
p += sizeof(uint16);
assert(p - buf <= sizeof(buf));
return dtls_send_handshake_msg(ctx, peer, DTLS_HT_CERTIFICATE_REQUEST,
buf, p - buf);
}
#endif /* DTLS_ECC */
static int
dtls_send_server_hello_done(dtls_context_t *ctx, dtls_peer_t *peer)
{
/* ServerHelloDone
*
* Start message construction at beginning of buffer. */
return dtls_send_handshake_msg(ctx, peer, DTLS_HT_SERVER_HELLO_DONE,
NULL, 0);
}
static int
dtls_send_server_hello_msgs(dtls_context_t *ctx, dtls_peer_t *peer)
{
int res;
res = dtls_send_server_hello(ctx, peer);
if (res < 0) {
dtls_debug("dtls_server_hello: cannot prepare ServerHello record\n");
return res;
}
#ifdef DTLS_ECC
if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(peer->handshake_params->cipher)) {
const dtls_ecdsa_key_t *ecdsa_key;
res = CALL(ctx, get_ecdsa_key, &peer->session, &ecdsa_key);
if (res < 0) {
dtls_crit("no ecdsa certificate to send in certificate\n");
return res;
}
res = dtls_send_certificate_ecdsa(ctx, peer, ecdsa_key);
if (res < 0) {
dtls_debug("dtls_server_hello: cannot prepare Certificate record\n");
return res;
}
res = dtls_send_server_key_exchange_ecdh(ctx, peer, ecdsa_key);
if (res < 0) {
dtls_debug("dtls_server_hello: cannot prepare Server Key Exchange record\n");
return res;
}
if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(peer->handshake_params->cipher) &&
is_ecdsa_client_auth_supported(ctx)) {
res = dtls_send_server_certificate_request(ctx, peer);
if (res < 0) {
dtls_debug("dtls_server_hello: cannot prepare certificate Request record\n");
return res;
}
}
}
#endif /* DTLS_ECC */
#ifdef DTLS_PSK
if (is_tls_psk_with_aes_128_ccm_8(peer->handshake_params->cipher)) {
unsigned char psk_hint[DTLS_PSK_MAX_CLIENT_IDENTITY_LEN];
int len;
/* The identity hint is optional, therefore we ignore the result
* and check psk only. */
len = CALL(ctx, get_psk_info, &peer->session, DTLS_PSK_HINT,
NULL, 0, psk_hint, DTLS_PSK_MAX_CLIENT_IDENTITY_LEN);
if (len < 0) {
dtls_debug("dtls_server_hello: cannot create ServerKeyExchange\n");
return len;
}
if (len > 0) {
res = dtls_send_server_key_exchange_psk(ctx, peer, psk_hint, (size_t)len);
if (res < 0) {
dtls_debug("dtls_server_key_exchange_psk: cannot send server key exchange record\n");
return res;
}
}
}
#endif /* DTLS_PSK */
res = dtls_send_server_hello_done(ctx, peer);
if (res < 0) {
dtls_debug("dtls_server_hello: cannot prepare ServerHelloDone record\n");
return res;
}
return 0;
}
static inline int
dtls_send_ccs(dtls_context_t *ctx, dtls_peer_t *peer) {
uint8 buf[1] = {1};
return dtls_send(ctx, peer, DTLS_CT_CHANGE_CIPHER_SPEC, buf, 1);
}
static int
dtls_send_client_key_exchange(dtls_context_t *ctx, dtls_peer_t *peer)
{
uint8 buf[DTLS_CKXEC_LENGTH];
uint8 *p;
dtls_handshake_parameters_t *handshake = peer->handshake_params;
p = buf;
switch (handshake->cipher) {
#ifdef DTLS_PSK
case TLS_PSK_WITH_AES_128_CCM_8: {
int len;
len = CALL(ctx, get_psk_info, &peer->session, DTLS_PSK_IDENTITY,
handshake->keyx.psk.identity, handshake->keyx.psk.id_length,
buf + sizeof(uint16),
min(sizeof(buf) - sizeof(uint16),
sizeof(handshake->keyx.psk.identity)));
if (len < 0) {
dtls_crit("no psk identity set in kx\n");
return len;
}
if (len + sizeof(uint16) > DTLS_CKXEC_LENGTH) {
memset(&handshake->keyx.psk, 0, sizeof(dtls_handshake_parameters_psk_t));
dtls_warn("the psk identity is too long\n");
return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
}
handshake->keyx.psk.id_length = (unsigned int)len;
memcpy(handshake->keyx.psk.identity, p + sizeof(uint16), len);
dtls_int_to_uint16(p, handshake->keyx.psk.id_length);
p += sizeof(uint16);
memcpy(p, handshake->keyx.psk.identity, handshake->keyx.psk.id_length);
p += handshake->keyx.psk.id_length;
break;
}
#endif /* DTLS_PSK */
#ifdef DTLS_ECC
case TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8: {
uint8 *ephemeral_pub_x;
uint8 *ephemeral_pub_y;
dtls_int_to_uint8(p, 1 + 2 * DTLS_EC_KEY_SIZE);
p += sizeof(uint8);
/* This should be an uncompressed point, but I do not have access to the spec. */
dtls_int_to_uint8(p, 4);
p += sizeof(uint8);
ephemeral_pub_x = p;
p += DTLS_EC_KEY_SIZE;
ephemeral_pub_y = p;
p += DTLS_EC_KEY_SIZE;
dtls_ecdsa_generate_key(peer->handshake_params->keyx.ecdsa.own_eph_priv,
ephemeral_pub_x, ephemeral_pub_y,
DTLS_EC_KEY_SIZE);
break;
}
#endif /* DTLS_ECC */
case TLS_NULL_WITH_NULL_NULL:
assert(!"NULL cipher requested");
return dtls_alert_fatal_create(DTLS_ALERT_INSUFFICIENT_SECURITY);
/* The following cases cover the enum symbols that are not
* included in this build. These must be kept just above the
* default case as they do nothing but fall through.
*/
#ifndef DTLS_PSK
case TLS_PSK_WITH_AES_128_CCM_8:
/* fall through to default */
#endif /* !DTLS_PSK */
#ifndef DTLS_ECC
case TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8:
/* fall through to default */
#endif /* !DTLS_ECC */
default:
dtls_crit("cipher %x04 not supported\n", handshake->cipher);
return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
}
assert((buf <= p) && ((unsigned int)(p - buf) <= sizeof(buf)));
return dtls_send_handshake_msg(ctx, peer, DTLS_HT_CLIENT_KEY_EXCHANGE,
buf, p - buf);
}
#ifdef DTLS_ECC
static int
dtls_send_certificate_verify_ecdh(dtls_context_t *ctx, dtls_peer_t *peer,
const dtls_ecdsa_key_t *key)
{
/* The ASN.1 Integer representation of an 32 byte unsigned int could be
* 33 bytes long add space for that */
uint8 buf[DTLS_CV_LENGTH + 2];
uint8 *p;
uint32_t point_r[9];
uint32_t point_s[9];
dtls_hash_ctx hs_hash;
unsigned char sha256hash[DTLS_HMAC_DIGEST_SIZE];
/* ServerKeyExchange
*
* Start message construction at beginning of buffer. */
p = buf;
copy_hs_hash(peer, &hs_hash);
dtls_hash_finalize(sha256hash, &hs_hash);
/* sign the ephemeral and its paramaters */
dtls_ecdsa_create_sig_hash(key->priv_key, DTLS_EC_KEY_SIZE,
sha256hash, sizeof(sha256hash),
point_r, point_s);
p = dtls_add_ecdsa_signature_elem(p, point_r, point_s);
assert(p - buf <= sizeof(buf));
return dtls_send_handshake_msg(ctx, peer, DTLS_HT_CERTIFICATE_VERIFY,
buf, p - buf);
}
#endif /* DTLS_ECC */
static int
dtls_send_finished(dtls_context_t *ctx, dtls_peer_t *peer,
const unsigned char *label, size_t labellen)
{
int length;
uint8 hash[DTLS_HMAC_MAX];
uint8 buf[DTLS_FIN_LENGTH];
dtls_hash_ctx hs_hash;
uint8 *p = buf;
copy_hs_hash(peer, &hs_hash);
length = dtls_hash_finalize(hash, &hs_hash);
dtls_prf(peer->handshake_params->tmp.master_secret,
DTLS_MASTER_SECRET_LENGTH,
label, labellen,
PRF_LABEL(finished), PRF_LABEL_SIZE(finished),
hash, length,
p, DTLS_FIN_LENGTH);
dtls_debug_dump("server finished MAC", p, DTLS_FIN_LENGTH);
p += DTLS_FIN_LENGTH;
assert((buf <= p) && ((unsigned int)(p - buf) <= sizeof(buf)));
return dtls_send_handshake_msg(ctx, peer, DTLS_HT_FINISHED,
buf, p - buf);
}
static int
dtls_send_client_hello(dtls_context_t *ctx, dtls_peer_t *peer,
uint8 cookie[], size_t cookie_length) {
uint8 buf[DTLS_CH_LENGTH_MAX];
uint8 *p = buf;
uint8_t cipher_size;
uint8_t extension_size;
int psk;
int ecdsa;
dtls_handshake_parameters_t *handshake = peer->handshake_params;
dtls_tick_t now;
psk = is_psk_supported(ctx);
ecdsa = is_ecdsa_supported(ctx, 1);
cipher_size = 2 + ((ecdsa) ? 2 : 0) + ((psk) ? 2 : 0);
extension_size = (ecdsa) ? 2 + 6 + 6 + 8 + 6: 0;
if (cipher_size == 0) {
dtls_crit("no cipher callbacks implemented\n");
}
dtls_int_to_uint16(p, DTLS_VERSION);
p += sizeof(uint16);
if (cookie_length > DTLS_COOKIE_LENGTH_MAX) {
dtls_warn("the cookie is too long\n");
return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
}
if (cookie_length == 0) {
/* Set client random: First 4 bytes are the client's Unix timestamp,
* followed by 28 bytes of generate random data. */
dtls_ticks(&now);
dtls_int_to_uint32(handshake->tmp.random.client, now / CLOCK_SECOND);
dtls_prng(handshake->tmp.random.client + sizeof(uint32),
DTLS_RANDOM_LENGTH - sizeof(uint32));
}
/* we must use the same Client Random as for the previous request */
memcpy(p, handshake->tmp.random.client, DTLS_RANDOM_LENGTH);
p += DTLS_RANDOM_LENGTH;
/* session id (length 0) */
dtls_int_to_uint8(p, 0);
p += sizeof(uint8);
/* cookie */
dtls_int_to_uint8(p, cookie_length);
p += sizeof(uint8);
if (cookie_length != 0) {
memcpy(p, cookie, cookie_length);
p += cookie_length;
}
/* add known cipher(s) */
dtls_int_to_uint16(p, cipher_size - 2);
p += sizeof(uint16);
if (ecdsa) {
dtls_int_to_uint16(p, TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8);
p += sizeof(uint16);
}
if (psk) {
dtls_int_to_uint16(p, TLS_PSK_WITH_AES_128_CCM_8);
p += sizeof(uint16);
}
/* compression method */
dtls_int_to_uint8(p, 1);
p += sizeof(uint8);
dtls_int_to_uint8(p, TLS_COMPRESSION_NULL);
p += sizeof(uint8);
if (extension_size) {
/* length of the extensions */
dtls_int_to_uint16(p, extension_size - 2);
p += sizeof(uint16);
}
if (ecdsa) {
/* client certificate type extension */
dtls_int_to_uint16(p, TLS_EXT_CLIENT_CERTIFICATE_TYPE);
p += sizeof(uint16);
/* length of this extension type */
dtls_int_to_uint16(p, 2);
p += sizeof(uint16);
/* length of the list */
dtls_int_to_uint8(p, 1);
p += sizeof(uint8);
dtls_int_to_uint8(p, TLS_CERT_TYPE_RAW_PUBLIC_KEY);
p += sizeof(uint8);
/* client certificate type extension */
dtls_int_to_uint16(p, TLS_EXT_SERVER_CERTIFICATE_TYPE);
p += sizeof(uint16);
/* length of this extension type */
dtls_int_to_uint16(p, 2);
p += sizeof(uint16);
/* length of the list */
dtls_int_to_uint8(p, 1);
p += sizeof(uint8);
dtls_int_to_uint8(p, TLS_CERT_TYPE_RAW_PUBLIC_KEY);
p += sizeof(uint8);
/* elliptic_curves */
dtls_int_to_uint16(p, TLS_EXT_ELLIPTIC_CURVES);
p += sizeof(uint16);
/* length of this extension type */
dtls_int_to_uint16(p, 4);
p += sizeof(uint16);
/* length of the list */
dtls_int_to_uint16(p, 2);
p += sizeof(uint16);
dtls_int_to_uint16(p, TLS_EXT_ELLIPTIC_CURVES_SECP256R1);
p += sizeof(uint16);
/* ec_point_formats */
dtls_int_to_uint16(p, TLS_EXT_EC_POINT_FORMATS);
p += sizeof(uint16);
/* length of this extension type */
dtls_int_to_uint16(p, 2);
p += sizeof(uint16);
/* number of supported formats */
dtls_int_to_uint8(p, 1);
p += sizeof(uint8);
dtls_int_to_uint8(p, TLS_EXT_EC_POINT_FORMATS_UNCOMPRESSED);
p += sizeof(uint8);
}
assert((buf <= p) && ((unsigned int)(p - buf) <= sizeof(buf)));
if (cookie_length != 0)
clear_hs_hash(peer);
return dtls_send_handshake_msg_hash(ctx, peer, &peer->session,
DTLS_HT_CLIENT_HELLO,
buf, p - buf, cookie_length != 0);
}
static int
check_server_hello(dtls_context_t *ctx,
dtls_peer_t *peer,
uint8 *data, size_t data_length)
{
dtls_handshake_parameters_t *handshake = peer->handshake_params;
/* This function is called when we expect a ServerHello (i.e. we
* have sent a ClientHello). We might instead receive a HelloVerify
* request containing a cookie. If so, we must repeat the
* ClientHello with the given Cookie.
*/
if (data_length < DTLS_HS_LENGTH + DTLS_HS_LENGTH)
return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
update_hs_hash(peer, data, data_length);
/* FIXME: check data_length before accessing fields */
/* Get the server's random data and store selected cipher suite
* and compression method (like dtls_update_parameters().
* Then calculate master secret and wait for ServerHelloDone. When received,
* send ClientKeyExchange (?) and ChangeCipherSpec + ClientFinished. */
/* check server version */
data += DTLS_HS_LENGTH;
data_length -= DTLS_HS_LENGTH;
if (dtls_uint16_to_int(data) != DTLS_VERSION) {
dtls_alert("unknown DTLS version\n");
return dtls_alert_fatal_create(DTLS_ALERT_PROTOCOL_VERSION);
}
data += sizeof(uint16); /* skip version field */
data_length -= sizeof(uint16);
/* store server random data */
memcpy(handshake->tmp.random.server, data, DTLS_RANDOM_LENGTH);
/* skip server random */
data += DTLS_RANDOM_LENGTH;
data_length -= DTLS_RANDOM_LENGTH;
SKIP_VAR_FIELD(data, data_length, uint8); /* skip session id */
/* Check cipher suite. As we offer all we have, it is sufficient
* to check if the cipher suite selected by the server is in our
* list of known cipher suites. Subsets are not supported. */
handshake->cipher = dtls_uint16_to_int(data);
if (!known_cipher(ctx, handshake->cipher, 1)) {
dtls_alert("unsupported cipher 0x%02x 0x%02x\n",
data[0], data[1]);
return dtls_alert_fatal_create(DTLS_ALERT_INSUFFICIENT_SECURITY);
}
data += sizeof(uint16);
data_length -= sizeof(uint16);
/* Check if NULL compression was selected. We do not know any other. */
if (dtls_uint8_to_int(data) != TLS_COMPRESSION_NULL) {
dtls_alert("unsupported compression method 0x%02x\n", data[0]);
return dtls_alert_fatal_create(DTLS_ALERT_INSUFFICIENT_SECURITY);
}
data += sizeof(uint8);
data_length -= sizeof(uint8);
return dtls_check_tls_extension(peer, data, data_length, 0);
error:
return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
}
static int
check_server_hello_verify_request(dtls_context_t *ctx,
dtls_peer_t *peer,
uint8 *data, size_t data_length)
{
dtls_hello_verify_t *hv;
int res;
if (data_length < DTLS_HS_LENGTH + DTLS_HV_LENGTH)
return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
hv = (dtls_hello_verify_t *)(data + DTLS_HS_LENGTH);
res = dtls_send_client_hello(ctx, peer, hv->cookie, hv->cookie_length);
if (res < 0)
dtls_warn("cannot send ClientHello\n");
return res;
}
#ifdef DTLS_ECC
static int
check_server_certificate(dtls_context_t *ctx,
dtls_peer_t *peer,
uint8 *data, size_t data_length)
{
int err;
dtls_handshake_parameters_t *config = peer->handshake_params;
update_hs_hash(peer, data, data_length);
assert(is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(config->cipher));
data += DTLS_HS_LENGTH;
if (dtls_uint24_to_int(data) != DTLS_EC_SUBJECTPUBLICKEY_SIZE) {
dtls_alert("expect length of %zu bytes for certificate\n",
DTLS_EC_SUBJECTPUBLICKEY_SIZE);
return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
}
data += sizeof(uint24);
if (memcmp(data, cert_asn1_header, sizeof(cert_asn1_header))) {
dtls_alert("got an unexpected Subject public key format\n");
return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
}
data += sizeof(cert_asn1_header);
memcpy(config->keyx.ecdsa.other_pub_x, data,
sizeof(config->keyx.ecdsa.other_pub_x));
data += sizeof(config->keyx.ecdsa.other_pub_x);
memcpy(config->keyx.ecdsa.other_pub_y, data,
sizeof(config->keyx.ecdsa.other_pub_y));
data += sizeof(config->keyx.ecdsa.other_pub_y);
err = CALL(ctx, verify_ecdsa_key, &peer->session,
config->keyx.ecdsa.other_pub_x,
config->keyx.ecdsa.other_pub_y,
sizeof(config->keyx.ecdsa.other_pub_x));
if (err < 0) {
dtls_warn("The certificate was not accepted\n");
return err;
}
return 0;
}
static int
check_server_key_exchange_ecdsa(dtls_context_t *ctx,
dtls_peer_t *peer,
uint8 *data, size_t data_length)
{
dtls_handshake_parameters_t *config = peer->handshake_params;
int ret;
unsigned char *result_r;
unsigned char *result_s;
unsigned char *key_params;
update_hs_hash(peer, data, data_length);
assert(is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(config->cipher));
data += DTLS_HS_LENGTH;
if (data_length < DTLS_HS_LENGTH + DTLS_SKEXEC_LENGTH) {
dtls_alert("the packet length does not match the expected\n");
return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
}
key_params = data;
if (dtls_uint8_to_int(data) != TLS_EC_CURVE_TYPE_NAMED_CURVE) {
dtls_alert("Only named curves supported\n");
return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
}
data += sizeof(uint8);
data_length -= sizeof(uint8);
if (dtls_uint16_to_int(data) != TLS_EXT_ELLIPTIC_CURVES_SECP256R1) {
dtls_alert("secp256r1 supported\n");
return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
}
data += sizeof(uint16);
data_length -= sizeof(uint16);
if (dtls_uint8_to_int(data) != 1 + 2 * DTLS_EC_KEY_SIZE) {
dtls_alert("expected 65 bytes long public point\n");
return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
}
data += sizeof(uint8);
data_length -= sizeof(uint8);
if (dtls_uint8_to_int(data) != 4) {
dtls_alert("expected uncompressed public point\n");
return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
}
data += sizeof(uint8);
data_length -= sizeof(uint8);
memcpy(config->keyx.ecdsa.other_eph_pub_x, data, sizeof(config->keyx.ecdsa.other_eph_pub_y));
data += sizeof(config->keyx.ecdsa.other_eph_pub_y);
data_length -= sizeof(config->keyx.ecdsa.other_eph_pub_y);
memcpy(config->keyx.ecdsa.other_eph_pub_y, data, sizeof(config->keyx.ecdsa.other_eph_pub_y));
data += sizeof(config->keyx.ecdsa.other_eph_pub_y);
data_length -= sizeof(config->keyx.ecdsa.other_eph_pub_y);
ret = dtls_check_ecdsa_signature_elem(data, data_length, &result_r, &result_s);
if (ret < 0) {
return ret;
}
data += ret;
data_length -= ret;
ret = dtls_ecdsa_verify_sig(config->keyx.ecdsa.other_pub_x, config->keyx.ecdsa.other_pub_y,
sizeof(config->keyx.ecdsa.other_pub_x),
config->tmp.random.client, DTLS_RANDOM_LENGTH,
config->tmp.random.server, DTLS_RANDOM_LENGTH,
key_params,
1 + 2 + 1 + 1 + (2 * DTLS_EC_KEY_SIZE),
result_r, result_s);
if (ret < 0) {
dtls_alert("wrong signature\n");
return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
}
return 0;
}
#endif /* DTLS_ECC */
#ifdef DTLS_PSK
static int
check_server_key_exchange_psk(dtls_context_t *ctx,
dtls_peer_t *peer,
uint8 *data, size_t data_length)
{
dtls_handshake_parameters_t *config = peer->handshake_params;
uint16_t len;
(void)ctx;
update_hs_hash(peer, data, data_length);
assert(is_tls_psk_with_aes_128_ccm_8(config->cipher));
data += DTLS_HS_LENGTH;
if (data_length < DTLS_HS_LENGTH + DTLS_SKEXECPSK_LENGTH_MIN) {
dtls_alert("the packet length does not match the expected\n");
return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
}
len = dtls_uint16_to_int(data);
data += sizeof(uint16);
if (len != data_length - DTLS_HS_LENGTH - sizeof(uint16)) {
dtls_warn("the length of the server identity hint is worng\n");
return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
}
if (len > DTLS_PSK_MAX_CLIENT_IDENTITY_LEN) {
dtls_warn("please use a smaller server identity hint\n");
return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
}
/* store the psk_identity_hint in config->keyx.psk for later use */
config->keyx.psk.id_length = len;
memcpy(config->keyx.psk.identity, data, len);
return 0;
}
#endif /* DTLS_PSK */
static int
check_certificate_request(dtls_context_t *ctx,
dtls_peer_t *peer,
uint8 *data, size_t data_length)
{
unsigned int i;
int auth_alg;
int sig_alg;
int hash_alg;
(void)ctx;
update_hs_hash(peer, data, data_length);
assert(is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(peer->handshake_params->cipher));
data += DTLS_HS_LENGTH;
if (data_length < DTLS_HS_LENGTH + 5) {
dtls_alert("the packet length does not match the expected\n");
return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
}
i = dtls_uint8_to_int(data);
data += sizeof(uint8);
if (i + 1 > data_length) {
dtls_alert("the cerfificate types are too long\n");
return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
}
auth_alg = 0;
for (; i > 0 ; i -= sizeof(uint8)) {
if (dtls_uint8_to_int(data) == TLS_CLIENT_CERTIFICATE_TYPE_ECDSA_SIGN
&& auth_alg == 0)
auth_alg = dtls_uint8_to_int(data);
data += sizeof(uint8);
}
if (auth_alg != TLS_CLIENT_CERTIFICATE_TYPE_ECDSA_SIGN) {
dtls_alert("the request authentication algorithm is not supproted\n");
return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
}
i = dtls_uint16_to_int(data);
data += sizeof(uint16);
if (i + 1 > data_length) {
dtls_alert("the signature and hash algorithm list is too long\n");
return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
}
hash_alg = 0;
sig_alg = 0;
for (; i > 0 ; i -= sizeof(uint16)) {
int current_hash_alg;
int current_sig_alg;
current_hash_alg = dtls_uint8_to_int(data);
data += sizeof(uint8);
current_sig_alg = dtls_uint8_to_int(data);
data += sizeof(uint8);
if (current_hash_alg == TLS_EXT_SIG_HASH_ALGO_SHA256 && hash_alg == 0 &&
current_sig_alg == TLS_EXT_SIG_HASH_ALGO_ECDSA && sig_alg == 0) {
hash_alg = current_hash_alg;
sig_alg = current_sig_alg;
}
}
if (hash_alg != TLS_EXT_SIG_HASH_ALGO_SHA256 ||
sig_alg != TLS_EXT_SIG_HASH_ALGO_ECDSA) {
dtls_alert("no supported hash and signature algorithem\n");
return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
}
/* common names are ignored */
peer->handshake_params->do_client_auth = 1;
return 0;
}
static int
check_server_hellodone(dtls_context_t *ctx,
dtls_peer_t *peer,
uint8 *data, size_t data_length)
{
int res;
#ifdef DTLS_ECC
const dtls_ecdsa_key_t *ecdsa_key;
#endif /* DTLS_ECC */
dtls_handshake_parameters_t *handshake = peer->handshake_params;
/* calculate master key, send CCS */
update_hs_hash(peer, data, data_length);
#ifdef DTLS_ECC
if (handshake->do_client_auth) {
res = CALL(ctx, get_ecdsa_key, &peer->session, &ecdsa_key);
if (res < 0) {
dtls_crit("no ecdsa certificate to send in certificate\n");
return res;
}
res = dtls_send_certificate_ecdsa(ctx, peer, ecdsa_key);
if (res < 0) {
dtls_debug("dtls_server_hello: cannot prepare Certificate record\n");
return res;
}
}
#endif /* DTLS_ECC */
/* send ClientKeyExchange */
res = dtls_send_client_key_exchange(ctx, peer);
if (res < 0) {
dtls_debug("cannot send KeyExchange message\n");
return res;
}
#ifdef DTLS_ECC
if (handshake->do_client_auth) {
res = dtls_send_certificate_verify_ecdh(ctx, peer, ecdsa_key);
if (res < 0) {
dtls_debug("dtls_server_hello: cannot prepare Certificate record\n");
return res;
}
}
#endif /* DTLS_ECC */
res = calculate_key_block(ctx, handshake, peer,
&peer->session, peer->role);
if (res < 0) {
return res;
}
res = dtls_send_ccs(ctx, peer);
if (res < 0) {
dtls_debug("cannot send CCS message\n");
return res;
}
/* and switch cipher suite */
dtls_security_params_switch(peer);
/* Client Finished */
return dtls_send_finished(ctx, peer, PRF_LABEL(client), PRF_LABEL_SIZE(client));
}
static int
decrypt_verify(dtls_peer_t *peer, uint8 *packet, size_t length,
uint8 **cleartext)
{
dtls_record_header_t *header = DTLS_RECORD_HEADER(packet);
dtls_security_parameters_t *security = dtls_security_params_epoch(peer, dtls_get_epoch(header));
int clen;
*cleartext = (uint8 *)packet + sizeof(dtls_record_header_t);
clen = length - sizeof(dtls_record_header_t);
if (!security) {
dtls_alert("No security context for epoch: %i\n", dtls_get_epoch(header));
return -1;
}
if (security->cipher == TLS_NULL_WITH_NULL_NULL) {
/* no cipher suite selected */
return clen;
} else { /* TLS_PSK_WITH_AES_128_CCM_8 or TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 */
/**
* length of additional_data for the AEAD cipher which consists of
* seq_num(2+6) + type(1) + version(2) + length(2)
*/
#define A_DATA_LEN 13
unsigned char nonce[DTLS_CCM_BLOCKSIZE];
unsigned char A_DATA[A_DATA_LEN];
if (clen < 16) /* need at least IV and MAC */
return -1;
memset(nonce, 0, DTLS_CCM_BLOCKSIZE);
memcpy(nonce, dtls_kb_remote_iv(security, peer->role),
dtls_kb_iv_size(security, peer->role));
/* read epoch and seq_num from message */
memcpy(nonce + dtls_kb_iv_size(security, peer->role), *cleartext, 8);
*cleartext += 8;
clen -= 8;
dtls_debug_dump("nonce", nonce, DTLS_CCM_BLOCKSIZE);
dtls_debug_dump("key", dtls_kb_remote_write_key(security, peer->role),
dtls_kb_key_size(security, peer->role));
dtls_debug_dump("ciphertext", *cleartext, clen);
/* re-use N to create additional data according to RFC 5246, Section 6.2.3.3:
*
* additional_data = seq_num + TLSCompressed.type +
* TLSCompressed.version + TLSCompressed.length;
*/
memcpy(A_DATA, &DTLS_RECORD_HEADER(packet)->epoch, 8); /* epoch and seq_num */
memcpy(A_DATA + 8, &DTLS_RECORD_HEADER(packet)->content_type, 3); /* type and version */
dtls_int_to_uint16(A_DATA + 11, clen - 8); /* length without nonce_explicit */
clen = dtls_decrypt(*cleartext, clen, *cleartext, nonce,
dtls_kb_remote_write_key(security, peer->role),
dtls_kb_key_size(security, peer->role),
A_DATA, A_DATA_LEN);
if (clen < 0)
dtls_warn("decryption failed\n");
else {
#ifndef NDEBUG
printf("decrypt_verify(): found %i bytes cleartext\n", clen);
#endif
dtls_security_params_free_other(peer);
dtls_debug_dump("cleartext", *cleartext, clen);
}
}
return clen;
}
static int
dtls_send_hello_request(dtls_context_t *ctx, dtls_peer_t *peer)
{
return dtls_send_handshake_msg_hash(ctx, peer, &peer->session,
DTLS_HT_HELLO_REQUEST,
NULL, 0, 0);
}
int
dtls_renegotiate(dtls_context_t *ctx, const session_t *dst)
{
dtls_peer_t *peer = NULL;
int err;
peer = dtls_get_peer(ctx, dst);
if (!peer) {
return -1;
}
if (peer->state != DTLS_STATE_CONNECTED)
return -1;
peer->handshake_params = dtls_handshake_new();
if (!peer->handshake_params)
return -1;
peer->handshake_params->hs_state.mseq_r = 0;
peer->handshake_params->hs_state.mseq_s = 0;
if (peer->role == DTLS_CLIENT) {
/* send ClientHello with empty Cookie */
err = dtls_send_client_hello(ctx, peer, NULL, 0);
if (err < 0)
dtls_warn("cannot send ClientHello\n");
else
peer->state = DTLS_STATE_CLIENTHELLO;
return err;
} else if (peer->role == DTLS_SERVER) {
return dtls_send_hello_request(ctx, peer);
}
return -1;
}
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) {
int err = 0;
/* This will clear the retransmission buffer if we get an expected
* handshake message. We have to make sure that no handshake message
* should get expected when we still should retransmit something, when
* we do everything accordingly to the DTLS 1.2 standard this should
* not be a problem. */
if (peer) {
dtls_stop_retransmission(ctx, peer);
}
/* The following switch construct handles the given message with
* respect to the current internal state for this peer. In case of
* error, it is left with return 0. */
dtls_debug("handle handshake packet of type: %s (%i)\n",
dtls_handshake_type_to_name(data[0]), data[0]);
switch (data[0]) {
/************************************************************************
* Client states
************************************************************************/
case DTLS_HT_HELLO_VERIFY_REQUEST:
if (state != DTLS_STATE_CLIENTHELLO) {
return dtls_alert_fatal_create(DTLS_ALERT_UNEXPECTED_MESSAGE);
}
err = check_server_hello_verify_request(ctx, peer, data, data_length);
if (err < 0) {
dtls_warn("error in check_server_hello_verify_request err: %i\n", err);
return err;
}
break;
case DTLS_HT_SERVER_HELLO:
if (state != DTLS_STATE_CLIENTHELLO) {
return dtls_alert_fatal_create(DTLS_ALERT_UNEXPECTED_MESSAGE);
}
err = check_server_hello(ctx, peer, data, data_length);
if (err < 0) {
dtls_warn("error in check_server_hello err: %i\n", err);
return err;
}
if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(peer->handshake_params->cipher))
peer->state = DTLS_STATE_WAIT_SERVERCERTIFICATE;
else
peer->state = DTLS_STATE_WAIT_SERVERHELLODONE;
/* update_hs_hash(peer, data, data_length); */
break;
#ifdef DTLS_ECC
case DTLS_HT_CERTIFICATE:
if ((role == DTLS_CLIENT && state != DTLS_STATE_WAIT_SERVERCERTIFICATE) ||
(role == DTLS_SERVER && state != DTLS_STATE_WAIT_CLIENTCERTIFICATE)) {
return dtls_alert_fatal_create(DTLS_ALERT_UNEXPECTED_MESSAGE);
}
err = check_server_certificate(ctx, peer, data, data_length);
if (err < 0) {
dtls_warn("error in check_server_certificate err: %i\n", err);
return err;
}
if (role == DTLS_CLIENT) {
peer->state = DTLS_STATE_WAIT_SERVERKEYEXCHANGE;
} else if (role == DTLS_SERVER){
peer->state = DTLS_STATE_WAIT_CLIENTKEYEXCHANGE;
}
/* update_hs_hash(peer, data, data_length); */
break;
#endif /* DTLS_ECC */
case DTLS_HT_SERVER_KEY_EXCHANGE:
#ifdef DTLS_ECC
if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(peer->handshake_params->cipher)) {
if (state != DTLS_STATE_WAIT_SERVERKEYEXCHANGE) {
return dtls_alert_fatal_create(DTLS_ALERT_UNEXPECTED_MESSAGE);
}
err = check_server_key_exchange_ecdsa(ctx, peer, data, data_length);
}
#endif /* DTLS_ECC */
#ifdef DTLS_PSK
if (is_tls_psk_with_aes_128_ccm_8(peer->handshake_params->cipher)) {
if (state != DTLS_STATE_WAIT_SERVERHELLODONE) {
return dtls_alert_fatal_create(DTLS_ALERT_UNEXPECTED_MESSAGE);
}
err = check_server_key_exchange_psk(ctx, peer, data, data_length);
}
#endif /* DTLS_PSK */
if (err < 0) {
dtls_warn("error in check_server_key_exchange err: %i\n", err);
return err;
}
peer->state = DTLS_STATE_WAIT_SERVERHELLODONE;
/* update_hs_hash(peer, data, data_length); */
break;
case DTLS_HT_SERVER_HELLO_DONE:
if (state != DTLS_STATE_WAIT_SERVERHELLODONE) {
return dtls_alert_fatal_create(DTLS_ALERT_UNEXPECTED_MESSAGE);
}
err = check_server_hellodone(ctx, peer, data, data_length);
if (err < 0) {
dtls_warn("error in check_server_hellodone err: %i\n", err);
return err;
}
peer->state = DTLS_STATE_WAIT_CHANGECIPHERSPEC;
/* update_hs_hash(peer, data, data_length); */
break;
case DTLS_HT_CERTIFICATE_REQUEST:
if (state != DTLS_STATE_WAIT_SERVERHELLODONE) {
return dtls_alert_fatal_create(DTLS_ALERT_UNEXPECTED_MESSAGE);
}
err = check_certificate_request(ctx, peer, data, data_length);
if (err < 0) {
dtls_warn("error in check_certificate_request err: %i\n", err);
return err;
}
break;
case DTLS_HT_FINISHED:
/* expect a Finished message from server */
if (state != DTLS_STATE_WAIT_FINISHED) {
return dtls_alert_fatal_create(DTLS_ALERT_UNEXPECTED_MESSAGE);
}
err = check_finished(ctx, peer, data, data_length);
if (err < 0) {
dtls_warn("error in check_finished err: %i\n", err);
return err;
}
if (role == DTLS_SERVER) {
/* send ServerFinished */
update_hs_hash(peer, data, data_length);
/* send change cipher spec message and switch to new configuration */
err = dtls_send_ccs(ctx, peer);
if (err < 0) {
dtls_warn("cannot send CCS message\n");
return err;
}
dtls_security_params_switch(peer);
err = dtls_send_finished(ctx, peer, PRF_LABEL(server), PRF_LABEL_SIZE(server));
if (err < 0) {
dtls_warn("sending server Finished failed\n");
return err;
}
}
dtls_handshake_free(peer->handshake_params);
peer->handshake_params = NULL;
dtls_debug("Handshake complete\n");
check_stack();
peer->state = DTLS_STATE_CONNECTED;
/* return here to not increase the message receive counter */
return err;
/************************************************************************
* Server states
************************************************************************/
case DTLS_HT_CLIENT_KEY_EXCHANGE:
/* handle ClientHello, update msg and msglen and goto next if not finished */
if (state != DTLS_STATE_WAIT_CLIENTKEYEXCHANGE) {
return dtls_alert_fatal_create(DTLS_ALERT_UNEXPECTED_MESSAGE);
}
err = check_client_keyexchange(ctx, peer->handshake_params, data, data_length);
if (err < 0) {
dtls_warn("error in check_client_keyexchange err: %i\n", err);
return err;
}
update_hs_hash(peer, data, data_length);
if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(peer->handshake_params->cipher) &&
is_ecdsa_client_auth_supported(ctx))
peer->state = DTLS_STATE_WAIT_CERTIFICATEVERIFY;
else
peer->state = DTLS_STATE_WAIT_CHANGECIPHERSPEC;
break;
#ifdef DTLS_ECC
case DTLS_HT_CERTIFICATE_VERIFY:
if (state != DTLS_STATE_WAIT_CERTIFICATEVERIFY) {
return dtls_alert_fatal_create(DTLS_ALERT_UNEXPECTED_MESSAGE);
}
err = check_client_certificate_verify(ctx, peer, data, data_length);
if (err < 0) {
dtls_warn("error in check_client_certificate_verify err: %i\n", err);
return err;
}
update_hs_hash(peer, data, data_length);
peer->state = DTLS_STATE_WAIT_CHANGECIPHERSPEC;
break;
#endif /* DTLS_ECC */
case DTLS_HT_CLIENT_HELLO:
if ((peer && state != DTLS_STATE_CONNECTED && state != DTLS_STATE_WAIT_CLIENTHELLO) ||
(!peer && state != DTLS_STATE_WAIT_CLIENTHELLO)) {
return dtls_alert_fatal_create(DTLS_ALERT_UNEXPECTED_MESSAGE);
}
/* When no DTLS state exists for this peer, we only allow a
Client Hello message with
a) a valid cookie, or
b) no cookie.
Anything else will be rejected. Fragementation is not allowed
here as it would require peer state as well.
*/
err = dtls_verify_peer(ctx, peer, session, state, data, data_length);
if (err < 0) {
dtls_warn("error in dtls_verify_peer err: %i\n", err);
return err;
}
if (err > 0) {
dtls_debug("server hello verify was sent\n");
break;
}
/* At this point, we have a good relationship with this peer. This
* state is left for re-negotiation of key material. */
/* As per RFC 6347 - section 4.2.8 if this is an attempt to
* rehandshake, we can delete the existing key material
* as the client has demonstrated reachibility by completing
* the cookie exchange */
if (peer && state == DTLS_STATE_WAIT_CLIENTHELLO) {
dtls_debug("removing the peer\n");
DEL_PEER(ctx->peers, peer);
dtls_free_peer(peer);
peer = NULL;
}
if (!peer) {
dtls_debug("creating new peer\n");
dtls_security_parameters_t *security;
/* msg contains a Client Hello with a valid cookie, so we can
* safely create the server state machine and continue with
* the handshake. */
peer = dtls_new_peer(session);
if (!peer) {
dtls_alert("cannot create peer\n");
return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
}
peer->role = DTLS_SERVER;
/* Initialize record sequence number to 1 for new peers. The first
* record with sequence number 0 is a stateless Hello Verify Request.
*/
security = dtls_security_params(peer);
security->rseq = 1;
if (dtls_add_peer(ctx, peer) < 0) {
dtls_alert("cannot add peer\n");
dtls_free_peer(peer);
return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
}
}
if (peer && !peer->handshake_params) {
dtls_handshake_header_t *hs_header = DTLS_HANDSHAKE_HEADER(data);
peer->handshake_params = dtls_handshake_new();
if (!peer->handshake_params)
return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
peer->handshake_params->hs_state.mseq_r = dtls_uint16_to_int(hs_header->message_seq);
peer->handshake_params->hs_state.mseq_s = 1;
}
clear_hs_hash(peer);
/* First negotiation step: check for PSK
*
* Note that we already have checked that msg is a Handshake
* message containing a ClientHello. dtls_get_cipher() therefore
* does not check again.
*/
err = dtls_update_parameters(ctx, peer, data, data_length);
if (err < 0) {
dtls_warn("error updating security parameters\n");
return err;
}
/* update finish MAC */
update_hs_hash(peer, data, data_length);
err = dtls_send_server_hello_msgs(ctx, peer);
if (err < 0) {
return err;
}
if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(peer->handshake_params->cipher) &&
is_ecdsa_client_auth_supported(ctx))
peer->state = DTLS_STATE_WAIT_CLIENTCERTIFICATE;
else
peer->state = DTLS_STATE_WAIT_CLIENTKEYEXCHANGE;
/* after sending the ServerHelloDone, we expect the
* ClientKeyExchange (possibly containing the PSK id),
* followed by a ChangeCipherSpec and an encrypted Finished.
*/
break;
case DTLS_HT_HELLO_REQUEST:
if (state != DTLS_STATE_CONNECTED) {
/* we should just ignore such packets when in handshake */
return 0;
}
if (peer && !peer->handshake_params) {
peer->handshake_params = dtls_handshake_new();
if (!peer->handshake_params)
return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
peer->handshake_params->hs_state.mseq_r = 0;
peer->handshake_params->hs_state.mseq_s = 0;
}
/* send ClientHello with empty Cookie */
err = dtls_send_client_hello(ctx, peer, NULL, 0);
if (err < 0) {
dtls_warn("cannot send ClientHello\n");
return err;
}
peer->state = DTLS_STATE_CLIENTHELLO;
break;
default:
dtls_crit("unhandled message %d\n", data[0]);
return dtls_alert_fatal_create(DTLS_ALERT_UNEXPECTED_MESSAGE);
}
if (peer && peer->handshake_params && err >= 0) {
peer->handshake_params->hs_state.mseq_r++;
}
return err;
}
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)
{
dtls_handshake_header_t *hs_header;
int res;
if (data_length < DTLS_HS_LENGTH) {
dtls_warn("handshake message too short\n");
return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
}
hs_header = DTLS_HANDSHAKE_HEADER(data);
dtls_debug("received handshake packet of type: %s (%i)\n",
dtls_handshake_type_to_name(hs_header->msg_type), hs_header->msg_type);
if (!peer || !peer->handshake_params) {
/* This is the initial ClientHello */
if (hs_header->msg_type != DTLS_HT_CLIENT_HELLO && !peer) {
dtls_warn("If there is no peer only ClientHello is allowed\n");
return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
}
/* This is a ClientHello or Hello Request send when doing TLS renegotiation */
if (hs_header->msg_type == DTLS_HT_CLIENT_HELLO ||
hs_header->msg_type == DTLS_HT_HELLO_REQUEST) {
return handle_handshake_msg(ctx, peer, session, role, state, data,
data_length);
} else {
dtls_warn("ignore unexpected handshake message\n");
return 0;
}
}
if (dtls_uint16_to_int(hs_header->message_seq) < peer->handshake_params->hs_state.mseq_r) {
dtls_warn("The message sequence number is too small, expected %i, got: %i\n",
peer->handshake_params->hs_state.mseq_r, dtls_uint16_to_int(hs_header->message_seq));
return 0;
} else if (dtls_uint16_to_int(hs_header->message_seq) > peer->handshake_params->hs_state.mseq_r) {
/* A packet in between is missing, buffer this packet. */
netq_t *n;
/* TODO: only add packet that are not too new. */
if (data_length > DTLS_MAX_BUF) {
dtls_warn("the packet is too big to buffer for reoder\n");
return 0;
}
netq_t *node = netq_head(&peer->handshake_params->reorder_queue);
while (node) {
dtls_handshake_header_t *node_header = DTLS_HANDSHAKE_HEADER(node->data);
if (dtls_uint16_to_int(node_header->message_seq) == dtls_uint16_to_int(hs_header->message_seq)) {
dtls_warn("a packet with this sequence number is already stored\n");
return 0;
}
node = netq_next(node);
}
n = netq_node_new(data_length);
if (!n) {
dtls_warn("no space in reoder buffer\n");
return 0;
}
n->peer = peer;
n->length = data_length;
memcpy(n->data, data, data_length);
if (!netq_insert_node(&peer->handshake_params->reorder_queue, n)) {
dtls_warn("cannot add packet to reoder buffer\n");
netq_node_free(n);
}
dtls_info("Added packet for reordering\n");
return 0;
} else if (dtls_uint16_to_int(hs_header->message_seq) == peer->handshake_params->hs_state.mseq_r) {
/* Found the expected packet, use this and all the buffered packet */
int next = 1;
res = handle_handshake_msg(ctx, peer, session, role, state, data, data_length);
if (res < 0)
return res;
/* We do not know in which order the packet are in the list just search the list for every packet. */
while (next && peer->handshake_params) {
next = 0;
netq_t *node = netq_head(&peer->handshake_params->reorder_queue);
while (node) {
dtls_handshake_header_t *node_header = DTLS_HANDSHAKE_HEADER(node->data);
if (dtls_uint16_to_int(node_header->message_seq) == peer->handshake_params->hs_state.mseq_r) {
netq_remove(&peer->handshake_params->reorder_queue, node);
next = 1;
res = handle_handshake_msg(ctx, peer, session, role, peer->state, node->data, node->length);
if (res < 0) {
return res;
}
break;
} else {
node = netq_next(node);
}
}
}
return res;
}
assert(0);
return 0;
}
static int
handle_ccs(dtls_context_t *ctx, dtls_peer_t *peer,
uint8 *record_header, uint8 *data, size_t data_length)
{
int err;
dtls_handshake_parameters_t *handshake = peer->handshake_params;
(void)record_header;
/* A CCS message is handled after a KeyExchange message was
* received from the client. When security parameters have been
* updated successfully and a ChangeCipherSpec message was sent
* by ourself, the security context is switched and the record
* sequence number is reset. */
if (!peer || peer->state != DTLS_STATE_WAIT_CHANGECIPHERSPEC) {
dtls_warn("expected ChangeCipherSpec during handshake\n");
return 0;
}
if (data_length < 1 || data[0] != 1)
return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
/* Just change the cipher when we are on the same epoch */
if (peer->role == DTLS_SERVER) {
err = calculate_key_block(ctx, handshake, peer,
&peer->session, peer->role);
if (err < 0) {
return err;
}
}
peer->state = DTLS_STATE_WAIT_FINISHED;
return 0;
}
/**
* Handles incoming Alert messages. This function returns \c 1 if the
* connection should be closed and the peer is to be invalidated.
*/
static int
handle_alert(dtls_context_t *ctx, dtls_peer_t *peer,
uint8 *record_header, uint8 *data, size_t data_length) {
int free_peer = 0; /* indicates whether to free peer */
(void)record_header;
if (data_length < 2)
return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
dtls_info("** Alert: level %d, description %d\n", data[0], data[1]);
if (!peer) {
dtls_warn("got an alert for an unknown peer, we probably already removed it, ignore it\n");
return 0;
}
/* The peer object is invalidated for FATAL alerts and close
* notifies. This is done in two steps.: First, remove the object
* from our list of peers. After that, the event handler callback is
* invoked with the still existing peer object. Finally, the storage
* used by peer is released.
*/
if (data[0] == DTLS_ALERT_LEVEL_FATAL || data[1] == DTLS_ALERT_CLOSE_NOTIFY) {
dtls_alert("%d invalidate peer\n", data[1]);
DEL_PEER(ctx->peers, peer);
#ifdef WITH_CONTIKI
#ifndef NDEBUG
PRINTF("removed peer [");
PRINT6ADDR(&peer->session.addr);
PRINTF("]:%d\n", uip_ntohs(peer->session.port));
#endif
#endif /* WITH_CONTIKI */
free_peer = 1;
}
(void)CALL(ctx, event, &peer->session,
(dtls_alert_level_t)data[0], (unsigned short)data[1]);
switch (data[1]) {
case DTLS_ALERT_CLOSE_NOTIFY:
/* If state is DTLS_STATE_CLOSING, we have already sent a
* close_notify so, do not send that again. */
if (peer->state != DTLS_STATE_CLOSING) {
peer->state = DTLS_STATE_CLOSING;
dtls_send_alert(ctx, peer, DTLS_ALERT_LEVEL_FATAL, DTLS_ALERT_CLOSE_NOTIFY);
} else
peer->state = DTLS_STATE_CLOSED;
break;
default:
;
}
if (free_peer) {
dtls_stop_retransmission(ctx, peer);
dtls_destroy_peer(ctx, peer, 1);
}
return free_peer;
}
static int dtls_alert_send_from_err(dtls_context_t *ctx, dtls_peer_t *peer,
session_t *session, int err)
{
int level;
int desc;
if (err < -(1 << 8) && err > -(3 << 8)) {
level = ((-err) & 0xff00) >> 8;
desc = (-err) & 0xff;
if (!peer) {
peer = dtls_get_peer(ctx, session);
}
if (peer) {
peer->state = DTLS_STATE_CLOSING;
return dtls_send_alert(ctx, peer, level, desc);
}
} else if (err == -1) {
if (!peer) {
peer = dtls_get_peer(ctx, session);
}
if (peer) {
peer->state = DTLS_STATE_CLOSING;
return dtls_send_alert(ctx, peer, DTLS_ALERT_LEVEL_FATAL, DTLS_ALERT_INTERNAL_ERROR);
}
}
return -1;
}
/**
* Handles incoming data as DTLS message from given peer.
*/
int
dtls_handle_message(dtls_context_t *ctx,
session_t *session,
uint8 *msg, int msglen) {
dtls_peer_t *peer = NULL;
unsigned int rlen; /* record length */
uint8 *data; /* (decrypted) payload */
int data_length; /* length of decrypted payload
(without MAC and padding) */
int err;
/* check if we have DTLS state for addr/port/ifindex */
peer = dtls_get_peer(ctx, session);
if (!peer) {
dtls_debug("dtls_handle_message: PEER NOT FOUND\n");
dtls_dsrv_log_addr(DTLS_LOG_DEBUG, "peer addr", session);
} else {
dtls_debug("dtls_handle_message: FOUND PEER\n");
}
while ((rlen = is_record(msg,msglen))) {
dtls_peer_type role;
dtls_state_t state;
dtls_debug("got packet %d (%d bytes)\n", msg[0], rlen);
if (peer) {
dtls_record_header_t *header = DTLS_RECORD_HEADER(msg);
dtls_security_parameters_t *security = dtls_security_params_epoch(peer, dtls_get_epoch(header));
if (!security) {
dtls_alert("No security context for epoch: %i\n", dtls_get_epoch(header));
data_length = -1;
} else {
uint64_t pkt_seq_nr = dtls_uint48_to_int(header->sequence_number);
if(pkt_seq_nr == 0 && security->cseq.cseq == 0) {
data_length = decrypt_verify(peer, msg, rlen, &data);
if (data_length) {
security->cseq.cseq = 0;
security->cseq.bitfield = -1;
}
} else if (pkt_seq_nr == security->cseq.cseq) {
dtls_info("Duplicate packet arrived (cseq=%" PRIu64 ")\n", security->cseq.cseq);
return 0;
} else if ((int64_t)(security->cseq.cseq-pkt_seq_nr) > 0) { /* pkt_seq_nr < security->cseq.cseq */
if (((security->cseq.cseq-1)-pkt_seq_nr) < 64) {
if(security->cseq.bitfield & (1<<((security->cseq.cseq-1)-pkt_seq_nr))) {
dtls_info("Duplicate packet arrived (bitfield)\n");
/* seen it */
return 0;
} else {
dtls_info("Packet arrived out of order\n");
data_length = decrypt_verify(peer, msg, rlen, &data);
if(data_length > 0) {
security->cseq.bitfield |= (1<<((security->cseq.cseq-1)-pkt_seq_nr));
}
}
} else {
dtls_info("Packet from before the bitfield arrived\n");
return 0;
}
} else { /* pkt_seq_nr > security->cseq.cseq */
data_length = decrypt_verify(peer, msg, rlen, &data);
if(data_length > 0) {
security->cseq.bitfield <<= (pkt_seq_nr-security->cseq.cseq);
security->cseq.bitfield |= 1<<((pkt_seq_nr-security->cseq.cseq)-1);
security->cseq.cseq = pkt_seq_nr;
dtls_debug("new packet arrived with seq_nr: %" PRIu64 "\n", pkt_seq_nr);
dtls_debug("new bitfield is : %" PRIx64 "\n", security->cseq.bitfield);
}
}
}
if (data_length < 0) {
if (hs_attempt_with_existing_peer(msg, rlen, peer)) {
data = msg + DTLS_RH_LENGTH;
data_length = rlen - DTLS_RH_LENGTH;
state = DTLS_STATE_WAIT_CLIENTHELLO;
role = DTLS_SERVER;
} else {
err = dtls_alert_fatal_create(DTLS_ALERT_DECRYPT_ERROR);
dtls_info("decrypt_verify() failed\n");
if (peer->state < DTLS_STATE_CONNECTED) {
dtls_alert_send_from_err(ctx, peer, &peer->session, err);
peer->state = DTLS_STATE_CLOSED;
dtls_stop_retransmission(ctx, peer);
dtls_destroy_peer(ctx, peer, 1);
}
return err;
}
} else {
role = peer->role;
state = peer->state;
}
} else {
/* is_record() ensures that msg contains at least a record header */
data = msg + DTLS_RH_LENGTH;
data_length = rlen - DTLS_RH_LENGTH;
state = DTLS_STATE_WAIT_CLIENTHELLO;
role = DTLS_SERVER;
}
dtls_debug_hexdump("receive header", msg, sizeof(dtls_record_header_t));
dtls_debug_hexdump("receive unencrypted", data, data_length);
/* Handle received record according to the first byte of the
* message, i.e. the subprotocol. We currently do not support
* combining multiple fragments of one type into a single
* record. */
switch (msg[0]) {
case DTLS_CT_CHANGE_CIPHER_SPEC:
if (peer) {
dtls_stop_retransmission(ctx, peer);
}
err = handle_ccs(ctx, peer, msg, data, data_length);
if (err < 0) {
dtls_warn("error while handling ChangeCipherSpec message\n");
dtls_alert_send_from_err(ctx, peer, session, err);
/* invalidate peer */
dtls_destroy_peer(ctx, peer, 1);
peer = NULL;
return err;
}
break;
case DTLS_CT_ALERT:
if (peer) {
dtls_stop_retransmission(ctx, peer);
}
err = handle_alert(ctx, peer, msg, data, data_length);
if (err < 0 || err == 1) {
dtls_warn("received alert, peer has been invalidated\n");
/* handle alert has invalidated peer */
peer = NULL;
return err < 0 ?err:-1;
}
break;
case DTLS_CT_HANDSHAKE:
/* Handshake messages other than Finish must use the current
* epoch, Finish has epoch + 1. */
if (peer) {
uint16_t expected_epoch = dtls_security_params(peer)->epoch;
uint16_t msg_epoch =
dtls_uint16_to_int(DTLS_RECORD_HEADER(msg)->epoch);
/* The new security parameters must be used for all messages
* that are sent after the ChangeCipherSpec message. This
* means that the client's Finished message uses epoch + 1
* while the server is still in the old epoch.
*/
if (role == DTLS_SERVER && state == DTLS_STATE_WAIT_FINISHED) {
expected_epoch++;
}
if (expected_epoch != msg_epoch) {
if (hs_attempt_with_existing_peer(msg, rlen, peer)) {
state = DTLS_STATE_WAIT_CLIENTHELLO;
role = DTLS_SERVER;
} else {
dtls_warn("Wrong epoch, expected %i, got: %i\n",
expected_epoch, msg_epoch);
break;
}
}
}
err = handle_handshake(ctx, peer, session, role, state, data, data_length);
if (err < 0) {
dtls_warn("error while handling handshake packet\n");
dtls_alert_send_from_err(ctx, peer, session, err);
return err;
}
if (peer && peer->state == DTLS_STATE_CONNECTED) {
/* stop retransmissions */
dtls_stop_retransmission(ctx, peer);
CALL(ctx, event, &peer->session, 0, DTLS_EVENT_CONNECTED);
}
break;
case DTLS_CT_APPLICATION_DATA:
dtls_info("** application data:\n");
if (!peer) {
dtls_warn("no peer available, send an alert\n");
// TODO: should we send a alert here?
return -1;
}
dtls_stop_retransmission(ctx, peer);
CALL(ctx, read, &peer->session, data, data_length);
break;
default:
dtls_info("dropped unknown message of type %d\n",msg[0]);
}
/* advance msg by length of ciphertext */
msg += rlen;
msglen -= rlen;
}
return 0;
}
dtls_context_t *
dtls_new_context(void *app_data) {
dtls_context_t *c;
dtls_tick_t now;
#ifndef WITH_CONTIKI
FILE *urandom = fopen("/dev/urandom", "r");
unsigned char buf[sizeof(unsigned long)];
#endif /* WITH_CONTIKI */
dtls_ticks(&now);
#ifdef WITH_CONTIKI
/* FIXME: need something better to init PRNG here */
dtls_prng_init(now);
#else /* WITH_CONTIKI */
if (!urandom) {
dtls_emerg("cannot initialize PRNG\n");
return NULL;
}
if (fread(buf, 1, sizeof(buf), urandom) != sizeof(buf)) {
dtls_emerg("cannot initialize PRNG\n");
return NULL;
}
fclose(urandom);
dtls_prng_init((unsigned long)*buf);
#endif /* WITH_CONTIKI */
c = malloc_context();
if (!c)
goto error;
memset(c, 0, sizeof(dtls_context_t));
c->app = app_data;
#ifdef WITH_CONTIKI
process_start(&dtls_retransmit_process, (char *)c);
PROCESS_CONTEXT_BEGIN(&dtls_retransmit_process);
/* the retransmit timer must be initialized to some large value */
etimer_set(&c->retransmit_timer, 0xFFFF);
PROCESS_CONTEXT_END(&coap_retransmit_process);
#endif /* WITH_CONTIKI */
if (dtls_prng(c->cookie_secret, DTLS_COOKIE_SECRET_LENGTH))
c->cookie_secret_age = now;
else
goto error;
return c;
error:
dtls_alert("cannot create DTLS context\n");
if (c)
dtls_free_context(c);
return NULL;
}
void dtls_reset_peer(dtls_context_t *ctx, dtls_peer_t *peer)
{
dtls_stop_retransmission(ctx, peer);
dtls_destroy_peer(ctx, peer, 1);
}
void
dtls_free_context(dtls_context_t *ctx) {
dtls_peer_t *p, *tmp;
if (!ctx) {
return;
}
if (ctx->peers) {
#ifdef DTLS_PEERS_NOHASH
LL_FOREACH_SAFE(ctx->peers, p, tmp) {
#else /* DTLS_PEERS_NOHASH */
HASH_ITER(hh, ctx->peers, p, tmp) {
#endif /* DTLS_PEERS_NOHASH */
dtls_destroy_peer(ctx, p, 1);
}
}
free_context(ctx);
}
int
dtls_connect_peer(dtls_context_t *ctx, dtls_peer_t *peer) {
int res;
assert(peer);
if (!peer)
return -1;
/* check if the same peer is already in our list */
if (peer == dtls_get_peer(ctx, &peer->session)) {
dtls_debug("found peer, try to re-connect\n");
return dtls_renegotiate(ctx, &peer->session);
}
/* set local peer role to client, remote is server */
peer->role = DTLS_CLIENT;
if (dtls_add_peer(ctx, peer) < 0) {
dtls_alert("cannot add peer\n");
return -1;
}
/* send ClientHello with empty Cookie */
peer->handshake_params = dtls_handshake_new();
if (!peer->handshake_params)
return -1;
peer->handshake_params->hs_state.mseq_r = 0;
peer->handshake_params->hs_state.mseq_s = 0;
res = dtls_send_client_hello(ctx, peer, NULL, 0);
if (res < 0)
dtls_warn("cannot send ClientHello\n");
else
peer->state = DTLS_STATE_CLIENTHELLO;
return res;
}
int
dtls_connect(dtls_context_t *ctx, const session_t *dst) {
dtls_peer_t *peer;
int res;
peer = dtls_get_peer(ctx, dst);
if (!peer)
peer = dtls_new_peer(dst);
if (!peer) {
dtls_crit("cannot create new peer\n");
return -1;
}
res = dtls_connect_peer(ctx, peer);
/* Invoke event callback to indicate connection attempt or
* re-negotiation. */
if (res > 0) {
CALL(ctx, event, &peer->session, 0, DTLS_EVENT_CONNECT);
} else if (res == 0) {
CALL(ctx, event, &peer->session, 0, DTLS_EVENT_RENEGOTIATE);
}
return res;
}
static void
dtls_retransmit(dtls_context_t *context, netq_t *node) {
if (!context || !node)
return;
/* re-initialize timeout when maximum number of retransmissions are not reached yet */
if (node->retransmit_cnt < DTLS_DEFAULT_MAX_RETRANSMIT) {
unsigned char sendbuf[DTLS_MAX_BUF];
size_t len = sizeof(sendbuf);
int err;
unsigned char *data = node->data;
size_t length = node->length;
dtls_tick_t now;
dtls_security_parameters_t *security = dtls_security_params_epoch(node->peer, node->epoch);
dtls_ticks(&now);
node->retransmit_cnt++;
node->t = now + (node->timeout << node->retransmit_cnt);
netq_insert_node(&context->sendqueue, node);
if (node->type == DTLS_CT_HANDSHAKE) {
dtls_handshake_header_t *hs_header = DTLS_HANDSHAKE_HEADER(data);
dtls_debug("** retransmit handshake packet of type: %s (%i)\n",
dtls_handshake_type_to_name(hs_header->msg_type), hs_header->msg_type);
} else {
dtls_debug("** retransmit packet\n");
}
err = dtls_prepare_record(node->peer, security, node->type, &data, &length,
1, sendbuf, &len);
if (err < 0) {
dtls_warn("can not retransmit packet, err: %i\n", err);
return;
}
dtls_debug_hexdump("retransmit header", sendbuf,
sizeof(dtls_record_header_t));
dtls_debug_hexdump("retransmit unencrypted", node->data, node->length);
(void)CALL(context, write, &node->peer->session, sendbuf, len);
return;
}
/* no more retransmissions, remove node from system */
dtls_debug("** removed transaction\n");
/* And finally delete the node */
netq_node_free(node);
}
static void
dtls_stop_retransmission(dtls_context_t *context, dtls_peer_t *peer) {
netq_t *node;
node = netq_head(&context->sendqueue);
while (node) {
if (dtls_session_equals(&node->peer->session, &peer->session)) {
netq_t *tmp = node;
node = netq_next(node);
netq_remove(&context->sendqueue, tmp);
netq_node_free(tmp);
} else
node = netq_next(node);
}
}
void
dtls_check_retransmit(dtls_context_t *context, clock_time_t *next) {
dtls_tick_t now;
netq_t *node = netq_head(&context->sendqueue);
dtls_ticks(&now);
while (node && node->t <= now) {
netq_pop_first(&context->sendqueue);
dtls_retransmit(context, node);
node = netq_head(&context->sendqueue);
}
if (next) {
*next = node ? node->t : 0;
}
}
#ifdef WITH_CONTIKI
/*---------------------------------------------------------------------------*/
/* message retransmission */
/*---------------------------------------------------------------------------*/
PROCESS_THREAD(dtls_retransmit_process, ev, data)
{
clock_time_t now;
netq_t *node;
PROCESS_BEGIN();
dtls_debug("Started DTLS retransmit process\r\n");
while(1) {
PROCESS_YIELD();
if (ev == PROCESS_EVENT_TIMER) {
if (etimer_expired(&the_dtls_context.retransmit_timer)) {
node = netq_head(&the_dtls_context.sendqueue);
now = clock_time();
if (node && node->t <= now) {
netq_pop_first(&the_dtls_context.sendqueue);
dtls_retransmit(&the_dtls_context, node);
node = netq_head(&the_dtls_context.sendqueue);
}
/* need to set timer to some value even if no nextpdu is available */
if (node) {
etimer_set(&the_dtls_context.retransmit_timer,
node->t <= now ? 1 : node->t - now);
} else {
etimer_set(&the_dtls_context.retransmit_timer, 0xFFFF);
}
}
}
}
PROCESS_END();
}
#endif /* WITH_CONTIKI */